Search code examples
unity-game-engineuv-mapping

Unity - How to recolor individual faces of a textured mesh?


I've got a textured mesh with a specific list of triangles that correspond to faces on the mesh that get recolored at runtime using code like this:

        _mesh = GetComponent<MeshFilter>().mesh;
        ...
        var colors = _mesh.colors;
        foreach (int n in trianglesIndexList)
        {
            colors[n] = Color.blue;
        }
        _mesh.colors = colors;

It works great but I had originally intended these faces to be a bright blue. Unfortunately the coloration seems to be blending with the material's texture that I've assigned to the mesh in Unity. Here you can see that the green grass on the texture is mixed with the rather green looking "blue":

enter image description here

I've tried playing with the lighting but that does not seem to be the issue. The mesh coloration that I've got requires use of specific shaders, so I've assigned the Particles / Standard Surface shader to the mesh.

Is there any way to recolor subsections of the texture at runtime and make these faces look bright blue?


Solution

  • Ok, nevermind, I found my own solution by learning how shader code works within about 3 hours and writing a custom shader. Simply use Unity's default standard shader as a starting point and add worldNormal and worldPos inputs like this:

    struct Input
    {
       float2 uv_MainTex;
       float3 worldNormal;
       float3 worldPos;
    };
    

    Then inside the surf function you can filter to just the upward facing normals (which is what I want in this case) and filter these mesh coordinates once again by the world space coordinates.

    void surf(Input IN, inout SurfaceOutputStandard o)
                    {
                    float2 UV;
                    fixed4 c;
    
                    if (abs(IN.worldNormal.x) > 0.5)
                    {
                        // normals facing the left and right side
                        UV = IN.worldPos.yz; 
                        c = tex2D(_MainTex, IN.uv_MainTex);
                    }
                    else if (abs(IN.worldNormal.z) > 0.5)
                    {
                        // normals facing forward and backward
                        UV = IN.worldPos.xy; 
                        c = tex2D(_MainTex, IN.uv_MainTex);
                    }
                    else
                    {
                        // normals facing up and down
                        if (abs(IN.worldPos.x) > 0.5)
                        {
                            UV = IN.worldPos.xz;
                            // COLOR IT BLUE
                            c = tex2D(_MainTex, IN.uv_MainTex) + (_Color * _SinTime.y);
                        }
                        else
                        {
                            UV = IN.worldPos.xz;
                            c = tex2D(_MainTex, IN.uv_MainTex);
                        }
                    }
    

    Can obviously simplify this quite a bit, but this is all I wanted to know how to do. I even figured out how to recolor over time using the built in SinTime variable. Super easy.