Triplanar mapping

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Post Reply
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Triplanar mapping

Post by TaaTT4 »

Hello everybody,

I'm trying to add triplanar mapping to the OGRE Terra system to avoid texture stretching on cliffs.
So far, my implementation seems to work on the edge case (a "cube" terrain), but miserably fail on a real case.

Here you can see the heightmap that creates a giant cube terrain and the triplanar mapping in action:
Image
Image

Instead, here you can see a less fake terrain heightmap and how poorly the triplanar mapping performs:
Image
Image
(in the example above I've used a dot texture instead of a checker one just to better show the distortion)

This is how I've modified the Terra HLSL shaders.
Firstly, I've added a Custom_piece_vs_piece_ps.hlsl file:

Code: Select all

@piece( custom_VStoPS )
	@property( triplanar )
		float3 worldPos : TEXCOORD@counter(texcoord);
	@end
@end


@piece( custom_vs_posExecution )
	@property( triplanar )
		outVs.worldPos = worldPos;
	@end
@end


@piece( triplanar_swizzle0 )yz@end
@piece( triplanar_swizzle1 )xz@end
@piece( triplanar_swizzle2 )xy@end
Then, I've replaced in the PixelShader_ps.hlsl file the code below:

Code: Select all

	/// Sample detail maps
@foreach( 4, n )
	@property( detail_map@n )
		float3 detailCol@n = textureMaps[@value(detail_map@n_idx)].Sample(
								samplerStates[@value(detail_map@n_idx)],
								float3( inPs.uv0.xy * material.detailOffsetScale[@value(currOffsetDetail)].zw +
										material.detailOffsetScale[@value(currOffsetDetail)].xy,
										@value(detail_map@n_idx_slice) ) ).xyz;
	@end @property( !detail_map@n )
with:

Code: Select all

@property( triplanar )
	float3 blending = pow( terrainNormals.Sample( terrainNormalsSamplerState, inPs.uv0.xy ).xyz * 2.0f - 1.0f, 4.0 );
	blending /= dot( blending, float3( 1.0, 1.0, 1.0 ) );
@end

	/// Sample detail maps
@foreach( 4, n )
	@property( detail_map@n )
		@property( triplanar )
			@foreach( 3, m )
				float3 detailCol@nTri@m = textureMaps[@value( detail_map@n_idx )].Sample( samplerStates[@value( detail_map@n_idx )], float3( inPs.worldPos.@insertpiece( triplanar_swizzle@m ) * material.detailOffsetScale[@value( currOffsetDetail )].zw, @value( detail_map@n_idx_slice ) ) ).xyz;
			@end
			float3 detailCol@n = detailCol@nTri0 * blending.x + detailCol@nTri1 * blending.y + detailCol@nTri2 * blending.z;
		@end @property( !triplanar )
		float3 detailCol@n = textureMaps[@value(detail_map@n_idx)].Sample(
								samplerStates[@value(detail_map@n_idx)],
								float3( inPs.uv0.xy * material.detailOffsetScale[@value(currOffsetDetail)].zw +
										material.detailOffsetScale[@value(currOffsetDetail)].xy,
										@value(detail_map@n_idx_slice) ) ).xyz;
		@end
	@end @property( !detail_map@n )
This is the material I'm using for the terrain:

Code: Select all

{
	"Terra":
	{
		"Terrain002":
		{
			"detail_weight":
			{
				"texture": "Red.png"
			},
			
			"detail0":
			{
				"scale": [0.05, 0.05],
				"diffuse_map": "CheckerDots.jpg",
				"roughness_map": "White.png"
			}
		}
	}
}
Also the compiled pixel shader seems to be correct (or at least, is what I'm expecting):

Code: Select all

...

	float3 blending = pow( terrainNormals.Sample( terrainNormalsSamplerState, inPs.uv0.xy ).xyz * 2.0f - 1.0f, 4.0 );
	blending /= dot( blending, float3( 1.0, 1.0, 1.0 ) );


	/// Sample detail maps

	
		
			
				float3 detailCol0Tri0 = textureMaps[1].Sample( samplerStates[1], float3( inPs.worldPos.yz * material.detailOffsetScale[0].zw, 0 ) ).xyz;
			
				float3 detailCol0Tri1 = textureMaps[1].Sample( samplerStates[1], float3( inPs.worldPos.xz * material.detailOffsetScale[0].zw, 0 ) ).xyz;
			
				float3 detailCol0Tri2 = textureMaps[1].Sample( samplerStates[1], float3( inPs.worldPos.xy * material.detailOffsetScale[0].zw, 0 ) ).xyz;
			
			float3 detailCol0 = detailCol0Tri0 * blending.x + detailCol0Tri1 * blending.y + detailCol0Tri2 * blending.z;

...
I've already checked blending value, and visually it appears correct either.

Code: Select all

	psOut.colour0.xyz = blending;
	return psOut;
Image
Look at the small hill in the center: it's greenish where is flattest (normal is pointing to world Y) and it's redish/bluish where there's some slope (normal is pointing to world X/world Z).
I was expecting a less intense red/blue since the hill isn't so steep and the world plane XZ should prevaricate the XY and YZ world planes (and infact, in the fourth image from the top, the stretched dots are the most visible ones).

I'm really lost and I've ended ideas.
My only doubt (hope!) is that terrainNormals.Sample( terrainNormalsSamplerState, inPs.uv0.xy ).xyz * 2.0f - 1.0f isn't the normal in world space.

Here are some references about triplanar mapping:
https://medium.com/@bgolus/normal-mappi ... bf39dca05a
https://gamedevelopment.tutsplus.com/ar ... edev-13821
https://developer.nvidia.com/gpugems/GP ... _ch01.html

Disclaimer: this is a work-in-progress task.
Some optimizations are missing (e.g.: don't re-sample the terrainNormals texture) and the triplanar mapping of other textures (diffuse map, roughess maps, metalness maps and normal maps) has to be done yet.

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

definitely should be more green than anything

try this blending I used to use

Code: Select all

	vec3 getTriPlanarBlend(vec3 _wNorm){
	    // in wNorm is the world-space normal of the fragment
	    vec3 blending = abs( _wNorm );
	    blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0
	    float b = (blending.x + blending.y + blending.z);
	    blending /= vec3(b, b, b);
	    return blending;
	}
edit: just noticed that this code is the same as the one in the second link (that's where I get it) so maybe you already tried it..
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

xrgo wrote: try this blending I used to use

Code: Select all

	vec3 getTriPlanarBlend(vec3 _wNorm){
	    // in wNorm is the world-space normal of the fragment
	    vec3 blending = abs( _wNorm );
	    blending = normalize(max(blending, 0.00001)); // Force weights to sum to 1.0
	    float b = (blending.x + blending.y + blending.z);
	    blending /= vec3(b, b, b);
	    return blending;
	}
edit: just noticed that this code is the same as the one in the second link (that's where I get it) so maybe you already tried it..
Yep, I've begun with your blending at the beginning, but I switched to pow approach because I liked it more (looking at the example images).
Btw, those are the results:
Image
Image
More or less, the same crop circles festival as before :evil:

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

what if you output the normals...?
I had a problem with that while ago
http://www.ogre3d.org/forums/viewtopic. ... 64#p538419
but few moments later matias pushed a fix
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

xrgo wrote: what if you output the normals...?
Here they are:
Image
They appear to be correct, I guess...

What is not really clear to me is the purpouse of the * 2.0f - 1.0f in the sampling of the normals texture (terrainNormals.Sample( terrainNormalsSamplerState, inPs.uv0.xy ).xyz * 2.0f - 1.0f).
Btw, this operation is present even in GLSL so I presume is needed for something.

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

TaaTT4 wrote:What is not really clear to me is the purpouse of the * 2.0f - 1.0f
that's usually how you get normals from unsigned textures, normals at some point might be negative, but textures usually goes from 0 to 1, by multipliying by 2 and substracting 1 you recenter the values.. so if you have a value of 0.3 for one channel (axis) of the texture after applying thistransform becomes 0.3*2-1= -0.4 and you get your negative value =D
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

xrgo wrote:
TaaTT4 wrote:What is not really clear to me is the purpouse of the * 2.0f - 1.0f
that's usually how you get normals from unsigned textures, normals at some point might be negative, but textures usually goes from 0 to 1, by multipliying by 2 and substracting 1 you recenter the values.. so if you have a value of 0.3 for one channel (axis) of the texture after applying thistransform becomes 0.3*2-1= -0.4 and you get your negative value =D
It makes sense.

So, everything is correct in theory, but sucks practically... WTF

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

I feel there's something wrong about your normals... I use the same map as you and it looks like this:
Image
as you can see its mostly green (tried to mimic the height, and in fact looks more height than yours, still greener)
are you sure you have the fix I told you before applied?

or maybe its the lightning affecting the screenshot, try returning right after showing the normals

Code: Select all

@property( !detail_maps_normal )
	// Geometric normal
	nNormal = texture( terrainNormals, inPs.uv0.xy ).xyz * 2.0 - 1.0;

outColour.xyz = nNormal.xyz*10;
return;
....
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

Lighting doesn't affect visualization.

Mmhhh... I have a script that copies and builds Terra example as a lib for my project.
I'm not at PC right now, but I have to double check it hasn't silently failed for some reason.

The other difference between you and me is the Render System (I presume you're with OpenGL), but it shouldn't affect normals.
I'll continue the investigation tomorrow morning.
Thanks for the help.

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

TaaTT4 wrote: Mmhhh... I have a script that copies and builds Terra example as a lib for my project.
I'm not at PC right now, but I have to double check it hasn't silently failed for some reason.
I'm a moron: my script has miserably failed without logging any warning/error.
Now that I've put Matias fix inside everything works OK.

Thanks for the help xrgo.
I really appreciated it.

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

Awesome! I would like a screenshot :3
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

xrgo wrote:Awesome! I would like a screenshot :3
Unfortunately, you'll have to wait a bit for artists :wink:

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

TaaTT4 wrote: Unfortunately, you'll have to wait a bit for artists :wink:
xD I meant the one with the circles, just to compare... but only if it takes you 10 seconds, don't bother too much ;D
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75
Contact:

Re: Triplanar mapping

Post by TaaTT4 »

Here they are:
Image
Image

It's perfect!
I've added another image where the terrain has a bit of slope since in the small hill the triplanar mapping is barely visible now.

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

xrgo
OGRE Expert User
OGRE Expert User
Posts: 1148
Joined: Sat Jul 06, 2013 10:59 pm
Location: Chile
x 168

Re: Triplanar mapping

Post by xrgo »

Niiiiiiiice!!!!!!!
Post Reply