在过去的几天里,我偶然发现了一个特别棘手的错误。我已将代码缩减为一组非常简单直接的示例。
这是我用来调用着色器的处理代码:
PGraphics test;
PShader testShader;
PImage testImage;
void setup() {
size(400, 400, P2D);
testShader = loadShader("test.frag", "vert2D.vert");
testImage = loadImage("test.png");
testShader.set("image", testImage);
testShader.set("size", testImage.width);
shader(testShader);
}
void draw() {
background(0, 0, 0);
shader(testShader);
beginShape(TRIANGLES);
vertex(-1, -1, 0, 1);
vertex(1, -1, 1, 1);
vertex(-1, 1, 0, 0);
vertex(1, -1, 1, 1);
vertex(-1, 1, 0, 0);
vertex(1, 1, 1, 0);
endShape();
}
这是我的顶点着色器:
attribute vec2 vertex;
attribute vec2 texCoord;
varying vec2 vertTexCoord;
void main() {
gl_Position = vec4(vertex, 0, 1);
vertTexCoord = texCoord;
}
当我调用此片段着色器时:
uniform sampler2D image;
varying vec2 vertTexCoord;
void main(void) {
gl_FragColor = texture2D(image, vertTexCoord);
}
我明白了:
这是预期的结果。但是,当我使用以下片段着色器将纹理坐标渲染到红色和绿色通道时:
uniform sampler2D image;
uniform float size;
varying vec2 vertTexCoord;
void main(void) {
gl_FragColor = vec4(vertTexCoord, 0, 1);
}
我明白了:
如您所见,屏幕的大部分是黑色,这表示在这些片段中,纹理坐标为[0,0]。但情况并非如此,因为当传递到texture2D函数时,它们被正确映射到图像中的相应位置。为了验证在这两种情况下都使用了完全相同的纹理坐标值,我将它们与下面的着色器结合起来。
uniform sampler2D image;
uniform float size;
varying vec2 vertTexCoord;
void main(void) {
gl_FragColor = texture2D(image, vertTexCoord) + vec4(vertTexCoord, 0, 0);
}
如果纹理坐标在屏幕上平滑变化,那么这正是您所期望的。所以我尝试了一个完全黑色的图像,期望在没有脸的情况下更清晰地看到这种变化。当我这样做时,我再次获得了带有两个三角形的图像。在玩了一些之后,我发现如果我有一个完全黑色的图像,除了左上角的像素透明,我得到这个:
这最终是我期望的坐标平滑变化的图像。这完全让我难过。为什么纹理查找工作正常但渲染实际坐标给我带来了大部分垃圾?
编辑:
我找到了一个我已经发布的解决方案,但我仍然不确定为什么这个bug首先存在。我遇到了一个有趣的测试用例,可能会提供更多关于为什么会发生这种情况的信息。
Fragment Shader:
varying vec2 vertTexCoord;
void main(void) {
gl_FragColor = vec4(vertTexCoord, 0, 0.5);
}
答案 0 :(得分:1)
我找到了两种不同的解决方案。两者都涉及处理代码的变化。我不知道这些改变是如何或为何使其发挥作用。
解决方案1:
传递屏幕空间坐标而不是剪辑空间坐标,并使用处理生成的变换矩阵将它们转换为顶点着色器中的剪辑空间。
处理代码:
PGraphics test;
PShader testShader;
PImage testImage;
void setup() {
size(400, 400, P2D);
testShader = loadShader("test.frag", "vert2D.vert");
testImage = loadImage("test.png");
testShader.set("image", testImage);
testShader.set("size", testImage.width);
shader(testShader);
}
void draw() {
background(0, 0, 0);
shader(testShader);
beginShape(TRIANGLES);
//Pass down screen space coordinates instead.
vertex(0, 400, 0, 1);
vertex(400, 400, 1, 1);
vertex(0, 0, 0, 0);
vertex(400, 400, 1, 1);
vertex(0, 0, 0, 0);
vertex(400, 0, 1, 0);
endShape();
}
顶点着色器:
attribute vec2 vertex;
attribute vec2 texCoord;
uniform mat4 transform;
varying vec2 vertTexCoord;
void main() {
//Multiply transform matrix.
gl_Position = transform * vec4(vertex, 0, 1);
vertTexCoord = texCoord;
}
结果:
注意通过屏幕中心的线条。这是因为我们没有在处理代码中调用noStroke()。仍然,纹理坐标被正确插值。
解决方案2:
如果我们只是在设置中调用noStroke(),我们可以向下传递剪辑空间坐标,没有任何问题,一切都按预期完成。无需更改着色器。
PGraphics test;
PShader testShader;
PImage testImage;
void setup() {
size(400, 400, P2D);
//Call noStroke()
noStroke();
testShader = loadShader("test.frag", "vert2D.vert");
testImage = loadImage("test.png");
testShader.set("image", testImage);
testShader.set("size", testImage.width);
shader(testShader);
}
void draw() {
background(0, 0, 0);
shader(testShader);
beginShape(TRIANGLES);
vertex(-1, -1, 0, 1);
vertex(1, -1, 1, 1);
vertex(-1, 1, 0, 0);
vertex(1, -1, 1, 1);
vertex(-1, 1, 0, 0);
vertex(1, 1, 1, 0);
endShape();
}
结果:
很容易解决。这个更改如何影响纹理坐标在片段着色器中进行插值/非插值的方式超出了我的范围。
对于那些可能更熟悉处理如何包装OpenGL的人来说,我可能很想知道这些错误存在的原因,我很想知道。