WebGL draw loop and translation - javascript

I'm want my javascript program to be shifting a triangle to the right.
What I'm focusing on right now are matrices and draw loops. I've never done such a thing so I may be off road but what I'm attempting to do, for study purposes is this:
-Setup the webgl pipeline so I display correctly a triangle (OK)
-Write a function with a matrix that allows me to pass in the values of translation (seems ok but I'm not sure)
var translation_prototype = [1,0,0,0,
0,1,0,0,
0,0,1,0,
tx,ty,0,1];
Leaving out for the moment rotation a scaling and modelview, since I'm perfectly happy(just for the sake of an exercise) with the orthographic view webgl provides.
-Setup a loop that cycles through the drawArrays (even here I'm not sure if the loop starts and ends in the correct place)
I suspect I'm really close, but the triangle doesn't move (tx remains constant).
Here's the code(I think I don't even need to clear the color and depth buffer since I'm only translating on x axis)
<!DOCTYPE HTML>
<html>
<canvas id = "can" width="400" height="400">
</canvas>
<script>
var webgl_canvas = document.getElementById('can');
var gl = webgl_canvas.getContext('experimental-webgl');
var triangles = [-0.8,-0.8,0,0.8,-0.8,0,0,0.8,0];
var vertexBuffer = gl.createBuffer();
var tx = 0.1;
var ty = 0;
var translation_prototype = [1,0,0,0,
0,1,0,0,
0,0,1,0,
tx,ty,0,1];
var vertexShader_source = 'attribute vec3 a_position;' + 'uniform vec4 u_translation;' + 'void main() { gl_Position = u_translation * vec4 (a_position,1);}';
var fragmentShader_source = 'precision mediump float;' + 'void main() { gl_FragColor = vec4 (0.9,0,0.1,1); }';
function getTimeInSeconds () {
return Date.now() * 0.001;
}
function makeTranslation (tx, ty) {
return translation_prototype;
}
//Compile shaders
var buildShader = function (shaderSource, typeOfShader) {
var shader = gl.createShader(typeOfShader);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert (gl.getShaderInfoLog(shader));
}
return shader;
}
var compiledVertexShader = buildShader (vertexShader_source, gl.VERTEX_SHADER);
var compiledFragmentShader = buildShader (fragmentShader_source, gl.FRAGMENT_SHADER);
//setup GLSL program
program = gl.createProgram();
gl.attachShader(program,compiledVertexShader);
gl.attachShader(program,compiledFragmentShader);
gl.linkProgram(program);
//Fill the buffer with vertex data
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(triangles), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
gl.clear(gl.COLOR_BUFFER_BIT);
var positionLocation = gl.getAttribLocation(program,"a_position");
gl.enableVertexAttribArray(positionLocation);
gl.useProgram(program);
var shaderTranlsationMatrix = gl.getUniformLocation(program, "u_translation");
gl.uniformMatrix4fv(shaderTranlsationMatrix,false,new Float32Array(translation_prototype));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
var startTime = 0;
function animate (time) {
//Draw loop
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var deltaTime = (time - startTime);
makeTranslation((tx*deltaTime),(ty*deltaTime));
console.log(tx,ty,deltaTime);
gl.drawArrays (gl.TRIANGLES, 0, vertexBuffer.numItems);
startTime = time;
window.requestAnimationFrame(animate);
}
animate(0);
</script>
</html>
<!-- start last edited snippet -->
<!DOCTYPE HTML>
<html>
<canvas id = "can" width="400" height="400">
</canvas>
<script>
var webgl_canvas = document.getElementById('can');
var gl = webgl_canvas.getContext('experimental-webgl');
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,0,1,-1,0,0,1,0]), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
var identityMatrix = [1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1];
function translation (tx,ty,tz) {
return [1,0,0,0,
0,1,0,0,
0,0,1,0,
tx,ty,tz,1]
}
var vertexShader_source = 'attribute vec3 a_position;' + 'uniform mat4 u_move;' + 'void main() { gl_Position = u_move * vec4 (a_position,1); }';
var fragmentShader_source = 'precision mediump float;' + 'void main() { gl_FragColor = vec4 (0.9,0,0.1,1); }';
//Compile shaders
var buildShader = function (shaderSource, typeOfShader) {
var shader = gl.createShader(typeOfShader);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert (gl.getShaderInfoLog(shader));
}
return shader;
}
var compiledVertexShader = buildShader (vertexShader_source, gl.VERTEX_SHADER);
var compiledFragmentShader = buildShader (fragmentShader_source, gl.FRAGMENT_SHADER);
//setup GLSL program
program = gl.createProgram();
gl.attachShader(program,compiledVertexShader);
gl.attachShader(program,compiledFragmentShader);
gl.linkProgram(program);
var positionLocation = gl.getAttribLocation(program,"a_position");
gl.enableVertexAttribArray(positionLocation);
gl.useProgram(program);
var tx = 0, ty = 0, tz = 0;
var translate = gl.getUniformLocation (program, "u_move");
gl.uniformMatrix4fv(translate,false,new Float32Array(identityMatrix));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
//Draw
var start_time =0;
var animate=function(time) {
var dt= time-start_time;
tx+=0.5;
translation((dt*tx),0,0);
console.log(dt);
console.log(tx);
start_time=time;
gl.drawArrays (gl.TRIANGLES, 0, vertexBuffer.numItems);
window.requestAnimationFrame(animate);
}
animate(0);
</script>
</html>
<!-- end last edited snippet -->

Here is the working snippet
JSFIDDLE
Your vertex shader should look like this:
attribute vec3 a_position;' + 'uniform mat4 u_translation;' + 'void main() { gl_Position = u_translation*vec4 (a_position,1);}
In order to make objects move in space you have to multiply all your vectors and matrices with the position vector to get the result. Tranlation Wiki
You have to update your translation_prototype variable every loop cycle:
deltaTime += 0.005;
makeTranslation(tx+deltaTime,ty+deltaTime);
the deltaTime was declared outside the loop and incremented every cycle
Also your makeTranslation function should look like this:
function makeTranslation (x, y) {
translation_prototype =
[1,0,0,0,
0,1,0,0,
0,0,1,0,
x,y,0,1]
return translation_prototype;
}
(you can get rid of the return statement if you are using global variables, though is recommended to use local variables)
(I had to try this new snippet feature :D)
var webgl_canvas = document.getElementById('can');
var gl = webgl_canvas.getContext('experimental-webgl');
var triangles = [-0.5,-0.5,0,0.5,-0.5,0,0,0.5,0];
var vertexBuffer = gl.createBuffer();
var tx = 0;
var ty = 0;
var translation_prototype = [1,0,0,0,
- 0,1,0,0,
0,0,1,0,
0,0,0,1];
var vertexShader_source = 'attribute vec3 a_position;' + 'uniform mat4 u_translation;' + 'void main() { gl_Position = u_translation*vec4 (a_position,1);}';
var fragmentShader_source = 'precision mediump float;' + 'void main() { gl_FragColor = vec4 (0.9,0,0.1,1); }';
function getTimeInSeconds () {
return Date.now() * 0.001;
}
function makeTranslation (x, y) {
translation_prototype =
[1,0,0,0,
0,1,0,0,
0,0,1,0,
x,y,0,1]
return translation_prototype;
}
//Compile shaders
var buildShader = function (shaderSource, typeOfShader) {
var shader = gl.createShader(typeOfShader);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert (gl.getShaderInfoLog(shader));
}
return shader;
}
var compiledVertexShader = buildShader (vertexShader_source, gl.VERTEX_SHADER);
var compiledFragmentShader = buildShader (fragmentShader_source, gl.FRAGMENT_SHADER);
//setup GLSL program
program = gl.createProgram();
gl.attachShader(program,compiledVertexShader);
gl.attachShader(program,compiledFragmentShader);
gl.linkProgram(program);
//Fill the buffer with vertex data
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(triangles), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
gl.clear(gl.COLOR_BUFFER_BIT);
var positionLocation = gl.getAttribLocation(program,"a_position");
gl.enableVertexAttribArray(positionLocation);
gl.useProgram(program);
var shaderTranlsationMatrix = gl.getUniformLocation(program, "u_translation");
gl.uniformMatrix4fv(shaderTranlsationMatrix,false,new Float32Array(translation_prototype));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
var startTime = 0;
var deltaTime = 0;
function animate (time) {
//Draw loop
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
deltaTime += 0.005;
makeTranslation(tx+deltaTime,ty+deltaTime);
gl.useProgram(program);
var shaderTranlsationMatrix = gl.getUniformLocation(program, "u_translation");
gl.uniformMatrix4fv(shaderTranlsationMatrix,false,new Float32Array(translation_prototype));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.drawArrays (gl.TRIANGLES, 0, vertexBuffer.numItems);
startTime = time;
window.requestAnimationFrame(animate);
}
animate(0);
<canvas id = "can" width="300" height="300">
</canvas>

Related

How do I optimize WebGL to render more objects quickly?

I am under the impression that WebGL is much more powerful than the 2d renderer in the browser but for some reason, my WebGL code runs much slower. Are there any recommended optimization strategies I can use to make my WebGL code run a little bit faster?
Are there any code in my rect function that should be left out because I am new to WebGL and most tutorials don't cover how to make a rect function.
WebGL Code
const canvas = document.getElementById("canvas");
const vertexCode = `
precision mediump float;
attribute vec4 position;
uniform mat4 matrix;
uniform vec4 color;
varying vec4 col;
void main() {
col = color;
gl_Position = matrix * position;
}
`;
const fragmentCode = `
precision mediump float;
varying vec4 col;
void main() {
gl_FragColor = col;
}
`;
const width = canvas.width;
const height = canvas.height;
const gl = canvas.getContext("webgl");
if(!gl) {
console.log("WebGL not supported");
}
const projectionMatrix = [
2/width, 0, 0, 0,
0, -2/height, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1
];
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexCode);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentCode);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
function rect(x, y, w, h) {
const vertex = [
x, y, 0, 1,
x+w, y, 0, 1,
x, y+h, 0, 1,
x+w, y+h, 0, 1
]
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertex), gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 4, gl.FLOAT, false, 0, 0);
const projectionLocation = gl.getUniformLocation(program, `matrix`);
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function fill(r, g, b, a) {
const projectionLocation = gl.getUniformLocation(program, `color`);
gl.uniform4fv(projectionLocation, [r, g, b, a]);
}
let lastTime = new Date();
function animate() {
let currentTime = new Date();
console.log(1000 / (currentTime.getTime() - lastTime.getTime()));
lastTime = new Date();
requestAnimationFrame(animate);
for(let i=0;i<200;i++) {
fill(1, 0, 0, 1);
rect(random(0, 800), random(0, 600), 10, 10);
}
}
animate();
function random(low, high) {
return low + Math.random() * (high-low)
}
Using the normal 2D renderer
const canvas = document.getElementById("canvas");
const c = canvas.getContext("2d");
let lastTime = new Date();
function animate() {
let currentTime = new Date();
console.log(1000 / (currentTime.getTime() - lastTime.getTime()));
lastTime = new Date();
requestAnimationFrame(animate);
c.fillStyle = "black";
c.fillRect(0, 0, 800, 600);
c.fillStyle = "red";
for(let i=0;i<200;i++) {
c.fillRect(random(0, 800), random(0, 600), 10, 10);
}
}
animate();
function random(low, high) {
return low + Math.random() * (high-low)
}
There are 1000s of ways to optimized WebGL. Which one you choose depends on your needs. The more you optimize generally the less flexible.
First you should do the obvious and not create new buffers for every rectangle as your code is doing now and also move anything outside the rendering that can be moved outside (like looking up locations) which is shown in the answer by Józef Podlecki.
Another is you could move and scale the rectangle use the uniform matrix rather than uploading new vertices for each rectangle. It's unclear whether or not updating the matrix would be faster or slower for rectangles but if you were drawing something with more vertices it would definitely be faster do it by matrix. This series of articles mentions that and builds up to matrices.
Another is you can use vertex arrays though that's not important for your example since you're only drawing a single thing.
Another is if the shapes are all the same (as yours are) you can use instanced drawing to draw all 200 rectangles with one draw call
If the shapes are not all the same you can use creative techniques like storing their data in textures.
Another is you can put more than one shape in a buffer. So for example instead of putting one rectangle in the buffer put all 200 rectangles in the buffer and then draw them with one draw call. From this presentation
Also note: the callback to requestAnimationFrame is passed the time since the page loaded so there is no need to create Date objects. Creating Date objects is slower than not and the time passed to requestAnimationFrame is more accurate than Date
let lastTime = 0;
function animate(currentTime) {
console.log(1000 / (currentTime - lastTime));
lastTime = currentTime;
...
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
It's also slow to print to the console. You'd be better off updating an element's content
let lastTime = 0;
function animate(currentTime) {
someElement.textContent = (1000 / (currentTime - lastTime)).toFixed(1);
lastTime = currentTime;
...
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
You can reuse buffer, extract getAttribLocation,getUniformLocation outside of rect function as well as vertexAttribPointer once you bound buffer and lastly call requestAnimationFrame after rendering
const canvas = document.getElementById("canvas");
const vertexCode = `
precision mediump float;
attribute vec4 position;
uniform mat4 matrix;
uniform vec4 color;
varying vec4 col;
void main() {
col = color;
gl_Position = matrix * position;
}
`;
const fragmentCode = `
precision mediump float;
varying vec4 col;
void main() {
gl_FragColor = col;
}
`;
const width = canvas.width;
const height = canvas.height;
const gl = canvas.getContext("webgl");
if(!gl) {
console.log("WebGL not supported");
}
const projectionMatrix = [
2/width, 0, 0, 0,
0, -2/height, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1
];
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexCode);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentCode);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const positionBuffer = gl.createBuffer();
const positionLocation = gl.getAttribLocation(program, "position");
const projectionLocation = gl.getUniformLocation(program, `matrix`);
const projectionColorLocation = gl.getUniformLocation(program, `color`);
gl.enableVertexAttribArray(positionLocation);
const vertex = [
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1
]
const floatArray = new Float32Array(vertex)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 4, gl.FLOAT, false, 0, 0);
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
function rect(x, y, w, h) {
floatArray[0] = x;
floatArray[1] = y;
floatArray[4] = x + w;
floatArray[5] = y;
floatArray[8] = x;
floatArray[9] = y + h;
floatArray[12] = x + w;
floatArray[13] = y + h;
gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function fill(r, g, b, a) {
gl.uniform4fv(projectionColorLocation, [r, g, b, a]);
}
let lastTime = new Date();
function animate() {
let currentTime = new Date();
console.log(1000 / (currentTime.getTime() - lastTime.getTime()));
lastTime = new Date();
for(let i=0;i<200;i++) {
fill(1, 0, 0, 1);
rect(random(0, 800), random(0, 600), 10, 10);
}
requestAnimationFrame(animate);
}
animate();
function random(low, high) {
return low + Math.random() * (high-low)
}
<canvas id="canvas" width="500" height="300"></canvas>

Why am i getting "out is null" (glmatrix) error? Webgl

The goal is a textured open cylinder. I believe the issue is with the setCamera() function. I havent made any changes from when I used the function as it is previously. Running on an http-server localhost in firefox(javascript is enabled). I would really appreciate a solution on how to make this work as well as any tips.
"glmatrix" is a Javascript Matrix and Vector library.
Here is the code:
var mat4 = glMatrix.mat4;
var segments = 64;
var cylVertices=[]; //sidevertices
var positionAttribLocation = null;
var gl = null;
var program = null;
var cylVertexBufferObject = null;
var matWorldUniformLocation =null;
var matViewUniformLocation =null;
var matProjectionUniformLocation =null;
var worldMatrix = null;
var viewMatrix = null;
var projectionMatrix = null;
var canvas = null;
var vertexShaderText =
[
'precision mediump float;',
'',
'attribute vec3 vertPosition;',
'attribute vec2 vertTexCoord;',
'varying vec2 fragTexCoord;',
'uniform mat4 mWorld;',
'uniform mat4 mView;',
'uniform mat4 mProjection;',
'',
'void main()',
'{',
' fragTexCoord = vertTexCoord;',
' gl_Position = mProjection * mView * mWorld * vec4(vertPosition, 1.0);',
'}'
].join('\n');
var fragmentTexShaderText =
[
'precision mediump float;',
'',
'varying vec2 fragTexCoord;',
'uniform sampler2D sampler;',
'void main()',
'{',
' gl_FragColor = texture2D(sampler,fragTexCoord);',
'}'
].join('\n');
var initDemo = function () {
console.log('This is working');
canvas = document.getElementById('game-surface');
gl = canvas.getContext('webgl');
if (!gl) {
console.log('WebGL not supported, falling back on experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}
if (!gl) {
alert('Your browser does not support WebGL');
}
clear();
gl.enable(gl.DEPTH_TEST);//enable drawing over if closer to the virtual camera
//
//Create Shaders
//
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader,vertexShaderText);
gl.shaderSource(fragmentShader,fragmentTexShaderText);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader,gl.COMPILE_STATUS)){
console.error('ERROR compiling vertex shader!', gl.getShaderInfoLog(vertexShader));
return;
}
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)){
console.error('ERROR compiling fragment shader!', gl.getShaderInfoLog(fragmentShader));
return;
}
program = gl.createProgram();
gl.attachShader(program,vertexShader);
gl.attachShader(program,fragmentShader)
gl.linkProgram(program);
if (!gl.getProgramParameter(program,gl.LINK_STATUS)){
console.error('ERROR linking program!', gl.getProgramInfoLog(program));
return;
}
a=0, b=0, y=0, u=0, v=0, s=0, t=0; //The origin
r=1.0,g=0.4,b=0.6;
theta = (Math.PI/180) * (360);
//theta = (Math.PI/180) * (360/(segments)); //Degrees = radians * (180 / )
for (i =0;i<=(segments);i++){
x = i*Math.cos(theta);
z = i*Math.sin(theta);
s=(theta+180)/360;
cylVertices.push(x, y, z, s,0); //Sidevertices along the bottom
cylVertices.push(x, y+2, z, s,2); //Sidevertices along the top
}
cylArray = new Float32Array(cylVertices); //sidearray
//
//------MAIN RENDER LOOP-------
//
var angle = 0;
var loop = function (){
setCamera();
angle = performance.now() / 1000 / 6 * 2 * Math.PI;
mat4.rotate(xRotationMatrix,identityMatrix, angle, [0,1,0]);//x rotation
gl.uniformMatrix4fv(matWorldUniformLocation, gl.FALSE, xRotationMatrix);
clear();
theVertexBufferObject = gl.createBuffer();
drawCylSide();
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
};
//
// --------------------functions--------------------
//
function drawCylSide(){
gl.bindBuffer(gl.ARRAY_BUFFER,theVertexBufferObject);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cylArray),gl.STATIC_DRAW);
setPointer(5);
var texCoordAttribLocation = gl.getAttribLocation(program,'vertTexCoord');
gl.vertexAttribPointer(
texCoordAttribLocation, //Attribute location
2, //Number of vertix elements
gl.FLOAT, //Type of elements
gl.FALSE, //Normalised
5 * Float32Array.BYTES_PER_ELEMENT, //Size of individual vertex
3 * Float32Array.BYTES_PER_ELEMENT, //Offset
);
gl.enableVertexAttribArray(positionAttribLocation);
gl.enableVertexAttribArray(texCoordAttribLocation);
var boxTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, boxTexture);
gl.texParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);
gl.texParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);
gl.texParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,gl.LINEAR);
gl.texParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,gl.LINEAR);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
gl.UNSIGNED_BYTE,
document.getElementById('wall-img')
);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.useProgram(program);
gl.bindTexture(gl.TEXTURE_2D, boxTexture);
gl.activeTexture(gl.TEXTURE0);
gl.drawArrays(gl.TRIANGLE_STRIP,0, segments);
};
function setPointer(n){
positionAttribLocation = gl.getAttribLocation(program,'vertPosition');
gl.vertexAttribPointer(
positionAttribLocation, //Attribute location
3, //Number of vertix elements
gl.FLOAT, //Type of elements
gl.FALSE, //Normalised
n * Float32Array.BYTES_PER_ELEMENT, //Size of individual vertex
0 //Offset
);
};
function clear(){
gl.clearColor(0.75, 0.85, 0.8, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};
function setCamera(){
mat4.identity(worldMatrix);
mat4.lookAt(viewMatrix,[0,1,-8],[0,0,0],[0,10,0]);
mat4.perspective(projectionMatrix,glMatrix.glMatrix.toRadian(45),canvas.width/canvas.height,0.1,1000.0);
gl.uniformMatrix4fv(matWorldUniformLocation,gl.FALSE,worldMatrix);
gl.uniformMatrix4fv(matViewUniformLocation,gl.FALSE,viewMatrix);
gl.uniformMatrix4fv(matProjectionUniformLocation,gl.FALSE,projectionMatrix);
};
You need to initialize vectors and matrices using the respective create function of glMatrix.
var worldMatrix = mat4.create();
var viewMatrix = mat4.create();
var projectionMatrix = mat4.create();

gl_Position will not move vertex when I pass in position to shader with attribute?

Try to use createVertices() to move the coords attribute: Plunkr live code:
Question: how can I move my gl_Position with an attribute?
It works when I manually set the vec4 value:
let gl;
let shaderProgram;
initGl();
createShaders();
draw();
function initGl() {
const canvas = document.getElementById('canvas');
gl = canvas.getContext('webgl');
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1, 0, 1, 1);
}
function createShaders() {
// Think of this as a point in 3d space.
const vs = `
void main(void) {
gl_Position = vec4(0.5, 0, 0, 1.0);
gl_PointSize = 10.0;
}
`; // !!!!!!!!!! this will offset the pixel by
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
// Think of this as a fragment.
const fs = `
void main(void) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
`;
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);
}
But when I add an attribute
function createVertices() {
const coords = gl.getAttribLocation(shaderProgram, "coords");
gl.vertexAttrib3f(coords, 0.5, 0, 0); // will not work
const pointSize = gl.getAttribLocation(shaderProgram, "pointSize");
gl.vertexAttrib1f(pointSize, 100); // works
}
const vs = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
gl_Position = coords;
gl_PointSize = pointSize;
}
`;
Please review Plunkr live code to see complete working code.
You have to disableVertexAttribArray. enableVertexAttribArray enable or a generic vertex attribute array and disableVertexAttribArray disables a generic vertex attribute array.
You have no vertex attrute array, but you have a constant attribute.
const coords = gl.getAttribLocation(shaderProgram, "coords");
const pointSize = gl.getAttribLocation(shaderProgram, "pointSize");
gl.disableVertexAttribArray(coords);
gl.disableVertexAttribArray(pointSize);
gl.vertexAttrib3f(coords, 0.5, 0, 0);
gl.vertexAttrib1f(pointSize, 100);
See also:
Khronos WebGL specification - Enabled Vertex Attributes and Range Checking
WebGL - which API to use?
Is it important to call glDisableVertexAttribArray()?
When should I enable/disable vertex position attributes in WebGL/OpenGL?
Example:
var gl,
shaderProgram;
initGL();
createShaders();
createVertices();
draw();
function initGL() {
var canvas = document.getElementById("canvas");
console.log(canvas);
gl = canvas.getContext("experimental-webgl");
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1, 1, 1, 1);
}
function createShaders() {
var vs = "";
vs += "attribute vec4 coords;";
vs += "attribute float pointSize;";
vs += "void main(void) {";
vs += " gl_Position = coords;";
vs += " gl_PointSize = pointSize;";
vs += "}";
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
var fs = "";
fs += "precision mediump float;";
fs += "uniform vec4 color;";
fs += "void main(void) {";
fs += " gl_FragColor = color;";
fs += "}";
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function createVertices() {
var coords = gl.getAttribLocation(shaderProgram, "coords");
var pointSize = gl.getAttribLocation(shaderProgram, "pointSize");
gl.disableVertexAttribArray(coords);
gl.disableVertexAttribArray(pointSize);
gl.vertexAttrib3f(coords, 0.0, 0.0, 0.0);
gl.vertexAttrib1f(pointSize, 100);
var color = gl.getUniformLocation(shaderProgram, "color");
gl.uniform4f(color, 1, 0, 1, 1);
}
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<canvas id="canvas" width="200" height="200"></canvas>
<script src="lib/script.js"></script>
</body>
</html>

WebGL Basics: Using Render Loops to Apply 2D Convolution Filters to Buffer Canvases

Very new to WebGL and attempting to port some 2D image processing shaders in order to get a handle on things. I initially was misled by the MDN tutorials into thinking WebGL was like OpenGL desktop, but then found these tutorials, which I found much more true to form as well as my purposes. However, I'm still having some trouble in formatting a render loop so I can pass a continually updating texture for processing. In cases where it does render, I just get a muddy mess and in cases where the vertex shader isn't a simple pass through I get nothing. I understand GLSL and the basics of how buffers work, but clearly am doing something very wrong here... Any help would be greatly appreciated. Thanks!
class GL {
constructor(canvas){
this.gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
if (!this.gl) {
alert("Unable to initialize WebGL. Your browser may not support it.");
this.gl = null;
}
//init shaders
var fragmentShader = getShader(this.gl, "fshader");
var vertexShader = getShader(this.gl, "vshader");
var shaderProgram = this.gl.createProgram();
this.gl.attachShader(shaderProgram, vertexShader);
this.gl.attachShader(shaderProgram, fragmentShader);
this.gl.linkProgram(shaderProgram);
this.gl.useProgram(shaderProgram);
this.positionLocation = this.gl.getAttribLocation(shaderProgram, "position");
this.texCoordLocation = this.gl.getAttribLocation(shaderProgram, "texcoord");
var resolutionLocation = this.gl.getUniformLocation(shaderProgram, "resolution");
this.width = this.gl.getUniformLocation(shaderProgram, "width");
//set resolution
this.gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
function getShader(gl, id) {
var shaderScript, theSource, currentChild, shader;
shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
theSource = "";
currentChild = shaderScript.firstChild;
while(currentChild) {
if (currentChild.nodeType == currentChild.TEXT_NODE) {
theSource += currentChild.textContent;
}
currentChild = currentChild.nextSibling;
}
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
// Unknown shader type
return null;
}
gl.shaderSource(shader, theSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
};
};
render(bufferCanvas, x, y) {
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
//texture coordinates
var texCoordBuffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
this.gl.enableVertexAttribArray(this.texCoordLocation);
this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 8, 0);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]),
this.gl.STATIC_DRAW);
//create texture
var texture = this.gl.createTexture();
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
//normalize image to powers of two
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
//load texture from 2d canvas
this.gl.texImage2D(this.gl.TEXTURE_2D,
0,
this.gl.RGBA,
this.gl.RGBA,
this.gl.UNSIGNED_BYTE,
bufferCanvas);
//load buffer
var buffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.enableVertexAttribArray(this.positionLocation);
this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 12, 0);
//draw size and position
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
x, y,
x + bufferCanvas.width, y,
x, y + bufferCanvas.height,
x, y + bufferCanvas.height,
x+ bufferCanvas.width, y,
x+ bufferCanvas.width, y + bufferCanvas.height]), this.gl.STATIC_DRAW);
//blur width
this.gl.enableVertexAttribArray(this.width);
this.gl.vertexAttribPointer(this.width, 1, this.gl.FLOAT, false, 12, 8);
//draw
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
};
};
var canvas2d = document.getElementById('buffer-canvas');
var context2d = canvas2d.getContext("2d");
var canvasGL = new GL(document.getElementById('main-canvas'));
canvasGL.width = 5.0;
for(var i=0; i<10; i++) {
var r = Math.floor(Math.random() * 255);
var g = Math.floor(Math.random() * 255);
var b = Math.floor(Math.random() * 255);
var a = Math.floor(Math.random() * 255);
context2d.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
var x = Math.random() * canvas2d.width;
var y = Math.random() * canvas2d.height;
var width = canvas2d.width - (Math.random() * canvas2d.width);
var height = canvas2d.height - (Math.random() * canvas2d.height);
context2d.fillRect(x, y, width , height);
canvasGL.render(canvas2d, canvas2d.getBoundingClientRect("left"), canvas2d.getBoundingClientRect("top"));
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sylvester/0.1.3/sylvester.min.js"></script>
<script src="https://github.com/mdn/webgl-examples/blob/gh-pages/tutorial/glUtils.js"></script>
<script id="vshader" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 position;
attribute vec2 texcoord;
uniform vec2 resolution;
uniform float width;
varying vec2 texcoord11;
varying vec2 texcoord00;
varying vec2 texcoord02;
varying vec2 texcoord20;
varying vec2 texcoord22;
void main()
{
gl_Position = vec4(((position / resolution) * 2.0 - 1.0) * vec2(1, -1), 0, 1);
// get texcoords
texcoord11 = texcoord;
texcoord00 = texcoord + vec2(-width, -width);
texcoord02 = texcoord + vec2( width, -width);
texcoord20 = texcoord + vec2( width, width);
texcoord22 = texcoord + vec2(-width, width);
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D image;
varying vec2 texcoord11;
varying vec2 texcoord00;
varying vec2 texcoord02;
varying vec2 texcoord20;
varying vec2 texcoord22;
void main()
{
vec4 blur;
blur = texture2D(image, texcoord11);
blur += texture2D(image, texcoord00);
blur += texture2D(image, texcoord02);
blur += texture2D(image, texcoord20);
blur += texture2D(image, texcoord22);
gl_FragColor = 0.2 * blur;
}
</script>
</head>
<body>
<canvas id="main-canvas" width="400" height="300" style="border:1px solid black;"></canvas>
<canvas id="buffer-canvas" width="400" height="300" style="visibility:hidden;"></canvas>
</body>
There's several issues with the code
neither of the script tags seem to be needed
You assign this.width to a uniform location
this.width = this.gl.getUniformLocation(shaderProgram, "width");
But then later destroy that with
canvasGL.width = 5.0
width is a uniform but you're trying to set it as an attribute
wrong
this.gl.enableVertexAttribArray(this.width);
this.gl.vertexAttribPointer(this.width, 1, this.gl.FLOAT, false, 12, 8);
right
this.gl.uniform1f(this.width, whateverYouWantedWidthToBe);
You've got unneeded strides on all your attributes
wrong
this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 8, 0);
this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 12, 0);
right
this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 0, 0);
this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 0, 0);
Well I suppose the texcoord one is not wrong if you want to set strides but the position one is wrong since you're using 2 floats per position not 3. Why not just set them to 0?
You're assigning the texCoordLocation to a var but then using it as a property on this
wrong
var texCoordBuffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
right?
this.texCoordBuffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
On top of that the structure of the code is probably not what you intended.
In WebGL you don't generally call gl.createXXX functions in your render function. You'd call gl.createXXX functions those during initialization. See here for a more typical structure.
It's not clear at all what this line is trying to do
canvasGL.render(canvas2d, canvas2d.getBoundingClientRect("left"),
canvas2d.getBoundingClientRect("top"))
What do you think canvas2d.getBoundingClientRect() is going to return that's useful for rendering? Also that's not how getBoundingClientRect works. It returns a rect, it doesn't take any arguments.
Once you have all that fixed it's not clear what you want width to be. I'm assuming you want it to be pixels but in that case it needs to be 1 / canvas2D.width and you need a separate value for height in your shader since you'll need different values to move up and down a certain number of pixels vs left and right.
other suggestions
Pull gl into a local variable. Then you don't have to do this.gl everywhere. Less typing, shorter, and faster
You can get the contents of script tag with just this
var theSource = shaderScript.text;
You're checking for shader compile errors but not link errors.
visibility: hidden; doesn't do what I think you think it does. You probably want display: none;.
Here's one version that does something.
class GL {
constructor(canvas){
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
if (!gl) {
alert("Unable to initialize WebGL. Your browser may not support it.");
return;
}
this.gl = gl;
//init shaders
var fragmentShader = getShader(gl, "fshader");
var vertexShader = getShader(gl, "vshader");
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("An error occurred linking the shaders: " + gl.getProgramInfoLog(shaderProgram));
return;
}
this.shaderProgram = shaderProgram;
this.positionLocation = gl.getAttribLocation(shaderProgram, "position");
this.texCoordLocation = gl.getAttribLocation(shaderProgram, "texcoord");
this.resolutionLocation = gl.getUniformLocation(shaderProgram, "resolution");
this.blurOffsetLocation = gl.getUniformLocation(shaderProgram, "blurOffset");
// init texture coordinates
this.texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]),
gl.STATIC_DRAW);
// create position buffer
this.positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
//create texture
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
//normalize image to powers of two
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
function getShader(gl, id) {
var shaderScript, theSource, currentChild, shader;
shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var theSource = shaderScript.text;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
// Unknown shader type
return null;
}
gl.shaderSource(shader, theSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
};
};
render(bufferCanvas, x, y, blurAmount) {
var gl = this.gl;
gl.clear(this.gl.COLOR_BUFFER_BIT);
gl.useProgram(this.shaderProgram);
// setup buffers and attributes
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
gl.enableVertexAttribArray(this.texCoordLocation);
gl.vertexAttribPointer(this.texCoordLocation, 2, gl.FLOAT, false, 0, 0);
//load buffer
gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
gl.enableVertexAttribArray(this.positionLocation);
gl.vertexAttribPointer(this.positionLocation, 2, gl.FLOAT, false, 0, 0);
//draw size and position
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x, y,
x + bufferCanvas.width, y,
x, y + bufferCanvas.height,
x, y + bufferCanvas.height,
x+ bufferCanvas.width, y,
x+ bufferCanvas.width, y + bufferCanvas.height]), gl.STATIC_DRAW);
//load texture from 2d canvas
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.texImage2D(gl.TEXTURE_2D,
0,
gl.RGBA,
gl.RGBA,
gl.UNSIGNED_BYTE,
bufferCanvas);
//blur width
gl.uniform2f(this.blurOffsetLocation,
blurAmount / bufferCanvas.width,
blurAmount / bufferCanvas.height);
//set resolution
gl.uniform2f(this.resolutionLocation,
gl.canvas.width,
gl.canvas.height);
//draw
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
};
var canvas2d = document.getElementById('buffer-canvas');
var context2d = canvas2d.getContext("2d");
var canvasGL = new GL(document.getElementById('main-canvas'));
function render(time) {
time *= 0.001;
var r = Math.floor(Math.random() * 255);
var g = Math.floor(Math.random() * 255);
var b = Math.floor(Math.random() * 255);
var a = Math.floor(Math.random() * 255);
context2d.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a + ")";
var x = Math.random() * canvas2d.width;
var y = Math.random() * canvas2d.height;
var width = canvas2d.width - (Math.random() * canvas2d.width);
var height = canvas2d.height - (Math.random() * canvas2d.height);
context2d.fillRect(x, y, width , height);
canvasGL.render(canvas2d, 0, 0, Math.sin(time) * 5);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script id="vshader" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 position;
attribute vec2 texcoord;
uniform vec2 resolution;
uniform vec2 blurOffset;
varying vec2 texcoord11;
varying vec2 texcoord00;
varying vec2 texcoord02;
varying vec2 texcoord20;
varying vec2 texcoord22;
void main()
{
gl_Position = vec4(((position / resolution) * 2.0 - 1.0) * vec2(1, -1), 0, 1);
// get texcoords
texcoord11 = texcoord;
texcoord00 = texcoord + blurOffset * vec2(-1, -1);
texcoord02 = texcoord + blurOffset * vec2( 1, -1);
texcoord20 = texcoord + blurOffset * vec2( 1, 1);
texcoord22 = texcoord + blurOffset * vec2(-1, 1);
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D image;
varying vec2 texcoord11;
varying vec2 texcoord00;
varying vec2 texcoord02;
varying vec2 texcoord20;
varying vec2 texcoord22;
void main()
{
vec4 blur;
blur = texture2D(image, texcoord11);
blur += texture2D(image, texcoord00);
blur += texture2D(image, texcoord02);
blur += texture2D(image, texcoord20);
blur += texture2D(image, texcoord22);
// do you really want to blend the alpha?
gl_FragColor = 0.2 * blur;
}
</script>
<canvas id="main-canvas" width="400" height="300" style="border:1px solid black;"></canvas>
<canvas id="buffer-canvas" width="400" height="300" style="display: none;"></canvas>

Single color from javascript to fragment shader

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?
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>
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>

Categories

Resources