Difference between revisions of "OpenGL Shaders (GLSL)"
(→Known issues: OS X 10.6 is ancient now, so remove from known issues list) |
|||
(17 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
− | |||
− | |||
==Introduction== | ==Introduction== | ||
− | |||
− | + | Starting with FS2 Open version 3.6.10, the graphics pipeline has been rewritten to make it possible to use Shaders written in [[wikipedia:GLSL|GLSL]]. These allow the use of various new graphical effects, such as parallax mapping, normal mapping, or pixel-accurate lighting. | |
+ | In FSO version 3.6.12, a post-processing pipeline was added, allowing the application of screen-space effects, like bloom, or noise effects. | ||
==Notes and system requirements== | ==Notes and system requirements== | ||
− | * | + | In FSO 3.6.12, the basic requirements for the use of shaders are: |
− | ** nVidia: GeForce 6xxx or newer card. | + | *A Shader Model 3.0 capable GPU is needed for shaders to work. So you need: |
− | ** ATI/AMD: Radeon (X1xxx), HD 2xxx or newer card. | + | **nVidia: GeForce 6xxx or newer card. |
− | * | + | **ATI/AMD: Radeon (X1xxx), HD 2xxx or newer card. |
− | + | *A set of shader files (as distributed with the [[MediaVPs]]) | |
− | |||
− | |||
− | |||
+ | As of version 3.6.13, the Shader Model requirement has been dropped to allow cards that are only capable of Shader Model 2 support to use shaders. | ||
==Known issues== | ==Known issues== | ||
Line 34: | Line 30: | ||
** Run the game. Expect possible bad performance. | ** Run the game. Expect possible bad performance. | ||
** (If you prefer, you can look for just old Atioglxx.dll download links in HLP forums). | ** (If you prefer, you can look for just old Atioglxx.dll download links in HLP forums). | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
===ForceWare 169.xx=== | ===ForceWare 169.xx=== | ||
These drivers are buggy and don't work fine (several graphical glitches). Why the hell are you using them? They're ancient. Update to a newer version NOW. | These drivers are buggy and don't work fine (several graphical glitches). Why the hell are you using them? They're ancient. Update to a newer version NOW. | ||
− | |||
==Description of shaders== | ==Description of shaders== | ||
Line 52: | Line 39: | ||
* They are placed in [[FS2_Data_Structure#effects|effects subfolder]] within general data structure. They work like any other game file: they can be packed in vp archives and they are fully affected by overall precedence priority. | * They are placed in [[FS2_Data_Structure#effects|effects subfolder]] within general data structure. They work like any other game file: they can be packed in vp archives and they are fully affected by overall precedence priority. | ||
* Starting from version 3.6.10., MediaVP package includes a full set of shaders within mv_core.vp. | * Starting from version 3.6.10., MediaVP package includes a full set of shaders within mv_core.vp. | ||
− | * The | + | * The MediaVPs shaders are so-called "unified" shaders. These are designed to perform all necessary operations in one file, where the specific behaviour for all possible usage scenarios is defined through the use of preprocessor directives. These shaders can be found in mv_root.vp, in the data/effects folder, under the names "main-f.sdr" and "main-v.sdr" |
− | + | ||
− | + | ==Examples== | |
− | + | ===Per-Fragment Lighting Shaders for FSO 3.6.13=== | |
− | ** | + | |
− | ** | + | These shaders use the so-called per-fragment lighting model. This means that, unlike the fixed-function graphics pipeline, lighting values are computed for every pixel (or fragment) on the screen. This leads to a more accurate, smoother lighting across the lit surface. The code examples here assume a working knowledge of GLSL, consult the GLSL Tutorial linked to below for details. |
− | + | Here's an example of fixed-function lighting (Also called per-vertex lighting): | |
− | ** | + | [[image:Pointgl.gif]] |
− | + | ||
− | ** | + | And the same scene with per-fragment lighting: |
+ | [[image:Pointpix.gif]] | ||
+ | |||
+ | We start off with the vertex shader, main-v.sdr. | ||
+ | |||
+ | <pre>#ifdef FLAG_ENV_MAP | ||
+ | uniform mat4 envMatrix; | ||
+ | varying vec3 envReflect; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_NORMAL_MAP | ||
+ | varying mat3 tbnMatrix; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_FOG | ||
+ | varying float fogDist; | ||
+ | #endif | ||
+ | |||
+ | varying vec4 position; | ||
+ | varying vec3 lNormal; | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | gl_TexCoord[0] = gl_MultiTexCoord0; | ||
+ | gl_Position = ftransform(); | ||
+ | gl_FrontColor = gl_Color; | ||
+ | gl_FrontSecondaryColor = vec4(0.0, 0.0, 0.0, 1.0); | ||
+ | |||
+ | // Transform the normal into eye space and normalize the result. | ||
+ | position = gl_ModelViewMatrix * gl_Vertex; | ||
+ | vec3 normal = normalize(gl_NormalMatrix * gl_Normal); | ||
+ | lNormal = normal; | ||
+ | |||
+ | #ifdef FLAG_NORMAL_MAP | ||
+ | // Setup stuff for normal maps | ||
+ | vec3 t = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz); | ||
+ | vec3 b = cross(normal, t) * gl_MultiTexCoord1.w; | ||
+ | tbnMatrix = mat3(t, b, normal); | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_ENV_MAP | ||
+ | // Environment mapping reflection vector. | ||
+ | envReflect = reflect(position.xyz, normal); | ||
+ | envReflect = vec3(envMatrix * vec4(envReflect, 0.0)); | ||
+ | envReflect = normalize(envReflect); | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_FOG | ||
+ | fogDist = clamp((gl_Position.z - gl_Fog.start) * 0.75 * gl_Fog.scale, 0.0, 1.0); | ||
+ | #endif | ||
+ | |||
+ | #ifdef __GLSL_CG_DATA_TYPES | ||
+ | // Check necessary for ATI specific behavior | ||
+ | gl_ClipVertex = (gl_ModelViewMatrix * gl_Vertex); | ||
+ | #endif | ||
+ | }</pre> | ||
+ | |||
+ | The fragment shader, main-f.sdr: | ||
+ | |||
+ | <pre>#ifdef FLAG_LIGHT | ||
+ | uniform int n_lights; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_DIFFUSE_MAP | ||
+ | uniform sampler2D sBasemap; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_GLOW_MAP | ||
+ | uniform sampler2D sGlowmap; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_SPEC_MAP | ||
+ | uniform sampler2D sSpecmap; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_ENV_MAP | ||
+ | uniform samplerCube sEnvmap; | ||
+ | uniform bool alpha_spec; | ||
+ | varying vec3 envReflect; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_NORMAL_MAP | ||
+ | uniform sampler2D sNormalmap; | ||
+ | varying mat3 tbnMatrix; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_FOG | ||
+ | varying float fogDist; | ||
+ | #endif | ||
+ | |||
+ | varying vec4 position; | ||
+ | varying vec3 lNormal; | ||
+ | |||
+ | #if SHADER_MODEL == 2 | ||
+ | #define MAX_LIGHTS 2 | ||
+ | #else | ||
+ | #define MAX_LIGHTS 8 | ||
+ | #endif | ||
+ | |||
+ | #define SPEC_INTENSITY_POINT 4.3 // Point light | ||
+ | #define SPEC_INTENSITY_DIRECTIONAL 1.5 // Directional light | ||
+ | #define SPECULAR_FACTOR 1.75 | ||
+ | #define SPECULAR_ALPHA 0.1 | ||
+ | #define SPEC_FACTOR_NO_SPEC_MAP 0.6 | ||
+ | #define ENV_ALPHA_FACTOR 0.3 | ||
+ | #define GLOW_MAP_INTENSITY 1.5 | ||
+ | #define AMBIENT_LIGHT_BOOST 1.0 | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | vec3 eyeDir = vec3(normalize(-position).xyz); // Camera is at (0,0,0) in ModelView space | ||
+ | vec4 lightAmbientDiffuse = vec4(0.0, 0.0, 0.0, 1.0); | ||
+ | vec4 lightDiffuse = vec4(0.0, 0.0, 0.0, 1.0); | ||
+ | vec4 lightAmbient = vec4(0.0, 0.0, 0.0, 1.0); | ||
+ | vec4 lightSpecular = vec4(0.0, 0.0, 0.0, 1.0); | ||
+ | vec2 texCoord = gl_TexCoord[0].xy; | ||
+ | |||
+ | #ifdef FLAG_LIGHT | ||
+ | #ifdef FLAG_NORMAL_MAP | ||
+ | // Normal map - convert from DXT5nm | ||
+ | vec3 normal; | ||
+ | normal.rg = (texture2D(sNormalmap, texCoord).ag * 2.0) - 1.0; | ||
+ | normal.b = sqrt(1.0 - dot(normal.rg, normal.rg)); | ||
+ | normal = normalize(tbnMatrix * normal); | ||
+ | #else | ||
+ | vec3 normal = lNormal; | ||
+ | #endif | ||
+ | |||
+ | vec3 lightDir; | ||
+ | lightAmbient = gl_FrontMaterial.emission + (gl_LightModel.ambient * gl_FrontMaterial.ambient); | ||
+ | |||
+ | #pragma optionNV unroll all | ||
+ | for (int i = 0; i < MAX_LIGHTS; ++i) { | ||
+ | #if SHADER_MODEL > 2 | ||
+ | if (i > n_lights) | ||
+ | break; | ||
+ | #endif | ||
+ | float specularIntensity = 1.0; | ||
+ | float attenuation = 1.0; | ||
+ | |||
+ | // Attenuation and light direction | ||
+ | #if SHADER_MODEL > 2 | ||
+ | if (gl_LightSource[i].position.w == 1.0) { | ||
+ | #else | ||
+ | if (gl_LightSource[i].position.w == 1.0 && i != 0) { | ||
+ | #endif | ||
+ | // Positional light source | ||
+ | float dist = distance(gl_LightSource[i].position.xyz, position.xyz); | ||
+ | |||
+ | float spotEffect = 1.0; | ||
+ | |||
+ | #if SHADER_MODEL > 2 | ||
+ | if (gl_LightSource[i].spotCutoff < 91.0) { | ||
+ | spotEffect = dot(normalize(gl_LightSource[i].spotDirection), normalize(-position.xyz)); | ||
+ | |||
+ | if (spotEffect < gl_LightSource[i].spotCosCutoff) { | ||
+ | spotEffect = 0.0; | ||
+ | } | ||
+ | } | ||
+ | #endif | ||
+ | |||
+ | attenuation = spotEffect / (gl_LightSource[i].constantAttenuation + (gl_LightSource[i].linearAttenuation * dist) + (gl_LightSource[i].quadraticAttenuation * dist * dist)); | ||
− | === | + | lightDir = normalize(gl_LightSource[i].position.xyz - position.xyz); |
+ | |||
+ | specularIntensity = SPEC_INTENSITY_POINT; // Point light | ||
+ | } else { | ||
+ | // Directional light source | ||
+ | lightDir = normalize(gl_LightSource[i].position.xyz); | ||
+ | |||
+ | specularIntensity = SPEC_INTENSITY_DIRECTIONAL; // Directional light | ||
+ | } | ||
+ | |||
+ | // Ambient and Diffuse | ||
+ | lightAmbient += (gl_FrontLightProduct[i].ambient * attenuation); | ||
+ | lightDiffuse += ((gl_FrontLightProduct[i].diffuse * max(dot(normal, lightDir), 0.0)) * attenuation); | ||
+ | |||
+ | // Specular | ||
+ | float NdotHV = clamp(dot(normal, normalize(eyeDir + lightDir)), 0.0, 1.0); | ||
+ | lightSpecular += ((gl_FrontLightProduct[i].specular * pow(max(0.0, NdotHV), gl_FrontMaterial.shininess)) * attenuation) * specularIntensity; | ||
+ | } | ||
+ | |||
+ | lightAmbientDiffuse = lightAmbient + lightDiffuse; | ||
+ | #else | ||
+ | lightAmbientDiffuse = gl_Color; | ||
+ | lightSpecular = gl_SecondaryColor; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_DIFFUSE_MAP | ||
+ | // Base color | ||
+ | vec4 baseColor = texture2D(sBasemap, texCoord); | ||
+ | #else | ||
+ | vec4 baseColor = gl_Color; | ||
+ | #endif | ||
+ | |||
+ | vec4 fragmentColor; | ||
+ | fragmentColor.rgb = baseColor.rgb * max(lightAmbientDiffuse.rgb * AMBIENT_LIGHT_BOOST, gl_LightModel.ambient.rgb - 0.425); | ||
+ | fragmentColor.a = baseColor.a; | ||
+ | |||
+ | #ifdef FLAG_SPEC_MAP | ||
+ | // Spec color | ||
+ | fragmentColor.rgb += lightSpecular.rgb * (texture2D(sSpecmap, texCoord).rgb * SPECULAR_FACTOR); | ||
+ | fragmentColor.a += (dot(lightSpecular.a, lightSpecular.a) * SPECULAR_ALPHA); | ||
+ | #else | ||
+ | fragmentColor.rgb += lightSpecular.rgb * (baseColor.rgb * SPEC_FACTOR_NO_SPEC_MAP); | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_ENV_MAP | ||
+ | // Env color | ||
+ | vec3 envIntensity = (alpha_spec) ? vec3(texture2D(sSpecmap, texCoord).a) : texture2D(sSpecmap, texCoord).rgb; | ||
+ | fragmentColor.a += (dot(textureCube(sEnvmap, envReflect).rgb, textureCube(sEnvmap, envReflect).rgb) * ENV_ALPHA_FACTOR); | ||
+ | fragmentColor.rgb += textureCube(sEnvmap, envReflect).rgb * envIntensity; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_GLOW_MAP | ||
+ | // Glow color | ||
+ | fragmentColor.rgb += texture2D(sGlowmap, texCoord).rgb * GLOW_MAP_INTENSITY; | ||
+ | #endif | ||
+ | |||
+ | #ifdef FLAG_FOG | ||
+ | fragmentColor.rgb = mix(fragmentColor.rgb, gl_Fog.color.rgb, fogDist); | ||
+ | #endif | ||
+ | |||
+ | gl_FragColor = fragmentColor; | ||
+ | }</pre> | ||
+ | |||
+ | ===Preprocessor defines=== | ||
+ | |||
+ | *FLAG_LIGHT | ||
+ | **The shader is expected to handle lighting. This flag has one associated uniform: | ||
+ | **uniform int n_lights // The number of lights in the current scene, 0..8 inclusive. | ||
+ | |||
+ | *FLAG_DIFFUSE_MAP | ||
+ | **The shader is expected to handle the application of the basic diffuse map. This flag has one associated uniform: | ||
+ | **uniform sampler2D sBasemap // The diffuse texture | ||
+ | |||
+ | *FLAG_GLOW_MAP | ||
+ | **The shader is expected to handle the application of the glowmap. This flag has one associated uniform: | ||
+ | **uniform sampler2D sGlowmap // The glow texture | ||
+ | |||
+ | *FLAG_SPEC_MAP | ||
+ | **The shader is expected to handle the application of the specular map. This flag has one associated uniform: | ||
+ | **uniform sampler2D sSpecmap // The specular texture | ||
+ | |||
+ | *FLAG_ENV_MAP | ||
+ | **This shader is expected to handle environment mapping. There are several uniforms associated with this flag: | ||
+ | **uniform samplerCube sEnvmap; //The cubemap | ||
+ | **uniform bool alpha_spec; //Whether or not there's a specular map with an alpha channel present. | ||
+ | **uniform mat4 envMatrix; | ||
+ | |||
+ | *FLAG_NORMAL_MAP | ||
+ | **This shader is expected to handle normal mapping. One associated uniform: | ||
+ | **uniform sampler2D sNormalmap // The normal map | ||
+ | |||
+ | *FLAG_HEIGHT_MAP | ||
+ | **This shader is expected to handle parallax mapping. One associated uniform: | ||
+ | **uniform sampler2D sHeightmap // The height map | ||
+ | |||
+ | *FLAG_FOG | ||
+ | **Fogging effects are enabled (as in a nebula). No associated uniforms. | ||
+ | {{Table3613| | ||
+ | *SHADER_MODEL | ||
+ | **An integer, which will tell you which shader model the current hardware supports.}} | ||
+ | |||
+ | ==Shader Limitations== | ||
+ | *Due to limitations of OpenGL, only 8 lights can be handled at once. While the Engine keeps track of up to 256 lights per scene, only the 8 strongest ones are passed to the shader. | ||
+ | |||
+ | |||
+ | ==External links:== | ||
http://en.wikipedia.org/wiki/Shader | http://en.wikipedia.org/wiki/Shader | ||
Line 70: | Line 323: | ||
http://www.opengl.org/documentation/glsl/ | http://www.opengl.org/documentation/glsl/ | ||
+ | ===OpenGL Org's Typhoon Labs Shader Designer=== | ||
+ | |||
+ | http://www.opengl.org/sdk/tools/ShaderDesigner/ | ||
+ | |||
+ | ===nVidia Shader applications:=== | ||
+ | |||
+ | http://developer.nvidia.com/object/fx_composer_home.html | ||
+ | |||
+ | http://developer.nvidia.com/object/nv_shader_debugger_home.html | ||
+ | |||
+ | http://developer.download.nvidia.com/shaderlibrary/webpages/shader_library.html | ||
+ | |||
+ | ===ATI Shader tools=== | ||
+ | |||
+ | http://developer.amd.com/gpu/rendermonkey/Pages/default.aspx | ||
+ | |||
+ | http://developer.amd.com/gpu/archive/ashli/Pages/default.aspx | ||
+ | |||
+ | ===Apple OpenGL tools=== | ||
+ | |||
+ | http://developer.apple.com/graphicsimaging/opengl/opengl_serious.html | ||
[[Category:Source Code Project]] | [[Category:Source Code Project]] |
Latest revision as of 05:14, 3 May 2015
Contents
Introduction
Starting with FS2 Open version 3.6.10, the graphics pipeline has been rewritten to make it possible to use Shaders written in GLSL. These allow the use of various new graphical effects, such as parallax mapping, normal mapping, or pixel-accurate lighting. In FSO version 3.6.12, a post-processing pipeline was added, allowing the application of screen-space effects, like bloom, or noise effects.
Notes and system requirements
In FSO 3.6.12, the basic requirements for the use of shaders are:
- A Shader Model 3.0 capable GPU is needed for shaders to work. So you need:
- nVidia: GeForce 6xxx or newer card.
- ATI/AMD: Radeon (X1xxx), HD 2xxx or newer card.
- A set of shader files (as distributed with the MediaVPs)
As of version 3.6.13, the Shader Model requirement has been dropped to allow cards that are only capable of Shader Model 2 support to use shaders.
Known issues
ATI/AMD X1xxx GPUs
These GPUs are, in theory, SM 3.0 capable so they should run the shaders fine. BUT THEY DON'T. The game usually crashes or freezes with these GPUs.
Several SCP people are studying the issue. Speculation about its lack of Vertex Texture Fetch support has been stated as the source of the issue. But nothing has been set as the final conclusion. Here you have a link to VTF article in opengl.org wiki.
Possible solutions:
- Use -no_glsl.
- Use-at-your-own-risk hack for Windows XP 32-bit users:
- Update your Catalyst to the latest version.
- Locate 7.9 or older Catalyst version.
- Extract the installation archive but do not install it.
- Grab Atioglxx.dll from it.
- Copy Atioglxx.dll in FS2 folder. In this way your system will be updated to the last Catalyst version BUT FS2 will use an old one.
- Run the game. Expect possible bad performance.
- (If you prefer, you can look for just old Atioglxx.dll download links in HLP forums).
ForceWare 169.xx
These drivers are buggy and don't work fine (several graphical glitches). Why the hell are you using them? They're ancient. Update to a newer version NOW.
Description of shaders
- Shader files have got .sdr extension.
- They are placed in effects subfolder within general data structure. They work like any other game file: they can be packed in vp archives and they are fully affected by overall precedence priority.
- Starting from version 3.6.10., MediaVP package includes a full set of shaders within mv_core.vp.
- The MediaVPs shaders are so-called "unified" shaders. These are designed to perform all necessary operations in one file, where the specific behaviour for all possible usage scenarios is defined through the use of preprocessor directives. These shaders can be found in mv_root.vp, in the data/effects folder, under the names "main-f.sdr" and "main-v.sdr"
Examples
Per-Fragment Lighting Shaders for FSO 3.6.13
These shaders use the so-called per-fragment lighting model. This means that, unlike the fixed-function graphics pipeline, lighting values are computed for every pixel (or fragment) on the screen. This leads to a more accurate, smoother lighting across the lit surface. The code examples here assume a working knowledge of GLSL, consult the GLSL Tutorial linked to below for details. Here's an example of fixed-function lighting (Also called per-vertex lighting):
And the same scene with per-fragment lighting:
We start off with the vertex shader, main-v.sdr.
#ifdef FLAG_ENV_MAP uniform mat4 envMatrix; varying vec3 envReflect; #endif #ifdef FLAG_NORMAL_MAP varying mat3 tbnMatrix; #endif #ifdef FLAG_FOG varying float fogDist; #endif varying vec4 position; varying vec3 lNormal; void main() { gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = ftransform(); gl_FrontColor = gl_Color; gl_FrontSecondaryColor = vec4(0.0, 0.0, 0.0, 1.0); // Transform the normal into eye space and normalize the result. position = gl_ModelViewMatrix * gl_Vertex; vec3 normal = normalize(gl_NormalMatrix * gl_Normal); lNormal = normal; #ifdef FLAG_NORMAL_MAP // Setup stuff for normal maps vec3 t = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz); vec3 b = cross(normal, t) * gl_MultiTexCoord1.w; tbnMatrix = mat3(t, b, normal); #endif #ifdef FLAG_ENV_MAP // Environment mapping reflection vector. envReflect = reflect(position.xyz, normal); envReflect = vec3(envMatrix * vec4(envReflect, 0.0)); envReflect = normalize(envReflect); #endif #ifdef FLAG_FOG fogDist = clamp((gl_Position.z - gl_Fog.start) * 0.75 * gl_Fog.scale, 0.0, 1.0); #endif #ifdef __GLSL_CG_DATA_TYPES // Check necessary for ATI specific behavior gl_ClipVertex = (gl_ModelViewMatrix * gl_Vertex); #endif }
The fragment shader, main-f.sdr:
#ifdef FLAG_LIGHT uniform int n_lights; #endif #ifdef FLAG_DIFFUSE_MAP uniform sampler2D sBasemap; #endif #ifdef FLAG_GLOW_MAP uniform sampler2D sGlowmap; #endif #ifdef FLAG_SPEC_MAP uniform sampler2D sSpecmap; #endif #ifdef FLAG_ENV_MAP uniform samplerCube sEnvmap; uniform bool alpha_spec; varying vec3 envReflect; #endif #ifdef FLAG_NORMAL_MAP uniform sampler2D sNormalmap; varying mat3 tbnMatrix; #endif #ifdef FLAG_FOG varying float fogDist; #endif varying vec4 position; varying vec3 lNormal; #if SHADER_MODEL == 2 #define MAX_LIGHTS 2 #else #define MAX_LIGHTS 8 #endif #define SPEC_INTENSITY_POINT 4.3 // Point light #define SPEC_INTENSITY_DIRECTIONAL 1.5 // Directional light #define SPECULAR_FACTOR 1.75 #define SPECULAR_ALPHA 0.1 #define SPEC_FACTOR_NO_SPEC_MAP 0.6 #define ENV_ALPHA_FACTOR 0.3 #define GLOW_MAP_INTENSITY 1.5 #define AMBIENT_LIGHT_BOOST 1.0 void main() { vec3 eyeDir = vec3(normalize(-position).xyz); // Camera is at (0,0,0) in ModelView space vec4 lightAmbientDiffuse = vec4(0.0, 0.0, 0.0, 1.0); vec4 lightDiffuse = vec4(0.0, 0.0, 0.0, 1.0); vec4 lightAmbient = vec4(0.0, 0.0, 0.0, 1.0); vec4 lightSpecular = vec4(0.0, 0.0, 0.0, 1.0); vec2 texCoord = gl_TexCoord[0].xy; #ifdef FLAG_LIGHT #ifdef FLAG_NORMAL_MAP // Normal map - convert from DXT5nm vec3 normal; normal.rg = (texture2D(sNormalmap, texCoord).ag * 2.0) - 1.0; normal.b = sqrt(1.0 - dot(normal.rg, normal.rg)); normal = normalize(tbnMatrix * normal); #else vec3 normal = lNormal; #endif vec3 lightDir; lightAmbient = gl_FrontMaterial.emission + (gl_LightModel.ambient * gl_FrontMaterial.ambient); #pragma optionNV unroll all for (int i = 0; i < MAX_LIGHTS; ++i) { #if SHADER_MODEL > 2 if (i > n_lights) break; #endif float specularIntensity = 1.0; float attenuation = 1.0; // Attenuation and light direction #if SHADER_MODEL > 2 if (gl_LightSource[i].position.w == 1.0) { #else if (gl_LightSource[i].position.w == 1.0 && i != 0) { #endif // Positional light source float dist = distance(gl_LightSource[i].position.xyz, position.xyz); float spotEffect = 1.0; #if SHADER_MODEL > 2 if (gl_LightSource[i].spotCutoff < 91.0) { spotEffect = dot(normalize(gl_LightSource[i].spotDirection), normalize(-position.xyz)); if (spotEffect < gl_LightSource[i].spotCosCutoff) { spotEffect = 0.0; } } #endif attenuation = spotEffect / (gl_LightSource[i].constantAttenuation + (gl_LightSource[i].linearAttenuation * dist) + (gl_LightSource[i].quadraticAttenuation * dist * dist)); lightDir = normalize(gl_LightSource[i].position.xyz - position.xyz); specularIntensity = SPEC_INTENSITY_POINT; // Point light } else { // Directional light source lightDir = normalize(gl_LightSource[i].position.xyz); specularIntensity = SPEC_INTENSITY_DIRECTIONAL; // Directional light } // Ambient and Diffuse lightAmbient += (gl_FrontLightProduct[i].ambient * attenuation); lightDiffuse += ((gl_FrontLightProduct[i].diffuse * max(dot(normal, lightDir), 0.0)) * attenuation); // Specular float NdotHV = clamp(dot(normal, normalize(eyeDir + lightDir)), 0.0, 1.0); lightSpecular += ((gl_FrontLightProduct[i].specular * pow(max(0.0, NdotHV), gl_FrontMaterial.shininess)) * attenuation) * specularIntensity; } lightAmbientDiffuse = lightAmbient + lightDiffuse; #else lightAmbientDiffuse = gl_Color; lightSpecular = gl_SecondaryColor; #endif #ifdef FLAG_DIFFUSE_MAP // Base color vec4 baseColor = texture2D(sBasemap, texCoord); #else vec4 baseColor = gl_Color; #endif vec4 fragmentColor; fragmentColor.rgb = baseColor.rgb * max(lightAmbientDiffuse.rgb * AMBIENT_LIGHT_BOOST, gl_LightModel.ambient.rgb - 0.425); fragmentColor.a = baseColor.a; #ifdef FLAG_SPEC_MAP // Spec color fragmentColor.rgb += lightSpecular.rgb * (texture2D(sSpecmap, texCoord).rgb * SPECULAR_FACTOR); fragmentColor.a += (dot(lightSpecular.a, lightSpecular.a) * SPECULAR_ALPHA); #else fragmentColor.rgb += lightSpecular.rgb * (baseColor.rgb * SPEC_FACTOR_NO_SPEC_MAP); #endif #ifdef FLAG_ENV_MAP // Env color vec3 envIntensity = (alpha_spec) ? vec3(texture2D(sSpecmap, texCoord).a) : texture2D(sSpecmap, texCoord).rgb; fragmentColor.a += (dot(textureCube(sEnvmap, envReflect).rgb, textureCube(sEnvmap, envReflect).rgb) * ENV_ALPHA_FACTOR); fragmentColor.rgb += textureCube(sEnvmap, envReflect).rgb * envIntensity; #endif #ifdef FLAG_GLOW_MAP // Glow color fragmentColor.rgb += texture2D(sGlowmap, texCoord).rgb * GLOW_MAP_INTENSITY; #endif #ifdef FLAG_FOG fragmentColor.rgb = mix(fragmentColor.rgb, gl_Fog.color.rgb, fogDist); #endif gl_FragColor = fragmentColor; }
Preprocessor defines
- FLAG_LIGHT
- The shader is expected to handle lighting. This flag has one associated uniform:
- uniform int n_lights // The number of lights in the current scene, 0..8 inclusive.
- FLAG_DIFFUSE_MAP
- The shader is expected to handle the application of the basic diffuse map. This flag has one associated uniform:
- uniform sampler2D sBasemap // The diffuse texture
- FLAG_GLOW_MAP
- The shader is expected to handle the application of the glowmap. This flag has one associated uniform:
- uniform sampler2D sGlowmap // The glow texture
- FLAG_SPEC_MAP
- The shader is expected to handle the application of the specular map. This flag has one associated uniform:
- uniform sampler2D sSpecmap // The specular texture
- FLAG_ENV_MAP
- This shader is expected to handle environment mapping. There are several uniforms associated with this flag:
- uniform samplerCube sEnvmap; //The cubemap
- uniform bool alpha_spec; //Whether or not there's a specular map with an alpha channel present.
- uniform mat4 envMatrix;
- FLAG_NORMAL_MAP
- This shader is expected to handle normal mapping. One associated uniform:
- uniform sampler2D sNormalmap // The normal map
- FLAG_HEIGHT_MAP
- This shader is expected to handle parallax mapping. One associated uniform:
- uniform sampler2D sHeightmap // The height map
- FLAG_FOG
- Fogging effects are enabled (as in a nebula). No associated uniforms.
- SHADER_MODEL
- An integer, which will tell you which shader model the current hardware supports.
Shader Limitations
- Due to limitations of OpenGL, only 8 lights can be handled at once. While the Engine keeps track of up to 256 lights per scene, only the 8 strongest ones are passed to the shader.
External links:
http://en.wikipedia.org/wiki/Shader
http://en.wikipedia.org/wiki/GLSL
http://www.opengl.org/documentation/glsl/
OpenGL Org's Typhoon Labs Shader Designer
http://www.opengl.org/sdk/tools/ShaderDesigner/
nVidia Shader applications:
http://developer.nvidia.com/object/fx_composer_home.html
http://developer.nvidia.com/object/nv_shader_debugger_home.html
http://developer.download.nvidia.com/shaderlibrary/webpages/shader_library.html
ATI Shader tools
http://developer.amd.com/gpu/rendermonkey/Pages/default.aspx
http://developer.amd.com/gpu/archive/ashli/Pages/default.aspx
Apple OpenGL tools
http://developer.apple.com/graphicsimaging/opengl/opengl_serious.html