在OpenGL中模拟定向光时的问题

时间:2016-01-16 02:10:19

标签: opengl glsl shader qt5

我正在使用QT5 Gui框架开发OpenGL应用程序,但是,我不是OpenGL的专家,在尝试模拟定向光时我遇到了一些问题。我使用的“几乎”算法与我在WebGL应用程序中使用的算法完全相同。

应用程序用于渲染大网格块的多个相邻单元格(每个单元格由8个独立顶点表示),这意味着整个网格块的某些顶点在VBO中重复。在几何着色器中按面部计算法线,如下面的代码所示。

QOpenGLWidget paintGL()body。

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);        

    m_camera = camera.toMatrix(); 
    m_world.setToIdentity(); 

    m_program->bind();
    m_program->setUniformValue(m_projMatrixLoc, m_proj);
    m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world); 

    QMatrix3x3 normalMatrix = (m_camera * m_world).normalMatrix();
    m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);

    QVector3D lightDirection = QVector3D(1,1,1);
    lightDirection.normalize();
    QVector3D directionalColor = QVector3D(1,1,1);
    QVector3D ambientLight = QVector3D(0.2,0.2,0.2);
    m_program->setUniformValue(m_lightDirectionLoc, lightDirection);
    m_program->setUniformValue(m_directionalColorLoc, directionalColor);
    m_program->setUniformValue(m_ambientColorLoc, ambientLight);

    geometries->drawGeometry(m_program);
    m_program->release();
    }
}

顶点着色器

#version 330
layout(location = 0) in vec4 vertex; 
uniform mat4 projMatrix;
uniform mat4 mvMatrix;

void main()
{
  gl_Position = projMatrix * mvMatrix * vertex;
}

几何着色器

#version 330
layout ( triangles ) in;
layout ( triangle_strip, max_vertices = 3 ) out;
out vec3 transformedNormal;
uniform mat3 normalMatrix;

void main()
{
    vec3 A = gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz;
    vec3 B = gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz;

    gl_Position = gl_in[0].gl_Position;
    transformedNormal = normalMatrix * normalize(cross(A,B));
    EmitVertex();
    gl_Position = gl_in[1].gl_Position;
    transformedNormal = normalMatrix * normalize(cross(A,B));
    EmitVertex(); 
    gl_Position = gl_in[2].gl_Position;
    transformedNormal = normalMatrix * normalize(cross(A,B));
    EmitVertex();
    EndPrimitive();
}

片段着色器

#version 330
in vec3 transformedNormal;
out vec4 fColor;
uniform vec3 lightDirection;
uniform vec3 ambientColor;
uniform vec3 directionalColor;

void main()
{
        highp float directionalLightWeighting = max(dot(transformedNormal, lightDirection), 0.0);
        vec3 vLightWeighting = ambientColor + directionalColor * directionalLightWeighting;
        highp vec3 color = vec3(1, 1, 0.0);
        fColor = vec4(color*vLightWeighting, 1.0);
}

第一个问题是,无论何时相机角度发生变化(相机位置不影响它,只影响角度),脸部的光线似乎都会发生变化。您可以在以下快照中看到此行为。我的猜测是我在计算普通矩阵时做错了什么,但我无法弄清楚它是什么。 enter image description here

第二个问题(导致我头疼的问题)是每当相机移动时,单元格的边缘显示出当相机移动时闪烁的块状和装配线。当有太多细胞聚集在一起时,这种效果会变得非常糟糕。

enter image description here

快照中使用的模型只是10个单元格的样本板,以更好地说明故障效果。实际模型(网格块)包含多达200K的单元格堆叠在一起。

  

编辑 :第二期解决方案。   我当时正在使用0.01f和50000.0f的znear/zfar   将znear更改为1.0f,此效果消失了。根据{{​​3}},这是由zNear剪裁平面值太接近0.0引起的。随着zNear剪切平面越来越接近0.0,深度缓冲区的有效精度会急剧下降

     

EDIT2 :我尝试按照评论中的建议调试绘制法线,   我很快意识到我可能不应该根据它来计算它们   gl_Position(在VS中的MVP矩阵乘法之后)我应该使用   原始顶点位置,所以我修改着色器如下:

顶点着色器(更新)

#version 330
layout(location = 0) in vec4 vertex; 
out vec3 vert;
uniform mat4 projMatrix;
uniform mat4 mvMatrix;

void main()
{
  vert = vertex.xyz;
  gl_Position = projMatrix * mvMatrix * vertex;
}

几何着色器(更新)

#version 330
layout ( triangles ) in;
layout ( triangle_strip, max_vertices = 3 ) out;
in vec3 vert [];
out vec3 transformedNormal;
uniform mat3 normalMatrix;

void main()
{
    vec3 A = vert[2].xyz - vert[0].xyz;
    vec3 B = vert[1].xyz - vert[0].xyz;

    gl_Position = gl_in[0].gl_Position;
    transformedNormal = normalize(normalMatrix * normalize(cross(A,B)));
    EmitVertex();
    gl_Position = gl_in[1].gl_Position;
    transformedNormal = normalize(normalMatrix * normalize(cross(A,B)));
    EmitVertex(); 
    gl_Position = gl_in[2].gl_Position;
    transformedNormal = normalize(normalMatrix * normalize(cross(A,B)));
    EmitVertex();
    EndPrimitive();
}

但即使经过这次修改,表面的法线仍会随着摄像机角度的变化而变化,如下图所示。我不知道正常的计算是错误的还是正常的矩阵计算错误或者两者都错...

OpenGL Wiki

  

EDIT3:第一期解决方案:改变GS中的正常计算   transformedNormal = normalize(normalMatrix * normalize(cross(A,B)));   到transformedNormal = normalize(cross(A,B));似乎解决了   问题。从计算中省略normalMatrix固定了   问题和法线不会随着视角而改变。

如果我遗漏了任何重要/相关信息,请在评论中通知我。

1 个答案:

答案 0 :(得分:1)

  1. 深度缓冲精度

    深度缓冲区通常存储为16位或24位缓冲区。它是规范化到特定范围的float的HW实现。因此,与标准float相比,您可以看到尾数/指数的位数非常少。

    如果我过度简化事物并假设integer值而不是float ,那么对于16位缓冲区,您获得了2^16个值。如果你得到znear=0.1zfar=50000.0那么你在整个范围内只得到了65535个值。现在,由于深度值是非线性的,因此znear附近的精度更高,zfar平面附近的更多更高,因此深度值会随着越来越高的步长而跳跃,从而导致精度问题任何2个多边形都在附近。

    我凭经验得到这个用于在我的视图中设置飞机:

    • (zfar-znear)/desired_accuracy_step > 0.3*(2^n)

    其中n是深度缓冲区的位宽,desired_accuracy_step是我需要的Z轴所需的分辨率。有时我看到它以znear值交换。