意外的纹理坐标插值 - 处理+ GLSL

时间:2016-04-13 19:23:57

标签: opengl glsl shader processing

在过去的几天里,我偶然发现了一个特别棘手的错误。我已将代码缩减为一组非常简单直接的示例。

这是我用来调用着色器的处理代码:

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);
}

我明白了:
enter image description here
这是预期的结果。但是,当我使用以下片段着色器将纹理坐标渲染到红色和绿色通道时:

uniform sampler2D image;
uniform float size;
varying vec2 vertTexCoord;
void main(void) {
    gl_FragColor = vec4(vertTexCoord, 0, 1);
}

我明白了:

enter image description here

如您所见,屏幕的大部分是黑色,这表示在这些片段中,纹理坐标为[0,0]。但情况并非如此,因为当传递到texture2D函数时,它们被正确映射到图像中的相应位置。为了验证在这两种情况下都使用了完全相同的纹理坐标值,我将它们与下面的着色器结合起来。

uniform sampler2D image;
uniform float size;
varying vec2 vertTexCoord;
void main(void) {
    gl_FragColor = texture2D(image, vertTexCoord) + vec4(vertTexCoord, 0, 0);
}

这产生了:
enter image description here

如果纹理坐标在屏幕上平滑变化,那么这正是您所期望的。所以我尝试了一个完全黑色的图像,期望在没有脸的情况下更清晰地看到这种变化。当我这样做时,我再次获得了带有两个三角形的图像。在玩了一些之后,我发现如果我有一个完全黑色的图像,除了左上角的像素透明,我得到这个:

enter image description here

这最终是我期望的坐标平滑变化的图像。这完全让我难过。为什么纹理查找工作正常但渲染实际坐标给我带来了大部分垃圾?

编辑:
我找到了一个我已经发布的解决方案,但我仍然不确定为什么这个bug首先存在。我遇到了一个有趣的测试用例,可能会提供更多关于为什么会发生这种情况的信息。

Fragment Shader:

varying vec2 vertTexCoord;
void main(void) {
    gl_FragColor = vec4(vertTexCoord, 0, 0.5);
}

结果:
enter image description here

1 个答案:

答案 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;
}

结果:
enter image description here
注意通过屏幕中心的线条。这是因为我们没有在处理代码中调用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();
}

结果:
enter image description here
很容易解决。这个更改如何影响纹理坐标在片段着色器中进行插值/非插值的方式超出了我的范围。

对于那些可能更熟悉处理如何包装OpenGL的人来说,我可能很想知道这些错误存在的原因,我很想知道。