着色器,顶点着色器:变量变量

时间:2016-05-20 13:44:33

标签: glsl webgl fragment-shader vertex-shader

我目前正在使用shader-school学习一些图形编程/ glsl,这非常有用。但是我仍然坚持关于顶点着色器的第二课。

要求解决方案的三角形似乎显示一个三角形,其中填充了YUV颜色空间的不同色调:
YUV-Wheel

我知道可以计算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;
}

渲染的三角形看起来像这样(左边的部分是要解决的问题,到目前为止,右边部分是我的解决方案):

shader-school lesson about vertex shaders #2

fragPosition.a不断1.0;为什么褪色看起来很奇怪?三角形内的坐标空间究竟如何?我无法绕过它,没有断点或打印陈述,这并不容易弄明白。为什么左下角完全变黑,为什么没有蓝点?

2 个答案:

答案 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三角形的所有角落。

  • 从左下角开始。
  • 这个角落里有什么坐标? -1,-1
  • 它有什么颜色?绿色
  • YUV的这种颜色是什么?那么,Y可能在某个范围内&lt; 0,1&gt; (但我猜它是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在范围&lt; 0,1&gt;范围内。 (但我猜它是0.5)

在着色器代码中,您尝试将位置用作颜色。好吧,我们说有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 =任何地方都没有蓝色。