Search Unity

Scaling in the vertex shader

Discussion in 'Shaders' started by electrohamster, Aug 28, 2013.

  1. electrohamster

    electrohamster

    Joined:
    Aug 28, 2013
    Posts:
    10
    I'm sure the answer is simple and I'm going to be mad at myself but I'm officially asking for help.

    I'm implementing a shader with 2 passes and in the 2nd pass i want to scale the object as a function of a slider value. This requires recalculating MVP in the shader itself so that i can multiply M by a scale matrix then multiply the result by VP. Per unity documentation I would assume that mul( _Object2World, UNITY_MATRIX_VP) would recreate MVP but is doesn't. Without writing ridiculous code to extract M from MVP, how does one get the M matrix? Is there an easier way to scale in the vertex shader?

    Thx!
     
  2. mouurusai

    mouurusai

    Joined:
    Dec 2, 2011
    Posts:
    350
    You want scale? Why not just multiply vertex position by some value?
     
  3. electrohamster

    electrohamster

    Joined:
    Aug 28, 2013
    Posts:
    10
    That is incorrect. You cannot scale by multiplying the vertex in the world coordinate system, it must be in the local coordinate system. Matrix multiplication is NOT communicative. Where S is the scale matrix MSVP does NOT equal MVPS.

    If you want to scale UNITY_MATRIX_MVP, you must first extract M, multiply it by S, then by VP. My problem is that I do not know how to get M. Does anyone know how to get M?

     
  4. electrohamster

    electrohamster

    Joined:
    Aug 28, 2013
    Posts:
    10
    So its clear what I'm trying to do, I want to scale the object INSIDE the code. I'm creating a custom multipass toonshader and the final pass is to redraw the object in pure black, slightly larger, to create the toon like outline. Only the back facing polygons are drawn which highlights the edges of the model AND any curves on the model that transition from front to back. I am not interested in the default toonshader that comes with unity.
     
  5. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
    By your notation, you want to do SMVP, not MSVP. The Model matrix transforms the vertex positions from model space to world space, not the other way around.
     
  6. electrohamster

    electrohamster

    Joined:
    Aug 28, 2013
    Posts:
    10
    @Dolkar

    You were 100% correct, SMVP works fine for scaling, however, now I'm encountering another problem. When ever I scale up by more than a few percent the scaled surfaces seem to be culled away. Scaling smaller works fine, only larger causes the culling problem. I've tried to disable culling in the shader (Cull off) but that doesn't help. If i had to guess I suspect that there is some unity magic at work that I don't know about. Suggestions?

    Code (csharp):
    1.  //scaling code
    2. float4x4 scaleMat;
    3. scaleMat[0][0] = 1.0 + _Outline;
    4. scaleMat[0][1] = 0.0;
    5. scaleMat[0][2] = 0.0;
    6. scaleMat[0][3] = 0.0;
    7. scaleMat[1][0] = 0.0;
    8. scaleMat[1][1] = 1.0 + _Outline;
    9. scaleMat[1][2] = 0.0;
    10. scaleMat[1][3] = 0.0;
    11. scaleMat[2][1] = 0.0;
    12. scaleMat[2][1] = 0.0;
    13. scaleMat[2][2] = 1.0 + _Outline;
    14. scaleMat[2][3] = 0.0;
    15. scaleMat[3][0] = 0.0;
    16. scaleMat[3][1] = 0.0;
    17. scaleMat[3][2] = 0.0;
    18. scaleMat[3][3] = 1.0;
    19. o.pos = mul( scaleMat, mul (UNITY_MATRIX_MVP, i.vertex));
     
  7. electrohamster

    electrohamster

    Joined:
    Aug 28, 2013
    Posts:
    10
    you know.... i take it back.... SMVP might not be right...
    It might only seem correct when _Outline is very small because that means scaleMat is close to the identity matrix, thus masking the error, but when I try to scale up it messes everything up creating my "culling" issue.
     
  8. mouurusai

    mouurusai

    Joined:
    Dec 2, 2011
    Posts:
    350
    I can't understand what is incorrect. Look at picture, object scaled in the object space just fine. You want object space scale did you?
    Code (csharp):
    1.  output.pos = mul(UNITY_MATRIX_MVP, input.vertex*_Scale);
     
  9. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
    You have culling issues because Unity handles frustum culling by computing the bounding boxes of your meshes. But Unity has no way of telling whether you're scaling or moving them in any way in your shader. So you should manually scale the bounding box to the maximum scale of the mesh.
     
  10. electrohamster

    electrohamster

    Joined:
    Aug 28, 2013
    Posts:
    10
    I'm not entirely certain where I should modify the bounding box. I tried in script, but it seems to have no effect. Should this be done in editor?

    Code (csharp):
    1. void Start ()
    2.     {  
    3.         Mesh mesh = GetComponent<MeshFilter>().mesh;
    4.         Bounds bounds = mesh.bounds;
    5.         bounds.Expand( 300000.0f );
    6.         mesh.bounds = bounds;
    7.     }
     
  11. Neutron

    Neutron

    Joined:
    Apr 25, 2011
    Posts:
    45
    Bear in mind that dynamic batching will screw up any effect like this, as multiple meshes get combined into one super mesh.
     
  12. gustavolsson

    gustavolsson

    Joined:
    Jan 14, 2011
    Posts:
    339
    Code (csharp):
    1. o.pos = mul( scaleMat, mul (UNITY_MATRIX_MVP, i.vertex));
    The above code will scale the coordinate in clip space, not in local space! What you want to do is:

    Code (csharp):
    1. o.pos = mul(UNITY_MATRIX_MVP, mul (scaleMat, i.vertex));
    Using matrix multiplication in this way is not very efficient though. A uniform scale matrix is equivalent to the following, like mouurusai said:

    Code (csharp):
    1. o.pos = mul(UNITY_MATRIX_MVP, i.vertex * _Outline);
    However, this scales the model with respect to the local origin of the mesh. For an outline effect, you probably want to move vertices outward along their normals, like this:

    Code (csharp):
    1. o.pos = mul(UNITY_MATRIX_MVP, i.vertex + i.normal * _Outline);
    (Also make sure that the mesh you're scaling is completely smooth, so that triangles share vertices)

    Hope this helps :)
     
    Last edited: Aug 31, 2013
    steril and Treenod like this.
  13. Corefire

    Corefire

    Joined:
    Feb 5, 2013
    Posts:
    1
    I've been playing around with Unity 5 and found out some interesting things through experimentation. The reason you can’t just multiply a vertex.xyz by scale is due to batching. If you only have one object then scale will be relative to the objects origin point. (It will scale right) If unity batches your objects then the origin becomes world space (0,0,0) and all the objects are scaled out and move away from their correct positions in world space.


    The way I got around this is to pass the local objects vertex positions into the shader by using any unused channel such as color or normal. I then multiplied my normal by scale and added it to the vertices.

    Code (CSharp):
    1. v.vertex.xyz += v.normal.xyz * _scale;
    2.  
    3. o.pos = mul(UNITY_MATRIX_MVP, v.vertex );
    You can tell if you passed them in right by just setting the vertex = normal. The object should be drawn at (0,0,0) regardless of where you move the game object. If you do this then scale of 0 will be regular size and 1 would be double, 2 would be 3 time the size, ect…

    You can also test this by scaling using vertex.xyz *= _scale with two objects that are batched then inverse one of the scales of the game object, you will see the set pass calls go up by one and both will be scaled and drawn in the right position.


    This is an old thread but I hope this helps people out. It took me a while to figure out why my code wasn’t working and a solution that worked.
     
  14. AlphaSilverback

    AlphaSilverback

    Joined:
    Aug 18, 2016
    Posts:
    10
    I found that simply scaling the W value of the vertex : Position sufficed. Here's a shader for Icons I used in the last game I worked on:


    Code (CSharp):
    1. Shader "Unlit/Icon"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _Color ("Fill Color", COLOR) = (1,1,1,1)
    7.         _Scale ("scale", Float) = 1
    8.         _IsConstSize ("Keep Size Constant", Float) = 1
    9.  
    10.     }
    11.     SubShader
    12.     {
    13.         Tags { "RenderType"="Fade" "Queue"="Overlay+1000"}
    14.         LOD 200
    15.  
    16.  
    17.         Pass
    18.         {
    19.             Cull Off
    20.             ZWrite Off
    21.             //Blend SrcAlpha DstAlpha
    22.             Blend SrcAlpha OneMinusSrcAlpha
    23.             ZTest Always
    24.             //Offset 1, 1
    25.  
    26.             CGPROGRAM
    27.             #pragma vertex vert
    28.             #pragma fragment frag
    29.             // make fog work
    30.             #pragma multi_compile_fog
    31.          
    32.             #include "UnityCG.cginc"
    33.          
    34.  
    35.          
    36.             struct appdata
    37.             {
    38.                 float4 vertex : POSITION;
    39.                 float2 uv : TEXCOORD0;
    40.             };
    41.  
    42.             struct v2f
    43.             {
    44.                 float2 uv : TEXCOORD0;
    45.                 UNITY_FOG_COORDS(1)
    46.                 float4 vertex : SV_POSITION;
    47.             };
    48.  
    49.             sampler2D _MainTex;
    50.             float4 _MainTex_ST;
    51.             float4 _Color;
    52.             float _Scale;
    53.             float _IsConstSize;
    54.          
    55.             v2f vert (appdata v)
    56.             {
    57.                 v2f o;
    58.                 //o.vertex = UnityObjectToClipPos(v.vertex);
    59.                 float3 pos = mul(UNITY_MATRIX_M ,v.vertex);
    60.                 float dist = length(pos - _WorldSpaceCameraPos);
    61.  
    62.                 float4x4 view = UNITY_MATRIX_MV ;
    63.  
    64.                 // First colunm.
    65.                 view._m00 = 1.0f;
    66.                 view._m10 = 0.0f;
    67.                 view._m20 = 0.0f;
    68.  
    69.                 // Second colunm.
    70.                 view._m01 = 0.0f;
    71.                 view._m11 = 1.0f;
    72.                 view._m21 = 0.0f;
    73.  
    74.                 // Thrid colunm.
    75.                 view._m02 = 0.0f;
    76.                 view._m12 = 0.0f;
    77.                 view._m22 = 1.0f;
    78.  
    79.                 if(_IsConstSize > 0)
    80.                     v.vertex.w /= dist;
    81.  
    82.                 v.vertex.w /= (_Scale * 0.1);
    83.  
    84.                 o.vertex = mul(UNITY_MATRIX_P, mul(view, v.vertex ) );
    85.  
    86.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    87.                 UNITY_TRANSFER_FOG(o,o.vertex);
    88.                 return o;
    89.             }
    90.          
    91.             fixed4 frag (v2f i) : SV_Target
    92.             {
    93.                 // sample the texture
    94.                 fixed4 col = tex2D(_MainTex, i.uv);
    95.              
    96.                 // apply fog
    97.                 //UNITY_APPLY_FOG(i.fogCoord, col);
    98.  
    99.                 col = col * _Color;
    100.  
    101.                 return col;
    102.             }
    103.  
    104.  
    105.  
    106.             ENDCG
    107.  
    108.  
    109.         }
    110.     }
    111. }
     
  15. NikodemGrz

    NikodemGrz

    Joined:
    Mar 3, 2019
    Posts:
    9
    Awesome, thanks!
    Was searching for an always visible waypoint but had the problem that it got too small over distance...