The textures then are blended together using the object space normal, and the rest is the lighting equation, which is performed over a directional light, and uses the object's material ambient, diffuse and specular colors to render.
Finally, it comes in 2 flavours: Open GL GLSL and DirectX 9C HLSL. Their functionality is identical (at least, rendermonkey shows identical results)
In alphabetical order:
GLSL
vertex program
--------------
Code: Select all
//Triplanar coordinate maps with normal maps support
//by Santiago A. Navascues
//Use at your own risk. I make no warranties.
//If you want to use it, give the proper credits
uniform mat4 matWorld;
uniform vec4 eyePosition;
uniform float tileSize;
varying vec2 TexCoordX;
varying vec2 TexCoordY;
varying vec2 TexCoordZ;
varying vec3 ViewDir;
varying mat3 tangentSpace;
void main( void )
{
gl_Position = ftransform();
vec4 worldPos = matWorld * gl_Vertex;//So we obtain the world position
TexCoordX = worldPos.zy/tileSize;//here are our texture coordinates...
TexCoordY = worldPos.xz/tileSize;
TexCoordZ = worldPos.xy/tileSize;
//binormal and tangent become normal dependant
vec3 xtan,ytan,ztan;
vec3 xbin,ybin,zbin;
xtan = vec3(0,0,1);//tangent space for the X aligned plane
xbin = vec3(0,1,0);
ytan = vec3(1,0,0);//tangent space for the Y aligned plane
ybin = vec3(0,0,1);
ztan = vec3(1,0,0);//tangent space for the Z aligned plane
zbin = vec3(0,1,0);
vec3 n = gl_Normal;
n*=n;
vec3 worldNormal = gl_Normal;
vec3 worldBinormal = xbin*n.x+ybin*n.y+zbin*n.z;//Average Binormal
vec3 worldTangent = xtan*n.x+ytan*n.y+ztan*n.z;//Average Tangent
ViewDir = (worldPos-eyePosition).xyz;
//This is done so it can be rotated freely
worldTangent = (matWorld*vec4(worldTangent,1)).xyz;
worldBinormal = (matWorld*vec4(worldBinormal,1)).xyz;
worldNormal = (matWorld*vec4(worldNormal,1)).xyz;
tangentSpace[0] = worldTangent;
tangentSpace[1] = worldBinormal;
tangentSpace[2] = worldNormal;
}
----------------
Code: Select all
//Note this is the 6 textures version, hence, you will need to recompile Irrlicht if you want
//to use it.
//Can be reduced to a 4 textures version if you sample the baseX instead of the baseZ texture
//and the normalX instead of the normalZ texture.
//And can be reduced further more if you use a single normal map, and sample it from every side
//everytime, leaving space for a shadowmap, the shader does the rest.
//By the way, lighting is performed in worldspace, because otherwise, the normal calculation
//and the rotation support could be too complex.
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
uniform float specularPower;
uniform vec3 lightDirection;
uniform sampler2D baseX;
uniform sampler2D baseY;
uniform sampler2D baseZ;
uniform sampler2D normalX;
uniform sampler2D normalY;
uniform sampler2D normalZ;
varying vec2 TexCoordX;
varying vec2 TexCoordY;
varying vec2 TexCoordZ;
varying vec3 ViewDir;
varying mat3 tangentSpace;
void main( void )
{
vec3 normal = tangentSpace[2];
vec3 n = normal;
n*=n;
vec4 col = texture2D(baseX,TexCoordX)*n.x+
texture2D(baseY,TexCoordY)*n.y+
texture2D(baseZ,TexCoordZ)*n.z;
vec4 nrm = (2.0*texture2D(normalX,TexCoordX)-1.0)*n.x+
(2.0*texture2D(normalY,TexCoordY)-1.0)*n.y+
(2.0*texture2D(normalZ,TexCoordZ)-1.0)*n.z;
vec3 realNormal = normalize(tangentSpace*nrm.xyz);
vec3 lightDir = normalize(lightDirection);
vec3 viewDir = normalize(ViewDir);
float NdL = max(0.0,dot(realNormal,lightDir));
vec3 reflVect = normalize(reflect(lightDir,realNormal));
float RdL = max(0.0,dot(reflVect,viewDir));
vec4 ambient,diffuse,specular,fresnel;
ambient = col*ambientColor;
diffuse = diffuseColor*col*NdL;
specular = specularColor*pow(RdL,specularPower)*col;
fresnel = max(0.0,dot(realNormal,viewDir))*specularColor;
gl_FragColor = ambient+diffuse+specular+fresnel;
}
vertex shader
-------------
Code: Select all
//Triplanar coordinate maps with normal maps support DirectX 9c version
//by Santiago A. Navascues
//Use at your own risk. I make no warranties.
//If you want to use it, give the proper credits
float4x4 matWorldViewProjection;
float4x4 matWorld;
float tileSize;
float4 viewPos;
struct VS_INPUT
{
float4 Position : POSITION0;
float2 Texcoord : TEXCOORD0;
float3 Normal : NORMAL0;
};
struct VS_OUTPUT
{
float4 Position : POSITION0;
float2 TexcoordX : TEXCOORD0;
float2 TexcoordY : TEXCOORD1;
float2 TexcoordZ : TEXCOORD2;
float3 viewDirection : TEXCOORD3;
float3x3 tangentSpace : TEXCOORD4;
};
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.Position = mul( Input.Position, matWorldViewProjection );
float4 realPos = mul( Input.Position,matWorld);//So we obtain the world position
Output.viewDirection = (realPos-viewPos).xyz;
Output.TexcoordX = realPos.zy/tileSize;//here are our texture coordinates...
Output.TexcoordY = realPos.xz/tileSize;
Output.TexcoordZ = realPos.xy/tileSize;
float3 worldNormal = Input.Normal;
float3 n = worldNormal;
n*=n;
//binormal and tangent become normal dependant
float3 xtan,ytan,ztan;
float3 xbin,ybin,zbin;
xtan = float3(0,0,1);//tangent space for the X aligned plane
xbin = float3(0,1,0);
ytan = float3(1,0,0);//tangent space for the Y aligned plane
ybin = float3(0,0,1);
ztan = float3(1,0,0);//tangent space for the Z aligned plane
zbin = float3(0,1,0);
float3 worldBinormal = xbin*n.x+ybin*n.y+zbin*n.z;//Average Binormal
float3 worldTangent = xtan*n.x+ytan*n.y+ztan*n.z;//Average Tangent
//This is done so the object can be later rotated
worldNormal = mul(matWorld,worldNormal);
worldBinormal = mul(matWorld,worldBinormal);
worldTangent = mul(matWorld,worldTangent);
Output.tangentSpace[0] = worldTangent;
Output.tangentSpace[1] = worldBinormal;
Output.tangentSpace[2] = worldNormal;
return( Output );
}
------------
Code: Select all
//Note this is the 6 textures version, hence, you will need to recompile Irrlicht if you want
//to use it.
//Can be reduced to a 4 textures version if you sample the baseX instead of the baseZ texture
//and the normalX instead of the normalZ texture.
//And can be reduced further more if you use a single normal map, and sample it from every side
//everytime, leaving space for a shadowmap, the shader does the rest.
//More good news: Only PS2 required!
float4 fvAmbient;
float4 fvSpecular;
float4 fvDiffuse;
float fSpecularPower;
float3 lightDirection;
sampler2D baseX;
sampler2D baseY;
sampler2D baseZ;
sampler2D normalX;
sampler2D normalY;
sampler2D normalZ;
struct PS_INPUT
{
float4 Position : POSITION0;
float2 TexcoordX : TEXCOORD0;
float2 TexcoordY : TEXCOORD1;
float2 TexcoordZ : TEXCOORD2;
float3 viewDirection : TEXCOORD3;
float3x3 tangentSpace : TEXCOORD4;
};
float4 ps_main( PS_INPUT Input ) : COLOR0
{
float3 fvLightDirection = normalize( lightDirection );
float3 fvNormal = normalize( Input.tangentSpace[2]);
float3 fvViewDirection = normalize( Input.viewDirection );
float3 n = fvNormal;
n*=n;
float4 fvBaseNormal = tex2D( normalX, Input.TexcoordX )*n.x+
tex2D( normalY, Input.TexcoordY )*n.y+
tex2D( normalZ, Input.TexcoordZ )*n.z;
float3 normal = normalize(mul(2.0*fvBaseNormal.xyz-1.0,Input.tangentSpace));
float fNDotL = max(0.0,dot( normal, fvLightDirection ));
float3 fvReflection = reflect(fvLightDirection,normal);
float fRDotV = max(0.0, dot( fvReflection, fvViewDirection ) );
float4 fvBaseColor = tex2D( baseX, Input.TexcoordX )*n.x+
tex2D( baseY, Input.TexcoordY )*n.y+
tex2D( baseZ, Input.TexcoordZ )*n.z;
float4 fvTotalAmbient = fvAmbient * fvBaseColor;
float4 fvTotalDiffuse = fvDiffuse * fNDotL * fvBaseColor;
float4 fvTotalSpecular = fvSpecular * pow( fRDotV, fSpecularPower )*fvBaseColor;
float4 fresnel = max(0.0,dot(fvViewDirection,normal))*fvSpecular;
return( fvTotalAmbient + fvTotalDiffuse + fvTotalSpecular+fresnel );
}