And I found this triplanar shader here: “http://www.martinpalko.com/triplanar-mapping/”
But wanted it to work on a terraformed sphere (which I have already, generated procedurally, along with the correct normals and tangents). I just found an old post here I thought could help: How to apply a Tri-planar shader to a planet? - Questions & Answers - Unity Discussions
End goal: Have the cliff texture apply to the sides (normal towards x & z), and blend the grass along the top (normal close to y).
However, in combining the two I get texture stretching in SOME areas. I put the code above at the top of the “surf” function of the triplanar shader. I converted it to work in model space, and it seems ok only on some sides, but the transition has bad stretching in some spots - perhaps the shader is ok and my normals are bad? I tested my normals and tangents before and they seem just fine (lighting works well). Here is my combined shader:
Shader "Custom/Triplanar/Planet"
{
Properties
{
_DiffuseMapX("Diffuse Map X", 2D) = "white" {}
_DiffuseMapY("Diffuse Map Y", 2D) = "white" {}
_DiffuseMapZ ("Diffuse Map Z", 2D) = "white" {}
_TextureScale("Texture Scale", float) = 1
_TriplanarBlendSharpness("Blend Sharpness", float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma target 3.0
#pragma surface surf Lambert
#pragma vertex vert
sampler2D _DiffuseMapX;
sampler2D _DiffuseMapY;
sampler2D _DiffuseMapZ;
float _TextureScale;
float _TriplanarBlendSharpness;
struct Input
{
float3 worldPos;
float3 worldNormal;
float3 vertex;
float3 normal;
};
void vert(inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input, o);
o.vertex = v.vertex;
o.normal = v.normal;
}
void surf (Input IN, inout SurfaceOutput o)
{
float3 up = normalize(IN.vertex);
float3 right = normalize(cross(up, float3(0, 1, 0)));
float3 forward = normalize(cross(right, up));
float3 localNormal = float3(dot(IN.normal, right), dot(IN.normal, up), dot(IN.normal, forward));
// Find our UVs for each axis based on world position of the fragment.
half2 yUV = IN.vertex.xz / _TextureScale;
half2 xUV = IN.vertex.zy / _TextureScale;
half2 zUV = IN.vertex.xy / _TextureScale;
// Now do texture samples from our diffuse map with each of the 3 UV set's we've just made.
half3 yDiff = tex2D (_DiffuseMapY, yUV);
half3 xDiff = tex2D (_DiffuseMapX, xUV);
half3 zDiff = tex2D (_DiffuseMapZ, zUV);
// Get the absolute value of the world normal.
// Put the blend weights to the power of BlendSharpness, the higher the value,
// the sharper the transition between the planar maps will be.
half3 blendWeights = pow (abs(localNormal), _TriplanarBlendSharpness);
// Divide our blend mask by the sum of it's 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.
o.Albedo = xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z;
}
ENDCG
}
}
This is for a Unity game I’m building here: http://martianworlds.com
Edit: I think my issue was the coordinate planes used for UV were not rotated with the normal, but that would always result in a UV of 0,0 on the X,Z plane (Y axis). Turns out plan B is working the best, with very little FPS drop, so not bad. I’ll work in this direction. For those who wish to know, the solution for me was to use work in local object space, using the triplanar routines for a single texture. I did this for two textures and blended between them using the dot product of the surface normal and the normalized local vertex point (the “up” vector from the planet object’s center to the surface).