Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Trying to throw shadows from a skewed quad

Discussion in 'Scripting' started by RealHandy, Aug 28, 2020.

  1. RealHandy

    RealHandy

    Joined:
    Jul 11, 2020
    Posts:
    21
    I'm using https://github.com/brandonjmatthews/unity-skew-quad to create a skewed quad, but I want to light it and have it throw shadows. I've tried a number of different attempts to merge the unlit skew shader from unity-skew-quad with the standard shader, shadow caster, or generated standard surface shader. None of them work right.
    skew problem.png
    As you can see in the pic, my tree is skewed the way I want, but there's also a 2x2 of the skewed tree that is what's highlighted when I select the quad when I've added the skew as a subshader of the standard shader. That 2x2 tile doesn't happen with the tree itself because its material has the unity-skew-quad unlit shader, which has this one line that is the only difference between the unity-skew-quad unlit shader and the default NewUnlitShader.

    //fixed4 col = tex2D(_MainTex, i.uv);
    fixed4 col = tex2D(_MainTex, i.uv.xy / i.uv2.x);

    If I use NewUnlitShader on my skewed quad material, the main tree turns into 4 tiled trees, too. So that's one problem.

    The other problem is that the shadow of the skewed tree is both A) 4 little trees, and B) not skewed! That is, the shadows are as though they are cast by invisible trees that are in the unskewed green rectangle that represents the quad asset.

    I'm a total noob to shader scripting, and I'm having the usual problems both with the language and with light, vertex, and fragment shader concepts. But it seems like someone who knows what they're doing could immediately spot the errors that are causing unexpected 2x2 tiling and non-skewing of the shadows.

    The skewed quad is created using this code, and its altering of the mesh is possibly not being applied in some way that results in the non-skewed shadows. Don't know how it would result in the 2x2 tiling issue, though.

    Can anyone suggest things that would help me eliminate these issues? Thanks!
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public enum Mapping {
    6.     SKEW,
    7.     AFFINE,
    8.     CLIP
    9. }
    10.  
    11. [ExecuteInEditMode]
    12. public class SkewQuad : MonoBehaviour {
    13.     public Mapping mapping = Mapping.SKEW;
    14.     public Vector3 TopLeft;
    15.     public Vector3 TopRight;
    16.     public Vector3 BottomLeft;
    17.     public Vector3 BottomRight;
    18.  
    19.     Mesh mesh;
    20.     MeshFilter mf;
    21.  
    22.     void Start () {
    23.         mesh = new Mesh();
    24.         mf = GetComponent<MeshFilter>();
    25.     }
    26.    
    27.     public void UpdateMeshAndTexture() {
    28.         bool updated = false;
    29.         // Build the Quad
    30.         Vector3[] vertices = new Vector3[4] {
    31.             BottomLeft,
    32.             BottomRight,
    33.             TopRight,
    34.             TopLeft
    35.         };
    36.  
    37.         mesh.vertices = vertices;
    38.  
    39.         int[] triangles = new int[6] {
    40.             0,2,1,
    41.             0,3,2
    42.         };
    43.  
    44.         mesh.triangles = triangles;
    45.  
    46.         Vector3[] normals = new Vector3[4] {
    47.             -Vector3.forward,
    48.             -Vector3.forward,
    49.             -Vector3.forward,
    50.             -Vector3.forward
    51.         };
    52.         mesh.normals = normals;
    53.  
    54.         Vector2[] uv = new Vector2[4] {
    55.             new Vector2(0,0),
    56.             new Vector2(1,0),
    57.             new Vector2(1,1),
    58.             new Vector2(0,1),
    59.         };
    60.  
    61.  
    62.         Vector2[] uv2 = new Vector2[4] {
    63.             new Vector2(1,0),
    64.             new Vector2(1,0),
    65.             new Vector2(1,0),
    66.             new Vector2(1,0),
    67.         };
    68.  
    69.         switch(mapping) {
    70.             case Mapping.SKEW:
    71.                 // I used uv (qu, qv) and uv2 (q), there is potential for this to be an issue on different devices.
    72.  
    73.                 float ax = vertices[2].x - vertices[0].x;
    74.                 float ay = vertices[2].y - vertices[0].y;
    75.                 float bx = vertices[3].x - vertices[1].x;
    76.                 float by = vertices[3].y - vertices[1].y;
    77.  
    78.                 float cross = (ax * by) - (ay * bx);
    79.  
    80.                 // Only skew if it's possible (ie. clockwise tri vertices and convex shape)
    81.                 if (cross != 0 && mapping == Mapping.SKEW) {
    82.                     float cy = vertices[0].y - vertices[1].y;
    83.                     float cx = vertices[0].x - vertices[1].x;
    84.  
    85.                     float s = (ax * cy - ay * cx) / cross;
    86.            
    87.                     if (s > 0 && s < 1) {
    88.                         float t = (bx * cy - by * cx) / cross;
    89.  
    90.                         if (t > 0 && t < 1) {
    91.                             float q0 = 1 / (1-t);
    92.                             float q1 = 1 / (1-s);
    93.                             float q2 = 1 / t;
    94.                             float q3 = 1 / s;
    95.                             uv[0] = new Vector2(uv[0].x * q0, uv[0].y * q0);
    96.                             uv[1] = new Vector2(uv[1].x * q1, uv[1].y * q1);
    97.                             uv[2] = new Vector2(uv[2].x * q2, uv[2].y * q2);
    98.                             uv[3] = new Vector2(uv[3].x * q3, uv[3].y * q3);
    99.                             uv2[0] = new Vector2(q0, 0);
    100.                             uv2[1] = new Vector2(q1, 0);
    101.                             uv2[2] = new Vector2(q2, 0);
    102.                             uv2[3] = new Vector2(q3, 0);
    103.                             updated = true;
    104.                         }
    105.                     }
    106.                 }
    107.  
    108.                 if (!updated) {
    109.                     throw new System.Exception("SKEW: Shape must be convex and triangle vertices must be clockwise.");
    110.                 }
    111.             break;
    112.             case Mapping.CLIP:
    113.                 uv[0] = vertices[0];
    114.                 uv[1] = vertices[1];
    115.                 uv[2] = vertices[2];
    116.                 uv[3] = vertices[3];
    117.        
    118.                 uv2 = new Vector2[4] {
    119.                     new Vector2(1,0),
    120.                     new Vector2(1,0),
    121.                     new Vector2(1,0),
    122.                     new Vector2(1,0),
    123.                 };
    124.  
    125.             break;
    126.             case Mapping.AFFINE:
    127.             break;
    128.         }
    129.  
    130.         mesh.uv = uv;
    131.         mesh.uv2 = uv2;
    132.         mf.mesh = mesh;
    133.  
    134.     }
    135. }
    136.  
     
  2. brandonjm

    brandonjm

    Joined:
    Mar 9, 2016
    Posts:
    3
    WIthout seeing your shader code I can't know for sure but it is most likely because your modified shader that provides shadows is using a Fallback shader to compute the shadows. The fallback shader would only use the standard UV map of the quad to compute the shadows while the rendered image is distorted using the modified UV.

    Code (CSharp):
    1. tex2D(_MainTex, i.uv.xy / i.uv2.x);
    The solution would be to compute the shadows using a custom shadow pass. The shader below is a modified version of the shader provided in the SkewQuad package which adds a shader pass that is clipped wherever the texture is transparent. It is still an unlit shader but you could add lighting support using an example here: https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html

    Code (CSharp):
    1. Shader "Custom/SkewQuadUnlit"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.        Tags {"Queue"="Transparent" "RenderType"="Transparent"}
    10.         Pass
    11.         {
    12.  
    13.         ZWrite Off
    14.         Blend SrcAlpha OneMinusSrcAlpha
    15.         LOD 100
    16.  
    17.             CGPROGRAM
    18.  
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             // make fog work
    22.             #pragma multi_compile_fog
    23.        
    24.             #include "UnityCG.cginc"
    25.  
    26.  
    27.             struct v2f
    28.             {
    29.                 float2 uv : TEXCOORD0;
    30.                 float2 uv2 : TEXCOORD1;
    31.                 UNITY_FOG_COORDS(1)
    32.                 float4 vertex : SV_POSITION;
    33.             };
    34.  
    35.             sampler2D _MainTex;
    36.             float4 _MainTex_ST;
    37.        
    38.             v2f vert (appdata_full v)
    39.             {
    40.                 v2f o;
    41.                 o.vertex = UnityObjectToClipPos(v.vertex);
    42.                 o.uv = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
    43.                 o.uv2 = v.texcoord1;
    44.                 UNITY_TRANSFER_FOG(o,o.vertex);
    45.                 return o;
    46.             }
    47.        
    48.             fixed4 frag (v2f i) : SV_Target
    49.             {
    50.                 // sample the texture
    51.                 fixed4 col = tex2D(_MainTex, i.uv.xy / i.uv2.x);
    52.                 //col = fixed4(i.uv2/i.uv3.x, 0, 0);
    53.                 // apply fog
    54.                 UNITY_APPLY_FOG(i.fogCoord, col);
    55.                 return col;
    56.             }
    57.             ENDCG
    58.         }
    59.  
    60.         Pass
    61.         {
    62.             Tags {"LightMode"="ShadowCaster"}
    63.             CGPROGRAM
    64.             #pragma vertex vert
    65.             #pragma fragment frag
    66.             #pragma multi_compile_shadowcaster
    67.             #include "UnityCG.cginc"
    68.             struct v2f {
    69.                 float2 uv : TEXCOORD0;
    70.                 float2 uv2 : TEXCOORD1;
    71.                 UNITY_FOG_COORDS(1)
    72.                 V2F_SHADOW_CASTER;
    73.             };
    74.  
    75.             sampler2D _MainTex;
    76.             float4 _MainTex_ST;
    77.        
    78.             v2f vert(appdata_full v)
    79.             {
    80.                 v2f o;
    81.                 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    82.                 o.uv2 = v.texcoord1;
    83.                 //object space
    84.                 //v.vertex.z += 1000.;
    85.                 TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    86.                 return o;
    87.             }
    88.             float4 frag(v2f i) : SV_Target
    89.             {
    90.                 fixed4 col = tex2D(_MainTex, i.uv.xy / i.uv2.x);
    91.                 clip(col.a - 0.5);
    92.                 SHADOW_CASTER_FRAGMENT(i)
    93.             }
    94.             ENDCG
    95.         }
    96.     }
    97. }
    98.  
    Result:
    skew transparent.PNG

    Hopefully that fixes your problem. I will update the GitHub repository with a shader that adds surface lighting support and shadow support.
     
    Last edited: Aug 30, 2020
    PraetorBlue likes this.
  3. RealHandy

    RealHandy

    Joined:
    Jul 11, 2020
    Posts:
    21
    Thanks, @brandonjm! That def gives me something to go on.
     
  4. brandonjm

    brandonjm

    Joined:
    Mar 9, 2016
    Posts:
    3
    I noticed sometimes the image disappears because that shader is using the incorrect render queue. You just need to move this line outside of the first 'Pass'. I have updated the shader above.
    Code (CSharp):
    1.        Tags {"Queue"="Transparent" "RenderType"="Transparent"}
     
  5. cp-

    cp-

    Joined:
    Mar 29, 2018
    Posts:
    77
    Hi @brandonjm just wanted to drop a quick thanks here for the package on github, helped me out a great deal :)