如何在opengl中翻译屏幕中的投影对象
How to translate the projected object in screen in opengl
我渲染了一个 3d 对象,它在图像中的 2d 投影是正确的。但是现在我想将 2d 投影对象移动一些像素。我该如何实现?
请注意,简单地平移 3d 对象是行不通的,因为在透视投影下,2d 投影对象可能会发生变化。我的目标是只移动图像中的二维对象而不改变其形状和大小。
如果您使用的是可编程管道,则可以在应用投影变换后应用转换。
唯一需要注意的是应用投影矩阵后变换后的坐标有一个w
坐标将用于透视分割。要使屏幕中的额外翻译量保持不变 space,您必须将其乘以 w
。顶点着色器的关键片段如下所示:
in vec4 Position;
uniform mat4 ModelViewProjMat;
uniform vec2 TranslationOffset;
void main() {
gl_Position = ModelViewProjMat * Position;
gl_Position.xy += TranslationOffset * gl_Position.w;
}
透视除以w
后,会得到一个固定的偏移。
同时使用可编程和固定管线的另一种可能性是移动视口。假设 window 大小是 vpWidth
倍 vpHeight
,并且您要应用的偏移量是 (xOffset, yOffset)
,您可以将视口设置为:
glViewport(xOffset, yOffset, vpWidth + xOffset, vpHeight + yOffset);
这里需要注意的是,几何体仍将被相同的视体积裁剪,但只会在应用裁剪后通过视口变换进行移动。如果几何体完全适合原始视口,则可以正常工作。但是,如果几何体最初会被裁剪,它仍将被同一平面裁剪,即使在应用偏移后它实际上可能位于 window 内。
作为 Reto Koradi 答案的补充:您不需要着色器,也不需要修改您使用的视口(答案中提到的裁剪问题)。您可以通过 预乘 一些平移(实际上将在投影变换后最后应用)来简单地修改投影矩阵:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranstlate(x,y,z); // <- this one was added
glFrustum(...) or gluPerspective(...) or whatever you use
glFrustum
和 gluPerspective
会将当前矩阵乘以它们构建的投影变换矩阵,这就是为什么人们通常首先加载身份。但是,它不一定必须是身份,这种情况是应该加载其他内容的罕见情况之一。
由于您想要以像素为单位移动,但该变换应用于剪辑 space,因此您需要进行一些单位转换。由于剪辑 space 只是规范化设备 space 的同质表示,其中平截头体在所有 3 个维度上都是 [-1,1](因此视口在 space),您可以使用以下内容:
glTranslate(x * 2.0f/viewport_width, y * 2.0f/viewport_height, 0.0f);
将输出偏移 (x,y)
像素。
请注意,虽然我为固定功能 GL 编写了此代码,但数学当然也适用于着色器,您可以以相同的方式简单地修改着色器使用的投影矩阵。
我渲染了一个 3d 对象,它在图像中的 2d 投影是正确的。但是现在我想将 2d 投影对象移动一些像素。我该如何实现?
请注意,简单地平移 3d 对象是行不通的,因为在透视投影下,2d 投影对象可能会发生变化。我的目标是只移动图像中的二维对象而不改变其形状和大小。
如果您使用的是可编程管道,则可以在应用投影变换后应用转换。
唯一需要注意的是应用投影矩阵后变换后的坐标有一个w
坐标将用于透视分割。要使屏幕中的额外翻译量保持不变 space,您必须将其乘以 w
。顶点着色器的关键片段如下所示:
in vec4 Position;
uniform mat4 ModelViewProjMat;
uniform vec2 TranslationOffset;
void main() {
gl_Position = ModelViewProjMat * Position;
gl_Position.xy += TranslationOffset * gl_Position.w;
}
透视除以w
后,会得到一个固定的偏移。
同时使用可编程和固定管线的另一种可能性是移动视口。假设 window 大小是 vpWidth
倍 vpHeight
,并且您要应用的偏移量是 (xOffset, yOffset)
,您可以将视口设置为:
glViewport(xOffset, yOffset, vpWidth + xOffset, vpHeight + yOffset);
这里需要注意的是,几何体仍将被相同的视体积裁剪,但只会在应用裁剪后通过视口变换进行移动。如果几何体完全适合原始视口,则可以正常工作。但是,如果几何体最初会被裁剪,它仍将被同一平面裁剪,即使在应用偏移后它实际上可能位于 window 内。
作为 Reto Koradi 答案的补充:您不需要着色器,也不需要修改您使用的视口(答案中提到的裁剪问题)。您可以通过 预乘 一些平移(实际上将在投影变换后最后应用)来简单地修改投影矩阵:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranstlate(x,y,z); // <- this one was added
glFrustum(...) or gluPerspective(...) or whatever you use
glFrustum
和 gluPerspective
会将当前矩阵乘以它们构建的投影变换矩阵,这就是为什么人们通常首先加载身份。但是,它不一定必须是身份,这种情况是应该加载其他内容的罕见情况之一。
由于您想要以像素为单位移动,但该变换应用于剪辑 space,因此您需要进行一些单位转换。由于剪辑 space 只是规范化设备 space 的同质表示,其中平截头体在所有 3 个维度上都是 [-1,1](因此视口在 space),您可以使用以下内容:
glTranslate(x * 2.0f/viewport_width, y * 2.0f/viewport_height, 0.0f);
将输出偏移 (x,y)
像素。
请注意,虽然我为固定功能 GL 编写了此代码,但数学当然也适用于着色器,您可以以相同的方式简单地修改着色器使用的投影矩阵。