best way to do branching logic in shaders?

Started by
4 comments, last by kalle_h 10 years, 2 months ago

I am using WebGL...OpenGL ES2. This is my vertex shader. Question...

When I have branching in my shader like turning a 'directional light' or 'ambient light' on/off (in my case) by passing an argument to the shader (1.0 to turn it on or 0.0 to turn it off) am I better off using IF/THEN or math? What is the performance hit (if any) when using IF statements in a shader verus handling it with math? Like this...


var VSHADER_SOURCE =
	'attribute vec4 a_Position;\n' +
	'attribute vec4 a_Color;\n' +
	'attribute vec4 a_Normal;\n' +
	'uniform mat4 u_ProjMatrix;\n' +
	'uniform mat4 u_ViewMatrix;\n' +
	'uniform mat4 u_ModelMatrix;\n' +
	'uniform mat4 u_NormalMatrix;\n' +
	'uniform vec3 u_DirectionalLightColor;\n' +
	'uniform vec3 u_AmbientLightColor;\n' +
	'uniform vec3 u_DirectionalLightDirection;\n' +
	'uniform vec3 u_GlobalColor;\n' +
	'uniform float u_UseGlobalColor;\n' +
	'uniform float u_UseDirectionalLight;\n' +
	'uniform float u_UseAmbientLight;\n' +
	'varying vec4 v_Color;\n' +
	'void main() {\n' +
	'	gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;\n' +
	'	vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
	'	float nDotL = max(dot(u_DirectionalLightDirection, normal), 0.0);\n' +
	'	vec3 colorToUse = u_GlobalColor + (vec3(a_Color) * u_UseGlobalColor) + (-u_GlobalColor * u_UseGlobalColor);\n' +
	'	vec3 diffuse = colorToUse + (u_DirectionalLightColor * nDotL * colorToUse * u_UseDirectionalLight) + (-colorToUse * u_UseDirectionalLight);\n' +
	'	vec3 ambient = u_UseAmbientLight * u_AmbientLightColor * colorToUse;\n' +
	'	v_Color = vec4(diffuse + ambient, a_Color.a);\n' +
	'}\n';

The end result is that setting u_UseAmbientLight or u_UseDirectionalLight to 1.0 turns them on...setting them to 0.0 turns them off.

Am I needlessly making my code harder to read or would there be an increase in performance by doing it this way (as opposed to an IF/THEN)?

Advertisement

As your conditionals are based on uniforms (i.e. they're not per vertex/per pixel), I believe* you're better off just using if statements. All your fragments will take the same path so the cost of the conditional will be very cheap, and the driver may even decide to eliminate the branch altogether by creating a separate shader program for you.

* although as with all things like this YMMV, and you should probably measure to be sure.

Modern OpenGL implementations compile following :

if (cond) { x = A; } else { x = B; }

to something like this:

x = cond * A + (1 - cond) * B;

(actually they have much better opcodes to express this kind of code to hardware)

So I would recommend you to use actual if conditionals - GLSL compiler in some cases could optimize code much better than you manually introducing additional multiply operations.

I would recommend reading the shader from a text file also :D

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Thanks for the information everyone! Will make the code a lot more readable.

Just premultiply light color with that intensity toggle at CPU.

This topic is closed to new replies.

Advertisement