Browse Source

Tonemapping WIP

BearishSun 9 years ago
parent
commit
59dd53307b

+ 15 - 6
Source/BansheeD3D11RenderAPI/Source/BsD3D11Mappings.cpp

@@ -467,9 +467,8 @@ namespace BansheeEngine
 		case DXGI_FORMAT_R8G8B8A8_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R8G8B8A8_UNORM:
-			return PF_R8G8B8A8;
 		case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
-			return PF_UNKNOWN;
+			return PF_R8G8B8A8;
 		case DXGI_FORMAT_R8G8B8A8_UINT:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_R8G8B8A8_SNORM:
@@ -553,19 +552,16 @@ namespace BansheeEngine
 		case DXGI_FORMAT_BC1_TYPELESS:
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_BC1_UNORM:
-			return PF_BC1;
 		case DXGI_FORMAT_BC1_UNORM_SRGB:
 			return PF_BC1;
 		case DXGI_FORMAT_BC2_TYPELESS:
 			return PF_BC2;
 		case DXGI_FORMAT_BC2_UNORM:
-			return PF_BC2;
 		case DXGI_FORMAT_BC2_UNORM_SRGB:
 			return PF_BC2;
 		case DXGI_FORMAT_BC3_TYPELESS:
 			return PF_BC3;
 		case DXGI_FORMAT_BC3_UNORM:
-			return PF_BC3;
 		case DXGI_FORMAT_BC3_UNORM_SRGB:
 			return PF_BC3;
 		case DXGI_FORMAT_BC4_TYPELESS:
@@ -587,7 +583,6 @@ namespace BansheeEngine
 		case DXGI_FORMAT_BC6H_TYPELESS:
 			return PF_BC6H;
 		case DXGI_FORMAT_BC7_UNORM:
-			return PF_BC7;
 		case DXGI_FORMAT_BC7_UNORM_SRGB:
 			return PF_BC7;
 		case DXGI_FORMAT_BC7_TYPELESS:
@@ -596,8 +591,10 @@ namespace BansheeEngine
 			return PF_UNKNOWN;
 		case DXGI_FORMAT_B5G5R5A1_UNORM:
 			return PF_UNKNOWN;
+		case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
 		case DXGI_FORMAT_B8G8R8A8_UNORM:
 			return PF_B8G8R8A8;
+		case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
 		case DXGI_FORMAT_B8G8R8X8_UNORM:
 			return PF_B8G8R8X8;
 		default:
@@ -624,10 +621,16 @@ namespace BansheeEngine
 		case PF_X8B8G8R8:
 			return DXGI_FORMAT_UNKNOWN;
 		case PF_R8G8B8A8:
+			if (gamma)
+				return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
 			return DXGI_FORMAT_R8G8B8A8_UNORM;
 		case PF_B8G8R8A8:
+			if (gamma)
+				return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
 			return DXGI_FORMAT_B8G8R8A8_UNORM;
 		case PF_B8G8R8X8:
+			if (gamma)
+				return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
 			return DXGI_FORMAT_B8G8R8X8_UNORM;
 		case PF_FLOAT16_R:
 			return DXGI_FORMAT_R16_FLOAT;
@@ -647,10 +650,16 @@ namespace BansheeEngine
 			return DXGI_FORMAT_R32G32B32A32_FLOAT;
 		case PF_BC1:
 		case PF_BC1a:
+			if(gamma)
+				return DXGI_FORMAT_BC1_UNORM_SRGB;
 			return DXGI_FORMAT_BC1_UNORM;
 		case PF_BC2:
+			if (gamma)
+				return DXGI_FORMAT_BC2_UNORM_SRGB;
 			return DXGI_FORMAT_BC2_UNORM;
 		case PF_BC3:
+			if (gamma)
+				return DXGI_FORMAT_BC3_UNORM_SRGB;
 			return DXGI_FORMAT_BC3_UNORM;
 		case PF_BC4:
 			return DXGI_FORMAT_BC4_UNORM;

+ 30 - 7
Source/BansheeEngine/Include/BsRendererUtility.h

@@ -62,9 +62,13 @@ namespace BansheeEngine
 		/**
 		 * Draws the specified mesh.
 		 *
+		 * @param[in]	mesh			Mesh to draw.
+		 * @param[in]	subMesh			Portion of the mesh to draw.
+		 * @param[in]	numInstances	Number of times to draw the mesh using instanced rendering.
+		 *
 		 * @note	Core thread.
 		 */
-		void draw(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh);
+		void draw(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh, UINT32 numInstances = 1);
 
 		/**
 		 * Blits contents of the provided texture into the currently bound render target. If the provided texture contains
@@ -79,15 +83,34 @@ namespace BansheeEngine
 		/**
 		 * Draws a quad over the entire viewport in normalized device coordinates.
 		 * 			
-		 * @param[in]	uv			UV coordinates to assign to the corners of the quad.
-		 * @param[in]	textureSize	Size of the texture the UV coordinates are specified for. If the UV coordinates are 
-		 *							already in normalized (0, 1) range then keep this value as is. If the UV coordinates 
-		 *							are in texels then set this value to the texture size so they can be normalized 
-		 *							internally.
+		 * @param[in]	uv				UV coordinates to assign to the corners of the quad.
+		 * @param[in]	textureSize		Size of the texture the UV coordinates are specified for. If the UV coordinates are 
+		 *								already in normalized (0, 1) range then keep this value as is. If the UV coordinates 
+		 *								are in texels then set this value to the texture size so they can be normalized 
+		 *								internally.
+		 * @param[in]	numInstances	How many instances of the quad to draw (using instanced rendering). Useful when
+		 *								drawing to 3D textures.
 		 * 			
 		 * @note	Core thread.
 		 */
-		void drawScreenQuad(const Rect2& uv = Rect2(0.0f, 0.0f, 1.0f, 1.0f), const Vector2I& textureSize = Vector2I(1, 1));
+		void drawScreenQuad(const Rect2& uv, const Vector2I& textureSize = Vector2I(1, 1), 
+			UINT32 numInstances = 1);
+
+		/**
+		 * Draws a quad over the entire viewport in normalized device coordinates.
+		 * 			
+		 * @param[in]	numInstances	How many instances of the quad to draw (using instanced rendering). Useful when
+		 *								drawing to 3D textures.
+		 * 			
+		 * @note	Core thread.
+		 */
+		void drawScreenQuad(UINT32 numInstances = 1)
+		{
+			Rect2 uv(0.0f, 0.0f, 1.0f, 1.0f);
+			Vector2I textureSize(1, 1);
+
+			drawScreenQuad(uv, textureSize, numInstances);
+		}
 
 		/** Returns a stencil mesh used for a point light (a unit sphere). */
 		SPtr<MeshCore> getPointLightStencil() const { return mPointLightStencilMesh; }

+ 5 - 4
Source/BansheeEngine/Source/BsRendererUtility.cpp

@@ -270,7 +270,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void RendererUtility::draw(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh)
+	void RendererUtility::draw(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh, UINT32 numInstances)
 	{
 		RenderAPICore& rs = RenderAPICore::instance();
 		SPtr<VertexData> vertexData = mesh->getVertexData();
@@ -308,7 +308,8 @@ namespace BansheeEngine
 		UINT32 indexCount = subMesh.indexCount;
 
 		rs.setIndexBuffer(indexBuffer);
-		rs.drawIndexed(subMesh.indexOffset + mesh->getIndexOffset(), indexCount, mesh->getVertexOffset(), vertexData->vertexCount);
+		rs.drawIndexed(subMesh.indexOffset + mesh->getIndexOffset(), indexCount, mesh->getVertexOffset(), 
+			vertexData->vertexCount, numInstances);
 
 		mesh->_notifyUsedOnGPU();
 	}
@@ -343,7 +344,7 @@ namespace BansheeEngine
 		drawScreenQuad(fArea);
 	}
 
-	void RendererUtility::drawScreenQuad(const Rect2& uv, const Vector2I& textureSize)
+	void RendererUtility::drawScreenQuad(const Rect2& uv, const Vector2I& textureSize, UINT32 numInstances)
 	{
 		// Note: Consider drawing the quad using a single large triangle for possibly better performance
 
@@ -396,7 +397,7 @@ namespace BansheeEngine
 		indices[5] = 2;
 
 		mFullScreenQuadMesh->writeSubresource(0, *meshData, true, false);
-		draw(mFullScreenQuadMesh, mFullScreenQuadMesh->getProperties().getSubMesh());
+		draw(mFullScreenQuadMesh, mFullScreenQuadMesh->getProperties().getSubMesh(), numInstances);
 	}
 
 	RendererUtility& gRendererUtility()

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

@@ -53,6 +53,33 @@ namespace BansheeEngine
 
 		/** Determines how quickly does the eye adaptation adjust to smaller values. In range [0.01f, 20.0f]. */
 		float eyeAdaptationSpeedDown;
+
+		/** Value that controls the shoulder (upper non-linear) section of the filmic curve. Affects high-range. */
+		float filmicCurveShoulderStrength;
+
+		/** Value that controls the linear (middle) section of the filmic curve. Affects mid-range. */
+		float filmicCurveLinearStrength;
+
+		/** Value that controls the linear (middle) section of the filmic curve. Affects mid-range. */
+		float filmicCurveLinearAngle;
+
+		/** Value that controls the toe (lower non-linear) section of the filmic curve. Affects low-range. */
+		float filmicCurveToeStrength;
+
+		/** Value that controls the toe (lower non-linear) section of the filmic curve. Affects low-range. */
+		float filmicCurveToeNumerator;
+
+		/** Value that controls the toe (lower non-linear) section of the filmic curve. Affects low-range. */
+		float filmicCurveToeDenominator;
+
+		/** Value that controls the white point of the filmic curve. Affects the entire curve. */
+		float filmicCurveLinearWhitePoint;
+
+		/** Determines should the final output be tonemapped. */
+		bool tonemapping;
+
+		/** Gamma value to adjust the image for. Larger values result in a brighter image. */
+		float gamma;
 	};
 
 	/** Contains per-camera data used by post process effects. */
@@ -64,6 +91,7 @@ namespace BansheeEngine
 		SPtr<PooledRenderTexture> histogramTex;
 		SPtr<PooledRenderTexture> histogramReduceTex;
 		SPtr<PooledRenderTexture> eyeAdaptationTex[2];
+		SPtr<PooledRenderTexture> colorLUT;
 		INT32 lastEyeAdaptationTex = 0;
 	};
 
@@ -193,8 +221,40 @@ namespace BansheeEngine
 		MaterialParamTextureCore mReducedHistogramTex;
 	};
 
+	BS_PARAM_BLOCK_BEGIN(CreateTonemapLUTParams)
+		BS_PARAM_BLOCK_ENTRY_ARRAY(Vector4, gTonemapParams, 2)
+		BS_PARAM_BLOCK_ENTRY(float, gGammaAdjustment)
+		BS_PARAM_BLOCK_ENTRY(int, gGammaCorrectionType)
+	BS_PARAM_BLOCK_END
+
+	/** 
+	 * Shader that creates a 3D lookup texture that is used to apply tonemapping, color grading, white balancing and gamma
+	 * correction.
+	 */
+	class CreateTonemapLUTMat : public RendererMaterial<CreateTonemapLUTMat>
+	{
+		RMAT_DEF("PPCreateTonemapLUT.bsl");
+
+	public:
+		CreateTonemapLUTMat();
+
+		/** Executes the post-process effect with the provided parameters. */
+		void execute(PostProcessInfo& ppInfo);
+
+		/** Releases the output render target. */
+		void release(PostProcessInfo& ppInfo);
+
+	private:
+		CreateTonemapLUTParams mParams;
+	};
+
+	BS_PARAM_BLOCK_BEGIN(TonemappingParams)
+		BS_PARAM_BLOCK_ENTRY(float, gRawGamma)
+	BS_PARAM_BLOCK_END
+
 	/** Shader that applies tonemapping and converts a HDR image into a LDR image. */
-	class TonemappingMat : public RendererMaterial<TonemappingMat>
+	template<bool GammaOnly>
+	class TonemappingMat : public RendererMaterial<TonemappingMat<GammaOnly>>
 	{
 		RMAT_DEF("PPTonemapping.bsl");
 
@@ -204,8 +264,14 @@ namespace BansheeEngine
 		/** Executes the post-process effect with the provided parameters. */
 		void execute(const SPtr<RenderTextureCore>& sceneColor, const SPtr<ViewportCore>& outputViewport,
 			PostProcessInfo& ppInfo);
+
+		/** Size of the 3D color lookup table. */
+		static const UINT32 LUT_SIZE = 32;
 	private:
+		TonemappingParams mParams;
+
 		MaterialParamTextureCore mInputTex;
+		MaterialParamTextureCore mColorLUT;
 		MaterialParamTextureCore mEyeAdaptationTex;
 	};
 
@@ -226,7 +292,10 @@ namespace BansheeEngine
 		EyeAdaptHistogramMat mEyeAdaptHistogram;
 		EyeAdaptHistogramReduceMat mEyeAdaptHistogramReduce;
 		EyeAdaptationMat mEyeAdaptation;
-		TonemappingMat mTonemapping;
+
+		CreateTonemapLUTMat mCreateLUT;
+		TonemappingMat<false> mTonemapping;
+		TonemappingMat<true> mTonemappingGammaOnly;
 	};
 
 	/** @} */

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

@@ -40,12 +40,6 @@ namespace BansheeEngine
 		 */
 		UINT32 msaa = 1;
 
-		/** 
-		 * All colors output from shaders will be automatically converted to gamma space when written to render target(s). 
-		 * Normally used when the renderer performs calculations in linear space.
-		 */
-		bool gammaCorrect = true; 
-
 		/**
 		 * High dynamic range allows light intensity to be more correctly recorded when rendering by allowing for a larger
 		 * range of values. The stored light is then converted into visible colors using a tone mapping operator depending

+ 98 - 15
Source/RenderBeast/Source/BsPostProcessing.cpp

@@ -11,7 +11,9 @@ namespace BansheeEngine
 	PostProcessSettings::PostProcessSettings()
 		: histogramLog2Min(-8.0f), histogramLog2Max(4.0f), histogramPctLow(0.8f), histogramPctHigh(0.985f)
 		, minEyeAdaptation(0.5f), maxEyeAdaptation(2.0f), exposureScale(0.0f), eyeAdaptationSpeedUp(3.0f)
-		, eyeAdaptationSpeedDown(3.0f)
+		, eyeAdaptationSpeedDown(3.0f), filmicCurveShoulderStrength(0.22f), filmicCurveLinearStrength(0.3f)
+		, filmicCurveLinearAngle(0.1f), filmicCurveToeStrength(0.2f), filmicCurveToeNumerator(0.01f)
+		, filmicCurveToeDenominator(0.3f), filmicCurveLinearWhitePoint(11.2f), tonemapping(true), gamma(2.2f)
 	{ }
 
 	DownsampleMat::DownsampleMat()
@@ -274,24 +276,94 @@ namespace BansheeEngine
 		rapi.setRenderTarget(nullptr);
 	}
 
-	TonemappingMat::TonemappingMat()
+	CreateTonemapLUTMat::CreateTonemapLUTMat()
 	{
+		mMaterial->setParamBlockBuffer("Input", mParams.getBuffer());
+	}
+
+	void CreateTonemapLUTMat::_initDefines(ShaderDefines& defines)
+	{
+		defines.set("LUT_SIZE", TonemappingMat<true>::LUT_SIZE);
+	}
+
+	void CreateTonemapLUTMat::execute(PostProcessInfo& ppInfo)
+	{
+		// Set parameters
+		mParams.gGammaAdjustment.set(2.2f / ppInfo.settings.gamma);
+
+		// Note: Assuming sRGB (PC monitor) for now, change to Rec.709 when running on console (value 1), or to raw 2.2
+		// gamma when running on Mac (value 2)
+		mParams.gGammaCorrectionType.set(0);
+
+		Vector4 tonemapParams[2];
+		tonemapParams[0].x = ppInfo.settings.filmicCurveShoulderStrength;
+		tonemapParams[0].y = ppInfo.settings.filmicCurveLinearStrength;
+		tonemapParams[0].z = ppInfo.settings.filmicCurveLinearAngle;
+		tonemapParams[0].w = ppInfo.settings.filmicCurveToeStrength;
+
+		tonemapParams[1].x = ppInfo.settings.filmicCurveToeNumerator;
+		tonemapParams[1].y = ppInfo.settings.filmicCurveToeDenominator;
+		tonemapParams[1].z = ppInfo.settings.filmicCurveLinearWhitePoint;
+		tonemapParams[1].w = 0.0f; // Unused
+
+		mParams.gTonemapParams.set(tonemapParams[0], 0);
+		mParams.gTonemapParams.set(tonemapParams[1], 1);
+
+		// Set output
+		UINT32 LUTSize = TonemappingMat<true>::LUT_SIZE;
+		POOLED_RENDER_TEXTURE_DESC outputDesc = POOLED_RENDER_TEXTURE_DESC::create3D(PF_B8G8R8X8, 
+			LUTSize, LUTSize, LUTSize, TU_RENDERTARGET);
+
+		// Render
+		ppInfo.colorLUT = RenderTexturePool::instance().get(outputDesc);
+
+		RenderAPICore& rapi = RenderAPICore::instance();
+		rapi.setRenderTarget(ppInfo.colorLUT->renderTexture);
+
+		gRendererUtility().setPass(mMaterial, 0);
+		gRendererUtility().drawScreenQuad(LUTSize);
+	}
+
+	void CreateTonemapLUTMat::release(PostProcessInfo& ppInfo)
+	{
+		RenderTexturePool::instance().release(ppInfo.colorLUT);
+	}
+
+	template<bool GammaOnly>
+	TonemappingMat<GammaOnly>::TonemappingMat()
+	{
+		mMaterial->setParamBlockBuffer("Input", mParams.getBuffer());
+
 		mInputTex = mMaterial->getParamTexture("gInputTex");
+		mColorLUT = mMaterial->getParamTexture("gColorLUT");
 		mEyeAdaptationTex = mMaterial->getParamTexture("gEyeAdaptationTex");
 	}
 
-	void TonemappingMat::_initDefines(ShaderDefines& defines)
+	template<bool GammaOnly>
+	void TonemappingMat<GammaOnly>::_initDefines(ShaderDefines& defines)
 	{
-		// Do nothing
+		if(GammaOnly)
+			defines.set("GAMMA_ONLY", 1);
+
+		defines.set("LUT_SIZE", LUT_SIZE);
 	}
 
-	void TonemappingMat::execute(const SPtr<RenderTextureCore>& sceneColor, const SPtr<ViewportCore>& outputViewport, 
+	template<bool GammaOnly>
+	void TonemappingMat<GammaOnly>::execute(const SPtr<RenderTextureCore>& sceneColor, const SPtr<ViewportCore>& outputViewport, 
 		PostProcessInfo& ppInfo)
 	{
+		mParams.gRawGamma.set(1.0f / ppInfo.settings.gamma);
+
 		// Set parameters
 		SPtr<TextureCore> colorTexture = sceneColor->getBindableColorTexture();
 		mInputTex.set(colorTexture);
 
+		SPtr<TextureCore> colorLUT;
+		if(ppInfo.colorLUT != nullptr)
+			colorLUT = ppInfo.colorLUT->texture;
+
+		mColorLUT.set(colorLUT);
+
 		SPtr<TextureCore> eyeAdaptationTexture = ppInfo.eyeAdaptationTex[ppInfo.lastEyeAdaptationTex]->texture;
 		mEyeAdaptationTex.set(eyeAdaptationTexture);
 
@@ -306,22 +378,33 @@ namespace BansheeEngine
 		gRendererUtility().drawScreenQuad();
 	}
 
+	template class TonemappingMat<true>;
+	template class TonemappingMat<false>;
+
 	void PostProcessing::postProcess(const SPtr<RenderTextureCore>& sceneColor, const SPtr<ViewportCore>& outputViewport, 
 		PostProcessInfo& ppInfo, float frameDelta)
 	{
-		mDownsample.execute(sceneColor, ppInfo);
-		mEyeAdaptHistogram.execute(ppInfo);
-		mDownsample.release(ppInfo);
+		if (ppInfo.settings.tonemapping)
+		{
+			mDownsample.execute(sceneColor, ppInfo);
+			mEyeAdaptHistogram.execute(ppInfo);
+			mDownsample.release(ppInfo);
 
-		mEyeAdaptHistogramReduce.execute(ppInfo);
-		mEyeAdaptHistogram.release(ppInfo);
+			mEyeAdaptHistogramReduce.execute(ppInfo);
+			mEyeAdaptHistogram.release(ppInfo);
 
-		mEyeAdaptation.execute(ppInfo, frameDelta);
-		mEyeAdaptHistogramReduce.release(ppInfo);
+			mEyeAdaptation.execute(ppInfo, frameDelta);
+			mEyeAdaptHistogramReduce.release(ppInfo);
 
-		// TODO - Generate LUT with white balancing, color grading, filmic tonemapping
-		// TODO - Apply LUT during tonemapping
+			// TODO - No need to generate LUT every frame, instead perhaps check for changes and only modify when needed?
+			mCreateLUT.execute(ppInfo);
 
-		mTonemapping.execute(sceneColor, outputViewport, ppInfo);
+			mTonemapping.execute(sceneColor, outputViewport, ppInfo);
+			mCreateLUT.release(ppInfo);
+		}
+		else
+		{
+			mTonemappingGammaOnly.execute(sceneColor, outputViewport, ppInfo);
+		}
 	}
 }