近距离 3d 显示搞砸了 [英] Close range 3d display messed up

查看:33
本文介绍了近距离 3d 显示搞砸了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我复制了 YouTube 的一段代码,关于在屏幕上用 Python 显示 3d 立方体,而不使用外部模块(如 PyOpenGL).它工作正常,但是当你在两个立方体之间移动时,显示会变得一团糟.这是我的代码:

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),color=None,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)):如果颜色 != 无:如果 len(color) == 3 或 len(color) == 4:self.colors = tuple((i in range(6)))如果 len(color) == 6:self.colors = 颜色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]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)占用 = []立方体1 = 立方体((0,0,0))立方体2 = 立方体((0,0,2))对象 = [立方体 1,立方体 2]而真: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 靠近立方体时阻止立方体变形吗?

解决方案

应用程序无法正确绘制几何图形,当面的一部分(原始面,立方体的侧面)在后面而另一部分在眼睛前面时位置.如果转换后的 z 坐标 (vert_list += [(x,y,z)]) 对某些顶点为正,而对于形成图元(面)的其他一些顶点为负,则会发生这种情况.

如果您跳过所有面,其中至少有一个 z 坐标为负(在眼睛后面),您可以轻松测试该行为:

while True:# [...]对于对象中的 obj:# [...]对于范围内的 f(len(obj.faces)):face = obj.faces[f]#on_screen = 假#for i in face:# x,y = screen_coords[i]# if vert_list[i][2]>0 and x>0 and x0 and y0 for i in face])如果在屏幕上:# [...]

<小时>

这个问题可以通过在假设的近平面上裁剪几何来解决.请参阅

I copied a code of YouTube, about displaying 3d cubes on a screen in Python, without the use of external modules (like PyOpenGL). It works fine, but the moment you go between two cubes, the display gets messed up. Here is my code:

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),color=None,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)):
        if color != None:
            if len(color) == 3 or len(color) == 4:
                self.colors = tuple((color for i in range(6)))
            if len(color) == 6:
                self.colors = color
        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]


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)
occupied = []

cube1 = Cube((0,0,0))
cube2 = Cube((0,0,2))
objects = [cube1, cube2]

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]**2 for i in range(3)) for j in face) / len(face)]

    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)

And here is what happens when you try and navigate between the two cubes:

Can someone please suggest an edit to the code that would stop the cubes from becoming distorted when the cam.pos is close to the cubes?

解决方案

The application does not correctly draw the geometry, when apart of a faces (primitive, side of a cube) is behind and the other part in front of the eye position. That happens if the transformed z coordinate (vert_list += [(x,y,z)]) is positive for the some vertices and negative for negative for some other vertices that form primitive (face).

You can test that behavior with ease, if you skip all the faces, where at least one z coordinate is negative (behind the eye):

while True:
    # [...]

    for obj in objects:
        # [...]

        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

            # draw a face if any projected coordinate (x, y) is in the viewing volume 
            on_screen = False
            for i in face:
                x,y = screen_coords[i]
                if x>0 and x<w and y>0 and y<h: on_screen = True; break
            # skip a face if NOT ALL z coordinates are positive
            if on_screen:
                on_screen = all([vert_list[i][2]>0 for i in face])

            if on_screen:
                # [...]


The issue can be solved by clipping the geometry at hypothetical near plane. See Viewing frustum:

while True:
    # [...]

    for obj in objects:
        # [...]

        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

            # clip geometry at near plane
            if on_screen:
                near = 0.01
                for i in face:
                    if vert_list[i][2]<0:
                        x, y, z = vert_list[i]
                        nearscale = 200/near
                        x,y = x*nearscale,y*nearscale
                        screen_coords[i] = (cx+int(x),cy+int(y))

            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]**2 for i in range(3)) for j in face) / len(face)]

这篇关于近距离 3d 显示搞砸了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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