Browse Source

Added PCF hardware filtering shader for cubemap shadows
Added code for standard deferred lighting (to be used for lights with shadows)

BearishSun 8 years ago
parent
commit
763441561c

+ 16 - 0
Data/Raw/Engine/DataList.json

@@ -135,6 +135,14 @@
         {
             "Path": "ShadowFiltering.bslinc",
             "UUID": "50cfc08b-4869-4373-9b7e-22de12c26b20"
+        },
+        {
+            "Path": "CubeShadowFiltering.bslinc",
+            "UUID": "f2b61e2a-20a7-4721-8808-76c1737251b4"
+        },
+        {
+            "Path": "DeferredLightCommon.bslinc",
+            "UUID": "8ffd1df9-3de4-442d-adca-95e8766fa81a"
         }
     ],
     "Shaders": [
@@ -269,6 +277,14 @@
         {
             "Path": "ShadowDepthDirectional.bsl",
             "UUID": "acd0f016-8084-4b32-806c-66a85b34ee5a"
+        },
+        {
+            "Path": "DeferredDirectionalLight.bsl",
+            "UUID": "17d573f8-1142-4257-9e32-90038d3786f3"
+        },
+        {
+            "Path": "DeferredPointLight.bslinc",
+            "UUID": "863e3d6c-7342-4715-900c-b38e03011798"
         }
     ],
     "Skin": [

+ 152 - 0
Data/Raw/Engine/Includes/CubeShadowFiltering.bslinc

@@ -0,0 +1,152 @@
+Technique : base("CubeShadowFiltering")
+{
+	Pass =
+	{
+		Fragment =
+		{
+			// Random samples on a disc of radius 2.5. Random values generated using low discrepancy
+			// Hammersley sequence and then mapped to the disc.
+			static const float2 discSamples4[4] =
+			{
+				float2(0, 0),
+				float2(-1.25, 6.69872e-08),
+				float2(4.73671e-08, 1.76777),
+				float2(-1.74038e-07, -2.16506)
+			};
+			
+			static const float2 discSamples12[12] =
+			{
+				float2(0, 0),
+				float2(-0.721688, 3.86751e-08),
+				float2(2.73474e-08, 1.02062),
+				float2(-1.00481e-07, -1.25),
+				float2(1.02062, 1.02062),
+				float2(-1.14109, -1.14109),
+				float2(-1.25, 1.25),
+				float2(1.35015, -1.35015),
+				float2(1.88586, 0.781149),
+				float2(-2.00026, -0.828534),
+				float2(-0.873351, 2.10846),
+				float2(0.915979, -2.21137)
+			};
+
+			static const float2 discSamples32[32] =
+			{
+				float2(0, 0),
+				float2(-0.441942, 2.36836e-08),
+				float2(1.67468e-08, 0.625),
+				float2(-6.15317e-08, -0.765466),
+				float2(0.625, 0.625),
+				float2(-0.698771, -0.698771),
+				float2(-0.765465, 0.765466),
+				float2(0.826797, -0.826797),
+				float2(1.15485, 0.478354),
+				float2(-1.2249, -0.507371),
+				float2(-0.534816, 1.29116),
+				float2(0.56092, -1.35418),
+				float2(0.585862, 1.4144),
+				float2(-0.609785, -1.47215),
+				float2(-1.52772, 0.632803),
+				float2(1.58134, -0.655014),
+				float2(1.7338, 0.344874),
+				float2(-1.78716, -0.355488),
+				float2(-0.365794, 1.83897),
+				float2(0.375818, -1.88936),
+				float2(1.09804, 1.64334),
+				float2(-1.12516, -1.68392),
+				float2(-1.72355, 1.15164),
+				float2(1.76228, -1.17752),
+				float2(1.80018, 1.20284),
+				float2(-1.83731, -1.22765),
+				float2(-1.25196, 1.87369),
+				float2(1.27581, -1.90938),
+				float2(0.456226, 2.2936),
+				float2(-0.464301, -2.3342),
+				float2(-2.3741, 0.472239),
+				float2(2.41335, -0.480045)
+			};
+			
+			TextureCube gShadowCubeTex;
+			SamplerComparisonState gShadowCubeSampler;
+			
+			cbuffer Params
+			{
+				float4x4 gFaceVPMatrices[6];
+				float gInvResolution;
+			};			
+			
+			float cubemapPCF(float3 worldPos, float3 lightPos, float lightRadius, float depthBias)
+			{
+				float3 toLight = lightPos - worldPos;
+				float distToLight = length(toLight);
+				
+				// No occlusion if outside radius
+				if(distToLight > lightRadius)
+					return 0.0f;
+					
+				float3 lightDir = toLight / distToLight;
+				
+				float3 up = abs(lightDir.z) < 0.999f ? float3(0, 0, 1) : float3(1, 0, 0);
+				float3 side = normalize(cross(up, lightDir));
+				up = cross(lightDir, side);
+				
+				up *= gInvResolution;
+				side *= gInvResolution;
+				
+				// Determine cube face to sample from
+				float3 absToLight = abs(toLight);
+				float maxComponent = max(absToLight.x, max(absToLight.y, absToLight.z));
+				
+				int faceIdx = 0;
+				if(maxComponent == absToLight.x)
+					faceIdx = toLight.x > 0.0f ? 0 : 1;
+				else if(maxComponent == absToLight.z)
+					faceIdx = toLight.z > 0.0f ? 4 : 5;
+				else
+					faceIdx = toLight.y > 0.0f ? 2 : 3;
+				
+				// Get position of the receiver in shadow space
+				float shadowPos = mul(gFaceVPMatrices[faceIdx], worldPos);
+				
+				float receiverDepth = shadowPos.z / shadowPos.w;
+				float shadowBias = depthBias / shadowPos.w;
+				
+				// TODO: Sampler state must be greater or equal
+				
+				float occlusion = 0.0f;
+				#if SHADOW_QUALITY <= 1
+					occlusion = gShadowCubeTex.SampleCmpLevelZero(gShadowCubeSampler, lightDir, receiverDepth + shadowBias);
+				#elif SHADOW_QUALITY == 2
+					[unroll]
+					for(int i = 0; i < 4; ++i)
+					{
+						float sampleDir = lightDir + side * discSamples4[i].x + up * discSamples4[i].y;
+						occlusion += gShadowCubeTex.SampleCmpLevelZero(gShadowCubeSampler, sampleDir, receiverDepth + shadowBias);
+					}
+					
+					occlusion /= 4;
+				#elif SHADOW_QUALITY == 3
+					[unroll]
+					for(int i = 0; i < 12; ++i)
+					{
+						float sampleDir = lightDir + side * discSamples12[i].x + up * discSamples12[i].y;
+						occlusion += gShadowCubeTex.SampleCmpLevelZero(gShadowCubeSampler, sampleDir, receiverDepth + shadowBias);
+					}
+					
+					occlusion /= 12;
+				#else
+					[unroll]
+					for(int i = 0; i < 32; ++i)
+					{
+						float sampleDir = lightDir + side * discSamples32[i].x + up * discSamples32[i].y;
+						occlusion += gShadowCubeTex.SampleCmpLevelZero(gShadowCubeSampler, sampleDir, receiverDepth + shadowBias);
+					}
+					
+					occlusion /= 32;
+				#endif
+				
+				return occlusion;
+			}
+		};
+	};
+};

+ 71 - 0
Data/Raw/Engine/Includes/DeferredLightCommon.bslinc

@@ -0,0 +1,71 @@
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+#include "$ENGINE$\LightingCommon.bslinc"
+
+Technique 
+ : base("DeferredLightCommon")
+ : inherits("GBufferInput")
+ : inherits("PerCameraData")
+ : inherits("LightingCommon") =
+{
+	Pass =
+	{
+		DepthWrite = false;
+	
+		#ifdef INSIDE_GEOMETRY
+		
+		DepthRead = false;
+		Cull = CW;
+		
+		#else
+		
+		DepthRead = true;
+		Cull = CCW;
+		
+		#endif
+	
+		Common = 
+		{
+			cbuffer PerLight
+			{
+				// x, y, z - World position of the light
+				// w - Radius of the area light source, zero if not an area light
+				float4 gLightPositionAndSrcRadius;
+				float4 gLightColorAndLuminance;
+				// x - outerAngle in radians, y - cos(outerAngle), z - 1.0f/(cos(innerAngle) - cos(outerAngle)), w - inverse light attenuation radius
+				float4 gLightSpotAnglesAndSqrdInvAttRadius;
+				float4 gLightDirectionAndAttRadius;
+				// xyz - light position shifted in order to adjust for area spot lights
+				// w - light type -> Directional = 0, Radial = >0, Spot = >0.5
+				float4 gShiftedLightPositionAndType;
+				
+				// x - Num sides (zero for radial lights)
+				// y - Num slices (zero for radial lights)
+				// z - Sphere radius for radial lights
+				// w - Cone radius for spot lights
+				float4 gLightGeometry; 
+				float4x4 gMatConeTransform;
+			}		
+		};
+
+		Fragment = 
+		{		
+			LightData getLightData()
+			{
+				LightData output;
+				
+				output.position = gLightPositionAndSrcRadius.xyz;
+				output.attRadius = gLightDirectionAndAttRadius.w;
+				output.direction = gLightDirectionAndAttRadius.xyz;
+				output.luminance = gLightColorAndLuminance.w;
+				output.spotAngles = gLightSpotAnglesAndSqrdInvAttRadius.xyz;
+				output.attRadiusSqrdInv = gLightSpotAnglesAndSqrdInvAttRadius.w;
+				output.color = gLightColorAndLuminance.rgb;
+				output.srcRadius = gLightPositionAndSrcRadius.w;
+				output.shiftedLightPosition = gShiftedLightPositionAndType.rgb;
+
+				return output;
+			}
+		};
+	};
+};

+ 2 - 2
Data/Raw/Engine/Includes/ImageBasedLighting.bslinc

@@ -38,10 +38,10 @@ Technique
 			
 			StructuredBuffer<ReflProbeData> gReflectionProbes;	
 
-			#ifdef USE_COMPUTE_INDICES
+			#if USE_COMPUTE_INDICES
 				groupshared uint gReflectionProbeIndices[MAX_PROBES];
 			#endif
-			#ifdef USE_LIGHT_GRID_INDICES
+			#if USE_LIGHT_GRID_INDICES
 				Buffer<uint> gReflectionProbeIndices;
 			#endif
 			

+ 153 - 134
Data/Raw/Engine/Includes/LightingCommon.bslinc

@@ -195,15 +195,161 @@ Technique
 				return diffuse + specular * specLobeEnergy;
 			}	
 			
-			StructuredBuffer<LightData> gLights;
+			float3 getLuminanceDirectional(LightData lightData, float3 worldPos, float3 V, float3 R, SurfaceData surfaceData)
+			{
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 L = -lightData.direction;
+				float NoL = saturate(dot(N, L));
+				float specEnergy = 1.0f;
+				
+				// Distant disk area light. Calculate its contribution analytically by
+				// finding the most important (least error) point on the area light and
+				// use it as a form of importance sampling.
+				if(lightData.srcRadius > 0)
+				{
+					float diskRadius = sin(lightData.srcRadius);
+					float distanceToDisk = cos(lightData.srcRadius);
+					
+					// Closest point to disk (approximation for distant disks)
+					float DoR = dot(L, R);
+					float3 S = normalize(R - DoR * L);
+					L = DoR < distanceToDisk ? normalize(distanceToDisk * L + S * diskRadius) : R;
+				}
+				
+				float3 surfaceShading = getSurfaceShading(V, L, specEnergy, surfaceData);
+				float illuminance = lightData.luminance * NoL;
+				return lightData.color * illuminance * surfaceShading;
+			}
+			
+			float3 getLuminanceRadial(LightData lightData, float3 worldPos, float3 V, float3 R, float roughness2, SurfaceData surfaceData)
+			{
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 toLight = lightData.position - worldPos;
+				float distToLightSqrd = dot(toLight, toLight);
+				float invDistToLight = rsqrt(distToLightSqrd);
+				
+				float3 L = toLight * invDistToLight;
+				float NoL = dot(N, L);
+				
+				float specEnergy = 1.0f;
+				float illuminance = 0.0f;
+
+				// Sphere area light. Calculate its contribution analytically by
+				// finding the most important (least error) point on the area light and
+				// use it as a form of importance sampling.
+				if(lightData.srcRadius > 0)
+				{
+					// Calculate illuminance depending on source size, distance and angle
+					illuminance = illuminanceSphereAreaLight(NoL, distToLightSqrd, lightData);	
+
+					// Energy conservation:
+					//    We are widening the specular distribution by the sphere's subtended angle, 
+					//    so we need to handle the increase in energy. It is not enough just to account
+					//    for the sphere solid angle, since the energy difference is highly dependent on
+					//    specular distribution. By accounting for this energy difference we ensure glossy
+					//    reflections have sharp edges, instead of being too blurry.
+					//    See http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf for reference
+					float sphereAngle = saturate(lightData.srcRadius * invDistToLight);
+					
+					specEnergy = roughness2 / saturate(roughness2 + 0.5f * sphereAngle);
+					specEnergy *= specEnergy;							
+				
+					// Find closest point on sphere to ray
+					float3 closestPointOnRay = dot(toLight, R) * R;
+					float3 centerToRay = closestPointOnRay - toLight;
+					float invDistToRay = rsqrt(dot(centerToRay, centerToRay));
+					float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.srcRadius * invDistToRay);
+					
+					toLight = closestPointOnSphere;
+					L = normalize(toLight);
+				}
+				else
+				{
+					NoL = saturate(NoL);
+					illuminance = illuminancePointLight(distToLightSqrd, NoL, lightData);
+				}
+				
+				float attenuation = getRadialAttenuation(distToLightSqrd, lightData);
+				float3 surfaceShading = getSurfaceShading(V, L, specEnergy, surfaceData);
+					
+				return lightData.color * illuminance * attenuation * surfaceShading;
+			}
+			
+			float3 getLuminanceSpot(LightData lightData, float3 worldPos, float3 V, float3 R, float roughness2, SurfaceData surfaceData)
+			{
+				float3 N = surfaceData.worldNormal.xyz;
+				float3 toLight = lightData.position - worldPos;
+				float distToLightSqrd = dot(toLight, toLight);
+				float invDistToLight = rsqrt(distToLightSqrd);
+				
+				float3 L = toLight * invDistToLight;
+				float NoL = dot(N, L);
+				
+				float specEnergy = 1.0f;
+				float illuminance = 0.0f;
+				float spotAttenuation = 1.0f;
+				
+				// Disc area light. Calculate its contribution analytically by
+				// finding the most important (least error) point on the area light and
+				// use it as a form of importance sampling.
+				if(lightData.srcRadius > 0)
+				{
+					// Calculate illuminance depending on source size, distance and angle
+					illuminance = illuminanceDiscAreaLight(NoL, distToLightSqrd, L, lightData);	
+				
+					// Energy conservation: Similar case as with radial lights
+					float rightDiscAngle = saturate(lightData.srcRadius * invDistToLight);
+					
+					// Account for disc orientation somewhat
+					float discAngle = rightDiscAngle * saturate(dot(lightData.direction, -L));
+					
+					specEnergy = roughness2 / saturate(roughness2 + 0.5f * discAngle);
+					specEnergy *= specEnergy;							
+				
+					// Find closest point on disc to ray
+					float3 discNormal = -lightData.direction;
+					float distAlongLightDir = max(dot(R, discNormal), 1e-6f);
+					float t = dot(toLight, discNormal) / distAlongLightDir;
+					float3 closestPointOnPlane = R * t; // Relative to shaded world point
+					
+					float3 centerToRay = closestPointOnPlane - toLight;
+					float invDistToRay = rsqrt(dot(centerToRay, centerToRay));
+					float3 closestPointOnDisc = toLight + centerToRay * saturate(lightData.srcRadius * invDistToRay);
+
+					toLight = closestPointOnDisc;
+					L = normalize(toLight);
+					
+					// Expand spot attenuation by disc radius (not physically based)
+					float3 toSpotEdge = normalize(lightData.shiftedLightPosition - worldPos);
+					spotAttenuation = getSpotAttenuation(toSpotEdge, lightData);
+					
+					// TODO - Spot attenuation fades out the specular highlight in a noticeable way
+				}
+				else
+				{
+					NoL = saturate(NoL);
+					illuminance = illuminancePointLight(distToLightSqrd, NoL, lightData);
+					
+					spotAttenuation = getSpotAttenuation(L, lightData);
+				}
+				
+				float radialAttenuation = getRadialAttenuation(distToLightSqrd, lightData);
+				float attenuation = spotAttenuation * radialAttenuation;
+				float3 surfaceShading = getSurfaceShading(V, L, specEnergy, surfaceData);
+					
+				return lightData.color * illuminance * attenuation * surfaceShading;
+			}
 			
-			#ifdef USE_COMPUTE_INDICES
+			#if USE_COMPUTE_INDICES
 				groupshared uint gLightIndices[MAX_LIGHTS];
 			#endif
-			#ifdef USE_LIGHT_GRID_INDICES
+			#if USE_LIGHT_GRID_INDICES
 				Buffer<uint> gLightIndices;
 			#endif
 			
+			#if USE_COMPUTE_INDICES || USE_LIGHT_GRID_INDICES
+			StructuredBuffer<LightData> gLights;
+			
 			float4 getDirectLighting(float3 worldPos, float3 V, float3 R, SurfaceData surfaceData, uint4 lightOffsets)
 			{
 				float3 N = surfaceData.worldNormal.xyz;
@@ -218,28 +364,7 @@ Technique
 					for(uint i = 0; i < lightOffsets.x; ++i)
 					{
 						LightData lightData = gLights[i];
-					
-						float3 L = -lightData.direction;
-						float NoL = saturate(dot(N, L));
-						float specEnergy = 1.0f;
-						
-						// Distant disk area light. Calculate its contribution analytically by
-						// finding the most important (least error) point on the area light and
-						// use it as a form of importance sampling.
-						if(lightData.srcRadius > 0)
-						{
-							float diskRadius = sin(lightData.srcRadius);
-							float distanceToDisk = cos(lightData.srcRadius);
-							
-							// Closest point to disk (approximation for distant disks)
-							float DoR = dot(L, R);
-							float3 S = normalize(R - DoR * L);
-							L = DoR < distanceToDisk ? normalize(distanceToDisk * L + S * diskRadius) : R;
-						}
-						
-						float3 surfaceShading = getSurfaceShading(V, L, specEnergy, surfaceData);
-						float illuminance = lightData.luminance * NoL;
-						outLuminance += lightData.color * illuminance * surfaceShading;
+						outLuminance += getLuminanceDirectional(lightData, worldPos, V, R, surfaceData);
 					}
 					
 					// Handle radial lights
@@ -248,55 +373,7 @@ Technique
                         uint lightIdx = gLightIndices[i];
 						LightData lightData = gLights[lightIdx];
                         
-						float3 toLight = lightData.position - worldPos;
-						float distToLightSqrd = dot(toLight, toLight);
-						float invDistToLight = rsqrt(distToLightSqrd);
-						
-						float3 L = toLight * invDistToLight;
-						float NoL = dot(N, L);
-						
-						float specEnergy = 1.0f;
-						float illuminance = 0.0f;
-
-						// Sphere area light. Calculate its contribution analytically by
-						// finding the most important (least error) point on the area light and
-						// use it as a form of importance sampling.
-						if(lightData.srcRadius > 0)
-						{
-							// Calculate illuminance depending on source size, distance and angle
-							illuminance = illuminanceSphereAreaLight(NoL, distToLightSqrd, lightData);	
-
-							// Energy conservation:
-							//    We are widening the specular distribution by the sphere's subtended angle, 
-							//    so we need to handle the increase in energy. It is not enough just to account
-							//    for the sphere solid angle, since the energy difference is highly dependent on
-							//    specular distribution. By accounting for this energy difference we ensure glossy
-							//    reflections have sharp edges, instead of being too blurry.
-							//    See http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf for reference
-							float sphereAngle = saturate(lightData.srcRadius * invDistToLight);
-							
-							specEnergy = roughness2 / saturate(roughness2 + 0.5f * sphereAngle);
-							specEnergy *= specEnergy;							
-						
-							// Find closest point on sphere to ray
-							float3 closestPointOnRay = dot(toLight, R) * R;
-							float3 centerToRay = closestPointOnRay - toLight;
-							float invDistToRay = rsqrt(dot(centerToRay, centerToRay));
-							float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.srcRadius * invDistToRay);
-							
-							toLight = closestPointOnSphere;
-							L = normalize(toLight);
-						}
-						else
-						{
-							NoL = saturate(NoL);
-							illuminance = illuminancePointLight(distToLightSqrd, NoL, lightData);
-						}
-						
-						float attenuation = getRadialAttenuation(distToLightSqrd, lightData);
-						float3 surfaceShading = getSurfaceShading(V, L, specEnergy, surfaceData);
-							
-						outLuminance += lightData.color * illuminance * attenuation * surfaceShading;
+						outLuminance += getLuminanceRadial(lightData, worldPos, V, R, roughness2, surfaceData);
                     }
 
 					// Handle spot lights
@@ -305,66 +382,7 @@ Technique
                         uint lightIdx = gLightIndices[i];
 						LightData lightData = gLights[lightIdx];
 						
-						float3 toLight = lightData.position - worldPos;
-						float distToLightSqrd = dot(toLight, toLight);
-						float invDistToLight = rsqrt(distToLightSqrd);
-						
-						float3 L = toLight * invDistToLight;
-						float NoL = dot(N, L);
-						
-						float specEnergy = 1.0f;
-						float illuminance = 0.0f;
-						float spotAttenuation = 1.0f;
-						
-						// Disc area light. Calculate its contribution analytically by
-						// finding the most important (least error) point on the area light and
-						// use it as a form of importance sampling.
-						if(lightData.srcRadius > 0)
-						{
-							// Calculate illuminance depending on source size, distance and angle
-							illuminance = illuminanceDiscAreaLight(NoL, distToLightSqrd, L, lightData);	
-						
-							// Energy conservation: Similar case as with radial lights
-							float rightDiscAngle = saturate(lightData.srcRadius * invDistToLight);
-							
-							// Account for disc orientation somewhat
-							float discAngle = rightDiscAngle * saturate(dot(lightData.direction, -L));
-							
-							specEnergy = roughness2 / saturate(roughness2 + 0.5f * discAngle);
-							specEnergy *= specEnergy;							
-						
-							// Find closest point on disc to ray
-							float3 discNormal = -lightData.direction;
-							float distAlongLightDir = max(dot(R, discNormal), 1e-6f);
-							float t = dot(toLight, discNormal) / distAlongLightDir;
-							float3 closestPointOnPlane = R * t; // Relative to shaded world point
-							
-							float3 centerToRay = closestPointOnPlane - toLight;
-							float invDistToRay = rsqrt(dot(centerToRay, centerToRay));
-							float3 closestPointOnDisc = toLight + centerToRay * saturate(lightData.srcRadius * invDistToRay);
-
-							toLight = closestPointOnDisc;
-							L = normalize(toLight);
-							
-							// Expand spot attenuation by disc radius (not physically based)
-							float3 toSpotEdge = normalize(lightData.shiftedLightPosition - worldPos);
-							spotAttenuation = getSpotAttenuation(toSpotEdge, lightData);
-							
-							// TODO - Spot attenuation fades out the specular highlight in a noticeable way
-						}
-						else
-						{
-							NoL = saturate(NoL);
-							illuminance = illuminancePointLight(distToLightSqrd, NoL, lightData);
-							
-							spotAttenuation = getSpotAttenuation(L, lightData);
-						}
-						
-						float radialAttenuation = getRadialAttenuation(distToLightSqrd, lightData);
-						float attenuation = spotAttenuation * radialAttenuation;
-						float3 surfaceShading = getSurfaceShading(V, L, specEnergy, surfaceData);
-							
-						outLuminance += lightData.color * illuminance * attenuation * surfaceShading;
+						outLuminance += getLuminanceSpot(lightData, worldPos, V, R, roughness2, surfaceData);
                     }
 					
 					// Ambient term for in-editor visualization, not used in actual lighting
@@ -374,6 +392,7 @@ Technique
 				
 				return float4(outLuminance, alpha);
 			}
+			#endif
 		};
 	};
 };

+ 71 - 0
Data/Raw/Engine/Shaders/DeferredDirectionalLight.bsl

@@ -0,0 +1,71 @@
+#include "$ENGINE$\DeferredLightCommon.bslinc"
+
+Technique : inherits("DeferredLightCommon") =
+{
+	Pass =
+	{
+		DepthRead = false;
+	
+		Common = 
+		{
+			struct VStoFS
+			{
+				float4 position : SV_POSITION;
+				float2 uv0 : TEXCOORD0;
+				float3 screenDir : TEXCOORD1;
+			};
+		};
+	
+		Vertex =
+		{
+			struct VertexInput
+			{
+				float2 screenPos : POSITION;
+				float2 uv0 : TEXCOORD0;
+			};
+			
+			VStoFS main(VertexInput input)
+			{
+				VStoFS output;
+			
+				output.position = float4(input.screenPos, 0, 1);
+				output.uv0 = input.uv0;
+				output.screenDir = mul(gMatInvProj, float4(input.screenPos, 1, 0)).xyz - gViewOrigin.xyz;
+			
+				return output;
+			}			
+		};
+		
+		Fragment = 
+		{
+			float4 main(VStoFS input) : SV_Target0
+			{
+				uint2 pixelPos = (uint2)(input.uv0 * (float2)gViewportRectangle.zw - ((float2)gViewportRectangle.xy + 0.5f));
+				
+				#if MSAA_COUNT > 1
+				SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#else
+				SurfaceData surfaceData = getGBufferData(pixelPos);
+				#endif			
+			
+				if(surfaceData.worldNormal.w > 0.0f)
+				{
+					float3 cameraDir = normalize(input.screenDir);
+					float3 worldPosition = input.screenDir * surfaceData.depth + gViewOrigin;
+					
+					float3 V = normalize(gViewOrigin - worldPosition);
+					float3 N = surfaceData.worldNormal.xyz;
+					float3 R = 2 * dot(V, N) * N - V;
+
+					float roughness2 = max(surfaceData.roughness, 0.08f);
+					roughness2 *= roughness2;
+					
+					LightData lightData = getLightData();
+					return float4(getLuminanceDirectional(lightData, worldPosition, V, R, surfaceData), 1.0f);
+				}
+				else
+					return float4(0.0f, 0.0f, 0.0f, 0.0f);
+			}
+		};
+	};
+};

+ 130 - 0
Data/Raw/Engine/Shaders/DeferredPointLight.bslinc

@@ -0,0 +1,130 @@
+#include "$ENGINE$\DeferredLightCommon.bslinc"
+
+Technique : inherits("DeferredLightCommon") =
+{
+	Pass =
+	{
+		Common = 
+		{
+			struct VStoFS
+			{
+				float4 position : SV_POSITION;
+				float4 screenPos : TEXCOORD0;
+			};
+		};
+	
+		Vertex =
+		{
+			struct VertexInput
+			{
+				float3 position : POSITION;
+				uint vertexIdx : SV_VERTEXID;
+			};
+			
+			VStoFS main(VertexInput input)
+			{
+				VStoFS output;
+				
+				float3 worldPosition;
+				uint numSides = gLightGeometry.x;
+				if(numSides > 0) // Generate spot light geometry
+				{
+					uint numSlices = gLightGeometry.y;
+					float radius = gLightGeometry.w;
+					float angle = gLightSpotAnglesAndSqrdInvAttRadius.x;
+				
+					// Extra scale to ensure edges lie on the circle, not inside it
+					// TODO - These can be precomputed
+					float extraRadiusScale = 1.0f / cos(PI / (float)numSides);
+					float angleTan = tan(angle);
+					float height = radius / angleTan;
+		
+					uint sphereStartIdx = numSides * numSlices;
+					// Cone vertices
+					if (input.vertexIdx < sphereStartIdx)
+					{
+						uint sliceIdx = input.vertexIdx / numSides;
+						uint sideIdx = input.vertexIdx % numSides;
+
+						float curAngle = sideIdx * 2 * PI / (float)numSides;
+						float sliceOffset = height * sliceIdx / (float)(numSlices - 1);
+						float sliceRadius = sliceOffset * angleTan * extraRadiusScale;
+
+						float4 localPos = float4(sliceRadius * cos(curAngle), 
+							sliceRadius * sin(curAngle), -sliceOffset, 1.0f);
+						worldPosition = (mul(gMatConeTransform, localPos)).xyz;
+					}
+					else // Sphere cap vertices
+					{
+						uint sphereVertexIdx = input.vertexIdx - sphereStartIdx;
+						uint sliceIdx = sphereVertexIdx / numSides;
+						uint sideIdx = sphereVertexIdx % numSides;
+
+						float curAngle = sideIdx * 2 * PI / (float)numSides;
+						float sliceOffset = radius * sliceIdx / (float)(numSlices - 1);
+						float sliceRadius = sqrt(max(0.0f, radius * radius - sliceOffset * sliceOffset)) * extraRadiusScale;
+
+						float4 localPos = float4(sliceRadius * cos(curAngle), 
+							sliceRadius * sin(curAngle), -height - sliceOffset, 1.0f);
+						worldPosition = (mul(gMatConeTransform, localPos)).xyz;
+					}
+				}
+				else // Scale and position pre-generated sphere geometry
+				{
+					worldPosition = input.position * gLightGeometry.z + gLightPositionAndSrcRadius.xyz;
+				}
+				
+				output.screenPos = mul(gMatViewProj, float4(worldPosition, 1));
+				output.position = output.screenPos;
+				
+				return output;
+			}			
+		};
+		
+		Fragment = 
+		{		
+			float4 main(VStoFS input, uint sampleIdx : SV_SampleIndex) : SV_Target0
+			{
+				float2 ndcPos = input.screenPos.xy / input.screenPos.w;
+				float2 screenUV = ndcPos * gClipToUVScaleOffset.xy + gClipToUVScaleOffset.zw;
+
+				uint2 pixelPos = (uint2)(screenUV * (float2)gViewportRectangle.zw - ((float2)gViewportRectangle.xy + 0.5f));
+				
+				#if MSAA_COUNT > 1
+				SurfaceData surfaceData = getGBufferData(pixelPos, sampleIdx);
+				#else
+				SurfaceData surfaceData = getGBufferData(pixelPos);
+				#endif
+
+				if(surfaceData.worldNormal.w > 0.0f)
+				{
+					// x, y are now in clip space, z, w are in view space
+					// We multiply them by a special inverse view-projection matrix, that had the projection entries that effect
+					// z, w eliminated (since they are already in view space)
+					// Note: Multiply by depth should be avoided if using ortographic projection
+					float4 mixedSpacePos = float4(ndcPos.xy * -surfaceData.depth, surfaceData.depth, 1);
+					float4 worldPosition4D = mul(gMatScreenToWorld, mixedSpacePos);
+					float3 worldPosition = worldPosition4D.xyz / worldPosition4D.w;
+
+					float3 V = normalize(gViewOrigin - worldPosition);
+					float3 N = surfaceData.worldNormal.xyz;
+					float3 R = 2 * dot(V, N) * N - V;
+					float3 specR = getSpecularDominantDir(N, R, surfaceData.roughness);
+					
+					float roughness2 = max(surfaceData.roughness, 0.08f);
+					roughness2 *= roughness2;
+					
+					LightData lightData = getLightData();
+					
+					bool isSpot = gShiftedLightPositionAndType.w > 0.5f;
+					if(isSpot)
+						return float4(getLuminanceSpot(lightData, worldPosition, V, R, roughness2, surfaceData), 1.0f);
+					else // Radial
+						return float4(getLuminanceRadial(lightData, worldPosition, V, R, roughness2, surfaceData), 1.0f);
+				}
+				else
+					return float4(0.0f, 0.0f, 0.0f, 0.0f);
+			}
+		};
+	};
+};

+ 1 - 1
Data/Raw/Engine/Shaders/LightGridLLCreation.bsl

@@ -1,5 +1,5 @@
 #include "$ENGINE$\PerCameraData.bslinc"
-#define USE_LIGHT_GRID_INDICES
+#define USE_LIGHT_GRID_INDICES 1
 #include "$ENGINE$\LightingCommon.bslinc"
 #include "$ENGINE$\ImageBasedLighting.bslinc"
 #include "$ENGINE$\LightGridCommon.bslinc"

+ 5 - 4
Data/Raw/Engine/Shaders/ReflectionCubeImportanceSample.bsl

@@ -23,23 +23,24 @@ Technique
 		{
 			#define PI 3.1415926
 		
-			// From Hacker's Delight
-			float reverseBits(uint bits)  
+			float radicalInverse(uint bits)  
 			{
+				// Reverse bits. Algorithm from Hacker's Delight.
 				bits = (bits << 16u) | (bits >> 16u);
 				bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
 				bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
 				bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
 				bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
 				
-				return float(bits) * 2.3283064365386963e-10; // 0x100000000
+				// Normalizes unsigned int in range [0, 4294967295] to [0, 1]
+				return float(bits) * 2.3283064365386963e-10;
 			}
 			
 			float2 hammersleySequence(uint i, uint count)
 			{
 				float2 output;
 				output.x = i / (float)count;
-				output.y = reverseBits(i);
+				output.y = radicalInverse(i);
 				
 				return output;
 			}

+ 1 - 1
Data/Raw/Engine/Shaders/TiledDeferredImageBasedLighting.bsl

@@ -1,7 +1,7 @@
 #include "$ENGINE$\GBufferInput.bslinc"
 #include "$ENGINE$\PerCameraData.bslinc"
 #include "$ENGINE$\ReflectionCubemapCommon.bslinc"
-#define USE_COMPUTE_INDICES
+#define USE_COMPUTE_INDICES 1
 #include "$ENGINE$\LightingCommon.bslinc"
 #include "$ENGINE$\ImageBasedLighting.bslinc"
 

+ 1 - 1
Data/Raw/Engine/Shaders/TiledDeferredLighting.bsl

@@ -1,6 +1,6 @@
 #include "$ENGINE$\GBufferInput.bslinc"
 #include "$ENGINE$\PerCameraData.bslinc"
-#define USE_COMPUTE_INDICES
+#define USE_COMPUTE_INDICES 1
 #include "$ENGINE$\LightingCommon.bslinc"
 #include "$ENGINE$\ReflectionCubemapCommon.bslinc"
 #include "$ENGINE$\ImageBasedLighting.bslinc"

+ 1 - 1
Data/Raw/Engine/Shaders/Transparent.bsl

@@ -1,7 +1,7 @@
 #include "$ENGINE$\BasePass.bslinc"
 #include "$ENGINE$\LightGridCommon.bslinc"
 #include "$ENGINE$\ReflectionCubemapCommon.bslinc"
-#define USE_LIGHT_GRID_INDICES
+#define USE_LIGHT_GRID_INDICES 1
 #include "$ENGINE$\LightingCommon.bslinc"
 #include "$ENGINE$\ImageBasedLighting.bslinc"
 #include "$ENGINE$\Surface.bslinc"

+ 1 - 1
Source/RenderBeast/Include/BsRendererScene.h

@@ -27,7 +27,7 @@ namespace bs
 		// Cameras and render targets
 		Vector<RendererRenderTarget> renderTargets;
 		Vector<RendererView*> views;
-		UnorderedMap<const Camera*, RendererView*> cameraToView;
+		UnorderedMap<const Camera*, UINT32> cameraToView;
 		
 		// Renderables
 		Vector<RendererObject*> renderables;

+ 6 - 0
Source/RenderBeast/Include/BsShadowRendering.h

@@ -242,6 +242,12 @@ namespace bs { namespace ct
 		/** For each visibile shadow casting light, renders a shadow map from its point of view. */
 		void renderShadowMaps(RendererScene& scene, const FrameInfo& frameInfo);
 
+		/** 
+		 * Renders shadow occlusion values for the specified light, into the currently bound render target. 
+		 * The system uses shadow maps rendered by renderShadowMaps().
+		 */
+		void renderShadowOcclusion(const RendererScene& scene, const RendererLight& light, UINT32 viewIdx);
+
 		/** Changes the default shadow map size. Will cause all shadow maps to be rebuilt. */
 		void setShadowMapSize(UINT32 size);
 	private:

+ 2 - 1
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -347,7 +347,8 @@ namespace bs { namespace ct
 			UINT32 numCameras = (UINT32)cameras.size();
 			for (UINT32 i = 0; i < numCameras; i++)
 			{
-				RendererView* viewInfo = sceneInfo.cameraToView.at(cameras[i]);
+				UINT32 viewIdx = sceneInfo.cameraToView.at(cameras[i]);
+				RendererView* viewInfo = sceneInfo.views[viewIdx];
 				views.push_back(viewInfo);
 			}
 		}

+ 6 - 7
Source/RenderBeast/Source/BsRendererScene.cpp

@@ -41,12 +41,11 @@ namespace bs {	namespace ct
 		view->setPostProcessSettings(camera->getPostProcessSettings());
 		view->updatePerViewBuffer();
 
-		mInfo.cameraToView[camera] = view;
-
-		UINT32 cameraId = (UINT32)mInfo.views.size();
+		UINT32 viewIdx = (UINT32)mInfo.views.size();
 		mInfo.views.push_back(view);
 
-		camera->setRendererId(cameraId);
+		mInfo.cameraToView[camera] = viewIdx;
+		camera->setRendererId(viewIdx);
 
 		updateCameraRenderTargets(camera);
 	}
@@ -99,14 +98,14 @@ namespace bs {	namespace ct
 		}
 		
 		// Last element is the one we want to erase
+		RendererView* view = mInfo.views[mInfo.views.size() - 1];
+		bs_delete(view);
+
 		mInfo.views.erase(mInfo.views.end() - 1);
 
 		auto iterFind = mInfo.cameraToView.find(camera);
 		if(iterFind != mInfo.cameraToView.end())
-		{
-			bs_delete(iterFind->second);
 			mInfo.cameraToView.erase(iterFind);
-		}
 
 		updateCameraRenderTargets(camera);
 	}

+ 38 - 0
Source/RenderBeast/Source/BsShadowRendering.cpp

@@ -364,6 +364,44 @@ namespace bs { namespace ct
 		}
 	}
 
+	void ShadowRendering::renderShadowOcclusion(const RendererScene& scene, const RendererLight& rendererLight, 
+		UINT32 viewIdx)
+	{
+		const Light* light = rendererLight.internal;
+		UINT32 lightIdx = light->getRendererId();
+
+		RenderAPI& rapi = RenderAPI::instance();
+		// TODO - Calculate and set a scissor rectangle for the light
+
+		switch (light->getType())
+		{
+		case LightType::Directional: 
+			// TODO
+			break;
+		case LightType::Radial:
+			{
+				const LightShadows& shadows = mRadialLightShadows[lightIdx];
+				for (UINT32 i = 0; i < shadows.numShadows; ++i)
+				{
+					UINT32 shadowIdx = shadows.startIdx + i;
+					const ShadowInfo& shadowInfo = mShadowInfos[shadowIdx];
+
+					if (shadowInfo.fadePerView[viewIdx] < 0.005f)
+						continue;
+
+					// TODO - Bind shader and necessary parameters
+
+				}
+			}
+			break;
+		case LightType::Spot: 
+			// TODO
+			break;
+		default: 
+			break;
+		}
+	}
+
 	void ShadowRendering::renderCascadedShadowMaps(UINT32 viewIdx, UINT32 lightIdx, RendererScene& scene, 
 		const FrameInfo& frameInfo)
 	{