// -*- mode: c -*- struct Material { vec3 ambient; bool useAmbientMap; vec3 diffuse; bool useDiffuseMap; vec3 specular; bool useSpecularMap; float shininess; bool useNormalMap; }; struct Light { bool enabled; int type; vec3 position; vec3 direction; vec4 color; float cutOff; }; #define MAX_LIGHTS 4 #ifdef GLSL120 varying vec3 fragWorldPos; varying vec3 fragNorm; varying vec2 fragTex; #else in vec3 fragWorldPos; in vec3 fragNorm; in vec2 fragTex; #endif #ifdef GLSL330 out vec4 fragColor; #endif uniform sampler2D ambientMap; uniform sampler2D diffuseMap; uniform sampler2D specularMap; uniform sampler2D normalMap; uniform Material material; uniform Light lights[MAX_LIGHTS]; uniform vec3 cameraPosition; uniform vec4 ambientLight; const float GAMMA = 2.2; #ifndef GLSL330 // Compatibility shim for older GLSL versions. vec2 texture(sampler2D tex, vec2 coord) { return texture2D(tex, coord); } #endif vec3 gammaCorrect(vec3 color) { return pow(color, vec3(1.0 / GAMMA)); } vec3 toneMap(vec3 color) { return color / (color + vec3(1.0)); } vec3 lightDirection(Light light) { if(light.type == 0 || light.type == 2) { // point and spot lights return normalize(light.position - fragWorldPos); } else if(light.type == 1) { // directional light return normalize(-light.direction); } return vec3(0.0); // should never be reached. } vec3 lightAttenuate(Light light) { float distance = length(light.position - fragWorldPos); float attenuation = 1.0 / (distance * distance); return light.color.rgb * attenuation; } vec3 lightRadiance(Light light, vec3 direction) { if(light.type == 0) { // point light return lightAttenuate(light); } else if(light.type == 1) { // directional light return light.color.rgb; } else if(light.type == 2) { // spot light float theta = dot(direction, normalize(-light.direction)); // Spot lights only shine light in a specific conical area. // They have no effect outside of that area. if(theta > light.cutOff) { // Feather out the light as it approaches the edge of its cone. float intensity = (theta - light.cutOff) / (1.0 - light.cutOff); return lightAttenuate(light) * intensity; } else { return vec3(0.0); } } return vec3(0.0); // should never be reached. } vec3 materialAmbient() { if(material.useAmbientMap) { return texture(ambientMap, fragTex).rgb; } else { return material.ambient; } } vec3 materialDiffuse() { if(material.useDiffuseMap) { vec4 color = texture(diffuseMap, fragTex); // discard transparent fragments. if(color.a == 0.0) { discard; } return color.rgb; } else { return material.diffuse; } } vec3 materialSpecular() { if(material.useSpecularMap) { return texture(specularMap, fragTex).rgb; } else { return material.specular; } } vec3 materialNormal() { if(material.useNormalMap) { // Compute tangent space using fragment data rather than relying // on tangent attributes. See: // http://www.thetenthplanet.de/archives/1180 vec3 tangentNormal = normalize(texture(normalMap, fragTex).xyz * 2.0 - 1.0); vec3 q1 = dFdx(fragWorldPos); vec3 q2 = dFdy(fragWorldPos); vec2 st1 = dFdx(fragTex); vec2 st2 = dFdy(fragTex); vec3 N = normalize(fragNorm); vec3 T = normalize(q1 * st2.t - q2 * st1.t); vec3 B = -normalize(cross(N, T)); mat3 TBN = mat3(T, B, N); return normalize(TBN * tangentNormal); } else { return normalize(fragNorm); } } void main() { vec3 viewDir = normalize(cameraPosition - fragWorldPos); vec3 ambientOcclusion = materialAmbient(); vec3 diffuseColor = materialDiffuse(); vec3 specularColor = materialSpecular(); vec3 normal = materialNormal(); vec3 color = vec3(0.0); // Apply direct lighting. for(int i = 0; i < MAX_LIGHTS; ++i) { Light light = lights[i]; if(!light.enabled) { continue; } vec3 lightDir = lightDirection(light); vec3 halfVector = normalize(lightDir + viewDir); vec3 radiance = lightRadiance(light, lightDir); float lambert = clamp(dot(normal, lightDir), 0.0, 1.0); vec3 diffuseLight = radiance * lambert; float specularFactor = clamp(dot(halfVector, normal), 0.0, 1.0) * float(lambert > 0.0); vec3 specularLight = radiance * pow(specularFactor, material.shininess); color += diffuseLight * diffuseColor + specularLight * specularColor; } // Apply ambient lighting. vec3 ambientColor = diffuseColor * ambientLight.rgb * ambientOcclusion; color += ambientColor; // Apply gamma correction and HDR tone mapping to get the final // color. vec4 finalColor = vec4(toneMap(gammaCorrect(color)), 1.0); #ifdef GLSL330 fragColor = finalColor; #else gl_FragColor = finalColor; #endif }