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

Unexpected Texture Coordinate Interpolation - Processing + GLSL

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

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

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

这产生了:

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


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

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

片段着色器:

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

结果:

我找到了两种不同的解决方案。两者都涉及处理代码的更改。我不知道这些更改如何或为何使其起作用。

解决方案一:
传递屏幕 space 坐标而不是剪辑 space 坐标,并使用处理生成的变换矩阵将其转换为顶点着色器中的剪辑 space。
处理代码:

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()。不过,纹理坐标已正确插值。

方案二:
如果我们只是在设置中调用 noStroke() ,我们可以毫无问题地传递剪辑 space 坐标,并且一切都按预期进行。无需更改着色器。

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

结果:

很容易修复。这一变化如何影响纹理坐标 interpolated/not 在片段着色器中插值的方式超出了我的范围。

对于任何可能更熟悉处理如何包装 OpenGL 并且可能了解为什么存在这些错误的人,我很想知道。