2014-01-09 75 views
0

如何使用pygame創建和渲染3D形狀,而不使用任何其他模塊。我想創建自己的簡單3D引擎。我可以繪製一個3D盒子,但不知道如何調整線條的長度和位置,以便在旋轉盒子時提供3D效果。如何使用pygame繪製3D形狀(無其他模塊)

我很難理解在陰影中physics,深度知覺和照明旋轉對象

說,當我有一個框:

class box1(): 
    x=100 
    y=100 
    z=100 

    size = 150 #length for distance between each point 

    point1 = 0,0,0 # top left front 
    point2 = 0,0,0 # top right front 
    point3 = 0,0,0 # bottom left front 
    point4 = 0,0,0 # bottom right front 
    point5 = 0,0,0 # top left back 
    point6 = 0,0,0 # top right back 
    point7 = 0,0,0 # bottom left back 
    point8 = 0,0,0 # bottom right back 



def set_points(): 
    x=box1.x 
    y=box1.y 
    z=box1.z 

    size = box1.size 

    #this part sets all the points x,y,x co-cords at the correct locations 
    # _____ 4____6 
    # |\____\ 1____2 
    # | | | Middle [x,y,z] 
    # |_| ` | 7____8 
    # \|____| 3____4 
    # 
    # the +50 is just a test to show the 'offset' of the behind points 
    box1.point1 = [x-(size/2),y-(size/2),z-(size/2)] # top left front 
    box1.point2 = [x+(size/2),y-(size/2),z-(size/2)] # top right front 
    box1.point3 = [x-(size/2),y+(size/2),z-(size/2)] # bottom left front 
    box1.point4 = [x+(size/2),y+(size/2),z-(size/2)] # bottom right front 
    box1.point5 = [x-(size/2)+50,y-(size/2)+50,z+(size/2)] # top left back 
    box1.point6 = [x+(size/2)+50,y-(size/2)+50,z+(size/2)] # top right back 
    box1.point7 = [x-(size/2)+50,y+(size/2)+50,z+(size/2)] # bottom left back 
    box1.point8 = [x+(size/2)+50,y+(size/2)+50,z+(size/2)] # bottom right back 


camara_pos = [20,20,20] # I don't know how to make the points based off this 
camara_angle = [45,0,0] # or this 



while True: 
    set_points() 
    g.DISPLAYSURF.fill((0,0,0)) 

    for event in pygame.event.get(): 
     if event.type == QUIT: 
      exit() 

    #draws all the lines connecting all the points . 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point1[0],box1.point1[1]),(box1.point2[0],box1.point2[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point3[0],box1.point3[1]),(box1.point4[0],box1.point4[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point2[0],box1.point2[1]),(box1.point4[0],box1.point4[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point1[0],box1.point1[1]),(box1.point3[0],box1.point3[1])) 

    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point5[0],box1.point5[1]),(box1.point6[0],box1.point6[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point7[0],box1.point7[1]),(box1.point8[0],box1.point8[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point6[0],box1.point6[1]),(box1.point8[0],box1.point8[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point5[0],box1.point5[1]),(box1.point7[0],box1.point7[1])) 

    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point1[0],box1.point1[1]),(box1.point5[0],box1.point5[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point2[0],box1.point2[1]),(box1.point6[0],box1.point6[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point3[0],box1.point3[1]),(box1.point7[0],box1.point7[1])) 
    pygame.draw.line(g.DISPLAYSURF, (128,128,128), (box1.point4[0],box1.point4[1]),(box1.point8[0],box1.point8[1])) 

    pygame.display.update() 

advanced-diagram

任何人都可以請解釋理論? 任何人都可以請給我看一些代碼來做點的數學嗎?

+0

這是一個開始http://rodrigo-silveira.com/3d-programming-transformation-matrix-tutorial/#。Us7uZoXgH4s和http://3dgep.com/?p=1700 – ninMonkey

+0

如果你不知道trig,你還有很長的路要走。 – PygameNerd

+0

我知道trig:D – p99will

回答

0

有很多的例子在那裏:

http://codentronix.com/2011/04/21/rotating-3d-wireframe-cube-with-python/

http://www.pygame.org/pcr/3d_wireframe/index.php

http://www.petercollingridge.co.uk/book/export/html/460

首先,你必須知道的OpenGL基於RHS(右手法則):

Is the OpenGL Coordinate System right-handed or left-handed?

http://www.ntu.edu.sg/home/ehchua/programming/opengl/CG_BasicsTheory.html

所以Z軸應該向你指着(有一些上面的URL鏈接之間的聯繫和公式的這種對比)。因此,假設Z軸指向左下側,則Z軸在XY平面上的投影將意味着以下(原始3D座標是orig_X,orig_Y,orig_Z,θ是Z的角度)軸指向左邊,相對於X軸):

X = orig_X - orig_Z * COS(THETA)

Y = orig_Y - orig_Z * SIN(THETA)

希望你能理解爲什麼orig_Z前面的負號出現了。

1

您需要知道的唯一魔法叫做旋轉矩陣

如果你在這樣一個矩陣和一個向量之間進行乘法運算,你會得到這個向量的旋轉。

有了這些信息(即複製後維基百科的3D旋轉矩陣),我結束了這個漂亮的東西:

import pygame 
from numpy import array 
from math import cos, sin 


###################### 
#     # 
# math section # 
#     # 
###################### 

X, Y, Z = 0, 1, 2 


def rotation_matrix(α, β, γ): 
    """ 
    rotation matrix of α, β, γ radians around x, y, z axes (respectively) 
    """ 
    sα, cα = sin(α), cos(α) 
    sβ, cβ = sin(β), cos(β) 
    sγ, cγ = sin(γ), cos(γ) 
    return (
     (cβ*cγ, -cβ*sγ, sβ), 
     (cα*sγ + sα*sβ*cγ, cα*cγ - sγ*sα*sβ, -cβ*sα), 
     (sγ*sα - cα*sβ*cγ, cα*sγ*sβ + sα*cγ, cα*cβ) 
    ) 


class Physical: 
    def __init__(self, vertices, edges): 
     """ 
     a 3D object that can rotate around the three axes 
     :param vertices: a tuple of points (each has 3 coordinates) 
     :param edges: a tuple of pairs (each pair is a set containing 2 vertices' indexes) 
     """ 
     self.__vertices = array(vertices) 
     self.__edges = tuple(edges) 
     self.__rotation = [0, 0, 0] # radians around each axis 

    def rotate(self, axis, θ): 
     self.__rotation[axis] += θ 

    @property 
    def lines(self): 
     location = self.__vertices.dot(rotation_matrix(*self.__rotation)) # an index->location mapping 
     return ((location[v1], location[v2]) for v1, v2 in self.__edges) 


###################### 
#     # 
# gui section  # 
#     # 
###################### 


BLACK, RED = (0, 0, 0), (255, 128, 128) 


class Paint: 
    def __init__(self, shape, keys_handler): 
     self.__shape = shape 
     self.__keys_handler = keys_handler 
     self.__size = 450, 450 
     self.__clock = pygame.time.Clock() 
     self.__screen = pygame.display.set_mode(self.__size) 
     self.__mainloop() 

    def __fit(self, vec): 
     """ 
     ignore the z-element (creating a very cheap projection), and scale x, y to the coordinates of the screen 
     """ 
     # notice that len(self.__size) is 2, hence zip(vec, self.__size) ignores the vector's last coordinate 
     return [round(70 * coordinate + frame/2) for coordinate, frame in zip(vec, self.__size)] 

    def __handle_events(self): 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       exit() 
     self.__keys_handler(pygame.key.get_pressed()) 

    def __draw_shape(self, thickness=4): 
     for start, end in self.__shape.lines: 
      pygame.draw.line(self.__screen, RED, self.__fit(start), self.__fit(end), thickness) 

    def __mainloop(self): 
     while True: 
      self.__handle_events() 
      self.__screen.fill(BLACK) 
      self.__draw_shape() 
      pygame.display.flip() 
      self.__clock.tick(40) 


###################### 
#     # 
#  main start  # 
#     # 
###################### 


def main(): 
    from pygame import K_q, K_w, K_a, K_s, K_z, K_x 

    cube = Physical( # 0   1   2   3   4   5   6   7 
     vertices=((1, 1, 1), (1, 1, -1), (1, -1, 1), (1, -1, -1), (-1, 1, 1), (-1, 1, -1), (-1, -1, 1), (-1, -1, -1)), 
     edges=({0, 1}, {0, 2}, {2, 3}, {1, 3}, 
       {4, 5}, {4, 6}, {6, 7}, {5, 7}, 
       {0, 4}, {1, 5}, {2, 6}, {3, 7}) 
    ) 

    counter_clockwise = 0.05 # radians 
    clockwise = -counter_clockwise 

    params = { 
     K_q: (X, clockwise), 
     K_w: (X, counter_clockwise), 
     K_a: (Y, clockwise), 
     K_s: (Y, counter_clockwise), 
     K_z: (Z, clockwise), 
     K_x: (Z, counter_clockwise), 
    } 

    def keys_handler(keys): 
     for key in params: 
      if keys[key]: 
       cube.rotate(*params[key]) 

    pygame.init() 
    pygame.display.set_caption('Control - q,w : X a,s : Y z,x : Z') 
    Paint(cube, keys_handler) 

if __name__ == '__main__': 
    main() 

注意,我沒有使用矩陣乘法模塊與NumPy(並用數學的三角函數);我認爲,「沒有其他模塊」是指「沒有任何3D庫」。 無論如何,你可以實現自己的矩陣乘法函數並使用泰勒級數計算sin \ cos,但這是非常不必要的。