Browse Source

Switch forward light calculations back to world space instead of tangent space to avoid light attenuation bugs when TBN matrix is not orthogonal. Shader code becomes simpler and more similar between forward and deferred, and number of vertex shader variations is reduced, but disadvantage is a more complex pixel shader which requires disabling expensive features on SM2.0 to avoid exceeding the arithmetic instruction limit. Fix cube mapped point light displaying the cube map upside down. Closes #276.

Lasse Öörni 11 years ago
parent
commit
c3425334a3
33 changed files with 517 additions and 817 deletions
  1. 1 1
      Bin/CoreData/Shaders/GLSL/DeferredLight.glsl
  2. 37 41
      Bin/CoreData/Shaders/GLSL/LitParticle.glsl
  3. 52 113
      Bin/CoreData/Shaders/GLSL/LitSolid.glsl
  4. 1 1
      Bin/CoreData/Shaders/GLSL/PrepassLight.glsl
  5. 52 62
      Bin/CoreData/Shaders/GLSL/TerrainBlend.glsl
  6. 1 0
      Bin/CoreData/Shaders/GLSL/Uniforms.glsl
  7. 8 13
      Bin/CoreData/Shaders/GLSL/Unlit.glsl
  8. 37 55
      Bin/CoreData/Shaders/GLSL/Vegetation.glsl
  9. 2 9
      Bin/CoreData/Shaders/HLSL/DeferredLight.hlsl
  10. 31 12
      Bin/CoreData/Shaders/HLSL/Lighting.hlsl
  11. 44 44
      Bin/CoreData/Shaders/HLSL/LitParticle.hlsl
  12. 59 126
      Bin/CoreData/Shaders/HLSL/LitSolid.hlsl
  13. 1 1
      Bin/CoreData/Shaders/HLSL/PrepassLight.hlsl
  14. 1 1
      Bin/CoreData/Shaders/HLSL/Samplers.hlsl
  15. 46 78
      Bin/CoreData/Shaders/HLSL/TerrainBlend.hlsl
  16. 1 0
      Bin/CoreData/Shaders/HLSL/Uniforms.hlsl
  17. 7 15
      Bin/CoreData/Shaders/HLSL/Unlit.hlsl
  18. 39 52
      Bin/CoreData/Shaders/HLSL/Vegetation.hlsl
  19. 1 1
      Bin/CoreData/Techniques/DiffNormalEnvCube.xml
  20. 1 1
      Bin/CoreData/Techniques/DiffNormalPackedEnvCube.xml
  21. 6 7
      Bin/Data/Scripts/Editor.as
  22. 11 5
      Docs/Reference.dox
  23. 1 0
      Source/Engine/Graphics/Batch.cpp
  24. 1 0
      Source/Engine/Graphics/GraphicsDefs.cpp
  25. 1 0
      Source/Engine/Graphics/GraphicsDefs.h
  26. 28 0
      Source/Engine/Graphics/Light.cpp
  27. 2 0
      Source/Engine/Graphics/Light.h
  28. 27 56
      Source/Engine/Graphics/Renderer.cpp
  29. 0 12
      Source/Engine/Graphics/Renderer.h
  30. 15 9
      Source/Engine/Graphics/View.cpp
  31. 2 0
      Source/Engine/LuaScript/pkgs/Graphics/Light.pkg
  32. 0 100
      Source/Engine/LuaScript/pkgs/Graphics/Renderer.pkg
  33. 1 2
      Source/Engine/Script/GraphicsAPI.cpp

+ 1 - 1
Bin/CoreData/Shaders/GLSL/DeferredLight.glsl

@@ -80,7 +80,7 @@ void PS()
         lightColor = spotPos.w > 0.0 ? texture2DProj(sLightSpotMap, spotPos).rgb * cLightColor.rgb : vec3(0.0);
     #elif defined(CUBEMASK)
         mat3 lightVecRot = mat3(cLightMatricesPS[0][0].xyz, cLightMatricesPS[0][1].xyz, cLightMatricesPS[0][2].xyz);
-        lightColor = textureCube(sLightCubeMap, lightVecRot * lightVec).rgb * cLightColor.rgb;
+        lightColor = textureCube(sLightCubeMap, lightVecRot * -lightVec).rgb * cLightColor.rgb;
     #else
         lightColor = cLightColor.rgb;
     #endif

+ 37 - 41
Bin/CoreData/Shaders/GLSL/LitParticle.glsl

@@ -5,14 +5,14 @@
 #include "Fog.glsl"
 
 varying vec2 vTexCoord;
-#ifdef HEIGHTFOG
-    varying vec3 vWorldPos;
-#endif
+varying vec4 vWorldPos;
 #ifdef VERTEXCOLOR
     varying vec4 vColor;
 #endif
 #ifdef PERPIXEL
-    varying vec4 vLightVec;
+    #ifdef SHADOW
+        varying vec4 vShadowPos[NUMCASCADES];
+    #endif
     #ifdef SPOTLIGHT
         varying vec4 vSpotPos;
     #endif
@@ -20,7 +20,7 @@ varying vec2 vTexCoord;
         varying vec3 vCubeMaskVec;
     #endif
 #else
-    varying vec4 vVertexLight;
+    varying vec3 vVertexLight;
 #endif
 
 void VS()
@@ -29,10 +29,7 @@ void VS()
     vec3 worldPos = GetWorldPos(modelMatrix);
     gl_Position = GetClipPos(worldPos);
     vTexCoord = GetTexCoord(iTexCoord);
-    
-    #ifdef HEIGHTFOG
-        vWorldPos = worldPos;
-    #endif
+    vWorldPos = vec4(worldPos, GetDepth(gl_Position));
 
     #ifdef VERTEXCOLOR
         vColor = iColor;
@@ -41,28 +38,28 @@ void VS()
     #ifdef PERPIXEL
         // Per-pixel forward lighting
         vec4 projWorldPos = vec4(worldPos, 1.0);
-    
-        #ifdef DIRLIGHT
-            vLightVec = vec4(cLightDir, GetDepth(gl_Position));
-        #else
-            vLightVec = vec4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(gl_Position));
+
+        #ifdef SHADOW
+            // Shadow projection: transform from world space to shadow space
+            for (int i = 0; i < NUMCASCADES; i++)
+                vShadowPos[i] = GetShadowPos(i, projWorldPos);
         #endif
-    
+
         #ifdef SPOTLIGHT
             // Spotlight projection: transform from world space to projector texture coordinates
             vSpotPos = cLightMatrices[0] * projWorldPos;
         #endif
     
         #ifdef POINTLIGHT
-            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * vLightVec.xyz;
+            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (worldPos - cLightPos.xyz);
         #endif
     #else
         // Ambient & per-vertex lighting
-        vVertexLight = vec4(GetAmbient(GetZonePos(worldPos)), GetDepth(gl_Position));
+        vVertexLight = GetAmbient(GetZonePos(worldPos));
 
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                vVertexLight.rgb += GetVertexLightVolumetric(i, worldPos) * cVertexLights[i * 3].rgb;
+                vVertexLight += GetVertexLightVolumetric(i, worldPos) * cVertexLights[i * 3].rgb;
         #endif
     #endif
 }
@@ -85,14 +82,31 @@ void PS()
         diffColor *= vColor;
     #endif
 
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(vWorldPos.w, vWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(vWorldPos.w);
+    #endif
+
     #ifdef PERPIXEL
         // Per-pixel forward lighting
         vec3 lightColor;
+        vec3 lightDir;
         vec3 finalColor;
         float diff;
-    
-        diff = GetDiffuseVolumetric(vLightVec.xyz);
 
+        #ifdef DIRLIGHT
+            diff = GetDiffuseVolumetric(cLightDirPS);
+        #else
+            vec3 lightVec = (cLightPosPS.xyz - vWorldPos.xyz) * cLightPosPS.w;
+            diff = GetDiffuseVolumetric(lightVec);
+        #endif
+
+        #ifdef SHADOW
+            diff *= GetShadow(vShadowPos, vWorldPos.w);
+        #endif
+    
         #if defined(SPOTLIGHT)
             lightColor = vSpotPos.w > 0.0 ? texture2DProj(sLightSpotMap, vSpotPos).rgb * cLightColor.rgb : vec3(0.0, 0.0, 0.0);
         #elif defined(CUBEMASK)
@@ -100,30 +114,12 @@ void PS()
         #else
             lightColor = cLightColor.rgb;
         #endif
-    
-        finalColor = diff * lightColor * diffColor.rgb;
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vLightVec.w, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vLightVec.w);
-        #endif
-        
-        #ifdef AMBIENT
-            finalColor += cAmbientColor * diffColor.rgb;
-            gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
-        #else
-            gl_FragColor = vec4(GetLitFog(finalColor, fogFactor), diffColor.a);
-        #endif
+        finalColor = diff * lightColor * diffColor.rgb;
+        gl_FragColor = vec4(GetLitFog(finalColor, fogFactor), diffColor.a);
     #else
         // Ambient & per-vertex lighting
-        vec3 finalColor = vVertexLight.rgb * diffColor.rgb;
-        
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vVertexLight.a, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vVertexLight.a);
-        #endif
+        vec3 finalColor = vVertexLight * diffColor.rgb;
 
         gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
     #endif

+ 52 - 113
Bin/CoreData/Shaders/GLSL/LitSolid.glsl

@@ -5,18 +5,15 @@
 #include "Lighting.glsl"
 #include "Fog.glsl"
 
-varying vec2 vTexCoord;
-#ifdef HEIGHTFOG
-    varying vec3 vWorldPos;
+#ifdef NORMALMAP
+    varying vec4 vTexCoord;
+    varying vec4 vTangent;
+#else
+    varying vec2 vTexCoord;
 #endif
+varying vec3 vNormal;
+varying vec4 vWorldPos;
 #ifdef PERPIXEL
-    varying vec4 vLightVec;
-    #ifdef SPECULAR
-        varying vec3 vEyeVec;
-    #endif
-    #ifndef NORMALMAP
-        varying vec3 vNormal;
-    #endif
     #ifdef SHADOW
         varying vec4 vShadowPos[NUMCASCADES];
     #endif
@@ -27,12 +24,7 @@ varying vec2 vTexCoord;
         varying vec3 vCubeMaskVec;
     #endif
 #else
-    varying vec4 vVertexLight;
-    varying vec3 vNormal;
-    #ifdef NORMALMAP
-        varying vec3 vTangent;
-        varying vec3 vBitangent;
-    #endif
+    varying vec3 vVertexLight;
     varying vec4 vScreenPos;
     #ifdef ENVCUBEMAP
         varying vec3 vReflectionVec;
@@ -47,24 +39,18 @@ void VS()
     mat4 modelMatrix = iModelMatrix;
     vec3 worldPos = GetWorldPos(modelMatrix);
     gl_Position = GetClipPos(worldPos);
-    vTexCoord = GetTexCoord(iTexCoord);
-    
-    #ifdef HEIGHTFOG
-        vWorldPos = worldPos;
-    #endif
-
-    #if defined(PERPIXEL) && defined(NORMALMAP)
-        vec3 vNormal;
-        vec3 vTangent;
-        vec3 vBitangent;
-    #endif
-
     vNormal = GetWorldNormal(modelMatrix);
+    vWorldPos = vec4(worldPos, GetDepth(gl_Position));
+
     #ifdef NORMALMAP
-        vTangent = GetWorldTangent(modelMatrix);
-        vBitangent = cross(vTangent, vNormal) * iTangent.w;
+        vec3 tangent = GetWorldTangent(modelMatrix);
+        vec3 bitangent = cross(tangent, vNormal) * iTangent.w;
+        vTexCoord = vec4(GetTexCoord(iTexCoord), bitangent.xy);
+        vTangent = vec4(tangent, bitangent.z);
+    #else
+        vTexCoord = GetTexCoord(iTexCoord);
     #endif
-    
+
     #ifdef PERPIXEL
         // Per-pixel forward lighting
         vec4 projWorldPos = vec4(worldPos, 1.0);
@@ -81,43 +67,22 @@ void VS()
         #endif
     
         #ifdef POINTLIGHT
-            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (cLightPos.xyz - worldPos);
-        #endif
-    
-        #ifdef NORMALMAP
-            mat3 tbn = mat3(vTangent, vBitangent, vNormal);
-            #ifdef DIRLIGHT
-                vLightVec = vec4(cLightDir * tbn, GetDepth(gl_Position));
-            #else
-                vLightVec = vec4((cLightPos.xyz - worldPos) * tbn * cLightPos.w, GetDepth(gl_Position));
-            #endif
-            #ifdef SPECULAR
-                vEyeVec = (cCameraPos - worldPos) * tbn;
-            #endif
-        #else
-            #ifdef DIRLIGHT
-                vLightVec = vec4(cLightDir, GetDepth(gl_Position));
-            #else
-                vLightVec = vec4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(gl_Position));
-            #endif
-            #ifdef SPECULAR
-                vEyeVec = cCameraPos - worldPos;
-            #endif
+            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (worldPos - cLightPos.xyz);
         #endif
     #else
         // Ambient & per-vertex lighting
         #if defined(LIGHTMAP) || defined(AO)
             // If using lightmap, disregard zone ambient light
             // If using AO, calculate ambient in the PS
-            vVertexLight = vec4(0.0, 0.0, 0.0, GetDepth(gl_Position));
+            vVertexLight = vec3(0.0, 0.0, 0.0);
             vTexCoord2 = iTexCoord2;
         #else
-            vVertexLight = vec4(GetAmbient(GetZonePos(worldPos)), GetDepth(gl_Position));
+            vVertexLight = GetAmbient(GetZonePos(worldPos));
         #endif
         
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                vVertexLight.rgb += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
+                vVertexLight += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
         #endif
         
         vScreenPos = GetScreenPos(gl_Position);
@@ -132,7 +97,7 @@ void PS()
 {
     // Get material diffuse albedo
     #ifdef DIFFMAP
-        vec4 diffInput = texture2D(sDiffMap, vTexCoord);
+        vec4 diffInput = texture2D(sDiffMap, vTexCoord.xy);
         #ifdef ALPHAMASK
             if (diffInput.a < 0.5)
                 discard;
@@ -144,11 +109,26 @@ void PS()
     
     // Get material specular albedo
     #ifdef SPECMAP
-        vec3 specColor = cMatSpecColor.rgb * texture2D(sSpecMap, vTexCoord).rgb;
+        vec3 specColor = cMatSpecColor.rgb * texture2D(sSpecMap, vTexCoord.xy).rgb;
     #else
         vec3 specColor = cMatSpecColor.rgb;
     #endif
 
+    // Get normal
+    #ifdef NORMALMAP
+        mat3 tbn = mat3(vTangent.xyz, vec3(vTexCoord.zw, vTangent.w), vNormal);
+        vec3 normal = normalize(tbn * DecodeNormal(texture2D(sNormalMap, vTexCoord.xy)));
+    #else
+        vec3 normal = normalize(vNormal);
+    #endif
+
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(vWorldPos.w, vWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(vWorldPos.w);
+    #endif
+
     #if defined(PERPIXEL)
         // Per-pixel forward lighting
         vec3 lightColor;
@@ -156,16 +136,15 @@ void PS()
         vec3 finalColor;
         float diff;
 
-        #ifdef NORMALMAP
-            vec3 normal = DecodeNormal(texture2D(sNormalMap, vTexCoord));
+        #ifdef DIRLIGHT
+            diff = GetDiffuse(normal, cLightDirPS, lightDir);
         #else
-            vec3 normal = normalize(vNormal);
+            vec3 lightVec = (cLightPosPS.xyz - vWorldPos.xyz) * cLightPosPS.w;
+            diff = GetDiffuse(normal, lightVec, lightDir);
         #endif
-    
-        diff = GetDiffuse(normal, vLightVec.xyz, lightDir);
-    
+
         #ifdef SHADOW
-            diff *= GetShadow(vShadowPos, vLightVec.w);
+            diff *= GetShadow(vShadowPos, vWorldPos.w);
         #endif
     
         #if defined(SPOTLIGHT)
@@ -177,18 +156,12 @@ void PS()
         #endif
     
         #ifdef SPECULAR
-            float spec = GetSpecular(normal, vEyeVec, lightDir, cMatSpecColor.a);
+            float spec = GetSpecular(normal, cCameraPosPS - vWorldPos.xyz, lightDir, cMatSpecColor.a);
             finalColor = diff * lightColor * (diffColor.rgb + spec * specColor * cLightColor.a);
         #else
             finalColor = diff * lightColor * diffColor.rgb;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vLightVec.w, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vLightVec.w);
-        #endif
-
         #ifdef AMBIENT
             finalColor += cAmbientColor * diffColor.rgb;
             finalColor += cMatEmissiveColor;
@@ -198,61 +171,40 @@ void PS()
         #endif
     #elif defined(PREPASS)
         // Fill light pre-pass G-Buffer
-        #ifdef NORMALMAP
-            mat3 tbn = mat3(vTangent, vBitangent, vNormal);
-            vec3 normal = tbn * DecodeNormal(texture2D(sNormalMap, vTexCoord.xy));
-        #else
-            vec3 normal = vNormal;
-        #endif
-
         float specPower = cMatSpecColor.a / 255.0;
 
         gl_FragData[0] = vec4(normal * 0.5 + 0.5, specPower);
-        gl_FragData[1] = vec4(EncodeDepth(vVertexLight.a), 0.0);
+        gl_FragData[1] = vec4(EncodeDepth(vWorldPos.w), 0.0);
     #elif defined(DEFERRED)
         // Fill deferred G-buffer
-        #ifdef NORMALMAP
-            mat3 tbn = mat3(vTangent, vBitangent, vNormal);
-            vec3 normal = tbn * DecodeNormal(texture2D(sNormalMap, vTexCoord));
-        #else
-            vec3 normal = vNormal;
-        #endif
-
         float specIntensity = specColor.g;
         float specPower = cMatSpecColor.a / 255.0;
 
-        vec3 finalColor = vVertexLight.rgb * diffColor.rgb;
+        vec3 finalColor = vVertexLight * diffColor.rgb;
         #ifdef AO
             // If using AO, the vertex light ambient is black, calculate occluded ambient here
             finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * cAmbientColor * diffColor.rgb;
         #endif
 
         #ifdef ENVCUBEMAP
-            normal = normalize(normal);
-            finalColor = cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
+            finalColor += cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
         #endif
         #ifdef LIGHTMAP
             finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * diffColor.rgb;
         #endif
         #ifdef EMISSIVEMAP
-            finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord).rgb;
+            finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb;
         #else
             finalColor += cMatEmissiveColor;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vVertexLight.a, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vVertexLight.a);
-        #endif
-
         gl_FragData[0] = vec4(GetFog(finalColor, fogFactor), 1.0);
         gl_FragData[1] = fogFactor * vec4(diffColor.rgb, specIntensity);
         gl_FragData[2] = vec4(normal * 0.5 + 0.5, specPower);
-        gl_FragData[3] = vec4(EncodeDepth(vVertexLight.a), 0.0);
+        gl_FragData[3] = vec4(EncodeDepth(vWorldPos.w), 0.0);
     #else
         // Ambient & per-vertex lighting
-        vec3 finalColor = vVertexLight.rgb * diffColor.rgb;
+        vec3 finalColor = vVertexLight * diffColor.rgb;
         #ifdef AO
             // If using AO, the vertex light ambient is black, calculate occluded ambient here
             finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * cAmbientColor * diffColor.rgb;
@@ -268,30 +220,17 @@ void PS()
         #endif
 
         #ifdef ENVCUBEMAP
-            #ifdef NORMALMAP
-                mat3 tbn = mat3(vTangent, vBitangent, vNormal);
-                vec3 normal = tbn * DecodeNormal(texture2D(sNormalMap, vTexCoord));
-            #else
-                vec3 normal = vNormal;
-            #endif
-            normal = normalize(normal);
             finalColor += cMatEnvMapColor * textureCube(sEnvCubeMap, reflect(vReflectionVec, normal)).rgb;
         #endif
         #ifdef LIGHTMAP
             finalColor += texture2D(sEmissiveMap, vTexCoord2).rgb * diffColor.rgb;
         #endif
         #ifdef EMISSIVEMAP
-            finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord).rgb;
+            finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb;
         #else
             finalColor += cMatEmissiveColor;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vVertexLight.a, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vVertexLight.a);
-        #endif
-
         gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
     #endif
 }

+ 1 - 1
Bin/CoreData/Shaders/GLSL/PrepassLight.glsl

@@ -78,7 +78,7 @@ void PS()
         lightColor = spotPos.w > 0.0 ? texture2DProj(sLightSpotMap, spotPos).rgb * cLightColor.rgb : vec3(0.0);
     #elif defined(CUBEMASK)
         mat3 lightVecRot = mat3(cLightMatricesPS[0][0].xyz, cLightMatricesPS[0][1].xyz, cLightMatricesPS[0][2].xyz);
-        lightColor = textureCube(sLightCubeMap, lightVecRot * lightVec).rgb * cLightColor.rgb;
+        lightColor = textureCube(sLightCubeMap, lightVecRot * -lightVec).rgb * cLightColor.rgb;
     #else
         lightColor = cLightColor.rgb;
     #endif

+ 52 - 62
Bin/CoreData/Shaders/GLSL/TerrainBlend.glsl

@@ -6,15 +6,9 @@
 #include "Fog.glsl"
 
 varying vec2 vTexCoord;
-#ifdef HEIGHTFOG
-    varying vec3 vWorldPos;
-#endif
+varying vec3 vNormal;
+varying vec4 vWorldPos;
 #ifdef PERPIXEL
-    varying vec4 vLightVec;
-    #ifdef SPECULAR
-        varying vec3 vEyeVec;
-    #endif
-    varying vec3 vNormal;
     #ifdef SHADOW
         varying vec4 vShadowPos[NUMCASCADES];
     #endif
@@ -25,9 +19,14 @@ varying vec2 vTexCoord;
         varying vec3 vCubeMaskVec;
     #endif
 #else
-    varying vec4 vVertexLight;
-    varying vec3 vNormal;
+    varying vec3 vVertexLight;
     varying vec4 vScreenPos;
+    #ifdef ENVCUBEMAP
+        varying vec3 vReflectionVec;
+    #endif
+    #if defined(LIGHTMAP) || defined(AO)
+        varying vec2 vTexCoord2;
+    #endif
 #endif
 
 #ifdef COMPILEPS
@@ -43,12 +42,9 @@ void VS()
     mat4 modelMatrix = iModelMatrix;
     vec3 worldPos = GetWorldPos(modelMatrix);
     gl_Position = GetClipPos(worldPos);
-    vTexCoord = GetTexCoord(iTexCoord);
     vNormal = GetWorldNormal(modelMatrix);
-
-    #ifdef HEIGHTFOG
-        vWorldPos = worldPos;
-    #endif
+    vWorldPos = vec4(worldPos, GetDepth(gl_Position));
+    vTexCoord = GetTexCoord(iTexCoord);
 
     #ifdef PERPIXEL
         // Per-pixel forward lighting
@@ -64,29 +60,31 @@ void VS()
             // Spotlight projection: transform from world space to projector texture coordinates
             vSpotPos = cLightMatrices[0] * projWorldPos;
         #endif
-
+    
         #ifdef POINTLIGHT
-            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (cLightPos.xyz - worldPos);
-        #endif
-
-        #ifdef DIRLIGHT
-            vLightVec = vec4(cLightDir, GetDepth(gl_Position));
-        #else
-            vLightVec = vec4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(gl_Position));
-        #endif
-        #ifdef SPECULAR
-            vEyeVec = cCameraPos - worldPos;
+            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (worldPos - cLightPos.xyz);
         #endif
     #else
         // Ambient & per-vertex lighting
-        vVertexLight = vec4(GetAmbient(GetZonePos(worldPos)), GetDepth(gl_Position));
-
+        #if defined(LIGHTMAP) || defined(AO)
+            // If using lightmap, disregard zone ambient light
+            // If using AO, calculate ambient in the PS
+            vVertexLight = vec3(0.0, 0.0, 0.0);
+            vTexCoord2 = iTexCoord2;
+        #else
+            vVertexLight = GetAmbient(GetZonePos(worldPos));
+        #endif
+        
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                vVertexLight.rgb += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
+                vVertexLight += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
         #endif
         
         vScreenPos = GetScreenPos(gl_Position);
+
+        #ifdef ENVCUBEMAP
+            vReflectionVec = worldPos - cCameraPos;
+        #endif
     #endif
 }
 
@@ -103,6 +101,16 @@ void PS()
     // Get material specular albedo
     vec3 specColor = cMatSpecColor.rgb;
 
+    // Get normal
+    vec3 normal = normalize(vNormal);
+
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(vWorldPos.w, vWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(vWorldPos.w);
+    #endif
+
     #if defined(PERPIXEL)
         // Per-pixel forward lighting
         vec3 lightColor;
@@ -110,14 +118,17 @@ void PS()
         vec3 finalColor;
         float diff;
 
-        vec3 normal = normalize(vNormal);
-
-        diff = GetDiffuse(normal, vLightVec.xyz, lightDir);
+        #ifdef DIRLIGHT
+            diff = GetDiffuse(normal, cLightDirPS, lightDir);
+        #else
+            vec3 lightVec = (cLightPosPS.xyz - vWorldPos.xyz) * cLightPosPS.w;
+            diff = GetDiffuse(normal, lightVec, lightDir);
+        #endif
 
         #ifdef SHADOW
-            diff *= GetShadow(vShadowPos, vLightVec.w);
+            diff *= GetShadow(vShadowPos, vWorldPos.w);
         #endif
-
+    
         #if defined(SPOTLIGHT)
             lightColor = vSpotPos.w > 0.0 ? texture2DProj(sLightSpotMap, vSpotPos).rgb * cLightColor.rgb : vec3(0.0, 0.0, 0.0);
         #elif defined(CUBEMASK)
@@ -125,54 +136,39 @@ void PS()
         #else
             lightColor = cLightColor.rgb;
         #endif
-
+    
         #ifdef SPECULAR
-            float spec = GetSpecular(normal, vEyeVec, lightDir, cMatSpecColor.a);
+            float spec = GetSpecular(normal, cCameraPosPS - vWorldPos.xyz, lightDir, cMatSpecColor.a);
             finalColor = diff * lightColor * (diffColor.rgb + spec * specColor * cLightColor.a);
         #else
             finalColor = diff * lightColor * diffColor.rgb;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vLightVec.w, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vLightVec.w);
-        #endif
-
         #ifdef AMBIENT
             finalColor += cAmbientColor * diffColor.rgb;
+            finalColor += cMatEmissiveColor;
             gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
         #else
             gl_FragColor = vec4(GetLitFog(finalColor, fogFactor), diffColor.a);
         #endif
     #elif defined(PREPASS)
         // Fill light pre-pass G-Buffer
-        vec3 normal = vNormal;
-
         float specPower = cMatSpecColor.a / 255.0;
 
         gl_FragData[0] = vec4(normal * 0.5 + 0.5, specPower);
-        gl_FragData[1] = vec4(EncodeDepth(vVertexLight.a), 0.0);
+        gl_FragData[1] = vec4(EncodeDepth(vWorldPos.w), 0.0);
     #elif defined(DEFERRED)
         // Fill deferred G-buffer
-        vec3 normal = vNormal;
-
         float specIntensity = specColor.g;
         float specPower = cMatSpecColor.a / 255.0;
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vVertexLight.a, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vVertexLight.a);
-        #endif
-
-        gl_FragData[0] = vec4(GetFog(vVertexLight.rgb * diffColor.rgb, fogFactor), 1.0);
+        gl_FragData[0] = vec4(GetFog(vVertexLight * diffColor.rgb, fogFactor), 1.0);
         gl_FragData[1] = fogFactor * vec4(diffColor.rgb, specIntensity);
         gl_FragData[2] = vec4(normal * 0.5 + 0.5, specPower);
-        gl_FragData[3] = vec4(EncodeDepth(vVertexLight.a), 0.0);
+        gl_FragData[3] = vec4(EncodeDepth(vWorldPos.w), 0.0);
     #else
         // Ambient & per-vertex lighting
-        vec3 finalColor = vVertexLight.rgb * diffColor.rgb;
+        vec3 finalColor = vVertexLight * diffColor.rgb;
 
         #ifdef MATERIAL
             // Add light pre-pass accumulation result
@@ -183,12 +179,6 @@ void PS()
             finalColor += lightInput.rgb * diffColor.rgb + lightSpecColor * specColor;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(vVertexLight.a, vWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(vVertexLight.a);
-        #endif
-
         gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
     #endif
 }

+ 1 - 0
Bin/CoreData/Shaders/GLSL/Uniforms.glsl

@@ -41,6 +41,7 @@ uniform mat4 cZone;
 #endif
 
 uniform vec3 cAmbientColor;
+uniform vec3 cCameraPosPS;
 uniform float cDeltaTimePS;
 uniform float cElapsedTimePS;
 uniform vec4 cFogParams;

+ 8 - 13
Bin/CoreData/Shaders/GLSL/Unlit.glsl

@@ -5,13 +5,10 @@
 #include "Fog.glsl"
 
 varying vec2 vTexCoord;
+varying vec4 vWorldPos;
 #ifdef VERTEXCOLOR
     varying vec4 vColor;
 #endif
-#ifdef HEIGHTFOG
-    varying vec3 vWorldPos;
-#endif
-varying float vDepth;
 
 void VS()
 {
@@ -19,11 +16,7 @@ void VS()
     vec3 worldPos = GetWorldPos(modelMatrix);
     gl_Position = GetClipPos(worldPos);
     vTexCoord = GetTexCoord(iTexCoord);
-    vDepth = GetDepth(gl_Position);
-
-    #ifdef HEIGHTFOG
-        vWorldPos = worldPos;
-    #endif
+    vWorldPos = vec4(worldPos, GetDepth(gl_Position));
 
     #ifdef VERTEXCOLOR
         vColor = iColor;
@@ -32,6 +25,7 @@ void VS()
 
 void PS()
 {
+    // Get material diffuse albedo
     #ifdef DIFFMAP
         vec4 diffColor = cMatDiffColor * texture2D(sDiffMap, vTexCoord);
         #ifdef ALPHAMASK
@@ -46,21 +40,22 @@ void PS()
         diffColor *= vColor;
     #endif
 
+    // Get fog factor
     #ifdef HEIGHTFOG
-        float fogFactor = GetHeightFogFactor(vDepth, vWorldPos.y);
+        float fogFactor = GetHeightFogFactor(vWorldPos.w, vWorldPos.y);
     #else
-        float fogFactor = GetFogFactor(vDepth);
+        float fogFactor = GetFogFactor(vWorldPos.w);
     #endif
 
     #if defined(PREPASS)
         // Fill light pre-pass G-Buffer
         gl_FragData[0] = vec4(0.5, 0.5, 0.5, 1.0);
-        gl_FragData[1] = vec4(EncodeDepth(vDepth), 0.0);
+        gl_FragData[1] = vec4(EncodeDepth(vWorldPos.w), 0.0);
     #elif defined(DEFERRED)
         gl_FragData[0] = vec4(GetFog(diffColor.rgb, fogFactor), diffColor.a);
         gl_FragData[1] = vec4(0.0, 0.0, 0.0, 0.0);
         gl_FragData[2] = vec4(0.5, 0.5, 0.5, 1.0);
-        gl_FragData[3] = vec4(EncodeDepth(vDepth), 0.0);
+        gl_FragData[3] = vec4(EncodeDepth(vWorldPos.w), 0.0);
     #else
         gl_FragColor = vec4(GetFog(diffColor.rgb, fogFactor), diffColor.a);
     #endif

+ 37 - 55
Bin/CoreData/Shaders/GLSL/Vegetation.glsl

@@ -8,18 +8,15 @@ uniform float cWindHeightPivot;
 uniform float cWindPeriod;
 uniform vec2 cWindWorldSpacing;
 
-varying vec2 vTexCoord;
-#ifdef HEIGHTFOG
-    varying vec3 vWorldPos;
+#ifdef NORMALMAP
+    varying vec4 vTexCoord;
+    varying vec4 vTangent;
+#else
+    varying vec2 vTexCoord;
 #endif
+varying vec3 vNormal;
+varying vec4 vWorldPos;
 #ifdef PERPIXEL
-    varying vec4 vLightVec;
-    #ifdef SPECULAR
-        varying vec3 vEyeVec;
-    #endif
-    #ifndef NORMALMAP
-        varying vec3 vNormal;
-    #endif
     #ifdef SHADOW
         varying vec4 vShadowPos[NUMCASCADES];
     #endif
@@ -30,13 +27,14 @@ varying vec2 vTexCoord;
         varying vec3 vCubeMaskVec;
     #endif
 #else
-    varying vec4 vVertexLight;
-    varying vec3 vNormal;
-    #ifdef NORMALMAP
-        varying vec3 vTangent;
-        varying vec3 vBitangent;
-    #endif
+    varying vec3 vVertexLight;
     varying vec4 vScreenPos;
+    #ifdef ENVCUBEMAP
+        varying vec3 vReflectionVec;
+    #endif
+    #if defined(LIGHTMAP) || defined(AO)
+        varying vec2 vTexCoord2;
+    #endif
 #endif
 
 void VS()
@@ -51,24 +49,18 @@ void VS()
     worldPos.z -= windStrength * cos(windPeriod);
 
     gl_Position = GetClipPos(worldPos);
-    vTexCoord = GetTexCoord(iTexCoord);
-
-    #ifdef HEIGHTFOG
-        vWorldPos = worldPos;
-    #endif
-
-    #if defined(PERPIXEL) && defined(NORMALMAP)
-        vec3 vNormal;
-        vec3 vTangent;
-        vec3 vBitangent;
-    #endif
-
     vNormal = GetWorldNormal(modelMatrix);
+    vWorldPos = vec4(worldPos, GetDepth(gl_Position));
+
     #ifdef NORMALMAP
-        vTangent = GetWorldTangent(modelMatrix);
-        vBitangent = cross(vTangent, vNormal) * iTangent.w;
+        vec3 tangent = GetWorldTangent(modelMatrix);
+        vec3 bitangent = cross(tangent, vNormal) * iTangent.w;
+        vTexCoord = vec4(GetTexCoord(iTexCoord), bitangent.xy);
+        vTangent = vec4(tangent, bitangent.z);
+    #else
+        vTexCoord = GetTexCoord(iTexCoord);
     #endif
-    
+
     #ifdef PERPIXEL
         // Per-pixel forward lighting
         vec4 projWorldPos = vec4(worldPos, 1.0);
@@ -85,38 +77,28 @@ void VS()
         #endif
     
         #ifdef POINTLIGHT
-            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (cLightPos.xyz - worldPos);
-        #endif
-    
-        #ifdef NORMALMAP
-            mat3 tbn = mat3(vTangent, vBitangent, vNormal);
-            #ifdef DIRLIGHT
-                vLightVec = vec4(cLightDir * tbn, GetDepth(gl_Position));
-            #else
-                vLightVec = vec4((cLightPos.xyz - worldPos) * tbn * cLightPos.w, GetDepth(gl_Position));
-            #endif
-            #ifdef SPECULAR
-                vEyeVec = (cCameraPos - worldPos) * tbn;
-            #endif
-        #else
-            #ifdef DIRLIGHT
-                vLightVec = vec4(cLightDir, GetDepth(gl_Position));
-            #else
-                vLightVec = vec4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(gl_Position));
-            #endif
-            #ifdef SPECULAR
-                vEyeVec = cCameraPos - worldPos;
-            #endif
+            vCubeMaskVec = mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz) * (worldPos - cLightPos.xyz);
         #endif
     #else
         // Ambient & per-vertex lighting
-        vVertexLight = vec4(GetAmbient(GetZonePos(worldPos)), GetDepth(gl_Position));
+        #if defined(LIGHTMAP) || defined(AO)
+            // If using lightmap, disregard zone ambient light
+            // If using AO, calculate ambient in the PS
+            vVertexLight = vec3(0.0, 0.0, 0.0);
+            vTexCoord2 = iTexCoord2;
+        #else
+            vVertexLight = GetAmbient(GetZonePos(worldPos));
+        #endif
         
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                vVertexLight.rgb += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
+                vVertexLight += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
         #endif
         
         vScreenPos = GetScreenPos(gl_Position);
+
+        #ifdef ENVCUBEMAP
+            vReflectionVec = worldPos - cCameraPos;
+        #endif
     #endif
 }

+ 2 - 9
Bin/CoreData/Shaders/HLSL/DeferredLight.hlsl

@@ -69,14 +69,7 @@ void PS(
         float4 normalInput = tex2Dproj(sNormalBuffer, iScreenPos);
     #endif
 
-    // With a cubemasked shadowed point light and hardware depth reconstruction, SM2 runs out of instructions,
-    // so skip normalization of normals in that case
-    #if defined(SM3) || defined(HWSHADOW) || !defined(POINTLIGHT) || !defined(SHADOW) || !defined(CUBEMASK)
-        float3 normal = normalize(normalInput.rgb * 2.0 - 1.0);
-    #else
-        float3 normal = normalInput.rgb * 2.0 - 1.0;
-    #endif
-
+    float3 normal = normalize(normalInput.rgb * 2.0 - 1.0);
     float4 projWorldPos = float4(worldPos, 1.0);
     float3 lightColor;
     float3 lightDir;
@@ -97,7 +90,7 @@ void PS(
         float4 spotPos = mul(projWorldPos, cLightMatricesPS[0]);
         lightColor = spotPos.w > 0.0 ? tex2Dproj(sLightSpotMap, spotPos).rgb * cLightColor.rgb : 0.0;
     #elif defined(CUBEMASK)
-        lightColor = texCUBE(sLightCubeMap, mul(lightVec, (float3x3)cLightMatricesPS[0])).rgb * cLightColor.rgb;
+        lightColor = texCUBE(sLightCubeMap, mul(-lightVec, (float3x3)cLightMatricesPS[0])).rgb * cLightColor.rgb;
     #else
         lightColor = cLightColor.rgb;
     #endif

+ 31 - 12
Bin/CoreData/Shaders/HLSL/Lighting.hlsl

@@ -65,7 +65,11 @@ float GetVertexLightVolumetric(int index, float3 worldPos)
 #ifdef SHADOW
 
 #ifdef DIRLIGHT
-    #define NUMCASCADES 4
+    #ifdef SM3
+        #define NUMCASCADES 4
+    #else
+        #define NUMCASCADES 3
+    #endif
 #else
     #define NUMCASCADES 1
 #endif
@@ -77,7 +81,9 @@ void GetShadowPos(float4 projWorldPos, out float4 shadowPos[NUMCASCADES])
         shadowPos[0] = mul(projWorldPos, cLightMatrices[0]);
         shadowPos[1] = mul(projWorldPos, cLightMatrices[1]);
         shadowPos[2] = mul(projWorldPos, cLightMatrices[2]);
-        shadowPos[3] = mul(projWorldPos, cLightMatrices[3]);
+        #ifdef SM3
+            shadowPos[3] = mul(projWorldPos, cLightMatrices[3]);
+        #endif
     #elif defined(SPOTLIGHT)
         shadowPos[0] = mul(projWorldPos, cLightMatrices[1]);
     #else
@@ -130,7 +136,11 @@ float GetIntensity(float3 color)
 #ifdef SHADOW
 
 #ifdef DIRLIGHT
-    #define NUMCASCADES 4
+    #ifdef SM3
+        #define NUMCASCADES 4
+    #else
+        #define NUMCASCADES 3
+    #endif
 #else
     #define NUMCASCADES 1
 #endif
@@ -207,15 +217,24 @@ float GetDirShadow(const float4 iShadowPos[NUMCASCADES], float depth)
 {
     float4 shadowPos;
 
-    if (depth < cShadowSplits.x)
-        shadowPos = iShadowPos[0];
-    else if (depth < cShadowSplits.y)
-        shadowPos = iShadowPos[1];
-    else if (depth < cShadowSplits.z)
-        shadowPos = iShadowPos[2];
-    else
-        shadowPos = iShadowPos[3];
-        
+    #ifdef SM3
+        if (depth < cShadowSplits.x)
+            shadowPos = iShadowPos[0];
+        else if (depth < cShadowSplits.y)
+            shadowPos = iShadowPos[1];
+        else if (depth < cShadowSplits.z)
+            shadowPos = iShadowPos[2];
+        else
+            shadowPos = iShadowPos[3];
+    #else
+        if (depth < cShadowSplits.x)
+            shadowPos = iShadowPos[0];
+        else if (depth < cShadowSplits.y)
+            shadowPos = iShadowPos[1];
+        else
+            shadowPos = iShadowPos[2];
+    #endif
+
     return GetDirShadowFade(GetShadow(shadowPos), depth);
 }
 

+ 44 - 44
Bin/CoreData/Shaders/HLSL/LitParticle.hlsl

@@ -21,19 +21,19 @@ void VS(float4 iPos : POSITION,
         float2 iSize : TEXCOORD1,
     #endif
     out float2 oTexCoord : TEXCOORD0,
-    #ifdef HEIGHTFOG
-        out float3 oWorldPos : TEXCOORD8,
-    #endif
+    out float4 oWorldPos : TEXCOORD3,
     #if PERPIXEL
-        out float4 oLightVec : TEXCOORD1,
+        #ifdef SHADOW
+            out float4 oShadowPos[NUMCASCADES] : TEXCOORD4,
+        #endif
         #ifdef SPOTLIGHT
-            out float4 oSpotPos : TEXCOORD2,
+            out float4 oSpotPos : TEXCOORD5,
         #endif
         #ifdef POINTLIGHT
-            out float3 oCubeMaskVec : TEXCOORD2,
+            out float3 oCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        out float4 oVertexLight : TEXCOORD1,
+        out float3 oVertexLight : TEXCOORD4,
     #endif
     #ifdef VERTEXCOLOR
         out float4 oColor : COLOR0,
@@ -44,10 +44,7 @@ void VS(float4 iPos : POSITION,
     float3 worldPos = GetWorldPos(modelMatrix);
     oPos = GetClipPos(worldPos);
     oTexCoord = GetTexCoord(iTexCoord);
-
-    #ifdef HEIGHTFOG
-        oWorldPos = worldPos;
-    #endif
+    oWorldPos = float4(worldPos, GetDepth(oPos));
 
     #ifdef VERTEXCOLOR
         oColor = iColor;
@@ -55,47 +52,46 @@ void VS(float4 iPos : POSITION,
 
     #ifdef PERPIXEL
         // Per-pixel forward lighting
-        float4 projWorldPos = float4(worldPos, 1.0);
-    
-        #ifdef DIRLIGHT
-            oLightVec = float4(cLightDir, GetDepth(oPos));
-        #else
-            oLightVec = float4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(oPos));
+        float4 projWorldPos = float4(worldPos.xyz, 1.0);
+
+        #ifdef SHADOW
+            // Shadow projection: transform from world space to shadow space
+            GetShadowPos(projWorldPos, oShadowPos);
         #endif
-    
+
         #ifdef SPOTLIGHT
             // Spotlight projection: transform from world space to projector texture coordinates
             oSpotPos = mul(projWorldPos, cLightMatrices[0]);
         #endif
-    
+
         #ifdef POINTLIGHT
-            oCubeMaskVec = mul(oLightVec.xyz, (float3x3)cLightMatrices[0]);
+            oCubeMaskVec = mul(worldPos - cLightPos.xyz, (float3x3)cLightMatrices[0]);
         #endif
     #else
         // Ambient & per-vertex lighting
-        oVertexLight = float4(GetAmbient(GetZonePos(worldPos)), GetDepth(oPos));
+        oVertexLight = GetAmbient(GetZonePos(worldPos));
 
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                oVertexLight.rgb += GetVertexLightVolumetric(i, worldPos) * cVertexLights[i * 3].rgb;
+                oVertexLight += GetVertexLightVolumetric(i, worldPos) * cVertexLights[i * 3].rgb;
         #endif
     #endif
 }
 
 void PS(float2 iTexCoord : TEXCOORD0,
-    #ifdef HEIGHTFOG
-        float3 iWorldPos : TEXCOORD8,
-    #endif
+    float4 iWorldPos : TEXCOORD3,
     #ifdef PERPIXEL
-        float4 iLightVec : TEXCOORD1,
+        #ifdef SHADOW
+            float4 iShadowPos[NUMCASCADES] : TEXCOORD4,
+        #endif
         #ifdef SPOTLIGHT
-            float4 iSpotPos : TEXCOORD2,
+            float4 iSpotPos : TEXCOORD5,
         #endif
         #ifdef CUBEMASK
-            float3 iCubeMaskVec : TEXCOORD2,
+            float3 iCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        float4 iVertexLight : TEXCOORD1,
+        float3 iVertexLight : TEXCOORD4,
     #endif
     #ifdef VERTEXCOLOR
         float4 iColor : COLOR0,
@@ -118,13 +114,29 @@ void PS(float2 iTexCoord : TEXCOORD0,
         diffColor *= iColor;
     #endif
 
-    #if PERPIXEL
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(iWorldPos.w, iWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(iWorldPos.w);
+    #endif
+
+    #ifdef PERPIXEL
         // Per-pixel forward lighting
         float3 lightColor;
         float3 finalColor;
         float diff;
     
-        diff = GetDiffuseVolumetric(iLightVec.xyz);
+        #ifdef DIRLIGHT
+            diff = GetDiffuseVolumetric(cLightDirPS);
+        #else
+            float3 lightVec = (cLightPosPS.xyz - iWorldPos.xyz) * cLightPosPS.w;
+            diff = GetDiffuseVolumetric(lightVec);
+        #endif
+
+        #ifdef SHADOW
+            diff *= GetShadow(iShadowPos, iWorldPos.w);
+        #endif
 
         #if defined(SPOTLIGHT)
             lightColor = iSpotPos.w > 0.0 ? tex2Dproj(sLightSpotMap, iSpotPos).rgb * cLightColor.rgb : 0.0;
@@ -134,23 +146,11 @@ void PS(float2 iTexCoord : TEXCOORD0,
             lightColor = cLightColor.rgb;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iLightVec.w, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iLightVec.w);
-        #endif
-    
         finalColor = diff * lightColor * diffColor.rgb;
         oColor = float4(GetLitFog(finalColor, fogFactor), diffColor.a);
     #else
         // Ambient & per-vertex lighting
-        float3 finalColor = iVertexLight.rgb * diffColor.rgb;
-        
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iVertexLight.a, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iVertexLight.a);
-        #endif
+        float3 finalColor = iVertexLight * diffColor.rgb;
 
         oColor = float4(GetFog(finalColor, fogFactor), diffColor.a);
     #endif

+ 59 - 126
Bin/CoreData/Shaders/HLSL/LitSolid.hlsl

@@ -29,18 +29,15 @@ void VS(float4 iPos : POSITION,
     #ifdef BILLBOARD
         float2 iSize : TEXCOORD1,
     #endif
-    out float2 oTexCoord : TEXCOORD0,
-    #ifdef HEIGHTFOG
-        out float3 oWorldPos : TEXCOORD8,
+    #ifndef NORMALMAP
+        out float2 oTexCoord : TEXCOORD0,
+    #else
+        out float4 oTexCoord : TEXCOORD0,
+        out float4 oTangent : TEXCOORD3,
     #endif
+    out float3 oNormal : TEXCOORD1,
+    out float4 oWorldPos : TEXCOORD2,
     #ifdef PERPIXEL
-        out float4 oLightVec : TEXCOORD1,
-        #ifndef NORMALMAP
-            out float3 oNormal : TEXCOORD2,
-        #endif
-        #ifdef SPECULAR
-            out float3 oEyeVec : TEXCOORD3,
-        #endif
         #ifdef SHADOW
             out float4 oShadowPos[NUMCASCADES] : TEXCOORD4,
         #endif
@@ -51,12 +48,7 @@ void VS(float4 iPos : POSITION,
             out float3 oCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        out float4 oVertexLight : TEXCOORD1,
-        out float3 oNormal : TEXCOORD2,
-        #ifdef NORMALMAP
-            out float3 oTangent : TEXCOORD3,
-            out float3 oBitangent : TEXCOORD4,
-        #endif
+        out float3 oVertexLight : TEXCOORD4,
         out float4 oScreenPos : TEXCOORD5,
         #ifdef ENVCUBEMAP
             out float3 oReflectionVec : TEXCOORD6,
@@ -70,34 +62,22 @@ void VS(float4 iPos : POSITION,
     float4x3 modelMatrix = iModelMatrix;
     float3 worldPos = GetWorldPos(modelMatrix);
     oPos = GetClipPos(worldPos);
-    oTexCoord = GetTexCoord(iTexCoord);
-
-    #ifdef HEIGHTFOG
-        oWorldPos = worldPos;
-    #endif
-
-    #if defined(PERPIXEL) && defined(NORMALMAP)
-        float3 oNormal;
-        float3 oTangent;
-        float3 oBitangent;
-    #endif
-
     oNormal = GetWorldNormal(modelMatrix);
+    oWorldPos = float4(worldPos, GetDepth(oPos));
+
     #ifdef NORMALMAP
-        oTangent = GetWorldTangent(modelMatrix);
-        oBitangent = cross(oTangent, oNormal) * iTangent.w;
+        float3 tangent = GetWorldTangent(modelMatrix);
+        float3 bitangent = cross(tangent, oNormal) * iTangent.w;
+        oTexCoord = float4(GetTexCoord(iTexCoord), bitangent.xy);
+        oTangent = float4(tangent, bitangent.z);
+    #else
+        oTexCoord = GetTexCoord(iTexCoord);
     #endif
 
     #ifdef PERPIXEL
         // Per-pixel forward lighting
-        float4 projWorldPos = float4(worldPos, 1.0);
+        float4 projWorldPos = float4(worldPos.xyz, 1.0);
 
-        #ifdef DIRLIGHT
-            oLightVec = float4(cLightDir, GetDepth(oPos));
-        #else
-            oLightVec = float4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(oPos));
-        #endif
-    
         #ifdef SHADOW
             // Shadow projection: transform from world space to shadow space
             GetShadowPos(projWorldPos, oShadowPos);
@@ -109,32 +89,22 @@ void VS(float4 iPos : POSITION,
         #endif
 
         #ifdef POINTLIGHT
-            oCubeMaskVec = mul(oLightVec.xyz, (float3x3)cLightMatrices[0]);
-        #endif
-
-        #ifdef NORMALMAP
-            float3x3 tbn = float3x3(oTangent, oBitangent, oNormal);
-            oLightVec.xyz = mul(tbn, oLightVec.xyz);
-            #ifdef SPECULAR
-                oEyeVec = mul(tbn, cCameraPos - worldPos);
-            #endif
-        #elif defined(SPECULAR)
-            oEyeVec = cCameraPos - worldPos;
+            oCubeMaskVec = mul(worldPos - cLightPos.xyz, (float3x3)cLightMatrices[0]);
         #endif
     #else
         // Ambient & per-vertex lighting
         #if defined(LIGHTMAP) || defined(AO)
             // If using lightmap, disregard zone ambient light
             // If using AO, calculate ambient in the PS
-            oVertexLight = float4(0.0, 0.0, 0.0, GetDepth(oPos));
+            oVertexLight = float3(0.0, 0.0, 0.0);
             oTexCoord2 = iTexCoord2;
         #else
-            oVertexLight = float4(GetAmbient(GetZonePos(worldPos)), GetDepth(oPos));
+            oVertexLight = GetAmbient(GetZonePos(worldPos));
         #endif
 
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                oVertexLight.rgb += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
+                oVertexLight += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
         #endif
         
         oScreenPos = GetScreenPos(oPos);
@@ -145,18 +115,16 @@ void VS(float4 iPos : POSITION,
     #endif
 }
 
-void PS(float2 iTexCoord : TEXCOORD0,
-    #ifdef HEIGHTFOG
-        float3 iWorldPos : TEXCOORD8,
+void PS(
+    #ifndef NORMALMAP
+        float2 iTexCoord : TEXCOORD0,
+    #else
+        float4 iTexCoord : TEXCOORD0,
+        float4 iTangent : TEXCOORD3,
     #endif
+    float3 iNormal : TEXCOORD1,
+    float4 iWorldPos : TEXCOORD2,
     #ifdef PERPIXEL
-        float4 iLightVec : TEXCOORD1,
-        #ifndef NORMALMAP
-            float3 iNormal : TEXCOORD2,
-        #endif
-        #ifdef SPECULAR
-            float3 iEyeVec : TEXCOORD3,
-        #endif
         #ifdef SHADOW
             float4 iShadowPos[NUMCASCADES] : TEXCOORD4,
         #endif
@@ -167,12 +135,7 @@ void PS(float2 iTexCoord : TEXCOORD0,
             float3 iCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        float4 iVertexLight : TEXCOORD1,
-        float3 iNormal : TEXCOORD2,
-        #ifdef NORMALMAP
-            float3 iTangent : TEXCOORD3,
-            float3 iBitangent : TEXCOORD4,
-        #endif
+        float3 iVertexLight : TEXCOORD4,
         float4 iScreenPos : TEXCOORD5,
         #ifdef ENVCUBEMAP
             float3 iReflectionVec : TEXCOORD6,
@@ -193,7 +156,7 @@ void PS(float2 iTexCoord : TEXCOORD0,
 {
     // Get material diffuse albedo
     #ifdef DIFFMAP
-        float4 diffInput = tex2D(sDiffMap, iTexCoord);
+        float4 diffInput = tex2D(sDiffMap, iTexCoord.xy);
         #ifdef ALPHAMASK
             if (diffInput.a < 0.5)
                 discard;
@@ -205,10 +168,25 @@ void PS(float2 iTexCoord : TEXCOORD0,
     
     // Get material specular albedo
     #ifdef SPECMAP
-        float3 specColor = cMatSpecColor.rgb * tex2D(sSpecMap, iTexCoord).rgb;
+        float3 specColor = cMatSpecColor.rgb * tex2D(sSpecMap, iTexCoord.xy).rgb;
     #else
         float3 specColor = cMatSpecColor.rgb;
     #endif
+    
+    // Get normal
+    #ifdef NORMALMAP
+        float3x3 tbn = float3x3(iTangent.xyz, float3(iTexCoord.zw, iTangent.w), iNormal);
+        float3 normal = normalize(mul(DecodeNormal(tex2D(sNormalMap, iTexCoord.xy)), tbn));
+    #else
+        float3 normal = normalize(iNormal);
+    #endif
+    
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(iWorldPos.w, iWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(iWorldPos.w);
+    #endif
 
     #if defined(PERPIXEL)
         // Per-pixel forward lighting
@@ -217,16 +195,15 @@ void PS(float2 iTexCoord : TEXCOORD0,
         float3 finalColor;
         float diff;
 
-        #ifdef NORMALMAP
-            float3 normal = DecodeNormal(tex2D(sNormalMap, iTexCoord));
+        #ifdef DIRLIGHT
+            diff = GetDiffuse(normal, cLightDirPS, lightDir);
         #else
-            float3 normal = normalize(iNormal);
+            float3 lightVec = (cLightPosPS.xyz - iWorldPos.xyz) * cLightPosPS.w;
+            diff = GetDiffuse(normal, lightVec, lightDir);
         #endif
 
-        diff = GetDiffuse(normal, iLightVec.xyz, lightDir);
-
         #ifdef SHADOW
-            diff *= GetShadow(iShadowPos, iLightVec.w);
+            diff *= GetShadow(iShadowPos, iWorldPos.w);
         #endif
     
         #if defined(SPOTLIGHT)
@@ -238,18 +215,12 @@ void PS(float2 iTexCoord : TEXCOORD0,
         #endif
     
         #ifdef SPECULAR
-            float spec = GetSpecular(normal, iEyeVec, lightDir, cMatSpecColor.a);
+            float spec = GetSpecular(normal, cCameraPosPS - iWorldPos.xyz, lightDir, cMatSpecColor.a);
             finalColor = diff * lightColor * (diffColor.rgb + spec * specColor * cLightColor.a);
         #else
             finalColor = diff * lightColor * diffColor.rgb;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iLightVec.w, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iLightVec.w);
-        #endif
-    
         #ifdef AMBIENT
             finalColor += cAmbientColor * diffColor.rgb;
             finalColor += cMatEmissiveColor;
@@ -259,35 +230,16 @@ void PS(float2 iTexCoord : TEXCOORD0,
         #endif
     #elif defined(PREPASS)
         // Fill light pre-pass G-Buffer
-        #ifdef NORMALMAP
-            float3x3 tbn = float3x3(iTangent, iBitangent, iNormal);
-            float3 normal = mul(DecodeNormal(tex2D(sNormalMap, iTexCoord.xy)), tbn);
-        #else
-            float3 normal = iNormal;
-        #endif
-
         float specPower = cMatSpecColor.a / 255.0;
 
         oColor = float4(normal * 0.5 + 0.5, specPower);
-        oDepth = iVertexLight.a;
+        oDepth = iWorldPos.w;
     #elif defined(DEFERRED)
         // Fill deferred G-buffer
-        #ifdef NORMALMAP
-            float3x3 tbn = float3x3(iTangent, iBitangent, iNormal);
-            float3 normal = mul(DecodeNormal(tex2D(sNormalMap, iTexCoord)), tbn);
-        #else
-            float3 normal = iNormal;
-        #endif
-
-        // If using SM2, light volume shader may not have instructions left to normalize the normal. Therefore do it here
-        #if !defined(SM3) || defined(ENVCUBEMAP)
-            normal = normalize(normal);
-        #endif
-
         float specIntensity = specColor.g;
         float specPower = cMatSpecColor.a / 255.0;
 
-        float3 finalColor = iVertexLight.rgb * diffColor.rgb;
+        float3 finalColor = iVertexLight * diffColor.rgb;
         #ifdef AO
             // If using AO, the vertex light ambient is black, calculate occluded ambient here
             finalColor += tex2D(sEmissiveMap, iTexCoord2).rgb * cAmbientColor * diffColor.rgb;
@@ -299,24 +251,18 @@ void PS(float2 iTexCoord : TEXCOORD0,
             finalColor += tex2D(sEmissiveMap, iTexCoord2).rgb * diffColor.rgb;
         #endif
         #ifdef EMISSIVEMAP
-            finalColor += cMatEmissiveColor * tex2D(sEmissiveMap, iTexCoord).rgb;
+            finalColor += cMatEmissiveColor * tex2D(sEmissiveMap, iTexCoord.xy).rgb;
         #else
             finalColor += cMatEmissiveColor;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iVertexLight.a, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iVertexLight.a);
-        #endif
-
         oColor = float4(GetFog(finalColor, fogFactor), 1.0);
         oAlbedo = fogFactor * float4(diffColor.rgb, specIntensity);
         oNormal = float4(normal * 0.5 + 0.5, specPower);
-        oDepth = iVertexLight.a;
+        oDepth = iWorldPos.w;
     #else
         // Ambient & per-vertex lighting
-        float3 finalColor = iVertexLight.rgb * diffColor.rgb;
+        float3 finalColor = iVertexLight * diffColor.rgb;
         #ifdef AO
             // If using AO, the vertex light ambient is black, calculate occluded ambient here
             finalColor += tex2D(sEmissiveMap, iTexCoord2).rgb * cAmbientColor * diffColor.rgb;
@@ -332,30 +278,17 @@ void PS(float2 iTexCoord : TEXCOORD0,
         #endif
 
         #ifdef ENVCUBEMAP
-            #ifdef NORMALMAP
-                float3x3 tbn = float3x3(iTangent, iBitangent, iNormal);
-                float3 normal = mul(DecodeNormal(tex2D(sNormalMap, iTexCoord)), tbn);
-            #else
-                float3 normal = iNormal;
-            #endif
-            normal = normalize(normal);
             finalColor += cMatEnvMapColor * texCUBE(sEnvCubeMap, reflect(iReflectionVec, normal)).rgb;
         #endif
         #ifdef LIGHTMAP
             finalColor += tex2D(sEmissiveMap, iTexCoord2).rgb * diffColor.rgb;
         #endif
         #ifdef EMISSIVEMAP
-            finalColor += cMatEmissiveColor * tex2D(sEmissiveMap, iTexCoord).rgb;
+            finalColor += cMatEmissiveColor * tex2D(sEmissiveMap, iTexCoord.xy).rgb;
         #else
             finalColor += cMatEmissiveColor;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iVertexLight.a, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iVertexLight.a);
-        #endif
-
         oColor = float4(GetFog(finalColor, fogFactor), diffColor.a);
     #endif
 }

+ 1 - 1
Bin/CoreData/Shaders/HLSL/PrepassLight.hlsl

@@ -89,7 +89,7 @@ void PS(
         float4 spotPos = mul(projWorldPos, cLightMatricesPS[0]);
         lightColor = spotPos.w > 0.0 ? tex2Dproj(sLightSpotMap, spotPos).rgb * cLightColor.rgb : 0.0;
     #elif defined(CUBEMASK)
-        lightColor = texCUBE(sLightCubeMap, mul(lightVec, (float3x3)cLightMatricesPS[0])).rgb * cLightColor.rgb;
+        lightColor = texCUBE(sLightCubeMap, mul(-lightVec, (float3x3)cLightMatricesPS[0])).rgb * cLightColor.rgb;
     #else
         lightColor = cLightColor.rgb;
     #endif

+ 1 - 1
Bin/CoreData/Shaders/HLSL/Samplers.hlsl

@@ -36,7 +36,7 @@ float3 DecodeNormal(float4 normalInput)
         normal.z = sqrt(max(1.0 - dot(normal.xy, normal.xy), 0.0));
         return normal;
     #else
-        return normalize(normalInput.rgb * 2.0 - 1.0);
+        return normalInput.rgb * 2.0 - 1.0;
     #endif
 }
 #endif

+ 46 - 78
Bin/CoreData/Shaders/HLSL/TerrainBlend.hlsl

@@ -26,15 +26,9 @@ void VS(float4 iPos : POSITION,
         float2 iSize : TEXCOORD1,
     #endif
     out float2 oTexCoord : TEXCOORD0,
-    #ifdef HEIGHTFOG
-        out float3 oWorldPos : TEXCOORD8,
-    #endif
-    out float3 oNormal : TEXCOORD2,
+    out float3 oNormal : TEXCOORD1,
+    out float4 oWorldPos : TEXCOORD2,
     #ifdef PERPIXEL
-        out float4 oLightVec : TEXCOORD1,
-        #ifdef SPECULAR
-            out float3 oEyeVec : TEXCOORD3,
-        #endif
         #ifdef SHADOW
             out float4 oShadowPos[NUMCASCADES] : TEXCOORD4,
         #endif
@@ -45,33 +39,29 @@ void VS(float4 iPos : POSITION,
             out float3 oCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        out float4 oVertexLight : TEXCOORD1,
-        out float4 oScreenPos : TEXCOORD3,
-    #endif
-    #ifdef VERTEXCOLOR
-        out float4 oColor : COLOR0,
+        out float3 oVertexLight : TEXCOORD4,
+        out float4 oScreenPos : TEXCOORD5,
     #endif
     out float4 oPos : POSITION)
 {
     float4x3 modelMatrix = iModelMatrix;
     float3 worldPos = GetWorldPos(modelMatrix);
     oPos = GetClipPos(worldPos);
-    oTexCoord = GetTexCoord(iTexCoord);
     oNormal = GetWorldNormal(modelMatrix);
+    oWorldPos = float4(worldPos, GetDepth(oPos));
 
-    #ifdef HEIGHTFOG
-        oWorldPos = worldPos;
+    #if defined(NORMALMAP)
+        float3 tangent = GetWorldTangent(modelMatrix);
+        float3 bitangent = cross(tangent, oNormal) * iTangent.w;
+        oTexCoord = float4(GetTexCoord(iTexCoord), bitangent.xy);
+        oTangent = float4(tangent, bitangent.z);
+    #else
+        oTexCoord = GetTexCoord(iTexCoord);
     #endif
 
     #ifdef PERPIXEL
         // Per-pixel forward lighting
-        float4 projWorldPos = float4(worldPos, 1.0);
-
-        #ifdef DIRLIGHT
-            oLightVec = float4(cLightDir, GetDepth(oPos));
-        #else
-            oLightVec = float4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(oPos));
-        #endif
+        float4 projWorldPos = float4(worldPos.xyz, 1.0);
 
         #ifdef SHADOW
             // Shadow projection: transform from world space to shadow space
@@ -84,35 +74,25 @@ void VS(float4 iPos : POSITION,
         #endif
 
         #ifdef POINTLIGHT
-            oCubeMaskVec = mul(oLightVec.xyz, (float3x3)cLightMatrices[0]);
-        #endif
-
-        #ifdef SPECULAR
-            oEyeVec = cCameraPos - worldPos;
+            oCubeMaskVec = mul(worldPos - cLightPos.xyz, (float3x3)cLightMatrices[0]);
         #endif
     #else
         // Ambient & per-vertex lighting
-        oVertexLight = float4(GetAmbient(GetZonePos(worldPos)), GetDepth(oPos));
+        oVertexLight = GetAmbient(GetZonePos(worldPos));
 
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                oVertexLight.rgb += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
+                oVertexLight += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
         #endif
-
+        
         oScreenPos = GetScreenPos(oPos);
     #endif
 }
 
 void PS(float2 iTexCoord : TEXCOORD0,
-    #ifdef HEIGHTFOG
-        float3 iWorldPos : TEXCOORD8,
-    #endif
+    float3 iNormal : TEXCOORD1,
+    float4 iWorldPos : TEXCOORD2,
     #ifdef PERPIXEL
-        float4 iLightVec : TEXCOORD1,
-        float3 iNormal : TEXCOORD2,
-        #ifdef SPECULAR
-            float3 iEyeVec : TEXCOORD3,
-        #endif
         #ifdef SHADOW
             float4 iShadowPos[NUMCASCADES] : TEXCOORD4,
         #endif
@@ -123,9 +103,8 @@ void PS(float2 iTexCoord : TEXCOORD0,
             float3 iCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        float4 iVertexLight : TEXCOORD1,
-        float3 iNormal : TEXCOORD2,
-        float4 iScreenPos : TEXCOORD3,
+        float3 iVertexLight : TEXCOORD4,
+        float4 iScreenPos : TEXCOORD5,
     #endif
     #ifdef PREPASS
         out float4 oDepth : COLOR1,
@@ -148,6 +127,16 @@ void PS(float2 iTexCoord : TEXCOORD0,
     // Get material specular albedo
     float3 specColor = cMatSpecColor.rgb;
 
+    // Get normal
+    float3 normal = normalize(iNormal);
+
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(iWorldPos.w, iWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(iWorldPos.w);
+    #endif
+
     #if defined(PERPIXEL)
         // Per-pixel forward lighting
         float3 lightDir;
@@ -155,12 +144,15 @@ void PS(float2 iTexCoord : TEXCOORD0,
         float3 finalColor;
         float diff;
 
-        float3 normal = normalize(iNormal);
-        
-        diff = GetDiffuse(normal, iLightVec.xyz, lightDir);
-    
+        #ifdef DIRLIGHT
+            diff = GetDiffuse(normal, cLightDirPS, lightDir);
+        #else
+            float3 lightVec = (cLightPosPS.xyz - iWorldPos.xyz) * cLightPosPS.w;
+            diff = GetDiffuse(normal, lightVec, lightDir);
+        #endif
+
         #ifdef SHADOW
-            diff *= GetShadow(iShadowPos, iLightVec.w);
+            diff *= GetShadow(iShadowPos, iWorldPos.w);
         #endif
     
         #if defined(SPOTLIGHT)
@@ -172,57 +164,39 @@ void PS(float2 iTexCoord : TEXCOORD0,
         #endif
     
         #ifdef SPECULAR
-            float spec = GetSpecular(normal, iEyeVec, lightDir, cMatSpecColor.a);
+            float spec = GetSpecular(normal, cCameraPosPS - iWorldPos.xyz, lightDir, cMatSpecColor.a);
             finalColor = diff * lightColor * (diffColor.rgb + spec * specColor * cLightColor.a);
         #else
             finalColor = diff * lightColor * diffColor.rgb;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iLightVec.w, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iLightVec.w);
-        #endif
-    
         #ifdef AMBIENT
             finalColor += cAmbientColor * diffColor.rgb;
+            finalColor += cMatEmissiveColor;
             oColor = float4(GetFog(finalColor, fogFactor), diffColor.a);
         #else
             oColor = float4(GetLitFog(finalColor, fogFactor), diffColor.a);
         #endif
     #elif defined(PREPASS)
         // Fill light pre-pass G-Buffer
-        float3 normal = iNormal;
-
         float specPower = cMatSpecColor.a / 255.0;
 
         oColor = float4(normal * 0.5 + 0.5, specPower);
-        oDepth = iVertexLight.a;
+        oDepth = iWorldPos.w;
     #elif defined(DEFERRED)
         // Fill deferred G-buffer
-        float3 normal = iNormal;
-
-        // If using SM2, light volume shader may not have instructions left to normalize the normal. Therefore do it here
-        #if !defined(SM3)
-            normal = normalize(normal);
-        #endif
-
         float specIntensity = specColor.g;
         float specPower = cMatSpecColor.a / 255.0;
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iVertexLight.a, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iVertexLight.a);
-        #endif
+        float3 finalColor = iVertexLight * diffColor.rgb;
 
-        oColor = float4(GetFog(iVertexLight.rgb * diffColor.rgb, fogFactor), 1.0);
+        oColor = float4(GetFog(finalColor, fogFactor), 1.0);
         oAlbedo = fogFactor * float4(diffColor.rgb, specIntensity);
         oNormal = float4(normal * 0.5 + 0.5, specPower);
-        oDepth = iVertexLight.a;
+        oDepth = iWorldPos.w;
     #else
         // Ambient & per-vertex lighting
-        float3 finalColor = iVertexLight.rgb * diffColor.rgb;
+        float3 finalColor = iVertexLight * diffColor.rgb;
 
         #ifdef MATERIAL
             // Add light pre-pass accumulation result
@@ -233,12 +207,6 @@ void PS(float2 iTexCoord : TEXCOORD0,
             finalColor += lightInput.rgb * diffColor.rgb + lightSpecColor * specColor;
         #endif
 
-        #ifdef HEIGHTFOG
-            float fogFactor = GetHeightFogFactor(iVertexLight.a, iWorldPos.y);
-        #else
-            float fogFactor = GetFogFactor(iVertexLight.a);
-        #endif
-
         oColor = float4(GetFog(finalColor, fogFactor), diffColor.a);
     #endif
 }

+ 1 - 0
Bin/CoreData/Shaders/HLSL/Uniforms.hlsl

@@ -31,6 +31,7 @@ uniform float4x4 cLightMatrices[4];
 #ifdef COMPILEPS
 // Pixel shader uniforms
 uniform float3 cAmbientColor;
+uniform float3 cCameraPosPS;
 uniform float cDeltaTimePS;
 uniform float cElapsedTimePS;
 uniform float4 cFogParams;

+ 7 - 15
Bin/CoreData/Shaders/HLSL/Unlit.hlsl

@@ -19,10 +19,7 @@ void VS(float4 iPos : POSITION,
         float2 iSize : TEXCOORD1,
     #endif
     out float2 oTexCoord : TEXCOORD0,
-    out float oDepth : TEXCOORD1,
-    #ifdef HEIGHTFOG
-        out float3 oWorldPos : TEXCOORD8,
-    #endif
+    out float4 oWorldPos : TEXCOORD2,
     #ifdef VERTEXCOLOR
         out float4 oColor : COLOR0,
     #endif
@@ -32,11 +29,7 @@ void VS(float4 iPos : POSITION,
     float3 worldPos = GetWorldPos(modelMatrix);
     oPos = GetClipPos(worldPos);
     oTexCoord = GetTexCoord(iTexCoord);
-    oDepth = GetDepth(oPos);
-
-    #ifdef HEIGHTFOG
-        oWorldPos = worldPos;
-    #endif
+    oWorldPos = float4(worldPos, GetDepth(oPos));
 
     #ifdef VERTEXCOLOR
         oColor = iColor;
@@ -44,10 +37,7 @@ void VS(float4 iPos : POSITION,
 }
 
 void PS(float2 iTexCoord : TEXCOORD0,
-    float iDepth : TEXCOORD1,
-    #ifdef HEIGHTFOG
-        float3 iWorldPos : TEXCOORD8,
-    #endif
+    float4 iWorldPos: TEXCOORD2,
     #ifdef VERTEXCOLOR
         float4 iColor : COLOR0,
     #endif
@@ -61,6 +51,7 @@ void PS(float2 iTexCoord : TEXCOORD0,
     #endif
     out float4 oColor : COLOR0)
 {
+    // Get material diffuse albedo
     #ifdef DIFFMAP
         float4 diffColor = cMatDiffColor * tex2D(sDiffMap, iTexCoord);
         #ifdef ALPHAMASK
@@ -75,10 +66,11 @@ void PS(float2 iTexCoord : TEXCOORD0,
         diffColor *= iColor;
     #endif
 
+    // Get fog factor
     #ifdef HEIGHTFOG
-        float fogFactor = GetHeightFogFactor(iDepth, iWorldPos.y);
+        float fogFactor = GetHeightFogFactor(iWorldPos.w, iWorldPos.y);
     #else
-        float fogFactor = GetFogFactor(iDepth);
+        float fogFactor = GetFogFactor(iWorldPos.w);
     #endif
 
     #if defined(PREPASS)

+ 39 - 52
Bin/CoreData/Shaders/HLSL/Vegetation.hlsl

@@ -13,7 +13,7 @@ uniform float2 cWindWorldSpacing;
 void VS(float4 iPos : POSITION,
     float3 iNormal : NORMAL,
     float2 iTexCoord : TEXCOORD0,
-    #ifdef LIGHTMAP
+    #if defined(LIGHTMAP) || defined(AO)
         float2 iTexCoord2 : TEXCOORD1,
     #endif
     #ifdef NORMALMAP
@@ -29,15 +29,15 @@ void VS(float4 iPos : POSITION,
     #ifdef BILLBOARD
         float2 iSize : TEXCOORD1,
     #endif
-    out float2 oTexCoord : TEXCOORD0,
+    #ifndef NORMALMAP
+        out float2 oTexCoord : TEXCOORD0,
+    #else
+        out float4 oTexCoord : TEXCOORD0,
+        out float4 oTangent : TEXCOORD3,
+    #endif
+    out float3 oNormal : TEXCOORD1,
+    out float4 oWorldPos : TEXCOORD2,
     #ifdef PERPIXEL
-        out float4 oLightVec : TEXCOORD1,
-        #ifndef NORMALMAP
-            out float3 oNormal : TEXCOORD2,
-        #endif
-        #ifdef SPECULAR
-            out float3 oEyeVec : TEXCOORD3,
-        #endif
         #ifdef SHADOW
             out float4 oShadowPos[NUMCASCADES] : TEXCOORD4,
         #endif
@@ -48,16 +48,14 @@ void VS(float4 iPos : POSITION,
             out float3 oCubeMaskVec : TEXCOORD5,
         #endif
     #else
-        out float4 oVertexLight : TEXCOORD1,
-        out float3 oNormal : TEXCOORD2,
-        #ifdef NORMALMAP
-            out float3 oTangent : TEXCOORD3,
-            out float3 oBitangent : TEXCOORD4,
-        #endif
+        out float3 oVertexLight : TEXCOORD4,
         out float4 oScreenPos : TEXCOORD5,
-    #endif
-    #ifdef HEIGHTFOG
-        out float3 oWorldPos : TEXCOORD8,
+        #ifdef ENVCUBEMAP
+            out float3 oReflectionVec : TEXCOORD6,
+        #endif
+        #if defined(LIGHTMAP) || defined(AO)
+            out float2 oTexCoord2 : TEXCOORD7,
+        #endif
     #endif
     out float4 oPos : POSITION)
 {
@@ -71,34 +69,22 @@ void VS(float4 iPos : POSITION,
     worldPos.z -= windStrength * cos(windPeriod);
 
     oPos = GetClipPos(worldPos);
-    oTexCoord = GetTexCoord(iTexCoord);
-
-    #ifdef HEIGHTFOG
-        oWorldPos = worldPos;
-    #endif
-
-    #if defined(PERPIXEL) && defined(NORMALMAP)
-        float3 oNormal;
-        float3 oTangent;
-        float3 oBitangent;
-    #endif
-
     oNormal = GetWorldNormal(modelMatrix);
+    oWorldPos = float4(worldPos, GetDepth(oPos));
+
     #ifdef NORMALMAP
-        oTangent = GetWorldTangent(modelMatrix);
-        oBitangent = cross(oTangent, oNormal) * iTangent.w;
+        float3 tangent = GetWorldTangent(modelMatrix);
+        float3 bitangent = cross(tangent, oNormal) * iTangent.w;
+        oTexCoord = float4(GetTexCoord(iTexCoord), bitangent.xy);
+        oTangent = float4(tangent, bitangent.z);
+    #else
+        oTexCoord = GetTexCoord(iTexCoord);
     #endif
 
     #ifdef PERPIXEL
         // Per-pixel forward lighting
-        float4 projWorldPos = float4(worldPos, 1.0);
+        float4 projWorldPos = float4(worldPos.xyz, 1.0);
 
-        #ifdef DIRLIGHT
-            oLightVec = float4(cLightDir, GetDepth(oPos));
-        #else
-            oLightVec = float4((cLightPos.xyz - worldPos) * cLightPos.w, GetDepth(oPos));
-        #endif
-    
         #ifdef SHADOW
             // Shadow projection: transform from world space to shadow space
             GetShadowPos(projWorldPos, oShadowPos);
@@ -110,27 +96,28 @@ void VS(float4 iPos : POSITION,
         #endif
 
         #ifdef POINTLIGHT
-            oCubeMaskVec = mul(oLightVec.xyz, (float3x3)cLightMatrices[0]);
-        #endif
-
-        #ifdef NORMALMAP
-            float3x3 tbn = float3x3(oTangent, oBitangent, oNormal);
-            oLightVec.xyz = mul(tbn, oLightVec.xyz);
-            #ifdef SPECULAR
-                oEyeVec = mul(tbn, cCameraPos - worldPos);
-            #endif
-        #elif defined(SPECULAR)
-            oEyeVec = cCameraPos - worldPos;
+            oCubeMaskVec = mul(cLightPos.xyz - worldPos, (float3x3)cLightMatrices[0]);
         #endif
     #else
         // Ambient & per-vertex lighting
-        oVertexLight = float4(GetAmbient(GetZonePos(worldPos)), GetDepth(oPos));
+        #if defined(LIGHTMAP) || defined(AO)
+            // If using lightmap, disregard zone ambient light
+            // If using AO, calculate ambient in the PS
+            oVertexLight = float3(0.0, 0.0, 0.0);
+            oTexCoord2 = iTexCoord2;
+        #else
+            oVertexLight = GetAmbient(GetZonePos(worldPos));
+        #endif
 
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
-                oVertexLight.rgb += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
+                oVertexLight += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
         #endif
         
         oScreenPos = GetScreenPos(oPos);
+
+        #ifdef ENVCUBEMAP
+            oReflectionVec = worldPos - cCameraPos;
+        #endif
     #endif
 }

+ 1 - 1
Bin/CoreData/Techniques/DiffNormalEnvCube.xml

@@ -2,7 +2,7 @@
     <pass name="base" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="NORMALMAP ENVCUBEMAP" />
     <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP" />
-    <pass name="material" vsdefines="ENVCUBEMAP" psdefines="MATERIAL ENVCUBEMAP" depthtest="equal" depthwrite="false" />
+    <pass name="material" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="MATERIAL NORMALMAP ENVCUBEMAP" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="DEFERRED NORMALMAP ENVCUBEMAP" />
     <pass name="depth" vs="Depth" ps="Depth" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />

+ 1 - 1
Bin/CoreData/Techniques/DiffNormalPackedEnvCube.xml

@@ -2,7 +2,7 @@
     <pass name="base" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="NORMALMAP PACKEDNORMAL ENVCUBEMAP" />
     <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP PACKEDNORMAL" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP PACKEDNORMAL" />
-    <pass name="material" vsdefines="ENVCUBEMAP" psdefines="MATERIAL ENVCUBEMAP" depthtest="equal" depthwrite="false" />
+    <pass name="material" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="MATERIAL NORMALMAP PACKEDNORMAL ENVCUBEMAP" depthtest="equal" depthwrite="false" />
     <pass name="deferred" vsdefines="NORMALMAP ENVCUBEMAP" psdefines="DEFERRED NORMALMAP PACKEDNORMAL ENVCUBEMAP" />
     <pass name="depth" vs="Depth" ps="Depth" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />

+ 6 - 7
Bin/Data/Scripts/Editor.as

@@ -17,8 +17,8 @@
 String configPath;
 String configFileName;
 
-// If loaded in OpenGL mode, remember the instancing setting in config instead of auto-disabling it
 bool instancingSetting = true;
+int shadowQualitySetting = 2;
 
 void Start()
 {
@@ -178,7 +178,7 @@ void LoadConfig()
         if (renderingElem.HasAttribute("texturequality")) renderer.textureQuality = renderingElem.GetInt("texturequality");
         if (renderingElem.HasAttribute("materialquality")) renderer.materialQuality = renderingElem.GetInt("materialquality");
         if (renderingElem.HasAttribute("shadowresolution")) SetShadowResolution(renderingElem.GetInt("shadowresolution"));
-        if (renderingElem.HasAttribute("shadowquality")) renderer.shadowQuality = renderingElem.GetInt("shadowquality");
+        if (renderingElem.HasAttribute("shadowquality")) renderer.shadowQuality = shadowQualitySetting = renderingElem.GetInt("shadowquality");
         if (renderingElem.HasAttribute("maxoccludertriangles")) renderer.maxOccluderTriangles = renderingElem.GetInt("maxoccludertriangles");
         if (renderingElem.HasAttribute("specularlighting")) renderer.specularLighting = renderingElem.GetBool("specularlighting");
         if (renderingElem.HasAttribute("dynamicinstancing")) renderer.dynamicInstancing = instancingSetting = renderingElem.GetBool("dynamicinstancing");
@@ -264,18 +264,17 @@ void SaveConfig()
     resourcesElem.SetAttribute("importpath", uiImportPath);
     resourcesElem.SetAttribute("recentscenes", Join(uiRecentScenes, ";"));
 
-    if (renderer !is null)
+    if (renderer !is null && graphics !is null)
     {
         renderingElem.SetInt("texturequality", renderer.textureQuality);
         renderingElem.SetInt("materialquality", renderer.materialQuality);
         renderingElem.SetInt("shadowresolution", GetShadowResolution());
-        renderingElem.SetInt("shadowquality", renderer.shadowQuality);
         renderingElem.SetInt("maxoccludertriangles", renderer.maxOccluderTriangles);
         renderingElem.SetBool("specularlighting", renderer.specularLighting);
-    }
-
-    if (graphics !is null)
+        // If Shader Model 3 is not supported, save the remembered instancing & quality settings instead of reduced settings
+        renderingElem.SetInt("shadowquality", graphics.sm3Support ? renderer.shadowQuality : shadowQualitySetting);
         renderingElem.SetBool("dynamicinstancing", graphics.sm3Support ? renderer.dynamicInstancing : instancingSetting);
+    }
 
     renderingElem.SetBool("framelimiter", engine.maxFps > 0);
 

+ 11 - 5
Docs/Reference.dox

@@ -686,12 +686,18 @@ Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling
 
 When setting the initial screen mode, Graphics does a few checks:
 
-- For Direct3D9, the supported shader model is checked. 2 is minimum, but 3 will be used if available. SM2 can be forced by calling \ref Graphics::SetForceSM2 "SetForceSM2()" before setting the initial screen mode.
+- For Direct3D9, the supported shader model is checked. 2 is minimum, but 3 will be used if available. For testing, SM2 can be forced by calling \ref Graphics::SetForceSM2 "SetForceSM2()" before setting the initial screen mode.
 - For OpenGL, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for.
 - Is hardware instancing supported? This requires %Shader %Model 3 on Direct3D9 and the ARB_instanced_arrays extension on OpenGL.
-- Are hardware shadow maps supported? Both ATI & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
+- Are hardware shadow maps supported? Both AMD & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
 - Are light pre-pass and deferred rendering modes supported? These require sufficient multiple rendertarget support, and R32F texture format support.
 
+%Shader model 2 has the following limitations due to limited pixel shader instruction count:
+
+- Directional light shadows support a maximum of 3 cascade splits instead of 4.
+- High quality (4 samples) shadow mapping is not supported.
+- Height fog is not supported.
+
 \section Rendering_Renderer Renderer
 
 Renderer implements the actual rendering of 3D views each frame, and controls global settings such as texture quality, material quality, specular lighting and shadow map base resolution.
@@ -723,7 +729,7 @@ In the default render paths, the rendering operations proceed in the following o
 
 \section Rendering_Drawable Rendering components
 
-The rendering-related components defined by the %Graphics, %UI and Urho2D libraries are:
+The rendering-related components defined by the %Graphics and %UI libraries are:
 
 - Octree: spatial partitioning of Drawables for accelerated visibility queries. Needs to be created to the Scene (root node.)
 - Camera: describes a viewpoint for rendering, including projection parameters (FOV, near/far distance, perspective/orthographic)
@@ -742,6 +748,8 @@ The rendering-related components defined by the %Graphics, %UI and Urho2D librar
 - Zone: defines ambient light and fog settings for objects inside the zone volume.
 - Text3D: text that is rendered into the 3D view.
 
+Additionally there are 2D drawable components defined by the \ref Urho2D "Urho2D" library.
+
 \section Rendering_Optimizations Optimizations
 
 The following techniques will be used to reduce the amount of CPU and GPU work when rendering. By default they are all on:
@@ -990,10 +998,8 @@ Vertex shader:
 
 - NUMVERTEXLIGHTS=1,2,3 or 4: number of vertex lights influencing the object
 - DIRLIGHT, SPOTLIGHT, POINTLIGHT: a per-pixel forward light is being used. Accompanied by the define PERPIXEL
-- SPEC: the per-pixel forward light has specular calculations
 - SHADOW: the per-pixel forward light has shadowing
 - SKINNED, INSTANCED, BILLBOARD: choosing the geometry type
-- HEIGHTFOG: object's zone has height fog mode
 
 Pixel shader:
 

+ 1 - 0
Source/Engine/Graphics/Batch.cpp

@@ -234,6 +234,7 @@ void Batch::Prepare(View* view, bool setModelTransform) const
         
         graphics->SetShaderParameter(VSP_CAMERAPOS, cameraEffectiveTransform.Translation());
         graphics->SetShaderParameter(VSP_CAMERAROT, cameraEffectiveTransform.RotationMatrix());
+        graphics->SetShaderParameter(PSP_CAMERAPOS, cameraEffectiveTransform.Translation());
         
         float nearClip = camera_->GetNearClip();
         float farClip = camera_->GetFarClip();

+ 1 - 0
Source/Engine/Graphics/GraphicsDefs.cpp

@@ -52,6 +52,7 @@ StringHash VSP_LIGHTMATRICES("LightMatrices");
 StringHash VSP_SKINMATRICES("SkinMatrices");
 StringHash VSP_VERTEXLIGHTS("VertexLights");
 StringHash PSP_AMBIENTCOLOR("AmbientColor");
+StringHash PSP_CAMERAPOS("CameraPosPS");
 StringHash PSP_DELTATIME("DeltaTimePS");
 StringHash PSP_ELAPSEDTIME("ElapsedTimePS");
 StringHash PSP_FOGCOLOR("FogColor");

+ 1 - 0
Source/Engine/Graphics/GraphicsDefs.h

@@ -221,6 +221,7 @@ extern StringHash VSP_LIGHTMATRICES;
 extern StringHash VSP_SKINMATRICES;
 extern StringHash VSP_VERTEXLIGHTS;
 extern StringHash PSP_AMBIENTCOLOR;
+extern StringHash PSP_CAMERAPOS;
 extern StringHash PSP_DELTATIME;
 extern StringHash PSP_ELAPSEDTIME;
 extern StringHash PSP_FOGCOLOR;

+ 28 - 0
Source/Engine/Graphics/Light.cpp

@@ -24,6 +24,7 @@
 #include "Camera.h"
 #include "Context.h"
 #include "DebugRenderer.h"
+#include "Graphics.h"
 #include "Light.h"
 #include "Node.h"
 #include "OctreeQuery.h"
@@ -386,6 +387,33 @@ Frustum Light::GetFrustum() const
     return ret;
 }
 
+int Light::GetNumShadowSplits() const
+{
+    int ret = 1;
+    
+    if (shadowCascade_.splits_[1] > shadowCascade_.splits_[0])
+    {
+        ++ret;
+        if (shadowCascade_.splits_[2] > shadowCascade_.splits_[1])
+        {
+            ++ret;
+            if (shadowCascade_.splits_[3] > shadowCascade_.splits_[2])
+                ++ret;
+        }
+    }
+    
+    ret = Min(ret, MAX_CASCADE_SPLITS);
+    // Shader Model 2 can only support 3 splits max. due to pixel shader instruction count limits
+    if (ret == 4)
+    {
+        Graphics* graphics = GetSubsystem<Graphics>();
+        if (graphics && !graphics->GetSM3Support())
+            --ret;
+    }
+    
+    return ret;
+}
+
 Matrix3x4 Light::GetDirLightTransform(Camera* camera, bool getNearQuad)
 {
     if (!camera)

+ 2 - 0
Source/Engine/Graphics/Light.h

@@ -250,6 +250,8 @@ public:
     Texture* GetShapeTexture() const { return shapeTexture_; }
     /// Return spotlight frustum.
     Frustum GetFrustum() const;
+    /// Return number of shadow map cascade splits for a directional light, considering also graphics API limitations.
+    int GetNumShadowSplits() const;
     /// Return whether light has negative (darkening) color.
     bool IsNegative() const { return GetEffectiveColor().SumRGB() < 0.0f; }
     

+ 27 - 56
Source/Engine/Graphics/Renderer.cpp

@@ -200,15 +200,9 @@ static const char* lightVSVariations[] =
     "PERPIXEL DIRLIGHT ",
     "PERPIXEL SPOTLIGHT ",
     "PERPIXEL POINTLIGHT ",
-    "PERPIXEL DIRLIGHT SPECULAR ",
-    "PERPIXEL SPOTLIGHT SPECULAR ",
-    "PERPIXEL POINTLIGHT SPECULAR ",
     "PERPIXEL DIRLIGHT SHADOW ",
     "PERPIXEL SPOTLIGHT SHADOW ",
     "PERPIXEL POINTLIGHT SHADOW ",
-    "PERPIXEL DIRLIGHT SPECULAR SHADOW ",
-    "PERPIXEL SPOTLIGHT SPECULAR SHADOW ",
-    "PERPIXEL POINTLIGHT SPECULAR SHADOW "
 };
 
 static const char* vertexLightVSVariations[] =
@@ -268,7 +262,6 @@ Renderer::Renderer(Context* context) :
     shadowMapSize_(1024),
     shadowQuality_(SHADOWQUALITY_HIGH_16BIT),
     maxShadowMaps_(1),
-    maxShadowCascades_(MAX_CASCADE_SPLITS),
     minInstances_(2),
     maxInstanceTriangles_(500),
     maxSortedInstances_(1000),
@@ -404,6 +397,9 @@ void Renderer::SetShadowQuality(int quality)
         quality |= SHADOWQUALITY_HIGH_16BIT;
     if (!graphics_->GetHiresShadowMapFormat())
         quality &= SHADOWQUALITY_HIGH_16BIT;
+    // Shader Model 2 can not reliably support four samples without exceeding pixel shader instruction count limit
+    if (!graphics_->GetSM3Support())
+        quality &= ~SHADOWQUALITY_HIGH_16BIT;
     
     if (quality != shadowQuality_)
     {
@@ -434,17 +430,6 @@ void Renderer::SetMaxShadowMaps(int shadowMaps)
     }
 }
 
-void Renderer::SetMaxShadowCascades(int cascades)
-{
-    cascades = Clamp(cascades, 1, MAX_CASCADE_SPLITS);
-    
-    if (cascades != maxShadowCascades_)
-    {
-        maxShadowCascades_ = cascades;
-        ResetShadowMaps();
-    }
-}
-
 void Renderer::SetDynamicInstancing(bool enable)
 {
     if (!instancingBuffer_)
@@ -839,9 +824,10 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     // Adjust the size for directional or point light shadow map atlases
     if (type == LIGHT_DIRECTIONAL)
     {
-        if (maxShadowCascades_ > 1)
+        unsigned numSplits = light->GetNumShadowSplits();
+        if (numSplits > 1)
             width *= 2;
-        if (maxShadowCascades_ > 2)
+        if (numSplits > 2)
             height *= 2;
     }
     else if (type == LIGHT_POINT)
@@ -1062,7 +1048,8 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     // Make sure shaders are loaded now
     if (vertexShaders.Size() && pixelShaders.Size())
     {
-        bool heightFog = graphics_->GetSM3Support() && batch.zone_ && batch.zone_->GetHeightFog();
+        // Height fog is not supported on Shader Model 2 due to possibly exceeding pixel shader instruction limit
+        bool heightFog = batch.zone_ && batch.zone_->GetHeightFog() && graphics_->GetSM3Support();
         
         // If instancing is not supported, but was requested, or the object is too large to be instanced,
         // choose static geometry vertex shader instead
@@ -1092,10 +1079,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
             
             bool materialHasSpecular = batch.material_ ? batch.material_->GetSpecular() : true;
             if (specularLighting_ && light->GetSpecularIntensity() > 0.0f && materialHasSpecular)
-            {
-                vsi += LVS_SPEC;
                 psi += LPS_SPEC;
-            }
             if (allowShadows && lightQueue->shadowMap_)
             {
                 vsi += LVS_SHADOW;
@@ -1123,10 +1107,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
             }
             
             if (heightFog)
-            {
-                vsi += MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS;
                 psi += MAX_LIGHT_PS_VARIATIONS;
-            }
             
             batch.vertexShader_ = vertexShaders[vsi];
             batch.pixelShader_ = pixelShaders[psi];
@@ -1141,17 +1122,11 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
                     numVertexLights = batch.lightQueue_->vertexLights_.Size();
                 
                 unsigned vsi = batch.geometryType_ * MAX_VERTEXLIGHT_VS_VARIATIONS + numVertexLights;
-                if (heightFog)
-                    vsi += MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS;
-                
                 batch.vertexShader_ = vertexShaders[vsi];
             }
             else
             {
                 unsigned vsi = batch.geometryType_;
-                if (heightFog)
-                    vsi += MAX_GEOMETRYTYPES;
-                
                 batch.vertexShader_ = vertexShaders[vsi];
             }
             
@@ -1430,6 +1405,8 @@ void Renderer::Initialize()
     
     if (!graphics_->GetShadowMapFormat())
         drawShadows_ = false;
+    // Validate the shadow quality level
+    SetShadowQuality(shadowQuality_);
     
     defaultLightRamp_ = cache->GetResource<Texture2D>("Textures/Ramp.png");
     defaultLightSpot_ = cache->GetResource<Texture2D>("Textures/Spot.png");
@@ -1503,31 +1480,29 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
     if (pass->GetLightingMode() == LIGHTING_PERPIXEL)
     {
         // Load forward pixel lit variations
-        vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS * 2);
+        vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
         pixelShaders.Resize(MAX_LIGHT_PS_VARIATIONS * 2);
         
-        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS * 2; ++j)
+        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS; ++j)
         {
-            unsigned k = j % (MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
-            unsigned h = j / (MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
-            unsigned g = k / MAX_LIGHT_VS_VARIATIONS;
-            unsigned l = k % MAX_LIGHT_VS_VARIATIONS;
+            unsigned g = j / MAX_LIGHT_VS_VARIATIONS;
+            unsigned l = j % MAX_LIGHT_VS_VARIATIONS;
             
             vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
-                lightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]);
+                lightVSVariations[l] + geometryVSVariations[g]);
         }
         for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS * 2; ++j)
         {
-            unsigned k = j % MAX_LIGHT_PS_VARIATIONS;
+            unsigned l = j % MAX_LIGHT_PS_VARIATIONS;
             unsigned h = j / MAX_LIGHT_PS_VARIATIONS;
-            if (k & LPS_SHADOW)
+            if (l & LPS_SHADOW)
             {
                 pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
-                    lightPSVariations[k] + shadowVariations[shadows] + heightFogVariations[h]);
+                    lightPSVariations[l] + shadowVariations[shadows] + heightFogVariations[h]);
             }
             else
                 pixelShaders[j] = graphics_->GetShader(PS, pass->GetPixelShader(), pass->GetPixelShaderDefines() + " " +
-                    lightPSVariations[k] + heightFogVariations[h]);
+                    lightPSVariations[l] + heightFogVariations[h]);
         }
     }
     else
@@ -1535,26 +1510,22 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
         // Load vertex light variations
         if (pass->GetLightingMode() == LIGHTING_PERVERTEX)
         {
-            vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS * 2);
-            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS * 2; ++j)
+            vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
+            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
             {
-                unsigned k = j % (MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
-                unsigned h = j / (MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
-                unsigned g = k / MAX_VERTEXLIGHT_VS_VARIATIONS;
-                unsigned l = k % MAX_VERTEXLIGHT_VS_VARIATIONS;
+                unsigned g = j / MAX_VERTEXLIGHT_VS_VARIATIONS;
+                unsigned l = j % MAX_VERTEXLIGHT_VS_VARIATIONS;
                 vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
-                    vertexLightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]);
+                    vertexLightVSVariations[l] + geometryVSVariations[g]);
             }
         }
         else
         {
-            vertexShaders.Resize(MAX_GEOMETRYTYPES * 2);
-            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * 2; ++j)
+            vertexShaders.Resize(MAX_GEOMETRYTYPES);
+            for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
             {
-                unsigned k = j % MAX_GEOMETRYTYPES;
-                unsigned h = j / MAX_GEOMETRYTYPES;
                 vertexShaders[j] = graphics_->GetShader(VS, pass->GetVertexShader(), pass->GetVertexShaderDefines() + " " +
-                    geometryVSVariations[k] + heightFogVariations[h]);
+                    geometryVSVariations[j]);
             }
         }
         

+ 0 - 12
Source/Engine/Graphics/Renderer.h

@@ -59,15 +59,9 @@ enum LightVSVariation
     LVS_DIR = 0,
     LVS_SPOT,
     LVS_POINT,
-    LVS_SPEC,
-    LVS_SPOTSPEC,
-    LVS_POINTSPEC,
     LVS_SHADOW,
     LVS_SPOTSHADOW,
     LVS_POINTSHADOW,
-    LVS_DIRSPECSHADOW,
-    LVS_SPOTSPECSHADOW,
-    LVS_POINTSPECSHADOW,
     MAX_LIGHT_VS_VARIATIONS
 };
 
@@ -193,8 +187,6 @@ public:
     void SetReuseShadowMaps(bool enable);
     /// Set maximum number of shadow maps created for one resolution. Only has effect if reuse of shadow maps is disabled.
     void SetMaxShadowMaps(int shadowMaps);
-    /// Set maximum number of directional light shadow map cascades. Affects the size of directional light shadow maps.
-    void SetMaxShadowCascades(int cascades);
     /// Set dynamic instancing on/off.
     void SetDynamicInstancing(bool enable);
     /// Set minimum number of instances required in a batch group to render as instanced.
@@ -240,8 +232,6 @@ public:
     bool GetReuseShadowMaps() const { return reuseShadowMaps_; }
     /// Return maximum number of shadow maps per resolution.
     int GetMaxShadowMaps() const { return maxShadowMaps_; }
-    /// Return maximum number of directional light shadow map cascades.
-    int GetMaxShadowCascades() const { return maxShadowCascades_; }
     /// Return whether dynamic instancing is in use.
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     /// Return minimum number of instances required in a batch group to render as instanced.
@@ -443,8 +433,6 @@ private:
     int shadowQuality_;
     /// Maximum number of shadow maps per resolution.
     int maxShadowMaps_;
-    /// Maximum number of directional light shadow cascades.
-    int maxShadowCascades_;
     /// Minimum number of instances required in a batch group to render as instanced.
     int minInstances_;
     /// Maximum triangles per object for instancing.

+ 15 - 9
Source/Engine/Graphics/View.cpp

@@ -1167,6 +1167,9 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue, BatchQ
     bool allowTransparentShadows = !renderer_->GetReuseShadowMaps();
     bool allowLitBase = useLitBase_ && !light->IsNegative() && light == drawable->GetFirstLight() &&
         drawable->GetVertexLights().Empty() && !hasAmbientGradient;
+    // Ambient + shadowed point light on Shader Model 2 may exceed the pixel shader instruction count
+    if (allowLitBase && !graphics_->GetSM3Support() && light->GetLightType() == LIGHT_POINT && light->GetCastShadows())
+        allowLitBase = false;
     
     for (unsigned i = 0; i < batches.Size(); ++i)
     {
@@ -2189,18 +2192,20 @@ IntRect View::GetShadowMapViewport(Light* light, unsigned splitIndex, Texture2D*
 {
     unsigned width = shadowMap->GetWidth();
     unsigned height = shadowMap->GetHeight();
-    int maxCascades = renderer_->GetMaxShadowCascades();
     
     switch (light->GetLightType())
     {
     case LIGHT_DIRECTIONAL:
-        if (maxCascades == 1)
-            return IntRect(0, 0, width, height);
-        else if (maxCascades == 2)
-            return IntRect(splitIndex * width / 2, 0, (splitIndex + 1) * width / 2, height);
-        else
-            return IntRect((splitIndex & 1) * width / 2, (splitIndex / 2) * height / 2, ((splitIndex & 1) + 1) * width / 2,
-                (splitIndex / 2 + 1) * height / 2);
+        {
+            int numSplits = light->GetNumShadowSplits();
+            if (numSplits == 1)
+                return IntRect(0, 0, width, height);
+            else if (numSplits == 2)
+                return IntRect(splitIndex * width / 2, 0, (splitIndex + 1) * width / 2, height);
+            else
+                return IntRect((splitIndex & 1) * width / 2, (splitIndex / 2) * height / 2, ((splitIndex & 1) + 1) * width / 2,
+                    (splitIndex / 2 + 1) * height / 2);
+        }
         
     case LIGHT_SPOT:
         return IntRect(0, 0, width, height);
@@ -2227,8 +2232,9 @@ void View::SetupShadowCameras(LightQueryResult& query)
             
             float nearSplit = camera_->GetNearClip();
             float farSplit;
+            int numSplits = light->GetNumShadowSplits();
             
-            while (splits < renderer_->GetMaxShadowCascades())
+            while (splits < numSplits)
             {
                 // If split is completely beyond camera far clip, we are done
                 if (nearSplit > camera_->GetFarClip())

+ 2 - 0
Source/Engine/LuaScript/pkgs/Graphics/Light.pkg

@@ -70,6 +70,7 @@ class Light : public Drawable
     Texture* GetRampTexture() const;
     Texture* GetShapeTexture() const;
     Frustum GetFrustum() const;
+    int GetNumShadowSplits() const;
     bool IsNegative() const;
     
     tolua_property__get_set LightType lightType;
@@ -91,6 +92,7 @@ class Light : public Drawable
     tolua_property__get_set Texture* rampTexture;
     tolua_property__get_set Texture* shapeTexture;
     tolua_readonly tolua_property__get_set Frustum frustum;
+    tolua_readonly tolua_property__get_set int numShadowSplits;
     tolua_readonly tolua_property__is_set bool negative;
     tolua_readonly tolua_property__get_set Color effectiveColor;
     tolua_readonly tolua_property__get_set float effectiveSpecularIntensity;

+ 0 - 100
Source/Engine/LuaScript/pkgs/Graphics/Renderer.pkg

@@ -1,102 +1,5 @@
 $#include "Renderer.h"
 
-static const int SHADOW_MIN_PIXELS;
-static const int INSTANCING_BUFFER_DEFAULT_SIZE;
-
-enum LightVSVariation
-{
-    LVS_DIR = 0,
-    LVS_SPOT,
-    LVS_POINT,
-    LVS_SPEC,
-    LVS_SPOTSPEC,
-    LVS_POINTSPEC,
-    LVS_SHADOW,
-    LVS_SPOTSHADOW,
-    LVS_POINTSHADOW,
-    LVS_DIRSPECSHADOW,
-    LVS_SPOTSPECSHADOW,
-    LVS_POINTSPECSHADOW,
-    MAX_LIGHT_VS_VARIATIONS
-};
-
-enum VertexLightVSVariation
-{
-    VLVS_NOLIGHTS = 0,
-    VLVS_1LIGHT,
-    VLVS_2LIGHTS,
-    VLVS_3LIGHTS,
-    VLVS_4LIGHTS,
-    MAX_VERTEXLIGHT_VS_VARIATIONS
-};
-
-enum LightPSVariation
-{
-    LPS_NONE = 0,
-    LPS_SPOT,
-    LPS_POINT,
-    LPS_POINTMASK,
-    LPS_SPEC,
-    LPS_SPOTSPEC,
-    LPS_POINTSPEC,
-    LPS_POINTMASKSPEC,
-    LPS_SHADOW,
-    LPS_SPOTSHADOW,
-    LPS_POINTSHADOW,
-    LPS_POINTMASKSHADOW,
-    LPS_SHADOWSPEC,
-    LPS_SPOTSHADOWSPEC,
-    LPS_POINTSHADOWSPEC,
-    LPS_POINTMASKSHADOWSPEC,
-    MAX_LIGHT_PS_VARIATIONS
-};
-
-enum DeferredLightVSVariation
-{
-    DLVS_NONE = 0,
-    DLVS_DIR,
-    DLVS_ORTHO,
-    DLVS_ORTHODIR,
-    MAX_DEFERRED_LIGHT_VS_VARIATIONS
-};
-
-enum DeferredLightPSVariation
-{
-    DLPS_NONE = 0,
-    DLPS_SPOT,
-    DLPS_POINT,
-    DLPS_POINTMASK,
-    DLPS_SPEC,
-    DLPS_SPOTSPEC,
-    DLPS_POINTSPEC,
-    DLPS_POINTMASKSPEC,
-    DLPS_SHADOW,
-    DLPS_SPOTSHADOW,
-    DLPS_POINTSHADOW,
-    DLPS_POINTMASKSHADOW,
-    DLPS_SHADOWSPEC,
-    DLPS_SPOTSHADOWSPEC,
-    DLPS_POINTSHADOWSPEC,
-    DLPS_POINTMASKSHADOWSPEC,
-    DLPS_ORTHO,
-    DLPS_ORTHOSPOT,
-    DLPS_ORTHOPOINT,
-    DLPS_ORTHOPOINTMASK,
-    DLPS_ORTHOSPEC,
-    DLPS_ORTHOSPOTSPEC,
-    DLPS_ORTHOPOINTSPEC,
-    DLPS_ORTHOPOINTMASKSPEC,
-    DLPS_ORTHOSHADOW,
-    DLPS_ORTHOSPOTSHADOW,
-    DLPS_ORTHOPOINTSHADOW,
-    DLPS_ORTHOPOINTMASKSHADOW,
-    DLPS_ORTHOSHADOWSPEC,
-    DLPS_ORTHOSPOTSHADOWSPEC,
-    DLPS_ORTHOPOINTSHADOWSPEC,
-    DLPS_ORTHOPOINTMASKSHADOWSPEC,
-    MAX_DEFERRED_LIGHT_PS_VARIATIONS
-};
-
 class Renderer
 {
     void SetNumViewports(unsigned num);
@@ -114,7 +17,6 @@ class Renderer
     void SetShadowQuality(int quality);
     void SetReuseShadowMaps(bool enable);
     void SetMaxShadowMaps(int shadowMaps);
-    void SetMaxShadowCascades(int cascades);
     void SetDynamicInstancing(bool enable);
     void SetMinInstances(int instances);
     void SetMaxInstanceTriangles(int triangles);
@@ -138,7 +40,6 @@ class Renderer
     int GetShadowQuality() const;
     bool GetReuseShadowMaps() const;
     int GetMaxShadowMaps() const;
-    int GetMaxShadowCascades() const;
     bool GetDynamicInstancing() const;
     int GetMinInstances() const;
     int GetMaxInstanceTriangles() const;
@@ -178,7 +79,6 @@ class Renderer
     tolua_property__get_set int shadowQuality;
     tolua_property__get_set bool reuseShadowMaps;
     tolua_property__get_set int maxShadowMaps;
-    tolua_property__get_set int maxShadowCascades;
     tolua_property__get_set bool dynamicInstancing;
     tolua_property__get_set int minInstances;
     tolua_property__get_set int maxInstanceTriangles;

+ 1 - 2
Source/Engine/Script/GraphicsAPI.cpp

@@ -807,6 +807,7 @@ static void RegisterLight(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Light", "void set_shapeTexture(Texture@+)", asMETHOD(Light, SetShapeTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "Texture@+ get_shapeTexture() const", asMETHOD(Light, GetShapeTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "Frustum get_frustum() const", asMETHOD(Light, GetFrustum), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Light", "int get_numShadowSplits() const", asMETHOD(Light, GetNumShadowSplits), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "bool get_negative() const", asMETHOD(Light, IsNegative), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "Color get_effectiveColor() const", asMETHOD(Light, GetEffectiveColor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Light", "float get_effectiveSpecularIntensity() const", asMETHOD(Light, GetEffectiveSpecularIntensity), asCALL_THISCALL);
@@ -1329,8 +1330,6 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "int get_shadowMapSize() const", asMETHOD(Renderer, GetShadowMapSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_shadowQuality(int)", asMETHOD(Renderer, SetShadowQuality), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_shadowQuality() const", asMETHOD(Renderer, GetShadowQuality), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "void set_maxShadowCascades(int)", asMETHOD(Renderer, SetMaxShadowCascades), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "int get_maxShadowCascades() const", asMETHOD(Renderer, GetMaxShadowCascades), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxShadowMaps(int)", asMETHOD(Renderer, SetMaxShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxShadowMaps() const", asMETHOD(Renderer, GetMaxShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_reuseShadowMaps(bool)", asMETHOD(Renderer, SetReuseShadowMaps), asCALL_THISCALL);