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 "Object" | |
#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; | |
// view vector, tangent space | |
float3 view: TEXCOORD1; | |
// light vectors, unnormalized, tangent space | |
// l0.x l1.x l2.x l3.x | |
// l0.y l1.y l2.y l3.y | |
// l0.z l1.z l2.z l3.z | |
// twice. | |
float4 lights[6]: TEXCOORD2; | |
}; | |
uniform float4x4 world_view_inverse: WorldViewInverse; | |
uniform float4x4 world_view_projection: WorldViewProjection; | |
// light position, object space | |
static float3 light_positions[8] = | |
{ | |
Lamp1Pos, Lamp2Pos, Lamp3Pos, Lamp4Pos, | |
Lamp5Pos, Lamp6Pos, Lamp7Pos, Lamp8Pos | |
}; | |
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; | |
// view vector | |
float3 eye_pos = mul(float4(0, 0, 0, 1), world_view_inverse); | |
float3 view = normalize(I.position - eye_pos); | |
// transform to tangent space | |
O.view.x = dot(view, I.tangent); | |
O.view.y = dot(view, I.bitangent); | |
O.view.z = dot(view, I.normal); | |
// note: this loop can be optimized if application feeds light data in SoA | |
// order, we can do SoA-style calculation here also. It's not quite easy to | |
// do in FX Composer, I guess, and anyway this is left as is for clarity. | |
for (int i = 0; i < 2; ++i) | |
{ | |
// compute light vectors in tangent space | |
float3 light_vectors[4]; | |
for (int j = 0; j < 4; ++j) | |
{ | |
float3 l = light_positions[i*4 + j] - I.position; | |
light_vectors[j].x = dot(l, I.tangent); | |
light_vectors[j].y = dot(l, I.bitangent); | |
light_vectors[j].z = dot(l, I.normal); | |
} | |
// splat in SoA order | |
O.lights[i*3 + 0] = float4(light_vectors[0].x, light_vectors[1].x, light_vectors[2].x, light_vectors[3].x); | |
O.lights[i*3 + 1] = float4(light_vectors[0].y, light_vectors[1].y, light_vectors[2].y, light_vectors[3].y); | |
O.lights[i*3 + 2] = float4(light_vectors[0].z, light_vectors[1].z, light_vectors[2].z, light_vectors[3].z); | |
} | |
return O; | |
} | |
// 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 | |
uniform 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) | |
{ | |
// compute squared lengths in parallel | |
float4 squared_lengths = 0; | |
for (int i = 0; i < 3; ++i) | |
{ | |
squared_lengths += pow(I.lights[light_bucket_index * 3 + i], 2); | |
} | |
// compute NdotL in parallel | |
float4 NdotL = 0; | |
for (int i = 0; i < 3; ++i) | |
{ | |
NdotL += I.lights[light_bucket_index * 3 + i] * normal[i]; | |
} | |
// compute RdotL in parallel | |
float3 reflected = reflect(I.view, normal); | |
float4 RdotL = 0; | |
for (int i = 0; i < 3; ++i) | |
{ | |
RdotL += I.lights[light_bucket_index * 3 + i] * reflected[i]; | |
} | |
// correct NdotL and RdotL | |
float4 correction = 1 / sqrt(squared_lengths); | |
NdotL = saturate(NdotL * correction); | |
RdotL = saturate(RdotL * correction); | |
// attenuation | |
// 1 - d^2 / r^2 for diffuse | |
float4 atten = squared_lengths * 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 | |
{ | |
float3 normal = tex2D(normal_map, I.texcoord).xyz * 2 - 1; | |
LightingData lights[] = | |
{ | |
computeLighting4(I, 0, normal), | |
computeLighting4(I, 1, normal) | |
}; | |
// final diffuse color | |
float3 diffuse = 0; | |
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_0 ps_main(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment