Parcourir la source

Reflection cubemap filtering

BearishSun il y a 9 ans
Parent
commit
7784a96128

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

@@ -410,6 +410,10 @@
             "Path": "PPWhiteBalance.bslinc",
             "Path": "PPWhiteBalance.bslinc",
             "UUID": "b46d1cee-75f7-4387-8b32-7eb646f099f4"
             "UUID": "b46d1cee-75f7-4387-8b32-7eb646f099f4"
         },
         },
+        {
+            "Path": "ReflectionCubemapCommon.bslinc",
+            "UUID": "eef92578-b8ed-45d6-8eed-d85697ade3c5"
+        },		
         {
         {
             "Path": "ResolveCommon.bslinc",
             "Path": "ResolveCommon.bslinc",
             "UUID": "1cde4f3c-534e-4128-8ee1-63e3bb620734"
             "UUID": "1cde4f3c-534e-4128-8ee1-63e3bb620734"
@@ -492,6 +496,10 @@
             "Path": "PPTonemapping.bsl",
             "Path": "PPTonemapping.bsl",
             "UUID": "a8aa01e7-7e72-4f84-89f8-a0097250b0d3"
             "UUID": "a8aa01e7-7e72-4f84-89f8-a0097250b0d3"
         },
         },
+        {
+            "Path": "ReflectionCubemapFilter.bsl",
+            "UUID": "31b4b9ae-2afa-4cd8-b532-fe82a7f42baa"
+        },		
         {
         {
             "Path": "Resolve.bsl",
             "Path": "Resolve.bsl",
             "UUID": "9d5f5101-2d7e-432c-b8ad-1998de9ca5c7"
             "UUID": "9d5f5101-2d7e-432c-b8ad-1998de9ca5c7"

+ 57 - 0
Data/Raw/Engine/Includes/ReflectionCubemapCommon.bslinc

@@ -0,0 +1,57 @@
+Technique : base("ReflectionCubemap") =
+{
+	Language = "HLSL11";
+
+	Pass =
+	{
+		Common = 
+		{
+			#define REFLECTION_MIP_OFFSET 2
+		
+			/**
+			 * Calculates a mip level to sample from based on roughness value.
+			 *
+			 * @param 	roughness	Roughness in range [0, 1]. Higher values yield more roughness.
+			 * @param	numMips		Total number of mip-map levels in the texture we'll be sampling from.
+			 * @return				Index of the mipmap level to sample.
+			 */		
+			float getMipFromRoughness(float roughness, float numMips)
+			{
+				// Note: This is just a heuristic as we don't calculate the exact size of the specular lobe,
+				// and neither do our filtered maps
+			
+				// We add an offset because we want to ignore the highest few mip levels
+				return numMips - 1 - REFLECTION_MIP_OFFSET - log2(roughness);
+			}
+		};
+	};
+};
+
+Technique : base("ReflectionCubemap") =
+{
+	Language = "GLSL";
+
+	Pass =
+	{
+		Common = 
+		{
+			#define REFLECTION_MIP_OFFSET 2
+		
+			/**
+			 * Calculates a mip level to sample from based on roughness value.
+			 *
+			 * @param 	roughness	Roughness in range [0, 1]. Higher values yield more roughness.
+			 * @param	numMips		Total number of mip-map levels in the texture we'll be sampling from.
+			 * @return				Index of the mipmap level to sample.
+			 */		
+			void getMipFromRoughness(float roughness, float numMips, out float mipLevel)
+			{
+				// Note: This is just a heuristic as we don't calculate the exact size of the specular lobe,
+				// and neither do our filtered maps
+			
+				// We add an offset because we want to ignore the highest few mip levels
+				mipLevel = numMips - 1 - REFLECTION_MIP_OFFSET - log2(roughness);
+			}
+		};
+	};
+};

+ 111 - 0
Data/Raw/Engine/Shaders/ReflectionCubemapFilter.bsl

@@ -0,0 +1,111 @@
+#include "$ENGINE$\PPBase.bslinc"
+
+Parameters =
+{
+	int			gCubeFace;
+	SamplerCUBE	gInputSamp : alias("gInputTex");
+	TextureCUBE gInputTex;
+};
+
+Blocks =
+{
+	Block Input;
+};
+
+Technique : inherits("PPBase") =
+{
+	Language = "HLSL11";
+	
+	Pass =
+	{
+		Fragment =
+		{
+			cbuffer Input
+			{
+				int gCubeFace;
+			}	
+		
+			SamplerState gInputSamp;
+			TextureCube gInputTex;
+
+			float4 main(VStoFS input) : SV_Target0
+			{
+				// Note: This is a rough approximation rather than something physically correct.
+				// For a more correct version we should sample along the specular lobe and weight
+				// the contributions. But even that wouldn't be fully correct as specular lobe
+				// shape changes according to view/normal due to occlusion. So instead we just 
+				// approximate everything.
+			
+				float2 scaledUV = input.uv0 * 2.0f - 1.0f;
+				
+				float3 dir;
+				if(gCubeFace == 0)
+					dir = float3(1.0f, -scaledUV.y, -scaledUV.x);
+				else if(gCubeFace == 1)
+					dir = float3(-1.0f, -scaledUV.y, scaledUV.x);
+				else if(gCubeFace == 2)
+					dir = float3(scaledUV.x, 1.0f, scaledUV.y);
+				else if(gCubeFace == 3)
+					dir = float3(scaledUV.x, -1.0f, -scaledUV.y);
+				else if(gCubeFace == 4)
+					dir = float3(scaledUV.x, -scaledUV.y, 1.0f);
+				else
+					dir = float3(-scaledUV.x, -scaledUV.y, -1.0f);
+				
+				return gInputTex.Sample(gInputSamp, dir);
+			}	
+		};
+	};
+};
+
+Technique : inherits("PPBase") =
+{
+	Language = "GLSL";
+	
+	Pass =
+	{
+		Fragment =
+		{
+			in VStoFS
+			{
+				layout(location = 0) vec2 uv0;
+			} FSInput;		
+		
+			layout(location = 0) out vec4 fragColor;
+		
+			layout(binding = 0) uniform Input
+			{
+				int gCubeFace;
+			};
+			
+			layout(binding = 1) uniform samplerCube gInputTex;
+			
+			void main()
+			{
+				// Note: This is a rough approximation rather than something physically correct.
+				// For a more correct version we should sample along the specular lobe and weight
+				// the contributions. But even that wouldn't be fully correct as specular lobe
+				// shape changes according to view/normal due to occlusion. So instead we just 
+				// approximate everything.
+			
+				vec2 scaledUV = FSInput.uv0 * 2.0f - 1.0f;
+				
+				vec3 dir;
+				if(gCubeFace == 0)
+					dir = vec3(1.0f, -scaledUV.y, -scaledUV.x);
+				else if(gCubeFace == 1)
+					dir = vec3(-1.0f, -scaledUV.y, scaledUV.x);
+				else if(gCubeFace == 2)
+					dir = vec3(scaledUV.x, 1.0f, scaledUV.y);
+				else if(gCubeFace == 3)
+					dir = vec3(scaledUV.x, -1.0f, -scaledUV.y);
+				else if(gCubeFace == 4)
+					dir = vec3(scaledUV.x, -scaledUV.y, 1.0f);
+				else
+					dir = vec3(-scaledUV.x, -scaledUV.y, -1.0f);
+				
+				fragColor = texture(gInputTex, dir);
+			}	
+		};
+	};
+};

+ 2 - 0
Source/RenderBeast/CMakeSources.cmake

@@ -11,6 +11,7 @@ set(BS_RENDERBEAST_INC_NOFILTER
 	"Include/BsPostProcessing.h"
 	"Include/BsPostProcessing.h"
 	"Include/BsRendererCamera.h"
 	"Include/BsRendererCamera.h"
 	"Include/BsRendererObject.h"
 	"Include/BsRendererObject.h"
+	"Include/BsReflectionCubemap.h"
 )
 )
 
 
 set(BS_RENDERBEAST_SRC_NOFILTER
 set(BS_RENDERBEAST_SRC_NOFILTER
@@ -25,6 +26,7 @@ set(BS_RENDERBEAST_SRC_NOFILTER
 	"Source/BsPostProcessing.cpp"
 	"Source/BsPostProcessing.cpp"
 	"Source/BsRendererCamera.cpp"
 	"Source/BsRendererCamera.cpp"
 	"Source/BsRendererObject.cpp"
 	"Source/BsRendererObject.cpp"
+	"Source/BsReflectionCubemap.cpp"
 )
 )
 
 
 source_group("Header Files" FILES ${BS_RENDERBEAST_INC_NOFILTER})
 source_group("Header Files" FILES ${BS_RENDERBEAST_INC_NOFILTER})

+ 50 - 0
Source/RenderBeast/Include/BsReflectionCubemap.h

@@ -0,0 +1,50 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+#include "BsRendererMaterial.h"
+#include "BsParamBlocks.h"
+
+namespace bs { namespace ct
+{
+	/** @addtogroup RenderBeast
+	 *  @{
+	 */
+
+	BS_PARAM_BLOCK_BEGIN(ReflectionCubemapFilterParamDef)
+		BS_PARAM_BLOCK_ENTRY(int, gCubeFace)
+	BS_PARAM_BLOCK_END
+
+	extern ReflectionCubemapFilterParamDef gReflectionCubemapFilterDef;
+
+	/** Performs filtering on cubemap faces in order for make them suitable for specular evaluation. */
+	class ReflectionCubemapFilterMat : public RendererMaterial<ReflectionCubemapFilterMat>
+	{
+		RMAT_DEF("ReflectionCubemapFilter.bsl")
+
+	public:
+		ReflectionCubemapFilterMat();
+
+		/** Downsamples the provided texture face and outputs it to the provided target. */
+		void execute(const SPtr<Texture>& source, UINT32 face, const TextureSurface& surface, 
+					 const SPtr<RenderTarget>& target);
+
+	private:
+		SPtr<GpuParamBlockBuffer> mParamBuffer;
+		GpuParamTexture mInputTexture;
+	};
+
+	/** Helper class that handles reflection cubemap generation. */
+	class ReflectionCubemap
+	{
+	public:
+		/**
+		 * Performs filtering on the cubemap, populating its mip-maps with filtered values that can be used for
+		 * evaluating specular reflections.
+		 */
+		static void filterCubemapForSpecular(const SPtr<Texture>& cubemap);
+	};
+
+	/** @} */
+}}

+ 64 - 0
Source/RenderBeast/Source/BsReflectionCubemap.cpp

@@ -0,0 +1,64 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsReflectionCubemap.h"
+#include "BsTexture.h"
+#include "BsGpuParamsSet.h"
+#include "BsRendererCamera.h"
+#include "BsRendererUtility.h"
+
+namespace bs { namespace ct
+{
+	ReflectionCubemapFilterParamDef gReflectionCubemapFilterDef;
+
+	ReflectionCubemapFilterMat::ReflectionCubemapFilterMat()
+	{
+		mParamBuffer = gReflectionCubemapFilterDef.createBuffer();
+
+		mParamsSet->setParamBlockBuffer("Input", mParamBuffer);
+		mParamsSet->getGpuParams()->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
+	}
+
+	void ReflectionCubemapFilterMat::_initDefines(ShaderDefines& defines)
+	{
+		// Do nothing
+	}
+
+	void ReflectionCubemapFilterMat::execute(const SPtr<Texture>& source, UINT32 face, const TextureSurface& surface, 
+											 const SPtr<RenderTarget>& target)
+	{
+		mInputTexture.set(source, surface);
+		gReflectionCubemapFilterDef.gCubeFace.set(mParamBuffer, face);
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(target);
+
+		gRendererUtility().setPass(mMaterial);
+		gRendererUtility().setPassParams(mParamsSet);
+		gRendererUtility().drawScreenQuad();
+	}
+
+	void ReflectionCubemap::filterCubemapForSpecular(const SPtr<Texture>& cubemap)
+	{
+		static ReflectionCubemapFilterMat filterMaterial;
+
+		UINT32 numMips = cubemap->getProperties().getNumMipmaps();
+
+		for (UINT32 mip = 1; mip < numMips; mip++)
+		{
+			for (UINT32 face = 0; face < 6; face++)
+			{
+				RENDER_TEXTURE_DESC cubeFaceRTDesc;
+				cubeFaceRTDesc.colorSurfaces[0].texture = cubemap;
+				cubeFaceRTDesc.colorSurfaces[0].face = face;
+				cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
+				cubeFaceRTDesc.colorSurfaces[0].mipLevel = mip;
+
+				SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
+
+				UINT32 sourceMip = mip - 1;
+				TextureSurface sourceSurface(sourceMip, 1, 0, 6);
+				filterMaterial.execute(cubemap, face, sourceSurface, target);
+			}
+		}
+	}
+}}

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

@@ -31,6 +31,7 @@
 #include "BsGpuBuffer.h"
 #include "BsGpuBuffer.h"
 #include "BsGpuParamsSet.h"
 #include "BsGpuParamsSet.h"
 #include "BsRendererExtension.h"
 #include "BsRendererExtension.h"
+#include "BsReflectionCubemap.h"
 #include "BsMeshData.h"
 #include "BsMeshData.h"
 
 
 using namespace std::placeholders;
 using namespace std::placeholders;
@@ -608,8 +609,6 @@ namespace bs { namespace ct
 		gCoreThread().queueCommand(std::bind(&RenderBeast::renderAllCore, this, gTime().getTime(), gTime().getFrameDelta()));
 		gCoreThread().queueCommand(std::bind(&RenderBeast::renderAllCore, this, gTime().getTime(), gTime().getFrameDelta()));
 	}
 	}
 
 
-	static SPtr<Texture> dbgSkyTex;
-
 	void RenderBeast::renderAllCore(float time, float delta)
 	void RenderBeast::renderAllCore(float time, float delta)
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
@@ -653,9 +652,6 @@ namespace bs { namespace ct
 			mRenderables[i]->perObjectParamBuffer->flushToGPU();
 			mRenderables[i]->perObjectParamBuffer->flushToGPU();
 		}
 		}
 
 
-		//if (dbgSkyTex == nullptr)
-		//	dbgSkyTex = captureSceneCubeMap(Vector3(0, 2, 0), true, 1024);
-
 		// Render everything, target by target
 		// Render everything, target by target
 		for (auto& rtInfo : mRenderTargets)
 		for (auto& rtInfo : mRenderTargets)
 		{
 		{
@@ -990,6 +986,7 @@ namespace bs { namespace ct
 		cubeMapDesc.format = hdr ? PF_FLOAT16_RGBA : PF_R8G8B8A8;
 		cubeMapDesc.format = hdr ? PF_FLOAT16_RGBA : PF_R8G8B8A8;
 		cubeMapDesc.width = size;
 		cubeMapDesc.width = size;
 		cubeMapDesc.height = size;
 		cubeMapDesc.height = size;
+		cubeMapDesc.numMips = PixelUtil::getMaxMipmaps(size, size, 1, cubeMapDesc.format);
 		cubeMapDesc.usage = TU_RENDERTARGET;
 		cubeMapDesc.usage = TU_RENDERTARGET;
 
 
 		SPtr<Texture> cubemap = Texture::create(cubeMapDesc);
 		SPtr<Texture> cubemap = Texture::create(cubeMapDesc);
@@ -1109,6 +1106,8 @@ namespace bs { namespace ct
 			render(&view, 0.0f);
 			render(&view, 0.0f);
 		}
 		}
 
 
+		ReflectionCubemap::filterCubemapForSpecular(cubemap);
+
 		return cubemap;
 		return cubemap;
 	}
 	}
 
 

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

@@ -34,7 +34,7 @@ namespace bs { namespace ct
 
 
 	void SkyboxMat::setParams(const SPtr<Texture>& texture)
 	void SkyboxMat::setParams(const SPtr<Texture>& texture)
 	{
 	{
-		mSkyTextureParam.set(texture);
+		mSkyTextureParam.set(texture, TextureSurface(1, 1, 0, 0));
 		gRendererUtility().setPassParams(mParamsSet);
 		gRendererUtility().setPassParams(mParamsSet);
 	}
 	}