Unity hand-painted Mesh realizes color gradient

The requirements I received are as follows:

Please see the final renderings

Let’s start

Simplifying the requirement is to create a Mesh with a width*2 and a color gradient based on 2 points.

Creating a square Mesh requires four points, and what I need here is only two, so I regard the two points as the center points and use a Width to get the four points, which is here.

Vector3 pos1 = nextDrawnMesh.transform.position + new Vector3(-width, 0, 0);
Vector3 pos2 = nextDrawnMesh.transform.position + new Vector3(width, 0, 0);
Vector3 pos3 = currentDrawnMesh.transform.position + new Vector3(width, 0, 0);
Vector3 pos4 = currentDrawnMesh.transform.position + new Vector3(-width, 0, 0);

You can see that it is a Vector3, so this value will have different directions when attached to different positions. You can test the desired effect yourself.

Or you can define four points yourself and assign values by yourself without the calculation here.

So there is a method addpos, which is used to add all points. addpos corresponds to adding points, and name corresponds to adding color addcolor.

currentDrawnMesh.SetShaderColor(“_ColorTop”, color);
currentDrawnMesh.SetShaderColor(“_ColorMid”, color);
nextDrawnMesh.SetShaderColor(“_ColorBot”, color);

You don’t need to understand that these three things are the attributes of the Shader. The Shader fields come here, which is a trap. This attribute name is not the name of the attribute in the Shader code. To set the attributes in the Shader, you must carefully look at the Shader. Correspond to each attribute name under the panel. I got it wrong at the beginning. I can’t modify the name in the code when I go to the Shader!

public class ProtractMeshController : MonoBehaviour
{
    public static ProtractMeshController interest;
    public float width = 0.002f;
    public GameObject MeshPosObj;
    public Color color;
    private void Awake()
    {
        interest = this;
        InitProtractMeshPos();
        widthRecording = width;
    }

    float widthRecording;

    public void Update()
    {
        if(widthRecording!= width)
        {
            widthRecording = width;
            InitProtractMeshPos();
        }
    }

    public void AddPos(List<GameObject> pos){
 
        for(int i=0;i< pos.Count; i + + )
        {
            GameObject go = GameObject.Instantiate(MeshPosObj, pos[i].transform.position,Quaternion.identity);
            go.transform.parent = this.transform;
        }

        InitProtractMeshPos();
    }

    public void AddColor(List<Color32> colors)
    {
        if (this.transform.childCount <= 0) return;

        for (int i = 0; i < colors.Count; i + + )
        {
            if (i + 1 < colors.Count)
            {
                DrawnMesh currentDrawnMesh = transform.GetChild(i).GetComponent<DrawnMesh>();
                DrawnMesh nextDrawnMesh = transform.GetChild(i + 1).GetComponent<DrawnMesh>();
                currentDrawnMesh.SetShaderColor("_ColorTop", colors[i + 1]);
                currentDrawnMesh.SetShaderColor("_ColorMid", colors[i + 1]);
                currentDrawnMesh.SetShaderColor("_ColorBot", colors[i]);
            }
        }
    }

    /// <summary>
    /// Initialize mesh points
    /// </summary>
    public void InitProtractMeshPos()
    {
        for (int i = 0; i < transform.childCount; i + + )
        {
            if (i + 1 < transform.childCount)
            {
                DrawnMesh currentDrawnMesh = transform.GetChild(i).GetComponent<DrawnMesh>();
                DrawnMesh nextDrawnMesh = transform.GetChild(i + 1).GetComponent<DrawnMesh>();
                Vector3 pos1 = nextDrawnMesh.transform.position + new Vector3(-width, 0, 0);
                Vector3 pos2 = nextDrawnMesh.transform.position + new Vector3(width, 0, 0);
                Vector3 pos3 = currentDrawnMesh.transform.position + new Vector3(width, 0, 0);
                Vector3 pos4 = currentDrawnMesh.transform.position + new Vector3(-width, 0, 0);
                currentDrawnMesh.posList = new List<Vector3>() { pos4, pos3, pos2, pos1 };
                currentDrawnMesh.SetMesh();
                currentDrawnMesh.SetShaderColor("_ColorTop", color);
                currentDrawnMesh.SetShaderColor("_ColorMid", color);
                nextDrawnMesh.SetShaderColor("_ColorBot", color);
            }
        }
    }

    public void Clean()
    {
        for(int i = transform.childCount; i> 0; i--)
        {
            GameObject.Destroy(transform.GetChild(i - 1).gameObject);
        }
    }
}

This is drawing Mesh! Be careful to add UVs to the mesh you draw, otherwise the material will look wrong!

public class DrawnMesh : MonoBehaviour
{
    public float proportion = 1f;
    public List<Vector3> posList = new List<Vector3>();//The order of assignment is clockwise or counterclockwise (for world coordinate position)
    public MeshRenderer meshRenderer;


    public GameObject ga;
    voidAwake()
    {
        meshRenderer = GetComponent<MeshRenderer>();
    }

    /// <summary>
    /// Set the color attribute of the shader
    /// </summary>
    /// <param name="attributeName"> Three "_ColorTop", "_ColorMid", "_ColorBot" </param>
    /// <param name="color"></param>
    public void SetShaderColor(string attributeName, Color color)
    {
        meshRenderer.materials[0].SetColor(attributeName, color);
    }

    public void SetMiddle(float value)
    {
        meshRenderer.materials[0].SetFloat("_Middle", value);
    }

    public void SetMesh()
    {
        List<Vector3> vertices = new List<Vector3>();
        for (int i = 0; i < posList.Count; i + + )
        {
            Vector3 pos = (posList[i] - transform.position) * proportion;
            vertices.Add(pos);
        }

        DrawPentagon(vertices);
    }

    void DrawPentagon(List<Vector3> vertices)
    {
        Mesh mesh = new Mesh();
        mesh.name = gameObject.name;
        int triangleAmount = vertices.Count - 2;
        int[] triangles = new int[3 * triangleAmount];

        //Based on the number of triangles, calculate the vertex order (index) of drawing triangles
        //The order must be clockwise or counterclockwise
        for (int i = 0; i < triangleAmount; i + + )
        {
            triangles[3 * i] = 0;
            triangles[3 * i + 1] = i + 1;
            triangles[3 * i + 2] = i + 2;
        }

        Vector2[] baseUVs = new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), };

        mesh.vertices = vertices.ToArray();
        mesh.uv = baseUVs;
        mesh.triangles = triangles;

        GetComponent<MeshFilter>().mesh = mesh;
    }
}

This is the color collision shader.

Shader "Unlit/Gradient"
{
Properties
{
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
_ColorTop("Top Color", Color) = (1, 1, 1, 1)
_ColorMid("Mid Color", Color) = (1, 1, 1, 1)
_ColorBot("Bot Color", Color) = (1, 1, 1, 1)
_Middle("Middle", Range(0.001, 0.999)) = 1
_test("Test", Range(0.001, 0.999)) = 1
}
SubShader
{
Tags {"Queue" = "Background" "IgnoreProjector" = "True"}
LOD 100
ZWriteOn
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment fragment
#include "UnityCG.cginc"
fixed4 _ColorTop;
fixed4 _ColorMid;
fixed4 _ColorBot;
float _Middle;
float _test;
struct v2f
{
float4 pos : SV_POSITION;
float4 texcoord : TEXCOORD0;
};
v2f vert(appdata_full v)
{
v2f o;
if (v.vertex.y > _test)
{
v.vertex.y = _test;
}
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoord = v.texcoord;
return o;
}
fixed4 frag(v2f i) : COLOR
{
fixed4 c = lerp(_ColorBot, _ColorMid, i.texcoord.y / _Middle) * step(i.texcoord.y, _Middle);
c + = lerp(_ColorMid, _ColorTop, (i.texcoord.y - _Middle) / (1 - _Middle)) * step(_Middle, i.texcoord.y);
c.a = 1;
return c;
}
ENDCG
}
}
}