Unity, modify standard shader to randomly flip texture?

If you want to flip use alpha = 0 for those objects, and for others alpha = 1.

In vertex data of your shader write something like this:

half flip = 1 -2*vertex.a;

So the flip would be -1 or 1.

From the comments:

enter image description here

Note however that each time you access .mesh , in Unity, you are making a whole new mesh:

public class TestFlipUV : MonoBehaviour
    {
        private void Awake()
        {

            Mesh mesh = GetComponent<MeshFilter>().mesh;
            // NOTE. IN UNITY, ACCESSING .mesh CREATES
            // A NEW INSTANCE OF THAT MESH EVERY TIME

            Vector2[] uv2 = mesh.uv;

            float rnd = Random.RandomRange(0.0f, 1.0f);
            if (rnd > 0.5f)
            {
                for (int i = 0; i < uv2.Length; i++)
                {
                    uv2[i].x *= -1;    
                }
            }

            rnd = Random.RandomRange(0.0f, 1.0f);
            if (rnd > 0.5f)
            {
                for (int i = 0; i < uv2.Length; i++)
                {
                    uv2[i].y *= -1;    
                }
            }
            mesh.uv2 = uv2;
        }
    }

... and then ...

Shader "Unlit/FlipUV"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float2 uv2 : TEXCOORD1;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _MainTex_TexelSize;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.uv *= v.uv2;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

I noticed that you really really want to do this with nothing but a shader. You can't do this with a shader alone. The issue is not being able to generate a random number in a shader. The problem is being able to do it once. I haven't found a way to do so and don't think you can.

This is a problem that should be tackled with a code on the C# side.

(1) it's totally trivial to generate a few different versions of the material, each with different tiling, and randomly select one of those for each cube. that's not the way to go

(2) note that if you vary a material, it of course makes more than one copy. you can't have thousands and thousands of materials.

Not a problem at-all. This is what MaterialPropertyBlock is used for. It allows you to modify a shader property without creating new instance of that material.

"Use it in situations where you want to draw multiple objects with the same material, but slightly different properties." MaterialPropertyBlock

The code below would have cause many instances of the material to be created:

void Start()
{
    MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
    int tileX = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
    int tileY = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
    Vector2 tile = new Vector2(tileX, tileY);
    meshRenderer.material.SetTextureScale("_MainTex", tile);
}

With MaterialPropertyBlock, this issue is solved. Material copy is not made. Since you care about performance, you should also use Shader.PropertyToID:

void Start()
{
    int propertyID = Shader.PropertyToID("_MainTex_ST");
    meshRenderer = gameObject.GetComponent<MeshRenderer>();
    int tileX = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
    int tileY = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
    Vector2 tile = new Vector2(tileX, tileY);

    MaterialPropertyBlock matPropBlock = new MaterialPropertyBlock();
    //Get the current MaterialPropertyBlock settings
    meshRenderer.GetPropertyBlock(matPropBlock);
    //Assign the new tile value
    matPropBlock.SetVector(propertyID, tile);
    //matPropBlock.SetVector(Shader.PropertyToID("_MainTex_ST"), tile);
    //Apply the modified MaterialPropertyBlock back to the renderer
    meshRenderer.SetPropertyBlock(matPropBlock);
}

If this is done once in the game, it doesn't really make sense to attach the script to each GameObject. Just attach it t to one empty GameObject only, find all the objects by tag then run the code on each one.

void Start()
{
    GameObject[] objs = GameObject.FindGameObjectsWithTag("YourObjTag");
    int propertyID = Shader.PropertyToID("_MainTex_ST");
    for (int i = 0; i < objs.Length; i++)
    {
        MeshRenderer meshRenderer = objs[i].GetComponent<MeshRenderer>();
        int tileX = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
        int tileY = (UnityEngine.Random.Range(1, 3) == 1) ? 1 : -1;
        Vector2 tile = new Vector2(tileX, tileY);

        MaterialPropertyBlock matPropBlock = new MaterialPropertyBlock();
        //Get the current MaterialPropertyBlock settings
        meshRenderer.GetPropertyBlock(matPropBlock);
        //Assign the new tile value
        matPropBlock.SetVector(propertyID, tile);
        //Apply the modified MaterialPropertyBlock back to the renderer
        meshRenderer.SetPropertyBlock(matPropBlock);
    }
}

Pseudorandomly flipped

Shader "RandomFlip"
{
Properties
{
    _MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
    Tags { "RenderType"="Opaque" "DisableBatching"="True" }
    LOD 100

    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile_fog

        #include "UnityCG.cginc"

        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            UNITY_FOG_COORDS(1)
            float4 vertex : SV_POSITION;
        };

        sampler2D _MainTex;
        float4 _MainTex_ST;

        v2f vert (appdata v)
        {
            // position of pivot in world space
            float3 pivotWorldPos = float3( unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w );

            // randomness achieved by feeding trigonometry function with large numbers
            float flipHorizontally = sin( ( pivotWorldPos.x + pivotWorldPos.y + pivotWorldPos.z ) * 1000 ) > 0;
            float flipVertically = cos( ( pivotWorldPos.x + pivotWorldPos.y + pivotWorldPos.z ) * 1000 ) > 0;

            // randomly flipping uvs 
            float2 uv = lerp( v.uv, float2( 1.0 - v.uv.x, v.uv.y ), flipVertically );
            uv = lerp( uv, float2( uv.x, 1.0 - uv.y ), flipHorizontally ); 

            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(uv, _MainTex);
            UNITY_TRANSFER_FOG(o,o.vertex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            // sample the texture
            fixed4 col = tex2D(_MainTex, i.uv);
            // apply fog
            UNITY_APPLY_FOG(i.fogCoord, col);
            return col;
        }
        ENDCG
    }
}
}

Though you better not moving these objects :D Because randomness here are coming from position of object in world space. Batching will also break the randomness.

Tags:

Shader

Unity3D