当 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 以防万一。
该代码是实际着色器的简化版本,预期图像为:
但我得到的是:
代码的随机轮换完全消除了伪影。例如,如果我更改行
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/