Browse Source

Refactoring the renderer to the new node based approach (WIP)

BearishSun 8 years ago
parent
commit
339c2bfc47

+ 13 - 0
Source/BansheeEngine/Include/BsIBLUtility.h

@@ -263,6 +263,16 @@ namespace bs { namespace ct
 		 */
 		static void scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip);
 
+		/**
+		 * Retrieves a 2D 2-channel texture containing a pre-integrated G and F factors of the microfactet BRDF. This is an
+		 * approximation used for image based lighting, so we can avoid sampling environment maps for each light. Works in
+		 * tandem with the importance sampled reflection cubemaps.
+		 * 
+		 * (u, v) = (NoV, roughness)
+		 * (r, g) = (scale, bias)
+		 */
+		static SPtr<Texture> getPreintegratedEnvBRDF();
+
 		static const UINT32 REFLECTION_CUBEMAP_SIZE;
 		static const UINT32 IRRADIANCE_CUBEMAP_SIZE;
 	private:
@@ -276,6 +286,9 @@ namespace bs { namespace ct
 		 */
 		static void downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip);
 
+		/** Generates the texture returned by getPreIntegratedEnvBRDF(). */
+		static SPtr<Texture> generatePreintegratedEnvBRDF();
+
 		struct Members;
 		static Members* m;
 	};

+ 146 - 0
Source/BansheeEngine/Source/BsIBLUtility.cpp

@@ -18,6 +18,8 @@ namespace bs { namespace ct
 		IrradianceReduceSHMat<3> shReduce3;
 		IrradianceReduceSHMat<5> shReduce5;
 		IrradianceProjectSHMat shProject5;
+
+		SPtr<Texture> preIntegratedGF;
 	};
 
 	IBLUtility::Members* IBLUtility::m = nullptr;
@@ -25,6 +27,8 @@ namespace bs { namespace ct
 	void IBLUtility::startUp()
 	{
 		m = bs_new<Members>();
+
+		m->preIntegratedGF = generatePreintegratedEnvBRDF();
 	}
 
 	void IBLUtility::shutDown()
@@ -418,6 +422,11 @@ namespace bs { namespace ct
 			downsampleCubemap(scratchTex, srcMip, dst, dstMip);
 	}
 
+	SPtr<Texture> IBLUtility::getPreintegratedEnvBRDF()
+	{
+		return m->preIntegratedGF;
+	}
+
 	void IBLUtility::downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip)
 	{
 		for (UINT32 face = 0; face < 6; face++)
@@ -434,4 +443,141 @@ namespace bs { namespace ct
 			m->downsampleMat.execute(src, face, sourceSurface, target);
 		}
 	}
+
+	// Reverse bits functions used for Hammersley sequence
+	float reverseBits(UINT32 bits)
+	{
+		bits = (bits << 16u) | (bits >> 16u);
+		bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+		bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+		bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+		bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+
+		return (float)(double(bits) / (double)0x100000000LL);
+	}
+
+	void hammersleySequence(UINT32 i, UINT32 count, float& e0, float& e1)
+	{
+		e0 = i / (float)count;
+		e1 = reverseBits(i);
+	}
+
+	Vector3 sphericalToCartesian(float cosTheta, float sinTheta, float phi)
+	{
+		Vector3 output;
+		output.x = sinTheta * cos(phi);
+		output.y = sinTheta * sin(phi);
+		output.z = cosTheta;
+
+		return output;
+	}
+
+	// Generates an angle in spherical coordinates, importance sampled for the specified roughness based on some uniformly
+	// distributed random variables in range [0, 1].
+	void importanceSampleGGX(float e0, float e1, float roughness4, float& cosTheta, float& phi)
+	{
+		// See GGXImportanceSample.nb for derivation (essentially, take base GGX, normalize it, generate PDF, split PDF into
+		// marginal probability for theta and conditional probability for phi. Plug those into the CDF, invert it.)				
+		cosTheta = sqrt((1.0f - e0) / (1.0f + (roughness4 - 1.0f) * e0));
+		phi = 2.0f * Math::PI * e1;
+	}
+
+	float calcMicrofacetShadowingSmithGGX(float roughness4, float NoV, float NoL)
+	{
+		// Note: See lighting shader for derivation. Includes microfacet BRDF divisor.
+		float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
+		float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
+		return 1.0f / (g1V * g1L);
+	}
+
+	SPtr<Texture> IBLUtility::generatePreintegratedEnvBRDF()
+	{
+		TEXTURE_DESC desc;
+		desc.type = TEX_TYPE_2D;
+		desc.format = PF_FLOAT16_RG;
+		desc.width = 128;
+		desc.height = 32;
+
+		SPtr<Texture> texture = Texture::create(desc);
+		PixelData pixelData = texture->lock(GBL_WRITE_ONLY_DISCARD);
+
+		for (UINT32 y = 0; y < desc.height; y++)
+		{
+			float roughness = (float)(y + 0.5f) / desc.height;
+			float m = roughness * roughness;
+			float m2 = m*m;
+
+			for (UINT32 x = 0; x < desc.width; x++)
+			{
+				float NoV = (float)(x + 0.5f) / desc.width;
+
+				Vector3 V;
+				V.x = sqrt(1.0f - NoV * NoV); // sine
+				V.y = 0.0f;
+				V.z = NoV;
+
+				// These are the two integrals resulting from the second part of the split-sum approximation. Described in
+				// Epic's 2013 paper:
+				//    http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+				float scale = 0.0f;
+				float offset = 0.0f;
+
+				// We use the same importance sampling function we use for reflection cube importance sampling, only we
+				// sample G and F, instead of D factors of the microfactet BRDF. See GGXImportanceSample.nb for derivation.
+				constexpr UINT32 NumSamples = 128;
+				for (UINT32 i = 0; i < NumSamples; i++)
+				{
+					float e0, e1;
+					hammersleySequence(i, NumSamples, e0, e1);
+
+					float cosTheta, phi;
+					importanceSampleGGX(e0, e1, m2, cosTheta, phi);
+
+					float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
+					Vector3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
+					Vector3 L = 2.0f * Vector3::dot(V, H) * H - V;
+
+					float VoH = std::max(Vector3::dot(V, H), 0.0f);
+					float NoL = std::max(L.z, 0.0f); // N assumed (0, 0, 1)
+					float NoH = std::max(H.z, 0.0f); // N assumed (0, 0, 1)
+
+					// Set second part of the split sum integral is split into two parts:
+					//   F0*I[G * (1 - (1 - v.h)^5) * cos(theta)] + I[G * (1 - v.h)^5 * cos(theta)] (F0 * scale + bias)
+
+					// We calculate the fresnel scale (1 - (1 - v.h)^5) and bias ((1 - v.h)^5) parts
+					float fc = pow(1.0f - VoH, 5.0f);
+					float fresnelScale = 1.0f - fc;
+					float fresnelOffset = fc;
+
+					// We calculate the G part
+					float G = calcMicrofacetShadowingSmithGGX(m2, NoV, NoL);
+
+					// When we factor out G and F, then divide D by PDF, this is what's left
+					// Note: This is based on PDF: D * NoH / (4 * VoH). (4 * VoH) factor comes from the Jacobian of the
+					// transformation from half vector to light vector
+					float pdfFactor = 4.0f * VoH / NoH;
+
+					if (NoL > 0.0f)
+					{
+						scale += NoL * pdfFactor * G * fresnelScale;
+						offset += NoL * pdfFactor * G * fresnelOffset;
+					}
+				}
+
+				scale /= NumSamples;
+				offset /= NumSamples;
+
+				Color color;
+				color.r = Math::clamp01(scale);
+				color.g = Math::clamp01(offset);
+
+				pixelData.setColorAt(color, x, y);
+			}
+		}
+
+		texture->unlock();
+
+		return texture;
+	}
+
 }}

+ 25 - 1
Source/BansheeUtility/Include/BsStringID.h

@@ -101,6 +101,9 @@ namespace bs
 			return mData->chars;
 		}
 
+		/** Returns the unique identifier of the string. */
+		UINT32 id() const { return mData ? mData->id : -1; }
+
 		static const StringID NONE;
 
 	private:
@@ -193,4 +196,25 @@ namespace bs
 
 	/** @endcond */
 	/** @} */
-}
+}
+
+/** @cond STDLIB */
+/** @addtogroup String
+ *  @{
+ */
+
+namespace std
+{
+/**	Hash value generator for StringID. */
+template<>
+struct hash<bs::StringID>
+{
+	size_t operator()(const bs::StringID& value) const
+	{
+		return (size_t)value.id();
+	}
+};
+}
+
+/** @} */
+/** @endcond */

+ 4 - 0
Source/RenderBeast/CMakeSources.cmake

@@ -17,6 +17,8 @@ set(BS_RENDERBEAST_INC_NOFILTER
 	"Include/BsRendererScene.h"
 	"Include/BsStandardDeferredLighting.h"
 	"Include/BsLightProbes.h"
+	"Include/BsRenderCompositor.h"
+	"Include/BsGlobalMaterials.h"
 )
 
 set(BS_RENDERBEAST_SRC_NOFILTER
@@ -37,6 +39,8 @@ set(BS_RENDERBEAST_SRC_NOFILTER
 	"Source/BsRendererScene.cpp"
 	"Source/BsStandardDeferredLighting.cpp"
 	"Source/BsLightProbes.cpp"
+	"Source/BsRenderCompositor.cpp"
+	"Source/BsGlobalMaterials.cpp"
 )
 
 source_group("Header Files" FILES ${BS_RENDERBEAST_INC_NOFILTER})

+ 49 - 0
Source/RenderBeast/Include/BsGlobalMaterials.h

@@ -0,0 +1,49 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+#include "BsModule.h"
+#include "BsRendererView.h"
+
+namespace bs { namespace ct
+{
+	/** Contains common materials used throughout the renderer. */
+	class GlobalMaterials : public Module<GlobalMaterials>
+	{
+	public:
+		GlobalMaterials();
+		~GlobalMaterials();
+
+		/** Returns a material that can be used for rendering a skybox using a specific texture. */
+		SkyboxMat<false>* getSkyboxTexture() const { return mSkyboxTextureMat; }
+
+		/** Returns a material that can be used for rendering a skybox using a solid color. */
+		SkyboxMat<true>* getSkyboxColor() const { return mSkyboxColorMat; }
+
+		/** Returns a material that can be used for converting a texture encoded in a buffer, back into texture form. */
+		FlatFramebufferToTextureMat* getUnflattenBuffer() const { return mFlatFramebufferToTextureMat; }
+
+		/**
+		 * Returns a version of the tile-deferred image based lighting material that matches the parameters.
+		 *
+		 * @param[in]   msaa	Number of samples per pixel.
+		 */
+		ITiledDeferredImageBasedLightingMat* getTileDeferredIBL(UINT32 msaa);
+
+		/**
+		 * Returns a version of the tile-deferred lighting material that matches the parameters.
+		 *
+		 * @param[in]   msaa	Number of samples per pixel.
+		 */
+		ITiledDeferredLightingMat* getTileDeferredLighting(UINT32 msaa);
+	private:
+		SkyboxMat<false>* mSkyboxTextureMat = nullptr;
+		SkyboxMat<true>* mSkyboxColorMat = nullptr;
+
+		FlatFramebufferToTextureMat* mFlatFramebufferToTextureMat = nullptr;
+
+		ITiledDeferredImageBasedLightingMat* mTiledDeferredIBLMats[4];
+		ITiledDeferredLightingMat* mTiledDeferredLightingMats[4];
+	};
+}}

+ 33 - 80
Source/RenderBeast/Include/BsImageBasedLighting.h

@@ -5,9 +5,11 @@
 #include "BsRenderBeastPrerequisites.h"
 #include "BsRendererMaterial.h"
 #include "BsParamBlocks.h"
+#include "BsLightRendering.h"
 
 namespace bs { namespace ct
 {
+	struct SkyInfo;
 	struct SceneInfo;
 	class RendererViewGroup;
 
@@ -93,7 +95,7 @@ namespace bs { namespace ct
 	struct ImageBasedLightingParams
 	{
 		/** 
-		 * Initializes the parameters from the provided @p params object. 
+		 * Initializes the parameters from the provided parameters. 
 		 *
 		 * @param[in]	paramsSet	GPU parameters object to look for the parameters in.
 		 * @param[in]	programType	Type of the GPU program to look up the parameters for.
@@ -103,12 +105,9 @@ namespace bs { namespace ct
 		void populate(const SPtr<GpuParamsSet>& paramsSet, GpuProgramType programType, bool optional, bool gridIndices);
 
 		GpuParamTexture skyReflectionsTexParam;
-		GpuParamSampState skyReflectionsSampParam;
-
 		GpuParamTexture skyIrradianceTexParam;
 
 		GpuParamTexture reflectionProbeCubemapsTexParam;
-		GpuParamSampState reflectionProbeCubemapsSampParam;
 
 		GpuParamTexture preintegratedEnvBRDFParam;
 		GpuParamBuffer reflectionProbesParam;
@@ -117,39 +116,38 @@ namespace bs { namespace ct
 		UINT32 reflProbeParamsBindingIdx;
 	};
 
+	/** Parameter buffer containing information about reflection probes. */
+	struct ReflProbeParamBuffer
+	{
+		ReflProbeParamBuffer();
+
+		/** Updates the parameter buffer contents with require refl. probe data. */
+		void populate(const SkyInfo& sky, const VisibleReflProbeData& probeData, 
+			const SPtr<Texture>& reflectionCubemaps, bool capturingReflections);
+
+		SPtr<GpuParamBlockBuffer> buffer;
+	};
+
 	/** Functionality common to all versions of TiledDeferredImageBasedLightingMat<T>. */
 	class TiledDeferredImageBasedLighting
 	{
 	public:
+		/** Containers for parameters to be passed to the execute() method. */
+		struct Inputs
+		{
+			GBufferInput gbuffer;
+			SPtr<Texture> lightAccumulation;
+			SPtr<Texture> sceneColorTex;
+			SPtr<GpuBuffer> sceneColorBuffer;
+			SPtr<Texture> preIntegratedGF;
+		};
+
 		TiledDeferredImageBasedLighting(const SPtr<Material>& material, const SPtr<GpuParamsSet>& paramsSet, 
 			UINT32 sampleCount);
 
 		/** Binds the material for rendering, sets up parameters and executes it. */
-		void execute(const SPtr<RenderTargets>& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera,
-					 const SPtr<Texture>& preintegratedGF);
-
-		/** Binds all the active reflection probes. */
-		void setReflectionProbes(const VisibleReflProbeData& probeData, const SPtr<Texture>& reflectionCubemaps, 
-			bool capturingReflections);
-
-		/** Binds the sky reflection & irradiance textures. Set textures to null if not available. */
-		void setSky(const SPtr<Texture>& skyReflections, const SPtr<Texture>& skyIrradiance, float brightness);
-
-		/** Returns a param buffer containing information required to evaluate reflection probe data. */
-		SPtr<GpuParamBlockBuffer> getReflectionsParamBuffer() const { return mReflectionsParamBuffer; }
-
-		/** Returns a sampler state object that should be used for evaluating reflection maps. */
-		SPtr<SamplerState> getReflectionsSamplerState() const { return mReflectionSamplerState; }
-
-		/**
-		 * Generates a 2D 2-channel texture containing a pre-integrated G and F factors of the microfactet BRDF. This is an
-		 * approximation used for image based lighting, so we can avoid sampling environment maps for each light. Works in
-		 * tandem with the importance sampled reflection cubemaps.
-		 * 
-		 * (u, v) = (NoV, roughness)
-		 * (r, g) = (scale, bias)
-		 */
-		static SPtr<Texture> generatePreintegratedEnvBRDF();
+		void execute(const RendererView& view, const SceneInfo& sceneInfo, const VisibleReflProbeData& probeData, 
+			const Inputs& inputs);
 
 		static const UINT32 TILE_SIZE;
 	private:
@@ -170,8 +168,7 @@ namespace bs { namespace ct
 		GpuParamBuffer mOutputBufferParam;
 
 		SPtr<GpuParamBlockBuffer> mParamBuffer;
-		SPtr<GpuParamBlockBuffer> mReflectionsParamBuffer;
-		SPtr<SamplerState> mReflectionSamplerState;
+		ReflProbeParamBuffer mReflProbeParamBuffer;
 	};
 
 	/** Interface implemented by all versions of TTiledDeferredImageBasedLightingMat<T>. */
@@ -181,21 +178,8 @@ namespace bs { namespace ct
 		virtual ~ITiledDeferredImageBasedLightingMat() {}
 
 		/** @copydoc TiledDeferredImageBasedLighting::execute() */
-		virtual void execute(const SPtr<RenderTargets>& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera,
-			const SPtr<Texture>& preintegratedGF) = 0;
-
-		/** @copydoc TiledDeferredImageBasedLighting::setReflectionProbes() */
-		virtual void setReflectionProbes(const VisibleReflProbeData& probeData, const SPtr<Texture>& reflectionCubemaps,
-			bool capturingReflections) = 0;
-
-		/** @copydoc TiledDeferredImageBasedLighting::setSky() */
-		virtual void setSky(const SPtr<Texture>& skyReflections, const SPtr<Texture>& skyIrradiance, float brightness) = 0;
-
-		/** @copydoc TiledDeferredImageBasedLighting::getReflectionsParamBuffer() */
-		virtual SPtr<GpuParamBlockBuffer> getReflectionsParamBuffer() const = 0;
-
-		/** @copydoc TiledDeferredImageBasedLighting::getReflectionsSamplerState() */
-		virtual SPtr<SamplerState> getReflectionsSamplerState() const = 0;
+		virtual void execute(const RendererView& view, const SceneInfo& sceneInfo, const VisibleReflProbeData& probeData, 
+			const TiledDeferredImageBasedLighting::Inputs& inputs) = 0;
 	};
 
 	/** Shader that performs a lighting pass over data stored in the Gbuffer. */
@@ -209,42 +193,11 @@ namespace bs { namespace ct
 		TTiledDeferredImageBasedLightingMat();
 
 		/** @copydoc ITiledDeferredImageBasedLightingMat::execute() */
-		void execute(const SPtr<RenderTargets>& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera,
-			const SPtr<Texture>& preintegratedGF) override;
-
-		/** @copydoc ITiledDeferredImageBasedLightingMat::setReflectionProbes() */
-		void setReflectionProbes(const VisibleReflProbeData& probeData, const SPtr<Texture>& reflectionCubemaps, 
-			bool capturingReflections) override;
-
-		/** @copydoc ITiledDeferredImageBasedLightingMat::setSky() */
-		void setSky(const SPtr<Texture>& skyReflections, const SPtr<Texture>& skyIrradiance, float brightness) override;
-
-		/** @copydoc ITiledDeferredImageBasedLightingMat::getReflectionsParamBuffer() */
-		SPtr<GpuParamBlockBuffer> getReflectionsParamBuffer() const override;
-
-		/** @copydoc ITiledDeferredImageBasedLightingMat::getReflectionsSamplerState() */
-		SPtr<SamplerState> getReflectionsSamplerState() const override;
+		void execute(const RendererView& view, const SceneInfo& sceneInfo, const VisibleReflProbeData& probeData, 
+			const TiledDeferredImageBasedLighting::Inputs& inputs) override;
 	private:
 		TiledDeferredImageBasedLighting mInternal;
 	};
 
-	/** Contains instances for all types of tile deferred image based lighting materials. */
-	class TiledDeferredImageBasedLightingMaterials
-	{
-	public:
-		TiledDeferredImageBasedLightingMaterials();
-		~TiledDeferredImageBasedLightingMaterials();
-
-		/**
-		 * Returns a version of the tile-deferred image based lighting material that matches the parameters.
-		 *
-		 * @param[in]   msaa						Number of samples per pixel.
-		 */
-		ITiledDeferredImageBasedLightingMat* get(UINT32 msaa);
-
-	private:
-		ITiledDeferredImageBasedLightingMat* mInstances[4];
-	};
-
 	/** @} */
-}}
+}}

+ 17 - 36
Source/RenderBeast/Include/BsLightRendering.h

@@ -56,6 +56,15 @@ namespace bs { namespace ct
 		Light* internal;
 	};
 
+	/** Container for all GBuffer textures. */
+	struct GBufferInput
+	{
+		SPtr<Texture> albedo;
+		SPtr<Texture> normals;
+		SPtr<Texture> roughMetal;
+		SPtr<Texture> depth;
+	};
+
 	/** Allows you to easily bind GBuffer textures to some material. */
 	class GBufferParams
 	{
@@ -65,6 +74,8 @@ namespace bs { namespace ct
 		/** Binds the GBuffer textures to the pipeline. */
 		void bind(const RenderTargets& renderTargets);
 
+		/** Binds the GBuffer textures to the pipeline. */
+		void bind(const GBufferInput& gbuffer);
 	private:
 		SPtr<Material> mMaterial;
 		SPtr<GpuParamsSet> mParamsSet;
@@ -141,11 +152,8 @@ namespace bs { namespace ct
 		TiledDeferredLighting(const SPtr<Material>& material, const SPtr<GpuParamsSet>& paramsSet, UINT32 sampleCount);
 
 		/** Binds the material for rendering, sets up parameters and executes it. */
-		void execute(const SPtr<RenderTargets>& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera, bool noLighting,
-			bool noShadows);
-
-		/** Binds all the active lights. */
-		void setLights(const VisibleLightData& lightData);
+		void execute(const RendererView& view, const VisibleLightData& lightData, const GBufferInput& gbuffer, 
+			const SPtr<Texture>& lightAccumTex, const SPtr<GpuBuffer>& lightAccumBuffer);
 
 		static const UINT32 TILE_SIZE;
 	private:
@@ -155,9 +163,6 @@ namespace bs { namespace ct
 
 		GBufferParams mGBufferParams;
 
-		Vector4I mUnshadowedLightCounts;
-		Vector4I mLightCounts;
-		Vector2I mLightStrides;
 		GpuParamBuffer mLightBufferParam;
 		GpuParamLoadStoreTexture mOutputTextureParam;
 		GpuParamBuffer mOutputBufferParam;
@@ -172,11 +177,8 @@ namespace bs { namespace ct
 		virtual ~ITiledDeferredLightingMat() {}
 
 		/** @copydoc TiledDeferredLighting::execute() */
-		virtual void execute(const SPtr<RenderTargets>& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera,
-			bool noLighting, bool noShadows) = 0;
-
-		/** @copydoc TiledDeferredLighting::setLights() */
-		virtual void setLights(const VisibleLightData& lightData) = 0;
+		virtual void execute(const RendererView& view, const VisibleLightData& lightData, const GBufferInput& gbuffer,
+			const SPtr<Texture>& lightAccumTex, const SPtr<GpuBuffer>& lightAccumBuffer) = 0;
 	};
 
 	/** Shader that performs a lighting pass over data stored in the Gbuffer. */
@@ -189,33 +191,12 @@ namespace bs { namespace ct
 		TTiledDeferredLightingMat();
 
 		/** @copydoc ITiledDeferredLightingMat::execute() */
-		void execute(const SPtr<RenderTargets>& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera,
-			bool noLighting, bool noShaodws) override;
-
-		/** @copydoc ITiledDeferredLightingMat::setLights() */
-		void setLights(const VisibleLightData& lightData) override;
+		void execute(const RendererView& view, const VisibleLightData& lightData, const GBufferInput& gbuffer,
+			const SPtr<Texture>& lightAccumTex, const SPtr<GpuBuffer>& lightAccumBuffer) override;
 	private:
 		TiledDeferredLighting mInternal;
 	};
 
-	/** Contains instances for all types of tile deferred lighting materials. */
-	class TiledDeferredLightingMaterials
-	{
-	public:
-		TiledDeferredLightingMaterials();
-		~TiledDeferredLightingMaterials();
-
-		/**
-		 * Returns a version of the tile-deferred lighting material that matches the parameters.
-		 * 
-		 * @param[in]   msaa					Number of samples per pixel.
-		 */
-		ITiledDeferredLightingMat* get(UINT32 msaa);
-
-	private:
-		ITiledDeferredLightingMat* mInstances[4];
-	};
-
 	BS_PARAM_BLOCK_BEGIN(FlatFramebufferToTextureParamDef)
 		BS_PARAM_BLOCK_ENTRY(Vector2I, gFramebufferSize)
 		BS_PARAM_BLOCK_ENTRY(INT32, gSampleCount)

+ 6 - 31
Source/RenderBeast/Include/BsRenderBeast.h

@@ -168,7 +168,7 @@ namespace bs
 		 *			
 		 * @note	Core thread only.
 		 */
-		void renderView(RendererView* viewInfo, float frameDelta);
+		void renderView(const RendererViewGroup& viewGroup, RendererView* viewInfo, float frameDelta);
 
 		/**
 		 * Renders all overlay callbacks of the provided view.
@@ -195,48 +195,23 @@ namespace bs
 		void destroyCore();
 
 		/** Updates light probes, rendering & filtering ones that are dirty and updating the global probe cubemap array. */
-		void updateLightProbes(const FrameInfo& frameInfo);
+		void renderReflectionProbes(const FrameInfo& frameInfo);
+
+		/** Renders the skybox filtered reflections and irradiance cubemap, if they require updating. */
+		void updateSkybox();
 
 		// Core thread only fields
 
 		// Scene data
 		SPtr<RendererScene> mScene;
 
-		//// Reflection probes
-		Vector<bool> mCubemapArrayUsedSlots;
-		SPtr<Texture> mReflCubemapArrayTex;
-
-		//// Sky light
-		Skybox* mSkybox = nullptr;
-		SPtr<Texture> mSkyboxTexture;
-		SPtr<Texture> mSkyboxFilteredReflections;
-		SPtr<Texture> mSkyboxIrradiance;
-
-		// Materials & GPU data
 		//// Base pass
 		ObjectRenderer* mObjectRenderer = nullptr;
 
-		//// Lighting
-		TiledDeferredLightingMaterials* mTiledDeferredLightingMats = nullptr;
-		LightGrid* mLightGrid = nullptr;
-		VisibleLightData* mVisibleLightInfo = nullptr;
-
-		//// Image based lighting
-		TiledDeferredImageBasedLightingMaterials* mTileDeferredImageBasedLightingMats = nullptr;
-		VisibleReflProbeData* mVisibleReflProbeInfo = nullptr;
-		SPtr<Texture> mPreintegratedEnvBRDF;
-
-		//// Sky
-		SkyboxMat<false>* mSkyboxMat;
-		SkyboxMat<true>* mSkyboxSolidColorMat;
-
-		//// Other
-		FlatFramebufferToTextureMat* mFlatFramebufferToTextureMat = nullptr;
-
 		SPtr<RenderBeastOptions> mCoreOptions;
 
 		// Helpers to avoid memory allocations
-		RendererViewGroup mMainViewGroup;
+		RendererViewGroup* mMainViewGroup = nullptr;
 
 		// Sim thread only fields
 		SPtr<RenderBeastOptions> mOptions;

+ 364 - 0
Source/RenderBeast/Include/BsRenderCompositor.h

@@ -0,0 +1,364 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+
+namespace bs { namespace ct
+{
+	struct SceneInfo;
+	class RendererViewGroup;
+	class RenderCompositorNode;
+	struct PooledStorageBuffer;
+
+	/** @addtogroup RenderBeast
+	 *  @{
+	 */
+	
+	// TODO - Doc
+	struct RenderCompositorNodeInputs
+	{
+		RenderCompositorNodeInputs(const RendererViewGroup& viewGroup, const RendererView& view, const SceneInfo& scene, 
+			const RenderBeastOptions& options, const SmallVector<RenderCompositorNode*, 4>& inputNodes)
+			: viewGroup(viewGroup), view(view), scene(scene), options(options), inputNodes(inputNodes)
+		{ }
+
+		const RendererViewGroup& viewGroup;
+		const RendererView& view;
+		const SceneInfo& scene;
+		const RenderBeastOptions& options;
+
+		SmallVector<RenderCompositorNode*, 4> inputNodes;
+	};
+
+	// TODO - Doc
+	class RenderCompositorNode
+	{
+	public:
+		virtual ~RenderCompositorNode() { }
+
+	protected:
+		friend class RenderCompositor;
+
+		// TODO - Doc
+		virtual void render(const RenderCompositorNodeInputs& inputs) = 0;
+
+		// TODO - Doc. Note this is meant to clean up data within a single render pass, if there is permanent data lasting
+		// multiple frames, only clear that when the node is destroyed.
+		virtual void clear() = 0;
+	};
+
+	// TODO - Doc
+	class RenderCompositor
+	{
+		// TODO - Doc
+		struct NodeInfo
+		{
+			RenderCompositorNode* node;
+			UINT32 lastUseIdx;
+			SmallVector<RenderCompositorNode*, 4> inputs;
+		};
+	public:
+		~RenderCompositor()
+		{
+			clear();
+		}
+
+		// TODO - Doc
+		void build(const RendererView& view, const StringID& finalNode);
+
+		// TODO - Doc
+		void execute(const RendererViewGroup& viewGroup, const RendererView& view, const SceneInfo& scene,
+			const RenderBeastOptions& options) const;
+
+	private:
+		// TODO - Doc
+		void clear()
+		{
+			for (auto& entry : mNodeInfos)
+				bs_delete(entry.node);
+
+			mNodeInfos.clear();
+			mIsValid = false;
+		}
+
+		Vector<NodeInfo> mNodeInfos;
+		bool mIsValid = false;
+
+		/************************************************************************/
+		/* 							NODE TYPES	                     			*/
+		/************************************************************************/
+	public:
+		// TODO - Doc
+		struct NodeType
+		{
+			virtual ~NodeType() {};
+
+			// TODO - Doc
+			virtual RenderCompositorNode* create() const = 0;
+
+			// TODO - Doc
+			virtual SmallVector<StringID, 4> getDependencies(const RendererView& view) const = 0;
+
+			StringID id;
+		};
+
+		// TODO - Doc
+		template<class T>
+		struct TNodeType : NodeType
+		{
+			// TODO - Doc
+			RenderCompositorNode* create() const override { return bs_new<T>(); }
+
+			// TODO - Doc
+			SmallVector<StringID, 4> getDependencies(const RendererView& view) const override
+			{
+				return T::getDependencies(view);
+			}
+		};
+
+		// TODO - Doc
+		template<class T>
+		static void registerNodeType()
+		{
+			auto findIter = mNodeTypes.find(T::getNodeId());
+			if (findIter != mNodeTypes.end())
+				LOGERR("Found two render compositor nodes with the same name \"" + String(T::getNodeId().cstr()) + "\".");
+
+			mNodeTypes[T::getNodeId()] = bs_new<TNodeType<T>>();
+		}
+
+		// TODO - Doc
+		static void cleanUp()
+		{
+			for (auto& entry : mNodeTypes)
+				bs_delete(entry.second);
+
+			mNodeTypes.clear();
+		}
+
+		static UnorderedMap<StringID, NodeType*> mNodeTypes;
+	};
+
+	/************************************************************************/
+	/* 							BASE PASS NODES	                     		*/
+	/************************************************************************/
+
+	// TODO - Doc
+	class RCNodeSceneDepth : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		SPtr<PooledRenderTexture> depthTex;
+
+		static StringID getNodeId() { return "SceneDepth"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeGBuffer : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		SPtr<PooledRenderTexture> albedoTex;
+		SPtr<PooledRenderTexture> normalTex;
+		SPtr<PooledRenderTexture> roughMetalTex;
+
+		SPtr<RenderTexture> renderTarget;
+
+		static StringID getNodeId() { return "GBuffer"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeSceneColor : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		/** 
+		 * Contains scene color. If MSAA is used this texture will be null until the flattened data from the buffer is
+		 * first resolved into this texture.
+		 */
+		SPtr<PooledRenderTexture> sceneColorTex;
+
+		/** 
+		 * Flattened, buffer version of sceneColorTex. Only available when MSAA is used, since random writes to multisampled
+		 * textures aren't supported on all render backends.
+		 */
+		SPtr<PooledStorageBuffer> flattenedSceneColorBuffer;
+
+		SPtr<RenderTexture> renderTarget;
+
+		static StringID getNodeId() { return "SceneColor"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	/************************************************************************/
+	/* 							LIGHTING NODES                     			*/
+	/************************************************************************/
+
+	// TODO - Doc
+	class RCNodeLightAccumulation : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		/** 
+		 * Contains lighting information accumulated from multiple lights. If MSAA is used this texture will be null until
+		 * the flattened data from the buffer is first resolved into this texture.
+		 */
+		SPtr<PooledRenderTexture> lightAccumulationTex;
+
+		/** 
+		 * Flattened, buffer version of lightAccumulationTex. Only available when MSAA is used, since random writes to
+		 * multisampled textures aren't supported on all render backends.
+		 */
+		SPtr<PooledStorageBuffer> flattenedLightAccumBuffer;
+
+		SPtr<RenderTexture> renderTarget;
+
+		static StringID getNodeId() { return "LightAccumulation"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeTiledDeferredLighting : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		RCNodeLightAccumulation* output;
+
+		static StringID getNodeId() { return "TiledDeferredLighting"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeStandardDeferredLighting : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		RCNodeLightAccumulation* output;
+
+		static StringID getNodeId() { return "StandardDeferredLighting"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+
+		SPtr<PooledRenderTexture> mLightOcclusionTex;
+		SPtr<RenderTexture> mRenderTarget;
+	};
+
+	// TODO - Doc
+	class RCNodeUnflattenLightAccum : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		RCNodeLightAccumulation* output;
+
+		static StringID getNodeId() { return "UnflattenLightAccum"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeTiledDeferredIBL : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		RCNodeLightAccumulation* output;
+
+		static StringID getNodeId() { return "TiledDeferredIBL"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeUnflattenSceneColor : public RenderCompositorNode
+	{
+	public:
+		// Outputs
+		RCNodeSceneColor* output;
+
+		static StringID getNodeId() { return "UnflattenSceneColor"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeSkybox : public RenderCompositorNode
+	{
+	public:
+		static StringID getNodeId() { return "Skybox"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	// TODO - Doc
+	class RCNodeFinalResolve : public RenderCompositorNode
+	{
+	public:
+		static StringID getNodeId() { return "FinalResolve"; }
+		static SmallVector<StringID, 4> getDependencies(const RendererView& view);
+	protected:
+		/** @copydoc RenderCompositorNode::render */
+		void render(const RenderCompositorNodeInputs& inputs) override;
+
+		/** @copydoc RenderCompositorNode::clear */
+		void clear() override;
+	};
+
+	/** @} */
+}}

+ 30 - 0
Source/RenderBeast/Include/BsRendererScene.h

@@ -21,6 +21,19 @@ namespace bs
 	 *  @{
 	 */
 
+	// Limited by max number of array elements in texture for DX11 hardware
+	constexpr UINT32 MaxReflectionCubemaps = 2048 / 6;
+
+	/** Contains information about the skybox in the current scene. */
+	struct SkyInfo
+	{
+		Skybox* skybox = nullptr;
+
+		SPtr<Texture> radiance;
+		SPtr<Texture> filteredReflections;
+		SPtr<Texture> irradiance;
+	};
+
 	/** Contains most scene objects relevant to the renderer. */
 	struct SceneInfo
 	{
@@ -43,6 +56,11 @@ namespace bs
 		// Reflection probes
 		Vector<RendererReflectionProbe> reflProbes;
 		Vector<Sphere> reflProbeWorldBounds;
+		Vector<bool> reflProbeCubemapArrayUsedSlots;
+		SPtr<Texture> reflProbeCubemapsTex;
+
+		// Sky
+		SkyInfo sky;
 
 		// Buffers for various transient data that gets rebuilt every frame
 		//// Rebuilt every frame
@@ -98,6 +116,15 @@ namespace bs
 		/** Updates the index at which the reflection probe's texture is stored at, in the global array. */
 		void setReflectionProbeArrayIndex(UINT32 probeIdx, UINT32 arrayIdx, bool markAsClean);
 
+		/** Registers a new sky texture in the scene. */
+		void registerSkybox(Skybox* skybox);
+
+		/** Updates information about the previously registered skybox. */
+		void updateSkybox(Skybox* skybox);
+
+		/** Removes a skybox from the scene. */
+		void unregisterSkybox(Skybox* skybox);
+
 		/** Returns a container with all relevant scene objects. */
 		const SceneInfo& getSceneInfo() const { return mInfo; }
 
@@ -121,6 +148,9 @@ namespace bs
 		 * @param[in]	frameInfo	Global information describing the current frame.
 		 */
 		void prepareRenderable(UINT32 idx, const FrameInfo& frameInfo);
+
+		/** Returns a modifiable version of SceneInfo. Only to be used by friends who know what they are doing. */
+		SceneInfo& _getSceneInfo() { return mInfo; }
 	private:
 		/** Creates a renderer view descriptor for the particular camera. */
 		RENDERER_VIEW_DESC createViewDesc(Camera* camera) const;

+ 50 - 11
Source/RenderBeast/Include/BsRendererView.h

@@ -10,6 +10,9 @@
 #include "BsBounds.h"
 #include "BsConvexVolume.h"
 #include "BsLight.h"
+#include "BsLightGrid.h"
+#include "BsShadowRendering.h"
+#include "BsRenderCompositor.h"
 
 namespace bs { namespace ct
 {
@@ -193,13 +196,8 @@ namespace bs { namespace ct
 		/** Returns the scene camera this object is based of. This can be null for manually constructed renderer cameras. */
 		Camera* getSceneCamera() const { return mCamera; }
 
-		/** 
-		 * 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 beginFrame(bool useGBuffer);
+		/** Prepares render targets for rendering. When done call endFrame(). */
+		void beginFrame();
 
 		/** Ends rendering and frees any acquired resources. */
 		void endFrame();
@@ -219,6 +217,9 @@ namespace bs { namespace ct
 		 */
 		const SPtr<RenderQueue>& getTransparentQueue() const { return mTransparentQueue; }
 
+		/** Returns the compositor in charge of rendering for this view. */
+		const RenderCompositor& getCompositor() const { return mCompositor; }
+
 		/**
 		 * Populates view render queues by determining visible renderable objects. 
 		 *
@@ -290,6 +291,15 @@ namespace bs { namespace ct
 		/** Returns a buffer that stores per-view parameters. */
 		SPtr<GpuParamBlockBuffer> getPerViewBuffer() const { return mParamBuffer; }
 
+		/** 
+		 * Returns information about visible lights, in the form of a light grid, used for forward rendering. Only valid
+		 * after a call to updateLightGrid().
+		 */
+		const LightGrid& getLightGrid() const { return mLightGrid; }
+
+		/** Updates the light grid used for forward rendering. */
+		void updateLightGrid(const VisibleLightData& visibleLightData, const VisibleReflProbeData& visibleReflProbeData);
+
 		/**
 		 * Extracts the necessary values from the projection matrix that allow you to transform device Z value (range [0, 1]
 		 * into view Z value.
@@ -323,20 +333,22 @@ namespace bs { namespace ct
 		SPtr<RenderQueue> mOpaqueQueue;
 		SPtr<RenderQueue> mTransparentQueue;
 
-		SPtr<RenderTargets> mRenderTargets;
+		RenderCompositor mCompositor;
+
+		SPtr<RenderTargets> mRenderTargets; // TODO - Remove
 		PostProcessInfo mPostProcessInfo;
-		bool mUsingGBuffer;
 
 		SPtr<GpuParamBlockBuffer> mParamBuffer;
 		VisibilityInfo mVisibility;
+		LightGrid mLightGrid;
 	};
 
 	/** Contains one or multiple RendererView%s that are in some way related. */
 	class RendererViewGroup
 	{
 	public:
-		RendererViewGroup() {}
-		RendererViewGroup(RendererView** views, UINT32 numViews);
+		RendererViewGroup();
+		RendererViewGroup(RendererView** views, UINT32 numViews, UINT32 shadowMapSize);
 
 		/** 
 		 * Updates the internal list of views. This is more efficient than always constructing a new instance of this class
@@ -357,6 +369,24 @@ namespace bs { namespace ct
 		 */
 		const VisibilityInfo& getVisibilityInfo() const { return mVisibility; }
 
+		/**
+		 * Returns information about lights visible from this group of views. Only valid after a call to 
+		 * determineVisibility().
+		 */
+		const VisibleLightData& getVisibleLightData() const { return mVisibleLightData; }
+
+		/**
+		 * Returns information about refl. probes visible from this group of views. Only valid after a call to 
+		 * determineVisibility().
+		 */
+		const VisibleReflProbeData& getVisibleReflProbeData() const { return mVisibleReflProbeData; }
+
+		/** Returns the object responsible for rendering shadows for this view group. */
+		ShadowRendering& getShadowRenderer() { return mShadowRenderer; }
+
+		/** Returns the object responsible for rendering shadows for this view group. */
+		const ShadowRendering& getShadowRenderer() const { return mShadowRenderer; }
+
 		/** 
 		 * Updates visibility information for the provided scene objects, from the perspective of all views in this group,
 		 * and updates the render queues of each individual view. Use getVisibilityInfo() to retrieve the calculated
@@ -367,6 +397,15 @@ namespace bs { namespace ct
 	private:
 		Vector<RendererView*> mViews;
 		VisibilityInfo mVisibility;
+
+		VisibleLightData mVisibleLightData;
+		VisibleReflProbeData mVisibleReflProbeData;
+
+		// Note: Ideally we would want to keep this global, so all views share it. This way each view group renders its
+		// own set of shadows, but there might be shadows that are shared, and therefore we could avoid rendering them
+		// multiple times. Since non-primary view groups are used for pre-processing tasks exclusively (at the moment) 
+		// this isn't an issue right now.
+		ShadowRendering mShadowRenderer;
 	};
 
 	/** @} */

+ 15 - 15
Source/RenderBeast/Include/BsShadowRendering.h

@@ -141,9 +141,9 @@ namespace bs { namespace ct
 	{
 		ShadowProjectParams(const Light& light, const SPtr<Texture>& shadowMap, UINT32 shadowMapFace,
 			const SPtr<GpuParamBlockBuffer>& shadowParams, const SPtr<GpuParamBlockBuffer>& perCameraParams,
-			const RenderTargets& renderTargets)
+			GBufferInput gbuffer)
 			: light(light), shadowMap(shadowMap), shadowMapFace(shadowMapFace), shadowParams(shadowParams)
-			, perCamera(perCameraParams), renderTargets(renderTargets)
+			, perCamera(perCameraParams), gbuffer(gbuffer)
 		{ }
 
 		/** Light which is casting the shadow. */
@@ -162,7 +162,7 @@ namespace bs { namespace ct
 		const SPtr<GpuParamBlockBuffer>& perCamera;
 
 		/** Contains the GBuffer textures. */
-		const RenderTargets& renderTargets;
+		GBufferInput gbuffer;
 	};
 
 	BS_PARAM_BLOCK_BEGIN(ShadowProjectParamsDef)
@@ -435,7 +435,7 @@ namespace bs { namespace ct
 	};
 
 	/** Provides functionality for rendering shadow maps. */
-	class ShadowRendering : public Module<ShadowRendering>
+	class ShadowRendering
 	{
 		/** Contains information required for generating a shadow map for a specific light. */
 		struct ShadowMapOptions
@@ -461,8 +461,8 @@ namespace bs { namespace ct
 		 * Renders shadow occlusion values for the specified light, into the currently bound render target. 
 		 * The system uses shadow maps rendered by renderShadowMaps().
 		 */
-		void renderShadowOcclusion(const RendererScene& scene, UINT32 shadowQuality, const RendererLight& light,
-			UINT32 viewIdx);
+		void renderShadowOcclusion(const SceneInfo& sceneInfo, UINT32 shadowQuality, const RendererLight& light,
+			UINT32 viewIdx, GBufferInput gbuffer) const;
 
 		/** Changes the default shadow map size. Will cause all shadow maps to be rebuilt. */
 		void setShadowMapSize(UINT32 size);
@@ -502,13 +502,13 @@ namespace bs { namespace ct
 		 * @param[in]	far			Location of the far plane, in NDC.
 		 * @param[in]	drawNear	If disabled, only the far plane will be drawn.
 		 */
-		void drawNearFarPlanes(float near, float far, bool drawNear = true);
+		void drawNearFarPlanes(float near, float far, bool drawNear = true) const;
 
 		/** 
 		 * Draws a frustum mesh using the provided vertices as its corners. Corners should be in the order specified
 		 * by AABox::Corner enum.
 		 */
-		void drawFrustum(const std::array<Vector3, 8>& corners);
+		void drawFrustum(const std::array<Vector3, 8>& corners) const;
 
 		/**
 		 * Calculates optimal shadow quality based on the quality set in the options and the actual shadow map resolution.
@@ -586,9 +586,9 @@ namespace bs { namespace ct
 		ShadowDepthCubeMat mDepthCubeMat;
 		ShadowDepthDirectionalMat mDepthDirectionalMat;
 
-		ShadowProjectStencilMaterials mProjectStencilMaterials;
-		ShadowProjectMaterials mProjectMaterials;
-		ShadowProjectOmniMaterials mProjectOmniMaterials;
+		mutable ShadowProjectStencilMaterials mProjectStencilMaterials;
+		mutable ShadowProjectMaterials mProjectMaterials;
+		mutable ShadowProjectOmniMaterials mProjectOmniMaterials;
 
 		UINT32 mShadowMapSize;
 
@@ -605,12 +605,12 @@ namespace bs { namespace ct
 		SPtr<VertexDeclaration> mPositionOnlyVD;
 
 		// Mesh information used for drawing near & far planes
-		SPtr<IndexBuffer> mPlaneIB;
-		SPtr<VertexBuffer> mPlaneVB;
+		mutable SPtr<IndexBuffer> mPlaneIB;
+		mutable SPtr<VertexBuffer> mPlaneVB;
 
 		// Mesh information used for drawing a shadow frustum
-		SPtr<IndexBuffer> mFrustumIB;
-		SPtr<VertexBuffer> mFrustumVB;
+		mutable SPtr<IndexBuffer> mFrustumIB;
+		mutable SPtr<VertexBuffer> mFrustumVB;
 
 		Vector<bool> mRenderableVisibility; // Transient
 		Vector<ShadowMapOptions> mSpotLightShadowOptions; // Transient

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

@@ -32,7 +32,8 @@ namespace bs { namespace ct {
 		DirectionalLightMat();
 
 		/** Binds the material for rendering and sets up any global parameters. */
-		void bind(const RenderTargets& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera);
+		void bind(const GBufferInput& gBufferInput, const SPtr<Texture>& lightOcclusion, 
+			const SPtr<GpuParamBlockBuffer>& perCamera);
 
 		/** Updates the per-light buffers used by the material. */
 		void setPerLightParams(const SPtr<GpuParamBlockBuffer>& perLight);
@@ -51,7 +52,8 @@ namespace bs { namespace ct {
 		PointLightMat();
 
 		/** Binds the material for rendering and sets up any global parameters. */
-		void bind(const RenderTargets& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera);
+		void bind(const GBufferInput& gBufferInput, const SPtr<Texture>& lightOcclusion, 
+			const SPtr<GpuParamBlockBuffer>& perCamera);
 
 		/** Updates the per-light buffers used by the material. */
 		void setPerLightParams(const SPtr<GpuParamBlockBuffer>& perLight);
@@ -68,7 +70,7 @@ namespace bs { namespace ct {
 
 		/** Calculates lighting for the specified light, using the standard deferred renderer. */
 		void renderLight(LightType type, const RendererLight& light, const RendererView& view, 
-			const RenderTargets& renderTargets);
+			const GBufferInput& gBufferInput, const SPtr<Texture>& lightOcclusion);
 
 	private:
 		SPtr<GpuParamBlockBuffer> mPerLightBuffer;

+ 60 - 0
Source/RenderBeast/Source/BsGlobalMaterials.cpp

@@ -0,0 +1,60 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGlobalMaterials.h"
+
+namespace bs { namespace ct
+{
+	GlobalMaterials::GlobalMaterials()
+	{
+		mTiledDeferredIBLMats[0] = bs_new<TTiledDeferredImageBasedLightingMat<1>>();
+		mTiledDeferredIBLMats[1] = bs_new<TTiledDeferredImageBasedLightingMat<2>>();
+		mTiledDeferredIBLMats[2] = bs_new<TTiledDeferredImageBasedLightingMat<4>>();
+		mTiledDeferredIBLMats[3] = bs_new<TTiledDeferredImageBasedLightingMat<8>>();
+
+		mTiledDeferredLightingMats[0] = bs_new<TTiledDeferredLightingMat<1>>();
+		mTiledDeferredLightingMats[1] = bs_new<TTiledDeferredLightingMat<2>>();
+		mTiledDeferredLightingMats[2] = bs_new<TTiledDeferredLightingMat<4>>();
+		mTiledDeferredLightingMats[3] = bs_new<TTiledDeferredLightingMat<8>>();
+
+		mSkyboxTextureMat = bs_new<SkyboxMat<false>>();
+		mSkyboxColorMat = bs_new<SkyboxMat<true>>();
+		mFlatFramebufferToTextureMat = bs_new<FlatFramebufferToTextureMat>();
+	}
+
+	GlobalMaterials::~GlobalMaterials()
+	{
+		for (UINT32 i = 0; i < 4; i++)
+			bs_delete(mTiledDeferredIBLMats[i]);
+
+		for (UINT32 i = 0; i < 4; i++)
+			bs_delete(mTiledDeferredLightingMats[i]);
+
+		bs_delete(mSkyboxTextureMat);
+		bs_delete(mSkyboxColorMat);
+		bs_delete(mFlatFramebufferToTextureMat);
+	}
+
+	ITiledDeferredImageBasedLightingMat* GlobalMaterials::getTileDeferredIBL(UINT32 msaa)
+	{
+		if (msaa == 1)
+			return mTiledDeferredIBLMats[0];
+		else if (msaa == 2)
+			return mTiledDeferredIBLMats[1];
+		else if (msaa == 4)
+			return mTiledDeferredIBLMats[2];
+		else
+			return mTiledDeferredIBLMats[3];
+	}
+
+	ITiledDeferredLightingMat* GlobalMaterials::getTileDeferredLighting(bs::UINT32 msaa)
+	{
+		if (msaa == 1)
+			return mTiledDeferredLightingMats[0];
+		else if (msaa == 2)
+			return mTiledDeferredLightingMats[1];
+		else if (msaa == 4)
+			return mTiledDeferredLightingMats[2];
+		else
+			return mTiledDeferredLightingMats[3];
+	}
+}}

+ 55 - 255
Source/RenderBeast/Source/BsImageBasedLighting.cpp

@@ -11,6 +11,7 @@
 #include "BsRenderTargets.h"
 #include "BsRenderBeast.h"
 #include "BsRendererUtility.h"
+#include "BsSkybox.h"
 
 namespace bs { namespace ct
 {
@@ -116,12 +117,6 @@ namespace bs { namespace ct
 		if (!optional || params->hasTexture(programType, "gSkyReflectionTex"))
 		{
 			params->getTextureParam(programType, "gSkyReflectionTex", skyReflectionsTexParam);
-
-			if(params->hasSamplerState(programType, "gSkyReflectionSamp"))
-				params->getSamplerStateParam(programType, "gSkyReflectionSamp", skyReflectionsSampParam);
-			else
-				params->getSamplerStateParam(programType, "gSkyReflectionTex", skyReflectionsSampParam);
-
 			params->getTextureParam(programType, "gSkyIrradianceTex", skyIrradianceTexParam);
 		}
 
@@ -129,14 +124,7 @@ namespace bs { namespace ct
 		if (!optional || params->hasTexture(programType, "gReflProbeCubemaps"))
 		{
 			params->getTextureParam(programType, "gReflProbeCubemaps", reflectionProbeCubemapsTexParam);
-
-			if(params->hasSamplerState(programType, "gReflProbeSamp"))
-				params->getSamplerStateParam(programType, "gReflProbeSamp", reflectionProbeCubemapsSampParam);
-			else
-				params->getSamplerStateParam(programType, "gReflProbeCubemaps", reflectionProbeCubemapsSampParam);
-
 			params->getBufferParam(programType, "gReflectionProbes", reflectionProbesParam);
-
 			params->getTextureParam(programType, "gPreintegratedEnvBRDF", preintegratedEnvBRDFParam);
 		}
 
@@ -176,48 +164,45 @@ namespace bs { namespace ct
 
 		mImageBasedParams.populate(mParamsSet, GPT_COMPUTE_PROGRAM, false, false);
 
-		mReflectionsParamBuffer = gReflProbeParamsParamDef.createBuffer();
-		mParamsSet->setParamBlockBuffer("ReflProbeParams", mReflectionsParamBuffer);
-
-		SAMPLER_STATE_DESC reflSamplerDesc;
-		reflSamplerDesc.magFilter = FO_LINEAR;
-		reflSamplerDesc.minFilter = FO_LINEAR;
-		reflSamplerDesc.mipFilter = FO_LINEAR;
-
-		mReflectionSamplerState = SamplerState::create(reflSamplerDesc);
-
-		mImageBasedParams.skyReflectionsSampParam.set(mReflectionSamplerState);
-		mImageBasedParams.reflectionProbeCubemapsSampParam.set(mReflectionSamplerState);
+		mParamsSet->setParamBlockBuffer("ReflProbeParams", mReflProbeParamBuffer.buffer);
 	}
 
-	void TiledDeferredImageBasedLighting::execute(const SPtr<RenderTargets>& renderTargets,
-		const SPtr<GpuParamBlockBuffer>& perCamera, const SPtr<Texture>& preintegratedGF)
+	void TiledDeferredImageBasedLighting::execute(const RendererView& view, const SceneInfo& sceneInfo, 
+		const VisibleReflProbeData& probeData, const Inputs& inputs)
 	{
+		const RendererViewProperties& viewProps = view.getProperties();
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+
 		Vector2I framebufferSize;
-		framebufferSize[0] = renderTargets->getWidth();
-		framebufferSize[1] = renderTargets->getHeight();
+		framebufferSize[0] = width;
+		framebufferSize[1] = height;
 		gTiledImageBasedLightingParamDef.gFramebufferSize.set(mParamBuffer, framebufferSize);
 
+		mReflProbeParamBuffer.populate(sceneInfo.sky, probeData, sceneInfo.reflProbeCubemapsTex, 
+			viewProps.renderingReflections);
+
 		mParamBuffer->flushToGPU();
-		mReflectionsParamBuffer->flushToGPU();
+		mReflProbeParamBuffer.buffer->flushToGPU();
 
-		mGBufferA.set(renderTargets->get(RTT_GBuffer, RT_COLOR0));
-		mGBufferB.set(renderTargets->get(RTT_GBuffer, RT_COLOR1));
-		mGBufferC.set(renderTargets->get(RTT_GBuffer, RT_COLOR2));
-		mGBufferDepth.set(renderTargets->get(RTT_GBuffer, RT_DEPTH));
+		mGBufferA.set(inputs.gbuffer.albedo);
+		mGBufferB.set(inputs.gbuffer.normals);
+		mGBufferC.set(inputs.gbuffer.roughMetal);
+		mGBufferDepth.set(inputs.gbuffer.depth);
 
-		mImageBasedParams.preintegratedEnvBRDFParam.set(preintegratedGF);
+		mImageBasedParams.preintegratedEnvBRDFParam.set(inputs.preIntegratedGF);
+		mImageBasedParams.reflectionProbesParam.set(probeData.getProbeBuffer());
+		mImageBasedParams.reflectionProbeCubemapsTexParam.set(sceneInfo.reflProbeCubemapsTex);
+		mImageBasedParams.skyReflectionsTexParam.set(sceneInfo.sky.filteredReflections);
+		mImageBasedParams.skyIrradianceTexParam.set(sceneInfo.sky.irradiance);
 
-		mParamsSet->setParamBlockBuffer("PerCamera", perCamera, true);
+		mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
 
-		mInColorTextureParam.set(renderTargets->get(RTT_LightAccumulation));
+		mInColorTextureParam.set(inputs.lightAccumulation);
 		if (mSampleCount > 1)
-			mOutputBufferParam.set(renderTargets->getSceneColorBuffer());
+			mOutputBufferParam.set(inputs.sceneColorBuffer);
 		else
-			mOutputTextureParam.set(renderTargets->get(RTT_SceneColor));
-
-		UINT32 width = renderTargets->getWidth();
-		UINT32 height = renderTargets->getHeight();
+			mOutputTextureParam.set(inputs.sceneColorTex);
 
 		UINT32 numTilesX = (UINT32)Math::ceilToInt(width / (float)TILE_SIZE);
 		UINT32 numTilesY = (UINT32)Math::ceilToInt(height / (float)TILE_SIZE);
@@ -228,175 +213,37 @@ namespace bs { namespace ct
 		RenderAPI::instance().dispatchCompute(numTilesX, numTilesY);
 	}
 
-	void TiledDeferredImageBasedLighting::setReflectionProbes(const VisibleReflProbeData& probeData,
-		const SPtr<Texture>& reflectionCubemaps, bool capturingReflections)
+	ReflProbeParamBuffer::ReflProbeParamBuffer()
 	{
-		mImageBasedParams.reflectionProbesParam.set(probeData.getProbeBuffer());
-		mImageBasedParams.reflectionProbeCubemapsTexParam.set(reflectionCubemaps);
-
-		gReflProbeParamsParamDef.gNumProbes.set(mReflectionsParamBuffer, probeData.getNumProbes());
-
-		UINT32 numMips = 0;
-		if (reflectionCubemaps != nullptr)
-			numMips = reflectionCubemaps->getProperties().getNumMipmaps() + 1;
-
-		gReflProbeParamsParamDef.gReflCubemapNumMips.set(mReflectionsParamBuffer, numMips);
-		gReflProbeParamsParamDef.gUseReflectionMaps.set(mReflectionsParamBuffer, capturingReflections ? 0 : 1);
+		buffer = gReflProbeParamsParamDef.createBuffer();
 	}
 
-	void TiledDeferredImageBasedLighting::setSky(const SPtr<Texture>& skyReflections, const SPtr<Texture>& skyIrradiance,
-		float brightness)
+	void ReflProbeParamBuffer::populate(const SkyInfo& sky, const VisibleReflProbeData& probeData, 
+		const SPtr<Texture>& reflectionCubemaps, bool capturingReflections)
 	{
-		mImageBasedParams.skyReflectionsTexParam.set(skyReflections);
-		mImageBasedParams.skyIrradianceTexParam.set(skyIrradiance);
-
 		UINT32 skyReflectionsAvailable = 0;
-		UINT32 numMips = 0;
-		if (skyReflections != nullptr)
+		UINT32 numSkyMips = 0;
+		if (sky.filteredReflections != nullptr)
 		{
-			numMips = skyReflections->getProperties().getNumMipmaps() + 1;
+			numSkyMips = sky.filteredReflections->getProperties().getNumMipmaps() + 1;
 			skyReflectionsAvailable = 1;
 		}
 
-		gReflProbeParamsParamDef.gSkyCubemapNumMips.set(mReflectionsParamBuffer, numMips);
-		gReflProbeParamsParamDef.gSkyCubemapAvailable.set(mReflectionsParamBuffer, skyReflectionsAvailable);
-		gReflProbeParamsParamDef.gSkyBrightness.set(mReflectionsParamBuffer, brightness);
-	}
-
-	// Reverse bits functions used for Hammersley sequence
-	float reverseBits(UINT32 bits)
-	{
-		bits = (bits << 16u) | (bits >> 16u);
-		bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
-		bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
-		bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
-		bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
-
-		return (float)(double(bits) / (double)0x100000000LL);
-	}
-
-	void hammersleySequence(UINT32 i, UINT32 count, float& e0, float& e1)
-	{
-		e0 = i / (float)count;
-		e1 = reverseBits(i);
-	}
-
-	Vector3 sphericalToCartesian(float cosTheta, float sinTheta, float phi)
-	{
-		Vector3 output;
-		output.x = sinTheta * cos(phi);
-		output.y = sinTheta * sin(phi);
-		output.z = cosTheta;
+		float brightness = 1.0f;
+		if (sky.skybox != nullptr)
+			brightness = sky.skybox->getBrightness();
 
-		return output;
-	}
+		gReflProbeParamsParamDef.gSkyCubemapNumMips.set(buffer, numSkyMips);
+		gReflProbeParamsParamDef.gSkyCubemapAvailable.set(buffer, skyReflectionsAvailable);
+		gReflProbeParamsParamDef.gSkyBrightness.set(buffer, brightness);
+		gReflProbeParamsParamDef.gNumProbes.set(buffer, probeData.getNumProbes());
 
-	// Generates an angle in spherical coordinates, importance sampled for the specified roughness based on some uniformly
-	// distributed random variables in range [0, 1].
-	void importanceSampleGGX(float e0, float e1, float roughness4, float& cosTheta, float& phi)
-	{
-		// See GGXImportanceSample.nb for derivation (essentially, take base GGX, normalize it, generate PDF, split PDF into
-		// marginal probability for theta and conditional probability for phi. Plug those into the CDF, invert it.)				
-		cosTheta = sqrt((1.0f - e0) / (1.0f + (roughness4 - 1.0f) * e0));
-		phi = 2.0f * Math::PI * e1;
-	}
-
-	float calcMicrofacetShadowingSmithGGX(float roughness4, float NoV, float NoL)
-	{
-		// Note: See lighting shader for derivation. Includes microfacet BRDF divisor.
-		float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
-		float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
-		return 1.0f / (g1V * g1L);
-	}
-
-	SPtr<Texture> TiledDeferredImageBasedLighting::generatePreintegratedEnvBRDF()
-	{
-		TEXTURE_DESC desc;
-		desc.type = TEX_TYPE_2D;
-		desc.format = PF_FLOAT16_RG;
-		desc.width = 128;
-		desc.height = 32;
-
-		SPtr<Texture> texture = Texture::create(desc);
-		PixelData pixelData = texture->lock(GBL_WRITE_ONLY_DISCARD);
-
-		for (UINT32 y = 0; y < desc.height; y++)
-		{
-			float roughness = (float)(y + 0.5f) / desc.height;
-			float m = roughness * roughness;
-			float m2 = m*m;
-
-			for (UINT32 x = 0; x < desc.width; x++)
-			{
-				float NoV = (float)(x + 0.5f) / desc.width;
-
-				Vector3 V;
-				V.x = sqrt(1.0f - NoV * NoV); // sine
-				V.y = 0.0f;
-				V.z = NoV;
-
-				// These are the two integrals resulting from the second part of the split-sum approximation. Described in
-				// Epic's 2013 paper:
-				//    http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
-				float scale = 0.0f;
-				float offset = 0.0f;
-
-				// We use the same importance sampling function we use for reflection cube importance sampling, only we
-				// sample G and F, instead of D factors of the microfactet BRDF. See GGXImportanceSample.nb for derivation.
-				constexpr UINT32 NumSamples = 128;
-				for (UINT32 i = 0; i < NumSamples; i++)
-				{
-					float e0, e1;
-					hammersleySequence(i, NumSamples, e0, e1);
-
-					float cosTheta, phi;
-					importanceSampleGGX(e0, e1, m2, cosTheta, phi);
-
-					float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
-					Vector3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
-					Vector3 L = 2.0f * Vector3::dot(V, H) * H - V;
-
-					float VoH = std::max(Vector3::dot(V, H), 0.0f);
-					float NoL = std::max(L.z, 0.0f); // N assumed (0, 0, 1)
-					float NoH = std::max(H.z, 0.0f); // N assumed (0, 0, 1)
-
-					// Set second part of the split sum integral is split into two parts:
-					//   F0*I[G * (1 - (1 - v.h)^5) * cos(theta)] + I[G * (1 - v.h)^5 * cos(theta)] (F0 * scale + bias)
-
-					// We calculate the fresnel scale (1 - (1 - v.h)^5) and bias ((1 - v.h)^5) parts
-					float fc = pow(1.0f - VoH, 5.0f);
-					float fresnelScale = 1.0f - fc;
-					float fresnelOffset = fc;
-
-					// We calculate the G part
-					float G = calcMicrofacetShadowingSmithGGX(m2, NoV, NoL);
-
-					// When we factor out G and F, then divide D by PDF, this is what's left
-					// Note: This is based on PDF: D * NoH / (4 * VoH). (4 * VoH) factor comes from the Jacobian of the
-					// transformation from half vector to light vector
-					float pdfFactor = 4.0f * VoH / NoH;
-
-					if (NoL > 0.0f)
-					{
-						scale += NoL * pdfFactor * G * fresnelScale;
-						offset += NoL * pdfFactor * G * fresnelOffset;
-					}
-				}
-
-				scale /= NumSamples;
-				offset /= NumSamples;
-
-				Color color;
-				color.r = Math::clamp01(scale);
-				color.g = Math::clamp01(offset);
-
-				pixelData.setColorAt(color, x, y);
-			}
-		}
-
-		texture->unlock();
+		UINT32 numReflProbeMips = 0;
+		if (reflectionCubemaps != nullptr)
+			numReflProbeMips = reflectionCubemaps->getProperties().getNumMipmaps() + 1;
 
-		return texture;
+		gReflProbeParamsParamDef.gReflCubemapNumMips.set(buffer, numReflProbeMips);
+		gReflProbeParamsParamDef.gUseReflectionMaps.set(buffer, capturingReflections ? 0 : 1);
 	}
 
 	template<int MSAA_COUNT>
@@ -413,62 +260,15 @@ namespace bs { namespace ct
 		defines.set("MSAA_COUNT", MSAA_COUNT);
 	}
 
-	template<int MSAA_COUNT>
-	void TTiledDeferredImageBasedLightingMat<MSAA_COUNT>::execute(const SPtr<RenderTargets>& gbuffer,
-		const SPtr<GpuParamBlockBuffer>& perCamera, const SPtr<Texture>& preintegratedGF)
-	{
-		mInternal.execute(gbuffer, perCamera, preintegratedGF);
-	}
-
-	template<int MSAA_COUNT>
-	void TTiledDeferredImageBasedLightingMat<MSAA_COUNT>::setReflectionProbes(const VisibleReflProbeData& probeData, 
-		const SPtr<Texture>& reflectionCubemaps, bool capturingReflections)
-	{
-		mInternal.setReflectionProbes(probeData, reflectionCubemaps, capturingReflections);
-	}
-
-	template<int MSAA_COUNT>
-	void TTiledDeferredImageBasedLightingMat<MSAA_COUNT>::setSky(const SPtr<Texture>& skyReflections,
-		const SPtr<Texture>& skyIrradiance, float brightness)
+	template <int MSAA_COUNT>
+	void TTiledDeferredImageBasedLightingMat<MSAA_COUNT>::execute(const RendererView& view, const SceneInfo& sceneInfo, 
+		const VisibleReflProbeData& probeData, const TiledDeferredImageBasedLighting::Inputs& inputs)
 	{
-		mInternal.setSky(skyReflections, skyIrradiance, brightness);
+		mInternal.execute(view, sceneInfo, probeData, inputs);
 	}
 
-	template<int MSAA_COUNT>
-	SPtr<GpuParamBlockBuffer> TTiledDeferredImageBasedLightingMat<MSAA_COUNT>::getReflectionsParamBuffer() const
-	{
-		return mInternal.getReflectionsParamBuffer();
-	}
-
-	template<int MSAA_COUNT>
-	SPtr<SamplerState> TTiledDeferredImageBasedLightingMat<MSAA_COUNT>::getReflectionsSamplerState() const
-	{
-		return mInternal.getReflectionsSamplerState();
-	}
-
-	TiledDeferredImageBasedLightingMaterials::TiledDeferredImageBasedLightingMaterials()
-	{
-		mInstances[0] = bs_new<TTiledDeferredImageBasedLightingMat<1>>();
-		mInstances[1] = bs_new<TTiledDeferredImageBasedLightingMat<2>>();
-		mInstances[2] = bs_new<TTiledDeferredImageBasedLightingMat<4>>();
-		mInstances[3] = bs_new<TTiledDeferredImageBasedLightingMat<8>>();
-	}
-
-	TiledDeferredImageBasedLightingMaterials::~TiledDeferredImageBasedLightingMaterials()
-	{
-		for (UINT32 i = 0; i < 4; i++)
-			bs_delete(mInstances[i]);
-	}
-
-	ITiledDeferredImageBasedLightingMat* TiledDeferredImageBasedLightingMaterials::get(UINT32 msaa)
-	{
-		if (msaa == 1)
-			return mInstances[0];
-		else if (msaa == 2)
-			return mInstances[1];
-		else if (msaa == 4)
-			return mInstances[2];
-		else
-			return mInstances[3];
-	}
-}}
+	template class TTiledDeferredImageBasedLightingMat<1>;
+	template class TTiledDeferredImageBasedLightingMat<2>;
+	template class TTiledDeferredImageBasedLightingMat<4>;
+	template class TTiledDeferredImageBasedLightingMat<8>;
+}}

+ 4 - 2
Source/RenderBeast/Source/BsLightGrid.cpp

@@ -245,8 +245,10 @@ namespace bs { namespace ct
 	void LightGrid::updateGrid(const RendererView& view, const VisibleLightData& lightData, const VisibleReflProbeData& probeData,
 		bool noLighting)
 	{
-		UINT32 width = view.getRenderTargets()->getWidth();
-		UINT32 height = view.getRenderTargets()->getHeight();
+		const RendererViewProperties& viewProps = view.getProperties();
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
 
 		Vector3I gridSize;
 		gridSize[0] = (width + CELL_XY_SIZE - 1) / CELL_XY_SIZE;

+ 53 - 74
Source/RenderBeast/Source/BsLightRendering.cpp

@@ -127,6 +127,16 @@ namespace bs { namespace ct
 		mMaterial->updateParamsSet(mParamsSet);
 	}
 
+	void GBufferParams::bind(const GBufferInput& gbuffer)
+	{
+		mGBufferA.set(gbuffer.albedo);
+		mGBufferB.set(gbuffer.normals);
+		mGBufferC.set(gbuffer.roughMetal);
+		mGBufferDepth.set(gbuffer.depth);
+
+		mMaterial->updateParamsSet(mParamsSet);
+	}
+
 	VisibleLightData::VisibleLightData()
 		:mNumLights{}, mNumShadowedLights{}
 	{ }
@@ -250,15 +260,22 @@ namespace bs { namespace ct
 		mParamsSet->setParamBlockBuffer("Params", mParamBuffer, true);
 	}
 
-	void TiledDeferredLighting::execute(const SPtr<RenderTargets>& renderTargets, 
-		const SPtr<GpuParamBlockBuffer>& perCamera, bool noLighting, bool noShadows)
+	void TiledDeferredLighting::execute(const RendererView& view, const VisibleLightData& lightData, 
+		const GBufferInput& gbuffer, const SPtr<Texture>& lightAccumTex, const SPtr<GpuBuffer>& lightAccumBuffer)
 	{
+		const RendererViewProperties& viewProps = view.getProperties();
+
+		mLightBufferParam.set(lightData.getLightBuffer());
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+
 		Vector2I framebufferSize;
-		framebufferSize[0] = renderTargets->getWidth();
-		framebufferSize[1] = renderTargets->getHeight();
+		framebufferSize[0] = width;
+		framebufferSize[1] = height;
 		gTiledLightingParamDef.gFramebufferSize.set(mParamBuffer, framebufferSize);
 
-		if (noLighting)
+		if (viewProps.noLighting)
 		{
 			Vector4I lightCounts;
 			lightCounts[0] = 0;
@@ -275,32 +292,39 @@ namespace bs { namespace ct
 		}
 		else
 		{
-			if(noShadows)
-				gTiledLightingParamDef.gLightCounts.set(mParamBuffer, mLightCounts);
+			Vector4I unshadowedLightCounts;
+			unshadowedLightCounts[0] = lightData.getNumUnshadowedLights(LightType::Directional);
+			unshadowedLightCounts[1] = lightData.getNumUnshadowedLights(LightType::Radial);
+			unshadowedLightCounts[2] = lightData.getNumUnshadowedLights(LightType::Spot);
+			unshadowedLightCounts[3] = unshadowedLightCounts[0] + unshadowedLightCounts[1] + unshadowedLightCounts[2];
+
+			Vector4I lightCounts;
+			lightCounts[0] = lightData.getNumLights(LightType::Directional);
+			lightCounts[1] = lightData.getNumLights(LightType::Radial);
+			lightCounts[2] = lightData.getNumLights(LightType::Spot);
+			lightCounts[3] = lightCounts[0] + lightCounts[1] + lightCounts[2];
+
+			Vector2I lightStrides;
+			lightStrides[0] = lightCounts[0];
+			lightStrides[1] = lightStrides[0] + lightCounts[1];
+
+			if(viewProps.noShadows)
+				gTiledLightingParamDef.gLightCounts.set(mParamBuffer, lightCounts);
 			else
-				gTiledLightingParamDef.gLightCounts.set(mParamBuffer, mUnshadowedLightCounts);
+				gTiledLightingParamDef.gLightCounts.set(mParamBuffer, unshadowedLightCounts);
 
-			gTiledLightingParamDef.gLightStrides.set(mParamBuffer, mLightStrides);
+			gTiledLightingParamDef.gLightStrides.set(mParamBuffer, lightStrides);
 		}
 
 		mParamBuffer->flushToGPU();
 
-		mGBufferParams.bind(*renderTargets);
-		mParamsSet->setParamBlockBuffer("PerCamera", perCamera, true);
+		mGBufferParams.bind(gbuffer);
+		mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
 
 		if (mSampleCount > 1)
-		{
-			SPtr<GpuBuffer> lightAccumulation = renderTargets->getLightAccumulationBuffer();
-			mOutputBufferParam.set(lightAccumulation);
-		}
+			mOutputBufferParam.set(lightAccumBuffer);
 		else
-		{
-			SPtr<Texture> lightAccumulation = renderTargets->get(RTT_LightAccumulation);
-			mOutputTextureParam.set(lightAccumulation);
-		}
-
-		UINT32 width = renderTargets->getWidth();
-		UINT32 height = renderTargets->getHeight();
+			mOutputTextureParam.set(lightAccumTex);
 
 		UINT32 numTilesX = (UINT32)Math::ceilToInt(width / (float)TILE_SIZE);
 		UINT32 numTilesY = (UINT32)Math::ceilToInt(height / (float)TILE_SIZE);
@@ -311,24 +335,6 @@ namespace bs { namespace ct
 		RenderAPI::instance().dispatchCompute(numTilesX, numTilesY);
 	}
 
-	void TiledDeferredLighting::setLights(const VisibleLightData& lightData)
-	{
-		mLightBufferParam.set(lightData.getLightBuffer());
-
-		mUnshadowedLightCounts[0] = lightData.getNumUnshadowedLights(LightType::Directional);
-		mUnshadowedLightCounts[1] = lightData.getNumUnshadowedLights(LightType::Radial);
-		mUnshadowedLightCounts[2] = lightData.getNumUnshadowedLights(LightType::Spot);
-		mUnshadowedLightCounts[3] = mUnshadowedLightCounts[0] + mUnshadowedLightCounts[1] + mUnshadowedLightCounts[2];
-
-		mLightCounts[0] = lightData.getNumLights(LightType::Directional);
-		mLightCounts[1] = lightData.getNumLights(LightType::Radial);
-		mLightCounts[2] = lightData.getNumLights(LightType::Spot);
-		mLightCounts[3] = mLightCounts[0] + mLightCounts[1] + mLightCounts[2];
-
-		mLightStrides[0] = mLightCounts[0];
-		mLightStrides[1] = mLightStrides[0] + mLightCounts[1];
-	}
-
 	template<int MSAA_COUNT>
 	TTiledDeferredLightingMat<MSAA_COUNT>::TTiledDeferredLightingMat()
 		:mInternal(mMaterial, mParamsSet, MSAA_COUNT)
@@ -344,43 +350,16 @@ namespace bs { namespace ct
 	}
 
 	template<int MSAA_COUNT>
-	void TTiledDeferredLightingMat<MSAA_COUNT>::execute(const SPtr<RenderTargets>& gbuffer,
-		const SPtr<GpuParamBlockBuffer>& perCamera, bool noLighting, bool noShadows)
-	{
-		mInternal.execute(gbuffer, perCamera, noLighting, noShadows);
-	}
-
-	template<int MSAA_COUNT>
-	void TTiledDeferredLightingMat<MSAA_COUNT>::setLights(const VisibleLightData& lightData)
-	{
-		mInternal.setLights(lightData);
-	}
-
-	TiledDeferredLightingMaterials::TiledDeferredLightingMaterials()
-	{
-		mInstances[0] = bs_new<TTiledDeferredLightingMat<1>>();
-		mInstances[1] = bs_new<TTiledDeferredLightingMat<2>>();
-		mInstances[2] = bs_new<TTiledDeferredLightingMat<4>>();
-		mInstances[3] = bs_new<TTiledDeferredLightingMat<8>>();
-	}
-
-	TiledDeferredLightingMaterials::~TiledDeferredLightingMaterials()
+	void TTiledDeferredLightingMat<MSAA_COUNT>::execute(const RendererView& view, const VisibleLightData& lightData, 
+		const GBufferInput& gbuffer, const SPtr<Texture>& lightAccumTex, const SPtr<GpuBuffer>& lightAccumBuffer)
 	{
-		for (UINT32 i = 0; i < 4; i++)
-			bs_delete(mInstances[i]);
+		mInternal.execute(view, lightData, gbuffer, lightAccumTex, lightAccumBuffer);
 	}
 
-	ITiledDeferredLightingMat* TiledDeferredLightingMaterials::get(UINT32 msaa)
-	{
-		if (msaa == 1)
-			return mInstances[0];
-		else if (msaa == 2)
-			return mInstances[1];
-		else if (msaa == 4)
-			return mInstances[2];
-		else
-			return mInstances[3];
-	}
+	template class TTiledDeferredLightingMat<1>;
+	template class TTiledDeferredLightingMat<2>;
+	template class TTiledDeferredLightingMat<4>;
+	template class TTiledDeferredLightingMat<8>;
 
 	FlatFramebufferToTextureParamDef gFlatFramebufferToTextureParamDef;
 

+ 166 - 332
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -36,14 +36,13 @@
 #include "BsSkybox.h"
 #include "BsShadowRendering.h"
 #include "BsStandardDeferredLighting.h"
+#include "BsGlobalMaterials.h"
+#include "BsRenderCompositor.h"
 
 using namespace std::placeholders;
 
 namespace bs { namespace ct
 {
-	// Limited by max number of array elements in texture for DX11 hardware
-	constexpr UINT32 MaxReflectionCubemaps = 2048 / 6;
-
 	RenderBeast::RenderBeast()
 	{
 		mOptions = bs_shared_ptr_new<RenderBeastOptions>();
@@ -73,27 +72,29 @@ namespace bs { namespace ct
 	void RenderBeast::initializeCore()
 	{
 		RendererUtility::startUp();
+		GpuResourcePool::startUp();
 
 		mCoreOptions = bs_shared_ptr_new<RenderBeastOptions>(); 
 		mScene = bs_shared_ptr_new<RendererScene>(mCoreOptions);
 		mObjectRenderer = bs_new<ObjectRenderer>();
 
-		mSkyboxMat = bs_new<SkyboxMat<false>>();
-		mSkyboxSolidColorMat = bs_new<SkyboxMat<true>>();
-		mFlatFramebufferToTextureMat = bs_new<FlatFramebufferToTextureMat>();
-
-		mTiledDeferredLightingMats = bs_new<TiledDeferredLightingMaterials>();
-		mTileDeferredImageBasedLightingMats = bs_new<TiledDeferredImageBasedLightingMaterials>();
-
-		mPreintegratedEnvBRDF = TiledDeferredImageBasedLighting::generatePreintegratedEnvBRDF();
-		mVisibleLightInfo = bs_new<VisibleLightData>();
-		mVisibleReflProbeInfo = bs_new<VisibleReflProbeData>();
-		mLightGrid = bs_new<LightGrid>();
+		mMainViewGroup = bs_new<RendererViewGroup>();
 
-		GpuResourcePool::startUp();
 		PostProcessing::startUp();
-		ShadowRendering::startUp(mCoreOptions->shadowMapSize);
 		StandardDeferred::startUp();
+		GlobalMaterials::startUp();
+
+		RenderCompositor::registerNodeType<RCNodeSceneDepth>();
+		RenderCompositor::registerNodeType<RCNodeGBuffer>();
+		RenderCompositor::registerNodeType<RCNodeLightAccumulation>();
+		RenderCompositor::registerNodeType<RCNodeSceneColor>();
+		RenderCompositor::registerNodeType<RCNodeStandardDeferredLighting>();
+		RenderCompositor::registerNodeType<RCNodeTiledDeferredLighting>();
+		RenderCompositor::registerNodeType<RCNodeTiledDeferredIBL>();
+		RenderCompositor::registerNodeType<RCNodeUnflattenLightAccum>();
+		RenderCompositor::registerNodeType<RCNodeFinalResolve>();
+		RenderCompositor::registerNodeType<RCNodeSkybox>();
+		RenderCompositor::registerNodeType<RCNodeUnflattenSceneColor>();
 	}
 
 	void RenderBeast::destroyCore()
@@ -103,27 +104,15 @@ namespace bs { namespace ct
 
 		mScene = nullptr;
 
-		mReflCubemapArrayTex = nullptr;
-		mSkyboxTexture = nullptr;
-		mSkyboxFilteredReflections = nullptr;
-		mSkyboxIrradiance = nullptr;
+		RenderCompositor::cleanUp();
 
+		GlobalMaterials::shutDown();
 		StandardDeferred::shutDown();
-		ShadowRendering::shutDown();
 		PostProcessing::shutDown();
-		GpuResourcePool::shutDown();
 
-		bs_delete(mSkyboxMat);
-		bs_delete(mSkyboxSolidColorMat);
-		bs_delete(mVisibleLightInfo);
-		bs_delete(mVisibleReflProbeInfo);
-		bs_delete(mLightGrid);
-		bs_delete(mFlatFramebufferToTextureMat);
-		bs_delete(mTiledDeferredLightingMats);
-		bs_delete(mTileDeferredImageBasedLightingMats);
-
-		mPreintegratedEnvBRDF = nullptr;
+		bs_delete(mMainViewGroup);
 
+		GpuResourcePool::shutDown();
 		RendererUtility::shutDown();
 	}
 
@@ -181,36 +170,6 @@ namespace bs { namespace ct
 	void RenderBeast::notifyReflectionProbeAdded(ReflectionProbe* probe)
 	{
 		mScene->registerReflectionProbe(probe);
-
-		// Find a spot in cubemap array
-		const SceneInfo& sceneInfo = mScene->getSceneInfo();
-
-		UINT32 probeId = probe->getRendererId();
-		const RendererReflectionProbe* probeInfo = &sceneInfo.reflProbes[probeId];
-
-		UINT32 numArrayEntries = (UINT32)mCubemapArrayUsedSlots.size();
-		for(UINT32 i = 0; i < numArrayEntries; i++)
-		{
-			if(!mCubemapArrayUsedSlots[i])
-			{
-				mScene->setReflectionProbeArrayIndex(probeId, i, false);
-				mCubemapArrayUsedSlots[i] = true;
-				break;
-			}
-		}
-
-		// No empty slot was found
-		if (probeInfo->arrayIdx == -1)
-		{
-			mScene->setReflectionProbeArrayIndex(probeId, numArrayEntries, false);
-			mCubemapArrayUsedSlots.push_back(true);
-		}
-
-		if(probeInfo->arrayIdx > MaxReflectionCubemaps)
-		{
-			LOGERR("Reached the maximum number of allowed reflection probe cubemaps at once. "
-				"Ignoring reflection probe data.");
-		}
 	}
 
 	void RenderBeast::notifyReflectionProbeUpdated(ReflectionProbe* probe)
@@ -220,14 +179,6 @@ namespace bs { namespace ct
 
 	void RenderBeast::notifyReflectionProbeRemoved(ReflectionProbe* probe)
 	{
-		const SceneInfo& sceneInfo = mScene->getSceneInfo();
-
-		UINT32 probeId = probe->getRendererId();
-		UINT32 arrayIdx = sceneInfo.reflProbes[probeId].arrayIdx;
-
-		if (arrayIdx != -1)
-			mCubemapArrayUsedSlots[arrayIdx] = false;
-
 		mScene->unregisterReflectionProbe(probe);
 	}
 
@@ -248,34 +199,17 @@ namespace bs { namespace ct
 
 	void RenderBeast::notifySkyboxAdded(Skybox* skybox)
 	{
-		mSkybox = skybox;
-
-		SPtr<Texture> skyTex = skybox->getTexture();
-		if (skyTex != nullptr && skyTex->getProperties().getTextureType() == TEX_TYPE_CUBE_MAP)
-			mSkyboxTexture = skyTex;
-
-		mSkyboxFilteredReflections = nullptr;
-		mSkyboxIrradiance = nullptr;
+		mScene->registerSkybox(skybox);
 	}
 
 	void RenderBeast::notifySkyboxTextureChanged(Skybox* skybox)
 	{
-		LightProbeCache::instance().notifyDirty(skybox->getUUID());
-
-		if (mSkybox == skybox)
-		{
-			mSkyboxTexture = skybox->getTexture();
-			mSkyboxFilteredReflections = nullptr;
-			mSkyboxIrradiance = nullptr;
-		}
+		mScene->updateSkybox(skybox);
 	}
 
 	void RenderBeast::notifySkyboxRemoved(Skybox* skybox)
 	{
-		LightProbeCache::instance().unloadCachedTexture(skybox->getUUID());
-
-		if (mSkybox == skybox)
-			mSkyboxTexture = nullptr;
+		mScene->unregisterSkybox(skybox);
 	}
 
 	SPtr<PostProcessSettings> RenderBeast::createPostProcessSettings() const
@@ -306,7 +240,9 @@ namespace bs { namespace ct
 		*mCoreOptions = options;
 
 		mScene->setOptions(mCoreOptions);
-		ShadowRendering::instance().setShadowMapSize(mCoreOptions->shadowMapSize);
+
+		ShadowRendering& shadowRenderer = mMainViewGroup->getShadowRenderer();
+		shadowRenderer.setShadowMapSize(mCoreOptions->shadowMapSize);
 	}
 
 	void RenderBeast::renderAll() 
@@ -365,17 +301,19 @@ namespace bs { namespace ct
 			}
 		}
 
-		mMainViewGroup.setViews(views.data(), (UINT32)views.size());
-		mMainViewGroup.determineVisibility(sceneInfo);
+		mMainViewGroup->setViews(views.data(), (UINT32)views.size());
+		mMainViewGroup->determineVisibility(sceneInfo);
 
 		// Render shadow maps
-		ShadowRendering::instance().renderShadowMaps(*mScene, mMainViewGroup, frameInfo);
+		ShadowRendering& shadowRenderer = mMainViewGroup->getShadowRenderer();
+		shadowRenderer.renderShadowMaps(*mScene, *mMainViewGroup, frameInfo);
 
-		// Update reflection probes
-		updateLightProbes(frameInfo);
+		// Update reflection probe and skybox textures, if required
+		renderReflectionProbes(frameInfo);
+		updateSkybox();
 
 		// Render everything
-		renderViews(mMainViewGroup, frameInfo);
+		renderViews(*mMainViewGroup, frameInfo);
 
 		gProfilerGPU().endFrame();
 
@@ -394,16 +332,6 @@ namespace bs { namespace ct
 		const SceneInfo& sceneInfo = mScene->getSceneInfo();
 		const VisibilityInfo& visibility = viewGroup.getVisibilityInfo();
 
-		// Note: I'm determining light and refl. probe visibility for the entire group. It might be more performance
-		// efficient to do it per view. Additionally I'm using a single GPU buffer to hold their information, which is
-		// then updated when each view group is rendered. It might be better to keep one buffer reserved per-view.
-
-		// Update GPU light data
-		mVisibleLightInfo->update(sceneInfo, viewGroup);
-
-		// Update reflection probe data
-		mVisibleReflProbeInfo->update(sceneInfo, viewGroup);
-
 		// Update various buffers required by each renderable
 		UINT32 numRenderables = (UINT32)sceneInfo.renderables.size();
 		for (UINT32 i = 0; i < numRenderables; i++)
@@ -422,11 +350,11 @@ namespace bs { namespace ct
 			if (view->getProperties().isOverlay)
 				renderOverlay(view);
 			else
-				renderView(view, frameInfo.timeDelta);
+				renderView(viewGroup, view, frameInfo.timeDelta);
 		}
 	}
 
-	void RenderBeast::renderView(RendererView* viewInfo, float frameDelta)
+	void RenderBeast::renderView(const RendererViewGroup& viewGroup, RendererView* viewInfo, float frameDelta)
 	{
 		gProfilerCPU().beginSample("Render");
 
@@ -452,34 +380,27 @@ namespace bs { namespace ct
 			}
 		}
 
-		viewInfo->beginFrame(true);
+		viewInfo->beginFrame();
+
+		const VisibleLightData& visibleLightData = viewGroup.getVisibleLightData();
+		const VisibleReflProbeData& visibleReflProbeData = viewGroup.getVisibleReflProbeData();
 
-		// Prepare light grid required for transparent object rendering
-		mLightGrid->updateGrid(*viewInfo, *mVisibleLightInfo, *mVisibleReflProbeInfo, viewProps.noLighting);
+		const LightGrid& lightGrid = viewInfo->getLightGrid();
 
 		SPtr<GpuParamBlockBuffer> gridParams;
 		SPtr<GpuBuffer> gridLightOffsetsAndSize, gridLightIndices;
 		SPtr<GpuBuffer> gridProbeOffsetsAndSize, gridProbeIndices;
-		mLightGrid->getOutputs(gridLightOffsetsAndSize, gridLightIndices, gridProbeOffsetsAndSize, gridProbeIndices, 
+		lightGrid.getOutputs(gridLightOffsetsAndSize, gridLightIndices, gridProbeOffsetsAndSize, gridProbeIndices, 
 			gridParams);
 
-		// Prepare image based material and its param buffer
-		ITiledDeferredImageBasedLightingMat* imageBasedLightingMat =
-			mTileDeferredImageBasedLightingMats->get(numSamples);
-
-		imageBasedLightingMat->setReflectionProbes(*mVisibleReflProbeInfo, mReflCubemapArrayTex, viewProps.renderingReflections);
-
-		float skyBrightness = 1.0f;
-		if (mSkybox != nullptr)
-			skyBrightness = mSkybox->getBrightness();
-
-		imageBasedLightingMat->setSky(mSkyboxFilteredReflections, mSkyboxIrradiance, skyBrightness);
+		// Prepare refl. probe param buffer
+		ReflProbeParamBuffer reflProbeParamBuffer;
+		reflProbeParamBuffer.populate(sceneInfo.sky, visibleReflProbeData, sceneInfo.reflProbeCubemapsTex, 
+			viewProps.renderingReflections);
 
 		// Assign camera and per-call data to all relevant renderables
 		const VisibilityInfo& visibility = viewInfo->getVisibilityMasks();
 		UINT32 numRenderables = (UINT32)sceneInfo.renderables.size();
-		SPtr<GpuParamBlockBuffer> reflParamBuffer = imageBasedLightingMat->getReflectionsParamBuffer();
-		SPtr<SamplerState> reflSamplerState = imageBasedLightingMat->getReflectionsSamplerState();
 		for (UINT32 i = 0; i < numRenderables; i++)
 		{
 			if (!visibility.renderables[i])
@@ -502,33 +423,26 @@ namespace bs { namespace ct
 
 				element.gridLightOffsetsAndSizeParam.set(gridLightOffsetsAndSize);
 				element.gridLightIndicesParam.set(gridLightIndices);
-				element.lightsBufferParam.set(mVisibleLightInfo->getLightBuffer());
+				element.lightsBufferParam.set(visibleLightData.getLightBuffer());
 
 				// Image based lighting params
 				ImageBasedLightingParams& iblParams = element.imageBasedParams;
 				if (iblParams.reflProbeParamsBindingIdx != -1)
-					element.params->setParamBlockBuffer(iblParams.reflProbeParamsBindingIdx, reflParamBuffer);
+					element.params->setParamBlockBuffer(iblParams.reflProbeParamsBindingIdx, reflProbeParamBuffer.buffer);
 
 				element.gridProbeOffsetsAndSizeParam.set(gridProbeOffsetsAndSize);
 
 				iblParams.reflectionProbeIndicesParam.set(gridProbeIndices);
-				iblParams.reflectionProbesParam.set(mVisibleReflProbeInfo->getProbeBuffer());
+				iblParams.reflectionProbesParam.set(visibleReflProbeData.getProbeBuffer());
 
-				iblParams.skyReflectionsTexParam.set(mSkyboxFilteredReflections);
-				iblParams.skyIrradianceTexParam.set(mSkyboxIrradiance);
+				iblParams.skyReflectionsTexParam.set(sceneInfo.sky.filteredReflections);
+				iblParams.skyIrradianceTexParam.set(sceneInfo.sky.irradiance);
 
-				iblParams.reflectionProbeCubemapsTexParam.set(mReflCubemapArrayTex);
-				iblParams.preintegratedEnvBRDFParam.set(mPreintegratedEnvBRDF);
-
-				iblParams.reflectionProbeCubemapsSampParam.set(reflSamplerState);
-				iblParams.skyReflectionsSampParam.set(reflSamplerState);
+				iblParams.reflectionProbeCubemapsTexParam.set(sceneInfo.reflProbeCubemapsTex);
+				iblParams.preintegratedEnvBRDFParam.set(IBLUtility::getPreintegratedEnvBRDF());
 			}
 		}
 
-		SPtr<RenderTargets> renderTargets = viewInfo->getRenderTargets();
-		renderTargets->allocate(RTT_GBuffer);
-		renderTargets->bind(RTT_GBuffer);
-
 		// Trigger pre-base-pass callbacks
 		auto iterRenderCallback = mCallbacks.begin();
 
@@ -547,22 +461,14 @@ namespace bs { namespace ct
 			}
 		}
 
-		// Render base pass
-		const Vector<RenderQueueElement>& opaqueElements = viewInfo->getOpaqueQueue()->getSortedElements();
-		for (auto iter = opaqueElements.begin(); iter != opaqueElements.end(); ++iter)
-		{
-			BeastRenderableElement* renderElem = static_cast<BeastRenderableElement*>(iter->renderElem);
-			renderElement(*renderElem, iter->passIdx, iter->applyPass, viewProj);
-		}
-
 		// Build HiZ buffer
 		bool isMSAA = numSamples > 1;
-		// TODO - Avoid generating it unless it actually gets used in some system
-		if (isMSAA)
-		{
-			renderTargets->allocate(RTT_ResolvedDepth);
-			renderTargets->generate(RTT_ResolvedDepth);
-		}
+		//// TODO - Avoid generating it unless it actually gets used in some system
+		//if (isMSAA)
+		//{
+		//	renderTargets->allocate(RTT_ResolvedDepth);
+		//	renderTargets->generate(RTT_ResolvedDepth);
+		//}
 
 		// Trigger post-base-pass callbacks
 		if (viewProps.triggerCallbacks)
@@ -580,120 +486,37 @@ namespace bs { namespace ct
 			}
 		}
 
-		renderTargets->allocate(RTT_HiZ);
-		renderTargets->generate(RTT_HiZ);
-
-		// Build AO if required
-		bool useSSAO = viewInfo->getPPInfo().settings->ambientOcclusion.enabled;
-		if(useSSAO)
-		{
-			renderTargets->allocate(RTT_AmbientOcclusion);
-
-			// Note: This could be done as async compute (and started earlier, right after base pass)
-			PostProcessing::instance().buildSSAO(*viewInfo);
-		}
-
-		RenderAPI& rapi = RenderAPI::instance();
-		rapi.setRenderTarget(nullptr);
-
-		// Accumulate all direct lighting into the light accumulation texture
-		renderTargets->allocate(RTT_LightAccumulation);
-
-		// Render non-shadowed lights into light accumulation texture (or buffer if MSAA)
-		ITiledDeferredLightingMat* lightingMat = mTiledDeferredLightingMats->get(numSamples);
-		lightingMat->setLights(*mVisibleLightInfo);
-		lightingMat->execute(renderTargets, perCameraBuffer, viewProps.noLighting, !allowShadows);
-
-		// If we're using flattened accumulation buffer for MSAA we need to copy its contents to the MSAA texture before
-		// continuing
-		if(isMSAA)
-		{
-			renderTargets->bind(RTT_LightAccumulation);
-			mFlatFramebufferToTextureMat->execute(renderTargets->getLightAccumulationBuffer(), 
-				renderTargets->get(RTT_LightAccumulation));
-		}
-
-		// Render shadowed lights into light accumulation texture, using standard deferred
-		if (allowShadows)
-		{
-			renderTargets->allocate(RTT_LightOcclusion);
-
-			UINT32 viewIdx = sceneCamera->getRendererId();
-
-			for(UINT32 i = 0; i < (UINT32)LightType::Count; i++)
-			{
-				LightType lightType = (LightType)i;
-
-				auto& lights = mVisibleLightInfo->getLights(lightType);
-				UINT32 count = mVisibleLightInfo->getNumShadowedLights(lightType);
-				UINT32 offset = mVisibleLightInfo->getNumUnshadowedLights(lightType);
-
-				for (UINT32 j = 0; j < count; j++)
-				{
-					renderTargets->bind(RTT_LightOcclusion);
-
-					UINT32 lightIdx = offset + j;
-					const RendererLight& light = *lights[lightIdx];
-					ShadowRendering::instance().renderShadowOcclusion(*mScene, mCoreOptions->shadowFilteringQuality,
-						light, viewIdx);
-
-					renderTargets->bind(RTT_LightAccumulation);
-					StandardDeferred::instance().renderLight(lightType, light, *viewInfo, *renderTargets);
-				}
-			}
-
-			renderTargets->release(RTT_LightOcclusion);
-		}
-
-		// Make sure light accumulation buffer isn't bound and can be read from
-		rapi.setRenderTarget(nullptr);
-
-		// Render image based lighting, add it to light accumulation and output scene color
-		renderTargets->allocate(RTT_SceneColor);
-		imageBasedLightingMat->execute(renderTargets, perCameraBuffer, mPreintegratedEnvBRDF);
-
-		if (useSSAO)
-			renderTargets->release(RTT_AmbientOcclusion);
+		const RenderCompositor& compositor = viewInfo->getCompositor();
+		compositor.execute(viewGroup, *viewInfo, sceneInfo, *mCoreOptions);
 
-		renderTargets->release(RTT_LightAccumulation);
-		renderTargets->release(RTT_GBuffer);
+		//renderTargets->allocate(RTT_HiZ);
+		//renderTargets->generate(RTT_HiZ);
 
-		renderTargets->bind(RTT_SceneColor, true);
+		//// Build AO if required
+		//bool useSSAO = viewInfo->getPPInfo().settings->ambientOcclusion.enabled;
+		//if(useSSAO)
+		//{
+		//	renderTargets->allocate(RTT_AmbientOcclusion);
 
-		// If we're using flattened framebuffer for MSAA we need to copy its contents to the MSAA scene texture before
-		// continuing
-		if(isMSAA)
-			mFlatFramebufferToTextureMat->execute(renderTargets->getSceneColorBuffer(), renderTargets->get(RTT_SceneColor));
-
-		// Render skybox (if any)
-		if (mSkyboxTexture != nullptr)
-		{
-			mSkyboxMat->bind(perCameraBuffer);
-			mSkyboxMat->setParams(mSkyboxTexture, Color::White);
-		}
-		else
-		{
-			Color clearColor = viewProps.clearColor;
-
-			mSkyboxSolidColorMat->bind(perCameraBuffer);
-			mSkyboxSolidColorMat->setParams(nullptr, clearColor);
-		}
+		//	// Note: This could be done as async compute (and started earlier, right after base pass)
+		//	PostProcessing::instance().buildSSAO(*viewInfo);
+		//}
 
-		SPtr<Mesh> mesh = gRendererUtility().getSkyBoxMesh();
-		gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
+		//if (useSSAO)
+		//	renderTargets->release(RTT_AmbientOcclusion);
 
-		renderTargets->bind(RTT_SceneColor, false);
+		//renderTargets->bind(RTT_SceneColor, false);
 
 		// Render transparent objects
 		// TODO: Transparent objects cannot receive shadows. In order to support this I'd have to render the light occlusion
 		// for all lights affecting this object into a single (or a few) textures. I can likely use texture arrays for this,
 		// or to avoid sampling many textures, perhaps just jam it all in one or few texture channels. 
-		const Vector<RenderQueueElement>& transparentElements = viewInfo->getTransparentQueue()->getSortedElements();
-		for (auto iter = transparentElements.begin(); iter != transparentElements.end(); ++iter)
-		{
-			BeastRenderableElement* renderElem = static_cast<BeastRenderableElement*>(iter->renderElem);
-			renderElement(*renderElem, iter->passIdx, iter->applyPass, viewProj);
-		}
+		//const Vector<RenderQueueElement>& transparentElements = viewInfo->getTransparentQueue()->getSortedElements();
+		//for (auto iter = transparentElements.begin(); iter != transparentElements.end(); ++iter)
+		//{
+		//	BeastRenderableElement* renderElem = static_cast<BeastRenderableElement*>(iter->renderElem);
+		//	renderElement(*renderElem, iter->passIdx, iter->applyPass, viewProj);
+		//}
 
 		// Trigger post-light-pass callbacks
 		if (viewProps.triggerCallbacks)
@@ -714,28 +537,29 @@ namespace bs { namespace ct
 		// Post-processing and final resolve
 		Rect2 viewportArea = viewProps.nrmViewRect;
 
-		if (viewProps.runPostProcessing)
+		//if (viewProps.runPostProcessing)
+		//{
+		//	// Post-processing code also takes care of writting to the final output target
+		//	PostProcessing::instance().postProcess(viewInfo, renderTargets, frameDelta);
+		//}
+		//else
 		{
-			// Post-processing code also takes care of writting to the final output target
-			PostProcessing::instance().postProcess(viewInfo, renderTargets, frameDelta);
-		}
-		else
-		{
-			// Just copy from scene color to output if no post-processing
-			SPtr<RenderTarget> target = viewProps.target;
+			//// Just copy from scene color to output if no post-processing
+			//SPtr<RenderTarget> target = viewProps.target;
 
-			rapi.setRenderTarget(target);
-			rapi.setViewport(viewportArea);
+			//RenderAPI& rapi = RenderAPI::instance();
+			//rapi.setRenderTarget(target);
+			//rapi.setViewport(viewportArea);
 
-			SPtr<Texture> sceneColor = renderTargets->get(RTT_SceneColor);
-			gRendererUtility().blit(sceneColor, Rect2I::EMPTY, viewProps.flipView);
+			//SPtr<Texture> sceneColor = renderTargets->get(RTT_SceneColor);
+			//gRendererUtility().blit(sceneColor, Rect2I::EMPTY, viewProps.flipView);
 		}
 
-		renderTargets->release(RTT_HiZ);
-		renderTargets->release(RTT_SceneColor);
+		//renderTargets->release(RTT_HiZ);
+		//renderTargets->release(RTT_SceneColor);
 
-		if (isMSAA)
-			renderTargets->release(RTT_ResolvedDepth);
+		//if (isMSAA)
+		//	renderTargets->release(RTT_ResolvedDepth);
 
 		// Trigger overlay callbacks
 		if (viewProps.triggerCallbacks)
@@ -763,7 +587,7 @@ namespace bs { namespace ct
 		gProfilerCPU().beginSample("RenderOverlay");
 
 		viewInfo->getPerViewBuffer()->flushToGPU();
-		viewInfo->beginFrame(false);
+		viewInfo->beginFrame();
 
 		auto& viewProps = viewInfo->getProperties();
 		const Camera* camera = viewInfo->getSceneCamera();
@@ -830,20 +654,20 @@ namespace bs { namespace ct
 				element.morphVertexDeclaration);
 	}
 
-	void RenderBeast::updateLightProbes(const FrameInfo& frameInfo)
+	void RenderBeast::renderReflectionProbes(const FrameInfo& frameInfo)
 	{
-		const SceneInfo& sceneInfo = mScene->getSceneInfo();
+		SceneInfo& sceneInfo = mScene->_getSceneInfo();
 		UINT32 numProbes = (UINT32)sceneInfo.reflProbes.size();
 
 		bs_frame_mark();
 		{		
 			UINT32 currentCubeArraySize = 0;
 
-			if(mReflCubemapArrayTex != nullptr)
-				mReflCubemapArrayTex->getProperties().getNumArraySlices();
+			if(sceneInfo.reflProbeCubemapsTex != nullptr)
+				currentCubeArraySize = sceneInfo.reflProbeCubemapsTex->getProperties().getNumArraySlices();
 
 			bool forceArrayUpdate = false;
-			if(mReflCubemapArrayTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))
+			if(sceneInfo.reflProbeCubemapsTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))
 			{
 				TEXTURE_DESC cubeMapDesc;
 				cubeMapDesc.type = TEX_TYPE_CUBE_MAP;
@@ -853,12 +677,12 @@ namespace bs { namespace ct
 				cubeMapDesc.numMips = PixelUtil::getMaxMipmaps(cubeMapDesc.width, cubeMapDesc.height, 1, cubeMapDesc.format);
 				cubeMapDesc.numArraySlices = std::min(MaxReflectionCubemaps, numProbes + 4); // Keep a few empty entries
 
-				mReflCubemapArrayTex = Texture::create(cubeMapDesc);
+				sceneInfo.reflProbeCubemapsTex = Texture::create(cubeMapDesc);
 
 				forceArrayUpdate = true;
 			}
 
-			auto& cubemapArrayProps = mReflCubemapArrayTex->getProperties();
+			auto& cubemapArrayProps = sceneInfo.reflProbeCubemapsTex->getProperties();
 
 			TEXTURE_DESC cubemapDesc;
 			cubemapDesc.type = TEX_TYPE_CUBE_MAP;
@@ -926,7 +750,7 @@ namespace bs { namespace ct
 					{
 						for(UINT32 face = 0; face < 6; face++)
 							for(UINT32 mip = 0; mip <= srcProps.getNumMipmaps(); mip++)
-								probeInfo.texture->copy(mReflCubemapArrayTex, face, mip, probeInfo.arrayIdx * 6 + face, mip);
+								probeInfo.texture->copy(sceneInfo.reflProbeCubemapsTex, face, mip, probeInfo.arrayIdx * 6 + face, mip);
 					}
 
 					mScene->setReflectionProbeArrayIndex(i, probeInfo.arrayIdx, true);
@@ -934,56 +758,68 @@ namespace bs { namespace ct
 
 				// Note: Consider pruning the reflection cubemap array if empty slot count becomes too high
 			}
-
-			// Get skybox image-based lighting textures if needed/available
-			if (mSkybox != nullptr && mSkyboxTexture != nullptr)
-			{
-				// If haven't assigned them already, do it now
-				if (mSkyboxFilteredReflections == nullptr)
-				{
-					if (!LightProbeCache::instance().isRadianceDirty(mSkybox->getUUID()))
-						mSkyboxFilteredReflections = LightProbeCache::instance().getCachedRadianceTexture(mSkybox->getUUID());
-					else
-					{
-						mSkyboxFilteredReflections = Texture::create(cubemapDesc);
-
-						IBLUtility::scaleCubemap(mSkyboxTexture, 0, mSkyboxFilteredReflections, 0);
-						IBLUtility::filterCubemapForSpecular(mSkyboxFilteredReflections, scratchCubemap);
-						LightProbeCache::instance().setCachedRadianceTexture(mSkybox->getUUID(), mSkyboxFilteredReflections);
-					}
-				}
-
-				if(mSkyboxIrradiance == nullptr)
-				{
-					if (!LightProbeCache::instance().isIrradianceDirty(mSkybox->getUUID()))
-						mSkyboxIrradiance = LightProbeCache::instance().getCachedIrradianceTexture(mSkybox->getUUID());
-					else
-					{
-						TEXTURE_DESC irradianceCubemapDesc;
-						irradianceCubemapDesc.type = TEX_TYPE_CUBE_MAP;
-						irradianceCubemapDesc.format = PF_FLOAT_R11G11B10;
-						irradianceCubemapDesc.width = IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
-						irradianceCubemapDesc.height = IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
-						irradianceCubemapDesc.numMips = 0;
-						irradianceCubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
-
-						mSkyboxIrradiance = Texture::create(irradianceCubemapDesc);
-
-						IBLUtility::filterCubemapForIrradiance(mSkyboxFilteredReflections, mSkyboxIrradiance);
-						LightProbeCache::instance().setCachedIrradianceTexture(mSkybox->getUUID(), mSkyboxFilteredReflections);
-					}
-				}
-			}
-			else
-			{
-				mSkyboxFilteredReflections = nullptr;
-				mSkyboxIrradiance = nullptr;
-			}
-
 		}
 		bs_frame_clear();
 	}
 
+	void RenderBeast::updateSkybox()
+	{
+		SkyInfo& sky = mScene->_getSceneInfo().sky;
+
+		// Get skybox image-based lighting textures if needed/available
+		if (sky.skybox != nullptr && sky.radiance != nullptr)
+		{
+			// If haven't assigned them already, do it now
+			if (sky.filteredReflections == nullptr)
+			{
+				if (!LightProbeCache::instance().isRadianceDirty(sky.skybox->getUUID()))
+					sky.filteredReflections = LightProbeCache::instance().getCachedRadianceTexture(sky.skybox->getUUID());
+				else
+				{
+					TEXTURE_DESC cubemapDesc;
+					cubemapDesc.type = TEX_TYPE_CUBE_MAP;
+					cubemapDesc.format = PF_FLOAT_R11G11B10;
+					cubemapDesc.width = IBLUtility::REFLECTION_CUBEMAP_SIZE;
+					cubemapDesc.height = IBLUtility::REFLECTION_CUBEMAP_SIZE;
+					cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
+					cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
+
+					sky.filteredReflections = Texture::create(cubemapDesc);
+
+					IBLUtility::scaleCubemap(sky.radiance, 0, sky.filteredReflections, 0);
+					IBLUtility::filterCubemapForSpecular(sky.filteredReflections, nullptr);
+					LightProbeCache::instance().setCachedRadianceTexture(sky.skybox->getUUID(), sky.filteredReflections);
+				}
+			}
+
+			if(sky.irradiance == nullptr)
+			{
+				if (!LightProbeCache::instance().isIrradianceDirty(sky.skybox->getUUID()))
+					sky.irradiance = LightProbeCache::instance().getCachedIrradianceTexture(sky.skybox->getUUID());
+				else
+				{
+					TEXTURE_DESC irradianceCubemapDesc;
+					irradianceCubemapDesc.type = TEX_TYPE_CUBE_MAP;
+					irradianceCubemapDesc.format = PF_FLOAT_R11G11B10;
+					irradianceCubemapDesc.width = IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
+					irradianceCubemapDesc.height = IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
+					irradianceCubemapDesc.numMips = 0;
+					irradianceCubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
+
+					sky.irradiance = Texture::create(irradianceCubemapDesc);
+
+					IBLUtility::filterCubemapForIrradiance(sky.filteredReflections, sky.irradiance);
+					LightProbeCache::instance().setCachedIrradianceTexture(sky.skybox->getUUID(), sky.filteredReflections);
+				}
+			}
+		}
+		else
+		{
+			sky.filteredReflections = nullptr;
+			sky.irradiance = nullptr;
+		}
+	}
+
 	void RenderBeast::captureSceneCubeMap(const SPtr<Texture>& cubemap, const Vector3& position, bool hdr, const FrameInfo& frameInfo)
 	{
 		const SceneInfo& sceneInfo = mScene->getSceneInfo();
@@ -1090,13 +926,11 @@ namespace bs { namespace ct
 
 			views[i].setView(viewDesc);
 			views[i].updatePerViewBuffer();
-
-			views[i].determineVisible(sceneInfo.renderables, sceneInfo.renderableCullInfos);
 		}
 
 		RendererView* viewPtrs[] = { &views[0], &views[1], &views[2], &views[3], &views[4], &views[5] };
 
-		RendererViewGroup viewGroup(viewPtrs, 6);
+		RendererViewGroup viewGroup(viewPtrs, 6, mCoreOptions->shadowMapSize);
 		viewGroup.determineVisibility(sceneInfo);
 
 		renderViews(viewGroup, frameInfo);

+ 732 - 0
Source/RenderBeast/Source/BsRenderCompositor.cpp

@@ -0,0 +1,732 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsRenderCompositor.h"
+#include "BsGpuResourcePool.h"
+#include "BsRendererView.h"
+#include "BsRendererUtility.h"
+#include "BsMesh.h"
+#include "BsGpuBuffer.h"
+#include "BsStandardDeferredLighting.h"
+#include "BsGlobalMaterials.h"
+#include "BsRenderBeastOptions.h"
+#include "BsCamera.h"
+#include "BsRendererScene.h"
+#include "BsIBLUtility.h"
+
+namespace bs { namespace ct
+{
+	UnorderedMap<StringID, RenderCompositor::NodeType*> RenderCompositor::mNodeTypes;
+
+	void RenderCompositor::build(const RendererView& view, const StringID& finalNode)
+	{
+		clear();
+
+		bs_frame_mark();
+		{
+			FrameUnorderedMap<StringID, UINT32> processedNodes;
+			mIsValid = true;
+
+			std::function<bool(const StringID&)> registerNode = [&](const StringID& nodeId)
+			{
+				// Find node type
+				auto iterFind = mNodeTypes.find(nodeId);
+				if (iterFind == mNodeTypes.end())
+				{
+					LOGERR("Cannot find render compositor node of type \"" + String(nodeId.cstr()) + "\".");
+					return false;
+				}
+
+				NodeType* nodeType = iterFind->second;
+
+				// Register current node
+				auto iterFind2 = processedNodes.find(nodeId);
+
+				// New node
+				if (iterFind2 == processedNodes.end())
+				{
+					// Mark it as invalid for now
+					processedNodes[nodeId] = -1;
+				}
+
+				// Register node dependencies
+				SmallVector<StringID, 4> depIds = nodeType->getDependencies(view);
+				for (auto& dep : depIds)
+				{
+					if (!registerNode(dep))
+						return false;
+				}
+
+				// Register current node
+				UINT32 curIdx;
+
+				// New node, properly populate its index
+				if (iterFind2 == processedNodes.end())
+				{
+					iterFind2 = processedNodes.find(nodeId);
+
+					curIdx = (UINT32)mNodeInfos.size();
+					mNodeInfos.push_back(NodeInfo());
+					processedNodes[nodeId] = curIdx;
+
+					NodeInfo& nodeInfo = mNodeInfos.back();
+					nodeInfo.node = nodeType->create();
+					nodeInfo.lastUseIdx = -1;
+
+					for (auto& depId : depIds)
+					{
+						iterFind2 = processedNodes.find(depId);
+
+						NodeInfo& depNodeInfo = mNodeInfos[iterFind2->second];
+						nodeInfo.inputs.push_back(depNodeInfo.node);
+					}
+				}
+				else // Existing node
+				{
+					curIdx = iterFind2->second;
+
+					// Check if invalid
+					if (curIdx == -1)
+					{
+						LOGERR("Render compositor nodes recursion detected. Node \"" + String(nodeId.cstr()) + "\" " +
+							"depends on node \"" + String(iterFind->first.cstr()) + "\" which is not available at " +
+							"this stage.");
+						return false;
+					}
+				}
+
+				// Update dependency last use counters
+				for (auto& dep : depIds)
+				{
+					iterFind2 = processedNodes.find(dep);
+
+					NodeInfo& depNodeInfo = mNodeInfos[iterFind2->second];
+					if (depNodeInfo.lastUseIdx == -1)
+						depNodeInfo.lastUseIdx = curIdx;
+					else
+						depNodeInfo.lastUseIdx = std::max(depNodeInfo.lastUseIdx, curIdx);
+				}
+
+				return true;
+			};
+
+			mIsValid = registerNode(finalNode);
+
+			if (!mIsValid)
+				clear();
+		}
+		bs_frame_clear();
+	}
+
+	void RenderCompositor::execute(const RendererViewGroup& viewGroup, const RendererView& view, const SceneInfo& scene, 
+		const RenderBeastOptions& options) const
+	{
+		if (!mIsValid)
+			return;
+
+		bs_frame_mark();
+		{
+			FrameVector<const NodeInfo*> activeNodes;
+
+			UINT32 idx = 0;
+			for (auto& entry : mNodeInfos)
+			{
+				RenderCompositorNodeInputs inputs(viewGroup, view, scene, options, entry.inputs);
+				entry.node->render(inputs);
+
+				activeNodes.push_back(&entry);
+
+				for (UINT32 i = 0; i < (UINT32)activeNodes.size(); ++i)
+				{
+					if (activeNodes[i] == nullptr)
+						continue;
+
+					if (activeNodes[i]->lastUseIdx <= idx)
+					{
+						activeNodes[i]->node->clear();
+						activeNodes[i] = nullptr;
+					}
+				}
+
+				idx++;
+			}
+		}
+		bs_frame_clear();
+
+		if (!mNodeInfos.empty())
+			mNodeInfos.back().node->clear();
+	}
+
+	void RCNodeSceneDepth::render(const RenderCompositorNodeInputs& inputs)
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+		UINT32 numSamples = viewProps.numSamples;
+
+		depthTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_D32_S8X24, width, height, TU_DEPTHSTENCIL,
+			numSamples, false));
+	}
+
+	void RCNodeSceneDepth::clear()
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		resPool.release(depthTex);
+	}
+
+	SmallVector<StringID, 4> RCNodeSceneDepth::getDependencies(const RendererView& view)
+	{
+		return {};
+	}
+
+	void RCNodeGBuffer::render(const RenderCompositorNodeInputs& inputs)
+	{
+		// Allocate necessary textures & targets
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+		UINT32 numSamples = viewProps.numSamples;
+
+		// Note: Consider customizable formats. e.g. for testing if quality can be improved with higher precision normals.
+		albedoTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_R8G8B8A8, width, height, TU_RENDERTARGET,
+			numSamples, true));
+		normalTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_UNORM_R10G10B10A2, width, height, TU_RENDERTARGET,
+			numSamples, false));
+		roughMetalTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT16_RG, width, height, TU_RENDERTARGET,
+			numSamples, false)); // Note: Metal doesn't need 16-bit float
+
+		RCNodeSceneDepth* sceneDepthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[0]);
+		SPtr<PooledRenderTexture> sceneDepthTex = sceneDepthNode->depthTex;
+
+		bool rebuildRT = false;
+		if (renderTarget != nullptr)
+		{
+			rebuildRT |= renderTarget->getColorTexture(0) != albedoTex->texture;
+			rebuildRT |= renderTarget->getColorTexture(1) != normalTex->texture;
+			rebuildRT |= renderTarget->getColorTexture(2) != roughMetalTex->texture;
+			rebuildRT |= renderTarget->getDepthStencilTexture() != sceneDepthTex->texture;
+		}
+		else
+			rebuildRT = true;
+
+		if (renderTarget == nullptr || rebuildRT)
+		{
+			RENDER_TEXTURE_DESC gbufferDesc;
+			gbufferDesc.colorSurfaces[0].texture = albedoTex->texture;
+			gbufferDesc.colorSurfaces[0].face = 0;
+			gbufferDesc.colorSurfaces[0].numFaces = 1;
+			gbufferDesc.colorSurfaces[0].mipLevel = 0;
+
+			gbufferDesc.colorSurfaces[1].texture = normalTex->texture;
+			gbufferDesc.colorSurfaces[1].face = 0;
+			gbufferDesc.colorSurfaces[1].numFaces = 1;
+			gbufferDesc.colorSurfaces[1].mipLevel = 0;
+
+			gbufferDesc.colorSurfaces[2].texture = roughMetalTex->texture;
+			gbufferDesc.colorSurfaces[2].face = 0;
+			gbufferDesc.colorSurfaces[2].numFaces = 1;
+			gbufferDesc.colorSurfaces[2].mipLevel = 0;
+
+			gbufferDesc.depthStencilSurface.texture = sceneDepthTex->texture;
+			gbufferDesc.depthStencilSurface.face = 0;
+			gbufferDesc.depthStencilSurface.mipLevel = 0;
+
+			renderTarget = RenderTexture::create(gbufferDesc);
+		}
+
+		// Render base pass
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(renderTarget);
+
+		Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
+		rapi.setViewport(area);
+
+		// Clear depth & stencil according to user defined values, don't clear color as all values will get written to
+		UINT32 clearFlags = viewProps.clearFlags & ~FBT_COLOR;
+		if (clearFlags != 0)
+		{
+			rapi.clearViewport(clearFlags, viewProps.clearColor, viewProps.clearDepthValue, 
+				viewProps.clearStencilValue, 0x01);
+		}
+
+		// Clear all non primary targets (Note: I could perhaps clear all but albedo, since it stores a per-pixel write mask)
+		rapi.clearViewport(FBT_COLOR, Color::ZERO, 1.0f, 0, 0xFF & ~0x01);
+
+		// Render all visible opaque elements
+		const Vector<RenderQueueElement>& opaqueElements = inputs.view.getOpaqueQueue()->getSortedElements();
+		for (auto iter = opaqueElements.begin(); iter != opaqueElements.end(); ++iter)
+		{
+			BeastRenderableElement* renderElem = static_cast<BeastRenderableElement*>(iter->renderElem);
+
+			SPtr<Material> material = renderElem->material;
+
+			if (iter->applyPass)
+				gRendererUtility().setPass(material, iter->passIdx, renderElem->techniqueIdx);
+
+			gRendererUtility().setPassParams(renderElem->params, iter->passIdx);
+
+			if(renderElem->morphVertexDeclaration == nullptr)
+				gRendererUtility().draw(renderElem->mesh, renderElem->subMesh);
+			else
+				gRendererUtility().drawMorph(renderElem->mesh, renderElem->subMesh, renderElem->morphShapeBuffer, 
+					renderElem->morphVertexDeclaration);
+		}
+	}
+
+	void RCNodeGBuffer::clear()
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+
+		resPool.release(albedoTex);
+		resPool.release(normalTex);
+		resPool.release(roughMetalTex);
+	}
+
+	SmallVector<StringID, 4> RCNodeGBuffer::getDependencies(const RendererView& view)
+	{
+		return { RCNodeSceneDepth::getNodeId() };
+	}
+
+	void RCNodeSceneColor::render(const RenderCompositorNodeInputs& inputs)
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+		UINT32 numSamples = viewProps.numSamples;
+
+		// Note: Consider customizable HDR format via options? e.g. smaller PF_FLOAT_R11G11B10 or larger 32-bit format
+		sceneColorTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT16_RGBA, width, height, TU_RENDERTARGET | 
+			TU_LOADSTORE, numSamples, false));
+
+		RCNodeSceneDepth* sceneDepthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[0]);
+		SPtr<PooledRenderTexture> sceneDepthTex = sceneDepthNode->depthTex;
+
+		if (viewProps.numSamples > 1)
+		{
+			UINT32 bufferNumElements = width * height * viewProps.numSamples;
+			flattenedSceneColorBuffer = resPool.get(POOLED_STORAGE_BUFFER_DESC::createStandard(BF_16X4F, bufferNumElements));
+		}
+		else
+			flattenedSceneColorBuffer = nullptr;
+
+		bool rebuildRT = false;
+		if (renderTarget != nullptr)
+		{
+			rebuildRT |= renderTarget->getColorTexture(0) != sceneColorTex->texture;
+			rebuildRT |= renderTarget->getDepthStencilTexture() != sceneDepthTex->texture;
+		}
+		else
+			rebuildRT = true;
+
+		if (rebuildRT)
+		{
+			RENDER_TEXTURE_DESC sceneColorDesc;
+			sceneColorDesc.colorSurfaces[0].texture = sceneColorTex->texture;
+			sceneColorDesc.colorSurfaces[0].face = 0;
+			sceneColorDesc.colorSurfaces[0].numFaces = 1;
+			sceneColorDesc.colorSurfaces[0].mipLevel = 0;
+
+			sceneColorDesc.depthStencilSurface.texture = sceneDepthTex->texture;
+			sceneColorDesc.depthStencilSurface.face = 0;
+			sceneColorDesc.depthStencilSurface.numFaces = 1;
+			sceneColorDesc.depthStencilSurface.mipLevel = 0;
+
+			renderTarget = RenderTexture::create(sceneColorDesc);
+		}
+	}
+
+	void RCNodeSceneColor::clear()
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		resPool.release(sceneColorTex);
+
+		if (flattenedSceneColorBuffer != nullptr)
+			resPool.release(flattenedSceneColorBuffer);
+	}
+
+	SmallVector<StringID, 4> RCNodeSceneColor::getDependencies(const RendererView& view)
+	{
+		return { RCNodeSceneDepth::getNodeId() };
+	}
+
+	void RCNodeLightAccumulation::render(const RenderCompositorNodeInputs& inputs)
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+		UINT32 numSamples = viewProps.numSamples;
+		
+		if (numSamples > 1)
+		{
+			UINT32 bufferNumElements = width * height * numSamples;
+			flattenedLightAccumBuffer =
+				resPool.get(POOLED_STORAGE_BUFFER_DESC::createStandard(BF_16X4F, bufferNumElements));
+
+			SPtr<GpuBuffer> buffer = flattenedLightAccumBuffer->buffer;
+			auto& bufferProps = buffer->getProperties();
+
+			UINT32 bufferSize = bufferProps.getElementSize() * bufferProps.getElementCount();
+			UINT16* data = (UINT16*)buffer->lock(0, bufferSize, GBL_WRITE_ONLY_DISCARD);
+			{
+				memset(data, 0, bufferSize);
+			}
+			buffer->unlock();
+		}
+		else
+			flattenedLightAccumBuffer = nullptr;
+
+		lightAccumulationTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_FLOAT16_RGBA, width,
+			height, TU_LOADSTORE | TU_RENDERTARGET, numSamples, false));
+
+		bool rebuildRT;
+		if (renderTarget != nullptr)
+			rebuildRT = renderTarget->getColorTexture(0) != lightAccumulationTex->texture;
+		else
+			rebuildRT = true;
+
+		if (rebuildRT)
+		{
+			RENDER_TEXTURE_DESC lightAccumulationRTDesc;
+			lightAccumulationRTDesc.colorSurfaces[0].texture = lightAccumulationTex->texture;
+			lightAccumulationRTDesc.colorSurfaces[0].face = 0;
+			lightAccumulationRTDesc.colorSurfaces[0].numFaces = 1;
+			lightAccumulationRTDesc.colorSurfaces[0].mipLevel = 0;
+
+			renderTarget = RenderTexture::create(lightAccumulationRTDesc);
+		}
+	}
+
+	void RCNodeLightAccumulation::clear()
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		resPool.release(lightAccumulationTex);
+
+		if (flattenedLightAccumBuffer)
+			resPool.release(flattenedLightAccumBuffer);
+	}
+
+	SmallVector<StringID, 4> RCNodeLightAccumulation::getDependencies(const RendererView& view)
+	{
+		return {};
+	}
+
+	void RCNodeTiledDeferredLighting::render(const RenderCompositorNodeInputs& inputs)
+	{
+		output = static_cast<RCNodeLightAccumulation*>(inputs.inputNodes[0]);
+
+		RCNodeGBuffer* gbufferNode = static_cast<RCNodeGBuffer*>(inputs.inputNodes[1]);
+		RCNodeSceneDepth* sceneDepthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[2]);
+
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+		ITiledDeferredLightingMat* tiledDeferredMat = 
+			GlobalMaterials::instance().getTileDeferredLighting(viewProps.numSamples);
+
+		GBufferInput gbuffer;
+		gbuffer.albedo = gbufferNode->albedoTex->texture;
+		gbuffer.normals = gbufferNode->normalTex->texture;
+		gbuffer.roughMetal = gbufferNode->roughMetalTex->texture;
+		gbuffer.depth = sceneDepthNode->depthTex->texture;
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(output->renderTarget, FBT_DEPTH | FBT_STENCIL, RT_COLOR0 | RT_DEPTH_STENCIL);
+
+		const VisibleLightData& lightData = inputs.viewGroup.getVisibleLightData();
+		SPtr<GpuBuffer> flattenedLightAccumBuffer;
+		if (output->flattenedLightAccumBuffer)
+			flattenedLightAccumBuffer = output->flattenedLightAccumBuffer->buffer;
+
+		tiledDeferredMat->execute(inputs.view, lightData, gbuffer, output->lightAccumulationTex->texture, 
+			flattenedLightAccumBuffer);
+	}
+
+	void RCNodeTiledDeferredLighting::clear()
+	{
+		output = nullptr;
+	}
+
+	SmallVector<StringID, 4> RCNodeTiledDeferredLighting::getDependencies(const RendererView& view)
+	{
+		return { RCNodeLightAccumulation::getNodeId(), RCNodeGBuffer::getNodeId(), RCNodeSceneDepth::getNodeId() };
+	}
+
+	void RCNodeStandardDeferredLighting::render(const RenderCompositorNodeInputs& inputs)
+	{
+		GpuResourcePool& resPool = GpuResourcePool::instance();
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+
+		UINT32 width = viewProps.viewRect.width;
+		UINT32 height = viewProps.viewRect.height;
+		UINT32 numSamples = viewProps.numSamples;
+
+		RCNodeTiledDeferredLighting* tileDeferredNode = static_cast<RCNodeTiledDeferredLighting*>(inputs.inputNodes[0]);
+		output = tileDeferredNode->output;
+
+		RCNodeGBuffer* gbufferNode = static_cast<RCNodeGBuffer*>(inputs.inputNodes[1]);
+		RCNodeSceneDepth* sceneDepthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[2]);
+
+		// Allocate light occlusion
+		mLightOcclusionTex = resPool.get(POOLED_RENDER_TEXTURE_DESC::create2D(PF_R8, width,
+			height, TU_RENDERTARGET, numSamples, false));
+
+		bool rebuildRT = false;
+		if (mRenderTarget != nullptr)
+		{
+			rebuildRT |= mRenderTarget->getColorTexture(0) != mLightOcclusionTex->texture;
+			rebuildRT |= mRenderTarget->getDepthStencilTexture() != sceneDepthNode->depthTex->texture;
+		}
+		else
+			rebuildRT = true;
+
+		if (rebuildRT)
+		{
+			RENDER_TEXTURE_DESC lightOcclusionRTDesc;
+			lightOcclusionRTDesc.colorSurfaces[0].texture = mLightOcclusionTex->texture;
+			lightOcclusionRTDesc.colorSurfaces[0].face = 0;
+			lightOcclusionRTDesc.colorSurfaces[0].numFaces = 1;
+			lightOcclusionRTDesc.colorSurfaces[0].mipLevel = 0;
+
+			lightOcclusionRTDesc.depthStencilSurface.texture = sceneDepthNode->depthTex->texture;
+			lightOcclusionRTDesc.depthStencilSurface.face = 0;
+			lightOcclusionRTDesc.depthStencilSurface.numFaces = 1;
+			lightOcclusionRTDesc.depthStencilSurface.mipLevel = 0;
+
+			mRenderTarget = RenderTexture::create(lightOcclusionRTDesc);
+		}
+
+		GBufferInput gbuffer;
+		gbuffer.albedo = gbufferNode->albedoTex->texture;
+		gbuffer.normals = gbufferNode->normalTex->texture;
+		gbuffer.roughMetal = gbufferNode->roughMetalTex->texture;
+		gbuffer.depth = sceneDepthNode->depthTex->texture;
+
+		const VisibleLightData& lightData = inputs.viewGroup.getVisibleLightData();
+		const ShadowRendering& shadowRenderer = inputs.viewGroup.getShadowRenderer();
+
+		Camera* sceneCamera = inputs.view.getSceneCamera();
+
+		// Note: Currently skipping shadow rendering for any views that aren't part of the scene (like temporary ones)
+		if (sceneCamera != nullptr)
+		{
+			RenderAPI& rapi = RenderAPI::instance();
+			UINT32 viewIdx = sceneCamera->getRendererId();
+			for (UINT32 i = 0; i < (UINT32)LightType::Count; i++)
+			{
+				LightType lightType = (LightType)i;
+
+				auto& lights = lightData.getLights(lightType);
+				UINT32 count = lightData.getNumShadowedLights(lightType);
+				UINT32 offset = lightData.getNumUnshadowedLights(lightType);
+
+				for (UINT32 j = 0; j < count; j++)
+				{
+					rapi.setRenderTarget(mRenderTarget, FBT_DEPTH, RT_DEPTH_STENCIL);
+
+					Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
+					rapi.setViewport(area);
+
+					rapi.clearViewport(FBT_COLOR, Color::ZERO);
+
+					UINT32 lightIdx = offset + j;
+					const RendererLight& light = *lights[lightIdx];
+					shadowRenderer.renderShadowOcclusion(inputs.scene, inputs.options.shadowFilteringQuality,
+						light, viewIdx, gbuffer);
+
+					rapi.setRenderTarget(output->renderTarget, FBT_DEPTH | FBT_STENCIL, RT_COLOR0 | RT_DEPTH_STENCIL);
+					StandardDeferred::instance().renderLight(lightType, light, inputs.view, gbuffer,
+						mLightOcclusionTex->texture);
+				}
+			}
+
+			// Makes sure light accumulation can be read by following passes
+			rapi.setRenderTarget(nullptr);
+		}
+
+		resPool.release(mLightOcclusionTex);
+	}
+
+	void RCNodeStandardDeferredLighting::clear()
+	{
+		output = nullptr;
+	}
+
+	SmallVector<StringID, 4> RCNodeStandardDeferredLighting::getDependencies(const RendererView& view)
+	{
+		SmallVector<StringID, 4> deps;
+
+		deps.push_back(RCNodeTiledDeferredLighting::getNodeId());
+		deps.push_back(RCNodeGBuffer::getNodeId());
+		deps.push_back(RCNodeSceneDepth::getNodeId());
+
+		if (view.getProperties().numSamples > 1)
+			deps.push_back(RCNodeUnflattenLightAccum::getNodeId());
+
+		return deps;
+	}
+
+	void RCNodeUnflattenLightAccum::render(const RenderCompositorNodeInputs& inputs)
+	{
+		RCNodeLightAccumulation* lightAccumNode = static_cast<RCNodeLightAccumulation*>(inputs.inputNodes[0]);
+		FlatFramebufferToTextureMat* material = GlobalMaterials::instance().getUnflattenBuffer();
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(lightAccumNode->renderTarget, FBT_DEPTH | FBT_STENCIL, RT_COLOR0 | RT_DEPTH_STENCIL);
+		material->execute(lightAccumNode->flattenedLightAccumBuffer->buffer, lightAccumNode->lightAccumulationTex->texture);
+	}
+
+	void RCNodeUnflattenLightAccum::clear()
+	{
+		output = nullptr;
+	}
+
+	SmallVector<StringID, 4> RCNodeUnflattenLightAccum::getDependencies(const RendererView& view)
+	{
+		return { RCNodeLightAccumulation::getNodeId() };
+	}
+
+	void RCNodeTiledDeferredIBL::render(const RenderCompositorNodeInputs& inputs)
+	{
+		RCNodeSceneColor* sceneColorNode = static_cast<RCNodeSceneColor*>(inputs.inputNodes[0]);
+		RCNodeGBuffer* gbufferNode = static_cast<RCNodeGBuffer*>(inputs.inputNodes[1]);
+		RCNodeSceneDepth* sceneDepthNode = static_cast<RCNodeSceneDepth*>(inputs.inputNodes[2]);
+		RCNodeLightAccumulation* lightAccumNode = static_cast <RCNodeLightAccumulation*>(inputs.inputNodes[3]);
+
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+		ITiledDeferredImageBasedLightingMat* material = GlobalMaterials::instance().getTileDeferredIBL(viewProps.numSamples);
+
+		TiledDeferredImageBasedLighting::Inputs iblInputs;
+		iblInputs.gbuffer.albedo = gbufferNode->albedoTex->texture;
+		iblInputs.gbuffer.normals = gbufferNode->normalTex->texture;
+		iblInputs.gbuffer.roughMetal = gbufferNode->roughMetalTex->texture;
+		iblInputs.gbuffer.depth = sceneDepthNode->depthTex->texture;
+		iblInputs.sceneColorTex = sceneColorNode->sceneColorTex->texture;
+		iblInputs.lightAccumulation = lightAccumNode->lightAccumulationTex->texture;
+		iblInputs.preIntegratedGF = IBLUtility::getPreintegratedEnvBRDF();
+
+		if(sceneColorNode->flattenedSceneColorBuffer)
+			iblInputs.sceneColorBuffer = sceneColorNode->flattenedSceneColorBuffer->buffer;
+
+		material->execute(inputs.view, inputs.scene, inputs.viewGroup.getVisibleReflProbeData(), iblInputs);
+	}
+
+	void RCNodeTiledDeferredIBL::clear()
+	{
+		output = nullptr;
+	}
+
+	SmallVector<StringID, 4> RCNodeTiledDeferredIBL::getDependencies(const RendererView& view)
+	{
+		SmallVector<StringID, 4> deps;
+
+		deps.push_back(RCNodeSceneColor::getNodeId());
+		deps.push_back(RCNodeGBuffer::getNodeId());
+		deps.push_back(RCNodeSceneDepth::getNodeId());
+		deps.push_back(RCNodeLightAccumulation::getNodeId());
+		deps.push_back(RCNodeStandardDeferredLighting::getNodeId());
+
+		return deps;
+	}
+
+	void RCNodeUnflattenSceneColor::render(const RenderCompositorNodeInputs& inputs)
+	{
+		RCNodeSceneColor* sceneColorNode = static_cast<RCNodeSceneColor*>(inputs.inputNodes[0]);
+		FlatFramebufferToTextureMat* material = GlobalMaterials::instance().getUnflattenBuffer();
+
+		int readOnlyFlags = FBT_DEPTH | FBT_STENCIL;
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(sceneColorNode->renderTarget, readOnlyFlags, RT_COLOR0 | RT_DEPTH_STENCIL);
+
+		Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
+		rapi.setViewport(area);
+
+		material->execute(sceneColorNode->flattenedSceneColorBuffer->buffer, sceneColorNode->sceneColorTex->texture);
+	}
+
+	void RCNodeUnflattenSceneColor::clear()
+	{
+		output = nullptr;
+	}
+
+	SmallVector<StringID, 4> RCNodeUnflattenSceneColor::getDependencies(const RendererView& view)
+	{
+		return { RCNodeSceneColor::getNodeId() };
+	}
+
+	void RCNodeSkybox::render(const RenderCompositorNodeInputs& inputs)
+	{
+		const SkyInfo& sky = inputs.scene.sky;
+
+		if (sky.radiance != nullptr)
+		{
+			SkyboxMat<false>* material = GlobalMaterials::instance().getSkyboxTexture();
+			material->bind(inputs.view.getPerViewBuffer());
+			material->setParams(sky.radiance, Color::White);
+		}
+		else
+		{
+			Color clearColor = inputs.view.getProperties().clearColor;
+
+			SkyboxMat<true>* material = GlobalMaterials::instance().getSkyboxColor();
+			material->bind(inputs.view.getPerViewBuffer());
+			material->setParams(nullptr, clearColor);
+		}
+
+		RCNodeSceneColor* sceneColorNode = static_cast<RCNodeSceneColor*>(inputs.inputNodes[1]);
+		int readOnlyFlags = FBT_DEPTH | FBT_STENCIL;
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(sceneColorNode->renderTarget, readOnlyFlags, RT_COLOR0 | RT_DEPTH_STENCIL);
+
+		Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
+		rapi.setViewport(area);
+
+		SPtr<Mesh> mesh = gRendererUtility().getSkyBoxMesh();
+		gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
+	}
+
+	void RCNodeSkybox::clear()
+	{ }
+
+	SmallVector<StringID, 4> RCNodeSkybox::getDependencies(const RendererView& view)
+	{
+		SmallVector<StringID, 4> deps;
+
+		deps.push_back(RCNodeTiledDeferredIBL::getNodeId());
+		deps.push_back(RCNodeSceneColor::getNodeId());
+
+		if (view.getProperties().numSamples > 1)
+			deps.push_back(RCNodeUnflattenSceneColor::getNodeId());
+
+		return deps;
+	}
+
+	void RCNodeFinalResolve::render(const RenderCompositorNodeInputs& inputs)
+	{
+		const RendererViewProperties& viewProps = inputs.view.getProperties();
+		RCNodeSceneColor* sceneColorNode = static_cast<RCNodeSceneColor*>(inputs.inputNodes[0]);
+
+		SPtr<RenderTarget> target = viewProps.target;
+
+		RenderAPI& rapi = RenderAPI::instance();
+		rapi.setRenderTarget(target);
+		rapi.setViewport(viewProps.nrmViewRect);
+
+		SPtr<Texture> sceneColor = sceneColorNode->sceneColorTex->texture;
+		gRendererUtility().blit(sceneColor, Rect2I::EMPTY, viewProps.flipView);
+	}
+
+	void RCNodeFinalResolve::clear()
+	{ }
+
+	SmallVector<StringID, 4> RCNodeFinalResolve::getDependencies(const RendererView& view)
+	{
+		return{ RCNodeSceneColor::getNodeId(), RCNodeSkybox::getNodeId() };
+	}
+}}

+ 66 - 0
Source/RenderBeast/Source/BsRendererScene.cpp

@@ -11,6 +11,7 @@
 #include "BsGpuParamsSet.h"
 #include "BsRenderBeastOptions.h"
 #include "BsRenderBeast.h"
+#include "BsSkybox.h"
 
 namespace bs {	namespace ct
 {
@@ -399,6 +400,31 @@ namespace bs {	namespace ct
 		RendererReflectionProbe& probeInfo = mInfo.reflProbes.back();
 
 		mInfo.reflProbeWorldBounds.push_back(probe->getBounds());
+
+		// Find a spot in cubemap array
+		UINT32 numArrayEntries = (UINT32)mInfo.reflProbeCubemapArrayUsedSlots.size();
+		for(UINT32 i = 0; i < numArrayEntries; i++)
+		{
+			if(!mInfo.reflProbeCubemapArrayUsedSlots[i])
+			{
+				setReflectionProbeArrayIndex(probeId, i, false);
+				mInfo.reflProbeCubemapArrayUsedSlots[i] = true;
+				break;
+			}
+		}
+
+		// No empty slot was found
+		if (probeInfo.arrayIdx == -1)
+		{
+			setReflectionProbeArrayIndex(probeId, numArrayEntries, false);
+			mInfo.reflProbeCubemapArrayUsedSlots.push_back(true);
+		}
+
+		if(probeInfo.arrayIdx > MaxReflectionCubemaps)
+		{
+			LOGERR("Reached the maximum number of allowed reflection probe cubemaps at once. "
+				"Ignoring reflection probe data.");
+		}
 	}
 
 	void RendererScene::updateReflectionProbe(ReflectionProbe* probe)
@@ -417,6 +443,10 @@ namespace bs {	namespace ct
 	void RendererScene::unregisterReflectionProbe(ReflectionProbe* probe)
 	{
 		UINT32 probeId = probe->getRendererId();
+		UINT32 arrayIdx = mInfo.reflProbes[probeId].arrayIdx;
+
+		if (arrayIdx != -1)
+			mInfo.reflProbeCubemapArrayUsedSlots[arrayIdx] = false;
 
 		ReflectionProbe* lastProbe = mInfo.reflProbes.back().probe;
 		UINT32 lastProbeId = lastProbe->getRendererId();
@@ -453,6 +483,42 @@ namespace bs {	namespace ct
 			probe->arrayDirty = false;
 	}
 
+	void RendererScene::registerSkybox(Skybox* skybox)
+	{
+		mInfo.sky.skybox = skybox;
+
+		SPtr<Texture> skyTex = skybox->getTexture();
+		if (skyTex != nullptr && skyTex->getProperties().getTextureType() == TEX_TYPE_CUBE_MAP)
+			mInfo.sky.radiance = skyTex;
+
+		mInfo.sky.filteredReflections = nullptr;
+		mInfo.sky.irradiance = nullptr;
+	}
+
+	void RendererScene::updateSkybox(Skybox* skybox)
+	{
+		LightProbeCache::instance().notifyDirty(skybox->getUUID());
+
+		if (mInfo.sky.skybox == skybox)
+		{
+			mInfo.sky.radiance = skybox->getTexture();
+			mInfo.sky.filteredReflections = nullptr;
+			mInfo.sky.irradiance = nullptr;
+		}
+	}
+
+	void RendererScene::unregisterSkybox(Skybox* skybox)
+	{
+		LightProbeCache::instance().unloadCachedTexture(skybox->getUUID());
+
+		if (mInfo.sky.skybox == skybox)
+		{
+			mInfo.sky.radiance = nullptr;
+			mInfo.sky.filteredReflections = nullptr;
+			mInfo.sky.irradiance = nullptr;
+		}
+	}
+
 	void RendererScene::setOptions(const SPtr<RenderBeastOptions>& options)
 	{
 		mOptions = options;

+ 52 - 10
Source/RenderBeast/Source/BsRendererView.cpp

@@ -73,13 +73,12 @@ namespace bs { namespace ct
 	}
 
 	RendererView::RendererView()
-		: mUsingGBuffer(false)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
 	}
 
 	RendererView::RendererView(const RENDERER_VIEW_DESC& desc)
-		: mProperties(desc), mTargetDesc(desc.target), mCamera(desc.sceneCamera), mUsingGBuffer(false)
+		: mProperties(desc), mTargetDesc(desc.target), mCamera(desc.sceneCamera)
 	{
 		mParamBuffer = gPerCameraParamDef.createBuffer();
 		mProperties.prevViewProjTransform = mProperties.viewProjTransform;
@@ -110,6 +109,9 @@ namespace bs { namespace ct
 			*mPostProcessInfo.settings = StandardPostProcessSettings();
 
 		mPostProcessInfo.settingDirty = true;
+
+		// Update compositor hierarchy
+		mCompositor.build(*this, RCNodeFinalResolve::getNodeId());
 	}
 
 	void RendererView::setTransform(const Vector3& origin, const Vector3& direction, const Matrix4& view, 
@@ -136,9 +138,9 @@ namespace bs { namespace ct
 		setStateReductionMode(desc.stateReduction);
 	}
 
-	void RendererView::beginFrame(bool useGBuffer)
+	void RendererView::beginFrame()
 	{
-		if (useGBuffer)
+		if (!mProperties.isOverlay)
 		{
 			// Render scene objects to g-buffer
 			bool createGBuffer = mRenderTargets == nullptr ||
@@ -149,7 +151,6 @@ namespace bs { namespace ct
 				mRenderTargets = RenderTargets::create(mTargetDesc, mProperties.isHDR);
 
 			mRenderTargets->prepare();
-			mUsingGBuffer = true;
 		}
 	}
 
@@ -161,11 +162,8 @@ namespace bs { namespace ct
 		mOpaqueQueue->clear();
 		mTransparentQueue->clear();
 
-		if(mUsingGBuffer)
-		{
+		if(!mProperties.isOverlay)
 			mRenderTargets->cleanup();
-			mUsingGBuffer = false;
-		}
 	}
 
 	void RendererView::determineVisible(const Vector<RendererObject*>& renderables, const Vector<CullInfo>& cullInfos,
@@ -474,10 +472,21 @@ namespace bs { namespace ct
 			gPerCameraParamDef.gAmbientFactor.set(mParamBuffer, 0.0f);
 	}
 
+	void RendererView::updateLightGrid(const VisibleLightData& visibleLightData, 
+		const VisibleReflProbeData& visibleReflProbeData)
+	{
+		mLightGrid.updateGrid(*this, visibleLightData, visibleReflProbeData, mProperties.noLighting);
+	}
+
 	template class SkyboxMat<true>;
 	template class SkyboxMat<false>;
 
-	RendererViewGroup::RendererViewGroup(RendererView** views, UINT32 numViews)
+	RendererViewGroup::RendererViewGroup()
+		:mShadowRenderer(1024)
+	{ }
+
+	RendererViewGroup::RendererViewGroup(RendererView** views, UINT32 numViews, UINT32 shadowMapSize)
+		:mShadowRenderer(shadowMapSize)
 	{
 		setViews(views, numViews);
 	}
@@ -494,6 +503,20 @@ namespace bs { namespace ct
 	{
 		UINT32 numViews = (UINT32)mViews.size();
 
+		// Early exit if no views render scene geometry
+		bool allViewsOverlay = false;
+		for (UINT32 i = 0; i < numViews; i++)
+		{
+			if (!mViews[i]->getProperties().isOverlay)
+			{
+				allViewsOverlay = false;
+				break;
+			}
+		}
+
+		if (allViewsOverlay)
+			return;
+
 		// Generate render queues per camera
 		mVisibility.renderables.resize(sceneInfo.renderables.size(), false);
 		mVisibility.renderables.assign(sceneInfo.renderables.size(), false);
@@ -512,6 +535,9 @@ namespace bs { namespace ct
 
 		for (UINT32 i = 0; i < numViews; i++)
 		{
+			if (mViews[i]->getProperties().isOverlay)
+				continue;
+
 			mViews[i]->determineVisible(sceneInfo.radialLights, sceneInfo.radialLightWorldBounds, LightType::Radial,
 				&mVisibility.radialLights);
 
@@ -535,5 +561,21 @@ namespace bs { namespace ct
 
 			mViews[i]->calculateVisibility(sceneInfo.reflProbeWorldBounds, mVisibility.reflProbes);
 		}
+
+		// Organize light and refl. probe visibility infomation in a more GPU friendly manner
+
+		// Note: I'm determining light and refl. probe visibility for the entire group. It might be more performance
+		// efficient to do it per view. Additionally I'm using a single GPU buffer to hold their information, which is
+		// then updated when each view group is rendered. It might be better to keep one buffer reserved per-view.
+		mVisibleLightData.update(sceneInfo, *this);
+		mVisibleReflProbeData.update(sceneInfo, *this);
+
+		for (UINT32 i = 0; i < numViews; i++)
+		{
+			if (mViews[i]->getProperties().isOverlay)
+				continue;
+
+			mViews[i]->updateLightGrid(mVisibleLightData, mVisibleReflProbeData);
+		}
 	}
 }}

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

@@ -200,7 +200,7 @@ namespace bs { namespace ct
 		TextureSurface surface;
 		surface.arraySlice = params.shadowMapFace;
 
-		mGBufferParams.bind(params.renderTargets);
+		mGBufferParams.bind(params.gbuffer);
 
 		mShadowMapParam.set(params.shadowMap, surface);
 		mShadowSamplerParam.set(mSamplerState);
@@ -304,7 +304,7 @@ namespace bs { namespace ct
 		Vector4 lightPosAndScale(params.light.getPosition(), params.light.getAttenuationRadius());
 		gShadowProjectVertParamsDef.gPositionAndScale.set(mVertParams, lightPosAndScale);
 
-		mGBufferParams.bind(params.renderTargets);
+		mGBufferParams.bind(params.gbuffer);
 
 		mShadowMapParam.set(params.shadowMap);
 		mShadowSamplerParam.set(mSamplerState);
@@ -756,13 +756,13 @@ namespace bs { namespace ct
 		return shadowMapTfrm * mixedToShadow;
 	}
 
-	void ShadowRendering::renderShadowOcclusion(const RendererScene& scene, UINT32 shadowQuality, 
-		const RendererLight& rendererLight, UINT32 viewIdx)
+	void ShadowRendering::renderShadowOcclusion(const SceneInfo& sceneInfo, UINT32 shadowQuality, 
+		const RendererLight& rendererLight, UINT32 viewIdx, GBufferInput gbuffer) const
 	{
 		const Light* light = rendererLight.internal;
 		UINT32 lightIdx = light->getRendererId();
 
-		RendererView* view = scene.getSceneInfo().views[viewIdx];
+		RendererView* view = sceneInfo.views[viewIdx];
 		auto viewProps = view->getProperties();
 
 		const Matrix4& viewP = viewProps.projTransform;
@@ -812,7 +812,7 @@ namespace bs { namespace ct
 				SPtr<Texture> shadowMap = mShadowCubemaps[shadowInfo.textureIdx].getTexture();
 				SPtr<RenderTargets> renderTargets = view->getRenderTargets();
 
-				ShadowProjectParams shadowParams(*light, shadowMap, 0, shadowOmniParamBuffer, perViewBuffer, *renderTargets);
+				ShadowProjectParams shadowParams(*light, shadowMap, 0, shadowOmniParamBuffer, perViewBuffer, gbuffer);
 				mProjectOmniMaterials.bind(effectiveShadowQuality, viewerInsideVolume, viewProps.numSamples > 1, shadowParams);
 
 				gRendererUtility().draw(gRendererUtility().getRadialLightStencil());
@@ -840,7 +840,7 @@ namespace bs { namespace ct
 			else // Directional
 			{
 				UINT32 mapIdx = mDirectionalLightShadows[lightIdx];
-				ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
+				const ShadowCascadedMap& cascadedMap = mCascadedShadowMaps[mapIdx];
 
 				// Render cascades in far to near order.
 				// Note: If rendering other non-cascade maps they should be rendered after cascades.
@@ -931,7 +931,7 @@ namespace bs { namespace ct
 
 				SPtr<RenderTargets> renderTargets = view->getRenderTargets();
 				ShadowProjectParams shadowParams(*light, shadowMap, shadowMapFace, shadowParamBuffer, perViewBuffer, 
-					*renderTargets);
+					gbuffer);
 				mProjectMaterials.bind(effectiveShadowQuality, isCSM, viewProps.numSamples > 1, shadowParams);
 
 				if (!isCSM)
@@ -1414,7 +1414,7 @@ namespace bs { namespace ct
 		size = std::max(effectiveMapSize - 2 * border, 1u);
 	}
 
-	void ShadowRendering::drawNearFarPlanes(float near, float far, bool drawNear)
+	void ShadowRendering::drawNearFarPlanes(float near, float far, bool drawNear) const
 	{
 		RenderAPI& rapi = RenderAPI::instance();
 		const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
@@ -1448,7 +1448,7 @@ namespace bs { namespace ct
 		rapi.drawIndexed(0, drawNear ? 12 : 6, 0, drawNear ? 8 : 4);
 	}
 
-	void ShadowRendering::drawFrustum(const std::array<Vector3, 8>& corners)
+	void ShadowRendering::drawFrustum(const std::array<Vector3, 8>& corners) const
 	{
 		RenderAPI& rapi = RenderAPI::instance();
 

+ 14 - 13
Source/RenderBeast/Source/BsStandardDeferredLighting.cpp

@@ -28,12 +28,13 @@ namespace bs { namespace ct {
 	}
 
 	template<bool MSAA>
-	void DirectionalLightMat<MSAA>::bind(const RenderTargets& renderTargets, const SPtr<GpuParamBlockBuffer>& perCamera)
+	void DirectionalLightMat<MSAA>::bind(const GBufferInput& gBufferInput, const SPtr<Texture>& lightOcclusion, 
+		const SPtr<GpuParamBlockBuffer>& perCamera)
 	{
 		RendererUtility::instance().setPass(mMaterial, 0);
 
-		mGBufferParams.bind(renderTargets);
-		mLightOcclusionTexParam.set(renderTargets.get(RTT_LightOcclusion));
+		mGBufferParams.bind(gBufferInput);
+		mLightOcclusionTexParam.set(lightOcclusion);
 		mParamsSet->setParamBlockBuffer("PerCamera", perCamera, true);
 	}
 
@@ -69,13 +70,13 @@ namespace bs { namespace ct {
 	}
 
 	template<bool MSAA, bool InsideGeometry>
-	void PointLightMat<MSAA, InsideGeometry>::bind(const RenderTargets& renderTargets, 
+	void PointLightMat<MSAA, InsideGeometry>::bind(const GBufferInput& gBufferInput, const SPtr<Texture>& lightOcclusion, 
 		const SPtr<GpuParamBlockBuffer>& perCamera)
 	{
 		RendererUtility::instance().setPass(mMaterial, 0);
 
-		mGBufferParams.bind(renderTargets);
-		mLightOcclusionTexParam.set(renderTargets.get(RTT_LightOcclusion));
+		mGBufferParams.bind(gBufferInput);
+		mLightOcclusionTexParam.set(lightOcclusion);
 		mParamsSet->setParamBlockBuffer("PerCamera", perCamera, true);
 	}
 
@@ -98,7 +99,7 @@ namespace bs { namespace ct {
 	}
 
 	void StandardDeferred::renderLight(LightType lightType, const RendererLight& light, const RendererView& view, 
-		const RenderTargets& renderTargets)
+		const GBufferInput& gBufferInput, const SPtr<Texture>& lightOcclusion)
 	{
 		const auto& viewProps = view.getProperties();
 
@@ -111,12 +112,12 @@ namespace bs { namespace ct {
 		{
 			if(isMSAA)
 			{
-				mDirLightMat_T.bind(renderTargets, perViewBuffer);
+				mDirLightMat_T.bind(gBufferInput, lightOcclusion, perViewBuffer);
 				mDirLightMat_T.setPerLightParams(mPerLightBuffer);
 			}
 			else
 			{
-				mDirLightMat_F.bind(renderTargets, perViewBuffer);
+				mDirLightMat_F.bind(gBufferInput, lightOcclusion, perViewBuffer);
 				mDirLightMat_F.setPerLightParams(mPerLightBuffer);
 			}
 
@@ -137,12 +138,12 @@ namespace bs { namespace ct {
 			{
 				if(isInside)
 				{
-					mPointLightMat_TT.bind(renderTargets, perViewBuffer);
+					mPointLightMat_TT.bind(gBufferInput, lightOcclusion, perViewBuffer);
 					mPointLightMat_TT.setPerLightParams(mPerLightBuffer);
 				}
 				else
 				{
-					mPointLightMat_TF.bind(renderTargets, perViewBuffer);
+					mPointLightMat_TF.bind(gBufferInput, lightOcclusion, perViewBuffer);
 					mPointLightMat_TF.setPerLightParams(mPerLightBuffer);
 				}
 			}
@@ -150,12 +151,12 @@ namespace bs { namespace ct {
 			{
 				if(isInside)
 				{
-					mPointLightMat_FT.bind(renderTargets, perViewBuffer);
+					mPointLightMat_FT.bind(gBufferInput, lightOcclusion, perViewBuffer);
 					mPointLightMat_FT.setPerLightParams(mPerLightBuffer);
 				}
 				else
 				{
-					mPointLightMat_FF.bind(renderTargets, perViewBuffer);
+					mPointLightMat_FF.bind(gBufferInput, lightOcclusion, perViewBuffer);
 					mPointLightMat_TF.setPerLightParams(mPerLightBuffer);
 				}
 			}