How does alpha work in webGL with blending disabled? - webgl

I render a couple of vertices in point mode, with blending disabled, here's the relevant code:
var canvas = htmlToElement('<canvas width="100" height="100" style="margin:auto;display:block;"></canvas>');
document.body.appendChild(canvas);
gl = canvas.getContext('experimental-webgl');
var vertices = [
-0.5, 0.5, 0.0,
0.0, 0.5, 0.0,
];
var vertLen = vertices.length / 3;
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var vertShader = createShader(document.getElementById('vert').textContent, gl.VERTEX_SHADER);
var fragShader = createShader(document.getElementById('frag').textContent, gl.FRAGMENT_SHADER);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coord);
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.disable(gl.BLEND);
//gl.blendEquation(gl.FUNC_ADD);
//gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.POINTS, 0, vertLen);
And here is the fragment shader:
<script type='x-shader/x-fragment' id='frag'>
void main(void) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);
}
</script>
This is what I get:
The red color on this image is [255,27,32].
So my question is, without blending, what is the formula that transforms half transparent red into [255,27,32]?
I think it's the same in OpenGL?

What do you mean it's [255, 27, 32]? How did you get that number?
The <canvas> element is blended with the rest of the HTML page just like all HTML elements so "in general" if you put 1, 0, 0, 0.5 in the fragment shader then 255, 0, 0, 127 is written in the canvas.
The canvas will then be blended with the page. What's behind the canvas is up to your CSS. Maybe you had <canvas style="background: purple;"></canvas> or maybe your CSS is set to
body {
background: #123456;
}
The background could be anything you set it to
The canvas will by default, be blended assuming the colors inside the canvas are using "premultiplied alpha" which means that color (255, 0, 0, 127) is an invalid color.
Pre-multipled alpha means R, G, and B have been multiplied by alpha. When R is 1 and alpha 0.5 the highest R can be is 0.5 (because 1.0 multiplied by 0.5 is 0.5)
So what happens in that case is undefined by the HTML spec.
You can tell the browser to use un-premultiplied alpha for drawing the canvas on top of the whatever it's over by passing premultipledAlpha: false when you create the WebGL context. You can also turn off alpha in the canvas with alpha: false when creating the context. (though IIRC it doesn't work in Safari, Safari being the new IE 🙄
examples:
test('#normal', {});
test('#unpremultiplied-alpha', {premultipliedAlpha: false});
test('#no-alpha', {alpha: false});
function test(selector, options) {
const canvas = document.querySelector(selector);
const gl = canvas.getContext('webgl', options);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(32, 32, 64, 64);
gl.clearColor(0.5, 0.25, 0.125, 0.5);
gl.clear(gl.COLOR_BUFFER_BIT);
}
/* make the background stripes */
html, body {
height: 100%;
background: repeating-linear-gradient(
45deg,
lime,
lime 10px,
orange 10px,
orange 20px
);
}
canvas {
border: 1px solid black;
}
<canvas width="128" height="128" id="normal"></canvas>
<canvas width="128" height="128" id="unpremultiplied-alpha"></canvas>
<canvas width="128" height="128" id="no-alpha"></canvas>

Related

Msaa in Webgl 2.0 - Perform Msaa on a Quad

I try to perform MSAA on a framebuffer, And in the standalone version where i draw a cube to the framebuffer and blit that framebuffer to the canvas it works like a charm:
var gl = canvas.getContext("webgl2", {
antialias: false
});
const frambuffer = gl.createFramebuffer();
const renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, this.width, this.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer);
.. Prepare scene
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
.. Draw scene
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, frambuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
gl.blitFramebuffer( 0, 0, canvas.width, canvas.height,
0, 0, canvas.width, canvas.height,
gl.COLOR_BUFFER_BIT, gl.LINEAR);
But when i do this in my engine with a deferred pipeline the blit is performed but the MultiSample (MSAA) not. The difference i can think of is that i am there writing an image drawn to a quad to the framebuffer and in the working example a cube.
as requested,In the case it is not working the setup is like this:
var gl = canvas.getContext("webgl2", {
antialias: false
});
.. Load resources ..
.. Prepare renderpasses ..
shadow_depth for every light
deferred scene
ssao
shadow for first light
convolution on ssao and shadow
convolution
uber for every light
tonemap
msaa
..
.. draw renderpasses ..
deferred scene
ssao
shadow for first light
convolution on ssao and shadow
convolution
uber for every light
tonemap
...
const frambuffer = gl.createFramebuffer();
const renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, this.width, this.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer);
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
..draw tonemap of scene to quad
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, frambuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
gl.blitFramebuffer( 0, 0, canvas.width, canvas.height,
0, 0, canvas.width, canvas.height,
gl.COLOR_BUFFER_BIT, gl.LINEAR);
renderbufferStorageMultisample needs to be applied only for the framebuffer object that has the initial 3D content. When doing post-processing, multisampling does not have an effect because only 1 or 2 triangles being rasterized and they span the entire viewport.

WebGL shader z position not used in depth calculations

I've been trying out some WebGL but there's a bug I cannot seem to find out how to fix.
Currently I have the following setup:
I have around 100 triangles which all have a position and are being drawn by a single gl.drawArrays function. To have them drawn in the correct order I used gl.enable(gl.DEPTH_TEST); which gave the correct result.
The problem I have now is that if I update the gl_Position of the triangles in the vertex shader the updated Z value is not being used in the depth test. The result is that a triangle with a gl_Position.z of 1 can be drawn on top of a triangle with a gl_Position.z of 10, which is not exactly what I want..
What have I tried?
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.GEQUAL);
with
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.clearDepth(0);
gl.drawArrays(gl.TRIANGLES, 0, verticesCount);
in the render function.
The following code is used to create the buffer:
gl.bindBuffer(gl.ARRAY_BUFFER, dataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positionBufferData, gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, false, 0, 0);
The triangles with a higher z value are much bigger in size (due to the perspective) but small triangles still appear over it (due to the render order).
In the fragment shader I've used gl_fragCoord.z to see if that was correct and smaller triangles (further away) received a higher alpha than bigger ones (up close).
What could be the cause of the weird drawing behaviour?
Depth in clipspace goes from -1 to 1. Depth written to the depth buffer goes from 0 to 1. You're clearing to 1. There is no depth value > 1 so the only things you should see drawn are at gl_Position.z = 1. Anything less than 1 will fail the test gl.depthFunc(gl.GEQUAL);. Anything > 1 will be clipped. Only 1 is both in the depth range and Greater than or Equal to 1
The example below draws smaller to larger rectangles with different z values. The red is standard gl.depthFunc(gl.LESS) with depth cleared to 1. The green is gl.depthFunc(gl.GEQUAL) with depth cleared to 0. The blue is gl.depthFunc(gl.GEQUAL) with depth cleared to 1. Notice blue only draws the single rectangle at gl_Position.z = 1 because all other rectangles fail the test since they are at Z < 1.
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const vs = `
attribute vec4 position;
varying vec4 v_position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
v_position = abs(position);
}
`;
const fs = `
precision mediump float;
varying vec4 v_position;
uniform vec4 color;
void main() {
gl_FragColor = vec4(1. - v_position.xxx, 1) * color;
}
`;
// compiles shaders, links program, looks up attributes
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBindbuffer, gl.bufferData for each array
const z0To1BufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: [
...makeQuad( .2, 0.00),
...makeQuad( .4, .25),
...makeQuad( .6, .50),
...makeQuad( .8, .75),
...makeQuad(1.0, 1.00),
],
});
const z1To0BufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: [
...makeQuad(.2, 1.00),
...makeQuad(.4, .75),
...makeQuad(.6, .50),
...makeQuad(.8, .25),
...makeQuad(1., 0.00),
],
});
function makeQuad(xy, z) {
return [
-xy, -xy, z,
xy, -xy, z,
-xy, xy, z,
-xy, xy, z,
xy, -xy, z,
xy, xy, z,
];
}
gl.useProgram(programInfo.program);
gl.enable(gl.DEPTH_TEST);
gl.clearDepth(1);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.depthFunc(gl.LESS);
drawRects(-0.66, z0To1BufferInfo, [1, 0, 0, 1]);
gl.clearDepth(0);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.depthFunc(gl.GEQUAL);
drawRects(0, z1To0BufferInfo, [0, 1, 0, 1]);
gl.clearDepth(1);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.depthFunc(gl.GEQUAL);
drawRects(0.66, z1To0BufferInfo, [0, 0, 1, 1]);
function drawRects(xoffset, bufferInfo, color) {
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
let mat = m4.translation([xoffset, 0, 0]);
mat = m4.scale(mat, [.3, .5, 1]);
// calls gl.uniformXXX
twgl.setUniforms(programInfo, {
color: color,
matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
}
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>
<pre>
red : depthFunc: LESS, clearDepth: 1
green: depthFunc: GEQUAL, clearDepth: 0
blue : depthFunc: GEQUAL, clearDepth: 1
</pre>

WebGL - unexpected behaviour while not clearing gl.COLOR_BUFFER_BIT

I was trying to implement a sort of motionblur effect in my page and tought to simply specify a blend function for the main framebuffer
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
and not to clear the gl.COLOR_BUFFER_BIT in my draw routine
//gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(/*gl.COLOR_BUFFER_BIT |*/ gl.DEPTH_BUFFER_BIT);
I expected to see the screen getting filled with old color values that would have never cleaned up but the actual result is the same as if I cleared the color_buffer_bit to white
Here's the link to the test page with all the code in it, it's very simple since it just plot a function on screen with colored particles
http://deepdown.altervista.org/WebGl/test/sample.html
is the color buffer cleared anyway if I don't specify so?
what need to be done in order to keep the rendered colors in each buffer without having them overwritten?
You might find this answer useful.
The short version is, by default WebGL clears the drawingBuffer.
As for alpha issues there's this answer.
The specification is totally clear about all of this. It's just that it's a specification and is extremely detailed and hard to follow as it's over 300 pages long since it's not only the WebGL spec but the WebGL spec says it's based on the OpenGL ES 2.0 spec and the GLSL ES 1.0 spec.
Why have an alpha buffer? Because it's HTML and all other elements have alpha. You can set text or background colors to rgba(255,0,0,0.5). You can display .PNG files with alpha transparency, and the 2D canvas has alpha and transparency so so does WebGL as the default.
Setting alpha to false is a concession to non-HTML based apps (ie, ports).
So, the down side is not being able to have transparency with the rest of the page. Not the best sample but here is one of the first examples of this. Without alpha you couldn't do that.
As for how to blend, blending with the webpage is by default using premulitplied alpha.
Otherwise if you want things to stack up you need probably need to blend with additive blending
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
Example:
"use strict";
var m4 = twgl.m4;
var gl = twgl.getWebGLContext(document.getElementById("c"), { preserveDrawingBuffer: true } );
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
var arrays = {
position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
function rand(min, max) {
return Math.random() * (max - min) + min;
}
function render(time) {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
var matrix = m4.identity();
m4.translate(matrix, [rand(-1, 1), rand(-1, 1), 0], matrix);
m4.scale(matrix, [rand(0.1, 0.2), rand(0.1, 0.2), 1], matrix);
var color = [Math.random(), Math.random(), Math.random(), 0.1];
var preMultipliedColor = [color[0] * color[3], color[1] * color[3], color[2] * color[3], color[3]];
var uniforms = {
matrix: matrix,
color: preMultipliedColor,
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
.msg {
display: flex;
justify-content: center;
align-content: center;
align-items: center;
width: 300px;
height: 150px;
}
.msg>div {
width: 200px;
}
canvas {
position: absolute;
left: 5px;
top: 5px;
border: 1px solid black;
}
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<script id="vs" type="notjs">
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
</script>
<script id="fs" type="notjs">
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
</script>
<div class="msg"><div>lets put some text under the canvas so we can see if things are blending and
being composited 😎😎😎😎</div></div>
<canvas id="c"></canvas>

Webgl coloring the entire geometry?

I am using triangles(using vertices and face position) to draw the graphics.I am storing color information for each vertex and applying colors accordingly. But the problem is all the geometries in my scene are of single color(say cone=red, cylinder=blue). SO, storing color for each vertex is apparently of no use to me.
Is their any other approach by which coloring can be done in webgl apart from storing color information of each vertices in the scene. Maybe something like coloring the entire geometry(say a cone).
It's clear from your question you might not really understand WebGL yet? You might want to check out these tutorials.
WebGL uses shaders, those shaders use whatever inputs you define and output whatever you tell them to output. That means WebGL doesn't require vertex colors. Vertex colors are something you decide on when you write your shaders. If you don't want to use vertex colors, don't write a shader that references vertex colors.
That said there if you have a shader that happens to use vertex colors you can easily provide the shader with a constant color. Let's assume you have shaders like this that just use vertex colors.
vertex shader:
attribute vec4 a_position;
attribute vec4 a_color; // vertex colors
varying vec4 v_color; // so we can pass the colors to the fragment shader
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * a_position;
v_color = a_color;
}
fragment shader:
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
Now, all you have to do to use a constant color is turn off the attribute for a_color and set a constant value with gl.vertexAttrib4f like this
// at init time
var a_colorLocation = gl.getAttribLocation(program, "a_color";
// at draw time
gl.disableVertexAttribArray(a_colorLocation); // turn off the attribute
gl.vertexAttrib4f(a_colorLocation, r, g, b, a); // supply a constant color
Note that turning off attribute 0 will slow down WebGL on desktops because if differences between OpenGL and OpenGL ES. It's possible a_colorLocation is attribute 0. To avoid this problem bind your attribute locations BEFORE you link your program. Specifically since you'll always use a position (which is called "a_position" in the example above) just bind that to location 0 like this
..compile shaders..
..attach shaders to program..
// Must happen before you call linkProgram
gl.bindAttribLocation(program, 0, "a_position");
gl.linkProgram(program);
...check for errors, etc...
This will force the attribute for "a_position" to be attribute 0 so you'll always enable it.
Here's a sample
function main() {
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
if (!gl) {
alert("no WebGL");
return;
}
// NOTE:! This function binds attribute locations
// based on the indices of the second array
var program = twgl.createProgramFromScripts(
gl,
["vshader", "fshader"],
["a_position", "a_color"]); // a_position will get location 0
// a_color will get location 1
var a_positionLoc = 0;
var a_colorLoc = 1;
var u_matrixLoc = gl.getUniformLocation(program, "u_matrix");
gl.useProgram(program);
var verts = [
1, 1,
-1, 1,
-1, -1,
1, 1,
-1, -1,
1, -1,
];
var colors = [
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
255, 255, 0, 255,
0, 255, 255, 255,
255, 0, 255, 255,
];
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.enableVertexAttribArray(a_positionLoc);
gl.vertexAttribPointer(a_positionLoc, 2, gl.FLOAT, false, 0, 0);
var colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(colors), gl.STATIC_DRAW);
gl.enableVertexAttribArray(a_colorLoc);
gl.vertexAttribPointer(a_colorLoc, 4, gl.UNSIGNED_BYTE, true, 0, 0);
// Draw in the bottom right corner
gl.uniformMatrix4fv(
u_matrixLoc,
false,
[0.5, 0, 0, 0,
0, 0.5, 0, 0,
0, 0, 1, 0,
-0.5, -0.5, 0, 1]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Now turn off the a_color attribute and supply a solid color
gl.disableVertexAttribArray(a_colorLoc);
var r = 0.5;
var g = 1;
var b = 0.5;
var a = 1;
gl.vertexAttrib4f(a_colorLoc, r, g, b, a); // greenish
// Draw in the top left corner
gl.uniformMatrix4fv(
u_matrixLoc,
false,
[0.5, 0, 0, 0,
0, 0.5, 0, 0,
0, 0, 1, 0,
0.5, 0.5, 0, 1]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
main();
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<script id="vshader" type="whatever">
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 v_color;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * a_position;
v_color = a_color;
}
</script>
<script id="fshader" type="whatever">
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
</script>
<canvas id="c" width="300" height="300"></canvas>
If your geometry has color per object, that doesn't change across the geometry, then you should pass that color as the uniform variable.
So you en up with only one attribute - position of vertices, few matrix uniforms - say model, view, projection matrices, that for the vertex shader, and one vector uniform variable for the fragment shader for "shading" the object.

webgl adding projection doesnt display object

I am having a look at web gl, and trying to render a cube, but I am having a problem when I try to add projection into the vertex shader. I have added an attribute, but when I use it to multiple the modelview and position, it stops displaying the cube. Im not sure why and was wondering if anyone could help? Ive tried looking at a few examples but just cant get this to work
vertex shader
attribute vec3 aVertexPosition;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
//gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0);
}
fragment shader
#ifdef GL_ES
precision highp float; // Not sure why this is required, need to google it
#endif
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
function init() {
// Get a reference to our drawing surface
canvas = document.getElementById("webglSurface");
gl = canvas.getContext("experimental-webgl");
/** Create our simple program **/
// Get our shaders
var v = document.getElementById("vertexShader").firstChild.nodeValue;
var f = document.getElementById("fragmentShader").firstChild.nodeValue;
// Compile vertex shader
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, v);
gl.compileShader(vs);
// Compile fragment shader
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, f);
gl.compileShader(fs);
// Create program and attach shaders
program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
// Some debug code to check for shader compile errors and log them to console
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
console.log(gl.getShaderInfoLog(vs));
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
console.log(gl.getShaderInfoLog(fs));
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
console.log(gl.getProgramInfoLog(program));
/* Create some simple VBOs*/
// Vertices for a cube
var vertices = new Float32Array([
-0.5, 0.5, 0.5, // 0
-0.5, -0.5, 0.5, // 1
0.5, 0.5, 0.5, // 2
0.5, -0.5, 0.5, // 3
-0.5, 0.5, -0.5, // 4
-0.5, -0.5, -0.5, // 5
-0.5, 0.5, -0.5, // 6
-0.5,-0.5, -0.5 // 7
]);
// Indices of the cube
var indicies = new Int16Array([
0, 1, 2, 1, 2, 3, // front
5, 4, 6, 5, 6, 7, // back
0, 1, 5, 0, 5, 4, // left
2, 3, 6, 6, 3, 7, // right
0, 4, 2, 4, 2, 6, // top
5, 3, 1, 5, 3, 7 // bottom
]);
// create vertices object on the GPU
vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Create indicies object on th GPU
ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indicies, gl.STATIC_DRAW);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
// Render scene every 33 milliseconds
setInterval(render, 33);
}
var mvMatrix = mat4.create();
var pMatrix = mat4.create();
function render() {
// Set our viewport and clear it before we render
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
// Bind appropriate VBOs
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
// Set the color for the fragment shader
program.uColor = gl.getUniformLocation(program, "uColor");
gl.uniform4fv(program.uColor, [0.3, 0.3, 0.3, 1.0]);
//
// code.google.com/p/glmatrix/wiki/Usage
program.uPMatrix = gl.getUniformLocation(program, "uPMatrix");
program.uMVMatrix = gl.getUniformLocation(program, "uMVMatrix");
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 1.0, 10.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0.0, -0.25, -1.0]);
gl.uniformMatrix4fv(program.uPMatrix, false, pMatrix);
gl.uniformMatrix4fv(program.uMVMatrix, false, mvMatrix);
// Set the position for the vertex shader
program.aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
gl.enableVertexAttribArray(program.aVertexPosition);
gl.vertexAttribPointer(program.aVertexPosition, 3, gl.FLOAT, false, 3*4, 0); // position
// Render the Object
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
}
Thanks in advance for any help
Problem is here:
..., gl.viewportWidth / gl.viewportHeight, ...
Both gl.viewportWidth and gl.viewportHeight are undefined values.
I think you missed this two lines:
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
You will see a lot of people doing this:
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
But please note that WebGL context also have this two attributes:
gl.drawingBufferWidth
gl.drawingBufferHeight
So your cube shows up without the perspective matrix, correct?
At first glance I would think that you may be clipping away your geometry with the near plane. You provide a near an far plane to the perpective function as 1.0 and 10.0 respectively. This means that for any fragments to be visible they must fall in the z range of [1, 10]. You cube is 1 unit per side, centered on (0, 0, 0), and you are moving it "back" from the camera 1 unit. This means that the nearest face to the camera will actually be at 0.5 Z, which is outside the clipping range and therefore discarded. About half of your cube WILL be at z > 1, but you'll be looking at the inside of the cube at that point. If you have backface culling turned on you won't see anything.
Long story short - Your cube is probably too close to the camera. Try this instead:
mat4.translate(mvMatrix, [0.0, -0.25, -3.0]);

Resources