瀏覽代碼

Move some code to its own class

Panagiotis Christopoulos Charitos 7 年之前
父節點
當前提交
4dd6f97f5a

+ 6 - 9
shaders/TraditionalDeferredShading.ankiprog

@@ -37,10 +37,6 @@ void main()
 		</shader>
 
 		<shader type="frag">
-			<inputs>
-				<input name="FB_SIZE" type="UVec2" const="1"/>
-			</inputs>
-
 			<source><![CDATA[
 #include <shaders/Pack.glsl>
 #include <shaders/LightFunctions.glsl>
@@ -51,10 +47,10 @@ void main()
 
 layout(location = 0) out Vec3 out_color;
 
-layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msRt0;
-layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_msRt1;
-layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_msRt2;
-layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_msDepthRt;
+layout(ANKI_TEX_BINDING(GBUFFER_RT0_BINDING.x, GBUFFER_RT0_BINDING.y)) uniform sampler2D u_msRt0;
+layout(ANKI_TEX_BINDING(GBUFFER_RT1_BINDING.x, GBUFFER_RT1_BINDING.y)) uniform sampler2D u_msRt1;
+layout(ANKI_TEX_BINDING(GBUFFER_RT2_BINDING.x, GBUFFER_RT2_BINDING.y)) uniform sampler2D u_msRt2;
+layout(ANKI_TEX_BINDING(GBUFFER_DEPTH_BINDING.x, GBUFFER_DEPTH_BINDING.y)) uniform sampler2D u_msDepthRt;
 
 layout(ANKI_UBO_BINDING(0, 1), row_major) uniform u1_
 {
@@ -81,11 +77,12 @@ layout(ANKI_UBO_BINDING(0, 1), row_major) uniform u1_
 #define u_camPos u_unis.m_camPosPad1.xyz
 #define u_inputTexUvScaleAndOffset u_unis.m_inputTexUvScaleAndOffset
 #define u_invViewProjMat u_unis.m_invViewProjMat
+#define u_fbSize u_unis.m_fbSizePad2.xy
 
 void main()
 {
 	// Compute UV coordinates
-	Vec2 uv = Vec2(gl_FragCoord.xy) / Vec2(FB_SIZE.x, FB_SIZE.y);
+	Vec2 uv = Vec2(gl_FragCoord.xy) / u_fbSize;
 	Vec2 uvToRead = fma(uv, u_inputTexUvScaleAndOffset.xy, u_inputTexUvScaleAndOffset.zw);
 
 	// Do manual depth test

+ 8 - 0
shaders/glsl_cpp_common/Common.h

@@ -16,6 +16,10 @@
 #	define ANKI_END_NAMESPACE }
 #	define ANKI_SHADER_FUNC_INLINE inline
 
+#	define ANKI_SHADER_INOUT(type_) type&
+#	define ANKI_SHADER_IN(type_) const type&
+#	define ANKI_SHADER_OUT(type_) type&
+
 ANKI_BEGIN_NAMESPACE
 template<typename T>
 inline F32 dot(const T& a, const T& b)
@@ -35,6 +39,10 @@ ANKI_END_NAMESPACE
 #	define ANKI_BEGIN_NAMESPACE
 #	define ANKI_END_NAMESPACE
 #	define ANKI_SHADER_FUNC_INLINE
+
+#	define ANKI_SHADER_INOUT(type_) inout type
+#	define ANKI_SHADER_IN(type_) in type
+#	define ANKI_SHADER_OUT(type_) out type
 #endif
 
 //

+ 12 - 0
shaders/glsl_cpp_common/TraditionalDeferredShading.h

@@ -15,6 +15,7 @@ struct DeferredPointLightUniforms
 	Vec4 m_inputTexUvScaleAndOffset; // Use this to get the correct face UVs
 	Mat4 m_invViewProjMat;
 	Vec4 m_camPosPad1;
+	Vec4 m_fbSizePad2;
 
 	// Light props
 	Vec4 m_posRadius; // xyz: Light pos in world space. w: The -1/radius
@@ -26,6 +27,7 @@ struct DeferredSpotLightUniforms
 	Vec4 m_inputTexUvScaleAndOffset; // Use this to get the correct face UVs
 	Mat4 m_invViewProjMat;
 	Vec4 m_camPosPad1;
+	Vec4 m_fbSizePad2;
 
 	// Light props
 	Vec4 m_posRadius; // xyz: Light pos in world space. w: The -1/radius
@@ -33,6 +35,16 @@ struct DeferredSpotLightUniforms
 	Vec4 m_lightDirInnerCos; // xyz: light dir, w: inner cosine of spot
 };
 
+struct DeferredVertexUniforms
+{
+	Mat4 m_mvp;
+};
+
+const UVec2 GBUFFER_RT0_BINDING = UVec2(0, 0);
+const UVec2 GBUFFER_RT1_BINDING = UVec2(0, 1);
+const UVec2 GBUFFER_RT2_BINDING = UVec2(0, 2);
+const UVec2 GBUFFER_DEPTH_BINDING = UVec2(0, 3);
+
 ANKI_END_NAMESPACE
 
 #endif

+ 20 - 151
src/anki/renderer/Indirect.cpp

@@ -16,13 +16,9 @@
 namespace anki
 {
 
-struct Indirect::LightPassVertexUniforms
-{
-	Mat4 m_mvp;
-};
-
 Indirect::Indirect(Renderer* r)
 	: RendererObject(r)
+	, m_lightShading(r)
 {
 }
 
@@ -133,29 +129,8 @@ Error Indirect::initLightShading(const ConfigSet& config)
 		m_lightShading.m_cubeArr = m_r->createAndClearRenderTarget(texinit);
 	}
 
-	// Init progs
-	{
-		ANKI_CHECK(getResourceManager().loadResource(
-			"shaders/TraditionalDeferredShading.ankiprog", m_lightShading.m_lightProg));
-
-		ShaderProgramResourceMutationInitList<1> mutators(m_lightShading.m_lightProg);
-		mutators.add("LIGHT_TYPE", 0);
-
-		ShaderProgramResourceConstantValueInitList<1> consts(m_lightShading.m_lightProg);
-		consts.add("FB_SIZE", UVec2(m_lightShading.m_tileSize, m_lightShading.m_tileSize));
-
-		const ShaderProgramResourceVariant* variant;
-		m_lightShading.m_lightProg->getOrCreateVariant(mutators.get(), consts.get(), variant);
-		m_lightShading.m_plightGrProg = variant->getProgram();
-
-		mutators[0].m_value = 1;
-		m_lightShading.m_lightProg->getOrCreateVariant(mutators.get(), consts.get(), variant);
-		m_lightShading.m_slightGrProg = variant->getProgram();
-	}
-
-	// Init meshes
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/Plight.ankimesh", m_lightShading.m_plightMesh, false));
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/Slight.ankimesh", m_lightShading.m_slightMesh, false));
+	// Init deferred
+	ANKI_CHECK(m_lightShading.m_deferred.init());
 
 	return Error::NONE;
 }
@@ -360,30 +335,6 @@ void Indirect::runGBuffer(CommandBufferPtr& cmdb)
 	cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
 }
 
-void Indirect::bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount)
-{
-	// Attrib
-	U32 bufferBinding;
-	Format fmt;
-	PtrSize relativeOffset;
-	mesh->getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferBinding, fmt, relativeOffset);
-
-	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);
-
-	// Vert buff
-	BufferPtr buff;
-	PtrSize offset, stride;
-	mesh->getVertexBufferInfo(bufferBinding, buff, offset, stride);
-
-	cmdb->bindVertexBuffer(0, buff, offset, stride);
-
-	// Idx buff
-	IndexType idxType;
-	mesh->getIndexBufferInfo(buff, offset, indexCount, idxType);
-
-	cmdb->bindIndexBuffer(buff, offset, idxType);
-}
-
 void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_ASSERT(faceIdx <= 6);
@@ -393,114 +344,32 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 
 	ANKI_ASSERT(m_ctx.m_probe);
 	const ReflectionProbeQueueElement& probe = *m_ctx.m_probe;
+	const RenderQueue& rqueue = *probe.m_renderQueues[faceIdx];
 
 	// Set common state for all lights
 	// NOTE: Use nearest sampler because we don't want the result to sample the near tiles
-	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
-	{
-		rgraphCtx.bindColorTextureAndSampler(0, i, m_ctx.m_gbufferColorRts[i], m_r->getNearestSampler());
-	}
+	rgraphCtx.bindColorTextureAndSampler(
+		GBUFFER_RT0_BINDING.x(), GBUFFER_RT0_BINDING.y(), m_ctx.m_gbufferColorRts[0], m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(
+		GBUFFER_RT1_BINDING.x(), GBUFFER_RT1_BINDING.y(), m_ctx.m_gbufferColorRts[1], m_r->getNearestSampler());
+	rgraphCtx.bindColorTextureAndSampler(
+		GBUFFER_RT2_BINDING.x(), GBUFFER_RT2_BINDING.y(), m_ctx.m_gbufferColorRts[2], m_r->getNearestSampler());
+
 	rgraphCtx.bindTextureAndSampler(0,
 		GBUFFER_COLOR_ATTACHMENT_COUNT,
 		m_ctx.m_gbufferDepthRt,
 		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
 		m_r->getNearestSampler());
-	cmdb->setViewport(0, 0, m_lightShading.m_tileSize, m_lightShading.m_tileSize);
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
-	cmdb->setCullMode(FaceSelectionBit::FRONT);
-
-	// Render lights
-	{
-		ANKI_ASSERT(probe.m_renderQueues[faceIdx]);
-		const RenderQueue& rqueue = *probe.m_renderQueues[faceIdx];
-
-		const Mat4& vpMat = rqueue.m_viewProjectionMatrix;
-		const Mat4 invViewProjMat = rqueue.m_viewProjectionMatrix.getInverse();
 
-		// Do point lights
-		U32 indexCount;
-		bindVertexIndexBuffers(m_lightShading.m_plightMesh, cmdb, indexCount);
-		cmdb->bindShaderProgram(m_lightShading.m_plightGrProg);
-
-		const PointLightQueueElement* plightEl = rqueue.m_pointLights.getBegin();
-		while(plightEl != rqueue.m_pointLights.getEnd())
-		{
-			// Update uniforms
-			LightPassVertexUniforms* vert =
-				allocateAndBindUniforms<LightPassVertexUniforms*>(sizeof(LightPassVertexUniforms), cmdb, 0, 0);
-
-			Mat4 modelM(plightEl->m_worldPosition.xyz1(), Mat3::getIdentity(), plightEl->m_radius);
-
-			vert->m_mvp = vpMat * modelM;
-
-			DeferredPointLightUniforms* light =
-				allocateAndBindUniforms<DeferredPointLightUniforms*>(sizeof(DeferredPointLightUniforms), cmdb, 0, 1);
-
-			light->m_inputTexUvScaleAndOffset = Vec4(1.0f / 6.0f, 1.0f, faceIdx * (1.0f / 6.0f), 0.0f);
-			light->m_invViewProjMat = invViewProjMat;
-			light->m_camPosPad1 = rqueue.m_cameraTransform.getTranslationPart();
-			light->m_posRadius =
-				Vec4(plightEl->m_worldPosition.xyz(), 1.0f / (plightEl->m_radius * plightEl->m_radius));
-			light->m_diffuseColorPad1 = plightEl->m_diffuseColor.xyz0();
-
-			// Draw
-			cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
-
-			++plightEl;
-		}
-
-		// Do spot lights
-		bindVertexIndexBuffers(m_lightShading.m_slightMesh, cmdb, indexCount);
-		cmdb->bindShaderProgram(m_lightShading.m_slightGrProg);
-
-		const SpotLightQueueElement* splightEl = rqueue.m_spotLights.getBegin();
-		while(splightEl != rqueue.m_spotLights.getEnd())
-		{
-			// Compute the model matrix
-			//
-			Mat4 modelM(splightEl->m_worldTransform.getTranslationPart().xyz1(),
-				splightEl->m_worldTransform.getRotationPart(),
-				1.0f);
-
-			// Calc the scale of the cone
-			Mat4 scaleM(Mat4::getIdentity());
-			scaleM(0, 0) = tan(splightEl->m_outerAngle / 2.0f) * splightEl->m_distance;
-			scaleM(1, 1) = scaleM(0, 0);
-			scaleM(2, 2) = splightEl->m_distance;
-
-			modelM = modelM * scaleM;
-
-			// Update vertex uniforms
-			LightPassVertexUniforms* vert =
-				allocateAndBindUniforms<LightPassVertexUniforms*>(sizeof(LightPassVertexUniforms), cmdb, 0, 0);
-			vert->m_mvp = vpMat * modelM;
-
-			// Update fragment uniforms
-			DeferredSpotLightUniforms* light =
-				allocateAndBindUniforms<DeferredSpotLightUniforms*>(sizeof(DeferredSpotLightUniforms), cmdb, 0, 1);
-
-			light->m_inputTexUvScaleAndOffset = Vec4(1.0f / 6.0f, 1.0f, faceIdx * (1.0f / 6.0f), 0.0f);
-			light->m_invViewProjMat = invViewProjMat;
-			light->m_camPosPad1 = rqueue.m_cameraTransform.getTranslationPart();
-
-			light->m_posRadius = Vec4(splightEl->m_worldTransform.getTranslationPart().xyz(),
-				1.0f / (splightEl->m_distance * splightEl->m_distance));
-
-			light->m_diffuseColorOuterCos = Vec4(splightEl->m_diffuseColor, cos(splightEl->m_outerAngle / 2.0f));
-
-			Vec3 lightDir = -splightEl->m_worldTransform.getZAxis().xyz();
-			light->m_lightDirInnerCos = Vec4(lightDir, cos(splightEl->m_innerAngle / 2.0f));
-
-			// Draw
-			cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
-
-			++splightEl;
-		}
-	}
-
-	// Restore state
-	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
-	cmdb->setCullMode(FaceSelectionBit::BACK);
+	m_lightShading.m_deferred.drawLights(rqueue.m_viewProjectionMatrix,
+		rqueue.m_viewProjectionMatrix.getInverse(),
+		rqueue.m_cameraTransform.getTranslationPart(),
+		UVec4(0, 0, m_lightShading.m_tileSize, m_lightShading.m_tileSize),
+		Vec2(faceIdx * (1.0f / 6.0f), 0.0f),
+		Vec2((faceIdx + 1) * (1.0f / 6.0f), 1.0f),
+		rqueue.m_pointLights,
+		rqueue.m_spotLights,
+		cmdb);
 }
 
 void Indirect::runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)

+ 7 - 12
src/anki/renderer/Indirect.h

@@ -7,6 +7,7 @@
 
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/RendererObject.h>
+#include <anki/renderer/TraditionalDeferredShading.h>
 #include <anki/renderer/Clusterer.h>
 #include <anki/resource/TextureResource.h>
 
@@ -57,8 +58,6 @@ anki_internal:
 	}
 
 private:
-	struct LightPassVertexUniforms;
-
 	class
 	{
 	public:
@@ -68,22 +67,19 @@ private:
 		FramebufferDescription m_fbDescr;
 	} m_gbuffer; ///< G-buffer pass.
 
-	class
+	class LS
 	{
 	public:
 		U32 m_tileSize = 0;
 		U32 m_mipCount = 0;
 		TexturePtr m_cubeArr;
 
-		ShaderProgramResourcePtr m_lightProg;
-		ShaderProgramPtr m_plightGrProg;
-		ShaderProgramPtr m_slightGrProg;
+		TraditionalDeferredLightShading m_deferred;
 
-		/// @name Meshes of light volumes.
-		/// @{
-		MeshResourcePtr m_plightMesh;
-		MeshResourcePtr m_slightMesh;
-		/// @}
+		LS(Renderer* r)
+			: m_deferred(r)
+		{
+		}
 	} m_lightShading; ///< Light shading.
 
 	class
@@ -144,7 +140,6 @@ private:
 	void runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
-	static void bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount);
 
 	// A RenderPassWorkCallback for G-buffer pass
 	static void runGBufferCallback(RenderPassWorkContext& rgraphCtx)

+ 178 - 0
src/anki/renderer/TraditionalDeferredShading.cpp

@@ -0,0 +1,178 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/renderer/TraditionalDeferredShading.h>
+#include <anki/renderer/RenderQueue.h>
+#include <anki/resource/ResourceManager.h>
+#include <anki/resource/ShaderProgramResource.h>
+#include <anki/resource/MeshResource.h>
+#include <shaders/glsl_cpp_common/TraditionalDeferredShading.h>
+
+namespace anki
+{
+
+TraditionalDeferredLightShading::TraditionalDeferredLightShading(Renderer* r)
+	: RendererObject(r)
+{
+}
+
+TraditionalDeferredLightShading::~TraditionalDeferredLightShading()
+{
+}
+
+Error TraditionalDeferredLightShading::init()
+{
+	// Init progs
+	{
+		ANKI_CHECK(getResourceManager().loadResource("shaders/TraditionalDeferredShading.ankiprog", m_lightProg));
+
+		ShaderProgramResourceMutationInitList<1> mutators(m_lightProg);
+		mutators.add("LIGHT_TYPE", 0);
+
+		const ShaderProgramResourceVariant* variant;
+		m_lightProg->getOrCreateVariant(mutators.get(), variant);
+		m_plightGrProg = variant->getProgram();
+
+		mutators[0].m_value = 1;
+		m_lightProg->getOrCreateVariant(mutators.get(), variant);
+		m_slightGrProg = variant->getProgram();
+	}
+
+	// Init meshes
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/Plight.ankimesh", m_plightMesh, false));
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/Slight.ankimesh", m_slightMesh, false));
+
+	return Error::NONE;
+}
+
+void TraditionalDeferredLightShading::bindVertexIndexBuffers(
+	MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount)
+{
+	// Attrib
+	U32 bufferBinding;
+	Format fmt;
+	PtrSize relativeOffset;
+	mesh->getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferBinding, fmt, relativeOffset);
+
+	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);
+
+	// Vert buff
+	BufferPtr buff;
+	PtrSize offset, stride;
+	mesh->getVertexBufferInfo(bufferBinding, buff, offset, stride);
+
+	cmdb->bindVertexBuffer(0, buff, offset, stride);
+
+	// Idx buff
+	IndexType idxType;
+	mesh->getIndexBufferInfo(buff, offset, indexCount, idxType);
+
+	cmdb->bindIndexBuffer(buff, offset, idxType);
+}
+
+void TraditionalDeferredLightShading::drawLights(const Mat4& vpMat,
+	const Mat4& invViewProjMat,
+	const Vec4& cameraPosWSpace,
+	const UVec4& viewport,
+	const Vec2& gbufferTexCoordsMin,
+	const Vec2& gbufferTexCoordsMax,
+	ConstWeakArray<PointLightQueueElement> plights,
+	ConstWeakArray<SpotLightQueueElement> slights,
+	CommandBufferPtr& cmdb)
+{
+	ANKI_ASSERT(gbufferTexCoordsMin < gbufferTexCoordsMax);
+
+	// Compute coords
+	Vec4 inputTexUvScaleAndOffset;
+	inputTexUvScaleAndOffset.x() = gbufferTexCoordsMax.x() - gbufferTexCoordsMin.x();
+	inputTexUvScaleAndOffset.y() = gbufferTexCoordsMax.y() - gbufferTexCoordsMin.y();
+	inputTexUvScaleAndOffset.z() = gbufferTexCoordsMin.x();
+	inputTexUvScaleAndOffset.w() = gbufferTexCoordsMin.y();
+
+	// Set common state for all lights
+	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
+	cmdb->setCullMode(FaceSelectionBit::FRONT);
+	cmdb->setViewport(viewport.x(), viewport.y(), viewport.z(), viewport.w());
+
+	// Do point lights
+	U32 indexCount;
+	bindVertexIndexBuffers(m_plightMesh, cmdb, indexCount);
+	cmdb->bindShaderProgram(m_plightGrProg);
+
+	for(const PointLightQueueElement& plightEl : plights)
+	{
+		// Update uniforms
+		DeferredVertexUniforms* vert =
+			allocateAndBindUniforms<DeferredVertexUniforms*>(sizeof(DeferredVertexUniforms), cmdb, 0, 0);
+
+		Mat4 modelM(plightEl.m_worldPosition.xyz1(), Mat3::getIdentity(), plightEl.m_radius);
+
+		vert->m_mvp = vpMat * modelM;
+
+		DeferredPointLightUniforms* light =
+			allocateAndBindUniforms<DeferredPointLightUniforms*>(sizeof(DeferredPointLightUniforms), cmdb, 0, 1);
+
+		light->m_inputTexUvScaleAndOffset = inputTexUvScaleAndOffset;
+		light->m_invViewProjMat = invViewProjMat;
+		light->m_camPosPad1 = cameraPosWSpace.xyz0();
+		light->m_fbSizePad2 = Vec4(viewport.z(), viewport.w(), 0.0f, 0.0f);
+		light->m_posRadius = Vec4(plightEl.m_worldPosition.xyz(), 1.0f / (plightEl.m_radius * plightEl.m_radius));
+		light->m_diffuseColorPad1 = plightEl.m_diffuseColor.xyz0();
+
+		// Draw
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
+	}
+
+	// Do spot lights
+	bindVertexIndexBuffers(m_slightMesh, cmdb, indexCount);
+	cmdb->bindShaderProgram(m_slightGrProg);
+
+	for(const SpotLightQueueElement& splightEl : slights)
+	{
+		// Compute the model matrix
+		//
+		Mat4 modelM(
+			splightEl.m_worldTransform.getTranslationPart().xyz1(), splightEl.m_worldTransform.getRotationPart(), 1.0f);
+
+		// Calc the scale of the cone
+		Mat4 scaleM(Mat4::getIdentity());
+		scaleM(0, 0) = tan(splightEl.m_outerAngle / 2.0f) * splightEl.m_distance;
+		scaleM(1, 1) = scaleM(0, 0);
+		scaleM(2, 2) = splightEl.m_distance;
+
+		modelM = modelM * scaleM;
+
+		// Update vertex uniforms
+		DeferredVertexUniforms* vert =
+			allocateAndBindUniforms<DeferredVertexUniforms*>(sizeof(DeferredVertexUniforms), cmdb, 0, 0);
+		vert->m_mvp = vpMat * modelM;
+
+		// Update fragment uniforms
+		DeferredSpotLightUniforms* light =
+			allocateAndBindUniforms<DeferredSpotLightUniforms*>(sizeof(DeferredSpotLightUniforms), cmdb, 0, 1);
+
+		light->m_inputTexUvScaleAndOffset = inputTexUvScaleAndOffset;
+		light->m_invViewProjMat = invViewProjMat;
+		light->m_camPosPad1 = cameraPosWSpace.xyz0();
+		light->m_fbSizePad2 = Vec4(viewport.z(), viewport.w(), 0.0f, 0.0f);
+
+		light->m_posRadius = Vec4(splightEl.m_worldTransform.getTranslationPart().xyz(),
+			1.0f / (splightEl.m_distance * splightEl.m_distance));
+
+		light->m_diffuseColorOuterCos = Vec4(splightEl.m_diffuseColor, cos(splightEl.m_outerAngle / 2.0f));
+
+		Vec3 lightDir = -splightEl.m_worldTransform.getZAxis().xyz();
+		light->m_lightDirInnerCos = Vec4(lightDir, cos(splightEl.m_innerAngle / 2.0f));
+
+		// Draw
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
+	}
+
+	// Restore state
+	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
+	cmdb->setCullMode(FaceSelectionBit::BACK);
+}
+
+} // end namespace anki

+ 52 - 0
src/anki/renderer/TraditionalDeferredShading.h

@@ -0,0 +1,52 @@
+// Copyright (C) 2009-2018, 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
+/// @{
+
+/// Helper for drawing using traditional deferred shading.
+class TraditionalDeferredLightShading : public RendererObject
+{
+public:
+	TraditionalDeferredLightShading(Renderer* r);
+
+	~TraditionalDeferredLightShading();
+
+	ANKI_USE_RESULT Error init();
+
+	/// Run the light shading. It will iterate over the lights and draw them. It doesn't bind anything related to
+	/// GBuffer or the output buffer.
+	void drawLights(const Mat4& vpMat,
+		const Mat4& invViewProjMat,
+		const Vec4& cameraPosWSpace,
+		const UVec4& viewport,
+		const Vec2& gbufferTexCoordsMin,
+		const Vec2& gbufferTexCoordsMax,
+		ConstWeakArray<PointLightQueueElement> plights,
+		ConstWeakArray<SpotLightQueueElement> slights,
+		CommandBufferPtr& cmdb);
+
+private:
+	ShaderProgramResourcePtr m_lightProg;
+	ShaderProgramPtr m_plightGrProg;
+	ShaderProgramPtr m_slightGrProg;
+
+	/// @name Meshes of light volumes.
+	/// @{
+	MeshResourcePtr m_plightMesh;
+	MeshResourcePtr m_slightMesh;
+	/// @}
+
+	static void bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount);
+};
+/// @}
+} // end namespace anki

+ 1 - 1
src/anki/util/WeakArray.h

@@ -222,7 +222,7 @@ public:
 
 	/// Construct from WeakArray.
 	ConstWeakArray(const WeakArray<T>& arr)
-		: ConstWeakArray(&arr[0], arr.getSize())
+		: ConstWeakArray((arr.getSize()) ? &arr[0] : nullptr, arr.getSize())
 	{
 	}