如何使用pygame创建和渲染3D形状,而不使用任何其他模块。我想创建自己的简单3D引擎。我可以画一个3D盒子,只是不知道如何调整线条的长度和位置,以便在旋转盒子时产生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()
有人可以解释这个理论吗? 有人可以给我看一些代码来计算积分吗?
答案 0 :(得分: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进行矩阵乘法(并使用数学作为trig);我认为“没有其他模块”你的意思是“没有任何3D库”。 无论如何,你可以实现自己的矩阵乘法函数并使用泰勒级数计算sin \ cos,但这是非常不必要的。
答案 1 :(得分: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前面的负号出现了。