现代OpenGL中透视投影下物体ortho axes绘制及尺寸恒定问题求解
嘿,刚好之前做过类似的需求,来帮你搞定这两个OpenGL问题!
一、在透视投影场景中绘制正交轴的实现思路
核心思路是分阶段渲染:先正常渲染你的透视3D场景,再切换到正交投影来单独绘制坐标轴,这样轴就不会受到透视变形的影响。具体步骤如下:
- 先完成透视场景的渲染,注意不要清除深度缓冲(或者调整深度测试规则,让轴能显示在物体上方,比如临时关闭深度测试,或者设置
glDepthFunc(GL_ALWAYS))。 - 把选中物体的世界空间坐标转换为屏幕窗口坐标:
- 用透视投影矩阵 + 视图矩阵把世界坐标转换为NDC(标准化设备坐标),再通过视口参数转换为屏幕像素坐标(你也可以用
gluProject函数快速完成这个转换,现代OpenGL里需要自己实现的话,公式也不难)。
- 用透视投影矩阵 + 视图矩阵把世界坐标转换为NDC(标准化设备坐标),再通过视口参数转换为屏幕像素坐标(你也可以用
- 切换到正交投影模式:
- 创建一个覆盖整个屏幕的正交投影矩阵,比如
ortho(0, windowWidth, 0, windowHeight, -1, 1)(注意OpenGL默认Y轴原点在左下角,如果你习惯左上角的话可以调整矩阵)。
- 创建一个覆盖整个屏幕的正交投影矩阵,比如
- 在屏幕坐标对应的位置绘制正交轴:
- 直接用屏幕像素长度定义轴的大小(比如X轴从
(screenX, screenY)画到(screenX+50, screenY)),这样轴就是完全正交的,不会有透视变形。
- 直接用屏幕像素长度定义轴的大小(比如X轴从
二、让轴的尺寸不受相机位置影响的两种方案
这个问题本质是要让轴在屏幕上的大小恒定,不管物体离相机多远,都保持同样的视觉尺寸,有两种常用方案:
方案1:屏幕空间绘制(最直接)
就是上面第一个问题里用到的方式——直接在屏幕像素坐标系下绘制轴。因为是基于屏幕像素定义长度,不管物体离相机多远,轴的像素大小都是固定的,完全不受透视缩放的影响。
现代OpenGL中的伪代码片段供你参考:
// 先渲染透视场景... // 切换到轴的渲染 axisShader.use(); // 计算选中物体的屏幕坐标 glm::vec3 worldPos = selectedObj->getWorldPos(); glm::vec4 ndc = perspectiveMat * viewMat * glm::vec4(worldPos, 1.0f); ndc /= ndc.w; // 透视除法 glm::vec2 screenPos = glm::vec2( (ndc.x + 1.0f) / 2.0f * windowWidth, (1.0f - ndc.y) / 2.0f * windowHeight ); // 设置正交投影和视图矩阵 glm::mat4 orthoProj = glm::ortho(0.0f, (float)windowWidth, 0.0f, (float)windowHeight, -1.0f, 1.0f); axisShader.setMat4("projection", orthoProj); axisShader.setMat4("view", glm::mat4(1.0f)); // 视图矩阵设为单位矩阵,因为在屏幕空间 // 模型矩阵:把轴移动到屏幕位置,设置固定像素长度 glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(screenPos.x, screenPos.y, 0.0f)); model = glm::scale(model, glm::vec3(50.0f, 50.0f, 50.0f)); // 轴长50像素 axisShader.setMat4("model", model); // 绘制轴的三条线段(X红、Y绿、Z蓝) glBindVertexArray(axisVAO); axisShader.setVec3("color", glm::vec3(1.0f, 0.0f, 0.0f)); glDrawArrays(GL_LINES, 0, 2); axisShader.setVec3("color", glm::vec3(0.0f, 1.0f, 0.0f)); glDrawArrays(GL_LINES, 2, 2); axisShader.setVec3("color", glm::vec3(0.0f, 0.0f, 1.0f)); glDrawArrays(GL_LINES, 4, 2);
方案2:3D空间动态缩放(保留3D位置感)
如果你希望轴是在3D空间中(比如和物体的局部坐标系绑定,看起来是物体的一部分),但视觉大小恒定,那需要根据相机到物体的距离动态计算缩放因子:
- 计算相机(eye)到选中物体世界坐标的直线距离
distance。 - 根据屏幕的垂直视场角
fovY(弧度)、屏幕高度screenHeight,以及你想要的轴在屏幕上的目标像素长度targetPixelLength,计算缩放因子:
这个公式的原理是利用相似三角形,把屏幕上的像素长度转换为3D空间中的长度,距离越远,缩放比例越大,刚好抵消透视带来的缩小效果。float scale = (targetPixelLength / screenHeight) * distance * tan(fovY / 2.0f) * 2.0f; - 绘制轴的时候,把这个缩放因子应用到轴的模型矩阵上,这样轴在3D空间中会随距离放大,最终在屏幕上的大小保持恒定。
内容的提问来源于stack exchange,提问作者Alex Gardner




