Here’s the draw code and shader. It’s for a spot light, but the shader is kind of in shambles now because I had to tear it apart to identify this issue. I can’t imagine the issue is with this code, unless there’s some weird bug in the shader that causes it to compile incorrectly rather than fail to compile at all. Because, as I said the functionality changes when I simply reorder the variable definitions at the top. The shader code could certainly be simplified from its current state, but seeing as it’s a potential bug with the compiler, I haven’t messed with it since I’ve seen this issue.
Draw code:
private void DrawSpotLights(IDictionary<Entity, IComponent> spotLights, IDictionary<Entity, IComponent> positions, RenderComponent renderComponent, CameraComponent camera, Matrix inverseViewProjection)
{
spotLightEffect.View = camera.View;
spotLightEffect.Projection = camera.Projection;
spotLightEffect.InverseViewProjection = inverseViewProjection;
spotLightEffect.CameraPosition = camera.Position;
//spotLightEffect.GBufferTexture0 = renderComponent.GBufferTargets[0].RenderTarget;
spotLightEffect.GBufferTexture1 = renderComponent.GBufferTargets[1].RenderTarget;
spotLightEffect.GBufferTexture2 = renderComponent.GBufferTargets[2].RenderTarget;
spotLightEffect.GBufferTextureSize = renderComponent.GBufferTextureSize;
renderComponent.GraphicsDevice.SetVertexBuffer(spotLightGeometry.VertexBuffer, spotLightGeometry.VertexOffset);
renderComponent.GraphicsDevice.Indices = spotLightGeometry.IndexBuffer;
foreach (var entityLight in spotLights)
{
SpotLightComponent light = (SpotLightComponent)entityLight.Value;
PositionComponent position = (PositionComponent)positions[entityLight.Key];
float lightAngleCos = light.GetLightAngleCos();
spotLightEffect.World = light.World;
spotLightEffect.LightViewProjection = light.View * light.Projection;
spotLightEffect.LightPosition = position.World.Translation;
spotLightEffect.LightColor = light.Color;
spotLightEffect.LightIntensity = light.Intensity;
spotLightEffect.LightDirection = Vector3.Down;//position.World.Forward;
spotLightEffect.LightAngleCos = lightAngleCos;
spotLightEffect.LightHeight = light.FarPlane;
spotLightEffect.Shadows = light.IsWithShadows;
spotLightEffect.ShadowMapSize = light.ShadowMapResoloution;
spotLightEffect.DepthPrecision = light.FarPlane;
spotLightEffect.DepthBias = light.DepthBias;
spotLightEffect.AttenuationTexture = light.AttenuationTexture;
spotLightEffect.ShadowMap = light.ShadowMap;
// Calculate cull mode
Vector3 L = camera.Position - position.World.Translation;
float SL = System.Math.Abs(Vector3.Dot(L, position.World.Forward));
// If SL is within the LightAngle, draw the back faces, otherwise draw the front faces
if (SL < lightAngleCos)
{
renderComponent.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
}
else
{
renderComponent.GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
}
spotLightEffect.Apply();
// Draw
renderComponent.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, spotLightGeometry.StartIndex, spotLightGeometry.PrimitiveCount);
}
// Restore cull mode
renderComponent.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
}
Shader code
float4x4 World;
float4x4 View;
float4x4 Projection;
float4x4 InverseViewProjection;
float3 CameraPosition;
float4x4 LightViewProjection;
float3 LightPosition;
float4 LightColor;
float LightIntensity;
float3 S;
float LightAngleCos;
float LightHeight;
float2 GBufferTextureSize;
bool Shadows;
float ShadowMapSize;
float DepthPrecision;
// DepthBias for the Shadowing... (1.0f / 2000.0f)
float DepthBias;
// GBuffer Texture0
texture GBufferTexture0;
sampler GBuffer0 = sampler_state
{
texture = <GBufferTexture0>;
MINFILTER = LINEAR;
MAGFILTER = LINEAR;
MIPFILTER = LINEAR;
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
};
// GBuffer Texture1
texture GBufferTexture1;
sampler GBuffer1 = sampler_state
{
texture = <GBufferTexture1>;
MINFILTER = LINEAR;
MAGFILTER = LINEAR;
MIPFILTER = LINEAR;
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
};
// GBuffer Texture2
texture GBufferTexture2;
sampler GBuffer2 = sampler_state
{
texture = <GBufferTexture2>;
MINFILTER = POINT;
MAGFILTER = POINT;
MIPFILTER = POINT;
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
};
// Attenuation Cookie
texture AttenuationTexture;
sampler Cookie = sampler_state
{
texture = <AttenuationTexture>;
MINFILTER = LINEAR;
MAGFILTER = LINEAR;
MIPFILTER = LINEAR;
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
};
// ShadowMap
texture ShadowMapTexture;
sampler ShadowMap = sampler_state
{
texture = <ShadowMapTexture>;
MINFILTER = POINT;
MAGFILTER = POINT;
MIPFILTER = POINT;
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
};
struct VSI
{
float4 Position : POSITION0;
};
struct VSO
{
float4 Position : POSITION0;
float4 ScreenPosition : TEXCOORD0;
};
VSO VS(VSI input)
{
VSO output;
// Transform Position
float4 worldPosition = mul(input.Position, World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
// Pass to ScreenPosition
output.ScreenPosition = output.Position;
return output;
}
// Manually Linear Sample
float4 manualSample(sampler Sampler, float2 UV, float2 textureSize)
{
float2 texelpos = textureSize * UV;
float2 lerps = frac(texelpos);
float2 texelSize = 1.0 / textureSize;
float4 sourcevals[4];
sourcevals[0] = tex2D(Sampler, UV);
sourcevals[1] = tex2D(Sampler, UV + float2(texelSize.x, 0));
sourcevals[2] = tex2D(Sampler, UV + float2(0, texelSize.y));
sourcevals[3] = tex2D(Sampler, UV + texelSize);
float4 interpolated = lerp(lerp(sourcevals[0], sourcevals[1], lerps.x), lerp(sourcevals[2], sourcevals[3], lerps.x), lerps.y);
return interpolated;
}
// Phong Shader
float4 Phong(float3 Position, float3 N, float radialAttenuation, float SpecularIntensity, float SpecularPower)
{
// Calculate Light vector
float3 L = LightPosition.xyz - Position.xyz;
// Calculate height Attenuation
float heightAttenuation = saturate(2.0f - length(L) / (LightHeight / 2));
// Calculate total Attenuation
float Attenuation = min(radialAttenuation, heightAttenuation) + 1;
// Now Normalize the Light
L = normalize(L);
// Calculate L.S
float SL = dot(L, S);
// No asymmetric returns in HLSL, so work around with this
float4 Shading = 0;
// If this pixel is in the SpotLights Cone
//if(SL <= LightAngleCos)
//{
// Calculate Reflection Vector
float3 R = normalize(reflect(-L, N));
// Calculate Eye Vector
float3 E = normalize(CameraPosition - Position.xyz);
// Calculate N.L
float NL = dot(N, L);
// Calculate Diffuse
float3 Diffuse = NL * LightColor.xyz;
// Calculate Specular
float Specular = SpecularIntensity * pow(saturate(dot(R, E)), SpecularPower);
// Calculate Final Product
Shading = Attenuation * LightIntensity * float4(Diffuse.rgb, Specular);
//}
//Return Shading Value
//return Shading;
return Shading * saturate(sign(50 - LightPosition.y)) + (SL + LightAngleCos) * 0.001;// *saturate(sign(LightAngleCos - SL) + 1);
}
// Decoding of GBuffer Normals
float3 decode(float3 enc)
{
return (2.0f * enc.xyz- 1.0f);
}
// Decode Color Vector to Float Value for shadowMap
float RGBADecode(float4 value)
{
const float4 bits = float4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1);
return dot(value.xyzw , bits);
}
float4 PS(VSO input) : COLOR0
{
// Get Screen Position
input.ScreenPosition.xy /= input.ScreenPosition.w;
// Calculate UV from ScreenPosition
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1);// -float2(1.0f / GBufferTextureSize.xy);
// Get All Data from Normal part of the GBuffer
half4 encodedNormal = tex2D(GBuffer1, UV);
// Decode Normal
half3 Normal = decode(encodedNormal.xyz);
// Get Specular Intensity from GBuffer
float SpecularIntensity = encodedNormal.w;
// Get Specular Power from GBuffer
float SpecularPower = 128;// encodedNormal.w * 255;
// Get Depth from GBuffer
float Depth = tex2D(GBuffer2, UV).x;// manualSample(GBuffer2, UV, GBufferTextureSize).x;
// Make Position in Homogenous Space using current ScreenSpace coordinates and the Depth from the GBuffer
float4 Position = 1.0f;
Position.xy = input.ScreenPosition.xy;
Position.z = Depth;
// Transform Position from Homogenous Space to World Space
Position = mul(Position, InverseViewProjection);
Position /= Position.w;
// Calculate Homogenous Position with respect to light
float4 LightScreenPos = mul(Position, LightViewProjection);
LightScreenPos /= LightScreenPos.w;
// Calculate Projected UV from Light POV
float2 LUV = 0.5f * (float2(LightScreenPos.x, -LightScreenPos.y) + 1);
// Load the Projected Depth from the Shadow Map, do manual linear filtering
float lZ = manualSample(ShadowMap, LUV, float2(ShadowMapSize, ShadowMapSize)).r;
// Get Attenuation factor from cookie
float Attenuation = tex2D(Cookie, LUV).r + 1;
// Assymetric Workaround...
float ShadowFactor = 1;
//// If Shadowing is on then get the Shadow Factor
//if(Shadows)
//{
// // Calculate distance to the light
// float len = max(0.01f, length(LightPosition - Position.xyz)) / DepthPrecision;
// // Calculate the Shadow Factor
// ShadowFactor = (lZ * exp(-(DepthPrecision * 0.5f) * (len - DepthBias)));
//}
// Return Phong Shaded Value Modulated by Shadows if Shadowing is on
return ShadowFactor * Phong(Position.xyz, Normal, Attenuation, SpecularIntensity, SpecularPower);
}
technique Default
{
pass p0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}