I see 3 different solutions here
First is do entire calculation in FS(fragment shader). This kind of solutions are good for postprocessing for example, but with high performance impact.
Design idea:
You pass only white triangle via VS(vertex shader), needs one attribute, and do all colors in FS based on position.
Second solution is to do colors in VS and use interpolation to achieve result in FS. Overal this solutions are the fastest, but not all kinds of problems contain simple interpolation.
Design idea:
Needs one attribute and one varying. You pass data for one triangle from js to VS, where you establish colors in corners and get result in FS from interpolated color values.
Third solution is to initalize colors in JS, pass it to VS where each corner has one color, use interpolation and get result from FS. This last solution offers high modularity and might allow interaction. Cost is longer code and might be slower.
Design idea: Use two attributes and one varying, pass postions and colorsYUV to VS, then do interpolation just for colors and the rest of result in FS.
I simplecoded last solution, because you could learn the most from it.
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
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);
gl.shaderSource(vs, vsSource.replace(/[^\x00-\x7F]/g, ""));
gl.shaderSource(fs, fsSource.replace(/[^\x00-\x7F]/g, ""));
gl.compileShader(vs);
gl.compileShader(fs);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vs);
gl.attachShader(shaderProgram, fs);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
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);
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);
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;
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
is continuously 1.0; why does the fading look so weird?
If we imagine that left picture is from YUV colors, then Y is probably 0.5 because there is grey color in right bottom corner.
This has nothing to do with fading, but your Y is 0 all the time, which mean black color, not grey. And so colors are darker.
How exactly does the coordinate space inside the triangle?
Lets say I use YUV colors in ranges <0,1> for Y and <-0.5,0.5> for U and V
Lets iterate trhu all corners of the triangle.
- Start with left bottom corner.
- What coordinate is in this corner? -1, -1
- What color does it have? green
- What is this color in YUV? Well, Y might be somewhere in range <0,1> (but I guess its 0.5), U must be -0.5 and V must be -0.5
Lets go next. Right bottom corner
- What coordinate is in this corner? 1, -1
- What color does it have? grey
- What is this color in YUV? U and V must be both 0, while Y is 0.5
Last corner, right top:
- What coordinate is in this corner? 1, 1
- What color does it have? orange
- What is this color in YUV? U must be -0.5 while V is 0.5 and Y is in range <0,1> (but I guess its 0.5)
In your shader code, you are trying to use positions as colors. Well lets say we have YUV colors. And we have XY positions. We set Y as constant (you used 0, but it should be 0.5). But then XY!=constant*UV + constant (no matter what constants are) because desired triangle has twisted color system from YUV color system. In the left bottom is green, which is ok. But in right bottom is grey instead of blue. Right top is orange not violet. And left top we dont know, and we dont care.
So there are two solutions for this. Create color buffer which will assign color for each corner or create your owen color system (in fragment shader). I used first solution so I kept classical YUV system.
Why is the bottom left corner completely black
You use coordinate as colors, so your gl_FragColor.rgba=position.xyzw
, which is [-1, -1, 0, 1] in left bottom corner, which will transform into [0,0,0,1] = black color
why is there no blue spot
On the left picture, author probably doesnt want blue. On your picture, blue is Z coordinate for you, which is 0 for entire time = no blue anywhere.