Last active
August 29, 2015 14:11
-
-
Save zeux/01d4555fb000fa25bc3c to your computer and use it in GitHub Desktop.
Shader source for blog post; this shader computes lighting for 8 point lights and tries hard to fit into 64 instructions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define LIGHT_SPACE "World" | |
#include "bindings.h" | |
struct VS_IN | |
{ | |
float4 position: POSITION; | |
float2 texcoord: TEXCOORD; | |
float3 tangent: TANGENT; | |
float3 bitangent: BINORMAL; | |
float3 normal: NORMAL; | |
}; | |
struct VS_OUT | |
{ | |
float2 texcoord: TEXCOORD0; | |
// position (world space) | |
float3 position: TEXCOORD1; | |
// view vector (world space) | |
float3 view: TEXCOORD2; | |
// tangent space->world space matrix | |
// pre-multiplied by (2 0 0 -1) | |
// (0 2 0 -1) | |
// (0 0 2 -1) | |
// (perform [0,1] -> [-1,1] expansion | |
float3x4 TBN: TEXCOORD3; | |
}; | |
uniform float4x4 world: World; | |
uniform float4x4 world_view_projection: WorldViewProjection; | |
uniform float4x4 view_inverse: ViewInverse; | |
VS_OUT vs_main(VS_IN I, out float4 clippos: POSITION) | |
{ | |
clippos = mul(I.position, world_view_projection); | |
VS_OUT O; | |
O.texcoord = I.texcoord; | |
O.position = mul(I.position, world); | |
float3 eye_pos = mul(float4(0, 0, 0, 1), view_inverse); | |
O.view = normalize(O.position - eye_pos); | |
// world space TBN basis | |
float3 tangent = mul(world, float4(I.tangent, 0)); | |
float3 bitangent = mul(world, float4(I.bitangent, 0)); | |
float3 normal = mul(world, float4(I.normal, 0)); | |
// tangent -> world space matrix | |
O.TBN[0] = float4(tangent.x, bitangent.x, normal.x, 0); | |
O.TBN[1] = float4(tangent.y, bitangent.y, normal.y, 0); | |
O.TBN[2] = float4(tangent.z, bitangent.z, normal.z, 0); | |
// normal [0, 1] -> [-1, 1] expansion | |
O.TBN = mul(O.TBN, float4x4(2, 0, 0, -1, | |
0, 2, 0, -1, | |
0, 0, 2, -1, | |
0, 0, 0, 1)); | |
return O; | |
} | |
// light position, world space | |
static float3 light_positions[8] = | |
{ | |
Lamp1Pos, Lamp2Pos, Lamp3Pos, Lamp4Pos, | |
Lamp5Pos, Lamp6Pos, Lamp7Pos, Lamp8Pos | |
}; | |
// light colors | |
static float3 light_colors[8] = | |
{ | |
Lamp1Col, Lamp2Col, Lamp3Col, Lamp4Col, | |
Lamp5Col, Lamp6Col, Lamp7Col, Lamp8Col | |
}; | |
// 1 / light radius^2 | |
uniform float4 light_radius_inv[2] = | |
{ | |
(1 / 0.25).xxxx, | |
(1 / 0.25).xxxx | |
}; | |
// specular power A, B constant | |
// http://www.gamasutra.com/features/20020801/beaudoin_01.htm | |
// m = 2, n = 18 => A = 6.645, B = -5.645 | |
static const float2 specular_ab = float2(6.645, -5.645); | |
texture diffuse_texture; | |
uniform sampler2D diffuse_map = TRILINEAR_SAMPLER(diffuse_texture); | |
texture normal_texture; | |
uniform sampler2D normal_map = TRILINEAR_SAMPLER(normal_texture); | |
texture specular_texture; | |
uniform sampler2D specular_map = TRILINEAR_SAMPLER(specular_texture); | |
float4 compute_specular_power(float4 v) | |
{ | |
// originally: pow(v, N) | |
// x^N is roughly equal to (max(Ax+B, 0))^2 | |
// A,B depend on N | |
float4 t = saturate(specular_ab.x * v + specular_ab.y); | |
return t * t; | |
} | |
struct LightingData | |
{ | |
float4 diffuse; | |
float4 specular; | |
}; | |
LightingData computeLighting4(VS_OUT I, int light_bucket_index, float3 normal) | |
{ | |
float3 reflected = reflect(I.view, normal); | |
float4 NdotL; | |
float4 RdotL; | |
float4 squared_distances; | |
for (int i = 0; i < 4; ++i) | |
{ | |
float3 light_vector = light_positions[i + light_bucket_index * 4] - I.position; | |
float3 light_direction = normalize(light_vector); | |
NdotL[i] = saturate(dot(light_direction, normal)); | |
RdotL[i] = saturate(dot(light_direction, reflected)); | |
squared_distances[i] = dot(light_vector, light_vector); | |
} | |
// attenuation | |
// 1 - d^2 / r^2 for diffuse | |
float4 atten = squared_distances * light_radius_inv[light_bucket_index]; | |
// modulate diffuse by attenuation | |
NdotL = saturate(NdotL - NdotL * atten); | |
// specular | |
float4 spec = compute_specular_power(RdotL); | |
LightingData data; | |
data.diffuse = NdotL; | |
data.specular = spec; | |
return data; | |
} | |
float4 ps_main(VS_OUT I): COLOR | |
{ | |
float4 normal_biased = tex2D(normal_map, I.texcoord); | |
float3 normal = mul(I.TBN, normal_biased); | |
LightingData lights[2] = | |
{ | |
computeLighting4(I, 0, normal), | |
computeLighting4(I, 1, normal) | |
}; | |
// final diffuse color | |
float3 diffuse = dot(lights[0].diffuse + lights[1].diffuse, 1).xxx; | |
for (int i = 0; i < 8; ++i) | |
{ | |
// diffuse += lights[i/4].diffuse[i%4] * light_colors[i]; | |
} | |
// final specular color | |
float4 specular_color = tex2D(specular_map, I.texcoord); | |
float3 specular = specular_color.rgb * saturate(dot(lights[0].specular + lights[1].specular, 1)); | |
// final color | |
float4 albedo = tex2D(diffuse_map, I.texcoord); | |
return float4(albedo.rgb * diffuse + specular, albedo.a); | |
} | |
technique t0 | |
{ | |
pass p0 | |
{ | |
VertexShader = compile vs_1_1 vs_main(); | |
PixelShader = compile ps_2_a ps_main(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment