• Create Account

Need scary sound effects or creepy audio loops for your next horror-themed game? Check out Highscore Vol.3 - The Horror Edition in our marketplace. 50 sounds and 10 loops for only \$9.99!

### #ActualHyunkel

Posted 17 March 2012 - 11:22 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.

### #2Hyunkel

Posted 17 March 2012 - 11:22 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.

### #1Hyunkel

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
if(VertexNormal.x < 0)
coord1.x = -coord1.x;
if(VertexNormal.y < 0)
coord2.x = -coord2.x;
if(VertexNormal.z > 0)
coord3.x = -coord3.x;
// 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.

PARTNERS