Browse Source

Renderer: Remove ghosting from volumetric fog

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
c185a0bbff

+ 1 - 1
shaders/Irradiance.frag.glsl

@@ -55,5 +55,5 @@ void main()
 		}
 		}
 	}
 	}
 
 
-	out_color = outCol / weight * (2 * PI);
+	out_color = outCol / weight;
 }
 }

+ 18 - 12
shaders/Volumetric.frag.glsl

@@ -7,7 +7,7 @@
 #include "shaders/Functions.glsl"
 #include "shaders/Functions.glsl"
 #include "shaders/Clusterer.glsl"
 #include "shaders/Clusterer.glsl"
 
 
-#define LIGHT_TEX_BINDING 2
+#define LIGHT_TEX_BINDING 3
 #define LIGHT_UBO_BINDING 0
 #define LIGHT_UBO_BINDING 0
 #define LIGHT_SS_BINDING 0
 #define LIGHT_SS_BINDING 0
 #define LIGHT_SET 0
 #define LIGHT_SET 0
@@ -17,20 +17,21 @@ layout(location = 0) in vec2 in_uv;
 
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msDepthRt;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msDepthRt;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2DArray u_noiseTex;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2DArray u_noiseTex;
+layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_historyRt;
 
 
-layout(std140, ANKI_UBO_BINDING(0, 3)) uniform ubo0_
+layout(std140, ANKI_UBO_BINDING(0, 3), row_major) uniform ubo0_
 {
 {
 	vec4 u_linearizeNoiseTexOffsetLayer;
 	vec4 u_linearizeNoiseTexOffsetLayer;
-	vec4 u_fogParticleColorBlendFactor;
+	vec4 u_fogParticleColorPad1;
+	mat4 u_prevViewProjMatMulInvViewProjMat;
 };
 };
 
 
 #define u_linearize u_linearizeNoiseTexOffsetLayer.xy
 #define u_linearize u_linearizeNoiseTexOffsetLayer.xy
 #define u_noiseYOffset u_linearizeNoiseTexOffsetLayer.z
 #define u_noiseYOffset u_linearizeNoiseTexOffsetLayer.z
 #define u_noiseLayer u_linearizeNoiseTexOffsetLayer.w
 #define u_noiseLayer u_linearizeNoiseTexOffsetLayer.w
-#define u_fogParticleColor u_fogParticleColorBlendFactor.rgb
-#define u_blendFactor u_fogParticleColorBlendFactor.w
+#define u_fogParticleColor u_fogParticleColorPad1.rgb
 
 
-layout(location = 0) out vec4 out_color;
+layout(location = 0) out vec3 out_color;
 
 
 #define ENABLE_SHADOWS 1
 #define ENABLE_SHADOWS 1
 const uint MAX_SAMPLES_PER_CLUSTER = 4u;
 const uint MAX_SAMPLES_PER_CLUSTER = 4u;
@@ -91,13 +92,16 @@ vec3 computeLightColor(vec3 fragPos, uint plightCount, uint plightIdx, uint slig
 void main()
 void main()
 {
 {
 	float depth = textureLod(u_msDepthRt, in_uv, 0.0).r;
 	float depth = textureLod(u_msDepthRt, in_uv, 0.0).r;
-	float farZ = u_lightingUniforms.projectionParams.z / (u_lightingUniforms.projectionParams.w + depth);
 
 
-	vec2 ndc = in_uv * 2.0 - 1.0;
+	vec3 ndc = UV_TO_NDC(vec3(in_uv, depth));
+
+	vec4 v4 = u_prevViewProjMatMulInvViewProjMat * vec4(ndc, 1.0);
+	vec2 oldUv = NDC_TO_UV(v4.xy / v4.w);
+	vec3 history = textureLod(u_historyRt, oldUv, 0.0).rgb;
 
 
 	vec3 farPos;
 	vec3 farPos;
-	farPos.xy = ndc * u_lightingUniforms.projectionParams.xy * farZ;
-	farPos.z = farZ;
+	farPos.z = u_lightingUniforms.projectionParams.z / (u_lightingUniforms.projectionParams.w + depth);
+	farPos.xy = ndc.xy * u_lightingUniforms.projectionParams.xy * farPos.z;
 	vec3 viewDir = normalize(farPos);
 	vec3 viewDir = normalize(farPos);
 
 
 	uint i = uint(in_uv.x * float(CLUSTER_COUNT.x));
 	uint i = uint(in_uv.x * float(CLUSTER_COUNT.x));
@@ -143,7 +147,7 @@ void main()
 		{
 		{
 			float zMedian = mix(kNear, kFar, factor);
 			float zMedian = mix(kNear, kFar, factor);
 
 
-			if(zMedian < farZ)
+			if(zMedian < farPos.z)
 			{
 			{
 				k = CLUSTER_COUNT.z; // Break the outer loop
 				k = CLUSTER_COUNT.z; // Break the outer loop
 				break;
 				break;
@@ -158,5 +162,7 @@ void main()
 	}
 	}
 
 
 	newCol *= u_fogParticleColor;
 	newCol *= u_fogParticleColor;
-	out_color = vec4(newCol, u_blendFactor);
+
+	history = max(history, newCol);
+	out_color = mix(history, newCol, 1.0 / 16.0);
 }
 }

+ 2 - 2
src/anki/renderer/Fs.cpp

@@ -95,11 +95,11 @@ void Fs::drawVolumetric(RenderingContext& ctx, CommandBufferPtr cmdb)
 	cmdb->informTextureSurfaceCurrentUsage(
 	cmdb->informTextureSurfaceCurrentUsage(
 		m_r->getDepthDownscale().m_qd.m_depthRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
 		m_r->getDepthDownscale().m_qd.m_depthRt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
 	cmdb->informTextureSurfaceCurrentUsage(
 	cmdb->informTextureSurfaceCurrentUsage(
-		m_r->getVolumetric().m_main.m_rt, TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
+		m_r->getVolumetric().m_main.getRt(), TextureSurfaceInfo(0, 0, 0, 0), TextureUsageBit::SAMPLED_FRAGMENT);
 
 
 	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_depthRt, m_r->getNearestSampler());
 	cmdb->bindTextureAndSampler(0, 0, m_r->getDepthDownscale().m_hd.m_depthRt, m_r->getNearestSampler());
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt, m_r->getNearestSampler());
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_qd.m_depthRt, m_r->getNearestSampler());
-	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_main.m_rt);
+	cmdb->bindTexture(0, 2, m_r->getVolumetric().m_main.getRt());
 	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture());
 	cmdb->bindTexture(0, 3, m_vol.m_noiseTex->getGrTexture());
 
 
 	m_r->drawQuad(cmdb);
 	m_r->drawQuad(cmdb);

+ 64 - 64
src/anki/renderer/Volumetric.cpp

@@ -19,23 +19,26 @@ Error VolumetricMain::init(const ConfigSet& config)
 	// Misc
 	// Misc
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
 	ANKI_CHECK(getResourceManager().loadResource("engine_data/BlueNoiseLdrRgb64x64.ankitex", m_noiseTex));
 
 
-	// RT
-	TextureInitInfo rtInit = m_r->create2DRenderTargetInitInfo(m_vol->m_width,
-		m_vol->m_height,
-		IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
-		SamplingFilter::LINEAR,
-		1,
-		"volmain");
-	rtInit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
-	m_rt = m_r->createAndClearRenderTarget(rtInit);
-
-	// FB
-	FramebufferInitInfo fbInit("volmain");
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::LOAD;
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+	for(U i = 0; i < 2; ++i)
+	{
+		// RT
+		TextureInitInfo rtInit = m_r->create2DRenderTargetInitInfo(m_vol->m_width,
+			m_vol->m_height,
+			IS_COLOR_ATTACHMENT_PIXEL_FORMAT,
+			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			SamplingFilter::LINEAR,
+			1,
+			"volmain");
+		rtInit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
+		m_rt[i] = m_r->createAndClearRenderTarget(rtInit);
+
+		// FB
+		FramebufferInitInfo fbInit("volmain");
+		fbInit.m_colorAttachmentCount = 1;
+		fbInit.m_colorAttachments[0].m_texture = m_rt[i];
+		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+		m_fb[i] = getGrManager().newInstance<Framebuffer>(fbInit);
+	}
 
 
 	// Shaders
 	// Shaders
 	ANKI_CHECK(m_r->createShaderf("shaders/Volumetric.frag.glsl",
 	ANKI_CHECK(m_r->createShaderf("shaders/Volumetric.frag.glsl",
@@ -55,11 +58,16 @@ Error VolumetricMain::init(const ConfigSet& config)
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
+TexturePtr VolumetricMain::getRt() const
+{
+	return m_rt[m_r->getFrameCount() & 1];
+}
+
 void VolumetricMain::setPreRunBarriers(RenderingContext& ctx)
 void VolumetricMain::setPreRunBarriers(RenderingContext& ctx)
 {
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::SAMPLED_FRAGMENT,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt[m_r->getFrameCount() & 1],
+		TextureUsageBit::NONE,
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 }
 
 
@@ -71,62 +79,51 @@ void VolumetricMain::run(RenderingContext& ctx)
 	// Main pass
 	// Main pass
 	//
 	//
 	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
 	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
-	cmdb->setBlendFactors(0, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA);
 
 
 	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
 	cmdb->bindTexture(0, 0, m_r->getDepthDownscale().m_qd.m_depthRt);
 	cmdb->bindTexture(0, 1, m_noiseTex->getGrTexture());
 	cmdb->bindTexture(0, 1, m_noiseTex->getGrTexture());
-	cmdb->bindTexture(0, 2, m_r->getSm().m_spotTexArray);
-	cmdb->bindTexture(0, 3, m_r->getSm().m_omniTexArray);
+	TexturePtr& history = m_rt[(m_r->getFrameCount() + 1) & 1];
+	cmdb->informTextureCurrentUsage(history, TextureUsageBit::SAMPLED_FRAGMENT);
+	cmdb->bindTexture(0, 2, history);
+	cmdb->bindTexture(0, 3, m_r->getSm().m_spotTexArray);
+	cmdb->bindTexture(0, 4, m_r->getSm().m_omniTexArray);
 
 
 	bindUniforms(cmdb, 0, 0, ctx.m_is.m_commonToken);
 	bindUniforms(cmdb, 0, 0, ctx.m_is.m_commonToken);
 	bindUniforms(cmdb, 0, 1, ctx.m_is.m_pointLightsToken);
 	bindUniforms(cmdb, 0, 1, ctx.m_is.m_pointLightsToken);
 	bindUniforms(cmdb, 0, 2, ctx.m_is.m_spotLightsToken);
 	bindUniforms(cmdb, 0, 2, ctx.m_is.m_spotLightsToken);
 
 
-	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4) * 2, cmdb, 0, 3);
-	computeLinearizeDepthOptimal(ctx.m_near, ctx.m_far, uniforms[0].x(), uniforms[0].y());
-
-	F32 texelOffset = 1.0 / m_noiseTex->getWidth();
-	uniforms[0].z() = m_r->getFrameCount() * texelOffset;
-	uniforms[0].w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
-
-	// Compute the blend factor. If the camera rotated or moved alot don't blend with previous frames
-	F32 dotZ = ctx.m_camTrfMat.getZAxis().xyz().dot(ctx.m_prevCamTransform.getZAxis().xyz());
-	F32 dotY = ctx.m_camTrfMat.getYAxis().xyz().dot(ctx.m_prevCamTransform.getYAxis().xyz());
-
-	const F32 ANG_TOLERANCE = cos(toRad(1.0f / 8.0f));
-	const F32 DIST_TOLERANCE = 0.1f;
-	F32 blendFactor;
-	const F32 dist = (ctx.m_camTrfMat.getTranslationPart().xyz0() - ctx.m_prevCamTransform.getTranslationPart().xyz0())
-						 .getLengthSquared();
-	if(clamp(dotZ, 0.0f, 1.0f) > ANG_TOLERANCE && clamp(dotY, 0.0f, 1.0f) > ANG_TOLERANCE
-		&& dist < DIST_TOLERANCE * DIST_TOLERANCE)
+	struct Unis
 	{
 	{
-		blendFactor = 1.0 / 4.0;
-	}
-	else
-	{
-		blendFactor = 1.0 / 2.0;
-	}
-
-	uniforms[1] = Vec4(m_fogParticleColor, blendFactor);
+		Vec4 m_linearizeNoiseTexOffsetLayer;
+		Vec4 m_fogParticleColorPad1;
+		Mat4 m_prevViewProjMatMulInvViewProjMat;
+	};
+
+	Unis* uniforms = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 3);
+	computeLinearizeDepthOptimal(ctx.m_near,
+		ctx.m_far,
+		uniforms->m_linearizeNoiseTexOffsetLayer.x(),
+		uniforms->m_linearizeNoiseTexOffsetLayer.y());
+	F32 texelOffset = 1.0 / m_noiseTex->getWidth();
+	uniforms->m_linearizeNoiseTexOffsetLayer.z() = m_r->getFrameCount() * texelOffset;
+	uniforms->m_linearizeNoiseTexOffsetLayer.w() = m_r->getFrameCount() & (m_noiseTex->getLayerCount() - 1);
+	uniforms->m_fogParticleColorPad1 = Vec4(m_fogParticleColor, 0.0);
+	uniforms->m_prevViewProjMatMulInvViewProjMat = ctx.m_prevViewProjMat * ctx.m_viewProjMat.getInverse();
 
 
 	bindStorage(cmdb, 0, 0, ctx.m_is.m_clustersToken);
 	bindStorage(cmdb, 0, 0, ctx.m_is.m_clustersToken);
 	bindStorage(cmdb, 0, 1, ctx.m_is.m_lightIndicesToken);
 	bindStorage(cmdb, 0, 1, ctx.m_is.m_lightIndicesToken);
 
 
 	cmdb->bindShaderProgram(m_prog);
 	cmdb->bindShaderProgram(m_prog);
 
 
-	cmdb->beginRenderPass(m_fb);
+	cmdb->beginRenderPass(m_fb[m_r->getFrameCount() & 1]);
 	m_r->drawQuad(cmdb);
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
 	cmdb->endRenderPass();
-
-	// Restore state
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
 }
 }
 
 
 void VolumetricMain::setPostRunBarriers(RenderingContext& ctx)
 void VolumetricMain::setPostRunBarriers(RenderingContext& ctx)
 {
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt,
-		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_rt[m_r->getFrameCount() & 1],
+		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 		TextureSurfaceInfo(0, 0, 0, 0));
 }
 }
@@ -173,7 +170,7 @@ void VolumetricHBlur::run(RenderingContext& ctx)
 {
 {
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 
-	cmdb->bindTexture(0, 0, m_vol->m_main.m_rt);
+	cmdb->bindTexture(0, 0, m_vol->m_main.m_rt[m_r->getFrameCount() & 1]);
 	cmdb->bindShaderProgram(m_prog);
 	cmdb->bindShaderProgram(m_prog);
 	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
 	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
 
 
@@ -193,11 +190,14 @@ void VolumetricHBlur::setPostRunBarriers(RenderingContext& ctx)
 Error VolumetricVBlur::init(const ConfigSet& config)
 Error VolumetricVBlur::init(const ConfigSet& config)
 {
 {
 	// Create FBs
 	// Create FBs
-	FramebufferInitInfo fbInit;
-	fbInit.m_colorAttachmentCount = 1;
-	fbInit.m_colorAttachments[0].m_texture = m_vol->m_main.m_rt;
-	fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fb = getGrManager().newInstance<Framebuffer>(fbInit);
+	for(U i = 0; i < 2; ++i)
+	{
+		FramebufferInitInfo fbInit;
+		fbInit.m_colorAttachmentCount = 1;
+		fbInit.m_colorAttachments[0].m_texture = m_vol->m_main.m_rt[i];
+		fbInit.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+		m_fb[i] = getGrManager().newInstance<Framebuffer>(fbInit);
+	}
 
 
 	ANKI_CHECK(m_r->createShaderf("shaders/LumaAwareBlurGeneric.frag.glsl",
 	ANKI_CHECK(m_r->createShaderf("shaders/LumaAwareBlurGeneric.frag.glsl",
 		m_frag,
 		m_frag,
@@ -215,7 +215,7 @@ Error VolumetricVBlur::init(const ConfigSet& config)
 
 
 void VolumetricVBlur::setPreRunBarriers(RenderingContext& ctx)
 void VolumetricVBlur::setPreRunBarriers(RenderingContext& ctx)
 {
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt[m_r->getFrameCount() & 1],
 		TextureUsageBit::NONE,
 		TextureUsageBit::NONE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureSurfaceInfo(0, 0, 0, 0));
 		TextureSurfaceInfo(0, 0, 0, 0));
@@ -229,14 +229,14 @@ void VolumetricVBlur::run(RenderingContext& ctx)
 	cmdb->bindShaderProgram(m_prog);
 	cmdb->bindShaderProgram(m_prog);
 	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
 	cmdb->setViewport(0, 0, m_vol->m_width, m_vol->m_height);
 
 
-	cmdb->beginRenderPass(m_fb);
+	cmdb->beginRenderPass(m_fb[m_r->getFrameCount() & 1]);
 	m_r->drawQuad(cmdb);
 	m_r->drawQuad(cmdb);
 	cmdb->endRenderPass();
 	cmdb->endRenderPass();
 }
 }
 
 
 void VolumetricVBlur::setPostRunBarriers(RenderingContext& ctx)
 void VolumetricVBlur::setPostRunBarriers(RenderingContext& ctx)
 {
 {
-	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt,
+	ctx.m_commandBuffer->setTextureSurfaceBarrier(m_vol->m_main.m_rt[m_r->getFrameCount() & 1],
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureUsageBit::SAMPLED_FRAGMENT,
 		TextureSurfaceInfo(0, 0, 0, 0));
 		TextureSurfaceInfo(0, 0, 0, 0));

+ 5 - 4
src/anki/renderer/Volumetric.h

@@ -24,8 +24,6 @@ class VolumetricMain : public RenderingPass
 	friend class VolumetricVBlur;
 	friend class VolumetricVBlur;
 
 
 anki_internal:
 anki_internal:
-	TexturePtr m_rt; ///< vRT
-
 	VolumetricMain(Renderer* r, Volumetric* vol)
 	VolumetricMain(Renderer* r, Volumetric* vol)
 		: RenderingPass(r)
 		: RenderingPass(r)
 		, m_vol(vol)
 		, m_vol(vol)
@@ -42,6 +40,8 @@ anki_internal:
 	void run(RenderingContext& ctx);
 	void run(RenderingContext& ctx);
 	void setPostRunBarriers(RenderingContext& ctx);
 	void setPostRunBarriers(RenderingContext& ctx);
 
 
+	TexturePtr getRt() const;
+
 private:
 private:
 	Volumetric* m_vol;
 	Volumetric* m_vol;
 
 
@@ -50,7 +50,8 @@ private:
 
 
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_frag;
 	ShaderProgramPtr m_prog;
 	ShaderProgramPtr m_prog;
-	FramebufferPtr m_fb;
+	Array<TexturePtr, 2> m_rt; ///< vRT
+	Array<FramebufferPtr, 2> m_fb;
 
 
 	TextureResourcePtr m_noiseTex;
 	TextureResourcePtr m_noiseTex;
 };
 };
@@ -114,7 +115,7 @@ private:
 
 
 	ShaderResourcePtr m_frag;
 	ShaderResourcePtr m_frag;
 	ShaderProgramPtr m_prog;
 	ShaderProgramPtr m_prog;
-	FramebufferPtr m_fb;
+	Array<FramebufferPtr, 2> m_fb;
 };
 };
 
 
 /// Volumetric effects.
 /// Volumetric effects.