Search Unity

Rotate/UV transform a projection shader

Discussion in 'Shaders' started by MJaksa, May 16, 2018.

  1. MJaksa

    MJaksa

    Joined:
    Jan 19, 2018
    Posts:
    3
    I'm sorry if this has been asked before, but I've only been able to find similar threads thus far and nothing that gets me exactly what I need.

    I need a shader that can both project onto a surface via a Unity projector and rotate UVs. The goal is a sort of radar 'blip' animation that gets projected onto my environment. Thus far I've had success either projecting the texture or rotating it, but not both (see comments in the code block below).

    I'm very new to shader scripting and most of this is cobbled together from other shaders I've seen. I'm sure it could be optimized a bit.

    Thanks in advance for any insights!

    Code (CSharp):
    1.  
    2. Shader "Custom/RadarBlip" {
    3.     Properties {
    4.         _Color ("Tint Color", Color) = (1,1,1,1)
    5.         _Attenuation ("Falloff", Range(0.0, 1.0)) = 1.0
    6.         _RotationTex ("Rotation Texture", 2D) = "gray" {}
    7.         _RotationSpeed("Rotation Speed", Float) = 0.0
    8.     }
    9.     Subshader {
    10.         Tags {"Queue"="Transparent"}
    11.  
    12.         Pass {
    13.             ZWrite Off
    14.             ColorMask RGB
    15.             Blend SrcAlpha One
    16.             Offset -1, -1
    17.  
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #include "UnityCG.cginc"
    22.             sampler2D _RotationTex;
    23.             float _RotationSpeed;
    24.  
    25.             float4x4 unity_Projector;
    26.  
    27.             fixed4 _Color;
    28.             float _Attenuation;
    29.  
    30.             struct appdata {
    31.                 float4 vertex : POSITION;
    32.                 float4 texcoord : TEXCOORD0;
    33.             };
    34.             struct v2f {
    35.                 float4 pos : SV_POSITION;
    36.                 float4 uvShadow : TEXCOORD0;
    37.                 float2 uv : TEXCOORD1;
    38.             };
    39.  
    40.             v2f vert (appdata v) {
    41.  
    42.                 float s = sin ( _RotationSpeed * _Time);
    43.                 float c = cos ( _RotationSpeed * _Time);
    44.                 float2x2 rotationMatrix = float2x2( c, -s, s, c);
    45.                 float offsetX = .5;
    46.                 float offsetY = .5;
    47.                 float x = v.texcoord.x - offsetX;
    48.                 float y = v.texcoord.y - offsetY;
    49.  
    50.                 v2f o;
    51.                 o.uv = mul (float2(x, y), rotationMatrix ) + float2(offsetX, offsetY);
    52.                 o.pos = UnityObjectToClipPos (v.vertex);
    53.                 o.uvShadow = mul (unity_Projector, v.vertex);
    54.  
    55.                 return o;
    56.             }
    57.  
    58.             fixed4 frag (v2f i) : SV_Target {
    59.                    
    60.                 // This returns a rotating texture, but it's not projected correcty -- it's giant and fills the entire surface of anything beneath it.
    61.                 return tex2D(_RotationTex, i.uv);
    62.  
    63.                 // This returns a correct projection, but it doesn't rotate anymore.
    64.                 // I need to merge these two commands.
    65.  
    66.                 fixed4 texCookie = tex2Dproj (_RotationTex, UNITY_PROJ_COORD(i.uvShadow));
    67.                 fixed4 outColor = _Color * texCookie.a;
    68.                 float depth = i.uvShadow.z;
    69.                 return outColor * clamp(1.0 - abs(depth) + _Attenuation, 0.0, 1.0);
    70.             }
    71.             ENDCG
    72.         }
    73.     }
    74. }
     
  2. MJaksa

    MJaksa

    Joined:
    Jan 19, 2018
    Posts:
    3
    Still stuck on this -- does anyone have any ideas?
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    You need to rotate the projected UV. The easiest way to go about that would be to rotate the projected UV after the perspective divide.

    That's going to take a long time to explain, and if you're curious look up the processes of transforming from camera or view space, to homogeneous clip space, to normalized device space. The short version is tex2Dproj(_tex, i.uvShadow)* can be rewritten like this:

    tex2D(_tex, i.uvShadow.xy / i.uvShadow.w);

    That divide by w is the perspective divide, but the result of that divide in this case is the usual float2 texture UV you might be more used to. The easiest solution for you is to do the rotation in the fragment shader on that projected UV.

    float s = sin ( _RotationSpeed * _Time);
    float c = cos ( _RotationSpeed * _Time);
    float2x2 rotationMatrix = float2x2( c, -s, s, c);

    float2 uv = i.uvShadow.xy / i.uvShadow.w;
    uv = mul(uv - float2(0.5, 0.5), rotationMatrix) + float2(0.5, 0.5);
    fixed4 = tex2D(_RotationTex, uv);


    And obviously remove the rotation code from the vertex shader.

    * Note: I skip the UNITY_PROJ_COORD here because it only does something when compiling the shader for the PS Vita.
     
  4. MJaksa

    MJaksa

    Joined:
    Jan 19, 2018
    Posts:
    3
    Thank you so much!! This is extremely helpful.

    I'm really interested in learning this stuff but have had a hard time finding good API documentation out there. Something that explains basic HLSL data types and expected values for noobs like me. Anyways, thanks again for the help.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Microsoft has HLSL documentation on their site. Like all things on MSDN it's slow and painful to navigate, but there's a lot of good data.
    https://msdn.microsoft.com/en-us/library/windows/desktop/bb509638(v=vs.85).aspx

    Nvidia's Cg documentation is nicer for getting a list of useful parts. Note that Unity uses HLSL and not Cg these days, regardless of Unity's own documentation on the topic. For the most part they're equivalent, but it's not 100% identical. If something isn't working for you when working from the Cg documentation, double check the HLSL documentation.
    http://developer.download.nvidia.com/cg/index_stdlib.html

    But that's just the basics of the shader language itself. For ShaderLab, Unity's own syntax for defining shader states, and the shader code, there are many resources out there, but Unity's own documentation can get you started:
    https://docs.unity3d.com/Manual/SL-Shader.html

    Otherwise I usually send people towards Alan Zucconi and CatLike Coding:
    https://www.alanzucconi.com/2015/06/10/a-gentle-introduction-to-shaders-in-unity3d/
    https://catlikecoding.com/unity/tutorials/rendering/

    For the math of what I did above, that's not really specific to HLSL, or rendering, that's just matrix math.
     
    Seyed_Morteza_Kamaly likes this.