Skip to content

Instantly share code, notes, and snippets.

@zeux
Last active August 29, 2015 14:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeux/01d4555fb000fa25bc3c to your computer and use it in GitHub Desktop.
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.
#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