WebGL Recreate Overlapping Triangles - javascript

I'm trying to create a program without css that displays several overlapping triangles on a black background. Once I can get the triangles to display (like in the picture attached only with different colors), I need to implement either a z-buffer or a painter's algorithm; but first, I can't figure out what's wrong in the code below that they aren't displaying at all. I think it's an issue with my shaders perhaps?
<!doctype html>
<html>
<body>
<canvas width = "500" height = "500" id = "canvas"></canvas>
<script>
var canvas = document.getElementById('canvas');
gl = canvas.getContext('webgl');
var positions = [
0, 1, -1, -1, 1, -1,
-1, -1, -0, -1, -1, -0.5,
1, -1, 0, -1, 1, -0.5,
1, 1, 0, 1, 1, -1,
-1, 1, -1, 0, -0, 1
];
var colors = [
1,0,1, 1,0,1, 1,0,1, 1,0,1,//purple
0,0,1, 0,0,1, 0,0,1, 0,0,1,//blue
1,1,0, 1,1,0, 1,1,0, 1,1,0,//yellow
1,0,0, 1,0,0, 1,0,0, 1,0,0,//red
0,1,0, 0,1,0, 0,1,0, 0,1,0, //green
];
//vertex buffer
var vbuffer = gl.createBuffer ();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
//color buffer
var cbuffer = gl.createBuffer ();
gl.bindBuffer(gl.ARRAY_BUFFER, cbuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
//shaders
var vCode =
`
attribute vec2 a_Position;
attribute vec3 a_Color;
varying vec3 v_Color;
void main()
{
gl_Position = vec4(a_Position, 0.0, 1.0);
v_Color = a_Color;
}`;
var fragCode =
`
precision mediump float;
varying vec3 v_Color;
void main()
{
gl_FragColor = vec4(v_Color, 1.0);
}`;
//vertex shader
var vShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vShader, vCode);
gl.compileShader(vShader);
//fragment shader
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fShader, fragCode);
gl.compileShader(fShader);
var shaders = gl.createProgram();
gl.attachShader(shaders, vShader);
gl.attachShader(shaders, fShader);
gl.linkProgram(shaders);
//vertex buffer
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
var _position = gl.getAttribLocation(shaders, "position");
gl.vertexAttribPointer(_position, 3, gl.FLOAT, false,0,0);
gl.enableVertexAttribArray(_position);
//color buffer
gl.bindBuffer(gl.ARRAY_BUFFER, cbuffer);
var _color = gl.getAttribLocation(shaders, "color");
gl.vertexAttribPointer(_color, 3, gl.FLOAT, false,0,0) ;
gl.enableVertexAttribArray(_color);
gl.useProgram(shaders);
//depth testing
gl.disable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.depthMask(gl.FALSE);
//clears buffer
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.clearDepth(1.0);
gl.viewport(0.0, 0.0, canvas.width, canvas.height);
gl.clear(gl.cbuffer_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 15);
</script>
</body>
</html>

There are four mistakes in the code. I corrected the mistakes and added explanations. Here is the working code:
<!doctype html>
<html>
<body>
<canvas width = "500" height = "500" id = "canvas"></canvas>
<script>
var canvas = document.getElementById('canvas');
gl = canvas.getContext('webgl');
var positions = [
0, 1, -1, -1, 1, -1,
-1, -1, -0, -1, -1, -0.5,
1, -1, 0, -1, 1, -0.5,
1, 1, 0, 1, 1, -1,
-1, 1, -1, 0, -0, 1
];
/*var colors = [
1,0,1, 1,0,1, 1,0,1, 1,0,1,//purple
0,0,1, 0,0,1, 0,0,1, 0,0,1,//blue
1,1,0, 1,1,0, 1,1,0, 1,1,0,//yellow
1,0,0, 1,0,0, 1,0,0, 1,0,0,//red
0,1,0, 0,1,0, 0,1,0, 0,1,0, //green
];
BUG 1: Color array should be 3 values per vertex. This code assumes 4 values are used
*/
var colors = [
1,0,1, 1,0,1, 1,0,1, //purple
0,0,1, 0,0,1, 0,0,1, //blue
1,1,0, 1,1,0, 1,1,0, //yellow
1,0,0, 1,0,0, 1,0,0, //red
0,1,0, 0,1,0, 0,1,0 //green
];
//vertex buffer
var vbuffer = gl.createBuffer ();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
//color buffer
var cbuffer = gl.createBuffer ();
gl.bindBuffer(gl.ARRAY_BUFFER, cbuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
//shaders
var vCode =
`
attribute vec2 a_Position;
attribute vec3 a_Color;
varying vec3 v_Color;
void main()
{
gl_Position = vec4(a_Position, 0.0, 1.0);
v_Color = a_Color;
}`;
var fragCode =
`
precision mediump float;
varying vec3 v_Color;
void main()
{
gl_FragColor = vec4(v_Color, 1.0);
}`;
//vertex shader
var vShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vShader, vCode);
gl.compileShader(vShader);
//fragment shader
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fShader, fragCode);
gl.compileShader(fShader);
var shaders = gl.createProgram();
gl.attachShader(shaders, vShader);
gl.attachShader(shaders, fShader);
gl.linkProgram(shaders);
//vertex buffer
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
//var _position = gl.getAttribLocation(shaders, "position"); BUG 2: attribute name is "a_Position"
var _position = gl.getAttribLocation(shaders, "a_Position");
//gl.vertexAttribPointer(_position, 3, gl.FLOAT, false,0,0); BUG 3: a_Position is a vec2. It only accepts 2 values.
gl.vertexAttribPointer(_position, 2, gl.FLOAT, false,0,0);
gl.enableVertexAttribArray(_position);
//color buffer
gl.bindBuffer(gl.ARRAY_BUFFER, cbuffer);
//var _color = gl.getAttribLocation(shaders, "color"); BUG 4: attribute name is "a_Color"
var _color = gl.getAttribLocation(shaders, "a_Color");
gl.vertexAttribPointer(_color, 3, gl.FLOAT, false,0,0) ;
gl.enableVertexAttribArray(_color);
gl.useProgram(shaders);
//depth testing
gl.disable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.depthMask(gl.FALSE);
//clears buffer
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.clearDepth(1.0);
gl.viewport(0.0, 0.0, canvas.width, canvas.height);
gl.clear(gl.cbuffer_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 15);
</script>
</body>
</html>

Related

WebGL Rotating cube overriding faces

I am creating a simple WebGL program that creates a cube rotating around the Z axis. The problem is that when its rotating some sides of it override the others and you don't see each individual side of the cube. I looked for a solution online but it makes it worse. They enable the face culling and for them, it seems to work but for me, unfortunately, it doesn't. Can anyone tell me what do I need to add into my code in order to fix the face overriding?
Full Source Code:
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
var a = 24;
var positions = [
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.5, 0.5, 0.0,
-0.5, 0.5, 0.0,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
//0.5, 0.5, 1.0,
];
var indices = [
0, 1, 2, 0, 2, 3, // Front
0, 4, 7, 0, 3, 7, // Side 1
1, 5, 6, 1, 2, 6, // Side 2
4, 5, 6, 6, 7, 4 // Back
];
var rotation_angle = 1;
var radians = (rotation_angle * Math.PI)/180;
var rotation = [Math.sin(radians), 0.0, Math.cos(radians)];
var _buffer = gl.createBuffer();
var i_buffer = gl.createBuffer();
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
precision mediump float;
attribute vec3 position;
uniform vec3 rotation;
varying vec3 f_col;
void main(){
vec2 newPos = vec2(
position.x * rotation.x - position.z * rotation.z,
position.x * rotation.z + position.z * rotation.x
);
gl_Position = vec4(newPos.x, position.y, newPos.y, 1.0);
f_col = position;
}
`);
gl.compileShader(vertexShader);
// Check if it compiled
var success2 = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS);
if (!success2) {
// Something went wrong during compilation; get the error
throw "could not compile shader:" + gl.getShaderInfoLog(vertexShader);
}
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
precision mediump float;
varying vec3 f_col;
void main() {
gl_FragColor = vec4(f_col.x, 0.2, f_col.z, 1.0);
}
`);
gl.compileShader(fragmentShader);
var success1 = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
if (!success1) {
// Something went wrong during compilation; get the error
throw "could not compile shader:" + gl.getShaderInfoLog(fragmentShader);
}
var program = gl.createProgram();
// Attach pre-existing shaders
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var attributeLoc = gl.getAttribLocation(program, "position");
gl.bindBuffer(gl.ARRAY_BUFFER, _buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions),
gl.STATIC_DRAW);
gl.vertexAttribPointer(attributeLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(attributeLoc);
gl.useProgram(program);
var rotation_location = gl.getUniformLocation(program, "rotation");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, i_buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),
gl.STATIC_DRAW);
function loop() {
gl.clearColor(0.75, 0.85, 0.8, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//gl.drawArrays(gl.TRIANGLE_FAN, 0, 8);
gl.drawElements(gl.TRIANGLES, a, gl.UNSIGNED_SHORT, 0);
//gl.drawArrays(gl.TRIANGLE_STRIP, 1, 3);
rotation_angle++;
var radians = (rotation_angle * Math.PI)/180;
var rotation = [Math.sin(radians), 0.0, Math.cos(radians)];
gl.uniform3fv(rotation_location, rotation);
requestAnimationFrame(loop);
}
loop();
<canvas id="canvas" width="500" height="500"></canvas>

WebGL "draw a square" program, need help debugging it

I am following the LearningWebGL tutorials, I am not able to reproduce the first code itself. Here is the buggy code :
var modelView = mat4.create()
var projection = mat4.create()
var vertexPositionPointer, modelViewMatrixPointer, projectionMatrixPointer
var vertexBuffer
var vertices =
[
0.5, 0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0
]
var vertexShaderSource =
"attribute vec3 vertexPosition;"+ "\n" +
"uniform mat4 modelView;"+ "\n" +
"uniform mat4 projection;"+ "\n" +
"void main(){"+ "\n" +
"gl_Position = projection * modelView * vec4(vertexPosition, 1.0);"+ "\n" +
"}";
var fragmentShaderSource =
"void main(){"+ "\n" +
"gl_FragColor = vec4(1.0, 0, 0, 1.0);"+ "\n" +
"}";
function setup(){
var canvas = document.getElementById('canvas')
var gl = canvas.getContext("webgl", {antialias : false})
gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight)
var vertexShader = getShader(gl, gl.VERTEX_SHADER, vertexShaderSource)
var fragmentShader = getShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource)
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
vertexPositionPointer = gl.getAttribLocation(program, "vertexPosition")
gl.enableVertexAttribArray(vertexPositionPointer)
modelViewMatrixPointer = gl.getUniformLocation(program, "modelView")
projectionMatrixPointer = gl.getUniformLocation(program, "projection")
vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
gl.vertexAttribPointer(vertexPositionPointer, 3, gl.FLOAT, gl.FALSE, 0, 0)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
mat4.translate(modelView, modelView, [0, 0, -1.4])
mat4.perspective(projection, Math.PI/3.5, canvas.clientWidth/canvas.clientHeight, 1, 1000)
gl.uniformMatrix4fv(modelViewMatrixPointer, false, modelView)
gl.uniformMatrix4fv(projectionMatrixPointer, false, projection)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
}
function getShader(gl, type, source){
var shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS))
console.log(gl.getShaderInfoLog(shader))
return shader;
}
setup()
<script src="https://rawgit.com/toji/gl-matrix/master/dist/gl-matrix.js"></script>
<canvas id="canvas" style="border: none;"></canvas>
The above code produces :
Even though the z-translate value is set to -1, the entire canvas is painted red which too is incorrect.
What have I done wrong ?....
(some dummy text so that my word count increases and the post gets passed...)

How to work with framebuffers in webgl?

I have been trying to understand framebuffer in WebGL/OpenGL-ES.
I know that we can blend multiple textures using framebuffer.
So, to understand that I wrote a sample by taking a 1*1 texture and tried to apply framebuffer logic on top of it.
But , it didn't work.
See snippet at bottom, if you click on "mix red and blue", the images doesn't get rendered, am I doing anything wrong?
Code :
`
var canvas, gl, attrPosition, texture, program, vertexBuffer, textureBuffer, vertices, texVertices, attrPos, attrTexPos, textures = [], framebuffers = [];
canvas = document.getElementById('canvas');
gl = getWebGL();
vertices = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0,
]);
texVertices = new Float32Array([
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0
]);
var getProgram = function () {
var vs = createVertexShader([
'attribute vec2 attrPos;',
'attribute vec2 attrTexPos;',
'varying highp vec2 vTexCoord;',
'void main() {',
'\tgl_Position = vec4(attrPos, 0.0, 1.0);',
'}'
].join('\n'));
var fs = createFragmentShader([
'varying highp vec2 vTexCoord;',
'uniform sampler2D uImage;',
'void main() {',
'\tgl_FragColor = texture2D(uImage, vTexCoord);',
'}'
].join('\n'));
return createAndLinkPrograms(vs, fs);
};
var render = function () {
gl.clear(gl.DEPTH_BUFFER_BIT|gl.COLOR_BUFFER_BIT);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(attrPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
gl.vertexAttribPointer(attrTexPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 5);
};
if (gl) {
gl.clearColor(0.1, 0.5, 1.0, 1.0);
render();
program = getProgram();
texture = createAndSetupTexture();
vertexBuffer = createAndBindBuffer(vertices, gl.ARRAY_BUFFER);
attrPos = gl.getUniformLocation(program, 'attrPos');
gl.enableVertexAttribArray(attrPos);
textureBuffer = createAndBindBuffer(texVertices, gl.ARRAY_BUFFER);
attrTexPos = gl.getUniformLocation(program, 'attrTexPos');
gl.enableVertexAttribArray(attrTexPos);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([123, 0, 60, 255]));
render();
}
var initPingPongTextures = function(textures, framebuffers) {
for (var i = 0; i < 2; ++i) {
var tex = createAndSetupTexture(gl);
textures.push(tex);
// make the texture the same size as the image
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// Create a framebuffer
var fbo = gl.createFramebuffer();
framebuffers.push(fbo);
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
// Attach a texture to it.
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
}
}
var setFramebuffer = function(fbo, width, height) {
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.viewport(0, 0, width, height);
};
var mixRedAndBlue = function () {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
setFramebuffer(framebuffers[0], 1, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
render();
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
setFramebuffer(framebuffers[1], 1, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 255, 0, 255]));
render();
gl.bindTexture(gl.TEXTURE_2D, textures[1]);
setFramebuffer(null, 1, 1);
render();
};`
var getWebGLContext = function(canvas) {
var webglContextParams = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
var webglContext = null;
for (var index = 0; index < webglContextParams.length; index++) {
try {
webglContext = canvas.getContext(webglContextParams[index]);
if(webglContext) {
//breaking as we got our context
break;
}
} catch (E) {
console.log(E);
}
}
if(webglContext === null) {
alert('WebGL is not supported on your browser.');
} else {
//WebGL is supported in your browser, lets render the texture
}
fillGLForCleanUp(webglContext);
return webglContext;
}
var createVertexShader = function (vertexShaderSource) {
console.log(vertexShaderSource);
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
return vertexShader;
}
var createFragmentShader = function (fragmentShaderSource) {
console.log(fragmentShaderSource);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
return fragmentShader;
}
var createAndLinkPrograms = function (vertexShader, fragmentShader) {
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
alert('Could not initialise shaders');
}
gl.useProgram(program);
return program;
}
var createAndBindBuffer = function (verticesOrIndices, bufferType) {
var buffer = gl.createBuffer();
gl.bindBuffer(bufferType, buffer);
gl.bufferData(bufferType, verticesOrIndices, gl.STATIC_DRAW);
//clear memory
gl.bindBuffer(bufferType, null);
return buffer;
}
var allowAllImageSizes = function() {
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.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// gl.bindTexture(gl.TEXTURE_2D, null);
}
var createAndSetupTexture = function() {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
allowAllImageSizes();
gl.textures.push(texture);
return texture;
}
var getWebGL = function (canvas, width, height) {
if(!canvas) {
canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = !width ? 512 : width;
canvas.height = !height ? 512 : height;
document.body.appendChild(canvas);
} else {
canvas.width = !width ? 512 : width;
canvas.height = !height ? 512 : height;
}
return getWebGLContext(canvas);
}
var fillGLForCleanUp = function (gl) {
gl.textures = [];
gl.framebuffers = [];
gl.array_buffer = [];
gl.element_array_buffers = [];
}
var canvas, gl, attrPosition, texture, program,
vertexBuffer, textureBuffer, vertices, texVertices,
attrPos, attrTexPos, textures = [], framebuffers = [];
canvas = document.getElementById('canvas');
gl = getWebGL(canvas);
vertices = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0,
]);
texVertices = new Float32Array([
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0
]);
var getProgram = function () {
var vs = createVertexShader([
'attribute vec2 attrPos;',
'attribute vec2 attrTexPos;',
'varying highp vec2 vTexCoord;',
'void main() {',
'\tgl_Position = vec4(attrPos, 0.0, 1.0);',
'}'
].join('\n'));
var fs = createFragmentShader([
'varying highp vec2 vTexCoord;',
'uniform sampler2D uImage;',
'void main() {',
'\tgl_FragColor = texture2D(uImage, vTexCoord);',
'}'
].join('\n'));
return createAndLinkPrograms(vs, fs);
};
var render = function () {
gl.clear(gl.DEPTH_BUFFER_BIT|gl.COLOR_BUFFER_BIT);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(attrPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
gl.vertexAttribPointer(attrTexPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 5);
};
if (gl) {
gl.clearColor(0.1, 0.5, 1.0, 1.0);
render();
program = getProgram();
texture = createAndSetupTexture();
vertexBuffer = createAndBindBuffer(vertices, gl.ARRAY_BUFFER);
attrPos = gl.getUniformLocation(program, 'attrPos');
gl.enableVertexAttribArray(attrPos);
textureBuffer = createAndBindBuffer(texVertices, gl.ARRAY_BUFFER);
attrTexPos = gl.getUniformLocation(program, 'attrTexPos');
gl.enableVertexAttribArray(attrTexPos);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([123, 0, 60, 255]));
render();
}
var initPingPongTextures = function(textures, framebuffers) {
for (var i = 0; i < 2; ++i) {
var tex = createAndSetupTexture(gl);
textures.push(tex);
// make the texture the same size as the image
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// Create a framebuffer
var fbo = gl.createFramebuffer();
framebuffers.push(fbo);
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
// Attach a texture to it.
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
}
}
var setFramebuffer = function(fbo, width, height) {
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.viewport(0, 0, width, height);
};
var mixRedAndBlue = function () {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
setFramebuffer(framebuffers[0], 1, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
render();
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
setFramebuffer(framebuffers[1], 1, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 255, 0, 255]));
render();
gl.bindTexture(gl.TEXTURE_2D, textures[1]);
setFramebuffer(null, 1, 1);
render();
};
<button id="redImg" onclick="mixRedAndBlue()">Mix Red and blue</button><hr/>
<canvas id="canvas" width=512 height=512></canvas>
Edit 1 :
I am trying to achieve the same for multiple programs with multiple fragment shaders because having if/else statements within the fragment shader is not recommended as it runs for each pixel.
`
Shaders.prototype.VS_Base = [
'attribute vec3 verticesPosition;',
'attribute vec2 texturePosition;',
'varying highp vec2 vTextureCoord;',
'void main(void) {',
'\tgl_Position = vec4(verticesPosition * vec3(1.0, -1.0, 1.0), 0.5);',
'\tvTextureCoord = texturePosition;',
'}'
].join('\n');
Shaders.prototype.FS_Base_Image_RED = [
'#ifdef GL_ES',
'precision highp float;',
'#endif',
'uniform sampler2D uImage;',
'varying highp vec2 vTextureCoord;',
'void main (void) {',
'\tgl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);//texture2D(uImage, vTextureCoord);',
'}'
].join('\n');
Shaders.prototype.FS_Base_Image_BLUE = [
'#ifdef GL_ES',
'precision highp float;',
'#endif',
'uniform sampler2D uImage;',
'varying highp vec2 vTextureCoord;',
'void main (void) {',
'\tgl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);//texture2D(uImage, vTextureCoord);',
'}'
].join('\n');`
Now I have 2 separate programs for both the fragment shader and I need to use framebuffers for mixing Red and Blue. I am not looking for mix() as the actual scenario is very complex and that's the reason I am using multiple programs with fragment shaders for avoiding conditional if/else statements.
It's not clear what you're trying to do. Framebuffers are just a list of attachments (textures and renderbuffers). You use them to render to a texture and/or renderbuffer. Then you can use the texture you just rendered to as input to some other render.
Here's an example with NO framebuffers. It blends 2 textures.
var vs = `
attribute vec4 position;
varying vec2 v_texcoord;
void main() {
gl_Position = position;
v_texcoord = position.xy * .5 + .5;
}
`;
var fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex1;
uniform sampler2D tex2;
void main() {
vec4 color1 = texture2D(tex1, v_texcoord);
vec4 color2 = texture2D(tex2, v_texcoord);
gl_FragColor = mix(color1, color2, 0.5);
}
`;
const gl = document.querySelector("canvas").getContext("webgl");
const program = twgl.createProgramFromSources(gl, [vs, fs]);
// make 2 textures with canvas 2d
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 64;
ctx.canvas.height = 64;
// first texture has a circle
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 64, 64);
ctx.strokeStyle = "yellow";
ctx.beginPath();
ctx.arc(32, 32, 20, 0, Math.PI * 2, false);
ctx.lineWidth = 12;
ctx.stroke();
const tex1 = createTextureFromCanvas(gl, ctx.canvas);
// second texture has a diamond (diagonal square)
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 64, 64);
ctx.fillStyle = "cyan";
ctx.beginPath();
ctx.moveTo(32, 6);
ctx.lineTo(58, 32);
ctx.lineTo(32, 58);
ctx.lineTo(6, 32);
ctx.lineTo(32, 6);
ctx.fill();
const tex2 = createTextureFromCanvas(gl, ctx.canvas);
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1,
1, -1,
-1, 1,
-1, 1,
1, -1,
1, 1,
]), gl.STATIC_DRAW);
const positionLoc = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
const tex1Loc = gl.getUniformLocation(program, "tex1");
const tex2Loc = gl.getUniformLocation(program, "tex2");
gl.useProgram(program);
gl.uniform1i(tex1Loc, 0);
gl.uniform1i(tex2Loc, 1);
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, tex1);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, tex2);
gl.drawArrays(gl.TRIANGLES, 0, 6);
function createTextureFromCanvas(gl, canvas) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ctx.canvas);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
return tex;
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<canvas></canvas>
For your purpose there is no difference about the blending part, the only difference is where the textures come from. Above the textures were created by using a 2d canvas. Instead you can use framebuffer to render to a texture. AFTER you've rendered to a texture you can then use that texture in some other render just like above.
To render to a texture first you create a framebuffer
var fb = gl.createFramebuffer();
Then you attach a texture to it
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0, // attach texture as COLOR_ATTACHMENT0
gl.TEXTURE_2D, // attach a 2D texture
someTexture, // the texture to attach
0); // the mip level to render to (must be 0 in WebGL1)
Depending on your attachments you should check if they work.
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
// these attachments don't work
}
The WebGL spec lists 3 combinations of attachments that are guaranteed to work. The example below is using one of those 3 so there's no need to check
Now if you bind the framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
Then when you call any gl.drawXXX function or gl.clear it will be drawing to the someTexture instead of the canvas. To start drawing to the canvas again bind null
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
Remember that if the canvas and the texture are different sizes you'll need to call gl.viewport to render correctly
var vs = `
attribute vec4 position;
uniform mat4 matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
v_texcoord = position.xy * .5 + .5;
}
`;
var colorFS = `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
var mixFS = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex1;
uniform sampler2D tex2;
void main() {
// probably should use different texture coords for each
// texture for more flexibility but I'm lazy
vec4 color1 = texture2D(tex1, v_texcoord);
vec4 color2 = texture2D(tex2, v_texcoord);
gl_FragColor = mix(color1, color2, 0.5);
}
`;
const gl = document.querySelector("canvas").getContext("webgl");
const colorProgram = twgl.createProgramFromSources(gl, [vs, colorFS]);
const mixProgram = twgl.createProgramFromSources(gl, [vs, mixFS]);
// make 2 textures by attaching them to framebuffers and rendering to them
const texFbPair1 = createTextureAndFramebuffer(gl, 64, 64);
const texFbPair2 = createTextureAndFramebuffer(gl, 64, 64);
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1,
1, -1,
-1, 1,
-1, 1,
1, -1,
1, 1,
]), gl.STATIC_DRAW);
function setAttributes(buf, positionLoc) {
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
}
const colorPrgPositionLoc = gl.getAttribLocation(colorProgram, "position");
setAttributes(buf, colorPrgPositionLoc);
const colorLoc = gl.getUniformLocation(colorProgram, "color");
const colorProgMatrixLoc = gl.getUniformLocation(colorProgram, "matrix");
// draw red rect to first texture through the framebuffer it's attached to
gl.useProgram(colorProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair1.fb);
gl.viewport(0, 0, 64, 64);
gl.uniform4fv(colorLoc, [1, 0, 0, 1]);
gl.uniformMatrix4fv(colorProgMatrixLoc, false, [
0.5, 0, 0, 0,
0,.25, 0, 0,
0, 0, 1, 0,
.2,.3, 0, 1,
]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Draw a blue rect to the second texture through the framebuffer it's attached to
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair2.fb);
gl.viewport(0, 0, 64, 64);
gl.uniform4fv(colorLoc, [0, 0, 1, 1]);
gl.uniformMatrix4fv(colorProgMatrixLoc, false, [
0.25, 0, 0, 0,
0,.5, 0, 0,
0, 0, 1, 0,
.2,.3, 0, 1,
]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Draw both textures to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
const mixPrgPositionLoc = gl.getAttribLocation(mixProgram, "position");
setAttributes(buf, mixPrgPositionLoc);
const mixProgMatrixLoc = gl.getUniformLocation(mixProgram, "matrix");
const tex1Loc = gl.getUniformLocation(mixProgram, "tex1");
const tex2Loc = gl.getUniformLocation(mixProgram, "tex2");
gl.useProgram(mixProgram);
gl.uniform1i(tex1Loc, 0);
gl.uniform1i(tex2Loc, 1);
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, texFbPair1.tex);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, texFbPair2.tex);
gl.uniformMatrix4fv(mixProgMatrixLoc, false, [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
function createTextureAndFramebuffer(gl, width, height) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
return {tex: tex, fb: fb};
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<canvas></canvas>
The only functional difference between the first program and the second is how the textures got their data. In the first example the textures got their data from a canvas 2d. In the 2nd example the textures got their data by rendering to them using WebGL.
As for why your example doesn't blend textures, in order to blend 2 textures you need a shader that uses two textures.

WebGL Multi-Textured Cube

I need to texture a cube with different textures for each side, unless there is an advantage doing it by single side. This is my Vertex Shader:..
precision mediump float;
attribute vec3 vertPosition;
attribute vec2 vertTexCoord;
attribute float aFace;
uniform mat4 mWorld;
uniform mat4 mView;
uniform mat4 mProj;
varying vec2 fragTexCoord;
varying float vFace;
void main()
{
fragTexCoord = vertTexCoord;
vFace = aFace;
gl_Position = mProj * mView * mWorld * vec4(vertPosition, 1.0);
}
Fragment Shader:..
precision mediump float;
uniform sampler2D front;
uniform sampler2D back;
uniform sampler2D top;
uniform sampler2D bottom;
uniform sampler2D right;
uniform sampler2D left;
varying vec2 fragTexCoord;
varying float vFace;
void main()
{
if(vFace < 0.1)
gl_FragColor = texture2D(front, fragTexCoord);
else if(vFace < 1.1)
gl_FragColor = texture2D(back, fragTexCoord);
else if(vFace < 2.1)
gl_FragColor = texture2D(top, fragTexCoord);
else if(vFace < 3.1)
gl_FragColor = texture2D(bottom, fragTexCoord);
else if(vFace < 4.1)
gl_FragColor = texture2D(right, fragTexCoord);
else
gl_FragColor = texture2D(left, fragTexCoord);
}totorials
Then before I start rendering this runs. (The variables are globally defined):..
cubeVertexBufferObject = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBufferObject);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeVertices), gl.STATIC_DRAW);
cubeIndexBufferObject = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeIndexBufferObject);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeIndices), gl.STATIC_DRAW);
textureLookUpBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureLookUpBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(lookUpArray), gl.STATIC_DRAW);
cubeVertexTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
positionAttributeLocation = gl.getAttribLocation(program, 'vertPosition');
gl.vertexAttribPointer(
positionAttributeLocation,
3,
gl.FLOAT,
gl.FALSE,
5 * Float32Array.BYTES_PER_ELEMENT,
0
);
texCoordAttributeLocation = gl.getAttribLocation(program, 'vertTexCoord');
gl.vertexAttribPointer(
texCoordAttributeLocation,
2,
gl.FLOAT,
gl.FALSE,
5 * Float32Array.BYTES_PER_ELEMENT,
3 * Float32Array.BYTES_PER_ELEMENT
);
textureLookUpAttribute = gl.getAttribLocation(program, "aFace");
gl.vertexAttribPointer(
textureLookUpAttribute,
1,
gl.FLOAT,
false,
0,
0
);
faces.front = gl.getUniformLocation(program,"front");
faces.back = gl.getUniformLocation(program,"back");
faces.top = gl.getUniformLocation(program,"top");
faces.bottom = gl.getUniformLocation(program,"bottom");
faces.right = gl.getUniformLocation(program,"right");
faces.left = gl.getUniformLocation(program,"left");
//
cubeMatWorldUniformLocation = gl.getUniformLocation(program, 'mWorld');
cubeMatViewUniformLocation = gl.getUniformLocation(program, 'mView');
cubeMatProjUniformLocation = gl.getUniformLocation(program, 'mProj');
worldMatrix = new Float32Array(16);
viewMatrix = new Float32Array(16);
projMatrix = new Float32Array(16);
mat4.identity(worldMatrix);
mat4.lookAt(viewMatrix, [0, 0, -8], [0, 0, 0], [0, 1, 0]);
mat4.perspective(projMatrix, glMatrix.toRadian(45), Canvas.width / Canvas.height, 0.1, 1000.0);
gl.uniformMatrix4fv(cubeMatWorldUniformLocation, gl.FALSE, worldMatrix);
gl.uniformMatrix4fv(cubeMatViewUniformLocation, gl.FALSE, viewMatrix);
gl.uniformMatrix4fv(cubeMatProjUniformLocation, gl.FALSE, projMatrix);
Finally every time I render this runs:..
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
gl.vertexAttribPointer(texCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBufferObject);
gl.vertexAttribPointer(texCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, textureLookUpBuffer);
gl.vertexAttribPointer(textureLookUpAttribute, 1, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeIndexBufferObject);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(faces.front, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, grass);
gl.uniform1i(faces.back, 1);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(faces.top, 2);
gl.activeTexture(gl.TEXTURE3);
gl.bindTexture(gl.TEXTURE_2D, grass);
gl.uniform1i(faces.bottom, 3);
gl.activeTexture(gl.TEXTURE4);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(faces.right, 4);
gl.activeTexture(gl.TEXTURE5);
gl.bindTexture(gl.TEXTURE_2D, grass);
gl.uniform1i(faces.left, 5);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
It gives me error messages. Their different when I changed the code slightly(When I was trying to fix it) so that why i don't have an exact error message. If you need the different arrays for the cube i can post them. I was fallowing online tutorials on how to do this(on in general) but obviously as you can see it didn't work.
The most common way to texture a cube with a different face on each side is to use a texture atlas. Put all the faces in 1 texture and use texture coordinates to select the right part of the texture for each face.
See the bottom of this article
The advantages are there's 1 texture. 1 texture unit to setup. 1 uniform to set. More samplers left for other things (normal maps, etc..) A simple shader that runs faster.

Translate 3D cube on x-axis

Im trying to translate two 3D cubes away from each other. I've tried using the clipspace aswell as the translation matrix but nothing has worked. The solution im looking for is the cubes side-by-side preferably on the x-axis.
Here is my code:
var gl,program,canvas;
var vBuffer, vPosition;
var idxBuffer;
var vertices = [
-0.5, 0.5, 1,
0.5, 0.5, 1,
0.5, -0.5, 1,
-0.5, -0.5, 1,
-0.5, 0.5, 0,
0.5, 0.5, 0,
0.5, -0.5, 0,
-0.5, -0.5, 0
];
var dVecIdx = new Uint16Array([
0, 1, 1, 2,
2, 3, 3, 0,
4, 5, 5, 6,
6, 7, 7, 4,
0, 4, 1, 5,
2, 6, 3, 7
]);
var projection = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 1,
0, 0, 0, 1
];
var a = Math.sqrt(0.5);
var rotation = [
a, 0, a, 0,
0, 1, 0, 0,
-a, 0, a, 0,
0, 0, 0, 1
];
window.onload = function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn't available"); }
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.enable(gl.DEPTH_TEST);
program = initShaders(gl, "vertex-shader", "fragment-shader");
vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
vPosition = gl.getAttribLocation(program, "vPosition");
gl.enableVertexAttribArray(vPosition);
gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);
idxBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, dVecIdx, gl.STATIC_DRAW);
render();
}
function render() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
projLoc = gl.getUniformLocation(program, "projectionMatrix");
loc = gl.getUniformLocation(program, "rotate");
gl.uniformMatrix4fv(projLoc, false, projection);
gl.uniformMatrix4fv(loc, false, projection);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
projLoc = gl.getUniformLocation(program, "projectionMatrix");
loc = gl.getUniformLocation(program, "rotate");
gl.uniformMatrix4fv(projLoc, false, projection);
gl.uniformMatrix4fv(loc, false, rotation);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
requestAnimFrame(render);
}
<html>
<head>
<script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/initShaders.js"></script>
<script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/MV.js"></script>
<script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/webgl-utils.js"></script>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec3 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
uniform mat4 projectionMatrix;
uniform mat4 rotate;
void main() {
gl_Position = projectionMatrix * rotate * vec4(vPosition, 1);
fColor = vColor;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
void main() {
gl_FragColor = fColor;
}
</script>
</head>
<body>
<div style="border: 1px dotted black;">
<div style="text-align:center">
<canvas id="gl-canvas" width="500" height="500"></canvas>
</div>
</div>
</body>
</html>
Any help is appreciated!
If you try getting too much more advanced with your setup I would recommend either using a WebGL library such as Three.js to abstract away some of the math or really taking the time to google around and understand object and camera transformation matrices.
Answer:
Having said that, the simple answer is to just add another matrix for translations and insert it between your projection and rotation matrix in the shader:
attribute vec3 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
uniform mat4 projectionMatrix;
uniform mat4 rotate;
uniform mat4 translate;
void main() {
gl_Position = projectionMatrix * translate * rotate * vec4(vPosition, 1);
fColor = vColor;
}
The translation matrix will look like:
var translation = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
x, y, z, 1
];
where x, y, and z are the translation distances along the X-axis, Y-axis, and Z-axis respectively.
This would then be added to the render method in the same way as the rotation matrix:
transLoc = gl.getUniformLocation(program, "translate");
gl.uniformMatrix4fv(transLoc, false, translation);
Optimizations:
Now also having said that, there are a few more optimizations/corrections you can make:
1) Since WebGL maintains its "state" until changed (keeps things bound/set/enabled/etc.), you can remove a lot of the repeated code in your render() method:
function render() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// set state
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
projLoc = gl.getUniformLocation(program, "projectionMatrix");
loc = gl.getUniformLocation(program, "rotate");
gl.uniformMatrix4fv(projLoc, false, projection);
// draw shape 1
gl.uniformMatrix4fv(loc, false, projection);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
// draw shape 2
gl.uniformMatrix4fv(loc, false, rotation);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
requestAnimFrame(render);
}
2) If you don't want to use a specific matrix during rendering you should set it to the identity matrix which doesn't change other matrices/vectors when multiplied:
var identity = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
This is what you should use for the rotation matrix on your first shape instead of the perspective matrix:
// draw shape 1
gl.uniformMatrix4fv(loc, false, identity);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
3) You can declare the progLoc, rotLoc, and transLoc as global variables and set their values as soon as the program is initialized. These won't change for a single program and don't need to be reset in the render loop.
program = initShaders(gl, "vertex-shader", "fragment-shader");
projLoc = gl.getUniformLocation(program, "projectionMatrix");
rotLoc = gl.getUniformLocation(program, "rotate");
transLoc = gl.getUniformLocation(program, "translate");
Making the final code:
var gl,program,canvas;
var vBuffer, vPosition;
var idxBuffer;
var projLoc, rotLoc, transLoc;
var vertices = [
-0.5, 0.5, 1,
0.5, 0.5, 1,
0.5, -0.5, 1,
-0.5, -0.5, 1,
-0.5, 0.5, 0,
0.5, 0.5, 0,
0.5, -0.5, 0,
-0.5, -0.5, 0
];
var dVecIdx = new Uint16Array([
0, 1, 1, 2,
2, 3, 3, 0,
4, 5, 5, 6,
6, 7, 7, 4,
0, 4, 1, 5,
2, 6, 3, 7
]);
var identity = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
var projection = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 1,
0, 0, 0, 1
];
var a = Math.sqrt(0.5);
var rotation = [
a, 0, a, 0,
0, 1, 0, 0,
-a, 0, a, 0,
0, 0, 0, 1
];
// actual translations are set in the render() function
var translation = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
window.onload = function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn't available"); }
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.enable(gl.DEPTH_TEST);
program = initShaders(gl, "vertex-shader", "fragment-shader");
projLoc = gl.getUniformLocation(program, "projectionMatrix");
rotLoc = gl.getUniformLocation(program, "rotate");
transLoc = gl.getUniformLocation(program, "translate");
vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
vPosition = gl.getAttribLocation(program, "vPosition");
gl.enableVertexAttribArray(vPosition);
gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);
idxBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, dVecIdx, gl.STATIC_DRAW);
render();
}
function render() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// set non-changing states
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.uniformMatrix4fv(projLoc, false, projection);
// draw shape 1
translation[12] = 1; // x-axis translation (y and z are 0)
gl.uniformMatrix4fv(transLoc, false, translation);
gl.uniformMatrix4fv(rotLoc, false, identity);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
// draw shape 2
translation[12] = -1; // set x-axis translation
gl.uniformMatrix4fv(transLoc, false, translation);
gl.uniformMatrix4fv(rotLoc, false, rotation);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
requestAnimFrame(render);
}
<html>
<head>
<script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/initShaders.js"></script>
<script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/MV.js"></script>
<script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/webgl-utils.js"></script>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec3 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
uniform mat4 projectionMatrix;
uniform mat4 rotate;
uniform mat4 translate;
void main() {
gl_Position = projectionMatrix * translate * rotate * vec4(vPosition, 1);
fColor = vColor;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
void main() {
gl_FragColor = fColor;
}
</script>
</head>
<body>
<div style="border: 1px dotted black;">
<div style="text-align:center">
<canvas id="gl-canvas" width="500" height="500"></canvas>
</div>
</div>
</body>
</html>
4) If you want to use your MV.js script you can also declare your matrices as mat4() objects and use mult() to multiply the matrices on the CPU before transferring data to the GPU (one multiplication per shape instead of one per vertex). You can also use it to create more versatile and accurate camera matrices:
var persp = perspective(30.0, 1, 0.1, 100); // fovy, aspect, near, far
var view = lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]); // eye, look, up
var projection2D = mult(persp, view);
var projection = []; // convert to 1D array
for(var i = 0; i < projection2D.length; i++) {
projection = projection.concat(projection2D[i]);
}
Hope this is helpful! Cheers!

Categories

Resources