0

I want to project a 3d point onto a 2d plane (screen).

Here is my code as well as the class that converts 3d coordinate to 2d coordinate.

The class to convert 3d coordinate to 2d coordinate:

class dtod(pygame.sprite.Sprite):
def __init__(self, point, scale):
    super().__init__()
    self.point = numpy.array(point)
    width, height = pygame.display.get_surface().get_size()
    self.angle = 0
    self.point[0] = (self.point[0] * 1) / self.point[2]
    self.point[1] = (self.point[1] * 1) / self.point[2]
    self.projection = numpy.array(
        [[math.cos(self.angle), math.sin(self.angle), 0.], [math.sin(self.angle), math.cos(self.angle), 0.]])
    self.point = numpy.dot(self.projection, self.point)
    self.point = ((self.point[0] * scale) + width / 2, height / 2 - (self.point[1] * scale))

The main code where I draw the things:

import pygame
import numpy as np
from coord import dtod
import math

pygame.init()
screen = pygame.display.set_mode((0, 0))
run = True
width, height = screen.get_size()
colors = {"white": (255, 255, 255), "red": (255, 0, 0), "green": (150, 253, 55), "blue": (0, 
227, 227), "orange": (255, 127, 39), "grey": (64, 64, 64), "yellow": (255, 240, 0)}
tx, ty, tz = 0., 0., 0.
fps = pygame.time.Clock()

def cos(x):
    return math.cos(x)


def sin(x):
    return math.sin(x)


rx = np.array([[1., 0., 0.], [0., cos(tx), -sin(tx)], [0., sin(tx), cos(tx)]])
ry = np.array([[cos(ty), 0., sin(ty)], [0., 1., 0.], [-sin(ty), 0., cos(ty)]])
rz = np.array([[cos(tz), -sin(ty), 0.], [sin(ty), cos(ty), 0.], [0., 0., 1.]])
scale = 200
p1 = np.array([0.5, 0.5, 1.])
p2 = np.array([-0.5, 0.5, 1.])
p3 = np.array([-0.5, -0.5, 1.])
p4 = np.array([0.5, -0.5, 1.])
p5 = np.array([0.5, 0.5, -2.])
p6 = np.array([-0.5, 0.5, -2.])
p7 = np.array([-0.5, -0.5, -2.])
p8 = np.array([0.5, -0.5, -2.])

def transform(tx, ty, tz):
    global rx, ry, rz, p1, p2, p3, p4, p5, p6, p7, p8
    rx = np.array([[1., 0., 0.], [0., cos(tx), -sin(tx)], [0., sin(tx), cos(tx)]])
    ry = np.array([[cos(ty), 0., sin(ty)], [0., 1., 0.], [-sin(ty), 0., cos(ty)]])
    rz = np.array([[cos(tz), -sin(ty), 0.], [sin(ty), cos(ty), 0.], [0., 0., 1.]])
    p1 = np.dot(rx, p1)
    p1 = np.dot(ry, p1)
    p1 = np.dot(rz, p1)
    p2 = np.dot(rx, p2)
    p2 = np.dot(ry, p2)
    p2 = np.dot(rz, p2)
    p3 = np.dot(rx, p3)
    p3 = np.dot(ry, p3)
    p3 = np.dot(rz, p3)
    p4 = np.dot(rx, p4)
    p4 = np.dot(ry, p4)
    p4 = np.dot(rz, p4)
    p5 = np.dot(rx, p5)
    p5 = np.dot(ry, p5)
    p5 = np.dot(rz, p5)
    p6 = np.dot(rx, p6)
    p6 = np.dot(ry, p6)
    p6 = np.dot(rz, p6)
    p7 = np.dot(rx, p7)
    p7 = np.dot(ry, p7)
    p7 = np.dot(rz, p7)
    p8 = np.dot(rx, p8)
    p8 = np.dot(ry, p8)
    p8 = np.dot(rz, p8)
while run:
    screen.fill((0, 0, 0))
    fps.tick(60)
    transform(tx, ty, tz)
    pygame.draw.circle(screen, colors["white"], dtod(p1, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p2, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p3, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p4, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p5, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p6, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p7, scale).point, 5)
    pygame.draw.circle(screen, colors["white"], dtod(p8, scale).point, 5)
    pygame.draw.line(screen, colors["white"], dtod(p1, scale).point, dtod(p2, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p2, scale).point, dtod(p3, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p3, scale).point, dtod(p4, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p4, scale).point, dtod(p1, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p5, scale).point, dtod(p6, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p6, scale).point, dtod(p7, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p7, scale).point, dtod(p8, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p8, scale).point, dtod(p5, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p1, scale).point, dtod(p5, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p6, scale).point, dtod(p2, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p7, scale).point, dtod(p3, scale).point)
    pygame.draw.line(screen, colors["white"], dtod(p8, scale).point, dtod(p4, scale).point)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                run = False

    pygame.display.update()
    pygame.quit()

enter image description here

The logic I used:

I m using orthographic projection technique, and to get a feel of perception, I m dividing the x and y coordinates by depth (z-component) before actually converting 3d->2d.

Thank you in advance

  • How did you generate this images? What do I have to do to rotate the cube in this way? – Rabbid76 Aug 20 '21 at 14:21
  • Related: [3D Projection in pygame](https://stackoverflow.com/questions/63944055/3d-projection-in-pygame/63944641#63944641), [How to rotate a square around x-axis in a 3D space](https://stackoverflow.com/questions/63651594/how-to-rotate-a-square-around-x-axis-in-a-3d-space/63654537#63654537), [Pygame rotating cubes around axis](https://stackoverflow.com/questions/56285017/pygame-rotating-cubes-around-axis/56286203#56286203) – Rabbid76 Aug 20 '21 at 14:24
  • @Rabbid76, The left one is the outcome of my experiment when I tried to convert orthographic to perspective. The right one is just downloaded from internet, so that I can tell in the question, what I want to achieve. – Samyak Marathe Aug 20 '21 at 14:31
  • @Rabbid76 I want to study 3d projection, how 3d graphics are rendered in 2d. Is there a good source for this like Khan academy or 3b1b YouTube Channel. I am an absolute beginner in that field. But have a good understanding of linear algebra and 2d graphs. Also I have created a lot of animations by programming so please recommend a good place which teach at advance level. – Samyak Marathe Aug 20 '21 at 14:37

1 Answers1

0

Finally I made that, oh my god I am so happy.

Use the following code, it works fine.

The class to convert 3d-coordinates -> 2d-coordinates:

class dtod(pygame.sprite.Sprite):
def __init__(self, point, scale):
    super().__init__()
    self.point = numpy.array(point)
    width, height = pygame.display.get_surface().get_size()
    self.angle = 2 * math.pi / 3
    self.length = 5
    self.projection = numpy.array(
        [[(self.length / math.tan(self.angle)) / (self.point[2] - (self.length / math.tan(self.angle))), 0., 0.],
         [0., (self.length / math.tan(self.angle)) / (self.point[2] - (self.length / math.tan(self.angle))), 0.]])
    self.point = numpy.dot(self.projection, self.point)
    self.point = ((self.point[0] * scale) + width / 2, height / 2 - (self.point[1] * scale))

The main code where I draw the cube:

import pygame
import numpy as np
from coord import dtod
import math

pygame.init()
screen = pygame.display.set_mode((0, 0))
run = True
width, height = screen.get_size()
colors = {"white": (255, 255, 255), "red": (255, 0, 0), "green": (150,253,55),"blue": (0, 227, 227),
      "orange": (255, 127, 39),
      "grey": (64, 64, 64), "yellow": (255, 240, 0)}
tx, ty, tz = 0., 0., 0.
fps = pygame.time.Clock()
x, y, z = 0, 0, 0


def cos(x):
    return math.cos(x)


def sin(x):
    return math.sin(x)


rotation_matrices = [np.array([[1., 0., 0.], [0., cos(tx), -sin(tx)], [0., 
sin(tx), cos(tx)]]), np.array([[cos(ty), 0., sin(ty)], [0., 1., 0.], [-sin(ty), 
0., cos(ty)]]), np.array([[cos(tz), -sin(ty), 0.], [sin(ty), cos(ty), 0.], [0., 
0., 1.]])]
scale = 200
points = [np.array([0.5, 0.5, 0.5]), np.array([-0.5, 0.5, 0.5]), np.array([-0.5, 
-0.5, 0.5]),
          np.array([0.5, -0.5, 0.5]), np.array([0.5, 0.5, -0.5]), 
np.array([-0.5, 0.5, -0.5]),
          np.array([-0.5, -0.5, -0.5]), np.array([0.5, -0.5, -0.5])]


def transform(tx, ty, tz):
    global points, rotation_matrices
    rotation_matrices = [np.array([[1., 0., 0.], [0., cos(tx), -sin(tx)], [0., 
sin(tx), cos(tx)]]),
                         np.array([[cos(ty), 0., sin(ty)], [0., 1., 0.], [- 
sin(ty), 0., cos(ty)]]),
                         np.array([[cos(tz), -sin(tz), 0.], [sin(tz), cos(tz), 
0.], [0., 0., 1.]])]
    for i in range(8):
        for j in range(3):
            points[i] = np.dot(rotation_matrices[j], points[i])


while run:
    screen.fill((0, 0, 0))
    fps.tick(60)
    transform(tx, ty, tz)
    for i in range(8):
        pygame.draw.circle(screen, colors["white"], dtod(points[i], 
    scale).point, 5)
    pygame.draw.line(screen, colors["white"], dtod(points[0], scale).point, 
    dtod(points[1], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[1], scale).point, 
    dtod(points[2], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[2], scale).point, 
    dtod(points[3], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[3], scale).point, 
    dtod(points[0], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[4], scale).point, 
    dtod(points[5], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[5], scale).point, 
    dtod(points[6], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[6], scale).point, 
    dtod(points[7], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[7], scale).point, 
    dtod(points[4], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[0], scale).point, 
    dtod(points[4], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[5], scale).point, 
    dtod(points[1], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[6], scale).point, 
    dtod(points[2], scale).point)
    pygame.draw.line(screen, colors["white"], dtod(points[7], scale).point, 
    dtod(points[3], scale).point)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                run = 0
            if event.key == pygame.K_x:
                x = 1
            if event.key == pygame.K_y:
                y = 1
            if event.key == pygame.K_z:
                z = 1
            if event.key == pygame.K_UP:
                if x == 1:
                    tx += math.pi / 180
                if y == 1:
                    ty += math.pi / 180
                if z == 1:
                    tz += math.pi / 180
            if event.key == pygame.K_DOWN:
                if x == 1:
                    tx -= math.pi / 180
                if y == 1:
                    ty -= math.pi / 180
                if z == 1:
                    tz -= math.pi / 180
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_x:
                x = 0
                tx = 0
            if event.key == pygame.K_y:
                y = 0
                ty = 0
            if event.key == pygame.K_z:
                z = 0
                tz = 0
    pygame.display.update()
    pygame.quit()

The problem was that I was applying parallax method but in the wrong way. I didn't consider the position of camera. Notice, now I m subtracting and multiplying some value from self.point[0] i.e. x component and same with y component. That value is the distance from camera, which can easily be proved by similar triangles.

enter image description here