#version 330 // // Lighting vertex shader // Copyright (C) 2009 Alexey Torkhov // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // Based on OGRE samples // Some ideas were borrowed from Arseny Kapoulkine's blog - http://zeuxcg.blogspot.com/2007/10/my-own-lighting-shader-with-blackjack.html /* Explanation of preprocessor defines used: SKINNING: Hardware linear skinning should be used. The value specifies how many bones to use. The vertex position if calculated by the number of bones using the worldMatrix3x4Array uniform. Linear interpolation is used. blendIndices and blendWeights are used to lookup bones and their weight. SKINNING_DQ: Hardware double quaternion skinning should be used. The value specifies how many bones to use. The worldDualQuaternion2x4Array uniform specifies the quaternions. INSTANCED Hardware instancing is used. Must be used with either INSTANCED_HW or INSTANCED_SHADER. INSTANCED_HW Position data is obtained from the uv* attributes. The "uv" coords being used depends on how many UV coords already are in use. The number specified correlated to the number of existing UV coords. We currently only support "1" and "2", but more can easily be added. INSTANCED_SHADER Position data is obtained from the worldMatrix3x4Array uniform. Position in the array is obtained from the blendIndices attribute. NORMAL_TEXTURE Normals are not defined per vertex but instead in a normal map, to be used in the fragment shader. This is mainly used for the terrain shaders, since because of LOD we don't want to use the vertex normals. NORMAL_MAP The fragment shader uses a normal map, and thus needs to have some extra values calculated, such as eyeDir in tangent space. SHADOW Shadow textures are used. TODO: * When using skinning, the final normals and tangents aren't correctly propagated to eyeNormal and TBN. Should be fixed. */ #include "dural/base/DualQuaternion_Common.glsl" // Type of fog #if !defined(FOG_NONE) && !defined(FOG_LINEAR) && !defined(FOG_EXP) && !defined(FOG_EXP2) #define FOG_NONE 1 #endif #ifndef FOG_NONE #define FOG_NONE 0 #endif #ifndef FOG_LINEAR #define FOG_LINEAR 0 #endif #ifndef FOG_EXP #define FOG_EXP 0 #endif #ifndef FOG_EXP2 #define FOG_EXP2 0 #endif // Number of lights // 3 is resonable number to support #ifndef NUM_LIGHTS #define NUM_LIGHTS 3 #endif // Either have shadows or not // Supports 3 lights #ifndef SHADOW #define SHADOW 0 #endif // Either use normal mapping or not #ifndef NORMAL_MAP #define NORMAL_MAP 0 #endif // Number of parallax steps - works when normal mapping is on as it needs height map // 3 is ok for small heights (scale < 0,05) // 5-7 for bigger ones #ifndef PARALLAX_STEPS #define PARALLAX_STEPS 0 #endif uniform mat4 viewProjMatrix; uniform vec4 fogColour; uniform vec4 fogParams; // density, linear start, linear end, 1/(end-start) uniform vec4 lightAttenuation[NUM_LIGHTS]; // range, constant, linear, quadric uniform vec4 lightPosition[NUM_LIGHTS]; ///The number of active lights. This can be less than NUM_LIGHTS depending on the scene. uniform float numberOfActiveLights; #if INSTANCED && INSTANCED_SHADER uniform vec4 worldMatrix3x4Array[240]; //240 = 80*3 #elif SKINNING_DQ mat2x4 blendTwoWeights(vec4 blendWgt, vec4 blendIdx, vec4 dualQuaternions[80]); mat2x4 blendTwoWeightsAntipod(vec4 blendWgt, vec4 blendIdx, vec4 dualQuaternions[80]); mat2x4 blendThreeWeightsAntipod(vec4 blendWgt, vec4 blendIdx, vec4 dualQuaternions[80]); mat2x4 blendFourWeightsAntipod(vec4 blendWgt, vec4 blendIdx, vec4 dualQuaternions[80]); vec3 calculateBlendPosition(vec3 position, mat2x4 blendDQ); vec3 calculateBlendNormal(vec3 normal, mat2x4 blendDQ); uniform vec4 worldDualQuaternion2x4Array[80]; #elif SKINNING uniform vec4 worldMatrix3x4Array[240]; // 240/3 = 80 bones #endif //INSTANCED && INSTANCED_SHADER //With instanced methods, the world matrix will be derived from either the uv* attributes, or worldMatrix3x4Array #if !INSTANCED uniform mat4 worldMatrix; #endif //!INSTANCED #if WAVE uniform float time; uniform float frequency; uniform vec4 direction; #endif //WAVE #if SHADOW #define LINEAR_RANGE 0 #if INSTANCED // Shadow view projection matrix uniform mat4 shadowViewProjectionMatrix0; uniform mat4 shadowViewProjectionMatrix1; uniform mat4 shadowViewProjectionMatrix2; uniform mat4 shadowViewProjectionMatrix3; uniform mat4 shadowViewProjectionMatrix4; #else // Shadow world view projection matrix uniform mat4 shadowWorldViewProjectionMatrix0; uniform mat4 shadowWorldViewProjectionMatrix1; uniform mat4 shadowWorldViewProjectionMatrix2; uniform mat4 shadowWorldViewProjectionMatrix3; uniform mat4 shadowWorldViewProjectionMatrix4; #endif //INSTANCED #if LINEAR_RANGE uniform vec4 shadowDepthRange0; uniform vec4 shadowDepthRange1; uniform vec4 shadowDepthRange2; uniform vec4 shadowDepthRange3; uniform vec4 shadowDepthRange4; #endif //LINEAR_RANGE #endif //SHADOW in vec4 vertex; in vec4 blendIndices; in vec4 blendWeights; in vec3 normal; in vec3 tangent; in vec3 binormal; in vec4 uv0; in vec4 uv1; in vec4 uv2; in vec4 uv3; #if INSTANCED_HW > 1 in vec4 uv4; #endif #if !NORMAL_MAP #if !NORMAL_TEXTURE // Fragment position in eye space out vec3 eyePosition; // Normal in eye space out vec3 eyeNormal; #endif // !NORMAL_TEXTURE // Light attenuation, packed to vector out vec3 attenuation; //#define ATTENUATION_I attenuation[i] #else // !NORMAL_MAP // Normal+height map texture //uniform sampler2D normalHeightMap; // Light vector in tangent space (not normalized to keep linearity) // 4-th component contains attenuation value out vec4 lightDir[NUM_LIGHTS]; //#define ATTENUATION_I lightDir[i].w // Eye direction in tangent space out vec3 eyeDir; #endif // NORMAL_MAP #if NORMAL_TEXTURE // Fragment position in object space out vec3 positionObjSpace; #endif // NORMAL_TEXTURE out vec4 oUv0; #if SHADOW // Shadow texture coordinates out vec4 shadowTexCoord0; out vec4 shadowTexCoord1; out vec4 shadowTexCoord2; out vec4 shadowTexCoord3; out vec4 shadowTexCoord4; #endif //SHADOW // Fog factor out float fog; void main() { vec4 position = vertex; #if WAVE if (uv0.y == 0.0) { position += direction * sin(time + vertex.x * frequency); } #endif //WAVE #if INSTANCED mat4 worldMatrix; #if INSTANCED_SHADER int idx = int(blendIndices[0]) * 3; worldMatrix[0] = worldMatrix3x4Array[idx]; worldMatrix[1] = worldMatrix3x4Array[idx + 1]; worldMatrix[2] = worldMatrix3x4Array[idx + 2]; worldMatrix[3] = vec4( 0, 0, 0, 1 ); #elif INSTANCED_HW #if INSTANCED_HW == 1 worldMatrix[0] = uv1; worldMatrix[1] = uv2; worldMatrix[2] = uv3; #elif INSTANCED_HW == 2 worldMatrix[0] = uv2; worldMatrix[1] = uv3; worldMatrix[2] = uv4; #endif worldMatrix[3] = vec4( 0.0, 0.0, 0.0, 1.0 ); #endif //INSTANCED_SHADER //this worldMatrix is transposed compared to non-instanced worldMatrix vec4 positionEyeSpace = position * worldMatrix; mat3 normalMatrix = inverse(mat3(worldMatrix)); #else #if SKINNING_DQ #if SKINNING_DQ == 1 mat2x4 blendDQ = blendWeights.x*mat2x4(worldDualQuaternion2x4Array[int(blendIndices.x) * 2], worldDualQuaternion2x4Array[int(blendIndices.x) * 2 + 1]); #elif SKINNING_DQ == 2 mat2x4 blendDQ = blendTwoWeightsAntipod(blendWeights, blendIndices, worldDualQuaternion2x4Array); #elif SKINNING_DQ == 3 mat2x4 blendDQ = blendThreeWeightsAntipod(blendWeights, blendIndices, worldDualQuaternion2x4Array); #elif SKINNING_DQ == 4 mat2x4 blendDQ = blendFourWeightsAntipod(blendWeights, blendIndices, worldDualQuaternion2x4Array); #endif //SKINNING_DQ == 1 float len = length(blendDQ[0]); blendDQ /= len; vec4 positionEyeSpace = worldMatrix * vec4(calculateBlendPosition(vertex.xyz, blendDQ), 1); //No need to normalize, the magnitude of the normal is preserved because only rotation is performed vec3 blendNormal = calculateBlendNormal(normal, blendDQ); // This isn't used further down as it should be. I.e. eyeNormal and the TBN matrix should be //calculated using the adjusted vertex positions. mat3 normalMatrix = inverse(transpose(mat3(worldMatrix))); //This does not take the skinned position into account. Should be fixed. #else #if SKINNING vec3 blendPos = vec3(0.0, 0.0, 0.0); vec3 blendNorm = vec3(0.0, 0.0, 0.0); for (int bone = 0; bone < SKINNING; ++bone) { float weight = blendWeights[bone]; // perform matrix multiplication manually since no 3x4 matrices // ATI GLSL compiler can't handle indexing an array within an array so calculate the inner index first int idx = int(blendIndices[bone]) * 3; // ATI GLSL compiler can't handle unrolling the loop so do it manually // ATI GLSL has better performance when mat4 is used rather than using individual dot product // There is a bug in ATI mat4 constructor (Cat 7.2) when indexed uniform array elements are used as vec4 parameter so manually assign mat4 boneWorldMatrix; boneWorldMatrix[0] = worldMatrix3x4Array[idx]; boneWorldMatrix[1] = worldMatrix3x4Array[idx + 1]; boneWorldMatrix[2] = worldMatrix3x4Array[idx + 2]; boneWorldMatrix[3] = vec4(0); // now weight this into final blendPos += (vertex * boneWorldMatrix).xyz * weight; mat3 worldRotMatrix = mat3(boneWorldMatrix[0].xyz, boneWorldMatrix[1].xyz, boneWorldMatrix[2].xyz); blendNorm += (normal * worldRotMatrix) * weight; } blendNorm = normalize(blendNorm); // This isn't used further down as it should be. I.e. eyeNormal and the TBN matrix should be //calculated using the adjusted vertex positions. vec4 positionEyeSpace = worldMatrix * vec4(blendPos, 1); mat3 normalMatrix = inverse(transpose(mat3(worldMatrix))); //This does not take the skinned position into account. Should be fixed. #else vec4 positionEyeSpace = worldMatrix * position; mat3 normalMatrix = inverse(transpose(mat3(worldMatrix))); #endif //SKINNING #endif //SKINNING_DQ #endif //INSTANCED gl_Position = viewProjMatrix * positionEyeSpace; oUv0 = uv0; #if !NORMAL_MAP #if !NORMAL_TEXTURE // Pass on position in eye space eyePosition = positionEyeSpace.xyz; // Transform normal to eye space eyeNormal = normalMatrix * normal; #endif // !NORMAL_TEXTURE #else // !NORMAL_MAP vec3 N = normalize(normalMatrix * normal); vec3 T = normalize(normalMatrix * tangent); // Seems that texture space is left-handed vec3 B = cross(T, N); //vec3 B = normalize(normalMatrix * attrBitangent); vec3 eye = -positionEyeSpace.xyz; // Multiply eye direction by TBN matrix to transform it to tangent space // eyeDir = mat3(T, B, N) * eye; eyeDir = normalize(vec3(dot(eye, T), dot(eye, B), dot(eye, N))); #endif // NORMAL_MAP #if NORMAL_TEXTURE // pass on the position in object space positionObjSpace = position.xyz; #endif // NORMAL_TEXTURE #if SHADOW #if INSTANCED // Set coordinates on shadow textures shadowTexCoord0 = shadowViewProjectionMatrix0 * positionEyeSpace; shadowTexCoord1 = shadowViewProjectionMatrix1 * positionEyeSpace; shadowTexCoord2 = shadowViewProjectionMatrix2 * positionEyeSpace; shadowTexCoord3 = shadowViewProjectionMatrix3 * positionEyeSpace; shadowTexCoord4 = shadowViewProjectionMatrix4 * positionEyeSpace; #else // Set coordinates on shadow textures shadowTexCoord0 = shadowWorldViewProjectionMatrix0 * position; shadowTexCoord1 = shadowWorldViewProjectionMatrix1 * position; shadowTexCoord2 = shadowWorldViewProjectionMatrix2 * position; shadowTexCoord3 = shadowWorldViewProjectionMatrix3 * position; shadowTexCoord4 = shadowWorldViewProjectionMatrix4 * position; #endif #if LINEAR_RANGE shadowTexCoord0.z = (shadowTexCoord0.z - shadowDepthRange0.x) * shadowDepthRange0.w; shadowTexCoord1.z = (shadowTexCoord1.z - shadowDepthRange1.x) * shadowDepthRange1.w; shadowTexCoord2.z = (shadowTexCoord2.z - shadowDepthRange2.x) * shadowDepthRange2.w; shadowTexCoord3.z = (shadowTexCoord3.z - shadowDepthRange3.x) * shadowDepthRange3.w; shadowTexCoord4.z = (shadowTexCoord4.z - shadowDepthRange4.x) * shadowDepthRange4.w; #endif // Pass depth for PSSM oUv0.p = gl_Position.z; #endif //We can't use a loop here, since that will break on ATI cards // TODO SK: rewrite without using built-in constants, reduce redundancy #if NUM_LIGHTS > 0 if (numberOfActiveLights > 0.0) { // Compute vector from surface to light position vec3 L = lightPosition[0].xyz - positionEyeSpace.xyz * lightPosition[0].w; float curAttenuation = 1.0; // Attenuation only for non-directional lights if (lightPosition[0].w > 0.0) { // Compute distance between surface and light position float d = length(L); // Compute attenuation per vertex curAttenuation = 1.0 / (lightAttenuation[0].y + lightAttenuation[0].z * d + lightAttenuation[0].w * d * d); } #if !NORMAL_MAP attenuation[0] = curAttenuation; #else // Multiply light direction by TBN matrix to transform it to tangent space // lightDir[i].xyz = mat3(T, B, N) * L; lightDir[0] = vec4(dot(L, T), dot(L, B), dot(L, N), curAttenuation); #endif } #endif #if NUM_LIGHTS > 1 if (numberOfActiveLights > 1.0) { // Compute vector from surface to light position vec3 L = lightPosition[1].xyz - positionEyeSpace.xyz * lightPosition[1].w; float curAttenuation = 1.0; // Attenuation only for non-directional lights if (lightPosition[1].w > 0.0) { // Compute distance between surface and light position float d = length(L); // Compute attenuation per vertex curAttenuation = 1.0 / (lightAttenuation[1].y + lightAttenuation[1].z * d + lightAttenuation[1].w * d * d); } #if !NORMAL_MAP attenuation[1] = curAttenuation; #else // Multiply light direction by TBN matrix to transform it to tangent space // lightDir[i].xyz = mat3(T, B, N) * L; lightDir[1] = vec4(dot(L, T), dot(L, B), dot(L, N), curAttenuation); #endif } #endif #if NUM_LIGHTS > 2 if (numberOfActiveLights > 2.0) { // Compute vector from surface to light position vec3 L = lightPosition[2].xyz - positionEyeSpace.xyz * lightPosition[2].w; float curAttenuation = 1.0; // Attenuation only for non-directional lights if (lightPosition[2].w > 0.0) { // Compute distance between surface and light position float d = length(L); // Compute attenuation per vertex curAttenuation = 1.0 / (lightAttenuation[2].y + lightAttenuation[2].z * d + lightAttenuation[2].w * d * d); } #if !NORMAL_MAP attenuation[2] = curAttenuation; #else // Multiply light direction by TBN matrix to transform it to tangent space // lightDir[i].xyz = mat3(T, B, N) * L; lightDir[2] = vec4(dot(L, T), dot(L, B), dot(L, N), curAttenuation); #endif } #endif #if FOG_NONE fog = 1.0; #else // Calculating fog factor per vertex, no visual difference with per fragment //If the fog colour alpha is null, we consider the fog disabled if (fogColour.a != 0.0) { #if FOG_LINEAR fog = clamp((fogParams.z - gl_Position.z) * fogParams.w, 0.0, 1.0); #endif #if FOG_EXP fog = clamp(exp(- fogParams.x * gl_Position.z), 0.0, 1.0); #endif #if FOG_EXP2 fog = clamp(exp(- fogParams.x * fogParams.x * gl_Position.z * gl_Position.z), 0.0, 1.0); #endif } else { fog = 1.0; } #endif }