Python 使用相机自己的新轴进行三维旋转
为了理解3D图形(主要是矩阵变换部分), 我用tkinter制作了一个简单的3D引擎 问题是,现在,在添加了一个摄像头之后,我被卡住了。我想绑定WASDEQ键来移动相机(W是向上的,S是向下的,A是向左的,D是向右的,E是向前的,Q是向后的)和箭头键来旋转它。然而,在尝试这些特性时,我发现它们总是相对于XYZ轴进行的。这意味着,如果我的相机指向正下方,我会期望(按下E键后)朝向它所看到的地方。但是,它指向负Z轴 旋转相机也是如此。由于某些原因,总是相对于相机的当前位置进行左右查看,但上下查看是相对于X轴进行的,而不是相对于相机的当前位置。如果有人能为我提供一个解决方案或材料,解释必要的转换顺序,我将不胜感激 以下是完整的代码,相对较短:Python 使用相机自己的新轴进行三维旋转,python,matrix,3d,Python,Matrix,3d,为了理解3D图形(主要是矩阵变换部分), 我用tkinter制作了一个简单的3D引擎 问题是,现在,在添加了一个摄像头之后,我被卡住了。我想绑定WASDEQ键来移动相机(W是向上的,S是向下的,A是向左的,D是向右的,E是向前的,Q是向后的)和箭头键来旋转它。然而,在尝试这些特性时,我发现它们总是相对于XYZ轴进行的。这意味着,如果我的相机指向正下方,我会期望(按下E键后)朝向它所看到的地方。但是,它指向负Z轴 旋转相机也是如此。由于某些原因,总是相对于相机的当前位置进行左右查看,但上下查看是相
import numpy
import math
import tkinter
#Window dimensions
width = 800
height = 800
#An empty canvas
cnv = tkinter.Canvas(bg='white', width=width, height=height)
#Triangle rasterization
def drawTriangle(triangle):
cnv.create_line(triangle[0][0],height-triangle[0][1],triangle[1][0],height-triangle[1][1])
cnv.create_line(triangle[1][0],height-triangle[1][1],triangle[2][0],height-triangle[2][1])
cnv.create_line(triangle[2][0],height-triangle[2][1],triangle[0][0],height-triangle[0][1])
#Adding a homogenous coordinate (w)
def homogenous(vertex):
vertex.append(1)
#Transforming row major vertexes to column major vertexes
def transpose(vertex):
return numpy.array([vertex]).T
#SPACE CONVERSION
#Our cube is in its model space. We want to put it onto our scene, while rotating it a bit and moving it further away from the camera.
#model space->world space
#Applying the transformation to all of our vertexes
def modelToWorld(vertex,x,y,z):
# Rotation angles
xangle = math.radians(x)
yangle = math.radians(y)
zangle = math.radians(z)
# Rotation matrices
xRotationMatrix = numpy.array(
[[1, 0, 0, 0], [0, math.cos(xangle), -math.sin(xangle), 0], [0, math.sin(xangle), math.cos(xangle), 0],
[0, 0, 0, 1]])
yRotationMatrix = numpy.array(
[[math.cos(yangle), 0, math.sin(yangle), 0], [0, 1, 0, 0], [-math.sin(yangle), 0, math.cos(yangle), 0],
[0, 0, 0, 1]])
zRotationMatrix = numpy.array(
[[math.cos(zangle), -math.sin(zangle), 0, 0], [math.sin(zangle), math.cos(zangle), 0, 0], [0, 0, 1, 0],
[0, 0, 0, 1]])
# Translation along the negative Z axis
TranslationMatrix = numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -6], [0, 0, 0, 1]])
# Combining the transformations into one model matrix
ModelMatrix = numpy.dot(yRotationMatrix, xRotationMatrix)
ModelMatrix = numpy.dot(zRotationMatrix, ModelMatrix)
ModelMatrix = numpy.dot(TranslationMatrix, ModelMatrix)
return numpy.dot(ModelMatrix,vertex)
#Now we want to move our camera
#We cannot move the camera itself, we need to move the world. So in order to move the camera 1 unit closer to the cube,
#we need to move the cube closer to the camera. Remember, the camera always points to the negative Z axis.
#world space->view space
zoom_num=0
#View matrix
#Applying the transformation to all of our vertexes
xcam=0
ycam=0
zcam=0
camXangle=0
camYangle=0
camZangle=0
def worldToView(vertex):
CamyRotationMatrix = numpy.array(
[[math.cos(math.radians(camYangle)), 0, math.sin(math.radians(camYangle)), 0], [0, 1, 0, 0], [-math.sin(math.radians(camYangle)), 0, math.cos(math.radians(camYangle)), 0],
[0, 0, 0, 1]])
CamxRotationMatrix = numpy.array(
[[1, 0, 0, 0], [0, math.cos(math.radians(camXangle)), -math.sin(math.radians(camXangle)), 0], [0, math.sin(math.radians(camXangle)), math.cos(math.radians(camXangle)), 0],
[0, 0, 0, 1]])
CamTranslationMatrix = numpy.array([[1, 0, 0, 0+xcam], [0, 1, 0, 0+ycam], [0, 0, 1, 0 + zcam], [0, 0, 0, 1]])
CamRotationMatrix = numpy.dot(CamyRotationMatrix, CamxRotationMatrix)
ViewMatrix = numpy.dot(CamRotationMatrix, CamTranslationMatrix)
return numpy.dot(ViewMatrix,vertex)
#Now we need to apply the projection matrix to create perspective.
#view space->clip space
#Projection matrix
ProjectionMatrix = numpy.array([[0.8,0,0,0], [0,0.8,0,0],[0,0,-1.22,-2.22],[0,0,-1,0]])
#ProjectionMatrix = numpy.array([[0.25,0,0,0], [0,0.25,0,0],[0,0,-0.22,-1.22],[0,0,0,1]])
def viewToClip(vertex):
return numpy.dot(ProjectionMatrix,vertex)
#In order to turn the resulting coordinates into NDC, we need to divide by W.
def perspectiveDivision(vertex):
for j in range(4):
vertex[j]=vertex[j]/vertex[3]
return vertex
#Turning values from -1 to 1 into individual pixels on the screen
def viewportTransformation(vertex):
vertex[0] = (vertex[0] * 0.5 + 0.5) * width
vertex[1] = (vertex[1] * 0.5 + 0.5) * height
return vertex
#Rounding the resulting values
def roundPixel(vertex):
vertex[0]= int(round(vertex[0][0]))
vertex[1] = int(round(vertex[1][0]))
return vertex
#Vertexes of cube triangles
cubeMesh2=[
[-1,-1,-1],[1,-1,-1],[1,-1,1] , [-1,-1,-1],[1,-1,1],[-1,-1,1], #TOP
[1,-1,-1],[1,1,-1],[1,1,1] , [1,-1,-1],[1,1,1],[1,-1,1], #RIGHT
[-1,1,-1],[-1,-1,-1],[-1,-1,1] , [-1,1,-1],[-1,-1,1],[-1,1,1], #LEFT
[1,1,-1],[-1,1,-1],[-1,1,1] , [1,1,-1],[-1,1,1],[1,1,1], #BOTTOM
[-1,-1,1],[1,-1,1],[1,1,1] , [-1,-1,1],[1,1,1],[-1,1,1], #NEAR
[1,-1,-1],[-1,-1,-1],[-1,1,-1] , [1,-1,-1],[-1,1,-1],[1,1,-1] #FAR
]
cubeMesh=[
[-2, 0, -2],[2, 0, -2],[-2, 0, 2],
[-2, 0, 2],[2, 0, -2],[2, 0, 2],
[-0.5, 0., -0.5 ],
[-0.5, 1., -0.5 ],
[-0.5, 0, 0.5],
[-0.5, 1, -0.5],
[-0.5, 0., 0.5],
[-0.5, 1., 0.5],
[-0.5, 0., -0.5],
[-0.5, 1., -0.5],
[0.5, 0., -0.5],
[-0.5, 1., -0.5],
[0.5, 0., -0.5],
[0.5, 1., -0.5],
[0.5, 0., -0.5],
[0.5, 1., -0.5],
[0.5, 0., 0.5],
[0.5, 1., -0.5],
[0.5, 0., 0.5],
[0.5, 1, 0.5],
[-0.5, 0., 0.5],
[-0.5, 1., 0.5],
[0.5, 0., 0.5],
[-0.5, 1., 0.5],
[0.5, 0., 0.5],
[0.5, 1., 0.5],
[-0.5, 1., -0.5],
[-0.5, 1., 0.5],
[0., 2., 0.0],
[0.5, 1., -0.5],
[0.5, 1., 0.5],
[0., 2., 0.0],
[-0.5, 1., -0.5],
[0.5, 1., -0.5],
[0., 2., 0.0],
[-0.5, 1, 0.5],
[0.5, 1, 0.5],
[0., 2., 0.0]
]
#An empty triangle
Triangle=[[0,0,0],[0,0,0],[0,0,0]]
#Colors
colors = [(255,0,0),(255,0,0),(0,255,0),(0,255,0),(0,0,255),(0,0,255),(255,255,0),(255,255,0),(0,255,255),(0,255,255),(255,0,255),(255,0,255)]
#Triangle counter
counter=0
cnv.pack()
for i in range(len(cubeMesh)):
homogenous(cubeMesh[i]) # Adding a homogenous coordinate
cubeMesh[i] = transpose(cubeMesh[i]) # Changing a row vector to a column vector
changingMesh = cubeMesh.copy()
j=0
def update():
cnv.delete("all")
counter=0
global j
for i in range(len(cubeMesh)):
changingMesh[i]=modelToWorld(cubeMesh[i],0,0,0) #Moving our model to its place on world coordinates
changingMesh[i]=worldToView(changingMesh[i]) #Moving the world relative to our camera ("moving" the camera)
changingMesh[i]=viewToClip(changingMesh[i]) #Applying projection
changingMesh[i] = perspectiveDivision(changingMesh[i]) # Dividing by W to get to normalised device coordinates
changingMesh[i] = viewportTransformation(changingMesh[i]) # Changing the normalised device coordinates to pixels on the screen
changingMesh[i] = roundPixel(changingMesh[i]) # Rounding the resulting values to nearest pixel
Triangle[i%3][0] = int(changingMesh[i][0])
Triangle[i%3][1] = int(changingMesh[i][1])
Triangle[i % 3][2] = int(changingMesh[i][2])
if i%3==2:
drawTriangle(Triangle)
counter+=1
j+=1
if j == 365:
j=0
cnv.after(5,update)
update()
def move(event):
global xcam
global ycam
global zcam
if event.char=='q':
zcam+=0.2
if event.char == 'e':
zcam-=0.2
if event.char=='a':
xcam+=0.2
if event.char == 'd':
xcam-=0.2
if event.char=='w':
ycam-=0.2
if event.char == 's':
ycam+=0.2
def rotateLeft(event):
global camYangle
camYangle-=4
def rotateRight(event):
global camYangle
camYangle+=4
def rotateUp(event):
print("hey")
global camXangle
camXangle+=4
def rotateDown(event):
global camXangle
camXangle-=4
cnv.bind_all('<Key>', move)
cnv.bind_all('<Left>',rotateLeft)
cnv.bind_all('<Right>',rotateRight)
cnv.bind_all('<Up>',rotateUp)
cnv.bind_all('<Down>',rotateDown)
tkinter.mainloop()
导入numpy
输入数学
进口tkinter
#窗口尺寸
宽度=800
高度=800
#空画布
cnv=tkinter.Canvas(bg='white',宽度=宽度,高度=高度)
#三角光栅化
def drawTriangle(三角形):
cnv.创建_线(三角形[0][0]、高度三角形[0][1]、三角形[1][0]、高度三角形[1][1])
cnv.创建_线(三角形[1][0]、高度三角形[1][1]、三角形[2][0]、高度三角形[2][1])
cnv.创建_线(三角形[2][0]、高度三角形[2][1]、三角形[0][0]、高度三角形[0][1])
#添加同质坐标(w)
def同质(顶点):
顶点追加(1)
#将行主顶点转换为列主顶点
def转置(顶点):
返回numpy.array([vertex]).T
#空间转换
#我们的立方体在它的模型空间中。我们想把它放到我们的场景中,同时旋转一点,把它移到离相机更远的地方。
#模型空间->世界空间
#将变换应用于所有顶点
def modelToWorld(顶点,x,y,z):
#旋转角度
x角=数学弧度(x)
杨乐=数学弧度(y)
赞格尔=数学弧度(z)
#旋转矩阵
xRotationMatrix=numpy.array(
[1,0,0,0],[0,math.cos(xangle),-math.sin(xangle),0],[0,math.sin(xangle),math.cos(xangle),0],
[0, 0, 0, 1]])
yRotationMatrix=numpy.array(
[math.cos(yangle),0,math.sin(yangle),0],[0,1,0,0],-math.sin(yangle),0,math.cos(yangle),0],
[0, 0, 0, 1]])
zRotationMatrix=numpy.array(
[math.cos(赞歌),-math.sin(赞歌),0,0],[math.sin(赞歌),math.cos(赞歌),0,0],[0,0,1,0],
[0, 0, 0, 1]])
#沿负Z轴的平移
TranslationMatrix=numpy.array([[1,0,0,0],[0,1,0,0],[0,0,1,6],[0,0,0,1])
#将转换组合成一个模型矩阵
ModelMatrix=numpy.dot(旋转矩阵,X旋转矩阵)
ModelMatrix=numpy.dot(zRotationMatrix,ModelMatrix)
ModelMatrix=numpy.dot(TranslationMatrix,ModelMatrix)
返回numpy.dot(ModelMatrix,顶点)
#现在我们想移动我们的相机
#我们不能移动相机本身,我们需要移动世界。因此,为了使摄像机1装置更靠近立方体,
#我们需要把立方体移近摄像机。请记住,摄影机始终指向负Z轴。
#世界空间->视图空间
缩放数量=0
#视图矩阵
#将变换应用于所有顶点
xcam=0
ycam=0
zcam=0
camXangle=0
camYangle=0
卡姆桑格尔=0
def worldToView(顶点):
CamyRotationMatrix=numpy.array(
[math.cos(math.radians(camYangle)),0,math.sin(math.radians(camYangle)),0],[0,1,0,0],-math.sin(math.radians(camYangle)),0,math.cos(math.radians(camYangle)),0],
[0, 0, 0, 1]])
CamxRotationMatrix=numpy.array(
[1,0,0,0],[0,math.cos(math.radians(camXangle)),-math.sin(math.radians(camXangle)),0],[0,math.sin(math.radians(camXangle)),math.cos(math.radians(camXangle)),0],
[0, 0, 0, 1]])
CamTranslationMatrix=numpy.array([[1,0,0,0+xcam],[0,1,0,0+ycam],[0,0,1,0+zcam],[0,0,0,1]]
CamRotationMatrix=numpy.dot(CamyRotationMatrix,CamxRotationMatrix)
ViewMatrix=numpy.dot(凸轮旋转矩阵、凸轮平移矩阵)
返回numpy.dot(ViewMatrix,顶点)
#现在我们需要应用投影矩阵来创建透视图。
#查看空间->剪辑空间
#投影矩阵
ProjectionMatrix=numpy.array([[0.8,0,0,0,0],[0,0.8,0,0],[0,0,-1.22,-2.22],[0,0,-1,0]])
#ProjectionMatrix=numpy.array([[0.25,0,0,0,0],[0,0.25,0,0],[0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0,0])
def viewToClip(顶点):
返回numpy.dot(投影矩阵,顶点)
#为了将结果坐标转换为NDC,我们需要除以W。
def透视分区(顶点):
对于范围(4)内的j:
顶点[j]=顶点[j]/顶点[3]
返回顶点
#将值从-1转换为屏幕上的单个像素
def视口转换(顶点):
顶点[0]=(顶点[0]*0.5+0.5)*宽度
顶点[1]=(顶点[1]*0.5+0.5)*高度
返回顶点
#将结果值四舍五入
def圆形像素(顶点):
顶点[0]=int(圆形(顶点[0][0]))
顶点[1]=int(圆形(顶点[1][0]))
返回顶点
#立方体三角形的顶点
立方二=[
[1,-1,-1],[1,-1,-1],[1,-1,1],[1,-1,1],[1,-1,1],-1,-1,1],#顶部
[1,-1,-1],[1,1,-1],[1,1,1],[1,-1,-1],[1,1,1],[1,-1,1],#对
[1,1,-1],-1,-1,-1],-1,-1,1],-1,1,-1,-1,1],-1,1,1],#左
[1,1,-1]、-1,1,-1]、-1,1,1]、[1,1,-1]、-1,1,1]、[1,1,1]、#底部
[1,-1,1],[1,-1,1],[1,1,1],-1,-1,1],[1,1,1],-1,1,1],#NEAR
[1,-1,-1]、-1,-1]、-1,1,-1]、[1,-1,-1]、-1,1,-1]、[1,1,-1]#
]
立方目=[
[-2, 0, -2],[2, 0, -2],[-2, 0, 2],
[-2, 0, 2],[2, 0, -2],[2, 0, 2],
[-0.5, 0., -0.5 ],
[-0.5, 1., -0.5 ],
[-0.5, 0, 0.5],
[-0.5, 1, -0.5],
[-0.5, 0., 0.5],