1
votes

I'm trying to generate a random color everytime the code executes and passing it to the fragment shader(to draw a triangle). Reading other posts, I've decided that the most effective solutions seems to be:

  • set a uniform variable in the fragment shader
  • link it to a javascript variable
  • write to the fragment shader with uniform4f providing the data

Trying to structure this, my snippet doesn't display the triangle but only the background. If I take away all the lines that deal with the color, the triangle appears again.

 var can = document.getElementById('cont'); 
var gl = can.getContext('experimental-webgl');
gl.clearColor(0.0, 0.1, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

red = Math.random();
green = Math.random();
blue = Math.random();
alpha = Math.random();
var vertices = [-1,-1,1,-1,0,0.5];
var colorData = [red,green,blue,alpha];

//Create color buffer
var cbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,cbo);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(colorData),gl.STATIC_DRAW);

//Create vertex buffer    
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vbo);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);


//Shaders
var vshaderCode = 'attribute vec2 pos;' + 'void main() {gl_Position = vec4 (pos,0.,1.);}';

var fshaderCode = 'precision mediump float;' + 'uniform vec4 uColor' + 'void main() {gl_FragColor = uColor;}';


function getShader(shaderSource,type) {
var shad = gl.createShader(type);
gl.shaderSource(shad,shaderSource);
gl.compileShader(shad);  
return shad;
}

var vertexShader = getShader (vshaderCode,gl.VERTEX_SHADER);
var fragmentShader = getShader (fshaderCode,gl.FRAGMENT_SHADER);

//Create the program to link shaders

var prog = gl.createProgram();
gl.attachShader(prog,vertexShader);
gl.attachShader(prog,fragmentShader);
gl.linkProgram(prog);

var _position = gl.getAttribLocation(prog, "pos");
var u_ColorLocation = gl.getUniformLocation(prog, "uColor");

//Draw
gl.enableVertexAttribArray(_position);
gl.useProgram(prog);
gl.vertexAttribPointer(_position, 2, gl.FLOAT, false, 0,0);
gl.uniform4f(u_ColorLocation, colorData);
gl.drawArrays(gl.TRIANGLES, 0, 3);

The console says that gl.uniform4f(u_ColorLocation, colorData); wants five arguments. That point is not clear to me.

Even if I change that line to something like gl.uniform4f(u_colorLocation, 0, 0, 1, 1); the program doesn't link.

How do I pass then my values from javascript to the fragment on the gpu so that my triangle has only one color, driven by javascript variables?

2

2 Answers

1
votes

You were missing a ; on your fshaderCode shader so it wasn't compiling.

Reading the COMPILE_STATUS of the shader can help you work out if your have any syntax problems.

if (!gl.getShaderParameter(shad, gl.COMPILE_STATUS)) {
    console.log("COMPILE_STATUS", gl.getShaderInfoLog(shad));
}

Updated code to show random colour triangle

var can = document.getElementById('cont'); 
var gl = can.getContext('experimental-webgl');
gl.clearColor(0.0, 0.1, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

red = Math.random();
green = Math.random();
blue = Math.random();
alpha = Math.random();
var vertices = [-1,-1,1,-1,0,0.5];
var colorData = [red,green,blue,alpha];

//Create color buffer
var cbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,cbo);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(colorData),gl.STATIC_DRAW);

//Create vertex buffer    
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vbo);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);


//Shaders
var vshaderCode = 'attribute vec2 pos;' + 'void main()' + '{gl_Position = vec4 (pos,0.,1.);' + '}';

var fshaderCode = 'precision mediump float;' + 'uniform vec4 uColor;' + 'void main() {gl_FragColor = uColor;}';


function getShader(shaderSource,type) {
var shad = gl.createShader(type);
gl.shaderSource(shad,shaderSource);
gl.compileShader(shad);
  
if (!gl.getShaderParameter(shad, gl.COMPILE_STATUS)) {
    console.log("COMPILE_STATUS", gl.getShaderInfoLog(shad));
}
  
return shad;
}

var vertexShader = getShader (vshaderCode,gl.VERTEX_SHADER);
var fragmentShader = getShader (fshaderCode,gl.FRAGMENT_SHADER);

//Create the program to link shaders

var prog = gl.createProgram();
gl.attachShader(prog,vertexShader);
gl.attachShader(prog,fragmentShader);
gl.linkProgram(prog);

var _position = gl.getAttribLocation(prog, "pos");
var u_ColorLocation = gl.getUniformLocation(prog, "uColor");

//Draw

var r = Math.random(), g= Math.random(), b = Math.random();

gl.enableVertexAttribArray(_position);
gl.useProgram(prog);
gl.vertexAttribPointer(_position, 2, gl.FLOAT, false, 0,0);
gl.uniform4f(u_ColorLocation, r, g, b, 1.0);
gl.drawArrays(gl.TRIANGLES, 0, 3);
<canvas id="cont"></canvas>
0
votes

In your fragmentshader you're missing a colon behind your uniform declaration:

var fshaderCode = 'precision mediump float;' + 'uniform vec4 uColor;' + 'void main() {gl_FragColor = uColor;}';

As mentioned in the answer below checking the compile status is helpful here, as writing shaders in its own files is.

As to setting uniforms there are uniformXX(ie. uniform4f) and uniformXXv(ie. uniform4fv) variants. the v stands for vector and needs to be used when you provide a vector of data like your color.

So you want to change this to uniform4fv too.

 var can = document.getElementById('cont'); 
var gl = can.getContext('experimental-webgl');
gl.clearColor(0.0, 0.1, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

red = Math.random();
green = Math.random();
blue = Math.random();
alpha = Math.random();
var vertices = [-1,-1,1,-1,0,0.5];
var colorData = [red,green,blue,alpha];

//Create color buffer
var cbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,cbo);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(colorData),gl.STATIC_DRAW);

//Create vertex buffer    
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vbo);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);


//Shaders
var vshaderCode = 'attribute vec2 pos;' + 'void main() {gl_Position = vec4 (pos,0.,1.);}';

var fshaderCode = 'precision mediump float;' + 'uniform vec4 uColor;' + 'void main() {gl_FragColor = uColor;}';


function getShader(shaderSource,type) {
var shad = gl.createShader(type);
gl.shaderSource(shad,shaderSource);
gl.compileShader(shad);  
return shad;
}

var vertexShader = getShader (vshaderCode,gl.VERTEX_SHADER);
var fragmentShader = getShader (fshaderCode,gl.FRAGMENT_SHADER);

//Create the program to link shaders

var prog = gl.createProgram();
gl.attachShader(prog,vertexShader);
gl.attachShader(prog,fragmentShader);
gl.linkProgram(prog);

var _position = gl.getAttribLocation(prog, "pos");
var u_ColorLocation = gl.getUniformLocation(prog, "uColor");

//Draw
gl.enableVertexAttribArray(_position);
gl.useProgram(prog);
gl.vertexAttribPointer(_position, 2, gl.FLOAT, false, 0,0);
gl.uniform4fv(u_ColorLocation, colorData);
gl.drawArrays(gl.TRIANGLES, 0, 3);
<canvas id="cont"></canvas>