使用 gluLookAt() 会导致对象旋转
Using gluLookAt() causes the objects to spin
我正在 Pygame 使用 OpenGL 制作游戏。到目前为止,我已经能够让立方体出现并制作十字线。当我尝试实现环顾四周时,事情变得……很奇怪。我会 运行 它,当我使用 gluLookAt()
功能时,甚至不移动我的鼠标它就会开始到处旋转屏幕。当我把它拿出来时,它起作用了,但我无法环顾四周。我正在做一些测试,我什至为函数设置了数据值,只是为了确保它们没有改变并且它仍然旋转。在此先感谢您提供的帮助,这是我的代码:My code on GitHub
您的代码中有 2 个问题。
gluLookAt
设置视图矩阵。但这并不是全部。
gluLookAt
将视图矩阵乘以矩阵堆栈上的当前矩阵,由 glMatrixMode
选择。
因此,您的代码将新的视图矩阵连接到现有的视图矩阵。这导致物体开始快速旋转。
在调用gluLookAt
之前设置Identity matrix来解决这个问题。这导致模型视图矩阵是从头开始设置的,与其以前的状态无关。
glLoadIdentity()
gluLookAt(0, 0, 0, facing[0], facing[1], lookingZ, 0, 1, 0)
这不会完全解决你的问题,因为当你设置视图矩阵时,模型视图矩阵堆栈上的当前矩阵是投影矩阵和视图矩阵的串联.这导致 glLoadIdentity
也跳过了投影矩阵。
这种行为可以轻松解决。将视图矩阵放在模型视图矩阵栈上(GL_MODELVIEW
),将投影矩阵放在投影矩阵栈上(GL_PROJECTION
):
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (width/height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
一个更好且完全可行的解决方案是将围绕 x 和 y 轴的旋转矩阵应用于视图矩阵。首先应用围绕 y 轴的旋转矩阵(向上向量),然后应用当前视图矩阵,最后应用 x 轴上的旋转:
view-matrix = rotate-X * view-matrix * rotate-Y
为此,当前视图矩阵必须由 glGetFloatv(GL_MODELVIEW_MATRIX)
:
读取
modelview = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
glRotate(-change[1]*0.1, 1, 0, 0)
glMultMatrixf(modelview)
glRotate(change[0]*0.1, 0, 1, 0)
注意,必须这样做而不是:
glLoadIdentity()
gluLookAt(0, 0, 0, facing[0], facing[1], lookingZ, 0, 1, 0)
完成 main
功能(renderingEngine.py),并进行建议的更改:
def main(world,x,y,z,width,height,renderDistance):
pygame.init()
pygame.display.set_mode((width,height), DOUBLEBUF | OPENGL)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glDepthMask(GL_TRUE)
glDepthFunc(GL_LESS)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glFrontFace(GL_CCW)
glShadeModel(GL_SMOOTH)
glDepthRange(0.0, 1.0)
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (width/height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
#pygame.mouse.set_visible(False)
facing = [0, 0, False]
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
pygame.mouse.set_visible(True)
pygame.quit()
return # TODO: Add pause
newMousePos = pygame.mouse.get_pos()
change = (newMousePos[0]-(width/2), newMousePos[1]-(height/2))
pygame.mouse.set_pos([width / 2, height / 2])
if facing[2]:
facing[0] -= change[0]
else:
facing[0] += change[0]
facing[1] += change[1]
while facing[0] > width:
facing[0] = 2*width-facing[0]
facing[2] = not facing[2]
while facing[0] < 0:
facing[0] = 0-facing[0]
facing[2] = not facing[2]
if facing[1] < 0:
facing[1] = 0
if facing[1] > height:
facing[1] = height
radius = (width**2+height**2)**.5+1
lookingZ = (-1*facing[0]**2-facing[1]**2+radius**2)**.5
if facing[2]:
lookingZ *= -1
#print(lookingZ, facing[0], facing[1], radius)
print(facing[0], facing[1], lookingZ)
#glLoadIdentity()
#gluLookAt(0, 0, 0, facing[0], facing[1], lookingZ, 0, 1, 0)
modelview = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
glRotate(-change[1]*0.1, 1, 0, 0)
glMultMatrixf(modelview)
glRotate(change[0]*0.1, 0, 1, 0)
xmin = round(x-renderDistance[0])
ymin = round(y-renderDistance[1])
zmin = round(z-renderDistance[2])
if xmin < 0:
xmin = 0
if ymin < 0:
ymin = 0
if zmin < 0:
zmin = 0
xmax = round(x+renderDistance[0])
ymax = round(y+renderDistance[1])
zmax = round(z+renderDistance[2])
dims = world.dims()
if xmax > dims[0]:
xmax = dims[0]
if ymax > dims[1]:
ymax = dims[1]
if zmax > dims[2]:
zmax = dims[2]
selection = world.select_data(xrange = (xmin, xmax), yrange = (ymin, ymax), zrange = (zmin, zmax))
blocks = selection.iterate(ignore=(None,))
glClearDepth(1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for bl in blocks:
locations = bl[0]
block = bl[1]
cube(locations[0] - x, locations[1] - y, locations[2] - z, block)
#print(locations[0],locations[1],locations[2])
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glOrtho(0.0, width, 0.0, height, -1.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
glDisable(GL_DEPTH_TEST)
crosshair(width/2, height/2, 20)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
glCullFace(GL_BACK)
pygame.display.flip()
time.sleep(.01)
我正在 Pygame 使用 OpenGL 制作游戏。到目前为止,我已经能够让立方体出现并制作十字线。当我尝试实现环顾四周时,事情变得……很奇怪。我会 运行 它,当我使用 gluLookAt()
功能时,甚至不移动我的鼠标它就会开始到处旋转屏幕。当我把它拿出来时,它起作用了,但我无法环顾四周。我正在做一些测试,我什至为函数设置了数据值,只是为了确保它们没有改变并且它仍然旋转。在此先感谢您提供的帮助,这是我的代码:My code on GitHub
您的代码中有 2 个问题。
gluLookAt
设置视图矩阵。但这并不是全部。gluLookAt
将视图矩阵乘以矩阵堆栈上的当前矩阵,由glMatrixMode
选择。
因此,您的代码将新的视图矩阵连接到现有的视图矩阵。这导致物体开始快速旋转。
在调用gluLookAt
之前设置Identity matrix来解决这个问题。这导致模型视图矩阵是从头开始设置的,与其以前的状态无关。
glLoadIdentity()
gluLookAt(0, 0, 0, facing[0], facing[1], lookingZ, 0, 1, 0)
这不会完全解决你的问题,因为当你设置视图矩阵时,模型视图矩阵堆栈上的当前矩阵是投影矩阵和视图矩阵的串联.这导致
glLoadIdentity
也跳过了投影矩阵。
这种行为可以轻松解决。将视图矩阵放在模型视图矩阵栈上(GL_MODELVIEW
),将投影矩阵放在投影矩阵栈上(GL_PROJECTION
):
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (width/height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
一个更好且完全可行的解决方案是将围绕 x 和 y 轴的旋转矩阵应用于视图矩阵。首先应用围绕 y 轴的旋转矩阵(向上向量),然后应用当前视图矩阵,最后应用 x 轴上的旋转:
view-matrix = rotate-X * view-matrix * rotate-Y
为此,当前视图矩阵必须由 glGetFloatv(GL_MODELVIEW_MATRIX)
:
modelview = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
glRotate(-change[1]*0.1, 1, 0, 0)
glMultMatrixf(modelview)
glRotate(change[0]*0.1, 0, 1, 0)
注意,必须这样做而不是:
glLoadIdentity()
gluLookAt(0, 0, 0, facing[0], facing[1], lookingZ, 0, 1, 0)
完成 main
功能(renderingEngine.py),并进行建议的更改:
def main(world,x,y,z,width,height,renderDistance):
pygame.init()
pygame.display.set_mode((width,height), DOUBLEBUF | OPENGL)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glDepthMask(GL_TRUE)
glDepthFunc(GL_LESS)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glFrontFace(GL_CCW)
glShadeModel(GL_SMOOTH)
glDepthRange(0.0, 1.0)
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (width/height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
#pygame.mouse.set_visible(False)
facing = [0, 0, False]
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
pygame.mouse.set_visible(True)
pygame.quit()
return # TODO: Add pause
newMousePos = pygame.mouse.get_pos()
change = (newMousePos[0]-(width/2), newMousePos[1]-(height/2))
pygame.mouse.set_pos([width / 2, height / 2])
if facing[2]:
facing[0] -= change[0]
else:
facing[0] += change[0]
facing[1] += change[1]
while facing[0] > width:
facing[0] = 2*width-facing[0]
facing[2] = not facing[2]
while facing[0] < 0:
facing[0] = 0-facing[0]
facing[2] = not facing[2]
if facing[1] < 0:
facing[1] = 0
if facing[1] > height:
facing[1] = height
radius = (width**2+height**2)**.5+1
lookingZ = (-1*facing[0]**2-facing[1]**2+radius**2)**.5
if facing[2]:
lookingZ *= -1
#print(lookingZ, facing[0], facing[1], radius)
print(facing[0], facing[1], lookingZ)
#glLoadIdentity()
#gluLookAt(0, 0, 0, facing[0], facing[1], lookingZ, 0, 1, 0)
modelview = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
glRotate(-change[1]*0.1, 1, 0, 0)
glMultMatrixf(modelview)
glRotate(change[0]*0.1, 0, 1, 0)
xmin = round(x-renderDistance[0])
ymin = round(y-renderDistance[1])
zmin = round(z-renderDistance[2])
if xmin < 0:
xmin = 0
if ymin < 0:
ymin = 0
if zmin < 0:
zmin = 0
xmax = round(x+renderDistance[0])
ymax = round(y+renderDistance[1])
zmax = round(z+renderDistance[2])
dims = world.dims()
if xmax > dims[0]:
xmax = dims[0]
if ymax > dims[1]:
ymax = dims[1]
if zmax > dims[2]:
zmax = dims[2]
selection = world.select_data(xrange = (xmin, xmax), yrange = (ymin, ymax), zrange = (zmin, zmax))
blocks = selection.iterate(ignore=(None,))
glClearDepth(1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for bl in blocks:
locations = bl[0]
block = bl[1]
cube(locations[0] - x, locations[1] - y, locations[2] - z, block)
#print(locations[0],locations[1],locations[2])
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glOrtho(0.0, width, 0.0, height, -1.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
glDisable(GL_DEPTH_TEST)
crosshair(width/2, height/2, 20)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
glCullFace(GL_BACK)
pygame.display.flip()
time.sleep(.01)