Parcourir la source

More work on temporal resolve shader

BearishSun il y a 8 ans
Parent
commit
55e942d2fa

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

@@ -337,6 +337,10 @@
         {
             "Path": "PPSSRStencil.bsl",
             "UUID": "a9eb5ddf-1196-48d8-9403-a80a9595cde0"
+        },
+        {
+            "Path": "PPSSRResolve.bsl",
+            "UUID": "86b9699c-162d-4729-9985-c94e0f45bacb"
         }
     ],
     "Skin": [

+ 4 - 0
Data/Raw/Engine/Includes/PerCameraData.bslinc

@@ -17,6 +17,10 @@ mixin PerCameraData
 			// and view_z/view_w in view space, into world space				
 			float4x4 gMatScreenToWorld;
 			
+			// Transforms a location in NDC, to the location of the same pixel on the previous frame. Used for
+			// determining camera movement for temporal filtering
+			float4x4 gNDCToPrevNDC;			
+			
 			// Converts device Z to world Z using this formula: worldZ = (1 / (deviceZ + y)) * x
 			float2 	 gDeviceZToWorldZ;
 			float2	 gNDCZToWorldZ;

+ 6 - 8
Data/Raw/Engine/Includes/TemporalResolve.bslinc

@@ -1,7 +1,7 @@
 #include "$ENGINE$/PerCameraData.bslinc"
 #include "$ENGINE$/ColorSpace.bslinc"
 
-mixin PPTemporalResolve
+mixin TemporalResolve
 {
 	mixin PerCameraData;
 	mixin ColorSpace;
@@ -112,7 +112,7 @@ mixin PPTemporalResolve
 			#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 _TEX2D(n) Texture2D n, SamplerState n##SampState, float2 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)
@@ -278,9 +278,9 @@ mixin PPTemporalResolve
 		
 		// Automatically convert from/to YCoCg space, if enabled
 		#if TEMPORAL_YCOCG
-			#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(RGBToYCoCg(_SAMPLEOFF(n, uv, offset)))
+			#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(RGBToYCoCg(_SAMPLEOFF(n, uv, offset).rgb))
 		#else // TEMPORAL_YCOCG
-			#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(_SAMPLEOFF(n, uv, offset))
+			#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(_SAMPLEOFF(n, uv, offset).rgb)
 		#endif // TEMPORAL_YCOCG
 						
 		///////////////////////////// MAIN /////////////////////////////////
@@ -291,12 +291,10 @@ mixin PPTemporalResolve
 			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)
+		// TODO - Add notes that velocity buffer isn't currenlty being used
 		float3 temporalResolve(
 			_TEX2D(sceneDepth), 
 			_TEX2D(sceneColor), 
@@ -338,7 +336,7 @@ mixin PPTemporalResolve
 			if(hasLocalVelocity)
 			{
 				velocity = decodeVelocity16SNORM(velocity);
-				prevNdcPos = float3(ndcPos - velocity);
+				prevNdcPos = float2(ndcPos - velocity);
 			}
 			else
 			{

+ 65 - 0
Data/Raw/Engine/Shaders/PPSSRResolve.bsl

@@ -0,0 +1,65 @@
+#include "$ENGINE$\PPBase.bslinc"
+#include "$ENGINE$\GBufferInput.bslinc"
+#include "$ENGINE$\PerCameraData.bslinc"
+
+#define TEMPORAL_LOCAL_VELOCITY 0
+#define TEMPORAL_SEARCH_NEAREST 0
+#define TEMPORAL_BLEND_FACTOR 8
+#include "$ENGINE$\TemporalResolve.bslinc"
+
+technique PPSSRStencil
+{
+	mixin PPBase;
+	mixin PerCameraData;
+	mixin TemporalResolve;
+
+	code
+	{
+		[internal]
+		cbuffer Input
+		{
+			float2 gSceneDepthTexelSize;
+			float2 gSceneColorTexelSize;
+		
+			float gManualExposure;
+		}
+		
+		#if MSAA
+			Texture2DMS gSceneDepth;
+			Texture2DMS gSceneColor;
+			Texture2DMS gPrevColor;
+		#else
+			Texture2D gSceneDepth;
+			Texture2D gSceneColor;
+			Texture2D gPrevColor;
+
+			SamplerState gPointSampler;
+			SamplerState gLinearSampler;
+		#endif		
+		
+		#if EYE_ADAPTATION
+			Texture2D gEyeAdaptationTex;
+		#endif
+		
+		float3 fsmain(VStoFS input) : SV_Target0
+		{
+			float exposureScale;
+			#if EYE_ADAPTATION
+				exposureScale = gEyeAdaptationTex.Load(int3(0, 0, 0)).r;
+			#else
+				exposureScale = gManualExposure;
+			#endif
+		
+			#if MSAA
+				return temporalResolve(gSceneDepth, gSceneColor, gPrevColor, 
+					exposureScale, input.uv0, input.screenPos, 0);
+			#else
+				return temporalResolve(
+					gSceneDepth, gPointSampler, gSceneDepthTexelSize,
+					gSceneColor, gPointSampler, gSceneColorTexelSize, 
+					gPrevColor, gLinearSampler, gSceneColorTexelSize,
+					exposureScale, input.uv0, input.screenPos, 0);
+			#endif
+		}	
+	};
+};

+ 53 - 2
Source/RenderBeast/Include/BsPostProcessing.h

@@ -823,10 +823,10 @@ namespace bs { namespace ct
 		 * 
 		 * @param[in]	view			Information about the view we're rendering from.
 		 * @param[in]	settings		Parameters used for controling the SSR effect.
-		 * @param[in]	destination		Output texture to which to write the results to.
+		 * @param[in]	destination		Render target to which to write the results to.
 		 */
 		void execute(const RendererView& view, const ScreenSpaceReflectionsSettings& settings, 
-			const SPtr<RenderTexture>& destination);
+			const SPtr<RenderTarget>& destination);
 
 		/**
 		 * Calculates a scale & bias that is used for transforming roughness into a fade out value. Anything that is below
@@ -840,6 +840,57 @@ namespace bs { namespace ct
 		GpuParamTexture mSceneColorTexture;
 	};
 
+	BS_PARAM_BLOCK_BEGIN(TemporalResolveParamDef)
+		BS_PARAM_BLOCK_ENTRY_ARRAY(float, gSampleWeights, 9)
+		BS_PARAM_BLOCK_ENTRY_ARRAY(float, gSampleWeightsLowpass, 9)
+	BS_PARAM_BLOCK_END
+
+	extern TemporalResolveParamDef gTemporalResolveParamDef;
+
+	BS_PARAM_BLOCK_BEGIN(SSRResolveParamDef)
+		BS_PARAM_BLOCK_ENTRY(Vector2, gSceneDepthTexelSize)
+		BS_PARAM_BLOCK_ENTRY(Vector2, gSceneColorTexelSize)
+		BS_PARAM_BLOCK_ENTRY(float, gManualExposure)
+	BS_PARAM_BLOCK_END
+
+	extern SSRResolveParamDef gSSRResolveParamDef;
+
+	/** 
+	 * Shader used for combining SSR information from the previous frame, in order to yield better quality. 
+	 * 
+	 * @tparam	EyeAdaptation	When true the shader will expect a texture containing an exposure value calculated by
+	 *							the eye adaptation shader. Otherwise the manually provided exposure value is used instead.
+	 */
+	template<bool EyeAdaptation>
+	class SSRResolveMat : public RendererMaterial<SSRResolveMat<EyeAdaptation>>
+	{
+		RMAT_DEF("PPSSRResolve.bsl");
+
+	public:
+		SSRResolveMat();
+
+		/** 
+		 * Renders the effect with the provided parameters. 
+		 * 
+		 * @param[in]	view			Information about the view we're rendering from.
+		 * @param[in]	prevFrame		SSR data calculated previous frame.
+		 * @param[in]	curFrame		SSR data calculated this frame.
+		 * @param[in]	sceneDepth		Buffer containing scene depth.
+		 * @param[in]	destination		Render target to which to write the results to.
+		 */
+		void execute(const RendererView& view, const SPtr<Texture>& prevFrame, const SPtr<Texture>& curFrame, 
+			const SPtr<Texture>& sceneDepth, const SPtr<RenderTarget>& destination);
+
+	private:
+		SPtr<GpuParamBlockBuffer> mSSRParamBuffer;
+		SPtr<GpuParamBlockBuffer> mTemporalParamBuffer;
+
+		GpuParamTexture mSceneColorTexture;
+		GpuParamTexture mPrevColorTexture;
+		GpuParamTexture mSceneDepthTexture;
+		GpuParamTexture mEyeAdaptationTexture;
+	};
+
 	/**
 	 * Renders post-processing effects for the provided render target.
 	 *

+ 5 - 3
Source/RenderBeast/Include/BsRendererView.h

@@ -29,6 +29,7 @@ namespace bs { namespace ct
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvProj)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvViewProj)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatScreenToWorld)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gNDCToPrevNDC)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gDeviceZToWorldZ)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gNDCZToWorldZ)
 		BS_PARAM_BLOCK_ENTRY(Vector2, gNDCZToDeviceZ)
@@ -126,6 +127,7 @@ namespace bs { namespace ct
 		RendererViewProperties(const RENDERER_VIEW_DESC& src);
 
 		Matrix4 viewProjTransform;
+		Matrix4 prevViewProjTransform;
 
 		SPtr<RenderTarget> target;
 		Rect2I viewRect;
@@ -192,15 +194,15 @@ namespace bs { namespace ct
 		Camera* getSceneCamera() const { return mCamera; }
 
 		/** 
-		 * Prepares render targets for rendering. When done call endRendering().
+		 * Prepares render targets for rendering. When done call endFrame().
 		 *
 		 * @param[in]	useGBuffer			Set to true if you will be rendering to internal render targets containing the
 		 *									GBuffer (retrieved via getRenderTargets()).
 		 */
-		void beginRendering(bool useGBuffer);
+		void beginFrame(bool useGBuffer);
 
 		/** Ends rendering and frees any acquired resources. */
-		void endRendering();
+		void endFrame();
 
 		/** Returns the view's renderTargets. Only valid if called in-between beginRendering() and endRendering() calls. */
 		SPtr<RenderTargets> getRenderTargets() const { return mRenderTargets; }

+ 192 - 2
Source/RenderBeast/Source/BsPostProcessing.cpp

@@ -1631,10 +1631,12 @@ namespace bs { namespace ct
 		:mGBufferParams(mMaterial, mParamsSet)
 	{
 		mParamBuffer = gSSRTraceParamDef.createBuffer();
-		mParamsSet->setParamBlockBuffer("Input", mParamBuffer);
 
 		SPtr<GpuParams> gpuParams = mParamsSet->getGpuParams();
 		gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSceneColor", mSceneColorTexture);
+
+		if(gpuParams->hasParamBlock(GPT_FRAGMENT_PROGRAM, "Input"))
+			gpuParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "Input", mParamBuffer);
 	}
 
 	void SSRTraceMat::_initDefines(ShaderDefines& defines)
@@ -1643,7 +1645,7 @@ namespace bs { namespace ct
 	}
 
 	void SSRTraceMat::execute(const RendererView& view, const ScreenSpaceReflectionsSettings& settings, 
-		const SPtr<RenderTexture>& destination)
+		const SPtr<RenderTarget>& destination)
 	{
 		const RendererViewProperties& viewProps = view.getProperties();
 		RenderTargets& renderTargets = *view.getRenderTargets();
@@ -1699,6 +1701,172 @@ namespace bs { namespace ct
 		return scaleBias;
 	}
 
+	TemporalResolveParamDef gTemporalResolveParamDef;
+	SSRResolveParamDef gSSRResolveParamDef;
+
+	template<bool EyeAdaptation>
+	SSRResolveMat<EyeAdaptation>::SSRResolveMat()
+	{
+		mSSRParamBuffer = gSSRResolveParamDef.createBuffer();
+		mTemporalParamBuffer = gTemporalResolveParamDef.createBuffer();
+
+		SPtr<GpuParams> gpuParams = mParamsSet->getGpuParams();
+		gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSceneDepth", mSceneDepthTexture);
+		gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSceneColor", mSceneColorTexture);
+		gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gPrevColor", mPrevColorTexture);
+
+		gpuParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "Input", mSSRParamBuffer);
+		gpuParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "TemporalInput", mTemporalParamBuffer);
+
+		SAMPLER_STATE_DESC pointSampDesc;
+		pointSampDesc.minFilter = FO_POINT;
+		pointSampDesc.magFilter = FO_POINT;
+		pointSampDesc.mipFilter = FO_POINT;
+		pointSampDesc.addressMode.u = TAM_CLAMP;
+		pointSampDesc.addressMode.v = TAM_CLAMP;
+		pointSampDesc.addressMode.w = TAM_CLAMP;
+
+		SPtr<SamplerState> pointSampState = SamplerState::create(pointSampDesc);
+		gpuParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gPointSampler", pointSampState);
+
+		SAMPLER_STATE_DESC linearSampDesc;
+		linearSampDesc.minFilter = FO_POINT;
+		linearSampDesc.magFilter = FO_POINT;
+		linearSampDesc.mipFilter = FO_POINT;
+		linearSampDesc.addressMode.u = TAM_CLAMP;
+		linearSampDesc.addressMode.v = TAM_CLAMP;
+		linearSampDesc.addressMode.w = TAM_CLAMP;
+
+		SPtr<SamplerState> linearSampState = SamplerState::create(linearSampDesc);
+		gpuParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gLinearSampler", linearSampState);
+	}
+
+	template<bool EyeAdaptation>
+	void SSRResolveMat<EyeAdaptation>::_initDefines(ShaderDefines& defines)
+	{
+		defines.set("EYE_ADAPTATION", EyeAdaptation ? 1 : 0);
+		defines.set("MSAA", 0);
+	}
+
+	template <bool EyeAdaptation>
+	void SSRResolveMat<EyeAdaptation>::execute(const RendererView& view, const SPtr<Texture>& prevFrame, 
+		const SPtr<Texture>& curFrame, const SPtr<Texture>& sceneDepth, const SPtr<RenderTarget>& destination)
+	{
+		// TODO - MSAA not supported (remember UV must be in pixels) 
+		
+		// Note: This shader should not be called when temporal AA is turned on
+		// Note: This shader doesn't have velocity texture enabled and will only account for camera movement (can be easily
+		//		 enabled when velocity texture is added)
+		//   - WHen added, velocity should use a 16-bit SNORM format
+
+		mPrevColorTexture.set(prevFrame);
+		mSceneColorTexture.set(curFrame);
+		mSceneDepthTexture.set(sceneDepth);
+
+		if(EyeAdaptation)
+		{
+			// TODO - Set eye adaptation texture
+		}
+
+		auto& colorProps = curFrame->getProperties(); // Assuming prev and current frame are the same size
+		auto& depthProps = sceneDepth->getProperties();
+
+		Vector2 colorPixelSize(1.0f / colorProps.getWidth(), 1.0f / colorProps.getHeight());
+		Vector2 depthPixelSize(1.0f / depthProps.getWidth(), 1.0f / depthProps.getHeight());
+
+		gSSRResolveParamDef.gSceneColorTexelSize.set(mSSRParamBuffer, colorPixelSize);
+		gSSRResolveParamDef.gSceneDepthTexelSize.set(mSSRParamBuffer, depthPixelSize);
+		// TODO - Set manual exposure value
+
+		// Generate samples
+		// Note: Move this code to a more general spot where it can be used by other temporal shaders.
+		
+		float sampleWeights[9];
+		float sampleWeightsLowPass[9];
+
+		float totalWeights = 0.0f;
+		float totalWeightsLowPass = 0.0f;
+
+		Vector2 jitter(BsZero); // Only relevant for general case, not using this type of jitter for SSR
+
+		// Weights are generated using an exponential fit to Blackman-Harris 3.3
+		bool useYCoCg = false; // Only relevant for general case, not using it for SSR
+		float sharpness = 1.0f; // Make this a customizable parameter eventually
+		if(useYCoCg)
+		{
+			static const Vector2 sampleOffsets[] = 
+			{
+				{  0.0f, -1.0f },
+				{ -1.0f,  0.0f },
+				{  0.0f,  0.0f },
+				{  1.0f,  0.0f },
+				{  0.0f,  1.0f },
+			};
+
+			for (UINT32 i = 0; i < 5; ++i)
+			{
+				// Get rid of jitter introduced by the projection matrix
+				Vector2 offset = sampleOffsets[i] - jitter;
+
+				offset *= 1.0f + sharpness * 0.5f;
+				sampleWeights[i] = exp(-2.29f * offset.dot(offset));
+				totalWeights += sampleWeights[i];
+			}
+
+			for (UINT32 i = 5; i < 9; ++i)
+				sampleWeights[i] = 0.0f;
+			
+			memset(sampleWeightsLowPass, 0, sizeof(sampleWeightsLowPass));
+			totalWeightsLowPass = 1.0f;
+		}
+		else
+		{
+			static const Vector2 sampleOffsets[] = 
+			{
+				{ -1.0f, -1.0f },
+				{  0.0f, -1.0f },
+				{  1.0f, -1.0f },
+				{ -1.0f,  0.0f },
+				{  0.0f,  0.0f },
+				{  1.0f,  0.0f },
+				{ -1.0f,  1.0f },
+				{  0.0f,  1.0f },
+				{  1.0f,  1.0f },
+			};
+
+			for (UINT32 i = 0; i < 9; ++i)
+			{
+				// Get rid of jitter introduced by the projection matrix
+				Vector2 offset = sampleOffsets[i] - jitter;
+
+				offset *= 1.0f + sharpness * 0.5f;
+				sampleWeights[i] = exp(-2.29f * offset.dot(offset));
+				totalWeights += sampleWeights[i];
+
+				// Low pass
+				offset *= 0.25f;
+				sampleWeightsLowPass[i] = exp(-2.29f * offset.dot(offset));
+				totalWeightsLowPass += sampleWeightsLowPass[i];
+			}
+		}
+
+		for (UINT32 i = 0; i < 9; ++i)
+		{
+			gTemporalResolveParamDef.gSampleWeights.set(mTemporalParamBuffer, sampleWeights[i] / totalWeights);
+			gTemporalResolveParamDef.gSampleWeightsLowpass.set(mTemporalParamBuffer, sampleWeightsLowPass[i] / totalWeightsLowPass);
+		}
+		
+		SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
+		mParamsSet->setParamBlockBuffer("PerCamera", perView);
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(destination);
+
+		gRendererUtility().setPass(mMaterial);
+		gRendererUtility().setPassParams(mParamsSet);
+		gRendererUtility().drawScreenQuad();
+	}
+
 	void PostProcessing::postProcess(RendererView* viewInfo, const SPtr<RenderTargets>& renderTargets, float frameDelta)
 	{
 		auto& viewProps = viewInfo->getProperties();
@@ -1747,6 +1915,21 @@ namespace bs { namespace ct
 			autoExposure = false;
 		}
 
+
+
+
+
+		//// DEBUG ONLY
+		//SSRTraceMat ssrTrace;
+
+		//renderTargets->allocate(RTT_ResolvedSceneColorSecondary);
+		//SPtr<RenderTarget> target = renderTargets->getRT(RTT_ResolvedSceneColorSecondary);
+
+		//ssrTrace.execute(*viewInfo, ppInfo.settings->screenSpaceReflections, target);
+
+		//RenderAPI::instance().setRenderTarget(renderTargets->getRT(RTT_ResolvedSceneColor));
+		//gRendererUtility().blit(renderTargets->get(RTT_ResolvedSceneColorSecondary));
+
 		bool performDOF = GaussianDOF::requiresDOF(settings.depthOfField);
 
 		SPtr<RenderTarget> tonemapTarget;
@@ -1760,6 +1943,13 @@ namespace bs { namespace ct
 
 		mTonemapping.execute(gammaOnly, autoExposure, msaa, sceneColor, tonemapTarget, viewportRect, ppInfo);
 
+		//renderTargets->release(RTT_ResolvedSceneColorSecondary);
+
+
+		//// DEBUG ONLY
+		//return;
+
+
 		if(performDOF)
 		{
 			SPtr<RenderTarget> dofTarget;

+ 4 - 4
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -452,7 +452,7 @@ namespace bs { namespace ct
 			}
 		}
 
-		viewInfo->beginRendering(true);
+		viewInfo->beginFrame(true);
 
 		// Prepare light grid required for transparent object rendering
 		mLightGrid->updateGrid(*viewInfo, *mVisibleLightInfo, *mVisibleReflProbeInfo, viewProps.noLighting);
@@ -753,7 +753,7 @@ namespace bs { namespace ct
 			}
 		}
 
-		viewInfo->endRendering();
+		viewInfo->endFrame();
 
 		gProfilerCPU().endSample("Render");
 	}
@@ -763,7 +763,7 @@ namespace bs { namespace ct
 		gProfilerCPU().beginSample("RenderOverlay");
 
 		viewInfo->getPerViewBuffer()->flushToGPU();
-		viewInfo->beginRendering(false);
+		viewInfo->beginFrame(false);
 
 		auto& viewProps = viewInfo->getProperties();
 		const Camera* camera = viewInfo->getSceneCamera();
@@ -808,7 +808,7 @@ namespace bs { namespace ct
 			++iterRenderCallback;
 		}
 
-		viewInfo->endRendering();
+		viewInfo->endFrame();
 
 		gProfilerCPU().endSample("RenderOverlay");
 	}

+ 1 - 1
Source/RenderBeast/Source/BsRenderTargets.cpp

@@ -472,7 +472,7 @@ namespace bs { namespace ct
 		case RTT_ResolvedSceneColor:
 			return mResolvedSceneColorTex1;
 		case RTT_ResolvedSceneColorSecondary:
-			return mResolvedSceneColorTex1;
+			return mResolvedSceneColorTex2;
 		case RTT_HiZ:
 			return mHiZ;
 		case RTT_ResolvedDepth:

+ 7 - 2
Source/RenderBeast/Source/BsRendererView.cpp

@@ -82,6 +82,7 @@ namespace bs { namespace ct
 		: mProperties(desc), mTargetDesc(desc.target), mCamera(desc.sceneCamera), mUsingGBuffer(false)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
+		mProperties.prevViewProjTransform = mProperties.viewProjTransform;
 
 		setStateReductionMode(desc.stateReduction);
 	}
@@ -135,7 +136,7 @@ namespace bs { namespace ct
 		setStateReductionMode(desc.stateReduction);
 	}
 
-	void RendererView::beginRendering(bool useGBuffer)
+	void RendererView::beginFrame(bool useGBuffer)
 	{
 		if (useGBuffer)
 		{
@@ -152,8 +153,11 @@ namespace bs { namespace ct
 		}
 	}
 
-	void RendererView::endRendering()
+	void RendererView::endFrame()
 	{
+		// Save view-projection matrix to use for temporal filtering
+		mProperties.prevViewProjTransform = mProperties.viewProjTransform;
+
 		mOpaqueQueue->clear();
 		mTransparentQueue->clear();
 
@@ -426,6 +430,7 @@ namespace bs { namespace ct
 		projZ[3][3] = 0.0f;
 		
 		gPerCameraParamDef.gMatScreenToWorld.set(mParamBuffer, invViewProj * projZ);
+		gPerCameraParamDef.gNDCToPrevNDC.set(mParamBuffer, mProperties.prevViewProjTransform * invViewProj);
 		gPerCameraParamDef.gViewDir.set(mParamBuffer, mProperties.viewDirection);
 		gPerCameraParamDef.gViewOrigin.set(mParamBuffer, mProperties.viewOrigin);
 		gPerCameraParamDef.gDeviceZToWorldZ.set(mParamBuffer, getDeviceZToViewZ(mProperties.projTransform));

+ 25 - 25
Source/RenderBeast/Source/BsShadowRendering.cpp

@@ -621,31 +621,7 @@ namespace bs { namespace ct
 		// Reserve space for shadow infos
 		mShadowInfos.resize(shadowInfoCount);
 
-		// Render shadow maps
-		for (UINT32 i = 0; i < (UINT32)sceneInfo.directionalLights.size(); ++i)
-		{
-			const RendererLight& light = sceneInfo.directionalLights[i];
-
-			if (!light.internal->getCastsShadow())
-				return;
-
-			for (UINT32 j = 0; j < (UINT32)sceneInfo.views.size(); ++j)
-				renderCascadedShadowMaps(j, i, scene, frameInfo);
-		}
-
-		for(auto& entry : mSpotLightShadowOptions)
-		{
-			UINT32 lightIdx = entry.lightIdx;
-			renderSpotShadowMap(sceneInfo.spotLights[lightIdx], entry, scene, frameInfo);
-		}
-
-		for (auto& entry : mRadialLightShadowOptions)
-		{
-			UINT32 lightIdx = entry.lightIdx;
-			renderRadialShadowMap(sceneInfo.radialLights[lightIdx], entry, scene, frameInfo);
-		}
-		
-		// Deallocate unused textures
+		// Deallocate unused textures (must be done before rendering shadows, in order to ensure indices don't change)
 		for(auto iter = mDynamicShadowMaps.begin(); iter != mDynamicShadowMaps.end(); ++iter)
 		{
 			if(iter->getLastUsedCounter() >= MAX_UNUSED_FRAMES)
@@ -671,6 +647,30 @@ namespace bs { namespace ct
 			else
 				++iter;
 		}
+
+		// Render shadow maps
+		for (UINT32 i = 0; i < (UINT32)sceneInfo.directionalLights.size(); ++i)
+		{
+			const RendererLight& light = sceneInfo.directionalLights[i];
+
+			if (!light.internal->getCastsShadow())
+				return;
+
+			for (UINT32 j = 0; j < (UINT32)sceneInfo.views.size(); ++j)
+				renderCascadedShadowMaps(j, i, scene, frameInfo);
+		}
+
+		for(auto& entry : mSpotLightShadowOptions)
+		{
+			UINT32 lightIdx = entry.lightIdx;
+			renderSpotShadowMap(sceneInfo.spotLights[lightIdx], entry, scene, frameInfo);
+		}
+
+		for (auto& entry : mRadialLightShadowOptions)
+		{
+			UINT32 lightIdx = entry.lightIdx;
+			renderRadialShadowMap(sceneInfo.radialLights[lightIdx], entry, scene, frameInfo);
+		}
 	}
 
 	/**