Ah, awesome!
This helped me smooth out terrain type edges in my prototype square-tile-based world so that it looks a bit less blocky, thanks very much!
Before:
After:
(TODO fix shader logic to prevent dimming)
In case it’s useful for anyone I’ll paste my WIP shader here - the next thing I’m going to do is attempt to replace all the different textures with a texture atlas, but I wanted to get a naiive texture blend working before attempting that.
Shader "Custom/VertexColors" {
Properties {
_GrasslandTex ("Grassland Texture", 2D) = "white" {}
_ForestTex ("Forest Texture", 2D) = "white" {}
_OceanTex ("Ocean Texture", 2D) = "white" {}
_LakeTex ("Lake Texture", 2D) = "white" {}
_RiverTex ("River Texture", 2D) = "white" {}
_CliffTex ("Cliff Texture", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:vert
#pragma target 4.0
struct Input {
float2 uv_GrasslandTex;
float2 uv_ForestTex;
float2 uv_OceanTex;
float2 uv_LakeTex;
float2 uv_RiverTex;
float2 uv_CliffTex;
float4 color : COLOR;
// Texture index for the tile to which this vertex belongs
float self_texture_index;
// Provided by Unity
float3 worldPos;
// Texture indices starting from SquareDirection#North and moving clockwise, ending with SquareDirection#Southeast
float4 neighbor_texture_indices_from_north;
// Texture indices starting from SquareDirection#South and moving clockwise, ending with SquareDirection#Northwest
float4 neighbor_texture_indicies_from_south;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
sampler2D _GrasslandTex;
sampler2D _ForestTex;
sampler2D _OceanTex;
sampler2D _LakeTex;
sampler2D _RiverTex;
sampler2D _CliffTex;
void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input, o);
// This is the hack(?) recommended by Unity devs.
// The custom data values we want on a per-pixel basis are packed into a number of
// otherwise unused fields (tangent, texcoord1, texcoord2).
// https://discussions.unity.com/t/383470
o.self_texture_index = v.tangent[0];
o.neighbor_texture_indices_from_north = v.texcoord1;
o.neighbor_texture_indicies_from_south = v.texcoord2;
}
float4 eval_texture(float texture_index, Input IN)
{
// Comparing with plenty of forgiveness to compensate for the inexact int->float conversion for this data.
if (texture_index <= 0.5)
{
return tex2D (_GrasslandTex, IN.uv_GrasslandTex);
}
if (texture_index <= 1.5)
{
return tex2D (_ForestTex, IN.uv_GrasslandTex);
}
if (texture_index <= 2.5)
{
return tex2D (_OceanTex, IN.uv_GrasslandTex);
}
if (texture_index <= 3.5)
{
return tex2D (_LakeTex, IN.uv_GrasslandTex);
}
if (texture_index <= 4.5)
{
return tex2D (_RiverTex, IN.uv_GrasslandTex);
}
if (texture_index <= 5.5)
{
return tex2D (_CliffTex, IN.uv_GrasslandTex);
}
return float4(1,1,1,1);
}
// Calculations in this method depend on a tile size of 1
fixed4 calc_blended_texture(Input IN)
{
const float tile_origin_offset = 0.5;
const float x_weight = (IN.worldPos.x + tile_origin_offset) % 1;
const float z_weight = (IN.worldPos.z + tile_origin_offset) % 1;
const float4 summed_texture = eval_texture(IN.self_texture_index, IN) +
eval_texture(IN.neighbor_texture_indices_from_north.x, IN) * z_weight +
eval_texture(IN.neighbor_texture_indices_from_north.y, IN) * sqrt(pow(x_weight, 2) + pow(z_weight, 2)) +
eval_texture(IN.neighbor_texture_indices_from_north.z, IN) * x_weight +
eval_texture(IN.neighbor_texture_indices_from_north.w, IN) * sqrt(pow(x_weight, 2) + pow(1 - z_weight, 2)) +
eval_texture(IN.neighbor_texture_indicies_from_south.x, IN) * (1 - z_weight) +
eval_texture(IN.neighbor_texture_indicies_from_south.y, IN) * sqrt(pow(1 - x_weight, 2) + pow(1 - z_weight, 2)) +
eval_texture(IN.neighbor_texture_indicies_from_south.z, IN) * (1 - x_weight) +
eval_texture(IN.neighbor_texture_indicies_from_south.w, IN) * sqrt(pow(1 - x_weight, 2) + pow(z_weight, 2));
const float diagonal_distance = 1.4142135623730951;
return fixed4(summed_texture / (5 + 4 * diagonal_distance));
}
void surf (Input IN, inout SurfaceOutputStandard o) {
const fixed4 c = calc_blended_texture(IN);
o.Albedo = c.rgb * IN.color;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}