浏览代码

[FEATURE] Some work on the SS reflections

Panagiotis Christopoulos Charitos 8 年之前
父节点
当前提交
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);
 	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
 // 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)
 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.subsurface = comp2.x;
 	g.metallic = comp2.y;
 	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);
 	comp = textureLod(rt2, uv, lod);
 	g.normal = signedOctDecode(comp.xyw);
 	g.normal = signedOctDecode(comp.xyw);

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

@@ -134,8 +134,11 @@ public:
 		TextureUsageBit& usage) const;
 		TextureUsageBit& usage) const;
 
 
 	/// Convenience method.
 	/// 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;
 		TexturePtr tex;
 		TextureUsageBit usage;
 		TextureUsageBit usage;
@@ -146,7 +149,7 @@ public:
 	}
 	}
 
 
 	/// Convenience method to bind the whole texture as color.
 	/// 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);
 		TexturePtr tex = getTexture(handle);
 		TextureViewInitInfo viewInit(tex); // Use the whole texture
 		TextureViewInitInfo viewInit(tex); // Use the whole texture
@@ -156,6 +159,17 @@ public:
 		m_commandBuffer->bindTextureAndSampler(set, binding, view, sampler, usage);
 		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.
 	/// Convenience method.
 	void bindStorageBuffer(U32 set, U32 binding, RenderPassBufferHandle handle)
 	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);
 		m_runCtx.m_exposureRt = rgraph.newRenderTarget(m_exposure.m_rtDescr);
 
 
 		// Set the render pass
 		// Set the render pass
-		GraphicsRenderPassDescription& rpass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Bloom Main");
+		GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("Bloom Main");
 		rpass.setWork(runExposureCallback, this, 0);
 		rpass.setWork(runExposureCallback, this, 0);
 		rpass.setFramebufferInfo(m_exposure.m_fbDescr, {{m_runCtx.m_exposureRt}}, {});
 		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_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.newConsumer({m_r->getTonemapping().getAverageLuminanceBuffer(), BufferUsageBit::STORAGE_FRAGMENT_READ});
 		rpass.newProducer({m_runCtx.m_exposureRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 		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);
 		m_runCtx.m_upscaleRt = rgraph.newRenderTarget(m_upscale.m_rtDescr);
 
 
 		// Set the render pass
 		// Set the render pass
-		GraphicsRenderPassDescription& rpass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Bloom Upscale");
+		GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("Bloom Upscale");
 		rpass.setWork(runUpscaleAndSslfCallback, this, 0);
 		rpass.setWork(runUpscaleAndSslfCallback, this, 0);
 		rpass.setFramebufferInfo(m_upscale.m_fbDescr, {{m_runCtx.m_upscaleRt}}, {});
 		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->setViewport(0, 0, m_exposure.m_width, m_exposure.m_height);
 	cmdb->bindShaderProgram(m_exposure.m_grProg);
 	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);
 	Vec4* uniforms = allocateAndBindUniforms<Vec4*>(sizeof(Vec4), cmdb, 0, 0);
 	*uniforms = Vec4(m_exposure.m_threshold, m_exposure.m_scale, 0.0, 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()
 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)
 Error DownscaleBlur::init(const ConfigSet& cfg)
 {
 {
-	ANKI_R_LOGI("Initializing dowscale blur");
-
 	Error err = initInternal(cfg);
 	Error err = initInternal(cfg);
 	if(err)
 	if(err)
 	{
 	{
@@ -52,22 +28,28 @@ Error DownscaleBlur::init(const ConfigSet& cfg)
 
 
 Error DownscaleBlur::initInternal(const ConfigSet&)
 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
 	// 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
 	// Shader programs
 	ANKI_CHECK(getResourceManager().loadResource("programs/DownscaleBlur.ankiprog", m_prog));
 	ANKI_CHECK(getResourceManager().loadResource("programs/DownscaleBlur.ankiprog", m_prog));
@@ -78,42 +60,16 @@ Error DownscaleBlur::initInternal(const ConfigSet&)
 	return Error::NONE;
 	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)
 void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 {
 {
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	m_runCtx.m_crntPassIdx = 0;
 	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
 	// Create passes
-	Array<CString, 8> passNames = {{"DownBlur #0",
+	static const Array<CString, 8> passNames = {{"DownBlur #0",
 		"Down/Blur #1",
 		"Down/Blur #1",
 		"Down/Blur #2",
 		"Down/Blur #2",
 		"Down/Blur #3",
 		"Down/Blur #3",
@@ -121,31 +77,61 @@ void DownscaleBlur::populateRenderGraph(RenderingContext& ctx)
 		"Down/Blur #5",
 		"Down/Blur #5",
 		"Down/Blur #6",
 		"Down/Blur #6",
 		"Down/Blur #7"}};
 		"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]);
 		GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[i]);
-
 		pass.setWork(runCallback, this, 0);
 		pass.setWork(runCallback, this, 0);
 
 
-		RenderTargetHandle renderRt, sampleRt;
 		if(i > 0)
 		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
 		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
 } // end namespace anki

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

@@ -31,30 +31,30 @@ anki_internal:
 
 
 	U getPassWidth(U pass) const
 	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
 	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;
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
 	ShaderProgramPtr m_grProg;
@@ -62,7 +62,7 @@ private:
 	class
 	class
 	{
 	{
 	public:
 	public:
-		DynamicArray<RenderTargetHandle> m_rts;
+		RenderTargetHandle m_rt;
 		U32 m_crntPassIdx = MAX_U32;
 		U32 m_crntPassIdx = MAX_U32;
 	} m_runCtx;
 	} 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)
 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
 	// Create program
 	ANKI_CHECK(getResourceManager().loadResource("programs/TonemappingAverageLuminance.ankiprog", m_prog));
 	ANKI_CHECK(getResourceManager().loadResource("programs/TonemappingAverageLuminance.ankiprog", m_prog));
 
 
 	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
 	ShaderProgramResourceConstantValueInitList<1> consts(m_prog);
 	consts.add("INPUT_TEX_SIZE",
 	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;
 	const ShaderProgramResourceVariant* variant;
 	m_prog->getOrCreateVariant(consts.get(), variant);
 	m_prog->getOrCreateVariant(consts.get(), variant);
@@ -62,17 +64,6 @@ Error Tonemapping::initInternal(const ConfigSet& initializer)
 	return Error::NONE;
 	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)
 void Tonemapping::populateRenderGraph(RenderingContext& ctx)
 {
 {
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
@@ -86,9 +77,27 @@ void Tonemapping::populateRenderGraph(RenderingContext& ctx)
 	pass.setWork(runCallback, this, 0);
 	pass.setWork(runCallback, this, 0);
 
 
 	pass.newConsumer({m_runCtx.m_buffHandle, BufferUsageBit::STORAGE_COMPUTE_READ_WRITE});
 	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});
 	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
 } // end namespace anki

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

@@ -35,7 +35,7 @@ anki_internal:
 private:
 private:
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
 	ShaderProgramPtr m_grProg;
-	U8 m_rtIdx;
+	U8 m_inputTexMip;
 
 
 	BufferPtr m_luminanceBuff;
 	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;
 		out = ShaderVariableDataType::IVEC4;
 	}
 	}
-	if(str == "uint")
+	else if(str == "uint")
 	{
 	{
 		out = ShaderVariableDataType::UINT;
 		out = ShaderVariableDataType::UINT;
 	}
 	}