OK after a great deal of struggle i have completed a 2-pass diff+spec point light shader, complete with skill-based light positions and colors, attenuation maps, corrected blinn specular, and diffuse/spec gradation map. I have made some changes from the Wolfgang Engle version..but you will need the diffspec and pointlight maps from his
RacorX10 example. The only improvement currently needed is a way to pass in multiple light sources at once.. so we can blend lights together.. as of now i have no idea how to do this, but i will work on it.. any input would be appreciated. The shader is ready now for most needs however..try constructing your entire worlds out of models, using planes for floor, cieling walls.. etc.. you will see how impressive this kind of lighting can be.
here is the shader:
Code:
bmap pointlight = <pointlight.tga>;
bmap diffspec = <diffspec.tga>;
material mat_diffspecpoint
{
skin1 = pointlight;
skin2 = diffspec;
event = mat_diffspecpoint_init;
flags = tangent;
effect =
"
texture mtlSkin1; //attenuation map
texture mtlSkin2; //diffspec map
texture entSkin1;
texture entSkin2;
matrix matWorldViewProj;
matrix matWorld;
vector vecViewPos;
vector vecSkill41; //light position
vector mtlSkill1; //light color
technique specpoint
{
pass p0
{
//load matrices
VertexShaderConstant[0] = <matWorld>; //World Matrix
VertexShaderConstant[8] = <matWorldViewProj>; //World*View*Proj Matrix
//light vector
VertexShaderConstant[12] = <vecSkill41>;
//Camera Information
VertexShaderConstant[16] = <vecViewPos>;
//range constant
VertexShaderConstant[33] = (0.005f, 0.55f, 0.0f, 0.0f);
//light color
VertexShaderConstant[40] = <mtlSkill1>;
Texture[0] = <mtlSkin1>; //pointlight attenuation
Texture[1] = <mtlSkin1>; //pointlight alpha
AddressU[0] = Clamp; // don't wrap around edges
AddressV[0] = Clamp;
AddressU[1] = Clamp; // don't wrap around edges
AddressV[1] = Clamp;
zWriteEnable=true; // enables writing to the z-buffer
AlphaBlendEnable=false; // disables alpha blending
VertexShader=
decl
{
stream 0;
float v0[3]; //position
float v3[3]; //normal
float v7[3]; //uv
float v8[3]; //tangent
}
asm
{
vs.1.1
; position in clip space
m4x4 oPos, v0, c8
; position in world space
m4x4 r2, v0, c0
; get light vector
add r10, r2, -c12
; Divide each component by the range value
mul r10, r10, c33.x
; multiply with 0.5 and add 0.5
mad r10, r10, c33.yyyy, c33.yyyy
; map the x and y components into the first texture
mov oT0.xy, r10.xy
; z-component v0
mov oT1.x, r10.z
};
PixelShader=
asm
{
ps.1.1
tex t0
tex t1
add r0, 1-t0, -t1.a
};
}
pass p1
{
//load matrices
VertexShaderConstant[0] = <matWorld>; //World Matrix
VertexShaderConstant[8] = <matWorldViewProj>; //World*View*Proj Matrix
//light vector
VertexShaderConstant[12] = <vecSkill41>;
//Camera Information
VertexShaderConstant[16] = <vecViewPos>;
//range constant
VertexShaderConstant[33] = (1.05f, 0.5f, 0f, 0.0f);
//light color
VertexShaderConstant[40] = <mtlSkill1>;
Texture[0] = <entSkin1>;
Texture[1] = <entSkin2>;
Texture[3] = <mtlSkin2>;
AddressU[3] = Clamp; // don't wrap around edges
AddressV[3] = Clamp;
zWriteEnable=true; // enables writing to the z-buffer
AlphaBlendEnable=true; // enable alpha blending
SrcBlend = zero;
DestBlend = SrcColor;
VertexShader=
decl
{
stream 0;
float v0[3]; //position
float v3[3]; //normal
float v7[3]; //uv
float v8[3]; //tangent
}
asm
{
vs.1.1
; position in clip space
m4x4 oPos, v0, c8
m3x3 r3, v8, c0 ; generate tangent U
m3x3 r5, v3, c0 ; generate normal W
; Cross product
; generate binormal V
mul r0, -r3.zxyw, r5.yzxw ;
mad r4, -r3.yzxw, r5.zxyw,-r0 ;
; position in world space
m4x4 r2, v0, c0
; get light vector
add r10, r2, -c12
; normalize light vector
; always first normalize than transform
dp3 r11.x, r10.xyz, r10.xyz
rsq r11.xyz, r11.x
mul r10.xyz, r10.xyz, r11.xyz
;transform the light vector with U, V, W
dp3 oT2.x, r3, -r10
dp3 oT2.y, r4, -r10
dp3 oT2.z, r5, -r10
;position in world space
m4x4 r6, v0, c0
; get a vector toward the camera/eye V
add r6, r6, -c16
; normalize eye vector V
dp3 r8.x, r6.xyz, r6.xyz
rsq r8.xyz, r8.x
mul r6.xyz, r6.xyz, r8.xyz
add r6, r6, r10 ;
mul r6,r6,c33.y ;V+L / 2 --half vector
; normalize half angle
dp3 r8.x, r6.xyz, r6.xyz
rsq r8.xyz, r8.x
mul r6.xyz, r6.xyz, r8.xyz
//raise the specular power
mul r6, r6, c33.x
; transform the half angle vector into texture space
dp3 oT3.x,r3,-r6
dp3 oT3.y,r4,-r6
dp3 oT3.z,r5,-r6
;output uvs
mov oT0.xy, v7.xy
mov oT1.xy, v7.xy
;output light color
mov oD0,c40
};
PixelShader=
asm
{
ps.1.1
tex t0 ; color map in t0.rgb + specular map in t0.a
tex t1 ; normal map
texm3x2pad t2, t1_bx2 ; u = t1 dot (t2) light vector
texm3x2tex t3, t1_bx2 ; v = t1 dot (t3) half vector
; fetch texture 4 at u, v
; t3.rgb = (N dot L)
; t3.a = (N dot H)^16
; r0 = (diffuse * color) + ((N dot H)^16 * specular value)
mul r1.rgb, t3, t0 ; (N dot L) * base texture
+mul_x4 r1.a, t0.a, t3.a ; (N dot H)^16 * specular value
add r0, r1.a, r1
mul_x4 r0,r0,v0 ; modulate against light color
};
}
}
";
}
and here is the entity action i use to pass light data.. note that currently you cant pass light positions objects in the map... but i am working on that..
also note that if you use this shader on everything and make your world out of models/terrains, you have no need at all of WED light entities, and thus the 8-light limit isnt a problem!
Code:
ACTION worldmodel
{
MY.passable=OFF;
MY.shadow=ON;
my.flare=off;
my.transparent=off;
my.skill41=float(-320); //x
my.skill42=float(32); //z
my.skill43=float(-224); //y
my.skill44=float(1);
my.skill51=100; //R
my.skill52=100; //G
my.skill53=100; //B
mat_diffspecpoint.skill1 = pixel_for_vec(vector(my.skill53,my.skill52,my.skill51),100,8888);
MY.material= mat_diffspecpoint;
}