c++ - GLSL NVidia 方形工件

标签 c++ opengl glsl artifacts

当 GLSL 着色器在以下 GPU 上生成不正确的图像时,我遇到了一个问题:
GT 430
GT 770
GTX 570
GTX 760

但在这些上正常工作:
英特尔核芯显卡 2500
英特尔高清 4000
英特尔 4400
GTX 740M
Radeon HD 6310M
Radeon 高清 8850

Shader代码如下:

bool PointProjectionInsideTriangle(vec3 p1, vec3 p2, vec3 p3, vec3 point)
{
  vec3 n = cross((p2 - p1), (p3 - p1));

  vec3 n1 = cross((p2 - p1), n);
  vec3 n2 = cross((p3 - p2), n);
  vec3 n3 = cross((p1 - p3), n);

  float proj1 = dot((point - p2), n1);
  float proj2 = dot((point - p3), n2);
  float proj3 = dot((point - p1), n3);

  if(proj1 > 0.0)
    return false;
  if(proj2 > 0.0)
    return false;
  if(proj3 > 0.0)
    return false;
  return true;
}

struct Intersection
{
    vec3 point;
    vec3 norm;
    bool valid;
};

Intersection GetRayTriangleIntersection(vec3 rayPoint, vec3 rayDir, vec3 p1, vec3 p2, vec3 p3)
{
    vec3 norm = normalize(cross(p1 - p2, p1 - p3));

    Intersection res;
    res.norm = norm;
    res.point = vec3(rayPoint.xy, 0.0);
    res.valid = PointProjectionInsideTriangle(p1, p2, p3, res.point);
    return res;
}

struct ColoredIntersection
{
    Intersection geomInt;
    vec4 color;
};

#define raysCount 15
void main(void)
{
    vec2 radius = (gl_FragCoord.xy / vec2(800.0, 600.0)) - vec2(0.5, 0.5);

    ColoredIntersection ints[raysCount];

    vec3 randomPoints[raysCount];
    int i, j;


    for(int i = 0; i < raysCount; i++)
    {
        float theta = 0.5 * float(i);
        float phi = 3.1415 / 2.0;
        float r = 1.0;
        randomPoints[i] = vec3(r * sin(phi) * cos(theta),  r * sin(phi)*sin(theta), r * cos(phi));

        vec3 tangent = normalize(cross(vec3(0.0, 0.0, 1.0), randomPoints[i]));
        vec3 trianglePoint1 = randomPoints[i] * 2.0 + tangent * 0.2;
        vec3 trianglePoint2 = randomPoints[i] * 2.0 - tangent * 0.2;

        ints[i].geomInt = GetRayTriangleIntersection(vec3(radius, -10.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, 0.0), trianglePoint1, trianglePoint2);
        if(ints[i].geomInt.valid)
        {
            float c = length(ints[i].geomInt.point);
            ints[i].color = vec4(c, c, c, 1.0);
        }
    }

    for(i = 0; i < raysCount; i++)
    {
        for(j = i + 1; j < raysCount; j++)
        {
            if(ints[i].geomInt.point.z < ints[i].geomInt.point.z - 10.0)
            {
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
                ColoredIntersection tmp = ints[j];
                ints[j] = ints[i];
                ints[i] = tmp;
            }
        }
    }

    vec4 resultColor = vec4(0.0, 0.0, 0.0, 0.0);
    for(i = 0; i < raysCount + 0; i++)
    {
        if(ints[i].geomInt.valid)
            resultColor += ints[i].color;
    }

    gl_FragColor = clamp(resultColor, 0.0, 1.0);
}

更新:我已经用内置函数替换了 vector 规范化,并添加了 gl_FragColor claming 以防万一。

该代码是实际着色器的简化版本,预期图像为:
http://img.owely.com/screens/131316/original_owely20140426-19006-1w4w4ye.?1398554177
但我得到的是:
http://img.owely.com/screens/131315/original_owely20140426-18968-a7fuxu.?1398553652

代码的随机轮换完全消除了伪影。例如,如果我更改行

if(ints[i].geomInt.valid) //1

if(ints[i].geomInt.valid == true) //1

这显然不应该以任何方式影响逻辑或完全删除不执行任何操作(标记为 2)的双循环工件消失。请注意,由于条件

,双循环根本不执行任何操作
if(ints[i].geomInt.point.z < ints[i].geomInt.point.z - 10.0)
{
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  return;
  ColoredIntersection tmp = ints[j];
  ints[j] = ints[i];
  ints[i] = tmp;
}

永远无法满足(左右两边的索引是 i,不是 i,j)并且没有 NaN。此代码绝对不执行任何操作,但会以某种方式产生工件。

您可以使用此项目自行测试着色器和演示(完整的 MSVS 2010 项目 + 源代码 + 编译的二进制文件和着色器,使用包含的 SFML):https://dl.dropboxusercontent.com/u/25635148/ShaderTest.zip

我在这个测试项目中使用了 sfml,但这 100% 无关,因为我遇到这个问题的实际项目没有使用这个库。

我想知道的是为什么会出现这些伪影以及如何可靠地避免它们。

最佳答案

我认为您的着色器没有任何问题。 openGL 管道渲染到帧缓冲区。如果您在渲染完成之前使用该帧缓冲区,您通常会得到您所看到的。请记住 glDrawArrays 和类似函数是异步的(该函数在 GPU 完成绘制顶点之前返回。)

这些方形人工制品最常见的用途是将生成的帧缓冲区用作纹理,然后用于进一步渲染。

OpenGL 驱动程序应该跟踪依赖关系并且应该知道如何等待依赖关系完成。

如果您跨线程共享一个帧缓冲区,但是,所有的赌注都关闭了,那么您可能需要使用栅栏同步(glFenceSync)之类的东西来确保一个线程等待另一个线程上发生的渲染线程。

作为一种解决方法,您可能会发现调用 glFinish 甚至 glReadPixels(使用一个像素)可以解决问题。

另请记住,此问题与时间相关,简化着色器很可能会解决此问题。

关于c++ - GLSL NVidia 方形工件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23323667/

相关文章:

c++ - Linux 上的简单服务发现

c++ - 加载难看的输入

c++ - 跳过回溯中最内层的帧

opengl - GLU NURBS - 如何设置控制点权重?

ios - 不使用任何 Apple API 加载 GLSL 着色器

c++ - cpp删除旧指针并重新初始化它

c++ - 为什么这个 OpenGL 旋转也会平移我的对象?

java - LWJGL 旋转物体

c++ - 计算着色器 OpenGL 写入纹理

opengl - 检索着色器存储缓冲区的属性