Search Unity

UV based on world position in surface shader normals problem

Discussion in 'Shaders' started by Kusras, Jul 1, 2017.

  1. Kusras

    Kusras

    Joined:
    Jul 9, 2015
    Posts:
    134
    Hi, I did simple shader which should ignore UVS and map topdown texture based on vertex position. All works except one thing normals. It seems that they are rotated by object rotation somewhere out of my code. Because normal before put into o.Normal looks without seems if put for debug into albedo and also o.Normals seems without seems... I also tried to make rotation clockwise the object rotation, result is better but not absolutely OK, and also scale started affecting it. So I am putting there code without that feature. If you have an idea how to make any trick to say do not rotate normals. I would be very glad :). (note it is not triplanar it is only for topdown surfaces and it is meant to have no seems between objects with different rotation)
    Code (CSharp):
    1. Shader "Custom/TopDown Global Map" {
    2.     Properties
    3.     {
    4.         _Color("Color", Color) = (1,1,1,1)
    5.         [NoScaleOffset]_MainTex("Albedo", 2D) = "white" {}
    6.          [NoScaleOffset]_MetallicGlossMap("MetallicMap", 2D) = "white" {}
    7.         _Glossiness("Smoothness", float) = 0.5
    8.         _Metallic("Metallic", float) = 0.0
    9.          [NoScaleOffset]_BumpMap("Normal Map", 2D) = "bump" {}
    10.          _BumpScale("Scale NormalMap", Float) = 1.0
    11.          [NoScaleOffset]_OcclusionMap("Occlusion", 2D) = "white" {}
    12.          _OcclusionStrength("Occlusion Strength",float) = 1.0
    13.          _Repeatance("Repeatance",float) = 1.0
    14.     }
    15.  
    16.     SubShader {
    17.         Tags { "RenderType"="Opaque" "DisableBatching" = "true"}
    18.         LOD 200
    19.        
    20.         CGPROGRAM
    21.         // Physically based Standard lighting model, and enable shadows on all light types
    22.         #pragma surface surf Standard vertex:vert
    23.         // Use shader model 3.0 target, to get nicer looking lighting
    24.         #pragma target 3.0
    25.  
    26.         sampler2D _MainTex,_MetallicGlossMap,_BumpMap,_OcclusionMap;//,_EmissionMap;
    27.          struct vertexInput {
    28.             float4 vertex : POSITION;
    29.             float3 normal : NORMAL;
    30.             float4 tangent : TANGENT;
    31.  
    32.          };
    33.  
    34.         struct Input {
    35.             float3 worldPos;
    36.             float4 vert;  
    37.         };
    38.  
    39.         half _Glossiness,_Metallic,_OcclusionStrength,_BumpScale,_Repeatance;
    40.         fixed4 _Color;
    41.  
    42. void vert (inout vertexInput v, out Input o) {
    43.           o.worldPos= mul(unity_ObjectToWorld,v.vertex).xyz;
    44.           o.vert = v.vertex;
    45.  
    46.       }
    47.  
    48.         void surf (Input IN, inout SurfaceOutputStandard o) {
    49.  
    50.             float2 uv = IN.worldPos.xz*_Repeatance;
    51.              
    52.             fixed4 c = tex2D (_MainTex, uv) * _Color;
    53.             o.Albedo = c.rgb;
    54.  
    55.             fixed4 m =tex2D(_MetallicGlossMap, uv);
    56.             o.Occlusion=tex2D(_OcclusionMap, uv)*_OcclusionStrength;
    57.             half3 nrm =UnpackScaleNormal(tex2D(_BumpMap, uv),_BumpScale);
    58.  
    59. o.Normal=nrm;
    60.  
    61.             o.Metallic =m.a*_Metallic;
    62.             o.Smoothness = _Glossiness*m.rgb;
    63.  
    64.         }
    65.         ENDCG
    66.     }
    67.     FallBack "Diffuse"
    68. }

    There is how shader looks, you can see lighting comes from different dirrections as these 3 cubes have 3 different rotations.



    There is unpacked Normal map as albedo - no visible problem



    o.Normal as albedo - again no problems... so the problem has to be somewhere in any default shader pass...

    Thank you for your advices in forward :)
     
  2. Kusras

    Kusras

    Joined:
    Jul 9, 2015
    Posts:
    134
    There is few lines, which helps reduce seems, without last line, there are just 2 shadow directions by 180. and that last row is magic line, have no clue, just reduced seam to minimum, but the stones does not look same...

    Code (CSharp):
    1. half3 transformator= mul(half4(1,0,1,0),unity_ObjectToWorld);
    2.             half rotor = abs(transformator.b-transformator.r);
    3.             nrm=lerp(half3(nrm.r*transformator.b,nrm.g*transformator.r,nrm.b),half3(nrm.g*(transformator.r)*0.5,nrm.r*(transformator.b)*0.5,nrm.b),rotor);
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Surface shaders assume the o.Normal you set in the surf function is a tangent space normal. That is to say a normal map with the same orientation as the mesh's stored UVs. To use programmaticly defined UVs with surface shaders you have to either convert the normals back into tangent space, or edit the generated shader by hand to remove the tangent to world transform. The second option is the best for performance, but kind of sucks if you are working in the shader still.

    Here's a post in which I outline the first option, along with a full triplanar shader, and a warning to never use it.
    https://forum.unity3d.com/threads/t...ormals-bump-mapping-help.426757/#post-2759284

    All told it probably adds some 60 operations and uses 3 extra interpolators to work around commenting out one line.
     
    Last edited: Jul 2, 2017
  4. Kusras

    Kusras

    Joined:
    Jul 9, 2015
    Posts:
    134
    Thanks, I knew that caused that problem, just wonder if there is no any kind of pragma or any part to override, which says use world space... Yesterday I played with random magic numbers and got result which is absolutely almost seamless (just if you watch it from few cm, what is impossible to camera clip :) )... just when you have 2 overlapping faces it little blinking as they are absolutely not same... but that is what I am trying to avoid :)

    Code (CSharp):
    1.     half4 bmp =tex2D(_BumpMap, uv);
    2.             half3 transformator= mul(half4(1,0,1,0),unity_ObjectToWorld);
    3.             half rotor = abs(transformator.b-transformator.r);
    4.             _BumpScale=lerp(_BumpScale,_BumpScale*0.56,rotor);
    5.             half3 nrm =UnpackScaleNormal(bmp,_BumpScale);
    6.             nrm=lerp(half3(nrm.r*transformator.b,nrm.g*transformator.r,nrm.b),half3(nrm.g*(transformator.r)*5,nrm.r*(-transformator.b)*5,nrm.b),rotor);
    7.             o.Normal=nrm;

    Other solution could be writting it in fragment shader, but I would not go into there, as I have to make it look same as surface shader and there is no surface shader in fragment source code, so I could just change that uv's and delete that MVP which rotates normals...
     
    Last edited: Jul 2, 2017
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    No, there isn't a "skip tangent to world normal transformation" option for surface shaders, hence the horrible code in my example.

    However surface shaders are just vertex fragment shaders, or more specifically they're vertex fragment shader generators. There's nothing a surface shader can do that a vertex fragment shader can't, it's just more manual coding.

    Just select your surface shader and click on "show generated code" and you now have the (somewhat ugly) vertex fragment shader the game actually uses. As long as you're not looking to change the #pragma surface parameters, some of which can significantly change the generated shader's code, working on that shader directly isn't too bad, especially if you do a bit of visual clean up.
     
  6. Kusras

    Kusras

    Joined:
    Jul 9, 2015
    Posts:
    134
    Thanks, I know that surface is nothing other, than fragment, but building only little changed standard material in fragment, I do not know all tricks about metalic, smothness etc in fragment, to behave exactly as in standard. I tried to find something, but could not. So without any frame, which i can change, i would not try to go there and do some mess in surface :D I use fragment shaders, but there I really need to look same as standard :( Anyway optimization of these few rows will hardly improve fps :)