我目前正在使用shader-school学习一些图形编程/ glsl,这非常有用。但是我仍然坚持关于顶点着色器的第二课。
要求解决方案的三角形似乎显示一个三角形,其中填充了YUV颜色空间的不同色调:
我知道可以计算RGB和YUV之间的转换,但是我不太清楚顶点或片段着色器中的坐标空间是如何工作的。
我的顶点着色器看起来像这样:
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
;为什么褪色看起来很奇怪?三角形内的坐标空间究竟如何?我无法绕过它,没有断点或打印陈述,这并不容易弄明白。为什么左下角完全变黑,为什么没有蓝点?
答案 0 :(得分:1)
opengl的默认空间是NDC空间。它是一个单位立方体,范围为[-1,1]。其中x = -1位于屏幕的左侧,x = 1位于屏幕的右侧,y = -1 bottom,y = + 1 top,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的转换。
答案 1 :(得分:1)
我在这里看到3种不同的解决方案
首先在FS(片段着色器)中进行整个计算。例如,这种解决方案适用于后处理,但具有高性能影响。
设计理念: 您只通过VS(顶点着色器)传递白色三角形,需要一个属性,并根据位置在FS中执行所有颜色。
第二种解决方案是在VS中进行颜色并使用插值来实现FS中的结果。总的来说这个解决方案是最快的,但并不是所有类型的问题都包含简单的插值。
设计理念: 需要一个属性,一个变化。将一个三角形的数据从js传递到VS,在角色中建立颜色并从插值颜色值中获得FS的结果。
第三种解决方案是将JS中的颜色初始化,将其传递给VS,其中每个角都有一种颜色,使用插值并从FS获得结果。最后一个解决方案提供高度模块化,并可能允许交互。成本较长,代码可能较慢。
设计理念:使用两个属性和一个变化,将位置和颜色YUV传递给VS,然后仅针对颜色进行插值,将其余结果插入FS中。
我对最后一个解决方案进行了简单编码,因为您可以从中学到最多。
// stackoverflow 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>
<强> Explinations 强>
fragPosition.a
持续1.0;为什么褪色看起来很奇怪?
如果我们想象左图是来自YUV颜色,那么Y可能是0.5,因为右下角有灰色。
这与衰落无关,但你的Y始终为0,这意味着黑色,而不是灰色。因此颜色较暗。
三角形内部的坐标空间究竟如何?
假设我在范围&lt; 0,1&gt;范围内使用YUV颜色。对于Y和&lt; -0.5,0.5&gt;对于U和V
让迭代trhu三角形的所有角落。
让我们接下来。右下角
最后一个角落,右上角:
在着色器代码中,您尝试将位置用作颜色。好吧,我们说有YUV颜色。我们有XY职位。我们将Y设置为常量(您使用0,但它应该是0.5)。但是XY!=常数* UV +常数(无论是什么常数),因为所需的三角形具有来自YUV颜色系统的扭曲颜色系统。在左下角是绿色,这没关系。但右下角是灰色而不是蓝色。右上角是橙色而不是紫色。离开顶部,我们不知道,我们不在乎。 所以有两个解决方案。创建颜色缓冲区,为每个角分配颜色或创建自己的颜色系统(在片段着色器中)。我使用了第一个解决方案所以我保留了经典的YUV系统
为什么左下角完全是黑色
你使用坐标作为颜色,所以你的gl_FragColor.rgba=position.xyzw
,在左下角是[-1,-1,0,1],它将转换为[0,0,0,1] =黑色颜色
为什么没有蓝点
在左图中,作者可能不想要蓝色。在你的图片上,蓝色是你的Z坐标,整个时间是0 =任何地方都没有蓝色。