微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

靠近相机时 3D 对象出现故障

如何解决靠近相机时 3D 对象出现故障

问题

我正在使用 Python 模块 Turtle 制作 3D 引擎。它现在可以“正常”工作,但存在一些错误(没有相机旋转,面部未按正确顺序渲染,投影看起来不正确)。 主要错误是,当我越过相机 y 的某个点(朝向和远离物体)时,我位于物体之间,它会导致一切在视觉上破裂,直到我经过它并且即使我仍在 y 轴上向前移动,物体也开始移动(它们也被翻转了)。 我不知道这是我的编码还是我没有实现一些数学,但这里有一个 gif 显示了什么是正在发生:

Problem

代码

这是我正在使用的代码。第一个是演示,第二个是引擎。

demo.py

import Engine3D as e3d
import time

camera = e3d.Camera(0,0)
screen = camera.screen
cursor = camera.cursor

screen.title("Demo")
screen.screensize(400,400)
screen.bgcolor("grey")

cursor.pensize(5)

if __name__ == "__main__":
    size = 100

    cube0_center = e3d.Point3D(100,220,100)
    cube0 = e3d.Cube(cube0_center,size,"None")
    cube1_center = e3d.Point3D(-100,100)
    cube1 = e3d.Cube(cube1_center,"Red")
    cube2_center = e3d.Point3D(100,-100)
    cube2 = e3d.Cube(cube2_center,["Red","Green","Yellow","Blue","White","Purple"])
    pyramid0_center = e3d.Point3D(-100,-100)
    pyramid0 = e3d.Pyramid(pyramid0_center,["None","None","Green"])

    objects = [cube0,cube1,cube2,pyramid0]
    keys = []

    screen.getcanvas().winfo_toplevel().bind("<KeyPress>",lambda x: (keys.append(str(x.keysym).lower()) if not str(x.keysym).lower() in keys else print("",end="")))
    screen.getcanvas().winfo_toplevel().bind("<keyrelease>",lambda x: (keys.remove(str(x.keysym).lower()) if str(x.keysym).lower() in keys else print("",end="")))

    while True:
        e3d.render(objects,camera)

        cube0.rotate(-0.01,0.01)
        cube1.rotate(-0.01,-0.01)
        cube2.rotate(0.01,0.01)
        pyramid0.rotate(0.01,-0.01)

        if "w" in keys: camera.move(0,2.5)
        if "s" in keys: camera.move(0,-2.5)
        if "d" in keys: camera.move(2.5,0)
        if "a" in keys: camera.move(-2.5,0)
        if "shift_l" in keys: camera.move(0,2.5,0)
        if "control_l" in keys: camera.move(0,-2.5,0)

        time.sleep(1 / 60)

Engine3D.py

import turtle
import math

class Point3D:
    def __init__(self,x,y,z):
        self.x = float(x)
        self.y = float(y)
        self.z = float(z)

    def combine(self,z):
        self.x += x
        self.y += y
        self.z += z
        
        return self

class Point2D:
    def __init__(self,y):
        self.x = float(x)
        self.y = float(y)

    def combine(self,y):
        self.x += x
        self.y += y
        
        return self

class Shape():
    def __init__(self,center,side,color=None):
        self.d = side/2
        self.center = center
        self.side = side

        self.vertices = [
        ]

        self.faces = [
        ]

        if (type(color) == list or type(color) == tuple) and len(color) == len(self.faces): self.colors = color
        elif (type(color) == list or type(color) == tuple) and len(color) != len(self.faces): self.colors = [color[0] for i in range(len(self.faces))]
        elif (type(color) == str): self.colors = [color for i in range(len(self.faces))]
        else: self.colors = ["None" for i in range(len(self.faces))]

    def rotate(self,theta,phi) :
        for point in self.vertices:
            ct = math.cos(theta);
            st = math.sin(theta);
            cp = math.cos(phi);
            sp = math.sin(phi);
    
            x = point.x - self.center.x;
            y = point.y - self.center.y;
            z = point.z - self.center.z;
    
            point.x = ct * x - st * cp * y + st * sp * z + self.center.x;
            point.y = st * x + ct * cp * y - ct * sp * z + self.center.y;
            point.z = sp * y + cp * z + self.center.z;

    def move(self,z):
        for point in self.vertices:
            point.x += x; self.center.x += x
            point.y += y; self.center.y += y
            point.z += z; self.center.z += z

class Cube(Shape):
    def __init__(self,color=None):
        self.d = side/2
        self.center = center
        self.side = side

        self.vertices = [
            Point3D(center.x - self.d,center.y - self.d,center.z + self.d),Point3D(center.x - self.d,center.z - self.d),Point3D(center.x + self.d,center.y + self.d,center.z + self.d)
        ]

        self.faces = [
            [self.vertices[0],self.vertices[1],self.vertices[2],self.vertices[3]],[self.vertices[3],self.vertices[5],self.vertices[4]],[self.vertices[4],self.vertices[6],self.vertices[7]],[self.vertices[7],self.vertices[0]],self.vertices[0],self.vertices[3],[self.vertices[1],self.vertices[2]]
        ]

        if (type(color) == list or type(color) == tuple) and len(color) == len(self.faces): self.colors = color
        elif (type(color) == list or type(color) == tuple) and len(color) != len(self.faces): self.colors = [color[0] for i in range(len(self.faces))]
        elif (type(color) == str): self.colors = [color for i in range(len(self.faces))]
        else: self.colors = ["None" for i in range(len(self.faces))]

class Pyramid(Shape):
    def __init__(self,color=None):
        self.d = side/2
        self.center = center
        self.side = side

        self.vertices = [
            Point3D(center.x,center.z),]

        self.faces = [
            [self.vertices[0],self.vertices[2]],[self.vertices[0],self.vertices[4]]
        ]

        if (type(color) == list or type(color) == tuple) and len(color) == len(self.faces): self.colors = color
        elif (type(color) == list or type(color) == tuple) and len(color) != len(self.faces): self.colors = [color[0] for i in range(len(self.faces))]
        elif (type(color) == str): self.colors = [color for i in range(len(self.faces))]
        else: self.colors = ["None" for i in range(len(self.faces))]

class Camera:
    def __init__(self,x=0,y=0,z=0):
        self.position = Point3D(x,z)

        self.screen = turtle.Screen()
        self.screen.delay(0)
        self.screen.tracer(0)

        self.cursor = turtle.Turtle()
        self.cursor.ht()
        self.cursor.pu()
        self.cursor.speed(0)

    def move(self,z):
        self.position.combine(-x,-y,z)

    def teleport(self,z):
        self.position = Point3D(-x,z)

    def rotate(self,z):
        pass

    def snap(self,z):
        pass

class Fill:
    def __init__(self,color,cursor):
        self.cursor = cursor
        self.color = color

    def __enter__(self):
        if not self.color.lower() == "none":
            self.cursor.fillcolor(self.color)
            self.cursor.begin_fill()

    def __exit__(self,type,value,traceback):
        if not self.color.lower() == "none":
            self.cursor.end_fill()

def project(M,camera):
    d = 100
    r = d / M.y
    return Point2D(r * M.x,r * M.z)

def mean(numbers):
    return sum(numbers)/len(numbers)

def midpoint3D(points):
    x = [x.x for x in points]
    y = [y.y for y in points]
    z = [z.z for z in points]
    return (mean(x),mean(y),mean(z))

def midpoint2D(points):
    x = [x.x for x in points]
    y = [y.y for y in points]
    return (mean(x),mean(y))

def shape(points,cursor):
    with Fill(color,cursor):
        cursor.pu()
        cursor.goto(points[0].x,points[0].y)
        cursor.pd()

        for point in points[1:]:
            cursor.goto(point.x,point.y)

        cursor.goto(points[0].x,points[0].y)
        cursor.pu()

def distance(x0,y0,z0,x1,y1,z1):
    return math.sqrt(math.pow(x1 - x0,2) + math.pow(y1 - y0,2) + math.pow(z1 - z0,2)* 1.0)

def render(objects,camera):
    camera.cursor.clear()

    for _object in objects:
        color = dict([(str(b),_object.colors[a]) for a,b in enumerate(_object.faces)])
        for index,face in enumerate(sorted(_object.faces,key=lambda o: min(list(map(lambda v: distance(camera.position.x,camera.position.y,camera.position.z,v.y,0),o))),reverse=True)):
            points2D = []
            points3D = []

            for point in face:
                cameraPoint = Point3D(point.x+camera.position.x,point.y+camera.position.y,point.z+camera.position.z)

                P = project(cameraPoint,camera)

                points2D.append(Point2D(P.x,-P.y))
                points3D.append(cameraPoint)

            shape(points2D,color[str(face)],camera.cursor)

            #midpoint = midpoint3D(points3D)
            #point = Point3D(midpoint[0],midpoint[1],midpoint[2])
            #P = project(point)
            #cursor.goto(P.x,-P.y)
            #cursor.dot(5,"black")

    camera.screen.update()

结束语

如果您知道如何修复此错误、其他错误、我的代码格式、变量名称和 ETC,请告诉我,以便我(希望)在未来的项目中对其进行更改。谢谢!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。