• FEATURED
• FEATURED
• FEATURED
• FEATURED
• FEATURED

View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# Using normal mapping with triplanar texture projection

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

6 replies to this topic

### #1Hyunkel  Members

Posted 16 March 2012 - 03:29 PM

I am working on my procedural planet generator / renderer.
I use triplanar texture mapping because it works nicely for procedural geometry.
This is the technique I'm currently using:
http://http.develope...gems3_ch01.html (Example 1-3. Texture Planar Projection)

This works rather well for diffuse texturing, and they have even provided a way to do bump mapping.
Their method is very different from what I'm used to doing though.
Usually I would generate a TBN matrix and multiply the tangent space normal (from the normal map) with it to get my normal vector.
However this does not work with triplanar texture projection.
Not only do I not know the tangent and bitangent vectors to do this, but since I'm using 3 projections simultaniously there is no unique correct TBN matrix.

The method illustrated in the gpu gems article does the following:
- Generate 3 sample coords: (Position being the object space vertex position)
float2 coord1 = Position.yz * TexScale;
float2 coord2 = Position.zx * TexScale;
float2 coord3 = Position.xy * TexScale;

- Get 3 tangent space normals from a normal map (with half intensity)
float3 nor1 = TNormal1.Sample(TextureSampler, coord1).rgb - 0.5;// * 2.0 - 1.0;
float3 nor2 = TNormal1.Sample(TextureSampler, coord2).rgb - 0.5;// * 2.0 - 1.0;
float3 nor3 = TNormal1.Sample(TextureSampler, coord3).rgb - 0.5;// * 2.0 - 1.0;

- Bring the 3 normal vectors into object/world space
float3 Normal1 = float3(0, nor1.x, nor1.y);
float3 Normal2 = float3(nor2.y, 0, nor2.x);
float3 Normal3 = float3(nor3.x, nor3.y, 0);

Then all 3 normals are blended together depending on the vertex normal and added onto the vertex normal.
According to the article this is designed for bump mapping, not normal mapping, but I don't understand why their bump map would have 2 channels.

It is the last step I am having trouble understanding, what exactly is going on here?

float3 Normal1 = float3(0, nor1.x, nor1.y);
float3 Normal2 = float3(nor2.y, 0, nor2.x);
float3 Normal3 = float3(nor3.x, nor3.y, 0);

There is no way this can produce even remotely correct normal vectors, which would explain why my results are okay-ish, but far from what I want:

- The left image is roughly what I want to achieve (to create the image I cheated and used a TBN matrix that is roughly correct for a single projection that is completely dominant over the other 2 in this specific location)
- The right image is what I'm getting using the bump technique from gpu gems (normals don't line up with the diffuse map, also note the repetitive pattern in the background and the different light intensity on the mountain in the foreground)

Now I understand that it is most likely impossible to do "proper" normal mapping, as it seems to be a hideously complicated task to make the normals line up with the diffuse maps.
So I guess what I'm asking is:

- What exactly does this do:

float3 Normal1 = float3(0, nor1.x, nor1.y);
float3 Normal2 = float3(nor2.y, 0, nor2.x);
float3 Normal3 = float3(nor3.x, nor3.y, 0);

- And is there any way to generate better normal vectors (either by improving this method or using a completely different approach)

Here's my current code for reference: (TDiffuse2 does not use a normal map which is why there is no TNormal2)
void TriplanarMapping( in float3 VertexNormal,
in float3 Position,
in float TexScale,
in float BumpIntensity,
in Texture2D TDiffuse1,
in Texture2D TDiffuse2,
in Texture2D TNormal1,
in SamplerState TextureSampler,
out float3 TriDiffuse1,
out float3 TriDiffuse2,
out float3 TriNormal1)
{
// Triplanar Texture Projection
float3 BlendWeights = abs(VertexNormal);
BlendWeights = (BlendWeights - 0.2f) * 7;
BlendWeights = max(BlendWeights, 0);
BlendWeights /= (BlendWeights.x + BlendWeights.y + BlendWeights.z);

// Triplanar sample coords
float2 coord1 = Position.yz * TexScale;
float2 coord2 = Position.zx * TexScale;
float2 coord3 = Position.xy * TexScale;

// Diffuse sampling
float3 col1 = TDiffuse1.Sample(TextureSampler, coord1).rgb;
float3 col2 = TDiffuse1.Sample(TextureSampler, coord2).rgb;
float3 col3 = TDiffuse1.Sample(TextureSampler, coord3).rgb;

float3 col4 = TDiffuse2.Sample(TextureSampler, coord1).rgb;
float3 col5 = TDiffuse2.Sample(TextureSampler, coord2).rgb;
float3 col6 = TDiffuse2.Sample(TextureSampler, coord3).rgb;

// Normal sampling
float3 nor1 = TNormal1.Sample(TextureSampler, coord1).rgb - 0.5;// * 2.0 - 1.0;
float3 nor2 = TNormal1.Sample(TextureSampler, coord2).rgb - 0.5;// * 2.0 - 1.0;
float3 nor3 = TNormal1.Sample(TextureSampler, coord3).rgb - 0.5;// * 2.0 - 1.0;

// Transform normals (this step is very crude)
float3 Normal1 = float3(0, nor1.x, nor1.y);
float3 Normal2 = float3(nor2.y, 0, nor2.x);
float3 Normal3 = float3(nor3.x, nor3.y, 0);

// Triplanar blending
TriDiffuse1 = col1 * BlendWeights.x +
col2 * BlendWeights.y +
col3 * BlendWeights.z;
TriDiffuse2 = col4 * BlendWeights.x +
col5 * BlendWeights.y +
col6 * BlendWeights.z;
TriNormal1 = Normal1 * BlendWeights.x +
Normal2 * BlendWeights.y +
Normal3 * BlendWeights.z;

// Bump vertex normal
TriNormal1 = normalize(VertexNormal + TriNormal1 * BumpIntensity);
}

Cheers,
Hyu

### #2Eric Lengyel  Members

Posted 16 March 2012 - 06:51 PM

You would probably find "Voxel-Based Terrain for Real-Time Virtual Simulations" to be very interesting. It thoroughly addresses the problem of tangent-space normal mapping for triplanar projections in Section 5.5. You can find the paper here:

http://www.terathon.com/voxels/

### #3Tasty Texel  Members

Posted 17 March 2012 - 08:25 AM

Take a look at this shader. It is designed to work with 3DGS:

bool AUTORELOAD;

float4x4 matWorldViewProj;
float4x4 matWorld;
float4x4 matWorldInv;
float4x4 matWorldView;
float4x4 matView;
float4x4 matViewInv;

float4 vecSunDir;
float4 vecViewPos;
float4 vecTime;

float4 vecSkill41;

texture entSkin1;
//texture entSkin2;

texture mtlSkin1;
texture mtlSkin2;
texture mtlSkin3;
texture mtlSkin4;

sampler AmbOccMapSampler = sampler_state
{
Texture   = <entSkin1>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};

sampler ColorMapSampler = sampler_state
{
Texture   = <mtlSkin1>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};

sampler BumpMapSampler = sampler_state
{
Texture   = <mtlSkin2>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};

sampler DepthMapSampler = sampler_state
{
Texture   = <mtlSkin3>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};

sampler DetailMapSampler = sampler_state
{
Texture   = <mtlSkin4>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};

void DiffuseVS
(
in float4 vPos	   	   : POSITION,
in float2 iTex			: TEXCOORD0,
in float3 normal   	   : NORMAL,

out float3 outnormal	 : TEXCOORD0,
out float3 oPos			: TEXCOORD1,
out float3 View			: TEXCOORD2,
out float3 OS_View		 : TEXCOORD5,
out float2 oTex			: TEXCOORD3,

out float4 Pos   		  : POSITION
)
{
normal=mul(normal,matWorld);
vPos=mul(vPos,matWorld);

oPos=vPos;
oTex=iTex;

outnormal=normal;

Pos = mul(vPos,matWorldViewProj);

float3 wPos=mul(vPos,matWorld);

View=normalize(vecViewPos-wPos);

OS_View=normalize(mul(vecViewPos,matWorldInv)-vPos);
}

///////////////////

float3 sample_ColorProj(sampler2D ColorSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3)
{
float3 Col1=tex2D(ColorSampler,Tex1.xy);
float3 Col2=tex2D(ColorSampler,Tex2.xy);
float3 Col3=tex2D(ColorSampler,Tex3.xy);

return Col1*BlendF.x + Col2*BlendF.y + Col3*BlendF.z;
}

float3 sample_DepthProj(sampler2D DepthSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3)
{
float3 D;
D.x=tex2Dlod(DepthSampler,float4(Tex1.xy,0,0)).x;
D.y=tex2Dlod(DepthSampler,float4(Tex2.xy,0,0)).x;
D.z=tex2Dlod(DepthSampler,float4(Tex3.xy,0,0)).x;

return dot(D,BlendF);
}

float3 sample_NormalProj(sampler2D NormalSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3, float3x3 matTangent13, float3x3 matTangent2)
{
float3 NM1=tex2D(NormalSampler,Tex1.xy)*2.f-1.f;
float3 NM2=tex2D(NormalSampler,Tex2.xy)*2.f-1.f;
float3 NM3=tex2D(NormalSampler,Tex3.xy)*2.f-1.f;

NM1=mul(NM1,matTangent13);
NM2=mul(NM2,matTangent2);
NM3=mul(NM3,matTangent13);

return normalize(NM1*BlendF.x + NM2*BlendF.y + NM3*BlendF.z);
}

float3 sample_NormalProj_simple(sampler2D NormalSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3, float3 Normal, float3 ds, float BumpWeight)
{
float3 NM1=tex2D(NormalSampler,Tex1.xy)*2.f-1.f;
float3 NM2=tex2D(NormalSampler,Tex2.xy)*2.f-1.f;
float3 NM3=tex2D(NormalSampler,Tex3.xy)*2.f-1.f;

NM1=float3(0,-NM1.y,NM1.x*ds.x);
NM2=float3(NM2.x,0,-NM2.y*ds.y);
NM3=float3(-NM3.x*ds.z,-NM3.y,0);

return normalize(Normal+(NM1*BlendF.x + NM2*BlendF.y + NM3*BlendF.z)*BumpWeight);
}

float IntersectionTest(
in float3 BlendF,
in float2 Tex1, in float2 Off1,
in float2 Tex2, in float2 Off2,
in float2 Tex3, in float2 Off3,
in float Bias)
{
const int linear_search_steps=15;

float depth_step=1.0/linear_search_steps;

float size=depth_step; // current size of search window

float depth=-Bias; // current depth position
float lastHeight=0;
float height=0;

// search from front to back for first point inside the object
for(int i=0;i<linear_search_steps-1;i++)
{
height=sample_DepthProj(DepthMapSampler, BlendF, Tex1+Off1*depth, Tex2+Off2*depth, Tex3+Off3*depth);

if (depth < height-Bias)
{
depth+=size;
lastHeight = height;
}

}

//	#define USE_BINARY_SEARCH
#ifdef USE_BINARY_SEARCH
const int binary_search_steps=7;

//search around first point (depth) for closest match
for ( int i=0; i<binary_search_steps;i++)
{
size*=0.5f;
height=sample_DepthProj(DepthMapSampler, BlendF, Tex1+Off1*depth, Tex2+Off2*depth, Tex3+Off3*depth);

if (depth<height-Bias)
depth += (2*size);

depth -= size;
}
#else
depth+=Bias;
depth = ( depth - size * ( 1 - ( (depth-size-lastHeight) / (height-size-lastHeight) ) ) );
depth-=Bias;
#endif

return depth;

}

float4 DiffusePS
(
in float3 normal	  : TEXCOORD0,
in float3 Pos		  : TEXCOORD1,
in float3 View	  : TEXCOORD2,
in float3 OS_View  : TEXCOORD5,

in float2 Tex	  : TEXCOORD3
) : COLOR
{
float4 c;
normal=normalize(normal);
View=normalize(View);
OS_View=normalize(OS_View);

float s=80;
Pos.xyz/=s;

///////////////////////////////////////////////////////
//===================================================//
float3 ds=sign(normal);

float2 Tex1=Pos.zy*float2(ds.x,-1.f);
float2 Tex2=Pos.xz*float2(1.f,-ds.y);
float2 Tex3=Pos.xy*float2(-ds.z,-1.f);

#define USE_REALTANGENTBASE //usually not required, but if you project on a sphere you can see the difference clearly

#ifdef USE_REALTANGENTBASE
float3x3 matTangent13;
matTangent13[0]=cross(normal,float3(0.f,1.f,0.f));
matTangent13[1]=cross(normal,matTangent13[0]);
matTangent13[2]=normal;

float3x3 matTangent2;
matTangent2[0]=cross(normal,float3(0.f,0.f,1.f));
matTangent2[1]=cross(normal,matTangent2[0]);
matTangent2[2]=normal;

matTangent2[0]*=ds.y;
matTangent2[1]*=ds.y;
#endif
//===================================================//
///////////////////////////////////////////////////////

float3 BlendF=pow(abs(normal), 60.f);
BlendF/=dot(BlendF.xyz,1.f);

///////////////////////////////////////////////////////
//===================================================//

const float off_scale=0.08f;

float3 ProjView=cross(cross(OS_View,normal),normal)*off_scale;

float2 offset_vec1=ProjView.zy*float2(ds.x,-1.f);
float2 offset_vec2=ProjView.xz*float2(1,-ds.y);
float2 offset_vec3=ProjView.xy*float2(-ds.z,-1.f);

//-----//dont use the following to create the offset_vecs, it´s slower and less consistent:
//	offset_vec1 =-mul(matTangent13,OS_View).xy*off_scale;
//	offset_vec2 =-mul(matTangent2 ,OS_View).xy*off_scale;
//	offset_vec3=offset_vec1;
//-----//

float offset_length = IntersectionTest(BlendF,
Tex1.xy, offset_vec1,
Tex2.xy, offset_vec2,
Tex3.xy, offset_vec3,1.f);

Tex1.xy+=offset_vec1*offset_length;
Tex2.xy+=offset_vec2*offset_length;
Tex3.xy+=offset_vec3*offset_length;

//===================================================//
///////////////////////////////////////////////////////

float3 Albedo=sample_ColorProj(ColorMapSampler,BlendF,Tex1,Tex2,Tex3);

#ifdef USE_REALTANGENTBASE
float3 Bump=sample_NormalProj(BumpMapSampler,BlendF,Tex1,Tex2,Tex3,matTangent13,matTangent2);
#else
float3 Bump=sample_NormalProj_simple(BumpMapSampler,BlendF,Tex1,Tex2,Tex3,normal,ds,1.f);
#endif

Bump=mul(Bump,matWorld);
normal=mul(normal,matWorld);

///////////////////////////////////////////////////////add some interesting lighting for visualisation:
//===================================================//

float AmbOcc=tex2D(AmbOccMapSampler,Tex).r;

float SunLight_Bump=dot(vecSunDir,Bump);
float SunLight_Smooth=dot(vecSunDir,normal);

float SunLight_Bump_BackBleed=SunLight_Bump*0.5f+0.5f;
float SunLight_Bump_BackBleed2=SunLight_Bump*0.2f+0.8f;
float SunLight_Smooth_BackBleed=SunLight_Smooth*0.5f+0.5f;

SunLight_Bump=saturate(SunLight_Bump);
SunLight_Smooth=saturate(SunLight_Smooth);

float SunLight_Amount=min(1.f, SunLight_Bump+SunLight_Smooth);

float SunLight_Back=SunLight_Bump_BackBleed*SunLight_Smooth_BackBleed;
SunLight_Back=lerp(SunLight_Bump_BackBleed2*0.05f,1.f,SunLight_Back);

c.rgb=Albedo*AmbOcc*lerp(SunLight_Back,SunLight_Bump,SunLight_Amount);

c.a=1;

//===================================================//
///////////////////////////////////////////////////////

//	c.rg=offset_vec1*BlendF.x+offset_vec2*BlendF.y+offset_vec3*BlendF.z;
//	c.rg=normalize(c.rg)*0.5+0.5;
//	c.b=0;

//	c.rgb=Bump;

return c;
}

technique DiffuseTechnique
{
pass P0
{
}
}


As you can see the shader also shows how parallax-occlusion mapping can work together with triplanar-projection.
If anything is unclear, just ask.

### #4Hyunkel  Members

Posted 17 March 2012 - 10:46 AM

You would probably find "Voxel-Based Terrain for Real-Time Virtual Simulations" to be very interesting. It thoroughly addresses the problem of tangent-space normal mapping for triplanar projections in Section 5.5. You can find the paper here:

http://www.terathon.com/voxels/

I cannot use that exact method with my rendering pipeline, but it made me understand that if I set up my projections correctly, I can limit myself to two continuous tangent spaces.
With this information I was able to come up with a more accurate implementation than the one in gpu gems:
void TriplanarMapping( in float3 VertexNormal,
in float3 Position,
in float TexScale,
in float BumpIntensity,
in Texture2D TDiffuse1,
in Texture2D TDiffuse2,
in Texture2D TNormal1,
in SamplerState TextureSampler,
out float3 TriDiffuse1,
out float3 TriDiffuse2,
out float3 TriNormal1)
{
// Calculate blending weights
float3 BlendWeights = abs(VertexNormal);
BlendWeights = (BlendWeights - 0.2f) * 7;
BlendWeights = max(BlendWeights, 0);
BlendWeights /= (BlendWeights.x + BlendWeights.y + BlendWeights.z);
// Triplanar sample coords
float2 coord1 = float2(Position.z, -Position.y) * TexScale; // ZY: Left and Right
float2 coord2 = float2(Position.x, -Position.z) * TexScale; // XZ: Top and Bottom
float2 coord3 = float2(Position.x, -Position.y) * TexScale; // XY: Front and Back

// Instead of mirroring, wrap the textures around the Y axis (Front, Back, Left, Right)
// and the Z axis (Back and Front)
// This way we only have to deal with 2 tangent spaces
float3 flip = sign(VertexNormal);
coord1.x *= flip.x;
coord2.x *= flip.y;
coord3.x *= -flip.z;
// Calculate tangents for both tangent spaces
// - Tangent1: wrapped around Y (Valid for XY and ZY projected textures)
// - Tangent2: wrapped around Z (Valid for XZ projected textures)
float3 Tangent1 = normalize(float3(-VertexNormal.z, 0, VertexNormal.x));
float3 Tangent2 = normalize(float3(VertexNormal.y, -VertexNormal.x, 0));
// Build TBN matrices for both tangent spaces
float3x3 TBN1, TBN2;
TBN1[0] = Tangent1;
TBN1[1] = cross(Tangent1, VertexNormal);
TBN1[2] = VertexNormal;
TBN2[0] = Tangent2;
TBN2[1] = cross(Tangent2, VertexNormal);
TBN2[2] = VertexNormal;
// === Diffuse maps ===
float3 col1 = TDiffuse1.Sample(TextureSampler, coord1).rgb;
float3 col2 = TDiffuse1.Sample(TextureSampler, coord2).rgb;
float3 col3 = TDiffuse1.Sample(TextureSampler, coord3).rgb;
float3 col4 = TDiffuse2.Sample(TextureSampler, coord1).rgb;
float3 col5 = TDiffuse2.Sample(TextureSampler, coord2).rgb;
float3 col6 = TDiffuse2.Sample(TextureSampler, coord3).rgb;
// Blend together
TriDiffuse1 =   col1 * BlendWeights.x +
col2 * BlendWeights.y +
col3 * BlendWeights.z;
TriDiffuse2 =   col4 * BlendWeights.x +
col5 * BlendWeights.y +
col6 * BlendWeights.z;
// === Normal maps ===
float3 nor1 = TNormal1.Sample(TextureSampler, coord1).rgb * 2.0 - 1.0;
float3 nor2 = TNormal1.Sample(TextureSampler, coord2).rgb * 2.0 - 1.0;
float3 nor3 = TNormal1.Sample(TextureSampler, coord3).rgb * 2.0 - 1.0;
// Transform normals into world space
float3 Normal1 = mul(nor1, TBN1);
float3 Normal2 = mul(nor2, TBN2);
float3 Normal3 = mul(nor3, TBN1);
// Blend together
TriNormal1 =   Normal1 * BlendWeights.x +
Normal2 * BlendWeights.y +
Normal3 * BlendWeights.z;

TriNormal1 = normalize(TriNormal1);
}


It's not perfect, but it's alright for now.

The textures are far from ideal for this scene, but I have no idea where to get decent ones.
I'll have to look into that.
I also need to move some of that code to the vertex shader.
The framerate is low due to fraps running, I get about ~200fps without it, which isn't bad considering I'm generating the entire planet from scratch every frame.

Take a look at this shader. It is designed to work with 3DGS:

As you can see the shader also shows how parallax-occlusion mapping can work together with triplanar-projection.
If anything is unclear, just ask.

Thanks!
I'll compare this to what I've come up with and see if I can improve my implementation.

### #5tapinkaR  Members

Posted 16 July 2012 - 12:24 AM

Hello,
I have similar issue. I made HLSL effect for my XNA game. I am using triplanar texture for my terrain. But result is moving with my model. There is probably some issue in position but I don't know how to fix it. Please help me with this issue. I went from many blogs and forums but I didn't find the solution.

here is my HLSL code:
[source lang="cpp"]// -------------------------------------------------// Matrices// -------------------------------------------------float4x4 matW : World;float4x4 matVI : ViewInverse;float4x4 matWVP : WorldViewProjection;// Lights// -------------------------------------------------float ambientLightColor;float3 light1Position;float3 light1Color;float3 light2Position;float3 light2Color;float3 lightDirection;// UV Tiles: 0-4 Diffuse texturesfloat2 uv1Tile;float2 uv2Tile;float2 uv3Tile;float2 uv4Tile;// Textures// -------------------------------------------------texture diffuseTexture1;texture diffuseTexture2;texture diffuseTexture3;texture diffuseTexture4;sampler2D diffuseSampler1 = sampler_state { texture = <diffuseTexture1>; MagFilter = Linear; MinFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap;};sampler2D diffuseSampler2 = sampler_state { texture = <diffuseTexture2>; MagFilter = Linear; MinFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap;};sampler2D diffuseSampler3 = sampler_state { texture = <diffuseTexture3>; MagFilter = Linear; MinFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap;};sampler2D diffuseSampler4 = sampler_state { texture = <diffuseTexture4>; MagFilter = Linear; MinFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap;};struct a2v{ float4 position : POSITION0; float2 uv0 : TEXCOORD0; float3 normal : NORMAL;float4 inTexWeights : TEXCOORD1;};struct v2f{ float4 hposition : POSITION; float3 lightVec1 : TEXCOORD0; float3 xlightDirection : TEXCOORD1;float4 TextureWeights : TEXCOORD2;float3 Normal : TEXCOORD3;float3 blendFactor : TEXCOORD4;float2 coord1 : TEXCOORD5;float2 coord2 : TEXCOORD6;float2 coord3 : TEXCOORD7;};float DotProduct(float3 lightPos, float3 pos3D, float3 normal){ float3 lightDir = normalize(pos3D - lightPos); return dot(-lightDir, normal); }v2f TerrainVS(a2v IN){v2f OUT; OUT.hposition = mul(IN.position, matWVP); OUT.Normal = normalize(mul(IN.normal, matW)); OUT.blendFactor = abs( OUT.Normal.xyz ); OUT.blendFactor = (OUT.blendFactor - 0.2) * 7; OUT.blendFactor = max(OUT.blendFactor, 1); OUT.blendFactor /= (OUT.blendFactor.x + OUT.blendFactor.y + OUT.blendFactor.z ).xxx; OUT.coord1 = OUT.hposition.yz * 0.01 ;//* tex_scale; mXY;OUT.coord2 = OUT.hposition.zx * 0.01;//* tex_scale; mXZ;OUT.coord3 = OUT.hposition.xy * 0.01;//* tex_scale; mYZ; // Light vectors float3 worldPosition = mul(IN.position, matW).xyz; OUT.lightVec1 = light1Position - worldPosition; OUT.xlightDirection.xyz = lightDirection; OUT.TextureWeights = IN.inTexWeights; return OUT;}float4 MultiTexturedPS(v2f IN) : COLOR0{float4 finalColor; float4 tempColor; float lightingFactor1 = DotProduct(light1Position,IN.lightVec1,IN.Normal); float lightingFactor = saturate(lightingFactor1); lightingFactor *= light1Color;tempColor = tex2D(diffuseSampler1, IN.coord1.xy)* IN.blendFactor.z; tempColor += tex2D(diffuseSampler1, IN.coord2.xy)* IN.blendFactor.x; tempColor += tex2D(diffuseSampler1, IN.coord3.xy)* IN.blendFactor.y;finalColor = tempColor * IN.TextureWeights.x;tempColor = tex2D(diffuseSampler2, IN.coord1.xy)* IN.blendFactor.z; tempColor += tex2D(diffuseSampler2, IN.coord2.xy)* IN.blendFactor.x; tempColor += tex2D(diffuseSampler2, IN.coord3.xy)* IN.blendFactor.y;finalColor += tempColor * IN.TextureWeights.y; tempColor = tex2D(diffuseSampler3, IN.coord1.xy)* IN.blendFactor.z; tempColor += tex2D(diffuseSampler3, IN.coord2.xy)* IN.blendFactor.x; tempColor += tex2D(diffuseSampler3, IN.coord3.xy)* IN.blendFactor.y;finalColor += tempColor * IN.TextureWeights.z; tempColor = tex2D(diffuseSampler4, IN.coord1.xy)* IN.blendFactor.z; tempColor += tex2D(diffuseSampler4, IN.coord2.xy)* IN.blendFactor.x; tempColor += tex2D(diffuseSampler4, IN.coord3.xy)* IN.blendFactor.y;finalColor += tempColor * IN.TextureWeights.w; finalColor *= lightingFactor + ambientLightColor; return finalColor;}technique Terrain{ pass p0{ VertexShader = compile vs_2_0 TerrainVS(); PixelShader = compile ps_2_0 MultiTexturedPS();}}[/source]

Here is stored the result.
http://www.uloz.to/xeP8UdE/triplanar-avi

### #6Tasty Texel  Members

Posted 16 July 2012 - 09:04 AM

Use IN.position instead of IN.hposition for our coords.

### #7JorenJoestar  Members

Posted 16 July 2012 - 09:46 AM

Maybe you can look at this!