3D 图形的深度问题 [英] Depth issue with 3D graphics

查看:26
本文介绍了3D 图形的深度问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近从 YouTube 上复制了一个代码,关于 Python 中的 3d 图形,而不使用 3D 模块,如 OpenGL.该视频仅适用于立方体,所以我尝试在形状中添加一个四面体,但事情开始出现在它们不应该出现的地方.代码如下:

import pygame, sys, math, randomdef rotate2d(pos, rad): x,y=pos;s,c = math.sin(rad),math.cos(rad);返回 x*c-y*s,y*c+x*s类凸轮:def __init__(self, pos=(0,0,0),rot=(0,0)):self.pos = list(pos)self.rot = 列表(腐烂)定义事件(自我,事件):如果 event.type == pygame.MOUSEMOTION:x, y = event.rel;x/=200;y/=200self.rot[0]+=y;self.rot[1]+=xdef 更新(self, dt, key):s = dt*10if key[pygame.K_q]: self.pos[1]+=sif key[pygame.K_e]: self.pos[1]-=sx,y = s*math.sin(self.rot[1]),s*math.cos(self.rot[1])if key[pygame.K_w]: self.pos[0]+=x;self.pos[2]+=yif key[pygame.K_s]: self.pos[0]-=x;self.pos[2]-=yif key[pygame.K_a]: self.pos[0]-=y;self.pos[2]+=xif key[pygame.K_d]: self.pos[0]+=y;self.pos[2]-=xif key[pygame.K_r]: self.pos[0]=0;self.pos[1]=0;self.pos[2]=-5;self.rot[0]=0;self.rot[1]=0类立方体:面 = (0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)颜色 = (255,0,0),(255,128,0),(255,255,0),(255,255,255),(0,0,255),(0,255,0)def __init__(self,pos=(0,0,0),v0=(-1,-1,-1),v1=(1,-1,-1),v2=(1,1,-1),v3=(-1,1,-1),v4=(-1,-1,1),v5=(1,-1,1),v6=(1,1,1),v7=(-1,1,1)):self.vertices = (v0,v1,v2,v3,v4,v5,v6,v7)x,y,z = 位置self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]四面体类:人脸 = (1,2,3),(0,1,2),(0,1,3),(0,2,3)颜色 = (255,0,0),(255,128,0),(255,255,0),(255,255,255)def __init__(self,pos=(0,0,0),v0=(0,0,.866),v1=(-.866,-1,-1),v2=(-.866,1,-1),v3=(.866,0,-1)):self.vertices = (v0,v1,v2,v3)x,y,z = 位置self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]pygame.init()w,h = 400,400;cx,cy = w//2, h//2屏幕 = pygame.display.set_mode((w,h))时钟 = pygame.time.Clock()凸轮 = 凸轮((0,0,-5))pygame.event.get();pygame.mouse.get_rel()pygame.mouse.set_visible(0);pygame.event.set_grab(1)对象 = [Cube((0,0,0)),Cube((0,0,2)),Tetrahedron((0,0,1))]而真:dt = 时钟.tick()/1000对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT: pygame.quit();系统退出()如果 event.type == pygame.KEYDOWN:如果 event.key == pygame.K_ESCAPE: pygame.quit();系统退出()cam.events(事件)屏幕填充((0,0,0))face_list = [];face_color = [];深度 = []对于对象中的 obj:vert_list = [];screen_coords = []对于 obj.verts 中的 x,y,z:x-=cam.pos[0];y-=cam.pos[1];z-=cam.pos[2]x,z=rotate2d((x,z),cam.rot[1])y,z = rotate2d((y,z),cam.rot[0])vert_list += [(x,y,z)]f = 200/zx,y = x*f,y*fscreen_coords+=[(cx+int(x),cy+int(y))]对于范围内的 f(len(obj.faces)):face = obj.faces[f]on_screen = 假面对我:x,y = screen_coords[i]如果 vert_list[i][2]>0 and x>0 and x0 and y

我的问题是,当 cam.pos 离物体至少 7.5 距离时,四面体部分会通过两边的立方体显示出来:

如您所见,四面体应该被埋在立方体下,但它仍然出现.有人可以建议对代码进行编辑以阻止四面体通过任何对象显示吗?

解决方案

您实际尝试做的是按

但请注意,如果人脸靠近另一个,仍然可能会出现问题(例如,在同一平面上覆盖人脸).这不是一个精确的算法.它不是为触摸"对象而设计的.

I copied a code off YouTube recently, about 3d graphics in Python without the use of 3D modules, like OpenGL. The video worked with only cubes, so I tried adding a tetrahedron to the shapes, and things started popping up where they weren't supposed to. The code is as follows:

import pygame, sys, math, random

def rotate2d(pos, rad): x,y=pos; s,c = math.sin(rad),math.cos(rad); return x*c-y*s,y*c+x*s

class Cam:
    def __init__(self, pos=(0,0,0),rot=(0,0)):
        self.pos = list(pos)
        self.rot = list(rot)

    def events(self, event):
        if event.type == pygame.MOUSEMOTION:
            x, y = event.rel; x/=200; y/=200
            self.rot[0]+=y; self.rot[1]+=x

    def update(self, dt, key):
        s = dt*10

        if key[pygame.K_q]: self.pos[1]+=s
        if key[pygame.K_e]: self.pos[1]-=s

        x,y = s*math.sin(self.rot[1]),s*math.cos(self.rot[1])
        if key[pygame.K_w]: self.pos[0]+=x; self.pos[2]+=y
        if key[pygame.K_s]: self.pos[0]-=x; self.pos[2]-=y
        if key[pygame.K_a]: self.pos[0]-=y; self.pos[2]+=x
        if key[pygame.K_d]: self.pos[0]+=y; self.pos[2]-=x
        if key[pygame.K_r]: self.pos[0]=0; self.pos[1]=0;
                                         self.pos[2]=-5; self.rot[0]=0; self.rot[1]=0


class Cube:
    faces = (0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)
    colors = (255,0,0),(255,128,0),(255,255,0),(255,255,255),(0,0,255),(0,255,0)
    def __init__(self,pos=(0,0,0),v0=(-1,-1,-1),v1=(1,-1,-1),v2=(1,1,-1),v3=(-1,1,-1),v4=(-1,-1,1),v5=(1,-1,1),v6=(1,1,1),v7=(-1,1,1)):
        self.vertices = (v0,v1,v2,v3,v4,v5,v6,v7)
        x,y,z = pos
        self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]

class Tetrahedron:
    faces = (1,2,3),(0,1,2),(0,1,3),(0,2,3)
    colors = (255,0,0),(255,128,0),(255,255,0),(255,255,255)
    def __init__(self,pos=(0,0,0),v0=(0,0,.866),v1=(-.866,-1,-1),v2=(-.866,1,-1),v3=(.866,0,-1)):
        self.vertices = (v0,v1,v2,v3)
        x,y,z = pos
        self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]

pygame.init()
w,h = 400,400; cx,cy = w//2, h//2
screen = pygame.display.set_mode((w,h))
clock = pygame.time.Clock()
cam = Cam((0,0,-5))
pygame.event.get(); pygame.mouse.get_rel()
pygame.mouse.set_visible(0); pygame.event.set_grab(1)

objects = [Cube((0,0,0)),Cube((0,0,2)),Tetrahedron((0,0,1))]

while True:
    dt = clock.tick()/1000

    for event in pygame.event.get():
        if event.type == pygame.QUIT: pygame.quit(); sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE: pygame.quit(); sys.exit()
        cam.events(event)

    screen.fill((0,0,0))

    face_list = []; face_color = []; depth = []

    for obj in objects:

        vert_list = []; screen_coords = []
        for x,y,z in obj.verts:
            x-=cam.pos[0]; y-=cam.pos[1];z-=cam.pos[2]
            x,z=rotate2d((x,z),cam.rot[1])
            y,z = rotate2d((y,z),cam.rot[0])
            vert_list += [(x,y,z)]

            f = 200/z
            x,y = x*f,y*f
            screen_coords+=[(cx+int(x),cy+int(y))]

        for f in range(len(obj.faces)):
            face = obj.faces[f]

            on_screen = False
            for i in face:
                x,y = screen_coords[i]
                if vert_list[i][2]>0 and x>0 and x<w and y>0 and y<h: on_screen = True; break

            if on_screen:
                coords = [screen_coords[i] for i in face]
                face_list += [coords]
                face_color += [obj.colors[f]]
                depth += [sum(sum(vert_list[j][i] for j in face)**2 for i in range(3))]

    order = sorted(range(len(face_list)),key=lambda i:depth[i],reverse=1)

    for i in order:
        try: pygame.draw.polygon(screen,face_color[i],face_list[i])
        except: pass

    key = pygame.key.get_pressed()
    pygame.display.flip()
    cam.update(dt,key)

My problem is that the tetrahedron part is showing up through the cubes on either side, when the cam.pos is at least 7.5 away from the object:

As you can see, the tetrahedron is supposed to be buried under the cube, but it's still showing up. Can someone please suggest an edit to the code which will stop the tetrahedron from showing up through any objects?

解决方案

What you actually try to do is to sort the faces by the square of the Euclidean distance to the vertices of the face.

But sum(vert_list[j][i] for j in face)**2 does not compute the square of the euclidean distance. It computes the square of the sum of the components of a coordinate. The square of the euclidean distance is

vert_list[j][0]**2 + vert_list[j][1]**2 + vert_list[j][2]**2 

That is not the same as

(vert_list[j][0] + vert_list[j][1] + vert_list[j][2])**2

Furthermore you do not take into account the number of vertices of a face, because the squared distances are summed.
Note, the faces of the cube are quads and have 4 vertices, but the faces of the tetrahedron are triangles and have 3 vertices.

Correct the computation of the squared distance and divide by the number of vertices, to solve the issue:

depth += [sum(sum(vert_list[j][i] for j in face)**2 for i in range(3))]

depth += [sum(sum(vert_list[j][i]**2 for i in range(3)) for j in face) / len(face)]

But note, if faces are close to another there still may occur an issue (e.g. covering faces in the same plane). That is not an exact algorithm. It is not designed for "touching" objects.

这篇关于3D 图形的深度问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆