- Home /
How to correctly access and use terrain texture tiling from custom terrain shader?
I've been recently attempting to make a triplanar shader for the terrain. I've worked out how to make terrain shaders, I've worked out how to use them on the terrain, I've worked out how to do the basics of triplanar shading but I'm really struggling to get each texture to tile correctly as defined in the inspector.
So the basics of the code I'm using for the triplanar stuff is as follows:
// Find our UVs for each axis based on world position of the fragment.
half2 yUV = wp.xz;
half2 xUV = wp.zy;
half2 zUV = wp.xy;
// Now get texture samples from the texture with each of the 3 UV sets we've just made.
half4 yProjection = tex2D (texToSample, yUV);
half4 xProjection = tex2D (texToSample, xUV);
half4 zProjection = tex2D (texToSample, zUV);
// Get the absolute value of the world normal.
half3 blendWeights = abs(wn);
// Divide our blend mask by the sum of its components, this will make x+y+z=1
blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z);
// Finally, blend together all three samples based on the blend mask.
return (xProjection * blendWeights.x) + (yProjection * blendWeights.y) + (zProjection * blendWeights.z);
"wp" is the world position while "texToSample" is the texture being used to texture the terrain. This function gets called once for each texture. I know this code works as when I use it it textures the terrain with each texture tiling over 1 square metre.
Then I tried to get each texture to tile over the distance specified in the inspector. First, I tried this:
half2 yUV = wp.xz / texST.xy;
half2 xUV = wp.zy / texST.xy;
half2 zUV = wp.xy / texST.xy;
"texST" is a float4 that gets passed into the function for each texture. I populate the float4s for each texture using this code that I'm told Unity automatically populates:
// The "_ST" properties are float4s that Unity automatically creates for each texture
// The first 2 values are tiling and the last 2 are scale offset
sampler2D _Control;
float4 _Control_ST;
sampler2D _Splat0,_Splat1,_Splat2,_Splat3;
float4 _Splat0_ST,_Splat1_ST,_Splat2_ST,_Splat3_ST;
This code kind of worked. Now each texture tiled differently, but the tiling was completely wrong. Next, I tried this:
half2 yUV = wp.xz * texST.xy;
half2 xUV = wp.zy * texST.xy;
half2 zUV = wp.xy * texST.xy;
This gave different results but was still incorrect. After looking at it for a lot longer and comparing each inspector tiling value with what I saw on the terrain, I worked out that the tiling seemed to be related to the actual terrain size. So I tried this:
half2 yUV = wp.xz / (half2(2000.0,2000.0) / texST.xy);
half2 xUV = wp.zy / (half2(2000.0, 2000.0) / texST.xy);
half2 zUV = wp.xy / (half2(2000.0, 2000.0) / texST.xy);
Amazingly, this seems to give the correct tiling. However, now I get black spots appearing all over the terrain. These appear even with the lighting and effects turned off in the scene view. Also, their size and location changes depending on which angle you look at them from and how far away you are. I thought it might be to do with some divide by 0 error, but the code below didn't work either:
half2 yUV = wp.xz / (half2(2000.0,2000.0) / (texST.xy + half2(0.01, 0.01)));
half2 xUV = wp.zy / (half2(2000.0, 2000.0) / (texST.xy + half2(0.01, 0.01)));
half2 zUV = wp.xy / (half2(2000.0, 2000.0) / (texST.xy + half2(0.01, 0.01)));
I'm not particularly experienced with shaders and would really appreciate some suggestions on how to fix this or even just some insight on the correct usage of the texture_ST float4s. I've included an image of the black spots on the terrain if it helps.
Your answer
Follow this Question
Related Questions
How does the unity terrain shader handle an arbitrary amount of textures ? 1 Answer
Shader for ZTest Between Terrain and Objects 0 Answers
Forward Rendering for Terrain, overriding the shader? 0 Answers
Shader with "alpha" parameter no longer writes to Z-Buffer 2 Answers
Terrain shader optimisation 1 Answer