Răsfoiți Sursa

[FEATURE] Some work on the SS reflections

Panagiotis Christopoulos Charitos 8 ani în urmă
părinte
comite
42564c57f2

+ 148 - 0
programs/Reflections.ankiprog

@@ -0,0 +1,148 @@
+<!-- 
+Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+All rights reserved.
+Code licensed under the BSD License.
+http://www.anki3d.org/LICENSE
+-->
+<shaderProgram>
+	<shaders>
+		<shader type="comp">
+			<inputs>
+				<input name="INPUT_TEX_SIZE" type="uvec2" const="1"/>
+				<input name="WORKGROUP_SIZE" type="uvec2" const="1"/>
+			</inputs>
+
+			<source><![CDATA[
+#include "shaders/Functions.glsl"
+#include "shaders/Pack.glsl"
+
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = 1) in;
+
+layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_gbufferRt1;
+layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_gbufferRt2;
+layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_depthRt;
+layout(ANKI_TEX_BINDING(0, 4)) uniform sampler2D u_lightBufferRt;
+
+layout(ANKI_IMAGE_BINDING(0, 0)) writeonly uniform image2D u_out;
+
+layout(ANKI_UBO_BINDING(0, 0), std140, row_major) uniform u0_
+{
+	mat4 u_prevViewProjMatMulInvViewProjMat;
+};
+
+const float ONE = 0.9;
+
+// Returns the Z of the position in view space
+float readZ(in vec2 uv)
+{
+	float depth = textureLod(u_depthRt, uv, 1.0).r;
+	float z = u_projectionParams.z / (u_projectionParams.w + depth);
+	return z;
+}
+
+vec2 projectXy(in vec3 p)
+{
+	vec4 a = u_projectionMat * vec4(p, 1.0);
+	return a.xy / a.w;
+}
+
+vec3 doSslr(vec3 r, vec3 posVSpace, vec2 uv, out float contribution)
+{
+	vec3 color = vec3(0.0);
+	vec3 p0 = posVSpace;
+	contribution = 1.0;
+
+	// Let p1 be the intersection of p0+r to the near plane, then
+	// p1 = p0 + t*r or
+	// p1.x = p0.x + t*r.x (1)
+	// p1.y = p0.y + t*r.y (2) and
+	// p1.z = p0.z + t*r.z (3)
+	// p1.z is known to be something ~0.0 so if we solve (3) t becomes:
+	float t = -p0.z / (r.z + 0.0000001);
+	vec3 p1 = p0 + r * t;
+
+	vec2 pp0 = uv * 2.0 - 1.0;
+	vec2 pp1 = projectXy(p1);
+
+	// Calculate the ray from p0 to p1 in 2D space and get the number of steps
+	vec2 dir = pp1 - pp0;
+	vec2 path = dir * 0.5; // (pp1/2+1/2)-(pp0.xy/2+1/2)
+	path *= vec2(float(WIDTH), float(HEIGHT));
+	path = abs(path);
+	float steps = max(path.x, path.y);
+
+	// Calculate the step increase
+	float len = length(dir);
+	float stepInc =  len / steps;
+	dir /= len; // Normalize dir at last
+
+	steps = min(steps, 300.0);
+
+	for(float i = 0.0; i < steps; i += 1.0)
+	{
+		vec2 ndc = pp0 + dir * (i * stepInc);
+
+		// Check if it's out of the view
+		vec2 comp = abs(ndc);
+		if(comp.x > ONE || comp.y > ONE)
+		{
+			//color = vec3(1, 0.0, 1);
+			return color;
+		}
+
+		// 'a' is ray that passes through the eye and into ndc
+		vec3 a;
+		a.z = -1.0;
+		a.xy = ndc * u_projectionParams.xy * a.z; // Unproject
+		a = normalize(a);
+
+		// Compute the intersection between 'a' (before normalization) and r
+		// 'k' is the value to multiply to 'a' to get the intersection
+		// c0 = cross(a, r);
+		// c1 = cross(p0, r);
+		// k = c1.x / c0.x; and the optimized:
+		vec2 tmpv2 = a.yz * r.zy;
+		float c0x = tmpv2.x - tmpv2.y;
+		tmpv2 = p0.yz * r.zy;
+		float c1x = tmpv2.x - tmpv2.y;
+		float k = c1x / c0x;
+
+		float intersectionZ = a.z * k; // intersectionXYZ = a * k;
+
+		vec2 texCoord = ndc * 0.5 + 0.5;
+		float depth = readZ(u_depthRt, texCoord);
+
+		float diffDepth = depth - intersectionZ;
+
+		if(diffDepth > 0.0)
+		{
+			if(diffDepth > 0.7)
+			{
+				return;
+			}
+
+			float factor = sin(length(ndc) * PI);
+			factor *= 1.0 - length(pp0);
+			//factor *= specColor;
+
+			color = textureLod(u_isRt, texCoord, 0.0).rgb * factor;
+
+			//color = vec3(1.0, 0.0, 1.0);
+			//color = vec3(1.0 - abs(pp0.xy), 0.0);
+			return color;
+		}
+	}
+
+	return color;
+}
+
+void main()
+{
+	vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(INPUT_TEX_SIZE);
+
+	doSslr();
+}
+			]]></source>
+		</shader>
+	</shaders>
+</shaderProgram>

+ 9 - 3
shaders/Pack.glsl

@@ -159,6 +159,14 @@ void readNormalFromGBuffer(in sampler2D rt2, in vec2 uv, out vec3 normal)
 	normal = signedOctDecode(texture(rt2, uv).rga);
 }
 
+// Read the roughness from the G-buffer
+void readRoughnessSpecularFromGBuffer(in sampler2D rt1, in vec2 uv, out float roughness, out vec3 specular)
+{
+	vec4 comp = textureLod(rt1, uv, 0.0);
+	specular = comp.xyz;
+	roughness = comp.w;
+}
+
 // Read from the G buffer
 void readGBuffer(in sampler2D rt0, in sampler2D rt1, in sampler2D rt2, in vec2 uv, in float lod, out GbufferInfo g)
 {
@@ -168,9 +176,7 @@ void readGBuffer(in sampler2D rt0, in sampler2D rt1, in sampler2D rt2, in vec2 u
 	g.subsurface = comp2.x;
 	g.metallic = comp2.y;
 
-	comp = textureLod(rt1, uv, lod);
-	g.specular = comp.xyz;
-	g.roughness = comp.w;
+	readRoughnessSpecularFromGBuffer(rt1, uv, g.roughness, g.specular);
 
 	comp = textureLod(rt2, uv, lod);
 	g.normal = signedOctDecode(comp.xyw);

+ 17 - 3
src/anki/gr/RenderGraph.h

@@ -134,8 +134,11 @@ public:
 		TextureUsageBit& usage) const;
 
 	/// Convenience method.
-	void bindTextureAndSampler(
-		U32 set, U32 binding, RenderTargetHandle handle, const TextureSubresourceInfo& subresource, SamplerPtr sampler)
+	void bindTextureAndSampler(U32 set,
+		U32 binding,
+		RenderTargetHandle handle,
+		const TextureSubresourceInfo& subresource,
+		const SamplerPtr& sampler)
 	{
 		TexturePtr tex;
 		TextureUsageBit usage;
@@ -146,7 +149,7 @@ public:
 	}
 
 	/// Convenience method to bind the whole texture as color.
-	void bindColorTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, SamplerPtr sampler)
+	void bindColorTextureAndSampler(U32 set, U32 binding, RenderTargetHandle handle, const SamplerPtr& sampler)
 	{
 		TexturePtr tex = getTexture(handle);
 		TextureViewInitInfo viewInit(tex); // Use the whole texture
@@ -156,6 +159,17 @@ public:
 		m_commandBuffer->bindTextureAndSampler(set, binding, view, sampler, usage);
 	}
 
+	/// Convenience method.
+	void bindImage(U32 set, U32 binding, RenderTargetHandle handle, const TextureSubresourceInfo& subresource)
+	{
+		TexturePtr tex;
+		TextureUsageBit usage;
+		getRenderTargetState(handle, subresource, tex, usage);
+		TextureViewInitInfo viewInit(tex, subresource, "TmpRenderGraph");
+		TextureViewPtr view = m_commandBuffer->getManager().newTextureView(viewInit);
+		m_commandBuffer->bindImage(set, binding, view);
+	}
+
 	/// Convenience method.
 	void bindStorageBuffer(U32 set, U32 binding, RenderPassBufferHandle handle)
 	{

+ 10 - 4
src/anki/renderer/Bloom.cpp

@@ -113,12 +113,14 @@ void Bloom::populateRenderGraph(RenderingContext& ctx)
 		m_runCtx.m_exposureRt = rgraph.newRenderTarget(m_exposure.m_rtDescr);
 
 		// Set the render pass
-		GraphicsRenderPassDescription& rpass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Bloom Main");
+		GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("Bloom Main");
 		rpass.setWork(runExposureCallback, this, 0);
 		rpass.setFramebufferInfo(m_exposure.m_fbDescr, {{m_runCtx.m_exposureRt}}, {});
 
+		TextureSubresourceInfo inputTexSubresource;
+		inputTexSubresource.m_firstMipmap = m_r->getDownscaleBlur().getMipmapCount() - 1;
+		rpass.newConsumer({m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_FRAGMENT, inputTexSubresource});
 		rpass.newConsumer({m_runCtx.m_exposureRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		rpass.newConsumer({m_r->getDownscaleBlur().getPassRt(MAX_U), TextureUsageBit::SAMPLED_FRAGMENT});
 		rpass.newConsumer({m_r->getTonemapping().getAverageLuminanceBuffer(), BufferUsageBit::STORAGE_FRAGMENT_READ});
 		rpass.newProducer({m_runCtx.m_exposureRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 	}
@@ -129,7 +131,7 @@ void Bloom::populateRenderGraph(RenderingContext& ctx)
 		m_runCtx.m_upscaleRt = rgraph.newRenderTarget(m_upscale.m_rtDescr);
 
 		// Set the render pass
-		GraphicsRenderPassDescription& rpass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Bloom Upscale");
+		GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("Bloom Upscale");
 		rpass.setWork(runUpscaleAndSslfCallback, this, 0);
 		rpass.setFramebufferInfo(m_upscale.m_fbDescr, {{m_runCtx.m_upscaleRt}}, {});
 
@@ -145,7 +147,11 @@ void Bloom::runExposure(RenderPassWorkContext& rgraphCtx)
 
 	cmdb->setViewport(0, 0, m_exposure.m_width, m_exposure.m_height);
 	cmdb->bindShaderProgram(m_exposure.m_grProg);
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDownscaleBlur().getPassRt(MAX_U), m_r->getLinearSampler());
+
+	TextureSubresourceInfo inputTexSubresource;
+	inputTexSubresource.m_firstMipmap = m_r->getDownscaleBlur().getMipmapCount() - 1;
+	rgraphCtx.bindTextureAndSampler(
+		0, 0, m_r->getDownscaleBlur().getRt(), inputTexSubresource, m_r->getLinearSampler());
 
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	*uniforms = Vec4(m_exposure.m_threshold, m_exposure.m_scale, 0.0, 0.0);

+ 64 - 78
src/anki/renderer/DownscaleBlur.cpp

@@ -12,35 +12,11 @@ namespace anki
 
 DownscaleBlur::~DownscaleBlur()
 {
-	m_passes.destroy(getAllocator());
-	m_runCtx.m_rts.destroy(getAllocator());
-}
-
-Error DownscaleBlur::initSubpass(U idx, const UVec2& inputTexSize)
-{
-	Subpass& pass = m_passes[idx];
-
-	pass.m_width = inputTexSize.x() / 2;
-	pass.m_height = inputTexSize.y() / 2;
-
-	// RT
-	StringAuto name(getAllocator());
-	name.sprintf("DownBlur #%u", idx);
-	pass.m_rtDescr = m_r->create2DRenderTargetDescription(pass.m_width,
-		pass.m_height,
-		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
-		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
-			| TextureUsageBit::SAMPLED_COMPUTE,
-		name.toCString());
-	pass.m_rtDescr.bake();
-
-	return Error::NONE;
+	m_fbDescrs.destroy(getAllocator());
 }
 
 Error DownscaleBlur::init(const ConfigSet& cfg)
 {
-	ANKI_R_LOGI("Initializing dowscale blur");
-
 	Error err = initInternal(cfg);
 	if(err)
 	{
@@ -52,22 +28,28 @@ Error DownscaleBlur::init(const ConfigSet& cfg)
 
 Error DownscaleBlur::initInternal(const ConfigSet&)
 {
-	const U passCount = computeMaxMipmapCount2d(m_r->getWidth(), m_r->getHeight(), DOWNSCALE_BLUR_DOWN_TO) - 1;
-	m_passes.create(getAllocator(), passCount);
-
-	UVec2 size(m_r->getWidth(), m_r->getHeight());
-	for(U i = 0; i < m_passes.getSize(); ++i)
-	{
-		ANKI_CHECK(initSubpass(i, size));
-		size /= 2;
-	}
+	m_passCount = computeMaxMipmapCount2d(m_r->getWidth(), m_r->getHeight(), DOWNSCALE_BLUR_DOWN_TO) - 1;
+	ANKI_R_LOGI("Initializing dowscale blur (passCount: %u)", U(m_passCount));
 
-	m_runCtx.m_rts.create(getAllocator(), passCount);
+	// Create the miped texture
+	TextureInitInfo texinit = m_r->create2DRenderTargetDescription(m_r->getWidth() / 2,
+		m_r->getHeight() / 2,
+		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
+			| TextureUsageBit::SAMPLED_COMPUTE,
+		"DownscaleBlur");
+	texinit.m_mipmapCount = m_passCount;
+	m_rtTex = m_r->createAndClearRenderTarget(texinit);
 
 	// FB descr
-	m_fbDescr.m_colorAttachmentCount = 1;
-	m_fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
-	m_fbDescr.bake();
+	m_fbDescrs.create(getAllocator(), m_passCount);
+	for(U pass = 0; pass < m_passCount; ++pass)
+	{
+		m_fbDescrs[pass].m_colorAttachmentCount = 1;
+		m_fbDescrs[pass].m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE;
+		m_fbDescrs[pass].m_colorAttachments[0].m_surface.m_level = pass;
+		m_fbDescrs[pass].bake();
+	}
 
 	// Shader programs
 	ANKI_CHECK(getResourceManager().loadResource("programs/DownscaleBlur.ankiprog", m_prog));
@@ -78,42 +60,16 @@ Error DownscaleBlur::initInternal(const ConfigSet&)
 	return Error::NONE;
 }
 
-void DownscaleBlur::run(RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	const U passIdx = m_runCtx.m_crntPassIdx++;
-
-	if(passIdx > 0)
-	{
-		// Bind the previous pass' Rt
-
-		rgraphCtx.bindColorTextureAndSampler(0, 0, m_runCtx.m_rts[passIdx - 1], m_r->getLinearSampler());
-	}
-	else
-	{
-		rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getTemporalAA().getRt(), m_r->getLinearSampler());
-	}
-
-	const Subpass& pass = m_passes[passIdx];
-	cmdb->setViewport(0, 0, pass.m_width, pass.m_height);
-	cmdb->bindShaderProgram(m_grProg);
-	drawQuad(cmdb);
-}
-
 void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 {
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	m_runCtx.m_crntPassIdx = 0;
 
-	// Create RTs
-	for(U i = 0; i < m_passes.getSize(); ++i)
-	{
-		m_runCtx.m_rts[i] = rgraph.newRenderTarget(m_passes[i].m_rtDescr);
-	}
+	// Create RT
+	m_runCtx.m_rt = rgraph.importRenderTarget("Down/Blur", m_rtTex, TextureUsageBit::NONE);
 
 	// Create passes
-	Array<CString, 8> passNames = {{"DownBlur #0",
+	static const Array<CString, 8> passNames = {{"DownBlur #0",
 		"Down/Blur #1",
 		"Down/Blur #2",
 		"Down/Blur #3",
@@ -121,31 +77,61 @@ void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 		"Down/Blur #5",
 		"Down/Blur #6",
 		"Down/Blur #7"}};
-	for(U i = 0; i < m_passes.getSize(); ++i)
+	for(U i = 0; i < m_passCount; ++i)
 	{
 		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[i]);
-
 		pass.setWork(runCallback, this, 0);
 
-		RenderTargetHandle renderRt, sampleRt;
 		if(i > 0)
 		{
-			renderRt = m_runCtx.m_rts[i];
-			sampleRt = m_runCtx.m_rts[i - 1];
+			TextureSubresourceInfo sampleSubresource;
+			TextureSubresourceInfo renderSubresource;
+
+			sampleSubresource.m_firstMipmap = i - 1;
+			renderSubresource.m_firstMipmap = i;
+
+			pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, renderSubresource});
+			pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::SAMPLED_FRAGMENT, sampleSubresource});
+
+			pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, renderSubresource});
 		}
 		else
 		{
-			renderRt = m_runCtx.m_rts[0];
-			sampleRt = m_r->getTemporalAA().getRt();
+			TextureSubresourceInfo renderSubresource;
+
+			pass.newConsumer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, renderSubresource});
+			pass.newConsumer({m_r->getTemporalAA().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
+
+			pass.newProducer({m_runCtx.m_rt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, renderSubresource});
 		}
 
-		pass.setFramebufferInfo(m_fbDescr, {{renderRt}}, {});
+		pass.setFramebufferInfo(m_fbDescrs[i], {{m_runCtx.m_rt}}, {});
+	}
+}
+
+void DownscaleBlur::run(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	const U passIdx = m_runCtx.m_crntPassIdx++;
+
+	if(passIdx > 0)
+	{
+		// Bind the Rt
 
-		pass.newConsumer({renderRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-		pass.newConsumer({sampleRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		TextureSubresourceInfo sampleSubresource;
+		sampleSubresource.m_firstMipmap = passIdx - 1;
 
-		pass.newProducer({renderRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		rgraphCtx.bindTextureAndSampler(0, 0, m_runCtx.m_rt, sampleSubresource, m_r->getLinearSampler());
+	}
+	else
+	{
+		rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getTemporalAA().getRt(), m_r->getLinearSampler());
 	}
+
+	cmdb->setViewport(0, 0, m_rtTex->getWidth() >> passIdx, m_rtTex->getHeight() >> passIdx);
+	cmdb->bindShaderProgram(m_grProg);
+	drawQuad(cmdb);
 }
 
 } // end namespace anki

+ 13 - 13
src/anki/renderer/DownscaleBlur.h

@@ -31,30 +31,30 @@ anki_internal:
 
 	U getPassWidth(U pass) const
 	{
-		return m_passes[min<U>(pass, m_passes.getSize() - 1)].m_width;
+		return m_rtTex->getWidth() >> min<U32>(pass, m_passCount - 1);
 	}
 
 	U getPassHeight(U pass) const
 	{
-		return m_passes[min<U>(pass, m_passes.getSize() - 1)].m_height;
+		return m_rtTex->getHeight() >> min<U32>(pass, m_passCount - 1);
 	}
 
-	RenderTargetHandle getPassRt(U pass) const
+	U getMipmapCount() const
 	{
-		return m_runCtx.m_rts[min<U>(pass, m_runCtx.m_rts.getSize() - 1)];
+		return m_passCount;
 	}
 
-private:
-	class Subpass
+	RenderTargetHandle getRt() const
 	{
-	public:
-		RenderTargetDescription m_rtDescr;
-		U32 m_width, m_height;
-	};
+		return m_runCtx.m_rt;
+	}
+
+private:
+	U8 m_passCount = 0; ///< It's also the mip count of the m_rtTex.
 
-	DynamicArray<Subpass> m_passes;
+	TexturePtr m_rtTex;
 
-	FramebufferDescription m_fbDescr;
+	DynamicArray<FramebufferDescription> m_fbDescrs;
 
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
@@ -62,7 +62,7 @@ private:
 	class
 	{
 	public:
-		DynamicArray<RenderTargetHandle> m_rts;
+		RenderTargetHandle m_rt;
 		U32 m_crntPassIdx = MAX_U32;
 	} m_runCtx;
 

+ 91 - 0
src/anki/renderer/Reflections.cpp

@@ -0,0 +1,91 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/renderer/Reflections.h>
+#include <anki/renderer/Renderer.h>
+#include <anki/renderer/GBuffer.h>
+#include <anki/renderer/DepthDownscale.h>
+#include <anki/renderer/DownscaleBlur.h>
+
+namespace anki
+{
+
+Reflections::~Reflections()
+{
+}
+
+Error Reflections::init(const ConfigSet& cfg)
+{
+	Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize reflection pass");
+	}
+	return err;
+}
+
+Error Reflections::initInternal(const ConfigSet& cfg)
+{
+	U32 width = m_r->getWidth() / 2;
+	U32 height = m_r->getHeight() / 2;
+	ANKI_R_LOGI("Initializing reflection pass (%ux%u)", width, height);
+
+	// Create RT descr
+	m_rtDescr = m_r->create2DRenderTargetDescription(width,
+		height,
+		LIGHT_SHADING_COLOR_ATTACHMENT_PIXEL_FORMAT,
+		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::IMAGE_COMPUTE_WRITE,
+		"Refl");
+	m_rtDescr.bake();
+
+	// Create shader
+	ANKI_CHECK(getResourceManager().loadResource("programs/Reflections.ankiprog", m_prog));
+
+	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
+	consts.add("OUT_TEX_SIZE", UVec2(width, height));
+
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(consts.get(), variant);
+	m_grProg = variant->getProgram();
+
+	return Error::NONE;
+}
+
+void Reflections::populateRenderGraph(RenderingContext& ctx)
+{
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// Create RT
+	m_runCtx.m_rt = rgraph.newRenderTarget(m_rtDescr);
+
+	// Create pass
+	ComputeRenderPassDescription& rpass = ctx.m_renderGraphDescr.newComputeRenderPass("Refl");
+	rpass.setWork(runCallback, this, 0);
+
+	rpass.newConsumer({m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
+	rpass.newConsumer({m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_COMPUTE});
+	rpass.newConsumer({m_r->getDepthDownscale().getHalfDepthColorRt(), TextureUsageBit::SAMPLED_COMPUTE});
+	rpass.newConsumer({m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_COMPUTE});
+
+	rpass.newProducer({m_runCtx.m_rt, TextureUsageBit::IMAGE_COMPUTE_WRITE});
+}
+
+void Reflections::run(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getGBuffer().getColorRt(2), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getDepthDownscale().getHalfDepthColorRt(), m_r->getLinearSampler());
+	rgraphCtx.bindColorTextureAndSampler(0, 2, m_r->getDownscaleBlur().getRt(), m_r->getLinearSampler());
+
+	TextureSubresourceInfo subresource;
+	rgraphCtx.bindImage(0, 0, m_runCtx.m_rt, subresource);
+
+	cmdb->bindShaderProgram(m_grProg);
+
+	cmdb->dispatchCompute(m_r->getWidth() / 2, m_r->getHeight() / 2, 1);
+}
+
+} // end namespace anki

+ 55 - 0
src/anki/renderer/Reflections.h

@@ -0,0 +1,55 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/renderer/RendererObject.h>
+
+namespace anki
+{
+
+/// @addtogroup renderer
+/// @{
+
+/// Reflections pass.
+class Reflections : public RendererObject
+{
+anki_internal:
+	Reflections(Renderer* r)
+		: RendererObject(r)
+	{
+	}
+
+	~Reflections();
+
+	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
+
+	/// Populate the rendergraph.
+	void populateRenderGraph(RenderingContext& ctx);
+
+private:
+	ShaderProgramResourcePtr m_prog;
+	ShaderProgramPtr m_grProg;
+
+	RenderTargetDescription m_rtDescr;
+
+	class
+	{
+	public:
+		RenderTargetHandle m_rt;
+	} m_runCtx;
+
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+
+	static void runCallback(RenderPassWorkContext& rgraphCtx)
+	{
+		static_cast<Reflections*>(rgraphCtx.m_userData)->run(rgraphCtx);
+	}
+
+	void run(RenderPassWorkContext& rgraphCtx);
+};
+/// @}
+
+} // end namespace anki

+ 23 - 14
src/anki/renderer/Tonemapping.cpp

@@ -24,14 +24,16 @@ Error Tonemapping::init(const ConfigSet& cfg)
 
 Error Tonemapping::initInternal(const ConfigSet& initializer)
 {
-	m_rtIdx = computeMaxMipmapCount2d(m_r->getWidth(), m_r->getHeight(), AVERAGE_LUMINANCE_RENDER_TARGET_SIZE) - 1;
+	m_inputTexMip =
+		computeMaxMipmapCount2d(m_r->getWidth(), m_r->getHeight(), AVERAGE_LUMINANCE_RENDER_TARGET_SIZE) - 1;
 
 	// Create program
 	ANKI_CHECK(getResourceManager().loadResource("programs/TonemappingAverageLuminance.ankiprog", m_prog));
 
 	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
 	consts.add("INPUT_TEX_SIZE",
-		UVec2(m_r->getDownscaleBlur().getPassWidth(m_rtIdx), m_r->getDownscaleBlur().getPassHeight(m_rtIdx)));
+		UVec2(
+			m_r->getDownscaleBlur().getPassWidth(m_inputTexMip), m_r->getDownscaleBlur().getPassHeight(m_inputTexMip)));
 
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(consts.get(), variant);
@@ -62,17 +64,6 @@ Error Tonemapping::initInternal(const ConfigSet& initializer)
 	return Error::NONE;
 }
 
-void Tonemapping::run(RenderPassWorkContext& rgraphCtx)
-{
-	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-	cmdb->bindShaderProgram(m_grProg);
-	rgraphCtx.bindStorageBuffer(0, 0, m_runCtx.m_buffHandle);
-	rgraphCtx.bindColorTextureAndSampler(0, 0, m_r->getDownscaleBlur().getPassRt(m_rtIdx), m_r->getLinearSampler());
-
-	cmdb->dispatchCompute(1, 1, 1);
-}
-
 void Tonemapping::populateRenderGraph(RenderingContext& ctx)
 {
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
@@ -86,9 +77,27 @@ void Tonemapping::populateRenderGraph(RenderingContext& ctx)
 	pass.setWork(runCallback, this, 0);
 
 	pass.newConsumer({m_runCtx.m_buffHandle, BufferUsageBit::STORAGE_COMPUTE_READ_WRITE});
-	pass.newConsumer({m_r->getDownscaleBlur().getPassRt(m_rtIdx), TextureUsageBit::SAMPLED_COMPUTE});
+
+	TextureSubresourceInfo inputTexSubresource;
+	inputTexSubresource.m_firstMipmap = m_inputTexMip;
+	pass.newConsumer({m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_COMPUTE, inputTexSubresource});
 
 	pass.newProducer({m_runCtx.m_buffHandle, BufferUsageBit::STORAGE_COMPUTE_READ_WRITE});
 }
 
+void Tonemapping::run(RenderPassWorkContext& rgraphCtx)
+{
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	cmdb->bindShaderProgram(m_grProg);
+	rgraphCtx.bindStorageBuffer(0, 0, m_runCtx.m_buffHandle);
+
+	TextureSubresourceInfo inputTexSubresource;
+	inputTexSubresource.m_firstMipmap = m_inputTexMip;
+	rgraphCtx.bindTextureAndSampler(
+		0, 0, m_r->getDownscaleBlur().getRt(), inputTexSubresource, m_r->getLinearSampler());
+
+	cmdb->dispatchCompute(1, 1, 1);
+}
+
 } // end namespace anki

+ 1 - 1
src/anki/renderer/Tonemapping.h

@@ -35,7 +35,7 @@ anki_internal:
 private:
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
-	U8 m_rtIdx;
+	U8 m_inputTexMip;
 
 	BufferPtr m_luminanceBuff;
 

+ 1 - 1
src/anki/resource/ShaderProgramResource.cpp

@@ -31,7 +31,7 @@ static ANKI_USE_RESULT Error computeShaderVariableDataType(const CString& str, S
 	{
 		out = ShaderVariableDataType::IVEC4;
 	}
-	if(str == "uint")
+	else if(str == "uint")
 	{
 		out = ShaderVariableDataType::UINT;
 	}