Big GLSL Problem

Started by
6 comments, last by Niddles 14 years, 10 months ago
Hello. I don't understand how GLSL is used in large applications. I have been building my shader up, to be an awesome shader I can use for anything. I was going to have it where I could pass in uniforms telling it what it needs to do at certain times. I had planned to have it all set up for multitexturing, 3 lights at a time that could be directional, point, or spotlight, normal mapping, parallax mapping, fog, and so on. I have been working on it loosely for the past couple days, but I have hit a road block. Currently it has the capabilities for mixing 2 textures, applying 2 textures as decals, and 1 light that can be a directional, point, or spot light. When I was expanding it to have capabilities for three lights at a time, it failed. I found out this was because I have too many varying variables. How do the big games make it all come together in their shaders? I can provide my shader code if needed, for critiquing if anybody is willing. I am probably just thinking about the whole thing completely wrong, though. Can anybody help me?
Advertisement
normally varying variables is ~32 floats
post the shader + ppl can suggest ideas how to decrease those

also having an ubershader is not a good idea WRT performance, youre better off having a shader tailored to each material
You will most likely encounter a limit of 8 vec4 varying variables. There are several ways to save varying variables.

1. Use reconstruction of variable.
2. Use a multipass approach and save temporary result in textures.
3. Move calculations from your vertex shader into your fragment shader.

Example 1:
Instead of transfering a 3 component normal, transfer just 2 components and reconstruct your normal (z=1-sqrt(x*x+y*y) ).

Example 2:
Almost all modern games use a multipass approach. I.e. a texture pass, one or more shadow passes, several lighting passes and post processing passes. In this context take a look at deferred rendering.

Example 3:

- Light/Normal mapping version 1:
Transform your lights into tangent space and interpolate the lights across your surface. This will use varying variable for each light.

- Light/Normal mapping version 2:
Interpolate your tangent space matrix across your surface and use it to transform your normal from tangent space to camera space in the fragement shader. Then transfer your lights as uniforms to your fragment shader.

In version 1 you will need x*n varying variable for n lights, where as in version 2 you will just need 3 varying variable independent of your number of lights.

--
Ashaman
I was taking the ubershader approach, and putting everything all into one shader. I should use multipass, I guess. I have had it explained to me, but I still don't really get how doing multipass rendering works. Here are my vertex and fragment shaders I have right now.

//Vertex Shaderuniform bool mixtex1;uniform bool mixtex2;uniform bool decaltex1;uniform bool decaltex2;uniform bool normalmaptex;uniform bool parallaxmaptex;uniform int light0;uniform int light1;uniform int light2;varying vec3 eyeNormal;varying vec3 light0dir;varying vec3 light0pos;varying vec3 light0halfvec;varying float light0distance;varying vec3 light1dir;varying vec3 light1pos;varying vec3 light1halfvec;varying float light1distance;varying vec3 light2dir;varying vec3 light2pos;varying vec3 light2halfvec;varying float light2distance;const int DIRECTIONALLIGHT = 1;const int POINTLIGHT = 2;const int SPOTLIGHT = 3;void main(){	vec3 vecPos;	eyeNormal = normalize(gl_NormalMatrix * gl_Normal);	if(light0 == DIRECTIONALLIGHT)	{		light0dir = normalize(vec3(gl_LightSource[0].position));		light0halfvec = normalize(gl_LightSource[0].halfVector.xyz);	}	if(light0 == POINTLIGHT)	{		vecPos = vec3(gl_ModelViewMatrix * gl_Vertex);		light0pos = normalize(vec3(gl_LightSource[0].position) - vecPos);		light0halfvec = normalize(gl_LightSource[0].halfVector.xyz);		light0distance = length(vec3(gl_LightSource[0].position) - vecPos);	}	if(light0 == SPOTLIGHT)	{		vecPos = vec3(gl_ModelViewMatrix * gl_Vertex);		light0pos = normalize(vec3(gl_LightSource[0].position) - vecPos);		light0halfvec = normalize(gl_LightSource[0].halfVector.xyz);		light0distance = length(vec3(gl_LightSource[0].position) - vecPos);	}	if(light1 == DIRECTIONALLIGHT)	{		light1dir = normalize(vec3(gl_LightSource[1].position));		light1halfvec = normalize(gl_LightSource[1].halfVector.xyz);	}	if(light1 == POINTLIGHT)	{		vecPos = vec3(gl_ModelViewMatrix * gl_Vertex);		light1pos = normalize(vec3(gl_LightSource[1].position) - vecPos);		light1halfvec = normalize(gl_LightSource[1].halfVector.xyz);		light1distance = length(vec3(gl_LightSource[1].position) - vecPos);	}	if(light1 == SPOTLIGHT)	{		vecPos = vec3(gl_ModelViewMatrix * gl_Vertex);		light1pos = normalize(vec3(gl_LightSource[1].position) - vecPos);		light1halfvec = normalize(gl_LightSource[1].halfVector.xyz);		light1distance = length(vec3(gl_LightSource[1].position) - vecPos);	}	if(light2 == DIRECTIONALLIGHT)	{		light2dir = normalize(vec3(gl_LightSource[2].position));		light2halfvec = normalize(gl_LightSource[2].halfVector.xyz);	}	if(light2 == POINTLIGHT)	{		vecPos = vec3(gl_ModelViewMatrix * gl_Vertex);		light2pos = normalize(vec3(gl_LightSource[2].position) - vecPos);		light2halfvec = normalize(gl_LightSource[2].halfVector.xyz);		light2distance = length(vec3(gl_LightSource[2].position) - vecPos);	}	if(light2 == SPOTLIGHT)	{		vecPos = vec3(gl_ModelViewMatrix * gl_Vertex);		light2pos = normalize(vec3(gl_LightSource[2].position) - vecPos);		light2halfvec = normalize(gl_LightSource[2].halfVector.xyz);		light2distance = length(vec3(gl_LightSource[2].position) - vecPos);	}	if(mixtex1)	{		gl_TexCoord[0] = gl_MultiTexCoord0;	}	if(mixtex2)	{		gl_TexCoord[1] = gl_MultiTexCoord1;	}	if(decaltex1)	{		gl_TexCoord[2] = gl_MultiTexCoord2;	}	if(decaltex2)	{		gl_TexCoord[3] = gl_MultiTexCoord3;	}	if(normalmaptex)	{		gl_TexCoord[4] = gl_MultiTexCoord4;	}	if(parallaxmaptex)	{		gl_TexCoord[5] = gl_MultiTexCoord5;	}		gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;}

//Fragment Shaderuniform bool mixtex1;uniform bool mixtex2;uniform bool decaltex1;uniform bool decaltex2;uniform bool normalmaptex;uniform bool parallaxmaptex;uniform int light0;uniform int light1;uniform int light2;uniform sampler2D tex0;uniform sampler2D tex1;uniform sampler2D tex2;uniform sampler2D tex3;uniform sampler2D tex4;uniform sampler2D tex5;uniform sampler2D tex6;uniform sampler2D tex7;varying vec3 eyeNormal;varying vec3 light0dir;varying vec3 light0pos;varying vec3 light0halfvec;varying float light0distance;varying vec3 light1dir;varying vec3 light1pos;varying vec3 light1halfvec;varying float light1distance;varying vec3 light2dir;varying vec3 light2pos;varying vec3 light2halfvec;varying float light2distance;const int DIRECTIONALLIGHT = 1;const int POINTLIGHT = 2;const int SPOTLIGHT = 3;vec4 outTexturing(){	vec4 outtex = gl_Color;	if(mixtex1)	{		outtex = texture2D(tex0, gl_TexCoord[0].st);	}	if(mixtex2)	{		outtex = mix(outtex, texture2D(tex1, gl_TexCoord[1].st), 0.5);	}	if(decaltex1)	{		float outalpha = texture2D(tex2, gl_TexCoord[2].st).w;		outalpha = outalpha * -1.0 + 1.0;		outtex.xyz = (outtex.xyz * outalpha) + texture2D(tex2, gl_TexCoord[2].st).xyz * texture2D(tex2, gl_TexCoord[2].st).w;	}	if(decaltex2)	{		float outalpha = texture2D(tex3, gl_TexCoord[3].st).w;		outalpha = outalpha * -1.0 + 1.0;		outtex.xyz = (outtex.xyz * outalpha) + texture2D(tex3, gl_TexCoord[3].st).xyz * texture2D(tex3, gl_TexCoord[3].st).w;	}	return outtex;}void main(){	vec3 norm, halfV;	vec4 amb = gl_LightModel.ambient * gl_FrontMaterial.ambient;	vec4 dif = vec4(0.0, 0.0, 0.0, 0.0);	vec4 spc = vec4(0.0, 0.0, 0.0, 0.0);	float NdotL, NdotHV;	float attenuation;	float spotEffect;		norm = normalize(eyeNormal);		if(light0 == DIRECTIONALLIGHT)	{		amb = amb + gl_FrontMaterial.ambient * gl_LightSource[0].ambient;		NdotL = max(dot(norm, light0dir), 0.0);		if(NdotL > 0.0)		{		  halfV = normalize(light0halfvec);			dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse) * NdotL;			NdotHV = max(dot(norm, halfV), 0.0);			spc = spc + (gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV, gl_FrontMaterial.shininess));		}	}	else if(light0 == POINTLIGHT)	{		NdotL = max(dot(norm, light0pos), 0.0);		if(NdotL > 0.0)		{			attenuation = 1.0 / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * light0distance + 													 gl_LightSource[0].quadraticAttenuation * light0distance * light0distance);			halfV = normalize(light0halfvec);			NdotHV = max(dot(norm, halfV), 0.0);			amb = amb + (gl_FrontMaterial.ambient * gl_LightSource[0].ambient) * attenuation;			dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse * NdotL) * attenuation;			spc = spc + (gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV, gl_FrontMaterial.shininess)) * attenuation;		}	}	else if(light0 == SPOTLIGHT)	{		NdotL = max(dot(norm, light0pos), 0.0);		if(NdotL > 0.0)		{			spotEffect = dot(normalize(gl_LightSource[0].spotDirection), normalize(-light0pos));			if(spotEffect > gl_LightSource[0].spotCosCutoff)			{				spotEffect = pow(spotEffect, gl_LightSource[0].spotExponent);				attenuation = 1.0 / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * light0distance + gl_LightSource[0].quadraticAttenuation * light0distance * light0distance);				halfV = normalize(light0halfvec);				NdotHV = max(dot(norm, halfV), 0.0);				amb = amb + (gl_FrontMaterial.ambient * gl_LightSource[0].ambient) * attenuation;				dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse * NdotL) * attenuation;				spc = spc + (gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV, gl_FrontMaterial.shininess)) * attenuation;			}		}	}	if(light1 == DIRECTIONALLIGHT)	{		amb = amb + gl_FrontMaterial.ambient * gl_LightSource[1].ambient;		NdotL = max(dot(norm, light0dir), 0.0);		if(NdotL > 0.0)		{		  halfV = normalize(light1halfvec);			dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse) * NdotL;			NdotHV = max(dot(norm, halfV), 0.0);			spc = spc + (gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV, gl_FrontMaterial.shininess));		}	}	else if(light1 == POINTLIGHT)	{		NdotL = max(dot(norm, light1pos), 0.0);		if(NdotL > 0.0)		{			attenuation = 1.0 / (gl_LightSource[1].constantAttenuation + gl_LightSource[1].linearAttenuation * light1distance + 													 gl_LightSource[1].quadraticAttenuation * light1distance * light1distance);			halfV = normalize(light1halfvec);			NdotHV = max(dot(norm, halfV), 0.0);			amb = amb + (gl_FrontMaterial.ambient * gl_LightSource[1].ambient) * attenuation;			dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse * NdotL) * attenuation;			spc = spc + (gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV, gl_FrontMaterial.shininess)) * attenuation;		}	}	else if(light1 == SPOTLIGHT)	{		NdotL = max(dot(norm, light1pos), 0.0);		if(NdotL > 0.0)		{			spotEffect = dot(normalize(gl_LightSource[1].spotDirection), normalize(-light1pos));			if(spotEffect > gl_LightSource[1].spotCosCutoff)			{				spotEffect = pow(spotEffect, gl_LightSource[1].spotExponent);				attenuation = 1.0 / (gl_LightSource[1].constantAttenuation + gl_LightSource[1].linearAttenuation * light1distance + gl_LightSource[1].quadraticAttenuation * light1distance * light1distance);				halfV = normalize(light1halfvec);				NdotHV = max(dot(norm, halfV), 0.0);				amb = amb + (gl_FrontMaterial.ambient * gl_LightSource[1].ambient) * attenuation;				dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse * NdotL) * attenuation;				spc = spc + (gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV, gl_FrontMaterial.shininess)) * attenuation;			}		}	}	if(light2 == DIRECTIONALLIGHT)	{		amb = amb + gl_FrontMaterial.ambient * gl_LightSource[2].ambient;		NdotL = max(dot(norm, light2dir), 0.0);		if(NdotL > 0.0)		{		  halfV = normalize(light2halfvec);			dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[2].diffuse) * NdotL;			NdotHV = max(dot(norm, halfV), 0.0);			spc = spc + (gl_FrontMaterial.specular * gl_LightSource[2].specular * pow(NdotHV, gl_FrontMaterial.shininess));		}	}	else if(light2 == POINTLIGHT)	{		NdotL = max(dot(norm, light2pos), 0.0);		if(NdotL > 0.0)		{			attenuation = 1.0 / (gl_LightSource[2].constantAttenuation + gl_LightSource[2].linearAttenuation * light2distance + 													 gl_LightSource[2].quadraticAttenuation * light2distance * light2distance);			halfV = normalize(light2halfvec);			NdotHV = max(dot(norm, halfV), 0.0);			amb = amb + (gl_FrontMaterial.ambient * gl_LightSource[2].ambient) * attenuation;			dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[2].diffuse * NdotL) * attenuation;			spc = spc + (gl_FrontMaterial.specular * gl_LightSource[2].specular * pow(NdotHV, gl_FrontMaterial.shininess)) * attenuation;		}	}	else if(light2 == SPOTLIGHT)	{		NdotL = max(dot(norm, light2pos), 0.0);		if(NdotL > 0.0)		{			spotEffect = dot(normalize(gl_LightSource[2].spotDirection), normalize(-light2pos));			if(spotEffect > gl_LightSource[2].spotCosCutoff)			{				spotEffect = pow(spotEffect, gl_LightSource[2].spotExponent);				attenuation = 1.0 / (gl_LightSource[2].constantAttenuation + gl_LightSource[2].linearAttenuation * light2distance + gl_LightSource[2].quadraticAttenuation * light2distance * light2distance);				halfV = normalize(light2halfvec);				NdotHV = max(dot(norm, halfV), 0.0);				amb = amb + (gl_FrontMaterial.ambient * gl_LightSource[2].ambient) * attenuation;				dif = dif + (gl_FrontMaterial.diffuse * gl_LightSource[2].diffuse * NdotL) * attenuation;				spc = spc + (gl_FrontMaterial.specular * gl_LightSource[2].specular * pow(NdotHV, gl_FrontMaterial.shininess)) * attenuation;			}		}	}	gl_FragColor = outTexturing() * amb + outTexturing() * dif + spc;}

Thanks for the help so far.
Wow, that's a lot of "if statements".
May I suggest that you toss out fixed functionality. Stop using all the built in stuff like gl_NormalMatrix, gl_LightSource, gl_TexCoord[3] since GL has gotten a facelift from GL 3.0 and after.
Of course, if you want to continue to use those, you can.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);
Do I just write my own matrices and coordinates and such then? Then I don't have limits on anything I do? I would still have limits on the number of varyings I can have, which is the problem right now...
its not gonna remove the limitations
looking at the shader (which would give the GPU a heart attack) ild say break it up into smaller shaders, use multipass (which is just redrawing the objects again with blending enabled eg GL_ONE GL_ONE)
honestly your framerate would go from ~5fps to ~100fps

ild have a diferent shader for each pointlight, spotlight + dirlight (dirlight doesnt need to know the light pos for example, theres less work done)
Does the order matter when doing multipass? E.g. calling the lighting before the texturing.

This topic is closed to new replies.

Advertisement