pyGame做3d吗?

egg*_*ert 23 python 3d pygame

我似乎无法在任何地方找到这个问题的答案.我意识到你必须使用pyOpenGL或类似的东西做openGL的东西,但我想知道它是否可以做非常基本的3D图形而没有任何其他依赖.

Kyl*_*tan 17

不,Pygame是SDL的包装器,它是2D api.Pygame不提供任何3D功能,可能永远不会.

Python的3D库包括Panda3DDirectPython,尽管它们使用起来可能非常复杂,尤其是后者.

  • 这有什么问题?Pygame不做3d.PyOpenGL做3d.重新阅读他的问题. (3认同)
  • 是的,我现在正在写一个3D游戏.我甚至没有使用OpenGL.我使用的方法就像Horst JENS发布的那样. (2认同)

Wes*_*s P 13

好吧,如果你可以做2d,你总是可以做3d.所有的3D都是倾斜的2维表面给人的印象,你正在看深度的东西.真正的问题是它可以做得好,你甚至想要.浏览pyGame文档一段时间后,看起来它只是一个SDL包装器.SDL不适用于3D编程,因此真正问题的答案是,不,我甚至都不会尝试.

  • 我试过了.事实上,我之前用C++编写了一个完整的OpenGL仿真.这是令人讨厌的,学习真正的3D API是一个更好的想法,直到你知道你正在做什么3D技术. (5认同)

小智 7

你可以只用pygame做伪3D游戏(比如"Doom"):

http://code.google.com/p/gh0stenstein/

如果你浏览pygame.org网站,你可能会发现更多使用python和pygame完成的"3d"游戏.

但是,如果你真的想进入3d编程,你应该研究OpenGl,Blender或任何其他真正的3D库.


Rab*_*d76 7

在没有其他依赖项帮助的情况下,Pygame 中的 3D 渲染很难实现,并且不会表现良好。Pygame 不提供任何绘制 3D 形状、网格、甚至透视和照明的功能。
如果你想用 Pygame 绘制 3D 场景,你需要使用向量算术计算顶点并使用多边形将几何图形缝合在一起。Pygame 绕轴旋转立方体
的答案示例:

这种方法不会给出令人满意的性能,仅对学习有价值。3D 场景是在 GPU 的帮助下生成的。仅使用 CPU 的方法无法达到所需的性能。

尽管如此,使用 2.5 维方法仍可以获得不错的结果。请参阅如何修复光线投射器中的墙壁变形?:

import pygame
import math

pygame.init()

tile_size, map_size = 50, 8
board = [
    '########',
    '#   #  #',
    '#   # ##',
    '#  ##  #',
    '#      #',
    '###  ###',
    '#      #',
    '########']

def cast_rays(sx, sy, angle):
    rx = math.cos(angle)
    ry = math.sin(angle)
    map_x = sx // tile_size
    map_y = sy // tile_size

    t_max_x = sx/tile_size - map_x
    if rx > 0:
        t_max_x = 1 - t_max_x
    t_max_y = sy/tile_size - map_y
    if ry > 0:
        t_max_y = 1 - t_max_y

    while True:
        if ry == 0 or t_max_x < t_max_y * abs(rx / ry):
            side = 'x'
            map_x += 1 if rx > 0 else -1
            t_max_x += 1
            if map_x < 0 or map_x >= map_size:
                break
        else:
            side = 'y'
            map_y += 1 if ry > 0 else -1
            t_max_y += 1
            if map_x < 0 or map_y >= map_size:
                break
        if board[int(map_y)][int(map_x)] == "#":
            break

    if side == 'x':
        x = (map_x + (1 if rx < 0 else 0)) * tile_size
        y = player_y + (x - player_x) * ry / rx
        direction = 'r' if x >= player_x else 'l'
    else:
        y = (map_y + (1 if ry < 0 else 0)) * tile_size
        x = player_x + (y - player_y) * rx / ry
        direction = 'd' if y >= player_y else 'u'
    return (x, y), math.hypot(x - sx, y - sy), direction   

def cast_fov(sx, sy, angle, fov, no_ofrays):
    max_d = math.tan(math.radians(fov/2))
    step = max_d * 2 / no_ofrays
    rays = []
    for i in range(no_ofrays):
        d = -max_d + (i + 0.5) * step
        ray_angle = math.atan2(d, 1)
        pos, dist, direction = cast_rays(sx, sy, angle + ray_angle)
        rays.append((pos, dist, dist * math.cos(ray_angle), direction))
    return rays

area_width = tile_size * map_size
window = pygame.display.set_mode((area_width*2, area_width))
clock = pygame.time.Clock()

board_surf = pygame.Surface((area_width, area_width))
for row in range(8):
    for col in range(8):
        color = (192, 192, 192) if board[row][col] == '#' else (96, 96, 96)
        pygame.draw.rect(board_surf, color, (col * tile_size, row * tile_size, tile_size - 2, tile_size - 2))

player_x, player_y = round(tile_size * 4.5) + 0.5, round(tile_size * 4.5) + 0.5
player_angle = 0
max_speed = 3
colors = {'r' : (196, 128, 64), 'l' : (128, 128, 64), 'd' : (128, 196, 64), 'u' : (64, 196, 64)}

run = True
while run:
    clock.tick(30)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False    
    
    keys = pygame.key.get_pressed()
    hit_pos_front, dist_front, side_front = cast_rays(player_x, player_y, player_angle)
    hit_pos_back, dist_back, side_back = cast_rays(player_x, player_y, player_angle + math.pi)
    player_angle += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 0.1
    speed = ((0 if dist_front <= max_speed else keys[pygame.K_UP]) - (0 if dist_back <= max_speed else keys[pygame.K_DOWN])) * max_speed
    player_x += math.cos(player_angle) * speed
    player_y += math.sin(player_angle) * speed
    rays = cast_fov(player_x, player_y, player_angle, 60, 40)

    window.blit(board_surf, (0, 0))
    for ray in rays:
        pygame.draw.line(window, (0, 255, 0), (player_x, player_y), ray[0])
    pygame.draw.line(window, (255, 0, 0), (player_x, player_y), hit_pos_front)
    pygame.draw.circle(window, (255, 0, 0), (player_x, player_y), 8)

    pygame.draw.rect(window, (128, 128, 255), (400, 0, 400, 200))
    pygame.draw.rect(window, (128, 128, 128), (400, 200, 400, 200))
    for i, ray in enumerate(rays):
        height = round(10000 / ray[2])
        width = area_width // len(rays)
        color = pygame.Color((0, 0, 0)).lerp(colors[ray[3]], min(height/256, 1))
        rect = pygame.Rect(area_width + i*width, area_width//2-height//2, width, height)
        pygame.draw.rect(window, color, rect)
    pygame.display.flip()

pygame.quit()
exit()
Run Code Online (Sandbox Code Playgroud)

另请参阅PyGameExamplesAndAnswers - 光线投射


我知道您问“...但我想知道是否可以在没有任何其他依赖项的情况下制作非常基本的 3D 图形。” 。不管怎样,我会给你一些带有其他依赖项的附加选项。

在 Python 中使 3D 场景更强大的一种方法是使用基于 OpenGL 的库,如 pygletModernGL

但是,您可以使用 Pygame 窗口创建OpenGL Contextpygame.OPENGL创建显示Surface时需要设置该标志(参见pygame.display.set_mode):

window = pg.display.set_mode((width, height), pygame.OPENGL | pygame.DOUBLEBUF)
Run Code Online (Sandbox Code Playgroud)

现代 OpenGL PyGame/PyOpenGL 示例:

import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL.shaders import *
import ctypes
import glm

glsl_vert = """
#version 330 core

layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec4 a_col;

out vec4 v_color;

uniform mat4 u_proj; 
uniform mat4 u_view; 
uniform mat4 u_model; 

void main()
{
    v_color     = a_col;
    gl_Position = u_proj * u_view * u_model * vec4(a_pos.xyz, 1.0);
}
"""

glsl_frag = """
#version 330 core

out vec4 frag_color;
in  vec4 v_color;

void main()
{
    frag_color = v_color;
}
"""

class Cube:
  
    def __init__(self):
        v = [(-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), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7)]
        surfaces = [(0,1,2,3), (5,4,7,6), (4,0,3,7),(1,5,6,2), (4,5,1,0), (3,2,6,7)]
        colors = [(1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (1,0.5,0)]
        line_color = [0, 0, 0]

        edge_attributes = []
        for e in edges:
            edge_attributes += v[e[0]]
            edge_attributes += line_color
            edge_attributes += v[e[1]]
            edge_attributes += line_color

        face_attributes = []
        for i, quad in enumerate(surfaces):
            for iv in quad:
                face_attributes += v[iv]
                face_attributes += colors[i]

        self.edge_vbo = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.edge_vbo)
        glBufferData(GL_ARRAY_BUFFER, (GLfloat * len(edge_attributes))(*edge_attributes), GL_STATIC_DRAW)
        self.edge_vao = glGenVertexArrays(1)
        glBindVertexArray(self.edge_vao)
        glVertexAttribPointer(0, 3, GL_FLOAT, False, 6*ctypes.sizeof(GLfloat), ctypes.c_void_p(0)) 
        glEnableVertexAttribArray(0) 
        glVertexAttribPointer(1, 3, GL_FLOAT, False, 6*ctypes.sizeof(GLfloat), ctypes.c_void_p(3*ctypes.sizeof(GLfloat))) 
        glEnableVertexAttribArray(1) 

        self.face_vbos = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.face_vbos)
        glBufferData(GL_ARRAY_BUFFER, (GLfloat * len(face_attributes))(*face_attributes), GL_STATIC_DRAW)
        self.face_vao = glGenVertexArrays(1)
        glBindVertexArray(self.face_vao)
        glVertexAttribPointer(0, 3, GL_FLOAT, False, 6*ctypes.sizeof(GLfloat), ctypes.c_void_p(0)) 
        glEnableVertexAttribArray(0) 
        glVertexAttribPointer(1, 3, GL_FLOAT, False, 6*ctypes.sizeof(GLfloat), ctypes.c_void_p(3*ctypes.sizeof(GLfloat))) 
        glEnableVertexAttribArray(1) 

    def draw(self):
        glEnable(GL_DEPTH_TEST)

        glLineWidth(5)

        glBindVertexArray(self.edge_vao)
        glDrawArrays(GL_LINES, 0, 12*2)
        glBindVertexArray(0)

        glEnable(GL_POLYGON_OFFSET_FILL)
        glPolygonOffset( 1.0, 1.0 )

        glBindVertexArray(self.face_vao)
        glDrawArrays(GL_QUADS, 0, 6*4)
        glBindVertexArray(0)
        
        glDisable(GL_POLYGON_OFFSET_FILL)

def set_projection(w, h):
    return glm.perspective(glm.radians(45), w / h, 0.1, 50.0)

pygame.init()
window = pygame.display.set_mode((400, 300), pygame.DOUBLEBUF | pygame.OPENGL | pygame.RESIZABLE)
clock = pygame.time.Clock()

proj = set_projection(*window.get_size())
view = glm.lookAt(glm.vec3(0, 0, 5), glm.vec3(0, 0, 0), glm.vec3(0, 1, 0))
model = glm.mat4(1)
cube = Cube()
angle_x, angle_y = 0, 0

program = compileProgram( 
    compileShader(glsl_vert, GL_VERTEX_SHADER),
    compileShader(glsl_frag, GL_FRAGMENT_SHADER))
attrib = { a : glGetAttribLocation(program, a) for a in ['a_pos', 'a_col'] }
print(attrib)
uniform = { u : glGetUniformLocation(program, u) for u in ['u_model', 'u_view', 'u_proj'] }
print(uniform)
glUseProgram(program)

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.VIDEORESIZE:
            glViewport(0, 0, event.w, event.h)
            proj = set_projection(event.w, event.h)

    model = glm.mat4(1)
    model = glm.rotate(model, glm.radians(angle_y), glm.vec3(0, 1, 0))
    model = glm.rotate(model, glm.radians(angle_x), glm.vec3(1, 0, 0))
   
    glUniformMatrix4fv(uniform['u_proj'], 1, GL_FALSE, glm.value_ptr(proj))
    glUniformMatrix4fv(uniform['u_view'], 1, GL_FALSE, glm.value_ptr(view))
    glUniformMatrix4fv(uniform['u_model'], 1, GL_FALSE, glm.value_ptr(model))

    angle_x += 1
    angle_y += 0.4

    glClearColor(0.5, 0.5, 0.5, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    cube.draw()    
    pygame.display.flip()

pygame.quit()
exit()
Run Code Online (Sandbox Code Playgroud)

旧版 OpenGL PyGame/PyOpenGL 示例:

import pygame
from OpenGL.GL import *
from OpenGL.GLU import *

class Cube:
  
    def __init__(self):
        self.v = [(-1,-1,-1), ( 1,-1,-1), ( 1, 1,-1), (-1, 1,-1), (-1,-1, 1), ( 1,-1, 1), ( 1, 1, 1), (-1, 1, 1)]
        self.edges = [(0,1), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7)]
        self.surfaces = [(0,1,2,3), (5,4,7,6), (4,0,3,7),(1,5,6,2), (4,5,1,0), (3,2,6,7)]
        self.colors = [(1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (1,0.5,0)]

    def draw(self):
        glEnable(GL_DEPTH_TEST)

        glLineWidth(5)
        glColor3fv((0, 0, 0))
        glBegin(GL_LINES)
        for e in self.edges:
            glVertex3fv(self.v[e[0]])
            glVertex3fv(self.v[e[1]])
        glEnd()

        glEnable(GL_POLYGON_OFFSET_FILL)
        glPolygonOffset( 1.0, 1.0 )

        glBegin(GL_QUADS)
        for i, quad in enumerate(self.surfaces):
            glColor3fv(self.colors[i])
            for iv in quad:
                glVertex3fv(self.v[iv])
        glEnd()

        glDisable(GL_POLYGON_OFFSET_FILL)

def set_projection(w, h):
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, w / h, 0.1, 50.0)
    glMatrixMode(GL_MODELVIEW)

def screenshot(display_surface, filename):
    size = display_surface.get_size()
    buffer = glReadPixels(0, 0, *size, GL_RGBA, GL_UNSIGNED_BYTE)
    screen_surf = pygame.image.fromstring(buffer, size, "RGBA")
    pygame.image.save(screen_surf, filename)

pygame.init()
window = pygame.display.set_mode((400, 300), pygame.DOUBLEBUF | pygame.OPENGL | pygame.RESIZABLE)
clock = pygame.time.Clock()

set_projection(*window.get_size())
cube = Cube()
angle_x, angle_y = 0, 0

run = True
while run:
    clock.tick(60)
    take_screenshot = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.VIDEORESIZE:
            glViewport(0, 0, event.w, event.h)
            set_projection(event.w, event.h)
        elif event.type == pygame.KEYDOWN:
            take_screenshot = True

    glLoadIdentity()
    glTranslatef(0, 0, -5)
    glRotatef(angle_y, 0, 1, 0)
    glRotatef(angle_x, 1, 0, 0)
    angle_x += 1
    angle_y += 0.4

    glClearColor(0.5, 0.5, 0.5, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    cube.draw()   
    if take_screenshot: 
        screenshot(window, "cube.png")
    pygame.display.flip()
    
pygame.quit()
exit()
Run Code Online (Sandbox Code Playgroud)


Dou*_*ner 6

Pygame 最初并不是用来做 3D 的,但有一种方法可以用任何 2D 图形库来做 3D。您所需要的只是以下函数,它将 3d 点转换为 2d 点,这样您只需在屏幕上画线即可制作任何 3d 形状。

def convert_to_2d(point=[0,0,0]):
    return [point[0]*(point[2]*.3),point[1]*(point[2]*.3)]
Run Code Online (Sandbox Code Playgroud)

这称为伪 3d 或 2.5d。这是可以做到的,但可能会很慢,而且非常难做到,所以建议您使用适用于 3d 的库。