need help reducing number of calls to bind/draw textures

Started by
0 comments, last by Krohm 10 years, 2 months ago

As sort of a self-teaching exercise I'm trying to three draw quads using WebGL...each quad has its own texture with its own model matrix (rotating on its own axis). The code works but I'm trying to reduce the number of calls to the GL context and better understand how texture binding/drawing works. A picture if it helps...

http://i295.photobucket.com/albums/mm151/coffeeaddict21/webglexper2_ss_zps897ba55e.jpg

I'm re-using gl.TEXTURE0 unit for every texture...could I load each texture into its own unit (TEXTURE0, TEXTURE1, TEXTURE2) and not do all the binding over and over again?

This is called before each call to gl.drawArrays (draw command) to load the texture I want to use in drawArrays and I'm not sure if all of it is necessary or if there is another way around it...can I reduce the number of GL calls or optimize this?


//NAME: loadTexture
//PURPOSE: tell GL how to load the specified texture into GL sampler
//IN: WebGL context, GL texture obj, sampler handle, image object, texture unit to use
//RETURN: void
function loadTexture(gl, texture, u_Sampler, image, texUnit){
	
	//make texture unit specified active
	gl.activeTexture(texUnit);
	//bind and activate texture
	gl.bindTexture(gl.TEXTURE_2D, texture);
	//tell GL what parameters to use to map the texture to the geometry
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	//tell GL to bind the specified image to the target
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
	//set specifed texture unit index to the sampler
	gl.uniform1i(u_Sampler, 0);
}

The code that calls loadTexture (see the bottom for the important stuff)...


//NAME: renderImages
//PURPOSE: load images into textures, apply tranaformations and then draw
//IN: array of pre-loaded images
//RETURN: none
function renderImages(images){
	console.log('render...');
	//get a pointer to the canvas
	var canvas = document.getElementById('webgl');
	
	//get the graphics context from the canvas
	var gl = getWebGLContext(canvas);
	if (!gl) {
		console.log('Failed to get the rendering context for WebGL');
		return;
	}
	
	//initialize shaders specified in this document
	if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
		console.log('Failed to initialize shaders.');
		return;
	}
	
	//initialize vertex buffers
	var n = initVertexBuffers(gl);
	if (n < 0){
		console.log('Failed to set the position of the vertices.');
		return;
	}
	
	if(!images){
		console.log('Failed to load images.');
		return;
	}
	
	if(images.length != 3){
		console.log('Failed to load all 3 images we need.');
		return;
	}
	
	var texture = gl.createTexture();
	if(!texture){
		console.log('CreateTexture GL call failed on texture initialization.');
		return false;
	}
	
	var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
	if(!u_Sampler){
		console.log('Unable to get storage location of u_Sampler during texture initialization.');
		return false;
	}
		
	var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
	if (u_ModelMatrix == null){
		console.log('Failed to get the storage location of u_ModelMatrix');
		return;
	}
	
	var modelMatrixes = [];
	var modelMatrix1 = new Matrix4(); 
	modelMatrix1.setIdentity();
	modelMatrix1.translate(0.0, 0.5, 0.0);
	modelMatrixes.push(modelMatrix1);
	
	var modelMatrix2 = new Matrix4();
	modelMatrix2.setIdentity();
	modelMatrix2.translate(0.5, 0.0, 0.0);
	modelMatrixes.push(modelMatrix2);
	
	var modelMatrix3 = new Matrix4();
	modelMatrix3.setIdentity();
	modelMatrix3.translate(-0.5, 0.0, 0.0);
	modelMatrixes.push(modelMatrix3);
	
	//set the color to use on gl.clear
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	
	//flip the image on axis on load
	gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
	
	var lastTick1 = 0;
	var lastTick2 = 0;
	var lastTick3 = 0;
	var frameTick = 0;
	var tick = function(){
		var timeElapsed = getElapsed();
		lastTick1 = lastTick1 + timeElapsed;
		lastTick2 = lastTick2 + timeElapsed;
		lastTick3 = lastTick3 + timeElapsed;
		frameTick = frameTick + timeElapsed;
		
		//run this code when counter exceeds 100ms
		if(lastTick1 > 30){
			lastTick1 = 0;
			modelMatrix1.translate(-0.05, 0.0, 0.0);
			modelMatrix1.rotate(5, 0, 0, 1);
		}
		//run this code when counter exceeds 50ms
		if(lastTick2 > 50){
			lastTick2 = 0;
			modelMatrix2.rotate(3 * DIRECTION, 0, 0, 1);
		}
		//run this code when counter exceeds 30ms
		if(lastTick3 > 100){
			lastTick3 = 0;
			modelMatrix3.rotate(-1 * DIRECTION, 0, 0, 1);
		}
		//run this code when counter exceeds 20ms
		if(frameTick > 20){
			frameTick = 0;
			console.log('frame');
			
			//clear the color buffer
			gl.clear(gl.COLOR_BUFFER_BIT);
			
			//draw everything
			for(var matrixIndex = 0; matrixIndex < modelMatrixes.length; matrixIndex++){
				gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrixes[matrixIndex].elements);
				loadTexture(gl, texture, u_Sampler, images[matrixIndex], gl.TEXTURE0);
				gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
			}
		}
		requestAnimationFrame(tick);
	};
	tick();
}
Advertisement
I'm re-using gl.TEXTURE0 unit for every texture...could I load each texture into its own unit (TEXTURE0, TEXTURE1, TEXTURE2) and not do all the binding over and over again?

Yes and no.

In this case, yes, you could fill your VB so all the vertices from quad 0 only take pixels from texture 0. You would set up the texcoords so the other two texcoords are sampled at constant pixels. For example you could (1) multiply the various texel values together, thus to ignore a texture completely you would sample the texture at a constant white coordinate. This would work pretty much on any hardware.

Or (2), you might add an index and (on more advanced, but still very widespread) conditionally sample a specific texture.

Or (3) you might use texture arrays, albeit I'm not sure they're available in WebGL.

In all the three cases, you need to edit your pixel shader accordingly.

There's a fourth option that is having a "texture atlas", or a chart of textures. I'm not quite on them, they tend to require special care in just too many cases.

EDIT: as for the "no" part, you might have to consider if it's really worth to optimize what's clearly just an experiment. Those kind of optimizations are worth exploring given realistic datasets.

Previously "Krohm"

This topic is closed to new replies.

Advertisement