BearishSun 8 лет назад
Родитель
Сommit
ca7ae6ab67

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

@@ -143,6 +143,14 @@
         {
             "Path": "RayMarch.bslinc",
             "UUID": "c64f9bee-c0c2-442e-9f59-35da0494e827"
+        },
+        {
+            "Path": "ColorSpace.bslinc",
+            "UUID": "9fc44e5a-b23f-4002-bd2e-793cf0d03c32"
+        },
+        {
+            "Path": "TemporalResolve.bslinc",
+            "UUID": "b0ce01c2-3326-445e-bf2b-35cf8d9d6e1b"
         }
     ],
     "Shaders": [

+ 37 - 0
Data/Raw/Engine/Includes/ColorSpace.bslinc

@@ -0,0 +1,37 @@
+mixin ColorSpace
+{
+	code
+	{
+		/**
+		 * Converts a color in RGB space into YCoCg color space. Note that Co and Cg
+		 * components are in [-0.5, 0.5] range and therefore cannot be stored as-is
+		 * in a normal color texture.
+		 */
+		float3 RGBToYCoCg(float3 input)
+		{
+			float Y = dot(input, float3(0.25f, 0.5f, 0.25f));
+			float Co = input.r * 0.5f + input.b * -0.5f;
+			float Cg = dot(input, float3(-0.25f, 0.5f, -0.25f));
+			
+			return float3(Y, Co, Cg);
+		}
+		
+		/**
+		 * Converts a color in YCoCg color space into RGB color space.
+		 */
+		float3 YCoCg(float3 input)
+		{
+			float R = input.r + input.g - input.b;
+			float G = input.r + input.b;
+			float B = input.r - input.g - input.b;
+			
+			return float3(R, G, B);
+		}
+		
+		/** Calculates luminance from a color in RGB color space. */
+		float LuminanceRGB(float3 input)
+		{
+			return 0.299f * input.r + 0.587f * input.g + 0.114f * input.b;
+		}
+	};
+};

+ 440 - 0
Data/Raw/Engine/Includes/TemporalResolve.bslinc

@@ -0,0 +1,440 @@
+#include "$ENGINE$/PerCameraData.bslinc"
+#include "$ENGINE$/ColorSpace.bslinc"
+
+mixin PPTemporalResolve
+{
+	mixin PerCameraData;
+	mixin ColorSpace;
+
+	code
+	{
+		////////////////// CUSTOMIZATION PARAMETERS /////////////////////////////
+	
+		// When enabled, the system will sample a specific sample from a MS texture. UV coordinates are assumed
+		// to be in pixel space in that case. When disabled sampleIdx parameter is ignored and UV coordinates
+		// are assumed be in standard [0, 1] range.
+		#ifndef MSAA
+			#define MSAA 0
+		#endif
+	
+		// 0 - System will use the velocity of the current pixel
+		// 1 - System will search 4 neighbor pixels in + pattern, and choose the velocity of the pixel nearest //     to the camera
+		// 2 - System will search 8 surrounding pixels and choose the velocity of the pixel nearest to the camera
+		//
+		// Searching the neighborhod instead of just using current velocity yields nicer edges for objects in 
+		// motion. See TEMPORAL_SEARCH_RADIUS in order to customize how far away to search.
+		//
+		// Only relevant if TEMPORAL_LOCAL_VELOCITY is enabled, since without it no per-object velocity
+		// information is present and everything is blended based on camera movement.
+		#ifndef TEMPORAL_SEARCH_NEAREST
+			#define TEMPORAL_SEARCH_NEAREST 1
+		#endif
+		
+		// Determine how far away to sample pixels when TEMPORAL_SEARCH_NEAREST is enabled. 
+		// 1 - Immediately adjacent pixels are searched
+		// 2 - Pixels two away are searched (looks better than 1)
+		// 3 - etc.
+		#ifndef TEMPORAL_SEARCH_RADIUS
+			#define TEMPORAL_SEARCH_RADIUS 2
+		#endif
+		
+		// 0 - The system will only account for velocity due to camera movement (not due to individual objects)
+		// 1 - The system will account both for velocity due to camera movement, as well as individual object 
+		//     movement. Requires the user to provide a per-pixel velocity buffer.
+		#ifndef TEMPORAL_LOCAL_VELOCITY
+			#define TEMPORAL_LOCAL_VELOCITY 1
+		#endif
+		
+		// When enabled, the resolve operation will be performed in YCoCg color space. This can yield better
+		// results, requires less color samples and no value clipping.
+		#ifndef TEMPORAL_YCOCG
+			#define TEMPORAL_YCOCG 0
+		#endif
+		
+		// When enabled, green color will be used instead of calculating luminosity. This will yield better
+		// performance but can result in lower quality. Ignored when TEMPORAL_YCOCG is enabled, since luminosity
+		// is already available as part of the YCoCg color space.
+		#ifndef TEMPORAL_GREEN_AS_LUMA
+			#define TEMPORAL_GREEN_AS_LUMA 0
+		#endif
+		
+		// When enabled the input samples will be tonemapped using the provided exposure value. Once the final
+		// value is resolved, it will be scaled back into original range. This ensures high frequency data from
+		// HDR content is removed, as it would cause aliasing otherwise. We scale the result back into high range
+		// so the high-quality tonemap shader can be ran on it.
+		#ifndef TEMPORAL_TONEMAP
+			#define TEMPORAL_TONEMAP 1
+		#endif
+	
+		// When enabled an extra low-pass filter is ran when sampling scene color, for better quality.
+		#ifndef TEMPORAL_LOWPASS
+			#define TEMPORAL_LOWPASS 1
+		#endif
+		
+		// When enabled, clamp/clip color neighborhood will be deduced using standard deviation of all the
+		// neighborhood samples. When disabled a min/max operation is performed instead.
+		#ifndef TEMPORAL_SMOOTH_NEIGHBORHOOD
+			#define TEMPORAL_SMOOTH_NEIGHBORHOOD 1
+		#endif
+	
+		////////////////////////// HELPER MACROS /////////////////////////
+		#if MSAA
+			#define _TEX2D(n) Texture2DMS n
+			#define _PTEX2D(n) n
+			#define _SAMPLE(n, uv) n.Load((int2)uv, sampleIdx)
+			#define _SAMPLEOFF(n, uv, offset) n.Load((int2)(uv) + offset)
+			#define _PIXSIZE(n) int2(1, 1)
+		#else
+			#define _TEX2D(n) Texture2D n, SamplerState n##SampState, float n##TexelSize
+			#define _PTEX2D(n) n, n##SampState, n##TexelSize
+			#define _SAMPLE(n, uv) n.Sample(n##SampState, uv)
+			#define _SAMPLEOFF(n, uv, offset) n.Sample(n##SampState, uv, offset)
+			#define _PIXSIZE(n) n##TexelSize
+		#endif
+		
+		///////////////////////// HELPER FUNCTIONS ////////////////////////
+		float3 findNearest3x3(_TEX2D(sceneDepth), float2 uv, int sampleIdx)
+		{
+			int r = TEMPORAL_SEARCH_RADIUS;
+			float3 dmin = float3(0, 0, 1);
+			
+			[unroll]
+			for(int y = -r; y <= r; y += r)
+			{
+				[unroll]
+				for(int x = -r; x <= r; x += r)
+				{
+					float depth = _SAMPLEOFF(sceneDepth, uv, int2(x, y)).x;
+					dmin = depth < dmin.z ? float3(x, y, depth) : dmin;	
+				}
+			}
+			
+			return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
+		}
+		
+		float3 findNearestCross(_TEX2D(sceneDepth), float2 uv, int sampleIdx)
+		{
+			int r = TEMPORAL_SEARCH_RADIUS;
+			float3 dmin = float3(0, 0, 1);
+			
+			{
+				float depth = _SAMPLE(sceneDepth, uv).x;
+				dmin = depth < dmin.z ? float3(0, 0, depth) : dmin;	
+			}
+			
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
+				dmin = depth < dmin.z ? float3(-r, 0, depth) : dmin;	
+			}
+			
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
+				dmin = depth < dmin.z ? float3(r, 0, depth) : dmin;	
+			}
+
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
+				dmin = depth < dmin.z ? float3(0, -r, depth) : dmin;	
+			}
+
+			{
+				float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
+				dmin = depth < dmin.z ? float3(0, r, depth) : dmin;	
+			}			
+			
+			return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
+		}		
+		
+		// Encodes velocity into a format suitable for storing in a 16-bit SNORM texture. 
+		// Velocity range of [-2, 2] is supported (full NDC).
+		float2 encodeVelocity16SNORM(float2 velocity)
+		{
+			return velocity * 0.5f;
+		}
+		
+		// Decodes velocity from an encoded 16-bit SNORM format. See encodeVelocity16SNORM().
+		// Velocity range of [-2, 2] is supported (full NDC).
+		float2 decodeVelocity16SNORM(float2 val)
+		{
+			return val * 2.0f;
+		}
+		
+		////////////////////// HDR SCALING HELPER FUNCTIONS ///////////////////
+		// HDR scaling methods use luminance based scaling, instead of a tonemap operator, as
+		// described here:
+		// http://graphicrants.blogspot.hr/2013/12/tone-mapping.html
+		float HDRScaleWeight(float luma, float exposureScale)
+		{
+			return rcp(1 + luma * exposureScale);
+		}
+		
+		float HDRScaleWeightInv(float luma, float exposureScale)
+		{
+			return rcp(1 - luma * exposureScale);
+		}		
+		
+		float3 HDRScaleRGB(float3 v, float exposureScale)
+		{
+			float luma = LuminanceRGB(v);
+			
+			return v * HDRScaleWeight(luma, exposureScale);
+		}
+		
+		float3 HDRScaleY(float3 v, float exposureScale)
+		{
+			float luma = v.r;
+			
+			return v * HDRScaleWeight(luma, exposureScale);
+		}
+		
+		float3 HDRScaleG(float3 v, float exposureScale)
+		{
+			float luma = v.g;
+			
+			return v * HDRScaleWeight(luma, exposureScale);
+		}
+
+		float3 HDRScaleRGBInv(float3 v, float exposureScale)
+		{
+			float luma = LuminanceRGB(v);
+			
+			return v * HDRScaleWeightInv(luma, exposureScale);
+		}
+		
+		float3 HDRScaleYInv(float3 v, float exposureScale)
+		{
+			float luma = v.r;
+			
+			return v * HDRScaleWeightInv(luma, exposureScale);
+		}
+		
+		float3 HDRScaleGInv(float3 v, float exposureScale)
+		{
+			float luma = v.g;
+			
+			return v * HDRScaleWeightInv(luma, exposureScale);
+		}		
+		
+		////////////////////// HELPER TONEMAP/COLOR SPACE DEFINES /////////////////////
+		// Automatically scale HDR values based on luminance, if enabled
+		#if TEMPORAL_TONEMAP
+			#if TEMPORAL_YCOCG
+				#define _TONEMAP_COLOR(v) HDRScaleY(v, exposureScale)
+			#elif TEMPORAL_GREEN_AS_LUMA
+				#define _TONEMAP_COLOR(v) HDRScaleG(v, exposureScale)
+			#else
+				#define _TONEMAP_COLOR(v) HDRScaleRGB(v, exposureScale)
+			#endif
+		#else // TEMPORAL_TONEMAP
+			#define _TONEMAP_COLOR(v) v
+		#endif // TEMPORAL_TONEMAP
+		
+		#if TEMPORAL_TONEMAP
+			#if TEMPORAL_YCOCG
+				#define _TONEMAP_COLOR_INV(v) HDRScaleYInv(v, exposureScale)
+			#elif TEMPORAL_GREEN_AS_LUMA
+				#define _TONEMAP_COLOR_INV(v) HDRScaleGInv(v, exposureScale)
+			#else
+				#define _TONEMAP_COLOR_INV(v) HDRScaleRGBInv(v, exposureScale)
+			#endif
+		#else // TEMPORAL_TONEMAP
+			#define _TONEMAP_COLOR_INV(v) v
+		#endif // TEMPORAL_TONEMAP		
+		
+		// Automatically convert from/to YCoCg space, if enabled
+		#if TEMPORAL_YCOCG
+			#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(RGBToYCoCg(_SAMPLEOFF(n, uv, offset)))
+		#else // TEMPORAL_YCOCG
+			#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(_SAMPLEOFF(n, uv, offset))
+		#endif // TEMPORAL_YCOCG
+				
+		#if TEMPORAL_YCOCG
+			#define _RESOLVE_COLOR(v) _TONEMAP_COLOR_INV(YCoCgToRGB(v))
+		#else // TEMPORAL_YCOCG
+			#define _RESOLVE_COLOR(v) _TONEMAP_COLOR_INV(v)
+		#endif // TEMPORAL_YCOCG
+		
+		///////////////////////////// MAIN /////////////////////////////////
+		[internal]
+		cbuffer TemporalInput
+		{
+			float gSampleWeights[9];
+			float gSampleWeightsLowpass[9];
+		}
+		
+		// TODO - Scene depth sampler must be POINT. Also CLAMP so neighbor search is clamped.
+		// TODO - UV must be in pixels for MSAA
+		// TODO - Need to use CLAMP for scene color, so neighbor search is clamped.
+		// TODO - Need to use SNORM 16-bit format for velocity
+		// TODO - Add gNDCToPrevNDC matrix to PerCameraData
+		// TODO - Generate C++ samples (make sure to account for YCoCg path, and remove jitter)
+		float3 temporalResolve(
+			_TEX2D(sceneDepth), 
+			_TEX2D(sceneColor), 
+			_TEX2D(prevColor), 
+			#if TEMPORAL_LOCAL_VELOCITY
+			_TEX2D(velocityBuffer),
+			#endif // TEMPORAL_LOCAL_VELOCITY
+			#if TEMPORAL_TONEMAP
+			float exposureScale,
+			#endif // TEMPORAL_TONEMAP
+			float2 uv,
+			float2 ndcPos, // Can be derived from UV, but we usually have it for free, so pass it directly
+			int sampleIdx)
+		{
+			///////////// DETERMINE PER-PIXEL VELOCITY & CURRENT DEPTH ///////////////////
+			float curDepth;
+			float2 velocity;
+			#if TEMPORAL_LOCAL_VELOCITY
+				#if TEMPORAL_SEARCH_NEAREST == 1
+					float3 nearest = findNearestCross(_PTEX2D(sceneDepth), uv, sampleIdx);
+					velocity = _SAMPLE(velocityBuffer, nearest.xy);
+					curDepth = nearest.z;
+				#elif TEMPORAL_SEARCH_NEAREST == 2
+					float3 nearest = findNearest3x3(_PTEX2D(sceneDepth), uv, sampleIdx);
+					velocity = _SAMPLE(velocityBuffer, nearest.xy);
+					curDepth = nearest.z;
+				#else // TEMPORAL_SEARCH_NEAREST
+					velocity = _SAMPLE(velocityBuffer, uv);
+					curDepth = _SAMPLE(sceneDepth, uv).x;
+				#endif // TEMPORAL_SEARCH_NEAREST
+			#else // TEMPORAL_LOCAL_VELOCITY
+				velocity = 0;
+				curDepth = _SAMPLE(sceneDepth, uv).x;
+			#endif // TEMPORAL_LOCAL_VELOCITY
+			
+			///////////////////// DETERMINE PREV. FRAME UV //////////////////////////////
+			float2 prevNdcPos;
+			bool hasLocalVelocity = (abs(velocity.x) + abs(velocity.y)) > 0;
+			if(hasLocalVelocity)
+			{
+				velocity = decodeVelocity16SNORM(velocity);
+				prevNdcPos = float3(ndcPos - velocity);
+			}
+			else
+			{
+				// Assumes velocity due to camera movement
+				float4 currentNDC = float4(ndcPos, curDepth, 1);
+				float4 prevClip = mul(gNDCToPrevNDC, currentNDC);
+				prevNdcPos = prevClip.xy / prevClip.w;
+			}
+			
+			#if MSAA
+			float prevUV = NDCToScreen(prevNdcPos);
+			#else
+			float2 prevUV = NDCToUV(prevNdcPos);
+			#endif
+			
+			/////////////// GET FILTERED COLOR VALUE AND NEIGHBORHOOD MIN/MAX /////////////
+			#if TEMPORAL_YCOCG
+			float3 neighbor[5];
+			neighbor[0] = _SAMPLE_COLOR(sceneColor, uv, int2(-1,  0));
+			neighbor[1] = _SAMPLE_COLOR(sceneColor, uv, int2( 0, -1));
+			neighbor[2] = _SAMPLE_COLOR(sceneColor, uv, int2( 0,  0));
+			neighbor[3] = _SAMPLE_COLOR(sceneColor, uv, int2( 1,  0));
+			neighbor[4] = _SAMPLE_COLOR(sceneColor, uv, int2( 0,  1));
+			
+			float3 filtered = 0;
+			[unroll]
+			for(uint i = 0; i < 5; ++i)
+				filtered += neighbor[i] * gSampleWeights[i];
+			
+			float3 filteredLow = filtered;
+			
+			float3 neighborMin = min(min(min(neighbor[0], neighbor[1]), min(neighbor[2], neighbor[3])), 
+				neighbor[4]);
+				
+			float3 neighborMax = max(max(max(neighbor[0], neighbor[1]), max(neighbor[2], neighbor[3])), 
+				neighbor[4]);
+			
+			#else // TEMPORAL_YCOCG
+			float3 neighbor[9];
+			neighbor[0] = _SAMPLE_COLOR(sceneColor, uv, int2(-1, -1));
+			neighbor[1] = _SAMPLE_COLOR(sceneColor, uv, int2( 0, -1));
+			neighbor[2] = _SAMPLE_COLOR(sceneColor, uv, int2( 1, -1));
+			neighbor[3] = _SAMPLE_COLOR(sceneColor, uv, int2(-1,  0));
+			neighbor[4] = _SAMPLE_COLOR(sceneColor, uv, int2( 0,  0));
+			neighbor[5] = _SAMPLE_COLOR(sceneColor, uv, int2( 1,  0));
+			neighbor[6] = _SAMPLE_COLOR(sceneColor, uv, int2(-1,  1));
+			neighbor[7] = _SAMPLE_COLOR(sceneColor, uv, int2( 0,  1));
+			neighbor[8] = _SAMPLE_COLOR(sceneColor, uv, int2( 1,  1));
+			
+			float3 filtered = 0;
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+				filtered += neighbor[i] * gSampleWeights[i];
+			
+			#if TEMPORAL_LOWPASS
+				float3 filteredLow = 0;
+				[unroll]
+				for(uint i = 0; i < 9; ++i)
+					filteredLow += neighbor[i] * gSampleWeightsLowpass[i];
+			#else
+				float3 filteredLow = filtered;
+			#endif // TEMPORAL_LOWPASS
+			
+			#if TEMPORAL_SMOOTH_NEIGHBORHOOD
+			
+			float3 mean = 0;
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+					mean += neighbor[i];
+			
+			mean /= 9.0f;
+			
+			float3 meanSqrd = 0;
+			[unroll]
+			for(uint i = 0; i < 9; ++i)
+				meanSqrd += neighbor[i] * neighbor[i];
+			
+			meanSqrd /= 9.0f;
+			
+			float3 stdDev = sqrt(abs(meanSqrd - mean * mean));
+			float3 neighborMin = mean - stdDev;
+			float3 neighborMax = mean + stdDev;
+			
+			#else // TEMPORAL_SMOOTH_NEIGHBORHOOD
+			float3 neighborMin = min(min(
+				min(min(neighbor[0], neighbor[1]), min(neighbor[2], neighbor[3])), 
+				min(min(neighbor[4], neighbor[5]), min(neighbor[6], neighbor[7]))), 
+				neighbor[8]);
+				
+			float3 neighborMax = max(max(
+				max(max(neighbor[0], neighbor[1]), max(neighbor[2], neighbor[3])), 
+				max(max(neighbor[4], neighbor[5]), max(neighbor[6], neighbor[7]))), 
+				neighbor[8]);
+			
+			#endif // TEMPORAL_SMOOTH_NEIGHBORHOOD
+			#endif // TEMPORAL_YCOCG
+			
+			/////////////////// GET PREVIOUS FRAME COLOR ///////////////////////
+			float3 prevColorVal = _SAMPLE_COLOR(prevColor, prevUV, int2(0, 0));
+			
+			// TODO - Do clamp
+			//  - Either basic or AABB based
+			
+			// TODO - Sharpen
+			//  - Needs more research
+			
+			// TODO - Blend
+			//  - Determine blend value based on colors, or use a fixed amount
+			//  - Reverse YCOCG and tonemap as required
+			
+			// TODO - Limbo implementation does unjitter operation in specific places, do I need to as well?
+			//  - This seems to be applied during sample generation
+			
+			// TODO - Not doing (investigate if needed):
+			//  - Sharpen
+			//  - AA border clamp
+		}
+		
+		#undef _TEX2D
+		#undef _PTEX2D
+		#undef _SAMPLE
+		#undef _PIXSIZE
+		#undef _TONEMAP_COLOR
+		#undef _TONEMAP_COLOR_INV
+		#undef _SAMPLE_COLOR
+		#undef _RESOLVE_COLOR
+	};
+};