Просмотр исходного кода

More work on post-processing pipeline

BearishSun 9 лет назад
Родитель
Сommit
5dae318f4a

+ 2 - 2
Documentation/Manuals/Native/index.md

@@ -18,10 +18,10 @@ The manuals generally do not cover user-facing functionality, and focus more on
 API reference provides a categorized and hierarchical view of all the engine's classes. 
 
 All classes are categorized into two primary groups:
- - **Layers** - Layers make up the core of the engine. Each layer is built directly on top of the previous one. This is what most people will be interested in. Each layer also contains an *[INTERNAL]* category which contains lower level classes that are not meant for normal use, but can be important for those modifying the engine.
+ - **Layers** - Layers make up the core of the engine. Each layer is built directly on top of the previous one. This is what most people will be interested in. Each layer also contains an **INTERNAL** category which contains lower level classes that are not meant for normal use, but can be important for those modifying the engine.
  - **Plugins** - Plugins are various interchangeable libraries that contain high level systems built on top of abstractions defined in the layers. If you are modifying the engine you might be interested in this documentation, but it can be skipped for most normal users. 
  
-A separate *[IMPLEMENTATION]* category is also provided which contains base classes and templates that are used in construction of other types. You will almost never need this documentation as those specialized types inherit the documentation from their parents. 
+A separate **IMPLEMENTATION** category is also provided which contains base classes and templates that are used in construction of other types. You will almost never need this documentation as those specialized types inherit the documentation from their parents. 
  
 You should read the [architecture](@ref architecture) manual for a more detailed breakdown of the architecture.
 

+ 4 - 1
Source/BansheeCore/Include/BsShaderDefines.h

@@ -18,7 +18,10 @@ namespace BansheeEngine
 		void set(const String& name, float value);
 
 		/** Adds a new define with an integer value. */
-		void set(const String& name, int value);
+		void set(const String& name, INT32 value);
+
+		/** Adds a new define with an integer value. */
+		void set(const String& name, UINT32 value);
 
 		/** Adds a new define with a string point value. */
 		void set(const String& name, const String& value);

+ 8 - 0
Source/BansheeCore/Include/BsTexture.h

@@ -462,6 +462,14 @@ namespace BansheeEngine
 		 */
 		static void releaseView(const TextureViewPtr& view);
 
+		/** Returns a plain white texture. */
+		static SPtr<TextureCore> WHITE;
+
+		/** Returns a plain black texture. */
+		static SPtr<TextureCore> BLACK;
+
+		/** Returns a plain normal map texture with normal pointing up (in Y direction). */
+		static SPtr<TextureCore> NORMAL;
 	protected:
 		/** @copydoc lock */
 		virtual PixelData lockImpl(GpuLockOptions options, UINT32 mipLevel = 0, UINT32 face = 0) = 0;

+ 3 - 0
Source/BansheeCore/Include/BsTextureManager.h

@@ -109,6 +109,9 @@ namespace BansheeEngine
     public:
 		virtual ~TextureCoreManager() { }
 
+		/** @copydoc Module::onStartUp */
+		void onStartUp() override;
+
 		/**
 		 * @copydoc	TextureManager::createTexture(TextureType, UINT32, UINT32, UINT32, int, PixelFormat, int, bool, UINT32)
 		 */

+ 6 - 1
Source/BansheeCore/Source/BsShaderDefines.cpp

@@ -9,7 +9,12 @@ namespace BansheeEngine
 		mDefines[name] = toString(value);
 	}
 
-	void ShaderDefines::set(const String& name, int value)
+	void ShaderDefines::set(const String& name, INT32 value)
+	{
+		mDefines[name] = toString(value);
+	}
+
+	void ShaderDefines::set(const String& name, UINT32 value)
 	{
 		mDefines[name] = toString(value);
 	}

+ 4 - 0
Source/BansheeCore/Source/BsTexture.cpp

@@ -82,6 +82,10 @@ namespace BansheeEngine
 		return dst;
 	}
 
+	SPtr<TextureCore> TextureCore::WHITE;
+	SPtr<TextureCore> TextureCore::BLACK;
+	SPtr<TextureCore> TextureCore::NORMAL;
+
 	TextureCore::TextureCore(TextureType textureType, UINT32 width, UINT32 height, UINT32 depth, UINT32 numMipmaps,
 		PixelFormat format, int usage, bool hwGamma, UINT32 multisampleCount, const PixelDataPtr& initData)
 		:mProperties(textureType, width, height, depth, numMipmaps, format, usage, hwGamma, multisampleCount), 

+ 40 - 0
Source/BansheeCore/Source/BsTextureManager.cpp

@@ -84,6 +84,46 @@ namespace BansheeEngine
 		return newRT;
 	}
 
+	void TextureCoreManager::onStartUp()
+    {
+		// White built-in texture
+		SPtr<TextureCore> whiteTexture = createTexture(TEX_TYPE_2D, 2, 2, 1, 0, PF_R8G8B8A8, TU_STATIC);
+
+		PixelDataPtr whitePixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		whitePixelData->setColorAt(Color::White, 0, 0);
+		whitePixelData->setColorAt(Color::White, 0, 1);
+		whitePixelData->setColorAt(Color::White, 1, 0);
+		whitePixelData->setColorAt(Color::White, 1, 1);
+
+		whiteTexture->writeData(*whitePixelData);
+		TextureCore::WHITE = whiteTexture;
+
+		// Black built-in texture
+		SPtr<TextureCore> blackTexture = createTexture(TEX_TYPE_2D, 2, 2, 1, 0, PF_R8G8B8A8, TU_STATIC);
+
+		PixelDataPtr blackPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+		blackPixelData->setColorAt(Color::Black, 0, 0);
+		blackPixelData->setColorAt(Color::Black, 0, 1);
+		blackPixelData->setColorAt(Color::Black, 1, 0);
+		blackPixelData->setColorAt(Color::Black, 1, 1);
+
+		blackTexture->writeData(*blackPixelData);
+		TextureCore::BLACK = blackTexture;
+
+		// Normal (Y = Up) built-in texture
+		SPtr<TextureCore> normalTexture = createTexture(TEX_TYPE_2D, 2, 2, 1, 0, PF_R8G8B8A8, TU_STATIC);
+		PixelDataPtr normalPixelData = PixelData::create(2, 2, 1, PF_R8G8B8A8);
+
+		Color encodedNormal(0.5f, 0.5f, 1.0f);
+		normalPixelData->setColorAt(encodedNormal, 0, 0);
+		normalPixelData->setColorAt(encodedNormal, 0, 1);
+		normalPixelData->setColorAt(encodedNormal, 1, 0);
+		normalPixelData->setColorAt(encodedNormal, 1, 1);
+
+		normalTexture->writeData(*normalPixelData);
+		TextureCore::NORMAL = normalTexture;
+    }
+
 	SPtr<TextureCore> TextureCoreManager::createTexture(TextureType texType, UINT32 width, UINT32 height, UINT32 depth,
 		int numMips, PixelFormat format, int usage, bool hwGammaCorrection, UINT32 multisampleCount)
 	{

+ 96 - 7
Source/RenderBeast/Include/BsPostProcessing.h

@@ -27,6 +27,30 @@ namespace BansheeEngine
 		 * [0, 16]. 
 		 */
 		float histogramLog2Max = 4.0f;
+
+		/** Percentage below which to ignore values in the eye adaptation histogram. In range [0.0f, 1.0f]. */
+		float histogramPctLow = 0.8f;
+
+		/** Percentage above which to ignore values in the eye adaptation histogram. In range [0.0f, 1.0f]. */
+		float histogramPctHigh = 0.983f;
+
+		/** Clamps the minimum eye adaptation scale (pre-exposure scale) to this value. In range [0.0f, 10.0f]. */
+		float minEyeAdaptation = 0.03f;
+
+		/** Clamps the maximum eye adaptation scale (pre-exposure scale) to this value. In range [0.0f, 10.0f]. */
+		float maxEyeAdaptation = 2.0f;
+
+		/** 
+		 * Log2 value to scale the eye adaptation by. Smaller values yield darker image, while larger yield brighter image. 
+		 * In range [-8, 8].
+		 */
+		float exposureScale = 0.0f;
+
+		/** Determines how quickly does the eye adaptation adjust to larger values. In range [0.01f, 20.0f]. */
+		float eyeAdaptationSpeedUp = 3.0f;
+
+		/** Determines how quickly does the eye adaptation adjust to smaller values. In range [0.01f, 20.0f]. */
+		float eyeAdaptationSpeedDown = 3.0f;
 	};
 
 	/** Contains per-camera data used by post process effects. */
@@ -36,7 +60,9 @@ namespace BansheeEngine
 
 		SPtr<PooledRenderTexture> downsampledSceneTex;
 		SPtr<PooledRenderTexture> histogramTex;
-		SPtr<PooledRenderTexture> eyeAdaptationTex;
+		SPtr<PooledRenderTexture> histogramReduceTex;
+		SPtr<PooledRenderTexture> eyeAdaptationTex[2];
+		INT32 lastEyeAdaptationTex = 0;
 	};
 
 	BS_PARAM_BLOCK_BEGIN(DownsampleParams)
@@ -83,13 +109,27 @@ namespace BansheeEngine
 		EyeAdaptHistogramMat();
 
 		/** Executes the post-process effect with the provided parameters. */
-		void execute(const SPtr<RenderTextureCore>& target, PostProcessInfo& ppInfo);
+		void execute(PostProcessInfo& ppInfo);
 
 		/** Releases the output render target. */
 		void release(PostProcessInfo& ppInfo);
 
 		/** Returns the render texture where the output was written. */
 		SPtr<RenderTextureCore> getOutput() const { return mOutput; }
+
+		/** Calculates the number of thread groups that need to execute to cover the provided render target. */
+		static Vector2I getThreadGroupCount(const SPtr<RenderTextureCore>& target);
+
+		/** 
+		 * Returns a vector containing scale and offset (in that order) that will be applied to luminance values
+		 * to determine their position in the histogram. 
+		 */
+		static Vector2 getHistogramScaleOffset(const PostProcessInfo& ppInfo);
+
+		static const UINT32 THREAD_GROUP_SIZE_X = 4;
+		static const UINT32 THREAD_GROUP_SIZE_Y = 4;
+		
+		static const UINT32 HISTOGRAM_NUM_TEXELS = (THREAD_GROUP_SIZE_X * THREAD_GROUP_SIZE_Y) / 4;
 	private:
 		EyeAdaptHistogramParams mParams;
 		MaterialParamTextureCore mSceneColor;
@@ -98,10 +138,57 @@ namespace BansheeEngine
 		POOLED_RENDER_TEXTURE_DESC mOutputDesc;
 		SPtr<RenderTextureCore> mOutput;
 
-		static const INT32 THREAD_GROUP_SIZE_X = 4;
-		static const INT32 THREAD_GROUP_SIZE_Y = 4;
-		static const INT32 LOOP_COUNT_X = 8;
-		static const INT32 LOOP_COUNT_Y = 8;
+		static const UINT32 LOOP_COUNT_X = 8;
+		static const UINT32 LOOP_COUNT_Y = 8;
+	};
+
+	BS_PARAM_BLOCK_BEGIN(EyeAdaptHistogramReduceParams)
+		BS_PARAM_BLOCK_ENTRY(Vector2I, gThreadGroupCount)
+	BS_PARAM_BLOCK_END
+
+	/** Shader that reduces the luminance histograms created by EyeAdaptHistogramMat into a single histogram. */
+	class EyeAdaptHistogramReduceMat : public RendererMaterial<EyeAdaptHistogramReduceMat>
+	{
+		RMAT_DEF("PPEyeAdaptHistogramReduce.bsl");
+
+	public:
+		EyeAdaptHistogramReduceMat();
+
+		/** Executes the post-process effect with the provided parameters. */
+		void execute(PostProcessInfo& ppInfo);
+
+		/** Releases the output render target. */
+		void release(PostProcessInfo& ppInfo);
+
+		/** Returns the render texture where the output was written. */
+		SPtr<RenderTextureCore> getOutput() const { return mOutput; }
+	private:
+		EyeAdaptHistogramReduceParams mParams;
+
+		MaterialParamTextureCore mHistogramTex;
+		MaterialParamTextureCore mEyeAdaptationTex;
+
+		POOLED_RENDER_TEXTURE_DESC mOutputDesc;
+		SPtr<RenderTextureCore> mOutput;
+	};
+
+	BS_PARAM_BLOCK_BEGIN(EyeAdaptationParams)
+		BS_PARAM_BLOCK_ENTRY_ARRAY(Vector4, gEyeAdaptationParams, 3)
+	BS_PARAM_BLOCK_END
+
+	/** Shader that computes the eye adaptation value based on scene luminance. */
+	class EyeAdaptationMat : public RendererMaterial<EyeAdaptationMat>
+	{
+		RMAT_DEF("PPEyeAdaptation.bsl");
+
+	public:
+		EyeAdaptationMat();
+
+		/** Executes the post-process effect with the provided parameters. */
+		void execute(PostProcessInfo& ppInfo, float frameDelta);
+	private:
+		EyeAdaptationParams mParams;
+		MaterialParamTextureCore mReducedHistogramTex;
 	};
 
 	/**
@@ -113,11 +200,13 @@ namespace BansheeEngine
 	{
 	public:
 		/** Renders post-processing effects for the provided render target. */
-		void postProcess(const SPtr<RenderTextureCore>& target, PostProcessInfo& ppInfo);
+		void postProcess(const SPtr<RenderTextureCore>& target, PostProcessInfo& ppInfo, float frameDelta);
 		
 	private:
 		DownsampleMat mDownsample;
 		EyeAdaptHistogramMat mEyeAdaptHistogram;
+		EyeAdaptHistogramReduceMat mEyeAdaptHistogramReduce;
+		EyeAdaptationMat mEyeAdaptation;
 	};
 
 	/** @} */

+ 4 - 2
Source/RenderBeast/Include/BsRenderBeast.h

@@ -167,10 +167,11 @@ namespace BansheeEngine
 		 * Performs rendering over all camera proxies.
 		 *
 		 * @param[in]	time	Current frame time in milliseconds.
+		 * @param[in]	delta	Time elapsed since the last frame.
 		 *
 		 * @note	Core thread only.
 		 */
-		void renderAllCore(float time);
+		void renderAllCore(float time, float delta);
 
 		/**
 		 * Populates camera render queues by determining visible renderable object.
@@ -184,10 +185,11 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	rtData	Render target data containing the camera to render.
 		 * @param[in]	camIdx	Index of the camera to render.
+		 * @param[in]	delta	Time elapsed since the last frame.
 		 * 					
 		 * @note	Core thread only.
 		 */
-		void render(RenderTargetData& rtData, UINT32 camIdx);
+		void render(RenderTargetData& rtData, UINT32 camIdx, float delta);
 
 		/**	Creates data used by the renderer on the core thread. */
 		void initializeCore();

+ 157 - 23
Source/RenderBeast/Source/BsPostProcessing.cpp

@@ -4,6 +4,7 @@
 #include "BsRenderTexture.h"
 #include "BsRenderTexturePool.h"
 #include "BsRendererUtility.h"
+#include "BsTextureManager.h"
 
 namespace BansheeEngine
 {
@@ -73,38 +74,25 @@ namespace BansheeEngine
 		defines.set("LOOP_COUNT_Y", LOOP_COUNT_Y);
 	}
 
-	void EyeAdaptHistogramMat::execute(const SPtr<RenderTextureCore>& target, PostProcessInfo& ppInfo)
+	void EyeAdaptHistogramMat::execute(PostProcessInfo& ppInfo)
 	{
 		// Set parameters
-		mSceneColor.set(target->getBindableColorTexture());
-
-		const PostProcessSettings& settings = ppInfo.settings;
-
-		float diff = settings.histogramLog2Max - settings.histogramLog2Min;
-		float scale = 1.0f / diff;
-		float offset = -settings.histogramLog2Min * scale;
+		SPtr<RenderTextureCore> target = ppInfo.downsampledSceneTex->renderTexture;
+		mSceneColor.set(ppInfo.downsampledSceneTex->texture);
 
 		const RenderTextureProperties& props = target->getProperties();
 		int offsetAndSize[4] = { 0, 0, (INT32)props.getWidth(), (INT32)props.getHeight() };
 
-		mParams.gHistogramParams.set(Vector2(scale, offset));
+		mParams.gHistogramParams.set(getHistogramScaleOffset(ppInfo));
 		mParams.gPixelOffsetAndSize.set(Vector4I(offsetAndSize));
 
-		const UINT32 texelsPerThreadGroupX = THREAD_GROUP_SIZE_X * LOOP_COUNT_X;
-		const UINT32 texelsPerThreadGroupY = THREAD_GROUP_SIZE_Y * LOOP_COUNT_Y;
-
-		Vector2I threadGroupCount;
-		threadGroupCount.x = ((INT32)props.getWidth() + texelsPerThreadGroupX - 1) / texelsPerThreadGroupX;
-		threadGroupCount.y = ((INT32)props.getHeight() + texelsPerThreadGroupY - 1) / texelsPerThreadGroupY;
-
+		Vector2I threadGroupCount = getThreadGroupCount(target);
 		mParams.gThreadGroupCount.set(threadGroupCount);
 
 		// Set output
-		UINT32 histogramSize = THREAD_GROUP_SIZE_X * THREAD_GROUP_SIZE_Y;
-		UINT32 histogramTexelCount = histogramSize / 4; // 4 entries per texel
 		UINT32 numHistograms = threadGroupCount.x * threadGroupCount.y;
 
-		mOutputDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT32_RGBA, histogramTexelCount, numHistograms, 
+		mOutputDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT16_RGBA, HISTOGRAM_NUM_TEXELS, numHistograms,
 			TU_LOADSTORE);
 
 		// Dispatch
@@ -125,14 +113,160 @@ namespace BansheeEngine
 		mOutput = nullptr;
 	}
 
-	void PostProcessing::postProcess(const SPtr<RenderTextureCore>& target, PostProcessInfo& ppInfo)
+	Vector2I EyeAdaptHistogramMat::getThreadGroupCount(const SPtr<RenderTextureCore>& target)
 	{
-		mDownsample.execute(target, ppInfo);
-		mEyeAdaptHistogram.execute(mDownsample.getOutput(), ppInfo);
+		const UINT32 texelsPerThreadGroupX = THREAD_GROUP_SIZE_X * LOOP_COUNT_X;
+		const UINT32 texelsPerThreadGroupY = THREAD_GROUP_SIZE_Y * LOOP_COUNT_Y;
+
+		const RenderTextureProperties& props = target->getProperties();
+	
+		Vector2I threadGroupCount;
+		threadGroupCount.x = ((INT32)props.getWidth() + texelsPerThreadGroupX - 1) / texelsPerThreadGroupX;
+		threadGroupCount.y = ((INT32)props.getHeight() + texelsPerThreadGroupY - 1) / texelsPerThreadGroupY;
+
+		return threadGroupCount;
+	}
+
+	Vector2 EyeAdaptHistogramMat::getHistogramScaleOffset(const PostProcessInfo& ppInfo)
+	{
+		const PostProcessSettings& settings = ppInfo.settings;
+
+		float diff = settings.histogramLog2Max - settings.histogramLog2Min;
+		float scale = 1.0f / diff;
+		float offset = -settings.histogramLog2Min * scale;
+
+		return Vector2(scale, offset);
+	}
+
+	EyeAdaptHistogramReduceMat::EyeAdaptHistogramReduceMat()
+	{
+		mMaterial->setParamBlockBuffer("Input", mParams.getBuffer());
+
+		mHistogramTex = mMaterial->getParamTexture("gHistogramTex");
+		mEyeAdaptationTex = mMaterial->getParamTexture("gEyeAdaptationTex");
+	}
+
+	void EyeAdaptHistogramReduceMat::_initDefines(ShaderDefines& defines)
+	{
+		// Do nothing
+	}
+
+	void EyeAdaptHistogramReduceMat::execute(PostProcessInfo& ppInfo)
+	{
+		// Set parameters
+		mHistogramTex.set(ppInfo.histogramTex->texture); // TODO - Unbind this from render target slot first?
+
+		SPtr<PooledRenderTexture> eyeAdaptationRT = ppInfo.eyeAdaptationTex[ppInfo.lastEyeAdaptationTex];
+		SPtr<TextureCore> eyeAdaptationTex;
+
+		if (eyeAdaptationRT != nullptr) // Could be that this is the first run
+			eyeAdaptationTex = eyeAdaptationRT->texture;
+		else
+			eyeAdaptationTex = TextureCore::WHITE;
+
+		mEyeAdaptationTex.set(eyeAdaptationTex);
+
+		Vector2I threadGroupCount = EyeAdaptHistogramMat::getThreadGroupCount(ppInfo.downsampledSceneTex->renderTexture);
+		mParams.gThreadGroupCount.set(threadGroupCount);
+
+		// Set output
+		mOutputDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT16_RGBA, EyeAdaptHistogramMat::HISTOGRAM_NUM_TEXELS, 2,
+			TU_LOADSTORE);
+
+		// Render
+		ppInfo.histogramReduceTex = RenderTexturePool::instance().get(mOutputDesc);
+
+		RenderAPICore& rapi = RenderAPICore::instance();
+		rapi.setRenderTarget(ppInfo.histogramReduceTex->renderTexture, true);
+
+		gRendererUtility().setPass(mMaterial, 0);
+		gRendererUtility().drawScreenQuad();
+
+		mOutput = ppInfo.histogramReduceTex->renderTexture;
+	}
+
+	void EyeAdaptHistogramReduceMat::release(PostProcessInfo& ppInfo)
+	{
+		RenderTexturePool::instance().release(ppInfo.histogramReduceTex);
+		mOutput = nullptr;
+	}
+
+	EyeAdaptationMat::EyeAdaptationMat()
+	{
+		mMaterial->setParamBlockBuffer("Input", mParams.getBuffer());
+
+		mReducedHistogramTex = mMaterial->getParamTexture("gHistogramTex");
+	}
+
+	void EyeAdaptationMat::_initDefines(ShaderDefines& defines)
+	{
+		defines.set("THREADGROUP_SIZE_X", EyeAdaptHistogramMat::THREAD_GROUP_SIZE_X);
+		defines.set("THREADGROUP_SIZE_Y", EyeAdaptHistogramMat::THREAD_GROUP_SIZE_Y);
+	}
+
+	void EyeAdaptationMat::execute(PostProcessInfo& ppInfo, float frameDelta)
+	{
+		bool texturesInitialized = ppInfo.eyeAdaptationTex[0] != nullptr && ppInfo.eyeAdaptationTex[1] != nullptr;
+		if(!texturesInitialized)
+		{
+			POOLED_RENDER_TEXTURE_DESC outputDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT32_R, 1, 1, TU_RENDERTARGET);
+			ppInfo.eyeAdaptationTex[0] = RenderTexturePool::instance().get(outputDesc);
+			ppInfo.eyeAdaptationTex[1] = RenderTexturePool::instance().get(outputDesc);
+		}
 
+		ppInfo.lastEyeAdaptationTex = (ppInfo.lastEyeAdaptationTex + 1) % 2; // TODO - Do I really need two targets?
+
+		// Set parameters
+		mReducedHistogramTex.set(ppInfo.histogramReduceTex->texture); // TODO - Unbind this from render target slot first?
+
+		Vector2 histogramScaleAndOffset = EyeAdaptHistogramMat::getHistogramScaleOffset(ppInfo);
+
+		const PostProcessSettings& settings = ppInfo.settings;
+
+		Vector4 eyeAdaptationParams[3];
+		eyeAdaptationParams[0].x = histogramScaleAndOffset.x;
+		eyeAdaptationParams[0].y = histogramScaleAndOffset.y;
+
+		float histogramPctHigh = Math::clamp01(settings.histogramPctHigh);
+
+		eyeAdaptationParams[0].z = std::min(Math::clamp01(settings.histogramPctLow), histogramPctHigh);
+		eyeAdaptationParams[0].w = histogramPctHigh;
+
+		eyeAdaptationParams[1].x = std::min(settings.minEyeAdaptation, settings.maxEyeAdaptation);
+		eyeAdaptationParams[1].y = settings.maxEyeAdaptation;
+
+		eyeAdaptationParams[1].z = settings.eyeAdaptationSpeedUp;
+		eyeAdaptationParams[1].w = settings.eyeAdaptationSpeedDown;
+
+		eyeAdaptationParams[2].x = Math::pow(2.0f, settings.exposureScale);
+		eyeAdaptationParams[2].y = frameDelta;
+
+		mParams.gEyeAdaptationParams.set(eyeAdaptationParams[0], 0);
+		mParams.gEyeAdaptationParams.set(eyeAdaptationParams[1], 1);
+		mParams.gEyeAdaptationParams.set(eyeAdaptationParams[2], 2);
+
+		// Render
+		SPtr<PooledRenderTexture> eyeAdaptationRT = ppInfo.eyeAdaptationTex[ppInfo.lastEyeAdaptationTex];
+
+		RenderAPICore& rapi = RenderAPICore::instance();
+		rapi.setRenderTarget(eyeAdaptationRT->renderTexture, true);
+
+		gRendererUtility().setPass(mMaterial, 0);
+		gRendererUtility().drawScreenQuad();
+	}
+
+	void PostProcessing::postProcess(const SPtr<RenderTextureCore>& target, PostProcessInfo& ppInfo, float frameDelta)
+	{
+		mDownsample.execute(target, ppInfo);
+		mEyeAdaptHistogram.execute(ppInfo);
 		mDownsample.release(ppInfo);
+
+		mEyeAdaptHistogramReduce.execute(ppInfo);
 		mEyeAdaptHistogram.release(ppInfo);
 
-		// TODO - Use plain white for initial eye adaptation tex needed for histogram reduce
+		mEyeAdaptation.execute(ppInfo, frameDelta);
+		mEyeAdaptHistogramReduce.release(ppInfo);
+
+		// TODO - Generate LUT, run tonemapping, output everything to scene
 	}
 }

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

@@ -420,7 +420,7 @@ namespace BansheeEngine
 			mOptionsDirty = false;
 		}
 
-		gCoreAccessor().queueCommand(std::bind(&RenderBeast::renderAllCore, this, gTime().getTime()));
+		gCoreAccessor().queueCommand(std::bind(&RenderBeast::renderAllCore, this, gTime().getTime(), gTime().getFrameDelta()));
 	}
 
 	void RenderBeast::syncRenderOptions(const RenderBeastOptions& options)
@@ -446,7 +446,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void RenderBeast::renderAllCore(float time)
+	void RenderBeast::renderAllCore(float time, float delta)
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
@@ -477,7 +477,7 @@ namespace BansheeEngine
 
 			UINT32 numCameras = (UINT32)cameras.size();
 			for (UINT32 i = 0; i < numCameras; i++)
-				render(renderTargetData, i);
+				render(renderTargetData, i, delta);
 
 			RenderAPICore::instance().endFrame();
 			RenderAPICore::instance().swapBuffers(target);
@@ -486,7 +486,7 @@ namespace BansheeEngine
 		gProfilerCPU().endSample("renderAllCore");
 	}
 
-	void RenderBeast::render(RenderTargetData& rtData, UINT32 camIdx)
+	void RenderBeast::render(RenderTargetData& rtData, UINT32 camIdx, float delta)
 	{
 		gProfilerCPU().beginSample("Render");
 
@@ -727,7 +727,7 @@ namespace BansheeEngine
 
 		if (hasGBuffer)
 		{
-			//PostProcessing::instance().postProcess(camData.target->getSceneColorRT(), camData.postProcessInfo);
+			//PostProcessing::instance().postProcess(camData.target->getSceneColorRT(), camData.postProcessInfo, delta);
 
 			// TODO - Instead of doing a separate resolve here I could potentially perform a resolve directly in some
 			// post-processing pass (e.g. tone mapping). Right now it is just an unnecessary blit.