【问题标题】:How can I draw a 3D shape using pygame (no other modules)如何使用 pygame 绘制 3D 形状(没有其他模块)
【发布时间】:2014-01-09 11:51:55
【问题描述】:

如何在不使用任何其他模块的情况下使用 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()

谁能解释一下这个理论? 谁能给我看一些代码来计算分数?

【问题讨论】:

标签: python 3d pygame theory shape


【解决方案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,但这完全没有必要。

【讨论】:

    【解决方案2】:

    有很多例子:

    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,theta是Z轴的角度, 指向左边,相对于 X 轴):

    X = orig_X - orig_Z * cos(theta)

    Y = orig_Y - orig_Z * sin(theta)

    希望你能理解为什么会出现 orig_Z 前面的负号。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多