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

How vertex normals are written inside a shader?

Discussion in 'Shaders' started by DimitriX89, Jun 11, 2018.

  1. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    Sorry if it is a stupid question. Are the Normal vector values in "appdata v" go from -1 to 1 or 0 to 1 ?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    -1 to 1

    Apart from color which is limited to 8 bits per component, all values on the mesh can have a full 32 bit float precision. I say can because Unity may quantize those values depending on the mesh asset / project's vertex compression settings. Either way the range for normals and tangents will default to a -1 to 1 range.
     
  3. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    So -1 to 1, thanks. Though it turned out that my problem wasnt in normal values. I was trying to add directional component (vector displacement) into free DX11 displacement shader. (Wouldnt want to pay for Asset Store VD shader since there are not enough examples on the page to see if it is working correctly). As I understand, I need to turn the RGB vector of the displacement map into tangent space, then use alpha value as strength of displacement. The original used vertex normals as direction. After some search on the internet, I've used the following matrix
    Code (CSharp):
    1.  
    2.                
    3.             DirMap.x =  DirMap.x * 2 - 1;
    4.             DirMap.y =  DirMap.y * 2 - 1;
    5.             DirMap.z =  DirMap.z * 2 - 1;
    6.             //converting to -1 / 1 space
    7.                float3x3 World2Tangent = float3x3
    8.                (
    9.                   v.tangent.xyz,//tangent
    10.                   cross(float4(v.normal,0).xyz,v.tangent.xyz*v.tangent.w),//bitangent(?)
    11.                    v.normal
    12.                );
    13.                 float3 Direction = mul(DirMap,World2Tangent);
    but the result wasnt satisfactory
    https://gyazo.com/538818d7d8436e3c0bddbaa40a1bd1fb
    (this is supposed to be mushroom, VD exported from Zbrush)
     
  4. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Glad you figured it out.

    To get the surface normals to match the vector displaced surface you'll need a tangent normal map with the displaced normals in it. You can't use Unity's "normal map" import setting since you can't use a reconstructed normal Z. That or you'll need to try to reconstruct the per vertex normals in the vertex shader by from the displacement map derivatives (ie: sample the displacement map from 3 or 4 points slightly offset in U and V and use the cross product to construct a new normal).

    Also your bitangent isn't quite right, but it's probably close enough. Here's the proper form:
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 bitangent = cross(v.normal.xyz, v.tangent.xyz) * tangentSign;
     
    Invertex and DimitriX89 like this.
  6. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    Thanks for your correction! It almost works across texture seams now, though still tears them apart a bit. I guess I better use it for simpler objects only. Too many factors impacting vector displacement, and not all of them can be accounted for in the shader. The DX 11 tesselator seems to use some other algoritm than Catmull/Clark (which most sculpting software operate with). I'll continue with it later, most likely with option 2 for normals. Might have more questions then
     
  7. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    By the way, is there any special way HDR maps need to be handled in shaders? Because while LDR (*.tiff) version of VD map works more or less consistently, *.exr version doesnt look remotely correct.
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Correct. GPU tessellation is most often tri based, though quad is an option outside of Unity. By default it is also a linear interpolation, though Phong is easy to implement. Generally speaking no one uses Catmull/Clark in real time rendering.

    http://www.nvidia.com/content/gtc-2010/pdfs/2129_gtc2010.pdf

    Some more recent AAA games have looked at OpenSubDiv, but afaik everyone end up using it as a reference, not as the actual implementation. Activision uses a compute based approach to approximate Catmull/Clark.
    http://www.graphics.stanford.edu/~niessner/papers/2016/4subdiv/brainerd2016efficient.pdf

    OpenEXR files are assumed to always be unsigned 16 bit per channel gamma color images. If they're 32 bit, signed, and / or linear data, Unity will clamp the input and apply the default sRGB gamma curve to the image anyway. The Bypass sRGB does nothing. :(

    Tiff image files can be 16 or 32 bit per channel unsigned and Unity should handle it properly assuming you use an uncompressed Half or Float format and check Bypass sRGB. The BC6H format won't work for 16 bit per channel data as it is also always using sRGB.
     
  9. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    Wasnt feeling well so I dropped the topic for a few days. About Catmull-Clark or whatever, maybe I read that wrong and it is not a subdivision algorithm itself that affects VD result, but if the subdivision smooths UV coordinates or not. DX11 tesselation shouldnt smooth UVs, right?

    And I managed to export 32 bit *.tiff from Zbrush, but Unity gives me an error on import. Did you have success using this format?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    I've only ever tried 16 bit tiff files, not 32. Sorry.
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    DX11 tessellation should keep the UVs the same, at least on flat surfaces. Not sure about curved surfaces, though I would expect phong smoothing to keep the UVs linear, it may not.
     
    DimitriX89 likes this.
  12. grejtoth

    grejtoth

    Joined:
    Aug 7, 2015
    Posts:
    14
    @DimitriX89 Sorry to use a Pheonix Down on this thread, but I'm curious if you were successful with the Vector Displacement Map usage, I'm trying it here myself and I'm finding the results "close enough" and having trouble reconstructing the normals.

    Right now I'm exporting a .exr 32 map using Mudbox (I was testing with ZBrush before but the workflow was more complex)
     
  13. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    Havent tried it ever since. Lack of support for 32 bit height info stopped me. Normals can be generated using either Sobel filter https://gamedev.stackexchange.com/q...rmal-map-from-height-map-using-sobel-operator or what I tried to use before, all with poor results. No idea if newer Unity supports 32 bit maps either. There should be store asset for VMD shader, if only the examples provided with it were better. Right now I cant tell if it is worth its 40$
     
  14. grejtoth

    grejtoth

    Joined:
    Aug 7, 2015
    Posts:
    14
    Thanks for the quick response!

    As far as I undertood the sobel filter here is being used on a heightmap right? Does this works on a VDM too?

    My approach was similar to what bgolus said abode, to create some vertex slightly offsetted and used the cross product to construct a new Normal, the result is close, but kinda blurred.



    Code (CSharp):
    1. float4 coord = float4(v.uv.xy, 0, 0);
    2.  
    3.                 float3 disp = tex2Dlod(_DispTex, coord);
    4.                 float3 dispT = tex2Dlod(_DispTex, coord + float4(_DispTex_TexelSize.x, 0, 0, 0));
    5.                 float3 dispB = tex2Dlod(_DispTex, coord + float4(0, _DispTex_TexelSize.y, 0, 0));
    6.  
    7.                 disp.r = -disp.r;
    8.                 dispT.r = -dispT.r;
    9.                 dispB.r = -dispB.r;
    10.  
    11.                 fixed4 tangent = v.tangent;
    12.                 float tangentSign = tangent.w * unity_WorldTransformParams.w;
    13.                 fixed3 bitangent = cross(v.normal, tangent * tangentSign);
    14.  
    15.                 float3 p0 = v.vertex + (disp * _Displacement);
    16.                 float3 p1 = (v.vertex + tangent.xyz * 0.001) + dispT * _Displacement;
    17.                 float3 p2 = (v.vertex + bitangent * 0.001) + dispB * _Displacement;
    18.                
    19.                 o.vertex = UnityObjectToClipPos(v.vertex + disp * _Displacement);
    20.                 o.normal = normalize(cross(p2 - p0, p1 - p0));

    Unity_2019-12-04_18-48-32.png

    On the left, the "target" with a material that just displays the normal (in a 0-1 range)
    On the right the displaced one with the shader above showing the new normals as colors (again in 0-1 range)
     
  15. DimitriX89

    DimitriX89

    Joined:
    Jun 3, 2015
    Posts:
    551
    I think the Sobel can work with heightmap alpha channel. But yeah, it is better to use cross product. Both will inevitably cause artifacts though, since they only interpolate a normal. Maybe, as bgolus suggested in fith post, render the separate tangent space normal for deformed object and write a custom code for it to bypass Unity packing/unpacking BS.