现代 OpenGL 投影视图模型转换不起作用

Posted

技术标签:

【中文标题】现代 OpenGL 投影视图模型转换不起作用【英文标题】:Modern OpenGL Projection View Model transformation not working 【发布时间】:2014-04-17 07:59:16 【问题描述】:

我试图在我的着色器中只使用一个转换(及其逆转置),但我的着色结果很奇怪,所以我猜我的法线转换不正确。网上很多教程建议使用“投影*视图*模型”三阶段变换,在“视图*模型”变换阶段计算光照。于是我尝试实现了这三个变换,但是不仅我的茶壶不显示,连一个简单的三角形都显示不出来。

这是我的顶点着色器代码:

#version 410

in vec3 position;  // position of the vertex (and fragment) in world space
in vec3 normal;  // surface normal vector in world space

uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
uniform mat4 modelView_it;

vec3 l_pos = vec3(10, 10, 10);

out VertexData 
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
 VertexOut;

void main()

    vec4 pos = view * model * vec4(position, 1.0);

    VertexOut.normal = normalize(vec3(modelView_it * vec4(normal, 0.0)));
    VertexOut.lightDir = l_pos - vec3(pos);
    VertexOut.eye = vec3(0, 0, 0);

    gl_Position = proj * view * model * vec4(position, 1);

这是我的片段着色器代码

#version 150

out vec4 colorOut;

vec3 kd = vec3(0.1, 1, 0.1);
vec3 ka = vec3(1, 1.0, 1.0);
vec3 ks = vec3(0.5, 1, 0.5);
float sp = 90;
vec3 intensity = vec3(1, 1, 1);

in VertexData 
    vec3 normal;
    vec3 eye;
    vec3 lightDir;
 VertexIn;

void main()

    vec3 n = normalize(VertexIn.normal);
    vec3 l = normalize(VertexIn.lightDir);
    vec3 e = normalize(VertexIn.eye);

    vec3 diffuse = kd*intensity*max(0, dot(l, n));
    vec3 r = -l + 2*dot(l,n) * n;
    vec3 specular = ks*intensity*max(pow(dot(r, e), sp),0);

// force to be white intentionally to test
    colorOut = vec4(1,1,1,1);

这是我用来生成投影和视图转换矩阵的一些函数(代码取自其他一些类似的堆栈溢出问题)

//generate projection matrix
void setPerspective(float fovY, float aspect, float near, float far, Matrix4f& mProjectionMatrix)

    float theta = fovY*0.5;
    float range = far - near;
    float invtan = 1./tan(theta);

    mProjectionMatrix(0,0) = invtan / aspect;
    mProjectionMatrix(1,1) = invtan;
    mProjectionMatrix(2,2) = -(near + far) / range;
    mProjectionMatrix(3,2) = -1;
    mProjectionMatrix(2,3) = -2 * near * far / range;
    mProjectionMatrix(3,3) = 0;

//generate view matrix
void lookAt(const Vector& position, const Vector& target, const Vector& up, Matrix4f& mViewMatrix)

    Matrix3f R;
    R.col(2) = (position-target).normalized();
    R.col(0) = up.cross(R.col(2)).normalized();
    R.col(1) = R.col(2).cross(R.col(0));
    mViewMatrix.topLeftCorner<3,3>() = R.transpose();
    mViewMatrix.topRightCorner<3,1>() = -R.transpose() * position;

这是我如何创建和设置制服

// Create transformation matrices
Transform3fAffine trans = IdentityTransform();
Matrix4f viewMat = Matrix4f::Zero();
Matrix4f projMat = Matrix4f::Zero();
setPerspective(45.0f, //field of view
               4.0f / 3.0f, //aspect ratio
               0.1f, //near clipping
               100.0f, //far clipping
               projMat); //matrix
lookAt(Vector(3,3,3), //camera position
       Vector(0,0,0), //target to look at
       Vector(0,1,0), //up vector
       viewMat);
GLint viewID = glGetUniformLocation(shaderProgram, "view");
GLint modelID = glGetUniformLocation(shaderProgram, "model");
GLint projID = glGetUniformLocation(shaderProgram, "proj");
GLint mvIT_ID = glGetUniformLocation(shaderProgram, "modelView_it");
transformModel(modelID, mvIT_ID, trans.matrix(), viewMat);
transformView(viewID, mvIT_ID, viewMat, trans.matrix());
transformProj(projID, projMat);

那些“transformXXX”函数看起来像这样

void transformModel(GLint& modelID, GLint& mvIT_ID, Matrix4f& modelMat, Matrix4f& viewMat) 
    glUniformMatrix4fv(modelID, 1, GL_FALSE, modelMat.data());
    glUniformMatrix4fv(mvIT_ID, 1, GL_FALSE, (viewMat*modelMat).inverse().transpose().data());


void transformView(GLint& viewID, GLint& mvIT_ID, Matrix4f& viewMat, Matrix4f& modelMat) 
    glUniformMatrix4fv(viewID, 1, GL_FALSE, viewMat.data());
    glUniformMatrix4fv(mvIT_ID, 1, GL_FALSE, (viewMat*modelMat).inverse().transpose().data());


void transformProj(GLint& projID, Matrix4f& projMat) 
    glUniformMatrix4fv(projID, 1, GL_FALSE, projMat.data());

尽管如此,屏幕还是黑的。甚至没有在原点使用简单的三角形。如果我在我的顶点着色器中去掉proj * view *(基本上只有“模型”是唯一的转换),我就可以看到我的形状并使用键盘事件来很好地转换它。这意味着我的基本管道不应该是问题,因为我能够看到形状。一旦我在“模型”之前添加“proj * view *”,一切都会变黑。

请帮忙!

更新:现在我发现了一个问题:glUniformMatrix4fv 给了我GL_INVALID_ENUM 错误,它根本没有设置统一变量。 (我通过将矩阵硬编码到着色器中来验证这一点)。我几乎找不到任何在线问题,而且似乎没有人使用此功能收到 INVALID_ENUM 错误。有什么想法吗?

【问题讨论】:

【参考方案1】:

transformXXX 函数看起来很奇怪。为什么有3个不同的?通常模型、视图和投影转换是一起进行的。并且transformModelandtransformView`都设置了modelview_IT矩阵。

通常您不会将模型和视图转换矩阵作为单独的制服传递。对于没有得到很好优化的着色器代码,这将强制对每个顶点执行 4×4 矩阵-矩阵乘法,结果可以很容易地预先计算。

您确实应该将transformModeltransformView 合并为一个transformModelView 函数。

另一件看起来很奇怪的事情是,您是从零矩阵开始的。通常你用 identity 初始化矩阵并从那里开始工作。

【讨论】:

好的,发现一个潜在的问题:glUniformMatrix4fv 在我的transformXXX 中总是得到GL_INVALID_ENUM 错误,并且默默地失败(因此甚至没有设置制服开始,我也通过硬编码验证了这一点着色器内的矩阵,原来没有设置制服)。你知道为什么会这样吗? @Dovizu:你是怎么找回错误的(无效的枚举不是glUniform…产生的错误)?一个鲜为人知的事实是 OpenGL 错误会累积。您必须循环调用glGetError,直到它返回GL_NO_ERROR。您以前可能因 OpenGL 误用而积累了一些其他错误,而且您还看到了这些错误。 我试图遍历错误,我得到的唯一一个是 0x500 INVALID_ENUM。所以你建议 INVALID_ENUM 不是glUniformMatrix4fv 产生的错误。那么除此之外,你知道为什么当我将矩阵硬编码到着色器中时,屏幕上会出现一些东西(只是闪烁,至少不是黑色),如果我在代码中设置这些制服,它就不起作用了吗? @Dovizu:我最好的选择是,您之前进行的一个 OpenGL 调用失败,这会触发一系列失败,最终阻止您设置制服。为每个 OpenGL 调用添加错误检查,以便查看失败的地方。 @Dovizu:哦,既然您使用的是类:请确保在调用这些方法时线程中的 OpenGL 上下文确实处于活动状态。特别是通过构造函数调用的代码有时会被调用,这对新手来说是相当不直观的。【参考方案2】:

矩阵运算顺序让很多人难以置信,我就是其中之一

理论上,您希望对顶点进行建模、查看、投影和放置到屏幕上。代码看起来就是这样做的。

你可能想看看你的投影矩阵

mProjectionMatrix(3,2) = -1;
mProjectionMatrix(2,3) = -2 * near * far / range;
mProjectionMatrix(3,3) = 0;

并尝试转置它。

mProjectionMatrix(2,3) = -1;
mProjectionMatrix(3,2) = -2 * near * far / range;
mProjectionMatrix(3,3) = 0;

您可以做的另一件事是将您的矩阵与 gl 实现进行比较,方法是使用gluPerspective 设置它们,然后通过glGet 获取它们。

【讨论】:

在模型和世界空间中发生的模型-视图转换应该在整个场景被压缩到视锥体之前发生,这并不令人难以置信。如果你想要一个你建议的参考实现,我会使用 GLM。

以上是关于现代 OpenGL 投影视图模型转换不起作用的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL:光照模型视图投影变换

WebGL或OpenGL关于模型视图投影变换的设置技巧

OpenGL的视图变换模型变换投影变换视口变换

Android OpenGL20 模型,视图,投影与Viewport <7>

模型,视图和投影矩阵不希望对opengl glsl有效

如何在 OpenGL ES 2.0 中将视图/模型/投影矩阵传递给我的顶点着色器?