How to modify UV mapping in Shader?

I know that I can modify UV mapping in script by modifying mesh.uv. However, how can I do it in Shader?


The gray part in the screenshot is transparent.

In my shader, I want to slice this texture into 9 pieces, and only render one of them each time under certain conditions. In script, for each rectangle, i can do

mesh.uv = Vector2[ ] topLeftCellUV {
new Vector2 (0f, 2f/3f), //bottom left
new Vector2 (oneThird, 2f/3f), //bottom right
new Vector2 (0f, 1f), //top left
new Vector2(1f/3f, 1f) //top right
};

But i have no idea how do to it in shader.

The feature i’m trying to implement is using projector to highlight my grid (not the whole grid, just the grid under selected objects).

This is the shader i’m referring to:

Shader "Projector/AdditiveTint" {
Properties {
    _Color ("Tint Color", Color) = (1,1,1,1)
    _Attenuation ("Falloff", Range(0.0, 1.0)) = 1.0
    _ShadowTex ("Cookie", 2D) = "gray" {}
}
Subshader {
    Tags {"Queue"="Transparent"}
    Pass {
        ZWrite Off
        ColorMask RGB
        Blend SrcAlpha One // Additive blending
        Offset -1, -1

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
       
        struct v2f {
            float4 uvShadow : TEXCOORD0;
            float4 pos : SV_POSITION;
        };
       
        float4x4 unity_Projector;
        float4x4 unity_ProjectorClip;
       
        v2f vert (float4 vertex : POSITION)
        {
            v2f o;
            o.pos = UnityObjectToClipPos (vertex);
            o.uvShadow = mul (unity_Projector, vertex);
            return o;
        }
       
        sampler2D _ShadowTex;
        fixed4 _Color;
        float _Attenuation;
       
        fixed4 frag (v2f i) : SV_Target
        {
            // Apply tint & alpha mask
            fixed4 texCookie = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
            fixed4 outColor = _Color * texCookie.a;
            // Distance attenuation
            float depth = i.uvShadow.z; // [-1(near), 1(far)]
            return outColor * clamp(1.0 - abs(depth) + _Attenuation, 0.0, 1.0);
        }
        ENDCG
    }
}

Thanks!!!

1 Like

The normal answer for the question in the title is you need to scale and offset the UVs. For something like a quad you can assume a uv range of 0.0 to 1.0 on the x and y, so if you want a square that’s only a third of that range, and offset to a particular trient, you need to multiply the UVs by 1/3, then add either 1/3 or 2/3 to the x and/or y.

For a projector this can be confusing is there’s no clear place where there’s a 0.0 to 1.0 range UV, but there is. The uvShadow variable is in projection space and is not in this range, but rather a 0.0 to w range, where w is the w component of the uvShadow variable. The tex2Dproj() converts that projection space coordinate to a 0.0 to 1.0 range by dividing the xy components by w. This division can be done manually to get a uv in 0.0 to 1.0 range which you can manipulate, and then you can use a tex2D() instead of tex2Dproj to sample the texture.

float2 uv = i.uvShadow.xy / i.uvShadow.w;
fixed4 texCookie = tex2D(_ShadowTex, uv);

By default all texture properties in Unity shaders show a scale & offset in the material inspector. For projector shaders these don’t do anything since it’s they’re being applied in the shader, but it can be trivially added using the built in macros most other shaders do use.

// uniform declaration (next to where “sampler2D _ShadowTex;” is)
float4 _ShadowTex_ST;

float2 uv = TRANSFORM_TEX(i.uvShadow.xy / i.uvShadow.w, _ShadowTex);
fixed4 texCookie = tex2D(_ShadowTex, uv);

That TRANSFORM_TEX macro really just takes the name of the texture you pass in, adds “_ST” to the end, and then multiplies and adds the xy and zw of that variable, like this:
Copied from UnityCG.cginc

// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)

That’s applying the scale and offset you set in the material’s properties. If you need to change it from script you can do so using this:

3 Likes

Thank you so much! this is very helpful!