#include "Macros.fxh" //from FNA // Effect applies normalmapped lighting to a 2D sprite. DECLARE_TEXTURE(Texture, 0); DECLARE_TEXTURE(Normal, 1); float3 AmbientColor; float3 PointLightPositions[8]; float3 PointLightColors[8]; float3 DirectionalLightDirection; float3 DirectionalLightColor; float4 UVOffsetAndDimensions; float4x4 WorldInverseTranspose; float4x4 World; float4x4 WorldViewProjection; struct VertexShaderInput { float4 Position : POSITION; float3 Normal : NORMAL; float2 TexCoord : TEXCOORD0; }; struct PixelShaderInput { float4 Position : SV_Position; float2 TexCoord : TEXCOORD0; float3 NormalWS : TEXCOORD1; float3 PositionWS : TEXCOORD2; }; PixelShaderInput main_vs(VertexShaderInput input) { PixelShaderInput output; output.Position = mul(input.Position, WorldViewProjection); output.NormalWS = normalize(mul(input.Normal, (float3x3)WorldInverseTranspose)); output.PositionWS = mul(input.Position, World).xyz; float2 texCoord; texCoord.x = (input.TexCoord.x * UVOffsetAndDimensions.z) + UVOffsetAndDimensions.x; texCoord.y = (input.TexCoord.y * UVOffsetAndDimensions.w) + UVOffsetAndDimensions.y; output.TexCoord = texCoord; return output; } // Easy trick to get tangent-normals to world-space to keep PBR code simplified. float3 GetNormalFromMap(float3 worldPos, float2 texCoords, float3 normal) { float3 tangentNormal = SAMPLE_TEXTURE(Normal, texCoords).xyz * 2.0 - 1.0; float3 Q1 = ddx(worldPos); float3 Q2 = ddy(worldPos); float2 st1 = ddx(texCoords); float2 st2 = ddy(texCoords); float3 N = normalize(normal); float3 T = normalize(Q1*st2.y - Q2*st1.y); float3 B = -normalize(cross(N, T)); float3x3 TBN = float3x3(T, B, N); return normalize(mul(tangentNormal, TBN)); } float4 LightColor(float3 worldPosition, float3 worldNormal) { float3 lightColor = float3(0.0, 0.0, 0.0); // point lights for (int i = 0; i < 8; i++) { float3 lightVec = PointLightPositions[i] - worldPosition; float distance = length(lightVec); float3 lightDir = normalize(lightVec); float diffuse = max(dot(worldNormal, lightDir), 0.0); float3 attenuation = 1.0 / (distance * distance); lightColor += diffuse * attenuation * PointLightColors[i]; } // directional light float directionalDiffuse = max(dot(worldNormal, DirectionalLightDirection), 0.0); lightColor += directionalDiffuse * DirectionalLightColor; // ambient light lightColor += AmbientColor; return float4(lightColor, 1.0); } float4 WithoutNormalMap(PixelShaderInput input) : COLOR0 { float4 tex = SAMPLE_TEXTURE(Texture, input.TexCoord); if (tex.a == 0.0) { discard; } float3 normalWS = normalize(input.NormalWS); return tex * LightColor(input.PositionWS, normalWS); } float4 WithNormalMap(PixelShaderInput input) : COLOR0 { float4 tex = SAMPLE_TEXTURE(Texture, input.TexCoord); if (tex.a == 0.0) { discard; } float3 normalWS = GetNormalFromMap(input.PositionWS, input.TexCoord, input.NormalWS); return tex * LightColor(input.PositionWS, normalWS); } PixelShader PSArray[2] = { compile ps_3_0 WithoutNormalMap(), compile ps_3_0 WithNormalMap() }; int PSIndices[2] = { 0, 1 }; int ShaderIndex = 0; Technique DiffuseLitSprite { pass { VertexShader = compile vs_3_0 main_vs(); PixelShader = (PSArray[PSIndices[ShaderIndex]]); } }