c++ - 使用自定义着色器的 OpenGL/ES 渲染 = 网格上的伪影

标签 c++ opengl opengl-es 3d opengl-es-2.0

我正在尝试在屏幕上渲染一个方形平面网格,分辨率为 255 x 255,128 个世界单位——读取 float 。

在 RAM 中分配和填充此网格,将其上传到 V-RAM,绑定(bind)到属性和索引缓冲区,正确调用 glDrawElements 都没有问题,对象正在按预期绘制。

这是我正在处理的水面模拟,顶点高度在每一帧都得到计算和上传。当在水面上“注册”点击时,会产生扰动,然后计算水流并更新法线(考虑从下到上从左到右的流)。

我的问题是,在网格的某些部分,我得到了您可以在下面的图片中看到的渲染(第三次无剔除)。

**THIS** **THIS** **THIS (sans culling)**

值得注意的是,在最后一张图片中,效果似乎从 ViewPort 最左侧的某处开始,并继续向右延伸另一半宽度。这种效果仅在相机向左平移时可见,在某些相对于网格的位置,但在其他位置不可见,并且相机的前向或 ViewMatrix 似乎对伪影没有影响。如果我将相机从一侧滑动到另一侧,我可以注意到工件的原点随着相机移动。这个原点似乎在 ViewPort 的中心,并且高度等于 ViewPort 的高度,但有时只有其宽度的四分之一,其他时候可以达到一半。正如我之前所说,相机的前向/注视 vector 对渲染没有影响。即使从“鸟瞰” View 进行渲染,仍然可以观察到效果。

我的着色器中的 gl_Position = mat4_ModelViewProjection * position; 位似乎存在相机位置干扰问题,或者可能是顶点中的位颜色计算,float dot = dot(N, V); 需要某种(怪物)验证?

这是我的一些代码:

顶点着色器

"attribute vec2 pos;"
"attribute vec2 normal;"
"attribute float height;"

"uniform vec3 sunColor;"
"uniform float ax;"

"varying PRECISION_MEDIUM vec4 waterColor_;"
"varying PRECISION_LOW vec2 tex_coord;"

"const vec3 g_WaterBlue  = vec3( 0.05, 0.25, 0.3 );"
"const vec3 g_WaterGreen = vec3( 0.05, 0.3, 0.2 );"

"vec3 CalculateWaterColor(vec3 V, vec3 N)"
"{"
    "float dot = dot(N, V);"

    "vec3 waterColor = mix(g_WaterGreen, g_WaterBlue, dot);"
    "waterColor = mix(sunColor, waterColor, dot);"
    "waterColor = mix(waterColor, g_WaterBlue, dot);"

    "return waterColor;"
"}"

"void main()"
"{"
    "vec4 position = vec4(pos.x, height, pos.y, 1.0);"

    "vec3 V = camera_pos.xyz + camera_forward.xyz - position.xyz;"
    "V = normalize(V);"

    "vec3 N = vec3(normal.x, 1, normal.y);"
    "N = normalize(N);"

    "waterColor_.xyz = CalculateWaterColor(V, N);"
    "waterColor_.w = 1.0;"//ax * 18 / length(camera_pos.xyz - position.xyz);"

    "gl_Position = mat4_ModelViewProjection * position;"
"}"

平面网格顶点生成

// set accelerators
accel1 = Size / static_cast<float>(Resolution - 1);
accel2 = -Size / 2.0f;

x = accel2;
y = accel2;
z = 1.0f;
// ..

// fill the vertex buffer
p_data = reinterpret_cast<float*>(buf_vertex->DataPointer) - 1;
for (index1 = 0; index1 < Resolution; ++index1)
{
    for (index2 = 0; index2 < Resolution; ++index2)
    {
        *++p_data = x;
        *++p_data = y;

        if (buf_vertex->Dimension == 3)
        {
            *++p_data = z;
        }

        x += accel1;
    }

    x = accel2; // Size / 2
    y += accel1;
}
// ..

平面网格索引生成器

// LEFT to RIGHT, BOTTOM to TOP
for (index1 = 0; index1 < (Resolution - 1); ++index1)
{
    for (index2 = 0; index2 < (Resolution - 1); ++index2)
    {
        // CLOCKWISE
        *++p_data = index2      + index1        * Resolution;
        *++p_data = index2      + (index1 + 1)  * Resolution;
        *++p_data = index2 + 1  + (index1 + 1)  * Resolution;

        *++p_data = index2      + index1        * Resolution;
        *++p_data = index2 + 1  + (index1 + 1)  * Resolution;
        *++p_data = index2 + 1  + index1        * Resolution;

        // COUNTER-CLOCKWISE
        //*++p_data = index2 +     (index1 + 1) * Resolution;
        //*++p_data = index2 +     index1       * Resolution;
        //*++p_data = index2 + 1 + index1       * Resolution;

        //*++p_data = index2 + 1 + index1       * Resolution;
        //*++p_data = index2 + 1 + (index1 + 1) * Resolution;
        //*++p_data = index2 +     (index1 + 1) * Resolution;
        // ..
    }
}

我尝试过的事情:

  • 启用/禁用深度测试
  • 为深度缓冲区大小设置一个更大的值(尝试了 16、32、8——所有值的结果相同)
  • 玩剔除
  • 使用 alpha 混合
  • 深度排序——没用,反正不做混合...
  • 将巨大的世界大小设置为网格 (10000.0f),以避免百万分之一世界单位 (1.0f) 的舍入误差
  • 使用不同的索引缓冲区,以不同的顺序渲染顶点,从上到下,从左到右以及它们各自的逆序——对减弱或加重伪像渲染没有任何影响。

最佳答案

问题在已发布的代码中——完全正确!

问题是这样的:

    // enable blending
    glEnable(GL_BLEND);
    // ..

    glBlendFunc
    (
        GL_SRC_ALPHA,
        GL_ONE_MINUS_SRC_ALPHA
    );

GL_ONE_MINUS_SRC_ALPHA 应该是 GL_ONE 以避免使用此类工件进行绘制。

这里有一些例子可以更好地理解 glBlendFunc 的工作原理。

  1. http://www.quake-1.com/docs/blending.jpg
  2. http://www.andersriggelsen.dk/glblendfunc.php

此外,重要的是,您应该记住在进行混合时 glDisable(GL_DEPTH_TEST)

关于c++ - 使用自定义着色器的 OpenGL/ES 渲染 = 网格上的伪影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35372473/

相关文章:

c++ - OpenGL 文本渲染方法和权衡

c++ - 为什么我必须在基类中定义虚函数?

c++ - 不允许成员函数的非指针类型定义?

java - Box2D 灯光在 scene2d Actor 绘制之间渲染

java - OpenGL : Why isn't discarding fragments the same as setting their alpha to zero?

ios - 使用叠加图像 OpenGLES 保存屏幕截图

java - gluUnProject 的使用

c++ - 需要一个 Qt 解决方法来将一个基础转换为另一个基础

c++ - 在 C++ 中,用于引用 32 位浮点值的关键字是什么?

c - 为什么显示函数被调用两次?