OpenGL中奇怪的方形光照伪影

7
我有一个程序可以生成高度图并使用OpenGL显示为网格。当我试图添加光照时,会出现奇怪的方形覆盖在网格上。它们在某些区域比其他地方更明显,但总是存在的。
我曾经使用四边形网格,但在切换到三角网格后仍然没有变化。我使用了至少三种不同的方法来计算顶点法线,但都产生了相同的效果。我以前是手动使用着色器进行光照,但使用OpenGL内置的光照系统时也没有任何变化。
以下是我的最新法线生成代码(faces是索引数组,指向顶点数组verts):
int i;
for (i = 0; i < NINDEX; i += 3) {
    vec v[3];
    v[0] = verts[faces[i + 0]];
    v[1] = verts[faces[i + 1]];
    v[2] = verts[faces[i + 2]];

    vec v1 = vec_sub(v[1], v[0]);
    vec v2 = vec_sub(v[2], v[0]);

    vec n = vec_norm(vec_cross(v2, v1));

    norms[faces[i + 0]] = vec_add(norms[faces[i + 0]], n);
    norms[faces[i + 1]] = vec_add(norms[faces[i + 1]], n);
    norms[faces[i + 2]] = vec_add(norms[faces[i + 2]], n);
}

for (i = 0; i < NVERTS; i++) {
    norms[i] = vec_norm(norms[i]);
}

虽然这不是我使用的唯一代码,所以我怀疑它并不是问题的原因。

我使用以下代码绘制网格:

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, verts);

glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, norms);

glDrawElements(GL_TRIANGLES, NINDEX, GL_UNSIGNED_SHORT, faces);

我当前没有使用任何着色器。

可能是什么原因导致了这个问题?

编辑:更全面的截图如下:

对于最后一张图,着色器代码为:

顶点着色器:

varying vec3 lightvec, normal;

void main() {
    vec3 lightpos = vec3(0, 0, 100);
    vec3 v = vec3(gl_ModelViewMatrix * gl_Vertex);
    normal = gl_NormalMatrix * gl_Normal;

    lightvec = normalize(lightpos - v);

    gl_Position = ftransform();
}

Fragment:

varying vec3 lightvec, normal;

void main(void) {
    float l = dot(lightvec, normal);
    gl_FragColor = vec4(l, l, l, 1);
} 

你能否发布同一场景的两张截图,一张带线框,一张不带? - Stefan Monov
norms 是否被初始化为零?在将向量累加到 norms 中之前,您没有先将其清零,但如果程序的其他地方已经将其清零,则不应该有问题。 - genpfault
你在着色器中的普通属性使用了平滑插值吗?也许暂时切换到“flat”可以帮助定位问题。此外,OpenGL的光照对我来说经常出现很多错误 - 我通常更加确定自己编写的代码。 - Bartek Banachewicz
@genpfault:在初始化时它被清零。 - sigill
2个回答

2

您需要在片段着色器中对法线进行规范化,如下所示:

varying vec3 lightvec, normal;

void main(void) {
    vec3 normalNormed = normalize(normal);
    float l = dot(lightvec, normalNormed);
    gl_FragColor = vec4(l, l, l, 1);
} 

但这可能会很昂贵。在这种情况下,使用定向光源,也可以使用顶点照明。因此,在顶点着色器中计算光照值。

varying float lightItensity;

void main() {
    vec3 lightpos = vec3(0, 0, 100);
    vec3 v = vec3(gl_ModelViewMatrix * gl_Vertex);
    normal = gl_NormalMatrix * gl_Normal;

    lightvec = normalize(lightpos - v);

    lightItensity = dot(normal, lightvec);

    gl_Position = ftransform();
}

并将其用于片元着色器中,

varying float light;

void main(void) {
    float l = light;
    gl_FragColor = vec4(l, l, l, 1);
} 

我希望这个能解决问题,如果不能,请告诉我。

编辑:这里有一个小图表,解释了最可能发生的情况。

enter image description here

编辑2:

如果这样做没有帮助,请添加更多的三角形。对你的高度图进行插值,并在其中添加一些顶点。

或者,尝试更改你的细分方案。例如,像这样的等边三角形网格可以使伪影不那么明显。

enter image description here

你需要在你的高度图上进行一些插值。

否则我也不知道怎么办了..祝好运!


谢谢,但不幸的是我已经尝试过类似的操作了。我开始怀疑问题出现在我的显卡上(1.5年前的英特尔集成芯片)。 - sigill
我非常怀疑。着色器代码应该在任何支持它的图形卡上执行相同的方式。 - Hannesh

1

对于非着色器版本,我没有明确的答案,但我想补充一下,如果您在片段着色器中进行每像素光照,则应该在片段着色器内规范化法线和光向量。

如果您不这样做,它们可能不是单位长度(两个归一化向量之间的线性插值不一定是归一化的)。这可能解释了您在着色器版本中看到的一些伪影,因为点积的大小会随着距离顶点的距离而变化,这有点像您所看到的。

编辑:另一个想法是,在渲染非着色器版本时,您是否进行任何非均匀缩放(不同的x、y、z)?如果您进行了缩放,则需要通过反比例缩放因子修改法线,或设置glEnable(GL_NORMALIZE)。更多信息请参见此处: http://www.lighthouse3d.com/tutorials/glsl-tutorial/normalization-issues/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接