Search Unity

Can ParallaxOffset & viewDir work for uv2 ?

Discussion in 'Shaders' started by chai, Oct 18, 2014.

  1. chai

    chai

    Joined:
    Jun 3, 2009
    Posts:
    84
    I'm using ParallaxOffset for clouds on a planet
    UV1 has planet cylinderical mapping, and UV2 is topdown planar for the poles.
    The parallax works great for first set of uvs and texture, however 2nd one (poles) gets distorted

    Digging deeper it seems viewDir is based on tangent space for 1st uvs. (see image below)
    (e.g. if I swap uvs, then the 2nd ones work fine but 1st gets distorted .. I need both)

    So is there any way to viewDir for uv2, or any way to generate my own ?



    Code (csharp):
    1. Shader "Hidden/Planet_TerranDebug" {
    2. Properties {
    3. _Normal ("Normalmap", 2D) = "bump" {}
    4. }
    5. SubShader {
    6. Tags { "RenderType"="Opaque" }
    7. LOD 400
    8.  
    9. CGPROGRAM
    10. #pragma surface surf BlinnPhong vertex:vert fullforwardshadows
    11. #pragma target 3.0
    12.  
    13. sampler2D _Normal;
    14.  
    15. struct Input {
    16. float2 uv_MainTex;
    17. float2 uv2_PoleTexA;
    18.  
    19. float3 viewDir; // builtin, for UV1
    20.  
    21. // INTERNAL_DATA
    22. };
    23.  
    24. void vert (inout appdata_full v, out Input o) {
    25.  
    26.  }
    27.  
    28. void surf (Input IN, inout SurfaceOutput o) {
    29.  
    30. // Test
    31. fixed4 texN = tex2D(_Normal, IN.uv_MainTex);
    32. float3 texG = IN.viewDir;
    33.  
    34. o.Albedo = 0;
    35. o.Alpha = 1;
    36. o.Normal = UnpackNormal(texN);
    37. o.Emission = texG;
    38.  
    39. }
    40. ENDCG
    41. }
    42.  
    43. FallBack "Specular"
    44. }
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    It's a common misconception that tangents and binormals are general model information. They are in fact related to a particular set of uv's. I see two possible solutions:

    1. Generate a viewDir2 for the second uv set and blend height and diffuse in the shader for the best overlap between uv1 and uv2. You could reuse some code of viewDir for viewDir2.

    2. Make one model with just uv1 and one with just uv2. First render one model and then the other with alpha blending enabled to blend between the two mappings. This is probably the easier solution. If your models are exactly the same, this can be done without any z-fight problems. (That also means that the pivot needs to stay exactly the same. In fact, I recommend to not even sneeze during this process ;-) )
     
  3. chai

    chai

    Joined:
    Jun 3, 2009
    Posts:
    84
    Thanks for the reply jvo3dc.

    It definitely seems like solution #1 is my only option for this project.
    Fiddled around with it, got local/world viewDir but can't get anything in tangent space like the shots above.
    So looked for code for viewDir in the editor source files but again nothing ...

    Is viewDir hard coded ? any ideas what the function would look like ?
     
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I'm also not finding the source for viewDir and I'm not sure whether you can pull this off in a surface shader. Another question is whether it's possible to obtain the tangent for uv2 at all in Unity. (It needs to somehow be generated for the model data.)

    There is one final trick to do this without any tangent or binormal. You can calculate the tangent using screen space derivatives. Here's some code that returns a world space normal using a tangent space normal map, uv0, world space position and world space normal as input.
    Code (csharp):
    1.  
    2. float3 normal(in_ps_main input) {
    3.    float3 normal_world = normalize(input.normal_world);
    4.    
    5.    float3 dx_pos_world = ddx(input.pos_world);
    6.    float3 dy_pos_world = ddy(input.pos_world);
    7.    float2 dx_uv0 = ddx(input.uv0);
    8.    float2 dy_uv0 = ddy(input.uv0);
    9.  
    10.    float3x3 mat = float3x3(dx_pos_world, dy_pos_world, cross(dx_pos_world, dy_pos_world));
    11.    float2x3 inv_trans_mat = float2x3(cross(mat[1], mat[2]), cross(mat[2], mat[0]));
    12.    float3 tangent_world = mul(float2(dx_uv0.x, dy_uv0.x), inv_trans_mat);
    13.    float3 binormal_world = mul(float2(dx_uv0.y, dy_uv0.y), inv_trans_mat);
    14.    
    15.    float3x3 tangent_to_world = float3x3(normalize(tangent_world), normalize(binormal_world), normal_world);
    16.    float3 normal_tangent = tex2D(normal_sampler, input.uv0).xyz * 2.f - 1.f;
    17.    normal_tangent.xy *= normal_effect;
    18.    normal_tangent = normalize(normal_tangent);
    19.    return mul(normal_tangent, tangent_to_world);
    20. }
    21.  
    I'm not sure whether ddx and ddy work on mobile platforms though. (I'm guessing they won't.) This method is also less precise. But, it might be worth a try.
     
  5. chai

    chai

    Joined:
    Jun 3, 2009
    Posts:
    84
    Cheers, not sure I follow but it seems ddx & ddxy only work for fragment shaders
     
  6. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    It's pretty complex code and ddx and ddy are indeed limited to the fragment shader. But that should not be an issue, since that's where you would do any parallax offsets.
     
  7. chai

    chai

    Joined:
    Jun 3, 2009
    Posts:
    84
    Hah Ye this is way over my head, didn't even know you can mix fragment and surface shaders.
    But thanks for your time, even learned a couple things !
     
  8. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    It's not so much that you can mix them. Surface shaders will generate fragment shader code that includes the lighting system in Unity.

    You can also use ddx and ddy in a surface shader. You only have to disable platforms that don't support ddx and ddy. I've used ddx and ddy in a surface shader before.

    I thought you were trying to use ddx and ddy in the vertex shader, which indeed is not possible.