OpenGL 中的等距视图
Isometric view in OpenGL
我想了解为什么等轴测视图不正确。最准确的表示是与垂直轴成 55 度,与水平轴成 45 度。在 mcve 中有两个点应该在每个轴上以 45 度对齐。我已经根据球体代数定义设置了相机运动。平面的边缘应该重合,但它们不重合。
MCVE:
from OpenGL.GL import glClear, GL_COLOR_BUFFER_BIT, glEnable, GL_DEPTH_TEST, glMatrixMode, GL_PROJECTION, \
glLoadIdentity, glOrtho, glClearColor, GL_DEPTH_BUFFER_BIT, GL_MODELVIEW, glLineWidth, glBegin, glColor, glVertex, \
glEnd, glPointSize, GL_POINT_SMOOTH, GL_POINTS, GL_BLEND, glBlendFunc, GL_SRC_ALPHA, \
GL_QUADS, glDisable, GL_LINES, GL_LINE_LOOP, glDepthMask, GL_FALSE, GL_TRUE, GL_ONE_MINUS_SRC_ALPHA
from OpenGL.GLU import gluLookAt
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QOpenGLWidget
from math import sin, cos, radians
class Renderizador(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.dx = 0
self.dy = 0
self.dz = 0
self.theta = 405
self.phi = 45
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
self.vertices_vertical = ((100, 100, 0), (-100, 100, 0), (-100, 0, 0), (100, 0, 0))
self.vertices_vertical_debajo = ((100, 0, 0), (-100, 0, 0), (-100, -100, 0), (100, -100, 0))
self.vertices_horizontal = ((100, 0, 0), (100, 0, 100), (-100, 0, 100), (-100, 0, 0))
self.vertices_horizontal_detras = ((100, 0, 0), (100, 0, -100), (-100, 0, -100), (-100, 0, 0))
self.vertices_borde_v = ((100, 100, 0), (100, -100, 0), (-100, -100, 0), (-100, 100, 0))
self.vertices_borde_h = ((100, 0, 100), (-100, 0, 100), (-100, 0, -100), (100, 0, -100))
self.puntos = [("a", 1, 1, 1), ("b", 10, 10, 10)]
def recalcular(self):
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, 1, 0)
self.update()
def dibujar_planos(self):
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDepthMask(GL_FALSE)
glBegin(GL_QUADS)
glColor(1, 0, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_horizontal_detras[vertex])
glColor(0, 1, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_vertical_debajo[vertex])
glColor(0, 1, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_vertical[vertex])
glColor(1, 0, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_horizontal[vertex])
glEnd()
glDepthMask(GL_TRUE)
glDisable(GL_BLEND)
glLineWidth(1)
glColor(0.2, 1, 0.2, 0.5)
glBegin(GL_LINE_LOOP)
for vertex in range(4):
glVertex(self.vertices_borde_v[vertex])
glColor(1, 0.2, 0.2, 0.5)
glEnd()
glBegin(GL_LINE_LOOP)
for vertex in range(4):
glVertex(self.vertices_borde_h[vertex])
glEnd()
@staticmethod
def dibujar_ejes():
glLineWidth(3)
glBegin(GL_LINES)
# X ROJO
glColor(1, 0, 0)
glVertex(0, 0, 0)
glVertex(10, 0, 0)
# Y VERDE
glColor(0, 1, 0)
glVertex(0, 0, 0)
glVertex(0, 10, 0)
# Z AZUL
glColor(0, 0, 1)
glVertex(0, 0, 0)
glVertex(0, 0, 10)
glEnd()
def dibujar_punto(self):
glColor(0, 0, 0, 0)
glPointSize(4)
glEnable(GL_POINT_SMOOTH)
glBegin(GL_POINTS)
for i in range(len(self.puntos)):
glVertex(self.puntos[i][1], self.puntos[i][3], self.puntos[i][2])
glEnd()
# self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
# self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
# self.y = cos(radians(self.theta)) + self.dy
# gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, 1, 0)
self.update()
def initializeGL(self):
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(100, -100, -100, 100, -500, 150)
glMatrixMode(GL_MODELVIEW)
def paintGL(self):
glClearColor(1, 1, 1, 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
up = 1
if self.theta == 360:
up = -1
gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, up, 0)
self.dibujar_ejes()
self.dibujar_planos()
self.dibujar_punto()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_W:
self.theta -= 5
elif event.key() == QtCore.Qt.Key_A:
self.phi -= 5
elif event.key() == QtCore.Qt.Key_S:
self.theta += 5
elif event.key() == QtCore.Qt.Key_D:
self.phi += 5
elif event.key() == QtCore.Qt.Key_Q:
self.dz += 1
elif event.key() == QtCore.Qt.Key_E:
self.dz -= 1
elif event.key() == QtCore.Qt.Key_Left:
self.dx -= 1
elif event.key() == QtCore.Qt.Key_Up:
self.dy += 1
elif event.key() == QtCore.Qt.Key_Right:
self.dx += 1
elif event.key() == QtCore.Qt.Key_Down:
self.dy -= 1
if self.theta < 360:
self.theta = 360
if self.theta > 540:
self.theta = 540
if self.phi >= 360:
self.phi -= 360
if self.phi < 0:
self.phi += 360
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
ui.actualizar()
self.update()
super().keyPressEvent(event)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
ventana.resize(1500, 1015)
self.widget_central = QtWidgets.QWidget(ventana)
self.Renderizador = Renderizador(self.widget_central)
self.Renderizador.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
self.Renderizador.setFocusPolicy(QtCore.Qt.StrongFocus)
self.label_5 = QtWidgets.QLabel(self.widget_central)
self.label_5.setGeometry(QtCore.QRect(1110, 49, 160, 20))
self.label_6 = QtWidgets.QLabel(self.widget_central)
self.label_6.setGeometry(QtCore.QRect(1010, 70, 111, 16))
self.label_7 = QtWidgets.QLabel(self.widget_central)
self.label_7.setGeometry(QtCore.QRect(1130, 70, 130, 16))
self.label_5.setText("X: Y: Z:")
self.label_6.setText("Ángulo vertical:")
self.label_7.setText("Ángulo horizontal:")
ventana.setCentralWidget(self.widget_central)
ventana.show()
def actualizar(self):
x = round(100 * (sin(radians(self.Renderizador.theta)) * cos(radians(self.Renderizador.phi)))
+ self.Renderizador.dx, 2)
z = round(100 * (sin(radians(self.Renderizador.theta)) * sin(radians(self.Renderizador.phi)))
+ self.Renderizador.dz, 2)
y = round(100 * (cos(radians(self.Renderizador.theta))) + self.Renderizador.dy, 2)
theta = self.Renderizador.theta - 360
phi = self.Renderizador.phi
if x == -0:
x = 0
if y == -0:
y = 0
if z == -0:
z = 0
self.label_5.setText("X: {} Y: {} Z: {}".format(x, z, y))
self.label_6.setText("Ángulo vertical: " + str(theta))
self.label_7.setText("Ángulo horizontal: " + str(phi))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
ventana = QtWidgets.QMainWindow()
ui = UiVentana()
sys.exit(app.exec_())
要获得完美的等距视图,视点必须位于立方体的对角线上。这意味着如果视图的目标是 (0, 0, 0),那么眼睛位置的 x、y 和 z 坐标的绝对值必须相等。
当您查看计算 x、y 和 z 的公式时:
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
这意味着您必须找到一个 phi
和 theta
使 x、y 和 z 相等。
对于 phi 这很容易。 Phi 仅用于计算 x 和 z。 x 取决于 cos(phi)
,z 取决于 sin(phi)
,因为 sin(45) == cos(45)
,phi
必须为 45°。
sin(45)
等于 1/sqrt(2)
.
所以必须找到一个theta
,其中x,y,z相等时
x = sin(theta) * 1/sqrt(2)
z = sin(theta) * 1/sqrt(2)
y = cos(theta)
这意味着
sin(theta) * 1/sqrt(2) = cos(theta)
从Pythagoras我们知道:
cos²(theta) + sin²(theta) = 1
所以
sin(theta) = cos(theta) * sqrt(2)
和 sin²(theta) = 1 - cos²(theta)
1 - cos²(theta) = cos²(theta) * 2
1 = cos(theta) * sqrt(3)
theta = acos(1/sqrt(3))
最后 phi
和 theta
必须是:
self.phi = 45
self.theta = 54.735610317245345684622999669982
我想了解为什么等轴测视图不正确。最准确的表示是与垂直轴成 55 度,与水平轴成 45 度。在 mcve 中有两个点应该在每个轴上以 45 度对齐。我已经根据球体代数定义设置了相机运动。平面的边缘应该重合,但它们不重合。
MCVE:
from OpenGL.GL import glClear, GL_COLOR_BUFFER_BIT, glEnable, GL_DEPTH_TEST, glMatrixMode, GL_PROJECTION, \
glLoadIdentity, glOrtho, glClearColor, GL_DEPTH_BUFFER_BIT, GL_MODELVIEW, glLineWidth, glBegin, glColor, glVertex, \
glEnd, glPointSize, GL_POINT_SMOOTH, GL_POINTS, GL_BLEND, glBlendFunc, GL_SRC_ALPHA, \
GL_QUADS, glDisable, GL_LINES, GL_LINE_LOOP, glDepthMask, GL_FALSE, GL_TRUE, GL_ONE_MINUS_SRC_ALPHA
from OpenGL.GLU import gluLookAt
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QOpenGLWidget
from math import sin, cos, radians
class Renderizador(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.dx = 0
self.dy = 0
self.dz = 0
self.theta = 405
self.phi = 45
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
self.vertices_vertical = ((100, 100, 0), (-100, 100, 0), (-100, 0, 0), (100, 0, 0))
self.vertices_vertical_debajo = ((100, 0, 0), (-100, 0, 0), (-100, -100, 0), (100, -100, 0))
self.vertices_horizontal = ((100, 0, 0), (100, 0, 100), (-100, 0, 100), (-100, 0, 0))
self.vertices_horizontal_detras = ((100, 0, 0), (100, 0, -100), (-100, 0, -100), (-100, 0, 0))
self.vertices_borde_v = ((100, 100, 0), (100, -100, 0), (-100, -100, 0), (-100, 100, 0))
self.vertices_borde_h = ((100, 0, 100), (-100, 0, 100), (-100, 0, -100), (100, 0, -100))
self.puntos = [("a", 1, 1, 1), ("b", 10, 10, 10)]
def recalcular(self):
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, 1, 0)
self.update()
def dibujar_planos(self):
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDepthMask(GL_FALSE)
glBegin(GL_QUADS)
glColor(1, 0, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_horizontal_detras[vertex])
glColor(0, 1, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_vertical_debajo[vertex])
glColor(0, 1, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_vertical[vertex])
glColor(1, 0, 0, 0.5)
for vertex in range(4):
glVertex(self.vertices_horizontal[vertex])
glEnd()
glDepthMask(GL_TRUE)
glDisable(GL_BLEND)
glLineWidth(1)
glColor(0.2, 1, 0.2, 0.5)
glBegin(GL_LINE_LOOP)
for vertex in range(4):
glVertex(self.vertices_borde_v[vertex])
glColor(1, 0.2, 0.2, 0.5)
glEnd()
glBegin(GL_LINE_LOOP)
for vertex in range(4):
glVertex(self.vertices_borde_h[vertex])
glEnd()
@staticmethod
def dibujar_ejes():
glLineWidth(3)
glBegin(GL_LINES)
# X ROJO
glColor(1, 0, 0)
glVertex(0, 0, 0)
glVertex(10, 0, 0)
# Y VERDE
glColor(0, 1, 0)
glVertex(0, 0, 0)
glVertex(0, 10, 0)
# Z AZUL
glColor(0, 0, 1)
glVertex(0, 0, 0)
glVertex(0, 0, 10)
glEnd()
def dibujar_punto(self):
glColor(0, 0, 0, 0)
glPointSize(4)
glEnable(GL_POINT_SMOOTH)
glBegin(GL_POINTS)
for i in range(len(self.puntos)):
glVertex(self.puntos[i][1], self.puntos[i][3], self.puntos[i][2])
glEnd()
# self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
# self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
# self.y = cos(radians(self.theta)) + self.dy
# gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, 1, 0)
self.update()
def initializeGL(self):
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(100, -100, -100, 100, -500, 150)
glMatrixMode(GL_MODELVIEW)
def paintGL(self):
glClearColor(1, 1, 1, 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
up = 1
if self.theta == 360:
up = -1
gluLookAt(self.x, self.y, self.z, self.dx, self.dy, self.dz, 0, up, 0)
self.dibujar_ejes()
self.dibujar_planos()
self.dibujar_punto()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_W:
self.theta -= 5
elif event.key() == QtCore.Qt.Key_A:
self.phi -= 5
elif event.key() == QtCore.Qt.Key_S:
self.theta += 5
elif event.key() == QtCore.Qt.Key_D:
self.phi += 5
elif event.key() == QtCore.Qt.Key_Q:
self.dz += 1
elif event.key() == QtCore.Qt.Key_E:
self.dz -= 1
elif event.key() == QtCore.Qt.Key_Left:
self.dx -= 1
elif event.key() == QtCore.Qt.Key_Up:
self.dy += 1
elif event.key() == QtCore.Qt.Key_Right:
self.dx += 1
elif event.key() == QtCore.Qt.Key_Down:
self.dy -= 1
if self.theta < 360:
self.theta = 360
if self.theta > 540:
self.theta = 540
if self.phi >= 360:
self.phi -= 360
if self.phi < 0:
self.phi += 360
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx
self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz
self.y = cos(radians(self.theta)) + self.dy
ui.actualizar()
self.update()
super().keyPressEvent(event)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
ventana.resize(1500, 1015)
self.widget_central = QtWidgets.QWidget(ventana)
self.Renderizador = Renderizador(self.widget_central)
self.Renderizador.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
self.Renderizador.setFocusPolicy(QtCore.Qt.StrongFocus)
self.label_5 = QtWidgets.QLabel(self.widget_central)
self.label_5.setGeometry(QtCore.QRect(1110, 49, 160, 20))
self.label_6 = QtWidgets.QLabel(self.widget_central)
self.label_6.setGeometry(QtCore.QRect(1010, 70, 111, 16))
self.label_7 = QtWidgets.QLabel(self.widget_central)
self.label_7.setGeometry(QtCore.QRect(1130, 70, 130, 16))
self.label_5.setText("X: Y: Z:")
self.label_6.setText("Ángulo vertical:")
self.label_7.setText("Ángulo horizontal:")
ventana.setCentralWidget(self.widget_central)
ventana.show()
def actualizar(self):
x = round(100 * (sin(radians(self.Renderizador.theta)) * cos(radians(self.Renderizador.phi)))
+ self.Renderizador.dx, 2)
z = round(100 * (sin(radians(self.Renderizador.theta)) * sin(radians(self.Renderizador.phi)))
+ self.Renderizador.dz, 2)
y = round(100 * (cos(radians(self.Renderizador.theta))) + self.Renderizador.dy, 2)
theta = self.Renderizador.theta - 360
phi = self.Renderizador.phi
if x == -0:
x = 0
if y == -0:
y = 0
if z == -0:
z = 0
self.label_5.setText("X: {} Y: {} Z: {}".format(x, z, y))
self.label_6.setText("Ángulo vertical: " + str(theta))
self.label_7.setText("Ángulo horizontal: " + str(phi))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
ventana = QtWidgets.QMainWindow()
ui = UiVentana()
sys.exit(app.exec_())
要获得完美的等距视图,视点必须位于立方体的对角线上。这意味着如果视图的目标是 (0, 0, 0),那么眼睛位置的 x、y 和 z 坐标的绝对值必须相等。
当您查看计算 x、y 和 z 的公式时:
self.x = sin(radians(self.theta)) * cos(radians(self.phi)) + self.dx self.z = sin(radians(self.theta)) * sin(radians(self.phi)) + self.dz self.y = cos(radians(self.theta)) + self.dy
这意味着您必须找到一个 phi
和 theta
使 x、y 和 z 相等。
对于 phi 这很容易。 Phi 仅用于计算 x 和 z。 x 取决于 cos(phi)
,z 取决于 sin(phi)
,因为 sin(45) == cos(45)
,phi
必须为 45°。
sin(45)
等于 1/sqrt(2)
.
所以必须找到一个theta
,其中x,y,z相等时
x = sin(theta) * 1/sqrt(2) z = sin(theta) * 1/sqrt(2) y = cos(theta)
这意味着
sin(theta) * 1/sqrt(2) = cos(theta)
从Pythagoras我们知道:
cos²(theta) + sin²(theta) = 1
所以
sin(theta) = cos(theta) * sqrt(2)
和 sin²(theta) = 1 - cos²(theta)
1 - cos²(theta) = cos²(theta) * 2
1 = cos(theta) * sqrt(3)
theta = acos(1/sqrt(3))
最后 phi
和 theta
必须是:
self.phi = 45
self.theta = 54.735610317245345684622999669982