I've been playing around some more. Now got (tangent space) normal mapping to work with my current shader.
Took some of the code from an example by evolved. It's got the ambient skylight, a directional light (like the sun), and obviously the blue point light. Not crazy fast, but there are some optimisations i can try. I notice evolved uses a lookup texture for normalisation. Seem unintuitive but maybe i can get some speed there.
Here's the shader if anyone's interested. It may be hella inefficient. If anyone has ideas please say.
//new version with norm mapping. using evolved's NormalMapping.fx from 2006 free shaders pack #20 for inspiration
//difference is that he seems to interpolate light attenuation from vertices. i will calc it per pixel, because i wish to be using light sources close
//to polygons for my terrain in SBSX for example.
//it may be that by doing terrain in a shader via heightmap may allow me to have v. high poly terrain though. that would allow to make the pix shader use
//approximations! anyway let's just do this and see what happens.
// The following lines are variables which are recognised and passed by the
// application and often referred to as "untweaks".
matrix wvp : WorldViewProjection;
//matrix WorldViewProj : WorldViewProjection;
matrix World : World;
matrix ViewInv : ViewInverse;
// The following lines are variables which can be set by the application
// (these are usually variables which the user may want to adjust from
// their program).
float3 lightDir = {0.577, -0.577, -0.577};
float4 lightColour = {0.3, 0.3, 0.25, 1.0}; // {red, green, blue, alpha} can make really big vals to saturate - great for super explosion lights!
//float4 lightColour = {0.0, 0.0,0.0, 1.0}; //switch off to debug
//float4 amblightColour = {0.0, 0.0, 0.5, 1.0}; // {red, green, blue, alpha}
float4 amblightColour = {0.3, 0.3, 0.35, 1.0}; // {red, green, blue, alpha}
float3 amblightDir = {0.0, -1.0, 0.0}; //amb light simulates omni light from sky, and different colour from ground.
float4 amblightgradColour = {0.15, 0.15, 0.2, 1.0}; // {red, green, blue, alpha}
//float4 amblightgradColour = {0.0, 0.0, 0.0, 1.0}; // {red, green, blue, alpha}
float3 pointlightPos = {0.0,3.0,0.0};
float pointlightRadsq = 0.25;
float4 pointlightColour = {0.0,0.0,25.0,1.0}; //not sure if really need alpha for light colours.
//float tantweak = 1.5f; // PI means no saturation but looks rubbish. small # means no effect (linear and with saturation.
// if make 0 will have /0 error. also if want 0, just switch off last bit of code!
// The following lines define structures that are used as input and output
// to the vertex and pixel shader functions.
struct VSInput {
float4 Pos : Position;
float3 Norm : Normal;
float2 Tex : TEXCOORD0;
//float3 Normal:NORMAL; //this is a rotation matrix rotated to the normal direction - used for tangent space normal mapping
float3 Tangent:TANGENT;
float3 Binormal:BINORMAL;
};
struct VSOutput {
float4 Pos : Position;
//float3 Norm : Normal; //can't use normal!!
float2 Tex : TEXCOORD0;
float3 Norm : Texcoord1; // NOW USING N. MAPPING. MAKES THIS REDUNDANT.
float3 PLPos : Texcoord2; //point light position relative to vertex.
float3 PLPosTS : Texcoord3; //point light position in tangent space (actually will may want this normalised -since it's the direction that's needed.
//get position from world space- since linear interpolation there works right.
float3 LightDirTS : Texcoord4; //directional light direction in tangent space
float3 amblightDirTS:Texcoord5;
};
// For pixel shader input i use vertexdata output.
// could make separate, don't need all of vert output in pix shader- eg position
struct PSOutput { float4 Col : Color; };
// texture stuff
texture Tex0 <
string texturetype = "2D" ;
string name = "d.jpg";
>;
sampler Sampler = sampler_state
{
Texture = (Tex0);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
texture NMap0 <
string texturetype = "2D" ;
string name = "nmap.jpg";
>;
sampler NormSampler = sampler_state
{
Texture = (NMap0);
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
// The vertex shader code:
VSOutput VShader (VSInput In, VSOutput Out)
{ // Transforms the objects model coordinates to
// screen space coordinates
Out.Pos = mul(In.Pos, wvp);
// return normal in world co-ordinates (actually could just keep the same), and
// get light vector in obj frame (once). - however different for different objects. so don't do that way
//Out.Norm = In.Norm;
//not allowed to pass object specific info.
//so cannot precalc light dir in obj frame.
//so rotate normals. inefficient but whatever.
//Out.Norm=mul(In.Norm,rotmatrix); //rotmatrix works, but can't have it as well as world.
Out.Norm=mul(In.Norm,World); //woot! this works! hopefully treats world as 3x3 matrix.
float3 tempp=mul(In.Pos,World); //treats world as 4x3 or 3x4 matrix? pretty sweet!
Out.PLPos=tempp - pointlightPos;
float3 WNor=mul(In.Norm,World); WNor=normalize(WNor); //normalisation shouldn't be nesc. maybe makes a little cleaner.
float3 Wtan=mul(In.Tangent,World); Wtan=normalize(Wtan); //evolved's shader uses this. perhaps because vert shader is v. quick
float3 Wbin=mul(In.Binormal,World); Wbin=normalize(Wbin); //his pixel shader seems highly optimised - eg using normaliser lookup texture.
float3x3 TBN={-Wtan,Wbin,WNor}; TBN=transpose(TBN);
Out.PLPosTS=mul(Out.PLPos,TBN); //point light pos in tangent space - will use this to dot with normal map.
Out.LightDirTS=mul(lightDir,TBN); //light direction in tangent space.
Out.amblightDirTS=mul(amblightDir,TBN); //amb light direction in tangent space.
Out.Tex=In.Tex;
return Out;
};
// The pixel shader code:
PSOutput PShader (VSOutput In,PSOutput Out)
{
//float3 tempn = normalize(In.Norm); //doesn't seem to have much effect OLD
//Out.Col= amblightColour - amblightgradColour*dot(amblightDir,tempn) + lightColour * saturate(-dot(lightDir,tempn)); //no norm map version..
//new norm map version?
float3 NormalMap=tex2D(NormSampler,In.Tex)*2-1; //get normal. actually 3rd component may be different, but small difference.
float4 dirlight=lightColour * saturate(-dot(In.LightDirTS,NormalMap));
float4 amblight=amblightColour - amblightgradColour*dot(In.amblightDirTS,NormalMap);
Out.Col= amblight + dirlight;
//Out.Col= amblightColour + dirlight;
// brightness goes as 1/distance sq. SWITCHED OFF POINT LIGHT FOR 1ST VERS OF NORM MAPPING SHADER
//Out.Col += pointlightColour *saturate(-dot(In.PLPos,tempn) / pow((dot(In.PLPos,In.PLPos)+ pointlightRadsq ),1.5) );
Out.Col += pointlightColour * saturate(-dot(normalize(In.PLPosTS),NormalMap)) / (dot(In.PLPos,In.PLPos)+ pointlightRadsq);
Out.Col=Out.Col*tex2D(Sampler, In.Tex); //multiply by base texture.
//instead of letting comp do auto saturate, use thing where as input-> inf, output->1
//Out.Col=atan(3.14f*Out.Col)/3.14f; //works, but looks a bit poo
//Out.Col=atan(tantweak*Out.Col)/tantweak; //should put this variable as a tweakable!
//Out.Col=pow(Out.Col,0.5); //try something!
//Out.Col.a=1.0f;
return Out;
};
// The following lines just put this together as a single technique
// with a single pass.
technique simplyOneColour //a relic of where i got the code framework from!
{
//float3 testx = {1.0f, 1.0f, 1.0f }; // can i put code here? - looks like you can't :(
pass p1
{
//float3 testx = {1.0f, 1.0f, 1.0f }; // can i put code here? - looks like you can't :(
VertexShader = compile vs_1_1 VShader();
PixelShader = compile ps_2_0 PShader();
}
}
I had a eureka moment whilst boiling the kettle - i could make a terrain shader for my game with automatic wrapping off the heightmap, level of detail etc, just by tweaking uv co-ords, and it would be fast and awesome. Then I checked on here and it's been done a million times before. So less braggable, but proof that it's going to work and be awesome.
Edit: just realised i can do shadows from non moving stuff (like terrain or the planet in asteroid worlds) without requiring realtime rendering of the shadow z-buffer thing.
I'm totally going to do a shaders version of asteroid worlds. Only trouble is there aren't enough hours in the day!