近距离3D显示混乱 [英] Close range 3d display messed up

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

问题描述

我复制了YouTube的代码,该代码在不使用外部模块(例如PyOpenGL)的情况下,使用Python在屏幕上显示3d多维数据集.它可以正常工作,但是当您在两个立方体之间移动时,显示会变得混乱.这是我的代码:

 导入pygame,sys,数学,随机def rotation2d(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 =列表(pos)self.rot =列表(腐烂)def事件(自身,事件):如果event.type == pygame.MOUSEMOTION:x,y = event.rel;x/= 200;y/= 200self.rot [0] + = y;self.rot [1] + = xdef更新(self,dt,key):s = dt * 10如果key [pygame.K_q]:self.pos [1] + = s如果key [pygame.K_e]:self.pos [1]-= sx,y = s * math.sin(self.rot [1]),s * math.cos(self.rot [1])如果key [pygame.K_w]:self.pos [0] + = x;self.pos [2] + = y如果key [pygame.K_s]:self.pos [0]-= x;self.pos [2]-= y如果key [pygame.K_a]:self.pos [0]-= y;self.pos [2] + = x如果key [pygame.K_d]:self.pos [0] + = y;self.pos [2]-= x如果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 =元组((i在range(6)中的颜色))如果len(color)== 6:self.colors =颜色self.vertices =(v0,v1,v2,v3,v4,v5,v6,v7)x,y,z = posself.verts = [self.vertices中的X,Y,Z的((x + X/2,y + Y/2,z + Z/2)]pygame.init()w,h = 400,400;cx,cy = w//2,h//2屏幕= pygame.display.set_mode((w,h))时钟= pygame.time.Clock()cam =凸轮((0,0,-5))pygame.event.get();pygame.mouse.get_rel()pygame.mouse.set_visible(0);pygame.event.set_grab(1)已占领= []cube1 =多维数据集((0,0,0))cube2 =多维数据集((0,0,2))对象= [多维数据集1,多维数据集2]而True:dt = clock.tick()/1000对于pygame.event.get()中的事件:如果event.type == pygame.QUIT:pygame.quit();sys.exit()如果event.type == pygame.KEYDOWN:如果event.key == pygame.K_ESCAPE:pygame.quit();sys.exit()cam.events(事件)screen.fill((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 = rotation2d((x,z),cam.rot [1])y,z = rotation2d((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)):脸= obj.faces [f]on_screen = False对于我的脸:x,y = screen_coords [i]如果vert_list [i] [2]> 0且x> 0并且x <w且y> 0且y <h:on_screen = True;休息如果在屏幕上:coords = [screen_coords [i] for i in face]face_list + = [坐标]face_color + = [obj.colors [f]]深度+ = [sum(sum(vert_list [j] [i] ** 2表示范围(3)中的i),j表示脸部j)/len(face)]顺序=已排序(范围(len(face_list)),key = lambda i:depth [i],reverse = 1)对我来说:尝试:pygame.draw.polygon(screen,face_color [i],face_list [i])除了:通过键= pygame.key.get_pressed()pygame.display.flip()cam.update(dt,key) 

当您尝试在两个多维数据集之间导航时,会发生以下情况:

有人可以建议对代码进行编辑,以防止cam.pos靠近立方体时阻止立方体变形吗?

解决方案

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

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

 为True时:#[...]对于对象中的obj:#[...]对于范围内的f(len(obj.faces)):脸= obj.faces [f]#on_screen =假#面对我#x,y = screen_coords [i]#如果vert_list [i] [2]> 0且x> 0且x <w且y> 0且y <h:on_screen = True;休息#如果查看体积中有任何投影坐标(x,y),则绘制人脸on_screen = False对于我的脸:x,y = screen_coords [i]如果x> 0且x <w且y> 0且y <h:on_screen = True;休息#如果不是所有z坐标都为正,则跳过脸如果在屏幕上:on_screen = all([面对我的i,[vert_list [i] [2]> 2]>)如果在屏幕上:#[...] 


可以通过在假设的近平面处修剪几何来解决此问题.请参见

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天全站免登陆