假设我的屏幕是(800*600)并且我使用Triangle_Strip(在NDC中)使用以下顶点位置绘制了Quad(2D ):
float[] vertices = {-0.2f,0.2f,-0.2f,-0.2f,0.2f,0.2f,0.2f,-0.2f};
Run Code Online (Sandbox Code Playgroud)
我以这种方式设置了转换矩阵:
Vector2f position = new Vector2f(0,0);
Vector2f size = new Vector2f(1.0f,1.0f);
Matrix4f tranMatrix = new Matrix4f();
tranMatrix.setIdentity();
Matrix4f.translate(position, tranMatrix, tranMatrix);
Matrix4f.scale(new Vector3f(size.x, size.y, 1f), tranMatrix, tranMatrix);
Run Code Online (Sandbox Code Playgroud)
我的顶点着色器:
#version 150 core
in vec2 in_Position;
uniform mat4 transMatrix;
void main(void) {
gl_Position = transMatrix * vec4(in_Position,0,1.0);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,我应该使用哪个公式来修改我的四边形的坐标转换(以像素为单位)?
例如 :
- 设置比例:(50px,50px)=>Vector2f(width,height)
- 设置位置:(100px,100px)=>Vector2f(x,y)
为了更好地理解,我将创建一个函数将我的像素数据转换为NDC,将它们发送到顶点着色器旁边.我被建议使用正交投影,但我不知道如何正确创建它,你可以看到我的顶点着色器我不使用任何投影矩阵.谢谢 !
这是一个类似于我的主题,但不是很清楚:
我按照公式创建了我的正交投影矩阵但似乎没有出现,这是我继续进行的方式:
public static Matrix4f glOrtho(float left, float right, float bottom, float top, float near, float far){
final Matrix4f matrix = new Matrix4f();
matrix.setIdentity();
matrix.m00 = 2.0f / (right - left);
matrix.m01 = 0;
matrix.m02 = 0;
matrix.m03 = 0;
matrix.m10 = 0;
matrix.m11 = 2.0f / (top - bottom);
matrix.m12 = 0;
matrix.m13 = 0;
matrix.m20 = 0;
matrix.m21 = 0;
matrix.m22 = -2.0f / (far - near);
matrix.m23 = 0;
matrix.m30 = -(right+left)/(right-left);
matrix.m31 = -(top+bottom)/(top-bottom);
matrix.m32 = -(far+near)/(far-near);
matrix.m33 = 1;
return matrix;
}
Run Code Online (Sandbox Code Playgroud)
然后我将我的矩阵包含在顶点着色器中
#version 140
in vec2 position;
uniform mat4 projMatrix;
void main(void){
gl_Position = projMatrix * vec4(position,0.0,1.0);
}
Run Code Online (Sandbox Code Playgroud)
我错过了什么 ?
在评论中作出澄清之后,被问到的问题可归纳为:
如何在GUI中有效地转换四边形像素?
正如原始问题中所提到的,最简单的方法是使用正交投影.什么是正交投影?
一种投影方法,其中描绘对象或使用平行线将其形状投影到平面上的表面.
在实践中,您可能会将其视为2D投影.距离不起作用,OpenGL坐标映射到像素坐标.有关更多信息,请参阅此答案.
通过使用正交投影而不是透视投影,您可以开始考虑像素方面的所有变换.
不是将四边形定义为(25 x 25)维度中的世界单位,而是维度中的(25 x 25)像素.
或者不是50沿着世界x轴的世界单位进行平移,而是50沿着屏幕x轴(向右)的像素进行平移.
那么如何创建正交投影?
首先,它们通常使用以下参数定义:
left - 左垂直剪裁平面的X坐标right - 右垂直剪裁平面的X坐标bottom - 底部水平剪裁平面的Y坐标top - Y顶部水平剪裁平面的坐标near - 近深度剪切平面far - 远深度剪裁平面请记住,所有单位都以像素为单位.典型的正交投影将定义为:
glOrtho(0.0, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Run Code Online (Sandbox Code Playgroud)
假设您没有(或不能)使用glOrtho(您有自己的Matrix课程或其他原因),那么您必须自己计算正交投影矩阵.
正交矩阵定义为:
2/(r-l) 0 0 -(r+l)/(r-l)
0 2/(t-b) 0 -(t+b)/(t-b)
0 0 -2/(f-n) -(f+n)/(f-n)
0 0 0 1
Run Code Online (Sandbox Code Playgroud)
此时我建议使用预先制作的数学库,除非您决定使用自己的数学库.我在实践中看到的最常见的bug源之一是矩阵相关的,调试矩阵的时间越少,你就越需要专注于其他更有趣的工作.
GLM是一个广泛使用和受尊重的库,用于建模GLSL功能.GLM的实施glOrtho可以看出这里的线100.
如何使用正交投影?
正交投影通常用于在3D场景的顶部渲染GUI.使用以下模式可以很容易地完成此操作:
请注意,这回答了错误的问题.它假设问题归结为"我如何从屏幕空间转换为NDC空间?".如果有人搜索这个问题寻找答案,那就留下了.
目标是从屏幕空间转换为NDC空间.因此,让我们首先定义这些空间是什么,然后我们可以创建转换.
标准化设备坐标
NDC空间只是在剪辑空间中对顶点执行透视分割的结果.
clip.xyz /= clip.w
Run Code Online (Sandbox Code Playgroud)
clip剪辑空间中的坐标在哪里.
这样做是将所有未剪裁的顶点放入一个单位立方体([-1, 1]在所有轴的范围内),屏幕中心位于(0, 0, 0).任何被剪裁的顶点(位于视锥体之外)都不在此单位立方体内并被GPU抛弃.
在OpenGL中,此步骤作为原始组件的一部分自动完成(D3D11在光栅化器阶段执行此操作).
屏幕坐标
通过将标准化坐标扩展到视口的范围,可以简单地计算屏幕坐标.
screen.x = ((view.w * 0.5) * ndc.x) + ((w * 0.5) + view.x)
screen.y = ((view.h * 0.5) * ndc.y) + ((h * 0.5) + view.y)
screen.z = (((view.f - view.n) * 0.5) * ndc.z) + ((view.f + view.n) * 0.5)
Run Code Online (Sandbox Code Playgroud)
哪里,
screen 是屏幕空间中的坐标ndc 是规范化空间中的坐标view.x 是视口x原点view.y 是视口的起源view.w 是视口宽度view.h 是视口高度view.f 是视口远view.n 是靠近的视口从屏幕转换为NDC
由于我们有从NDC到屏幕的转换,因此很容易计算出反向.
ndc.x = ((2.0 * screen.x) - (2.0 * x)) / w) - 1.0
ndc.y = ((2.0 * screen.y) - (2.0 * y)) / h) - 1.0
ndc.z = ((2.0 * screen.z) - f - n) / (f - n)) - 1.0
Run Code Online (Sandbox Code Playgroud)
例:
viewport (w, h, n, f) = (800, 600, 1, 1000)
screen.xyz = (400, 300, 200)
ndc.xyz = (0.0, 0.0, -0.599)
screen.xyz = (575, 100, 1)
ndc.xyz = (0.4375, -0.666, -0.998)
Run Code Online (Sandbox Code Playgroud)
进一步阅读
有关所有变换空间的更多信息,请阅读OpenGL Transformation.
编辑评论
在对原始问题的评论中,Bo将屏幕空间原点指定为左上角.
对于OpenGL,视口原点(以及屏幕空间原点)位于左下角.请参阅glViewport.
如果您的像素坐标是真正的左上原点则需要改造时,要考虑screen.y到ndc.y.
ndc.y = 1.0 - ((2.0 * screen.y) - (2.0 * y)) / h)
Run Code Online (Sandbox Code Playgroud)
如果您正在将屏幕/ gui上的鼠标单击坐标转换为NDC空间(作为完全转换为世界空间的一部分),则需要这样做.