Browse Source

CSM is feature complete

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
8e162f0713

+ 10 - 0
shaders/LightFunctions.glsl

@@ -174,6 +174,16 @@ F32 computeShadowFactorDirLight(DirectionalLight light, U32 cascadeIdx, Vec3 wor
 	return shadowFactor;
 }
 
+// Compute the shadow factor of a directional light
+F32 computeShadowFactorDirLight(Mat4 lightProjectionMat, Vec3 worldPos, sampler2DShadow shadowMap)
+{
+	Vec4 texCoords4 = lightProjectionMat * Vec4(worldPos, 1.0);
+	Vec3 texCoords3 = texCoords4.xyz / texCoords4.w;
+
+	F32 shadowFactor = textureLod(shadowMap, texCoords3, 0.0);
+	return shadowFactor;
+}
+
 // Compute the cubemap texture lookup vector given the reflection vector (r) the radius squared of the probe (R2) and
 // the frag pos in sphere space (f)
 Vec3 computeCubemapVecAccurate(in Vec3 r, in F32 R2, in Vec3 f)

+ 56 - 39
shaders/TraditionalDeferredShading.glslp

@@ -5,19 +5,31 @@
 
 // Classic deferred lighting shader
 
-#pragma anki mutator LIGHT_TYPE 0 1
+#pragma anki mutator LIGHT_TYPE 0 1 2
+
+#define POINT_LIGHT_TYPE 0
+#define SPOT_LIGHT_TYPE 1
+#define DIR_LIGHT_TYPE 2
 
 // VERT
 #pragma anki start vert
 #include <shaders/Common.glsl>
 
-layout(location = 0) in Vec3 in_position;
-
 out gl_PerVertex
 {
 	Vec4 gl_Position;
 };
 
+#if LIGHT_TYPE == DIR_LIGHT_TYPE
+void main()
+{
+	Vec2 uv = Vec2(gl_VertexID & 1, gl_VertexID >> 1) * 2.0;
+	Vec2 pos = uv * 2.0 - 1.0;
+	gl_Position = Vec4(pos, 0.0, 1.0);
+}
+#else
+layout(location = 0) in Vec3 in_position;
+
 layout(ANKI_UBO_BINDING(0, 0), row_major) uniform u0_
 {
 	Mat4 u_mvp;
@@ -27,6 +39,7 @@ void main()
 {
 	gl_Position = u_mvp * Vec4(in_position, 1.0);
 }
+#endif
 #pragma anki end
 
 // FRAG
@@ -35,15 +48,12 @@ void main()
 #include <shaders/LightFunctions.glsl>
 #include <shaders/glsl_cpp_common/TraditionalDeferredShading.h>
 
-#define POINT_LIGHT_TYPE 0
-#define SPOT_LIGHT_TYPE 1
-
 layout(location = 0) out Vec3 out_color;
 
-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_TEX_BINDING(GBUFFER_RT0_BINDING[0], GBUFFER_RT0_BINDING[1])) uniform sampler2D u_msRt0;
+layout(ANKI_TEX_BINDING(GBUFFER_RT1_BINDING[0], GBUFFER_RT1_BINDING[1])) uniform sampler2D u_msRt1;
+layout(ANKI_TEX_BINDING(GBUFFER_RT2_BINDING[0], GBUFFER_RT2_BINDING[1])) uniform sampler2D u_msRt2;
+layout(ANKI_TEX_BINDING(GBUFFER_DEPTH_BINDING[0], GBUFFER_DEPTH_BINDING[1])) uniform sampler2D u_msDepthRt;
 
 layout(ANKI_UBO_BINDING(0, 1), row_major) uniform u1_
 {
@@ -51,65 +61,72 @@ layout(ANKI_UBO_BINDING(0, 1), row_major) uniform u1_
 	DeferredPointLightUniforms u_unis;
 #elif LIGHT_TYPE == SPOT_LIGHT_TYPE
 	DeferredSpotLightUniforms u_unis;
+#elif LIGHT_TYPE == DIR_LIGHT_TYPE
+	DeferredDirectionalLightUniforms u_unis;
 #else
 #	error See file
 #endif
 };
 
-#if LIGHT_TYPE == POINT_LIGHT_TYPE
-#	define u_ldiff u_unis.m_diffuseColorPad1.xyz
-#else
-#	define u_ldiff u_unis.m_diffuseColorOuterCos.xyz
-#	define u_lightDir u_unis.m_lightDirInnerCos.xyz
-#	define u_outerCos u_unis.m_diffuseColorOuterCos.w
-#	define u_innerCos u_unis.m_lightDirInnerCos.w
+#if LIGHT_TYPE == DIR_LIGHT_TYPE
+layout(ANKI_TEX_BINDING(
+	GBUFFER_SHADOW_ATLAS_BINDING[0], GBUFFER_SHADOW_ATLAS_BINDING[1])) uniform sampler2DShadow u_shadowMap;
 #endif
 
-#define u_pos u_unis.m_posRadius.xyz
-#define u_radius u_unis.m_posRadius.w
-#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) / u_fbSize;
-	Vec2 uvToRead = fma(uv, u_inputTexUvScaleAndOffset.xy, u_inputTexUvScaleAndOffset.zw);
+	Vec2 uv = Vec2(gl_FragCoord.xy) / u_unis.m_fbSize;
+	Vec2 uvToRead = fma(uv, u_unis.m_inputTexUvScale, u_unis.m_inputTexUvOffset);
 
-	// Do manual depth test
 	F32 depth = texture(u_msDepthRt, uvToRead).r;
+
+#if LIGHT_TYPE != DIR_LIGHT_TYPE
+	// Do manual depth test
 	if(gl_FragCoord.z < depth)
 	{
 		discard;
 	}
+#endif
 
 	// Decode and process gbuffer
 	GbufferInfo gbuffer;
 	readGBuffer(u_msRt0, u_msRt1, u_msRt2, uvToRead, 0.0, gbuffer);
 
-	Vec4 worldPos4 = u_invViewProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
+	Vec4 worldPos4 = u_unis.m_invViewProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
 	Vec3 worldPos = worldPos4.xyz / worldPos4.w;
 
-	// Calculate the light color
-	Vec3 viewDir = normalize(u_camPos - worldPos);
-	Vec3 frag2Light = u_pos - worldPos;
+	// Compute diff
+	Vec3 diffC = diffuseLambert(gbuffer.m_diffuse);
+
+	// Compute spec
+	Vec3 viewDir = normalize(u_unis.m_camPos - worldPos);
+#if LIGHT_TYPE == DIR_LIGHT_TYPE
+	Vec3 l = u_unis.m_lightDir;
+#else
+	Vec3 frag2Light = u_unis.m_position - worldPos;
 	Vec3 l = normalize(frag2Light);
 	F32 nol = max(0.0, dot(gbuffer.m_normal, l));
-
+#endif
 	Vec3 specC = computeSpecularColorBrdf(gbuffer, viewDir, l);
-	Vec3 diffC = diffuseLambert(gbuffer.m_diffuse);
-
-	F32 att = computeAttenuationFactor(u_radius, frag2Light);
-	F32 lambert = nol;
 
+	// Compute factors
 #if LIGHT_TYPE == POINT_LIGHT_TYPE
-	out_color = (specC + diffC) * u_ldiff * (att * max(lambert, gbuffer.m_subsurface));
+	F32 att = computeAttenuationFactor(u_unis.m_oneOverSquareRadius, frag2Light);
+	F32 lambert = nol;
+	F32 factor = att * max(lambert, gbuffer.m_subsurface);
+#elif LIGHT_TYPE == SPOT_LIGHT_TYPE
+	F32 att = computeAttenuationFactor(u_unis.m_oneOverSquareRadius, frag2Light);
+	F32 lambert = nol;
+	F32 spot = computeSpotFactor(l, u_unis.m_outerCos, u_unis.m_innerCos, u_unis.m_lightDir);
+	F32 factor = att * spot * max(lambert, gbuffer.m_subsurface);
 #else
-	F32 spot = computeSpotFactor(l, u_outerCos, u_innerCos, u_lightDir);
-	out_color = (diffC + specC) * u_ldiff * (att * spot * max(lambert, gbuffer.m_subsurface));
+	F32 shadowFactor = computeShadowFactorDirLight(u_unis.m_lightMatrix, worldPos, u_shadowMap);
+	F32 lambert = dot(l, gbuffer.m_normal);
+	F32 factor = shadowFactor * max(gbuffer.m_subsurface, lambert);
 #endif
+
+	out_color = (specC + diffC) * u_unis.m_diffuseColor * factor;
 }
 
 #pragma anki end

+ 58 - 11
shaders/glsl_cpp_common/TraditionalDeferredShading.h

@@ -11,27 +11,73 @@ ANKI_BEGIN_NAMESPACE
 
 struct DeferredPointLightUniforms
 {
-	Vec4 m_inputTexUvScaleAndOffset; // Use this to get the correct face UVs
+	// Use these to get the correct face UVs
+	Vec2 m_inputTexUvScale;
+	Vec2 m_inputTexUvOffset;
+
 	Mat4 m_invViewProjMat;
-	Vec4 m_camPosPad1;
-	Vec4 m_fbSizePad2;
+
+	Vec3 m_camPos;
+	F32 m_padding;
+
+	Vec2 m_fbSize;
+	Vec2 m_padding1;
 
 	// Light props
-	Vec4 m_posRadius; // xyz: Light pos in world space. w: The -1/radius
-	Vec4 m_diffuseColorPad1; // xyz: diff color
+	Vec3 m_position;
+	F32 m_oneOverSquareRadius; // 1/radius^2
+
+	Vec3 m_diffuseColor;
+	F32 m_padding2;
 };
 
 struct DeferredSpotLightUniforms
 {
-	Vec4 m_inputTexUvScaleAndOffset; // Use this to get the correct face UVs
+	// Use these to get the correct face UVs
+	Vec2 m_inputTexUvScale;
+	Vec2 m_inputTexUvOffset;
+
+	Mat4 m_invViewProjMat;
+
+	Vec3 m_camPos;
+	F32 m_padding;
+
+	Vec2 m_fbSize;
+	Vec2 m_padding1;
+
+	// Light props
+	Vec3 m_position;
+	F32 m_oneOverSquareRadius; // 1/radius^2
+
+	Vec3 m_diffuseColor;
+	F32 m_outerCos;
+
+	Vec3 m_lightDir;
+	F32 m_innerCos;
+};
+
+struct DeferredDirectionalLightUniforms
+{
+	// Use these to get the correct face UVs
+	Vec2 m_inputTexUvScale;
+	Vec2 m_inputTexUvOffset;
+
 	Mat4 m_invViewProjMat;
-	Vec4 m_camPosPad1;
-	Vec4 m_fbSizePad2;
+
+	Vec3 m_camPos;
+	F32 m_padding;
+
+	Vec2 m_fbSize;
+	Vec2 m_padding1;
 
 	// Light props
-	Vec4 m_posRadius; // xyz: Light pos in world space. w: The -1/radius
-	Vec4 m_diffuseColorOuterCos; // xyz: diff color, w: outer cosine of spot
-	Vec4 m_lightDirInnerCos; // xyz: light dir, w: inner cosine of spot
+	Vec3 m_diffuseColor;
+	F32 m_padding2;
+
+	Vec3 m_lightDir;
+	F32 m_padding3;
+
+	Mat4 m_lightMatrix;
 };
 
 struct DeferredVertexUniforms
@@ -43,5 +89,6 @@ 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);
+const UVec2 GBUFFER_SHADOW_ATLAS_BINDING = UVec2(0, 4);
 
 ANKI_END_NAMESPACE

+ 1 - 0
src/anki/renderer/Common.h

@@ -45,6 +45,7 @@ class DebugDrawer;
 class RenderQueue;
 class RenderableQueueElement;
 class PointLightQueueElement;
+class DirectionalLightQueueElement;
 class SpotLightQueueElement;
 class ReflectionProbeQueueElement;
 class DecalQueueElement;

+ 57 - 0
src/anki/renderer/Indirect.cpp

@@ -204,6 +204,15 @@ Error Indirect::initShadowMapping(const ConfigSet& cfg)
 	m_shadowMapping.m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
 	m_shadowMapping.m_fbDescr.bake();
 
+	// Shadow sampler
+	{
+		SamplerInitInfo inf;
+		inf.m_compareOperation = CompareOperation::LESS_EQUAL;
+		inf.m_addressing = SamplingAddressing::CLAMP;
+		inf.m_mipmapFilter = SamplingFilter::BASE;
+		m_shadowMapping.m_shadowSampler = getGrManager().newSampler(inf);
+	}
+
 	return Error::NONE;
 }
 
@@ -408,12 +417,26 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
 		m_r->getNearestSampler());
 
+	// Get shadowmap info
+	const Bool hasDirLight = probe.m_renderQueues[0]->m_directionalLight.m_uuid;
+	if(hasDirLight)
+	{
+		ANKI_ASSERT(m_ctx.m_shadowMapRt.isValid());
+
+		rgraphCtx.bindTextureAndSampler(GBUFFER_SHADOW_ATLAS_BINDING.x(),
+			GBUFFER_SHADOW_ATLAS_BINDING.y(),
+			m_ctx.m_shadowMapRt,
+			TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+			m_shadowMapping.m_shadowSampler);
+	}
+
 	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),
+		(hasDirLight) ? &probe.m_renderQueues[faceIdx]->m_directionalLight : nullptr,
 		rqueue.m_pointLights,
 		rqueue.m_spotLights,
 		cmdb);
@@ -565,10 +588,35 @@ void Indirect::populateRenderGraph(RenderingContext& rctx)
 	if(probeToUpdate->m_renderQueues[0]->m_directionalLight.m_uuid
 		&& probeToUpdate->m_renderQueues[0]->m_directionalLight.m_shadowCascadeCount > 0)
 	{
+		// Update light matrices
 		for(U i = 0; i < 6; ++i)
 		{
 			ANKI_ASSERT(probeToUpdate->m_renderQueues[i]->m_directionalLight.m_uuid
 						&& probeToUpdate->m_renderQueues[i]->m_directionalLight.m_shadowCascadeCount == 1);
+
+			const F32 xScale = 1.0f / 6.0f;
+			const F32 yScale = 1.0f;
+			const F32 xOffset = F32(i) * (1.0f / 6.0f);
+			const F32 yOffset = 0.0f;
+			const Mat4 atlasMtx(xScale,
+				0.0f,
+				0.0f,
+				xOffset,
+				0.0f,
+				yScale,
+				0.0f,
+				yOffset,
+				0.0f,
+				0.0f,
+				1.0f,
+				0.0f,
+				0.0f,
+				0.0f,
+				0.0f,
+				1.0f);
+
+			Mat4& lightMat = probeToUpdate->m_renderQueues[i]->m_directionalLight.m_textureMatrices[0];
+			lightMat = atlasMtx * lightMat;
 		}
 
 		// RT
@@ -629,6 +677,11 @@ void Indirect::populateRenderGraph(RenderingContext& rctx)
 			pass.newDependency({m_ctx.m_gbufferDepthRt,
 				TextureUsageBit::SAMPLED_FRAGMENT,
 				TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
+
+			if(m_ctx.m_shadowMapRt.isValid())
+			{
+				pass.newDependency({m_ctx.m_shadowMapRt, TextureUsageBit::SAMPLED_FRAGMENT});
+			}
 		}
 	}
 
@@ -830,6 +883,8 @@ Bool Indirect::findBestCacheEntry(U64 probeUuid, U32& cacheEntryIdxAllocated, Bo
 
 void Indirect::runShadowMapping(CommandBufferPtr& cmdb)
 {
+	cmdb->setPolygonOffset(7.0f, 5.0f);
+
 	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
 		ANKI_ASSERT(m_ctx.m_probe);
@@ -858,6 +913,8 @@ void Indirect::runShadowMapping(CommandBufferPtr& cmdb)
 			cascadeRenderQueue.m_renderables.getBegin(),
 			cascadeRenderQueue.m_renderables.getEnd());
 	}
+
+	cmdb->setPolygonOffset(0.0f, 0.0f);
 }
 
 } // end namespace anki

+ 1 - 0
src/anki/renderer/Indirect.h

@@ -105,6 +105,7 @@ private:
 	public:
 		RenderTargetDescription m_rtDescr;
 		FramebufferDescription m_fbDescr;
+		SamplerPtr m_shadowSampler;
 	} m_shadowMapping;
 
 	class CacheEntry

+ 48 - 14
src/anki/renderer/TraditionalDeferredShading.cpp

@@ -38,6 +38,10 @@ Error TraditionalDeferredLightShading::init()
 		mutators[0].m_value = 1;
 		m_lightProg->getOrCreateVariant(mutators.get(), variant);
 		m_slightGrProg = variant->getProgram();
+
+		mutators[0].m_value = 2;
+		m_lightProg->getOrCreateVariant(mutators.get(), variant);
+		m_dirLightGrProg = variant->getProgram();
 	}
 
 	// Init meshes
@@ -78,6 +82,7 @@ void TraditionalDeferredLightShading::drawLights(const Mat4& vpMat,
 	const UVec4& viewport,
 	const Vec2& gbufferTexCoordsMin,
 	const Vec2& gbufferTexCoordsMax,
+	DirectionalLightQueueElement* directionalLight,
 	ConstWeakArray<PointLightQueueElement> plights,
 	ConstWeakArray<SpotLightQueueElement> slights,
 	CommandBufferPtr& cmdb)
@@ -93,9 +98,34 @@ void TraditionalDeferredLightShading::drawLights(const Mat4& vpMat,
 
 	// 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());
 
+	// Dir light
+	if(directionalLight)
+	{
+		ANKI_ASSERT(directionalLight->m_uuid && directionalLight->m_shadowCascadeCount == 1);
+
+		cmdb->bindShaderProgram(m_dirLightGrProg);
+
+		DeferredDirectionalLightUniforms* unis = allocateAndBindUniforms<DeferredDirectionalLightUniforms*>(
+			sizeof(DeferredDirectionalLightUniforms), cmdb, 0, 1);
+
+		unis->m_inputTexUvScale = inputTexUvScaleAndOffset.xy();
+		unis->m_inputTexUvOffset = inputTexUvScaleAndOffset.zw();
+		unis->m_invViewProjMat = invViewProjMat;
+		unis->m_camPos = cameraPosWSpace.xyz();
+		unis->m_fbSize = Vec2(viewport.z(), viewport.w());
+
+		unis->m_diffuseColor = directionalLight->m_diffuseColor;
+		unis->m_lightDir = directionalLight->m_direction;
+		unis->m_lightMatrix = directionalLight->m_textureMatrices[0];
+
+		drawQuad(cmdb);
+	}
+
+	// Set other light state
+	cmdb->setCullMode(FaceSelectionBit::FRONT);
+
 	// Do point lights
 	U32 indexCount;
 	bindVertexIndexBuffers(m_plightMesh, cmdb, indexCount);
@@ -114,12 +144,14 @@ void TraditionalDeferredLightShading::drawLights(const Mat4& vpMat,
 		DeferredPointLightUniforms* light =
 			allocateAndBindUniforms<DeferredPointLightUniforms*>(sizeof(DeferredPointLightUniforms), cmdb, 0, 1);
 
-		light->m_inputTexUvScaleAndOffset = inputTexUvScaleAndOffset;
+		light->m_inputTexUvScale = inputTexUvScaleAndOffset.xy();
+		light->m_inputTexUvOffset = inputTexUvScaleAndOffset.zw();
 		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();
+		light->m_camPos = cameraPosWSpace.xyz();
+		light->m_fbSize = Vec2(viewport.z(), viewport.w());
+		light->m_position = plightEl.m_worldPosition;
+		light->m_oneOverSquareRadius = 1.0f / (plightEl.m_radius * plightEl.m_radius);
+		light->m_diffuseColor = plightEl.m_diffuseColor;
 
 		// Draw
 		cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
@@ -153,18 +185,20 @@ void TraditionalDeferredLightShading::drawLights(const Mat4& vpMat,
 		DeferredSpotLightUniforms* light =
 			allocateAndBindUniforms<DeferredSpotLightUniforms*>(sizeof(DeferredSpotLightUniforms), cmdb, 0, 1);
 
-		light->m_inputTexUvScaleAndOffset = inputTexUvScaleAndOffset;
+		light->m_inputTexUvScale = inputTexUvScaleAndOffset.xy();
+		light->m_inputTexUvOffset = inputTexUvScaleAndOffset.zw();
 		light->m_invViewProjMat = invViewProjMat;
-		light->m_camPosPad1 = cameraPosWSpace.xyz0();
-		light->m_fbSizePad2 = Vec4(viewport.z(), viewport.w(), 0.0f, 0.0f);
+		light->m_camPos = cameraPosWSpace.xyz();
+		light->m_fbSize = Vec2(viewport.z(), viewport.w());
 
-		light->m_posRadius = Vec4(splightEl.m_worldTransform.getTranslationPart().xyz(),
-			1.0f / (splightEl.m_distance * splightEl.m_distance));
+		light->m_position = splightEl.m_worldTransform.getTranslationPart().xyz();
+		light->m_oneOverSquareRadius = 1.0f / (splightEl.m_distance * splightEl.m_distance);
 
-		light->m_diffuseColorOuterCos = Vec4(splightEl.m_diffuseColor, cos(splightEl.m_outerAngle / 2.0f));
+		light->m_diffuseColor = splightEl.m_diffuseColor;
+		light->m_outerCos = cos(splightEl.m_outerAngle / 2.0f);
 
-		Vec3 lightDir = -splightEl.m_worldTransform.getZAxis().xyz();
-		light->m_lightDirInnerCos = Vec4(lightDir, cos(splightEl.m_innerAngle / 2.0f));
+		light->m_lightDir = -splightEl.m_worldTransform.getZAxis().xyz();
+		light->m_innerCos = cos(splightEl.m_innerAngle / 2.0f);
 
 		// Draw
 		cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);

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

@@ -31,6 +31,7 @@ public:
 		const UVec4& viewport,
 		const Vec2& gbufferTexCoordsMin,
 		const Vec2& gbufferTexCoordsMax,
+		DirectionalLightQueueElement* directionalLight,
 		ConstWeakArray<PointLightQueueElement> plights,
 		ConstWeakArray<SpotLightQueueElement> slights,
 		CommandBufferPtr& cmdb);
@@ -39,6 +40,7 @@ private:
 	ShaderProgramResourcePtr m_lightProg;
 	ShaderProgramPtr m_plightGrProg;
 	ShaderProgramPtr m_slightGrProg;
+	ShaderProgramPtr m_dirLightGrProg;
 
 	/// @name Meshes of light volumes.
 	/// @{