着色器学校,顶点着色器:可变变量
shader-school, vertex shaders: varying variables
我目前正在使用 shader-school 学习一些图形编程/glsl,效果很好。但是我卡在了关于顶点着色器的第二课。
要求解决的三角形似乎显示了一个充满了YUV颜色不同色调的三角形space:
我知道可以计算 RGB 和 YUV 之间的转换,但是我不太确定顶点或片段着色器中的坐标 space 是如何工作的。
我的顶点着色器看起来像这样:
precision highp float;
attribute vec4 position;
attribute vec3 color;
varying vec4 fragPosition;
void main() {
gl_Position = position;
fragPosition = position; // send the position to the fragment shader
}
而我的片段着色器看起来像这样:
precision highp float;
varying vec4 fragPosition; // is set by vertex shader
void main() {
gl_FragColor = fragPosition.rgba;
}
渲染的三角形看起来像这样(左边是要解决的问题,右边是我目前的解决方案):
fragPosition.a
连续1.0
;为什么褪色看起来很奇怪?三角形内部的坐标space到底是多少?我无法全神贯注,如果没有断点或打印语句,就不容易弄清楚。为什么左下角全黑,为什么没有蓝点?
opengl 的默认 space 是 NDC space。它是 [-1,1] 范围内的单位立方体。其中x=-1在屏幕左侧,x=1在屏幕右侧,y=-1底部,y=+1顶部,近平面z=-1,z=+1在远平面。
您现在在着色器中所做的基本上是将片段坐标显示为一种颜色。
三角形的顶部是绿色的,因为坐标类似于 [0,1,0,1],当解释为颜色时,它会呈现绿色。三角形右边的坐标为 [1,-1,0,1],因此呈现红色。底部很可能是 [0,-1,0,1] 之类的东西,它是黑色的。
顺便说一句,我不确定 fragment.a
是否真的是 1.0,你可以通过 gl_FragColor = fragPosition.aaaa;
来检查。如果 .a
是 1.0 那么你应该只能看到白色,如果 .a
是~0.5,它应该是灰色等等。
我在这里猜测,我认为你在本课中应该做的是使用颜色输入代替 (attribute vec3 color;
) 并在着色器中实现 rgba 到 yuv 的转换。
我在这里看到 3 种不同的解决方案
首先是在 FS(片段着色器)中进行整个计算。例如,这种解决方案适用于后处理,但对性能影响很大。
设计思路:
您通过 VS(顶点着色器)仅传递白色三角形,需要一个属性,并根据位置在 FS 中处理所有颜色。
第二种方案是在VS中做颜色,在FS中使用插值得到结果。总体而言,此解决方案是最快的,但并非所有类型的问题都包含简单插值。
设计思路:
需要一种属性和一种变化。您将一个三角形的数据从 js 传递到 VS,在那里您在角落建立颜色并从插值颜色值获得 FS 的结果。
第三种解决方案是在JS中初始化颜色,将其传递给VS,每个角都有一种颜色,使用插值从FS得到结果。最后一个解决方案提供了高度模块化,并可能允许交互。成本是更长的代码并且可能更慢。
设计思路:两个属性一个varying,将positions和colorsYUV传给VS,然后只对colors做插值,其余结果在FS。
我对最后一个解决方案进行了简单编码,因为您可以从中学到最多的东西。
// Whosebug purposes
// initalize canvas and webgl
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
// create shaders
var vsSource = `
precision mediump float;
attribute vec2 a_position;
attribute vec3 a_color_yuv;
varying vec3 v_color_yuv;
void main() {
v_color_yuv = a_color_yuv;
gl_Position = vec4(a_position, 0., 1.);
}
`;
var fsSource = `
precision mediump float;
varying vec3 v_color_yuv; // is set by vertex shader
void main() {
float r = v_color_yuv.x + 1.4075 * (v_color_yuv.z);
float g = v_color_yuv.x - 0.3455 * (v_color_yuv.y) - (0.7169 * (v_color_yuv.z));
float b = v_color_yuv.x + 1.7790 * (v_color_yuv.y);
gl_FragColor = vec4(r, g, b, 1.);
}
`;
var vs = gl.createShader(gl.VERTEX_SHADER);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
// replace removes nonascii chars from shaders sources, because I created them with http://es6-features.org/#StringInterpolation
gl.shaderSource(vs, vsSource.replace(/[^\x00-\x7F]/g, ""));
gl.shaderSource(fs, fsSource.replace(/[^\x00-\x7F]/g, ""));
gl.compileShader(vs);
gl.compileShader(fs);
// create webgl program
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vs);
gl.attachShader(shaderProgram, fs);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// set uniform and attribute locations
var a_position = gl.getAttribLocation(shaderProgram, "a_position");
gl.enableVertexAttribArray(a_position);
var a_color_yuv = gl.getAttribLocation(shaderProgram, "a_color_yuv");
gl.enableVertexAttribArray(a_color_yuv);
// set attribute buffers
var trianglePositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglePositionBuffer);
var positions = [
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
trianglePositionBuffer.itemSize = 2;
var triangleColorYUVBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleColorYUVBuffer);
// modify this
var colorsYUV = [
0.5, -0.5, -0.5,
0.5, 0.0, 0.0,
0.5, -0.5, 0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colorsYUV), gl.STATIC_DRAW);
triangleColorYUVBuffer.itemSize = 3;
var bufferSize = 3;
// final draw
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, trianglePositionBuffer);
gl.vertexAttribPointer(a_position, trianglePositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleColorYUVBuffer);
gl.vertexAttribPointer(a_color_yuv, triangleColorYUVBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, bufferSize);
<canvas id="c" style="border: none;" width="500" height="500"></canvas>
解释
fragPosition.a
is continuously 1.0; why does the fading look so weird?
假设左图是YUV颜色,那么Y大概是0.5,因为右下角有灰色。
这跟褪色没有关系,但是你的Y一直是0,就是黑色,不是灰色。所以颜色更深。
How exactly does the coordinate space inside the triangle?
假设我使用范围 <0,1> 的 YUV 颜色用于 Y,<-0.5,0.5> 用于 U 和 V
让我们遍历三角形的所有角。
- 从左下角开始。
- 这个角的坐标是什么? -1, -1
- 它有什么颜色?绿色
- YUV 中的这个颜色是什么?好吧,Y 可能在 <0,1> 范围内的某个地方(但我猜它是 0.5),U 必须是 -0.5 并且 V 必须是 -0.5
接下来开始吧。右下角
- 这个角的坐标是什么? 1, -1
- 它有什么颜色?灰色
- YUV 中的这个颜色是什么? U和V必须都为0,而Y为0.5
最后一个角,右上角:
- 这个角的坐标是什么? 1, 1
- 它有什么颜色?橙色
- YUV 中的这个颜色是什么? U 必须是 -0.5 而 V 是 0.5 并且 Y 在 <0,1> 范围内(但我猜它是 0.5)
在您的着色器代码中,您试图将位置用作颜色。好吧,假设我们有 YUV 颜色。我们有 XY 位置。我们将 Y 设置为常量(你使用了 0,但它应该是 0.5)。但是然后 XY!=constant*UV + 常数(不管常数是什么)因为所需的三角形具有来自 YUV 颜色系统的扭曲颜色系统。左下角是绿色的,没问题。但在右下角是灰色而不是蓝色。右上角是橙色而不是紫色。左上角我们不知道,我们也不在乎。
因此,有两种解决方案。创建将为每个角分配颜色的颜色缓冲区或创建您自己的颜色系统(在片段着色器中)。我使用了第一个解决方案,所以我保留了经典的 YUV 系统。
Why is the bottom left corner completely black
你用坐标作为颜色,所以你的gl_FragColor.rgba=position.xyzw
,也就是左下角的[-1, -1, 0, 1],会变成[0,0,0,1] = 黑色
why is there no blue spot
左图,作者可能不想要蓝色。在您的图片上,蓝色是您的 Z 坐标,整个时间为 0 = 任何地方都没有蓝色。
我目前正在使用 shader-school 学习一些图形编程/glsl,效果很好。但是我卡在了关于顶点着色器的第二课。
要求解决的三角形似乎显示了一个充满了YUV颜色不同色调的三角形space:
我知道可以计算 RGB 和 YUV 之间的转换,但是我不太确定顶点或片段着色器中的坐标 space 是如何工作的。
我的顶点着色器看起来像这样:
precision highp float;
attribute vec4 position;
attribute vec3 color;
varying vec4 fragPosition;
void main() {
gl_Position = position;
fragPosition = position; // send the position to the fragment shader
}
而我的片段着色器看起来像这样:
precision highp float;
varying vec4 fragPosition; // is set by vertex shader
void main() {
gl_FragColor = fragPosition.rgba;
}
渲染的三角形看起来像这样(左边是要解决的问题,右边是我目前的解决方案):
fragPosition.a
连续1.0
;为什么褪色看起来很奇怪?三角形内部的坐标space到底是多少?我无法全神贯注,如果没有断点或打印语句,就不容易弄清楚。为什么左下角全黑,为什么没有蓝点?
opengl 的默认 space 是 NDC space。它是 [-1,1] 范围内的单位立方体。其中x=-1在屏幕左侧,x=1在屏幕右侧,y=-1底部,y=+1顶部,近平面z=-1,z=+1在远平面。
您现在在着色器中所做的基本上是将片段坐标显示为一种颜色。
三角形的顶部是绿色的,因为坐标类似于 [0,1,0,1],当解释为颜色时,它会呈现绿色。三角形右边的坐标为 [1,-1,0,1],因此呈现红色。底部很可能是 [0,-1,0,1] 之类的东西,它是黑色的。
顺便说一句,我不确定 fragment.a
是否真的是 1.0,你可以通过 gl_FragColor = fragPosition.aaaa;
来检查。如果 .a
是 1.0 那么你应该只能看到白色,如果 .a
是~0.5,它应该是灰色等等。
我在这里猜测,我认为你在本课中应该做的是使用颜色输入代替 (attribute vec3 color;
) 并在着色器中实现 rgba 到 yuv 的转换。
我在这里看到 3 种不同的解决方案
首先是在 FS(片段着色器)中进行整个计算。例如,这种解决方案适用于后处理,但对性能影响很大。
设计思路: 您通过 VS(顶点着色器)仅传递白色三角形,需要一个属性,并根据位置在 FS 中处理所有颜色。
第二种方案是在VS中做颜色,在FS中使用插值得到结果。总体而言,此解决方案是最快的,但并非所有类型的问题都包含简单插值。
设计思路: 需要一种属性和一种变化。您将一个三角形的数据从 js 传递到 VS,在那里您在角落建立颜色并从插值颜色值获得 FS 的结果。
第三种解决方案是在JS中初始化颜色,将其传递给VS,每个角都有一种颜色,使用插值从FS得到结果。最后一个解决方案提供了高度模块化,并可能允许交互。成本是更长的代码并且可能更慢。
设计思路:两个属性一个varying,将positions和colorsYUV传给VS,然后只对colors做插值,其余结果在FS。
我对最后一个解决方案进行了简单编码,因为您可以从中学到最多的东西。
// Whosebug purposes
// initalize canvas and webgl
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
// create shaders
var vsSource = `
precision mediump float;
attribute vec2 a_position;
attribute vec3 a_color_yuv;
varying vec3 v_color_yuv;
void main() {
v_color_yuv = a_color_yuv;
gl_Position = vec4(a_position, 0., 1.);
}
`;
var fsSource = `
precision mediump float;
varying vec3 v_color_yuv; // is set by vertex shader
void main() {
float r = v_color_yuv.x + 1.4075 * (v_color_yuv.z);
float g = v_color_yuv.x - 0.3455 * (v_color_yuv.y) - (0.7169 * (v_color_yuv.z));
float b = v_color_yuv.x + 1.7790 * (v_color_yuv.y);
gl_FragColor = vec4(r, g, b, 1.);
}
`;
var vs = gl.createShader(gl.VERTEX_SHADER);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
// replace removes nonascii chars from shaders sources, because I created them with http://es6-features.org/#StringInterpolation
gl.shaderSource(vs, vsSource.replace(/[^\x00-\x7F]/g, ""));
gl.shaderSource(fs, fsSource.replace(/[^\x00-\x7F]/g, ""));
gl.compileShader(vs);
gl.compileShader(fs);
// create webgl program
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vs);
gl.attachShader(shaderProgram, fs);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// set uniform and attribute locations
var a_position = gl.getAttribLocation(shaderProgram, "a_position");
gl.enableVertexAttribArray(a_position);
var a_color_yuv = gl.getAttribLocation(shaderProgram, "a_color_yuv");
gl.enableVertexAttribArray(a_color_yuv);
// set attribute buffers
var trianglePositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trianglePositionBuffer);
var positions = [
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
trianglePositionBuffer.itemSize = 2;
var triangleColorYUVBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleColorYUVBuffer);
// modify this
var colorsYUV = [
0.5, -0.5, -0.5,
0.5, 0.0, 0.0,
0.5, -0.5, 0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colorsYUV), gl.STATIC_DRAW);
triangleColorYUVBuffer.itemSize = 3;
var bufferSize = 3;
// final draw
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, trianglePositionBuffer);
gl.vertexAttribPointer(a_position, trianglePositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleColorYUVBuffer);
gl.vertexAttribPointer(a_color_yuv, triangleColorYUVBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, bufferSize);
<canvas id="c" style="border: none;" width="500" height="500"></canvas>
解释
fragPosition.a
is continuously 1.0; why does the fading look so weird?
假设左图是YUV颜色,那么Y大概是0.5,因为右下角有灰色。
这跟褪色没有关系,但是你的Y一直是0,就是黑色,不是灰色。所以颜色更深。
How exactly does the coordinate space inside the triangle?
假设我使用范围 <0,1> 的 YUV 颜色用于 Y,<-0.5,0.5> 用于 U 和 V
让我们遍历三角形的所有角。
- 从左下角开始。
- 这个角的坐标是什么? -1, -1
- 它有什么颜色?绿色
- YUV 中的这个颜色是什么?好吧,Y 可能在 <0,1> 范围内的某个地方(但我猜它是 0.5),U 必须是 -0.5 并且 V 必须是 -0.5
接下来开始吧。右下角
- 这个角的坐标是什么? 1, -1
- 它有什么颜色?灰色
- YUV 中的这个颜色是什么? U和V必须都为0,而Y为0.5
最后一个角,右上角:
- 这个角的坐标是什么? 1, 1
- 它有什么颜色?橙色
- YUV 中的这个颜色是什么? U 必须是 -0.5 而 V 是 0.5 并且 Y 在 <0,1> 范围内(但我猜它是 0.5)
在您的着色器代码中,您试图将位置用作颜色。好吧,假设我们有 YUV 颜色。我们有 XY 位置。我们将 Y 设置为常量(你使用了 0,但它应该是 0.5)。但是然后 XY!=constant*UV + 常数(不管常数是什么)因为所需的三角形具有来自 YUV 颜色系统的扭曲颜色系统。左下角是绿色的,没问题。但在右下角是灰色而不是蓝色。右上角是橙色而不是紫色。左上角我们不知道,我们也不在乎。 因此,有两种解决方案。创建将为每个角分配颜色的颜色缓冲区或创建您自己的颜色系统(在片段着色器中)。我使用了第一个解决方案,所以我保留了经典的 YUV 系统。
Why is the bottom left corner completely black
你用坐标作为颜色,所以你的gl_FragColor.rgba=position.xyzw
,也就是左下角的[-1, -1, 0, 1],会变成[0,0,0,1] = 黑色
why is there no blue spot
左图,作者可能不想要蓝色。在您的图片上,蓝色是您的 Z 坐标,整个时间为 0 = 任何地方都没有蓝色。