你的猜测是对的。 OpenGL 驱动程序将四边形细分为两个三角形,其中顶点颜色按重心插值,从而产生您所看到的结果。
解决此问题的常用方法是在片段着色器中“手动”执行插值,这会考虑目标拓扑(在您的情况下是四边形)。或者简而言之,您必须执行重心插值,而不是基于三角形,而是基于四边形。您可能还想应用透视校正。
我现在还没有准备好阅读手头的资源,但我会尽快更新这个答案(实际上可能意味着,我必须自己写)。
Update
首先我们必须理解这个问题:大多数 OpenGL 实现将较高的图元分解为三角形并将它们本地化,即无需进一步了解图元的其余部分,例如一个四边形。所以我们必须自己做这件事。
我就是这样做的。
#version 330 // vertex shader
当然我们还需要平时的制服
uniform mat4x4 MV;
uniform mat4x4 P;
首先我们需要这个着色器执行实例处理的顶点的位置
layout (location=0) in vec3 pos;
接下来我们需要一些顶点属性来描述四边形本身。这意味着它的角位置
layout (location=1) in vec3 qp0;
layout (location=2) in vec3 qp1;
layout (location=3) in vec3 qp2;
layout (location=4) in vec3 qp3;
和颜色
layout (location=5) in vec3 qc0;
layout (location=6) in vec3 qc1;
layout (location=7) in vec3 qc2;
layout (location=8) in vec3 qc3;
我们将它们放入变量中供片段着色器处理。
out vec3 position;
out vec3 qpos[4];
out vec3 qcolor[4];
void main()
{
qpos[0] = qp0;
qpos[1] = qp1;
qpos[2] = qp2;
qpos[3] = qp3;
qcolor[0] = qc0;
qcolor[1] = qc1;
qcolor[2] = qc2;
qcolor[3] = qc3;
gl_Position = P * MV * position;
}
在片段着色器中,我们使用它来实现颜色分量的距离加权:
#version 330 // fragment shader
in vec3 position;
in vec3 qpos[4];
in vec3 qcolor[4];
void main()
{
vec3 color = vec3(0);
以下可以简化组合,但为了清楚起见,我将其写出来:
对于顶点的每个角点,将所有角点的颜色与它们之间的边缘上的位置投影作为混合因子进行混合。
for(int i=0; i < 4; i++) {
vec3 p = position - qpos[i];
for(int j=0; j < 4; j++) {
vec3 edge = qpos[i] - qpos[j];
float edge_length = length(edge);
edge = normalize(edge);
float tau = dot(edge_length, p) / edge_length;
color += mix(qcolor[i], qcolor[j], tau);
}
}
由于我们对每个角点查看了 4 次,因此缩小了 1/4
color *= 0.25;
gl_FragColor = color; // and maybe other things.
}
我们快完成了。在客户端我们需要传递附加信息。当然我们不想重复数据。为此我们使用glVertexBindingDivisor
这样顶点属性仅每 4 个顶点(即四边形)前进,在qp…
and qc…
位置,即位置 1 到 8
typedef float vec3[3];
extern vec3 *quad_position;
extern vec3 *quad_color;
glVertexAttribute(0, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[0]);
glVertexBindingDivisor(1, 4);
glVertexAttribute (1, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[0]);
glVertexBindingDivisor(2, 4);
glVertexAttribute (2, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[1]);
glVertexBindingDivisor(3, 4);
glVertexAttribute (3, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[2]);
glVertexBindingDivisor(4, 4);
glVertexAttribute (4, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[3]);
glVertexBindingDivisor(5, 4);
glVertexAttribute (5, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[0]);
glVertexBindingDivisor(6, 4);
glVertexAttribute (6, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[1]);
glVertexBindingDivisor(7, 4);
glVertexAttribute (7, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[2]);
glVertexBindingDivisor(8, 4);
glVertexAttribute (8, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[3]);
将以上内容放入顶点数组对象是有意义的。使用 VBO 也是有意义的,但随后您必须手动计算偏移大小;因为typedef float vec3
编译器为我们的 ATM 进行数学计算。
完成所有这些设置后,您最终可以独立地细分绘制您的四边形。