Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Transition from Unity4 to Unity5, shader not functioning

Discussion in 'Shaders' started by frikic, Sep 21, 2015.

  1. frikic

    frikic

    Joined:
    Dec 22, 2011
    Posts:
    44
    HI,
    I have a problem with custom made shader that worked perfectly in Unity4 but give strange result in Unity5.
    Shader will normally produce an Xray kind of effect with circular shape when used with texture on a plane.

    In Unity5 i will instead get elliptical shape, with dimensions being connected to the plane scale.
    I havent wrote this shader myself, and my shader writting knowledge is basic, but i managed to find possible problem in unity documentation:

    Non-uniform mesh scale has to be taken into account in shaders
    In Unity 5.0, non-uniform meshes are not “prescaled” on the CPU anymore. This means that normal & tangent vectors can be non-normalized in the vertex shader. If you’re doing manual lighting calculations there, you’d have to normalize them. If you’re using Unity’s surface shaders, then all necessary code will be generated for you.​

    Im assuming that since my planes are scaled trough transform component, that this is making problems in Unity, heres the code to help with possible solution.

    Code (CSharp):
    1. Shader "Custom/VisionShader" {
    2.     Properties {
    3.       _MainTex ("Texture", 2D) = "white" {}
    4.       _EffectX("X Coord", float) = 0.5
    5.       _EffectY("Y Coord", float) = 0.5
    6.       _EffectRadius("Effect Radius", float) = 4.0
    7.       _ScaleX("X Scale", float) = 1.0
    8.       _ScaleY("Y Scale", float) = 1.0
    9.     }
    10.     SubShader {
    11.         Tags { "Queue" = "Transparent" }
    12.       Pass {
    13.    
    14.          ZWrite Off
    15.          Blend SrcAlpha OneMinusSrcAlpha
    16.          Cull Off
    17.          CGPROGRAM
    18.          #pragma vertex vert
    19.          #pragma fragment frag
    20.           uniform sampler2D _MainTex;
    21.           float _EffectX, _EffectY, dist, _EffectRadius, _ScaleX, _ScaleY;
    22.           float4 designColor;
    23.        
    24.          struct vertexInput {
    25.             float4 vertex : POSITION;
    26.             float4 texcoord : TEXCOORD1;
    27.          };
    28.          struct vertexOutput {
    29.             float4 pos : SV_POSITION;
    30.             float4 posInObjectCoords : TEXCOORD0;
    31.             float4 tex : TEXCOORD1;
    32.          };
    33.          vertexOutput vert(vertexInput input)
    34.          {
    35.             vertexOutput output;
    36.             output.pos =  mul(UNITY_MATRIX_MVP, input.vertex);
    37.             output.posInObjectCoords = input.vertex;
    38.          
    39.             output.tex = input.texcoord;
    40.             return output;
    41.          }
    42.          float4 frag(vertexOutput input) : COLOR
    43.          {
    44.              designColor = tex2D(_MainTex, float2(input.tex));
    45.          
    46.              dist = (input.posInObjectCoords.z - _EffectY) * (input.posInObjectCoords.z - _EffectY)  + (input.posInObjectCoords.x - _EffectX)*(input.posInObjectCoords.x - _EffectX) ;
    47.        
    48.             if (dist < _EffectRadius && _EffectRadius > 0)
    49.             {
    50.                 if (designColor.a > (dist/_EffectRadius))
    51.                 {
    52.                     designColor = float4(designColor.r, designColor.g , designColor.b, ((dist*dist/_EffectRadius)/_EffectRadius));
    53.                 }
    54.             }
    55.                    
    56.             return designColor;
    57.          }
    58.          ENDCG
    59.       }
    60.    }
    61.     Fallback "Diffuse"
    62.   }
    I looked for solution on internet and found that similar problem was solved using suggestion above to normalize vectors, changing

    output.pos = mul(UNITY_MATRIX_MVP, input.vertex);​
    to
    output.pos = normalize(mul(UNITY_MATRIX_MVP, input.vertex));​

    but this didnt worked for me.
    I have also put variables _ScaleX and _ScaleY that will hold scale of the transform component so that they can be used inside of shader.
     
    Last edited: Sep 21, 2015
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Normalize takes a vector and makes it unit length. In other terms it'll take any float3 and make its length 1. If you normalize the pos value (which is used to determine where to render it) it'll just end up being some tiny sliver near the center of the scene, which is not what you want.

    It seems like what you want is a circle with a world space radius that is offset from the center of the object, yes? Is there a reason to not just have the center of the circle defined in the world space position rather than some object relative offset?

    If you change this line
    output.posInObjectCoords= input.vertex;
    to
    output.posInObjectCoords= mul(_Object2World, input.vertex);

    and change the _EffectX and _EffectY to world space that should work.

    Alternatively you could try
    output.posInObjectCoords = mul((float3x3)_Object2World, input.vertex.xyz).xyzz;

    Which might just work if your object doesn't have any rotation.
     
  3. frikic

    frikic

    Joined:
    Dec 22, 2011
    Posts:
    44
    Thanks a lot for your efforts bgolus, but your solution didnt solved my problem, i got even stranger results such as instead of circle i would see a vertical bar type of shape. I wanted to clarify what im doing and what is the problem by giving graphic example of the issue, which might help you in understanding the problem.

    So what im trying to do is to have this shader work on Unity5, since it works perfectly on Unity4. That means that for example if i have two Unity Plane meshes one behind each other, and in the image first plane is with black texture and plane behind it is with wooden crate texture, when i move mouse around VisionShader will make circular hole in the first texture. So i will be able to see trough the first plane and crates will be visible behind. Below left is unity4 image where shader is working as intended. On the other hand if i switch to unity5 i will instead get eliptical shape instead, look below right.

    Unity4Circle.png Unity5Elipse.png

    So basically if you compare those two images below, you will come to conclusion that elipse has same proportion as the black box plane with transform components scale x = 2.9 y = 1 and Z = 1.2 (in this case because of plane oriantation z axis is what we usually think of y axes on the image above. Below is how that looks when compared one to the other.

    U4vsU5.png

    So somewhere in mouse handling code i feed a data to shader _EffectX and _EffectY that are actually being a center position of circle where mouse is currently positioned and _EffectRadius is current radius of the circle. _ScaleX and _ScaleY are there just for possible solution of this problem.
    Let me know if you need any further clarification and if all of this makes sense?
     
    Last edited: Sep 28, 2015
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    A couple of random things. You shouldn't define variables outside of a struct or function unless they're the ones coming from the material, so dist and designColor have been moved to inside of the frag function. You're on the right path with passing the scale, all you really need to do is multiply the posInObjectCoords by those values properly. You were passing a full float4 of the object position when you only care about 2 of those dimensions so the below shader only passes the two you want (and scales them) in the vert function.

    Code (CSharp):
    1. Shader "Custom/VisionShader" {
    2.     Properties {
    3.       _MainTex ("Texture", 2D) = "white" {}
    4.       _EffectX("X Coord", float) = 0.5
    5.       _EffectY("Y Coord", float) = 0.5
    6.       _EffectRadius("Effect Radius", float) = 4.0
    7.       _ScaleX("X Scale", float) = 1.0
    8.       _ScaleY("Y Scale", float) = 1.0
    9.     }
    10.     SubShader {
    11.         Tags { "Queue" = "Transparent" }
    12.       Pass {
    13.  
    14.          ZWrite Off
    15.          Blend SrcAlpha OneMinusSrcAlpha
    16.          Cull Off
    17.          CGPROGRAM
    18.          #pragma vertex vert
    19.          #pragma fragment frag
    20.           uniform sampler2D _MainTex;
    21.           float _EffectX, _EffectY, _EffectRadius, _ScaleX, _ScaleY;
    22.      
    23.          struct vertexInput {
    24.             float4 vertex : POSITION;
    25.             float4 texcoord : TEXCOORD1;
    26.          };
    27.          struct vertexOutput {
    28.             float4 pos : SV_POSITION;
    29.             float2 posInObjectCoords : TEXCOORD0;
    30.             float4 tex : TEXCOORD1;
    31.          };
    32.          vertexOutput vert(vertexInput input)
    33.          {
    34.             vertexOutput output;
    35.             output.pos =  mul(UNITY_MATRIX_MVP, input.vertex);
    36.             output.posInObjectCoords = input.vertex.xz * float2(_ScaleX, _ScaleY);
    37.        
    38.             output.tex = input.texcoord;
    39.             return output;
    40.          }
    41.          float4 frag(vertexOutput input) : COLOR
    42.          {
    43.             float4 designColor = tex2D(_MainTex, float2(input.tex));
    44.             float2 offset = input.posInObjectCoords - float2(_EffectX, _EffectY);
    45.             float dist = dot(offset, offset);
    46.      
    47.             if (dist < _EffectRadius && _EffectRadius > 0)
    48.             {
    49.                 if (designColor.a > (dist/_EffectRadius))
    50.                 {
    51.                     designColor = float4(designColor.rgb, ((dist*dist/_EffectRadius)/_EffectRadius));
    52.                 }
    53.             }
    54.                  
    55.             return designColor;
    56.          }
    57.          ENDCG
    58.       }
    59.    }
    60.     Fallback "Diffuse"
    61.   }
    Lastly there's a change in the dist calculation. You were doing all but the last step of the right angle triangle / pythagorean theorem calculation in long hand form (writing out each value and subtraction individually). That's fine, but the shader above now does the same with built in functions so it's slightly faster. It's a cheap enough shader that it likely doesn't matter either way, but I changed it anyway.

    Really the value you have in dist in both your shader and mine is the distance squared and not the distance but I didn't want to change how the shader functions.

    You can change:
    float2 offset = input.posInObjectCoords - float2(_EffectX, _EffectY);
    float dist = dot(offset, offset);

    to
    float dist = length(input.posInObjectCoords - float2(_EffectX, _EffectY));
    And now it's the actual distance, but that'll mean _EffectRadius won't act the same anymore, and the falloff won't quite be the same so
    designColor = float4(designColor.rgb, ((dist*dist/_EffectRadius)/_EffectRadius));
    will have to be
    designColor = float4(designColor.rgb, ((pow(dist,4)/_EffectRadius)/_EffectRadius));
    to get the same falloff.
     
  5. frikic

    frikic

    Joined:
    Dec 22, 2011
    Posts:
    44
    Thanks for all explanations and code corrections, I also found one line that i copied wrong in original code and that didnt want to compile, so i changed
    float4 designColor = tex2D(_MainTex, float2(input.tex));
    to
    float4 designColor = tex2D(_MainTex, float2(input.tex.xy));
    and then i was able to use shader.

    Unfortunately nothing seems to be changed after implementing your code, i still get elliptical shape, which makes me wonder if frag functions should be also/instead changed to include scale?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Are you setting _ScaleX and _ScaleY to 2.9 and 1.2?
     
  7. frikic

    frikic

    Joined:
    Dec 22, 2011
    Posts:
    44
    Oh man that did it!, the script that was feeding that info was not attached to the object, i moved code to another script and now it works like charm!

    I dont know if I can thank you enough cause this really saved my day, but im glad there is people like you that spend their time helping other folks.

    Im just putting the working script here, in case anyone would like to use it.

    Code (CSharp):
    1. Shader "Custom/VisionShader" {
    2.     Properties {
    3.       _MainTex ("Texture", 2D) = "white" {}
    4.       _EffectX("X Coord", float) = 0.5
    5.       _EffectY("Y Coord", float) = 0.5
    6.       _EffectRadius("Effect Radius", float) = 4.0
    7.       _ScaleX("X Scale", float) = 1
    8.       _ScaleY("Y Scale", float) = 1
    9.     }
    10.     SubShader {
    11.         Tags { "Queue" = "Transparent" }
    12.       Pass {
    13.          ZWrite Off
    14.          Blend SrcAlpha OneMinusSrcAlpha
    15.          Cull Off
    16.          CGPROGRAM
    17.          #pragma vertex vert
    18.          #pragma fragment frag
    19.           uniform sampler2D _MainTex;
    20.           float _EffectX, _EffectY, _EffectRadius, _ScaleX, _ScaleY;
    21.    
    22.          struct vertexInput {
    23.             float4 vertex : POSITION;
    24.             float4 texcoord : TEXCOORD1;
    25.          };
    26.          struct vertexOutput {
    27.             float4 pos : SV_POSITION;
    28.             float2 posInObjectCoords : TEXCOORD0;
    29.             float4 tex : TEXCOORD1;
    30.          };
    31.          vertexOutput vert(vertexInput input)
    32.          {
    33.             vertexOutput output;
    34.             output.pos =  mul(UNITY_MATRIX_MVP, input.vertex);
    35.             output.posInObjectCoords = input.vertex.xz * float2(_ScaleX, _ScaleY);
    36.      
    37.             output.tex = input.texcoord;
    38.             return output;
    39.          }
    40.          float4 frag(vertexOutput input) : COLOR
    41.          {
    42.             float4 designColor = tex2D(_MainTex, float2(input.tex.xy));
    43.             float2 offset = input.posInObjectCoords - float2(_EffectX, _EffectY);
    44.             float dist = dot(offset, offset);
    45.    
    46.             if (dist < _EffectRadius && _EffectRadius > 0)
    47.             {
    48.                 if (designColor.a > (dist/_EffectRadius))
    49.                 {
    50.                     designColor = float4(designColor.rgb, ((dist*dist/_EffectRadius)/_EffectRadius));
    51.                 }
    52.             }
    53.             return designColor;
    54.          }
    55.          ENDCG
    56.       }
    57.    }
    58.     Fallback "Diffuse"
    59. }
    And i will experiment with distance and use your code also just to see where it gets me.
    Big thanks!