OK, took me a while figuring it out. I've not worked yet with Irrlicht 1.8.5, but I suppose it's mostly same as Irrlicht trunk here.
First thing: Don't use any of the EMT_NORMAL_x or EMT_PARALLAX_x. They seem to be _only_ useful for the build-in shaders. And the mess you get is likely because as soon as you use those you get some parts of the build-in stuff mixed in.
So what I used was EMT_SOLID (if you need alpha use one of the alpha base materials). And strangely enough despite code looking like it disables second texture for EMT_SOLID - it doesn't and using second texture still worked for me (maybe only does disable for fixed function pipeline and shader does not care?).
And the thing I also missed for a while: tangent and binormal are _not_ passed as TANGENT0 and BINORMAL0, but as TEXCOORD1 and TEXCOORD2. After I found that out I also noticed that's documented in S3DVertexTangents (*sigh*).
So the very simplest shader (basically original d3d9.hlsl just with added variables for tangent, binormal and samplers):
Code: Select all
float4x4 mWorldViewProj; // World * View * Projection transformation
float4x4 mInvWorld; // Inverted world matrix
float4x4 mTransWorld; // Transposed world matrix
float3 mLightPos; // Light position (actually just camera-pos in this case)
float4 mLightColor; // Light color
// Vertex shader output structure
struct VS_OUTPUT
{
float4 Position : POSITION; // vertex position
float4 Diffuse : COLOR0; // vertex diffuse color
float2 TexCoord : TEXCOORD0; // tex coords
float3 Tangent : TEXCOORD1;
float3 Binormal : TEXCOORD2;
};
VS_OUTPUT vertexMain(in float4 vPosition : POSITION,
in float3 vNormal : NORMAL,
float2 texCoord : TEXCOORD0,
in float3 vTangent : TEXCOORD1,
in float3 vBinormal : TEXCOORD2
)
{
VS_OUTPUT Output;
Output.Tangent = vTangent;
Output.Binormal = vBinormal;
// transform position to clip space
Output.Position = mul(vPosition, mWorldViewProj);
// transform normal somehow (NOTE: for the real vertex normal you would use an inverse-transpose world matrix instead of mInvWorld)
float3 normal = mul(float4(vNormal,0.0), mInvWorld);
// renormalize normal
normal = normalize(normal);
// position in world coordinates (NOTE: not sure why transposed world is used instead of world?)
float3 worldpos = mul(mTransWorld, vPosition);
// calculate light vector, vtxpos - lightpos
float3 lightVector = worldpos - mLightPos;
// normalize light vector
lightVector = normalize(lightVector);
// calculate light color
float3 tmp = dot(-lightVector, normal);
tmp = lit(tmp.x, tmp.y, 1.0);
tmp = mLightColor * tmp.y;
Output.Diffuse = float4(tmp.x, tmp.y, tmp.z, 0);
Output.TexCoord = texCoord;
return Output;
}
// Pixel shader output structure
struct PS_OUTPUT
{
float4 RGBColor : COLOR0; // Pixel color
};
sampler2D myTexture : register(s0);
sampler2D normalTexture : register(s1);
PS_OUTPUT pixelMain(float2 TexCoord : TEXCOORD0,
float4 Position : POSITION0,
float4 Diffuse : COLOR0,
float3 Tangent : TEXCOORD1,
float3 Binormal : TEXCOORD2
)
{
PS_OUTPUT Output;
float4 col = tex2D( myTexture, TexCoord );
float4 col2 = tex2D( normalTexture, TexCoord );
// just experimenting
// Output.RGBColor = col * col2;
Output.RGBColor = col2;
Output.RGBColor.rgb *= Tangent;
// Output.RGBColor.rgb = Binormal;
return Output;
}
Shader callback - the one from Irrlicht shader example works - no need to change anything. Textures don't have to be initialized in there at all (not for D3D anyway, for OpenGL you would have to).