Browse Source

Improved linear SSR ray trace

BearishSun 8 năm trước cách đây
mục cha
commit
d5f79dd800

+ 16 - 17
Data/Raw/Engine/Includes/RayMarch.bslinc

@@ -7,7 +7,7 @@ mixin RayMarch
 	code
 	{
 		#ifndef NUM_STEPS
-			#define NUM_STEPS 12
+			#define NUM_STEPS 16
 		#endif
 		
 		#ifndef HI_Z
@@ -33,15 +33,18 @@ mixin RayMarch
 			for(int i = 0; i < numSteps; ++i)
 			{
 				float3 rayPos = rayStart + rayStep * t;
-			
+
 				#if HI_Z
 				float sampleDepth = depth.Sample(samp, rayPos.xy).r;
 				#else
 				float sampleDepth = depth.SampleLevel(samp, rayPos.xy, 0).r;
 				#endif
 				
+				// Check if ray is behind an object, but not too much behind otherwise we'll have false positives.
+				// Instead we treat "compareTolerance" as an approximate thickness of the object. Proper
+				// thickness you be calculated by rendering depth buffer for backfaces.
 				float depthDiff = rayPos.z - sampleDepth;
-				bool hit = depthDiff > -compareTolerance;
+				bool hit = abs(depthDiff - compareTolerance) < compareTolerance;
 				if(hit)
 				{
 					// Refine hit using line segment intersection
@@ -129,22 +132,18 @@ mixin RayMarch
 			float4 hiZUVMapping; // From NDC to HiZ UV. .xy - multiply, .zw - add
 			float3 rayOrigin; // World space
 			float3 rayDir; // World space
-			float rayLength;
 			float jitterOffset;
 		};
 	
-		float4 rayMarch(Texture2D depth, SamplerState samp, RayMarchParams params)
+		float4 rayMarch(Texture2D depth, SamplerState samp, RayMarchParams params, out float dbg)
 		{
-			float3 viewOrigin = mul(float4(params.rayOrigin, 1), gMatView);
-			float3 viewDir = mul(float4(params.rayDir, 0), gMatView);
+			dbg = 0.0f;
+		
+			float3 viewOrigin = mul(gMatView, float4(params.rayOrigin, 1));
+			float3 viewDir = mul(gMatView, float4(params.rayDir, 0));
 		
-			// Clip ray length so it doesn't go past the near plane
-			float rayLength = (viewOrigin.z + viewDir.z * params.rayLength) > gNearFar.x 
-				? (gNearFar.x - viewOrigin.z) / viewDir.z 
-				: params.rayLength;
-			
 			float3 ndcStart = viewToNDC(viewOrigin);
-			float3 ndcEnd = viewToNDC(viewOrigin + viewDir * rayLength);
+			float3 ndcEnd = viewToNDC(viewOrigin + viewDir);
 			float3 ndcStep = ndcEnd - ndcStart;
 			
 			// Resize ray so it reaches screen edge
@@ -162,12 +161,12 @@ mixin RayMarch
 			uvStart.z = NDCZToDeviceZ(ndcStart.z);
 			
 			float3 uvStep;
-			uvStep.xy = ndcStep.xy * params.hiZUVMapping.xy + params.hiZUVMapping.zw;
+			uvStep.xy = ndcStep.xy * params.hiZUVMapping.xy;
 			uvStep.z = NDCZToDeviceZ(ndcStep.z);
 		
 			#else
 			float3 uvStart = float3(NDCToUV(ndcStart.xy), NDCZToDeviceZ(ndcStart.z));
-			float3 uvStep = float3(NDCToUV(ndcStep.xy), NDCZToDeviceZ(ndcStep.z));
+			float3 uvStep = float3(ndcStep.xy * gClipToUVScaleOffset.xy, NDCZToDeviceZ(ndcStep.z));
 			#endif
 		
 			float stepIncrement = 1.0f / NUM_STEPS;
@@ -176,7 +175,7 @@ mixin RayMarch
 			float t = stepIncrement + stepIncrement * params.jitterOffset;
 			
 			// Note: Perhaps tweak this value
-			float compareTolerance = uvStep.z * stepIncrement;
+			float compareTolerance = uvStep.z * stepIncrement * 2.0f;
 			
 			// Always do three steps of linear search
 			// (HiZ search is more expensive for short runs)
@@ -191,7 +190,7 @@ mixin RayMarch
 			#else
 			
 			// Plain linear search
-			if(linearSearch(depth, samp, uvStart, uvStep, NUM_STEPS - 3, stepIncrement, compareTolerance, t))
+			if(linearSearch(depth, samp, uvStart, uvStep, NUM_STEPS - 4, stepIncrement, compareTolerance, t))
 				return float4(uvStart + uvStep * t, t);
 			#endif
 			

+ 46 - 7
Data/Raw/Engine/Shaders/PPSSRTrace.bsl

@@ -29,6 +29,19 @@ technique PPSSRTrace
 		
 		Texture2D gSceneColor;
 		SamplerState gSceneColorSamp;
+		
+		float random (float2 st) 
+		{
+			// From https://thebookofshaders.com/10/
+			return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
+		}
+		
+		// Specialized morton code for 4x4 tiles
+		uint mortonCode4x4(uint x, uint y)
+		{
+			return (x & 0x1) 		| ((x << 1) & 0x4)
+				 | (y << 1) & 0x2 	| ((y << 2) & 0x8);
+		}		
 
 		float4 fsmain(VStoFS input, float4 pixelPos : SV_Position) : SV_Target0
 		{
@@ -37,7 +50,13 @@ technique PPSSRTrace
 			SurfaceData surfData = getGBufferData(input.uv0);
 			float3 P = NDCToWorld(input.screenPos, surfData.depth);
 			float3 V = normalize(P - gViewOrigin);
-			float3 N = surfData.worldNormal.xzy;
+			float3 N = surfData.worldNormal.xyz;
+			
+			// TODO - DEBUG ONLY - Only handle reflections on up facing surfaces
+			if(dot(N, float3(0,1,0)) < 0.8)
+				return gSceneColor.Sample(gSceneColorSamp, input.uv0);	
+			else
+				N = float3(0,1,0);
 			
 			// TODO - Allow number of steps and rays be customized using a quality level
 			//  - And HiZ vs linear search
@@ -46,7 +65,16 @@ technique PPSSRTrace
 			//  - Clip BRDF lobe? And renormalize PDF?
 			// TODO - Generate random ray step offset
 			// TODO - Reject rays pointing under the surface
-			float3 R = reflect(-V, N);
+			
+			// Eliminate rays pointing towards the viewer. They won't hit anything, plus they can screw up precision
+			// and cause ray step offset to be too small, causing self-intersections.
+			float3 R = normalize(reflect(V, N));
+			if(dot(R, gViewDir) < 0.0f)
+				return 0.0f;
+			
+			// Jitter ray offset in 4x4 tile, in order to avoid stairstep artifacts
+			uint pixelIdx = mortonCode4x4((uint)pixelPos.x, (uint)pixelPos.y);
+			float jitterOffset = (pixelIdx & 15) / 15.0f - 0.5f; // TODO - Also add per-frame jitter
 			
 			RayMarchParams rayMarchParams;
 			rayMarchParams.bufferSize = gHiZSize;
@@ -54,15 +82,26 @@ technique PPSSRTrace
 			rayMarchParams.hiZUVMapping = gHiZUVMapping;
 			rayMarchParams.rayOrigin = P;
 			rayMarchParams.rayDir = R;
-			rayMarchParams.rayLength = -surfData.depth; // Arbitrary since I resize the ray anyway?
-			rayMarchParams.jitterOffset = 0.0f;
+			rayMarchParams.jitterOffset = jitterOffset;
 			
 			// TODO - Fade based on roughness
 			
-			float4 rayHit = rayMarch(gDepthBufferTex, gDepthBufferSamp, rayMarchParams);
+			float dbg = 0.0f;
+			float4 rayHit = rayMarch(gDepthBufferTex, gDepthBufferSamp, rayMarchParams, dbg);
 			if(rayHit.w < 1.0f) // Hit
-				return gSceneColor.Sample(gSceneColorSamp, rayHit.xy);
-
+			{
+				float4 output = gSceneColor.Sample(gSceneColorSamp, rayHit.xy);
+				
+				// Fade out near screen edges
+				float2 rayHitNDC = UVToNDC(rayHit.xy);
+				float2 vignette = saturate(abs(rayHitNDC) * 5.0f - 4.0f);
+	
+				return output * (1.0f - dot(vignette, vignette));
+			}
+			
+			if(dbg > 0.5f)
+				return float4(1.0f, 0.0f, 0.0f, 1.0f);
+				
 			return 0.0f;
 		}	
 	};