# Flat interpolation between vertex colors

I’m struggling to implement a flat transition between two vertices of different colours using a URP Shader Graph. In my head, I feel like I understand exactly what needs to be done for it to work, but I don’t have a well-developed understanding of shaders or shader graph.

Essentially, within a script I generate a mesh, and I assign each vertex a colour. Within the shader graph, I feed the Vertex Colour into a Fragment shader’s Base Colour attribute. This results in a gradient/linear interpolation between each vertex, like so:

Here is a picture of my shader graph.

The desired effect is a flat transition between the colours at the midpoints between the vertices. Consider the example,

where the black line would represent the “edge” between the two colours.

From my limited understanding, this is a result of the Base Colour/Albedo property having the “smooth” interpolation qualifier, whereas what I would need is that “flat” qualifier. I could be barking up the wrong tree with that, however.

If anyone has any experience with this issue and knows any ways to solve it I would greatly appreciate your help. Thanks

The “flat” qualifier you want doesn’t exist. There is a `flat` interpolation modifier for GLSL, which is the equivalent of `nointerpolation` in HLSL. But these don’t do what you’re describing. Instead they use one value over the inter triangle (hence “no interpolation” or “flat”). However it gets that value from the “invoking vertex” of the triangle.

Unfortunately which vertex is considered the “invoking” vertex isn’t consistent between APIs or devices, and is probably not going to automatically be the vertex you want it to be. Best case is each “square” is a solid color, no diagonals. Worst case you get some diagonals, but on transition edges you get every other triangle being different.

However there is a way to get what you want. Instead of interpolating the actual colors between vertices, interpolate a value of 0.0 or 1.0 based on if you want one color or the other. Then in the shader round the weight and lerp between the two colors which are set as material properties. You won’t get diagonals anywhere but where the geometry has a diagonal, but you can have diagonals.

Even better, you can paint the weights in a kind of blurry manner, then you can get diagonals everywhere!

You can also do a little extra math to get nearly free anti-aliased edges.

``````Shader "Unlit/Sharp Blend Interpolation"
{
Properties
{
_ShapeTex ("Shape", 2D) = "grey" {}
_ColorA ("Color A", Color) = (1,0,0,1)
_ColorB ("Color B", Color) = (0,0,1,1)
}
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct v2f
{
float4 pos : SV_POSITION;
float weight : TEXCOORD0;
};

sampler2D _ShapeTex;
float4 _ShapeTex_ST;

float4 _ColorA, _ColorB;

v2f vert (appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);

// getting the weights from a random texture because I didn't want to have to import in a vertex painted mesh
o.weight = tex2Dlod(_ShapeTex, float4(TRANSFORM_TEX(v.texcoord.xy, _ShapeTex), 0, 0)).r;

// uncomment this to test out what it looks like to use binary values of 0 or 1 for the weight
// o.weight = round(o.weight);

// example of weights stored in the vertex color, how you'd probably _actually_ want to do it
// o.weight = v.color.r;

return o;
}

fixed4 frag (v2f i) : SV_Target
{
// preview the weights
// return i.weight;

// anti-aliased sharp weight
float weight = saturate((i.weight - 0.5) / max(0.00001, fwidth(i.weight)));

// lerp between the two colors
return lerp(_ColorA, _ColorB, weight);
}
ENDCG
}
}
}
``````

While the above isn’t written using Shader Graph, it’s not a ton of work to translate it to nodes. The confusing bit is `fwidth()` is the DDXY node. Otherwise use the Vertex Color node as the “weight” and do the 6 nodes worth of math in the frag function.

4 Likes