Webgl: Prevent Color Buffer from Being Cleared

WebGL - unexpected behaviour while not clearing gl.COLOR_BUFFER_BIT

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 lt;/div></div><canvas id="c"></canvas>

Why WebGL 'clear' draw to front buffer?

Because that's the way WebGL works.

WebGL swaps/copies automatically. Anytime you do anything that effects the WebGL drawingBuffer (think "backbuffer) it gets marked to swap/copy. The next time the browser composites the web page it will do either a swap or a copy. You can tell it to always copy. You can not tell it to always swap

Specifically, creating the WebGL context with {preserveDrawingBuffer: true} as in

gl = someCanvas.getContext("webgl", {preserveDrawingBuffer: true});

Tells WebGL you always want it to do a copy.

The default is WebGL chooses swap or copy depending on various factors. For example if anti-aliasing is on it's always effectively a copy (a resolve) where as if anti-aliasing is off it might be a swap. Also, in this default case, when preserveDrawingBuffer is false after it does a copy or swap it will clear the backbuffer. This is to try to make it appear consistent regardless of whether it chooses to copy or swap.

If preserveDrawingBuffer = true then it never clears the backbuffer.

If you want to do a bunch of work over multiple JavaScript events and not let the user see the results until all your work is done you'll need to render to a framebuffer with an attached texture or renderbuffer and then when all your work is done render than attachment to the canvas (the backbuffer).

as for gl.finish that's a no-op in WebGL. It has no point.

why is that gl.clear(gl.COLOR_BUFFER_BIT) and requestAnimationFrame will clear all the primitives I drew before

This is generally the way WebGL works.

WebGL is just draws into a rectangle of pixels. There is no memory of primitives. There is no structure. There is just code and the resulting canvas which is an rectangle of pixels.

Most WebGL programs/pages clear the entire canvas every frame and redraw 100% of the things they want to show every time they draw. For tetris the general code might be something like

function render()  {
clear the canvas
draw the grid
draw all the stable pieces
draw the current piece
draw the next piece
draw the effects
draw the score
}

Any knowledge of primitives or other structure is entirely up to your code.

If you want the grid lines to be static then either set a static background with CSS or use another canvas

Using a background:

const gl = document.querySelector('#c').getContext('webgl');
function render(time) { time *= 0.001;
gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); drawBlocks(gl, time); requestAnimationFrame(render);}requestAnimationFrame(render);
// --- below this line not important to the answer
function drawBlocks(gl, time) { gl.enable(gl.SCISSOR_TEST); const numBlocks = 5; for (let i = 0; i < numBlocks; ++i) { const u = i / numBlocks; gl.clearColor(i / 5, i / 2 % 1, i / 3 % 1, 1); const x = 150 + Math.sin(time + u * Math.PI * 2) * 130; const y = 75 + Math.cos(time + u * Math.PI * 2) * 55; gl.scissor(x, y, 20, 20); gl.clear(gl.COLOR_BUFFER_BIT); } gl.disable(gl.SCISSOR_TEST);}
#c {  background-image: url(https://i.imgur.com/ZCfccZh.png);}
<canvas id="c"></canvas>

Is my webgl FBO color attachment being cleared?

This ended up being a problem with Regl and WebViz. I was calling React.useState to set the whatever resource that regl uses for the texture. For some reason, this seems like it was invoked, which "resets" the texture to an empty 1x1.

WebGL rendering fails when using color buffer

You're overwriting your vertex positions with your colors:

 webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(vertices), webgl.STATIC_DRAW);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(colors), webgl.STATIC_DRAW);

At any given time there can only be one buffer bound for each type (ARRAY_BUFFER and ELEMENT_ARRAY_BUFFER), so
the flow is to bind a buffer and set its data followed by setting up the vertex attribute pointers for that specific buffer, then proceed to the next buffer:

// setup positions
var VextexBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, VextexBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(vertices), webgl.STATIC_DRAW);
var coord = webgl.getAttribLocation(shaderProgram,"coordinates");
webgl.vertexAttribPointer(coord, 3, webgl.FLOAT, false ,0,0);

// setup indices
var IndexBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, IndexBuffer);
webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), webgl.STATIC_DRAW);

// setup colors
var colorBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, colorBuffer);
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(colors), webgl.STATIC_DRAW);
var color = webgl.getAttribLocation(shaderProgram, "color");
webgl.vertexAttribPointer(color, 3, webgl.FLOAT, false ,0,0);

For applications that work with multiple different buffers and intend to be realtime the flow is to create and set the buffers during initialization phase to then only bind the buffer and set the vertex attribute pointers during the animation loop.

<!doctype html><html>
<body><canvas id ="ctx" width = "300" height = "300"></canvas> <script> //Getting Webgl Context
var ctx = document.getElementById("ctx"); var webgl = ctx.getContext("experimental-webgl");
/*Creating Shader*/
//Vertex Code var vertexCode = 'attribute vec3 coordinates;'+ 'attribute vec3 color;'+ 'varying vec3 vColor;'+ 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + 'vColor = color;'+ '}';
//Creating Shader Object var vertexShader = webgl.createShader(webgl.VERTEX_SHADER);
//Assigning the Source webgl.shaderSource(vertexShader, vertexCode);
//Compiling the Source webgl.compileShader(vertexShader);
//Fragment Shader Code var fragmentCode ='precision mediump float;'+ 'varying vec3 vColor;'+ 'void main(void) {'+ 'gl_FragColor = vec4(vColor, 1.);'+ '}';
//Creating Shader Object var fragmentShader = webgl.createShader(webgl.FRAGMENT_SHADER);
//Assigning the Source webgl.shaderSource(fragmentShader, fragmentCode);
//Compiling the Source webgl.compileShader(fragmentShader);
//Creating Program to store Shader var shaderProgram = webgl.createProgram();
//Attaching the shaders webgl.attachShader(shaderProgram, vertexShader); webgl.attachShader(shaderProgram, fragmentShader);
//linking the Program webgl.linkProgram(shaderProgram);
//using the Program webgl.useProgram(shaderProgram);
//Defining geometry 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 colors = [0,0,1, 1,0,0, 0,1,0, 1,0,1,];
indices = [3,2,1,3,1,0];
// setup positions var VextexBuffer = webgl.createBuffer(); webgl.bindBuffer(webgl.ARRAY_BUFFER, VextexBuffer); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(vertices), webgl.STATIC_DRAW); var coord = webgl.getAttribLocation(shaderProgram,"coordinates"); webgl.vertexAttribPointer(coord, 3, webgl.FLOAT, false ,0,0);
// setup indices var IndexBuffer = webgl.createBuffer(); webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, IndexBuffer); webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), webgl.STATIC_DRAW);
// setup colors var colorBuffer = webgl.createBuffer(); webgl.bindBuffer(webgl.ARRAY_BUFFER, colorBuffer); webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(colors), webgl.STATIC_DRAW); var color = webgl.getAttribLocation(shaderProgram, "color"); webgl.vertexAttribPointer(color, 3, webgl.FLOAT, false ,0,0);
//enabling the attributes webgl.enableVertexAttribArray(coord); webgl.enableVertexAttribArray(color); //Unbind Array

/*Drawing the Triangle*/
//Clearing the Colour webgl.clearColor(.5,.5,.5,1);
//Enabling the depth test webgl.enable(webgl.DEPTH_TEST);
//Clearing colour nuffer bit webgl.clear(webgl.COLOR_BUFFER_BIT);
//Setting a viewport webgl.viewport(0,0,ctx.width,ctx.height);
//Draw the triangle webgl.drawElements(webgl.TRIANGLES,indices.length,webgl.UNSIGNED_SHORT,0);
</script></body></html>

Clear canvas and rerender the webgl context with different image

Please next time post a working sample so we don't have to spend the time doing it ourselves. You can load images from imgur.

The issue is the first time you call handleLoadedTexture at the bottom it sets the active texture unit to 1 with gl.activeTexture(gl.TEXTURE1) which means the second time handleLoadedTexture is called it's binding the texture to texture unit 1 where as the first 2 shaders are using texture unit 0 which still has the texture from the first time handleLoadedTexture was called bound to it.

Otherwise, other issues with the code

  • I had to wait for img1 and img2 to load otherwise I can't read img.width and img.height

    Now maybe in your actual code they are already loaded but if they are already loaded then there's no reason to load them again

  • The code is compiling all 3 shaders, once for each call to drawfilter but arguably it should compile them once at init time and use the same shaders on all calls to drawFilter

  • It code is creating new buffers for every draw call. You only need one set of buffers which again should happen at init time. Setting the attributes needs to happen before each draw call, creating the buffers and putting data in them does not.

    Well, technically, setting the attributes only needs to happen if they need to be different. If you force the position and a_texCoord attributes to be at the same locations with bindAttribLocation before calling linkProgram so that they match locations across programs then you'd only need to set the attributes once assuming you also use the same buffers with the same data (see previous point)

  • || (logical or) is not the same as | (binary or). For gl.clear you need to use binary or gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT|gl.STENCIL_BUFFER_BIT otherwise the value you pass to gl.clear will be wrong and your canvas won't be cleared. There isn't much reason to clear in this example though as blending is not on and the draw calls draw every pixel of the canvas

  • setting gl.clearColor for each framebuffer doesn't do anything unless you call gl.clear but like above, since the draw will effect every pixel and blending is off calling gl.clear would not change the results.

  • viewport settings don't match the framebuffers. The framebuffer textures are created to be the same size as the images but the viewport settings were set to the size of the canvas. They should be set the same size as the framebuffer attachments

const vertexShaderSource = `
attribute vec4 position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = position;
v_texCoord = a_texCoord;
}
`;

const fragmentShaderSourceA = `
precision mediump float;
uniform sampler2D uTexture;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(uTexture, v_texCoord);
}
`;

const fragmentShaderSourceB = `
precision mediump float;
uniform sampler2D uTexture;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(uTexture, v_texCoord.yx);
}
`;

const fragmentShaderSourceC = `
precision mediump float;
uniform sampler2D uTexture;
uniform sampler2D originalTexture;
varying vec2 v_texCoord;
void main() {
vec4 color1 = texture2D(uTexture, v_texCoord);
vec4 color2 = texture2D(originalTexture, v_texCoord);
gl_FragColor = color1 * color2; //??
}
`;

function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => { resolve(img); };
img.onerror = reject;
img.crossOrigin = "anonymous"; // only needed because images are on another domain
img.src = url;
});
}

async function main() {
// we need to wait for the images to load otherwise
// width and height will not be set.
const [img1, img2] = await Promise.all([
'https://i.imgur.com/KjUybBD.png',
'https://i.imgur.com/v38pV.jpg',
].map(loadImage));

/* img1, img2 are img elements I get from other part of the program according to user selections.
As per user input more than 2 images can also be received. Demonstrating issue using two */
const canvas = document.getElementById("canvas"); //the canvas on which I am rendering.
const gl = canvas.getContext("webgl");
drawfilter(gl, img1, img1.width, img1.height); // first displaying image one
drawfilter(gl, img2, img2.width, img2.height); // when second image is received the function is called again

function drawfilter(gl, img, width, height) {
gl.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);

function createShader(gl, type, shaderSource) {
const shader = gl.createShader(type);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);

const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!success) {
console.warn(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}

return shader;
}

//the shaderssources cannot be displayed here
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); //simple vertex shader
const fragmentShaderA = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSourceA); //simple fragment shader
const fragmentShaderB = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSourceB); //simple fragment shader
/* this shader takes two texture inputs. 1- original image,
2- ShadersourceA applied on original image then on output shadersouceB is applied and the result is passed as second texture to this fragment shader */
const fragmentShaderC = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSourceC);

function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!success) {
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
return program;
}

const programA = createProgram(gl, vertexShader, fragmentShaderA);
const programB = createProgram(gl, vertexShader, fragmentShaderB);
const programC = createProgram(gl, vertexShader, fragmentShaderC);
const texFbPair1 = createTextureAndFramebuffer(gl);
const texFbPair2 = createTextureAndFramebuffer(gl);

function setAttributes(program) {
const positionLocation = gl.getAttribLocation(program, 'position');
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, -1,
1, 1, 1, -1, -1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 1.0,
0.0, 0.0,
1.0, 1.0,
1.0, 0.0,
1.0, 1.0,
0.0, 0.0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
}

const texture = gl.createTexture();
texture.image = new Image();
texture.image.onload = function() {
handleLoadedTexture(gl, texture);
};
texture.image.crossOrigin = '';
texture.image.src = img.getAttribute('src');

function handleLoadedTexture(gl, texture, callback) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
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_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);

setAttributes(programA);
gl.useProgram(programA);
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair1.fb);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.clearColor(0, 0, 1, 1);
gl.viewport(0, 0, width, height);
gl.drawArrays(gl.TRIANGLES, 0, 6);

setAttributes(programB);
gl.useProgram(programB);
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair2.fb);
gl.bindTexture(gl.TEXTURE_2D, texFbPair1.tex);
gl.clearColor(0, 0, 0, 1);
gl.viewport(0, 0, width, height);
gl.drawArrays(gl.TRIANGLES, 0, 6)

setAttributes(programC);
gl.useProgram(programC);
var uTextureLocation = gl.getUniformLocation(programC, "uTexture");
var originalTextureLocation = gl.getUniformLocation(programC, "originalTexture");
// set which texture units to render with.
gl.uniform1i(uTextureLocation, 0); // texture unit 0
gl.uniform1i(originalTextureLocation, 1); // texture unit 1
// Set each texture unit to use a particular texture.
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texFbPair2.tex);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.clearColor(0, 0, 0, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6)
}

function createTextureAndFramebuffer(gl) {
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
};
}
}
}

main();
canvas { border: 1px solid black; }
<canvas id="canvas"></canvas>

WebGL only colors the furthest side, instead of nearest side

We know that the CVV coordinate system, the non-transformed drawing axes
(CVV == the canonical view volume, the +/-1 cube volume we depict on-screen)
have RIGHT-HANDED coordinates, with origin at the center of the CVV cube, x increasing rightwards, y increasing upwards, and z increasing outwards,
towards your eye. But it is not necessary that the fragments with LARGER, MORE-POSITIVE Z depths in the CVV get drawn as 'nearer' to us, and the fragments with SMALLER, MORE-NEGATIVE Z at the same screen location be overlapped. Why?

WebGL (and OpenGL-ES, and OpenGL) has a historical quirk in an otherwise-sensible method for 3D drawing: it defines 'depth' as a computed value between 0.0 and 1.0, and it is NOT simply the z value in CVV coordinates.

By default, WebGL defines depth as:

  • 0.0 for 'least' depth, nearest the screen or camera; (z = +1 in CVV)
  • 1.0 for 'most' depth, farthest from the screen or camera; (z = -1 in CVV)

While the GPU computes depth automatically, correct results require vertices that were transformed by a 3D camera-projection matrix to mimic a camera lens. Sometimes, if we didn't define or use the projection matrix, the GPU depth calculation reduces to:

depth = 0.5 + 0.5*(gl_position.z / gl.position.w);

In other words, without this 'camera-projection' matrix, the GPU computes
'depth' backwards -- vertices at z = -1 get depth of 0 when it SHOULD be 1. For more details about how the GPU computes depth, see:
How does WebGL set values in the depth buffer?

Known this, we could easily fix this by either:

  1. We could write code that always sets the first ModelMatrix transform
    to change the sign of z, like this: myMatrix.setScale(1,1,-1);
    Then all z values we compute will have reversed sign.
    (That's a little messy when combined with a complicated scene-graph...).
  2. We could leave the z values unchanged, and instead tell WebGL to
    apply different rules for its use of the depth-buffer; that is a more snesible solution. Try add these lines of code
gl.enable(gl.DEPTH_TEST); // enabled by default, but let's be SURE.
gl.clearDepth(0.0); // each time we 'clear' our depth buffer, set all
// pixel depths to 0.0 (1.0 is DEFAULT)
gl.depthFunc(gl.GREATER); // gl.LESS is DEFAULT; reverse it!
// draw a pixel only if its depth value is GREATER
// than the depth buffer's stored value.

Credit to my Professor on computer graphics - John E. Tumblin



Related Topics



Leave a reply



Submit