Search Unity

shader problem - affecting other materials..?!

Discussion in 'Shaders' started by Foxxis, Mar 18, 2007.

  1. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    So, a while back I got some help with a transparent water shader (without refraction/reflection - indie license for now).
    It works well, and since it's using a gradient for the alpha it looks pretty good for what it is and allows me to use some old-school tricks like objects below water to fake reflectivity and so on.

    Anyway, the weird part is that the shader affects other objects than the one it's applied on. If I have an object with an alpha (diffuse+alpha) over the water plane, it too will become fully transparant at certain angles. It doesn't follow the fade-in/out caused by the fresnel-gradient in the water material either, it just switches on/off.

    If I disable the water shader or switch it to the standard (indie) water material, the problem goes away. So it seems this shader is affecting more of the drawing than just the water plane.

    I have no experience with writing shaders myself, so I can't really pinpoint the problem myself.
    Any help would be much appreciated!

    Here's parts of the code, I can post it all if need be:

    Code (csharp):
    1. // -----------------------------------------------------------
    2. // ARB fragment program
    3.  
    4. Subshader {
    5.     Tags { "Queue" = "Transparent" }
    6.     Blend SrcAlpha OneMinusSrcAlpha
    7.     ColorMask RGB
    8.    
    9.     Pass {
    10.  
    11. CGPROGRAM
    12. // profiles arbfp1
    13. // vertex vert
    14. // fragment frag
    15. // fragmentoption ARB_precision_hint_fastest
    16. // fragmentoption ARB_fog_exp2
    17.  
    18. sampler2D _BumpMap : register(s0);
    19. sampler2D _ColorControl : register(s1);
    20.  
    21. half4 frag( v2f i ) : COLOR
    22. {
    23.     half3 bump1 = tex2D( _BumpMap, i.bumpuv[0] ).rgb;
    24.     half3 bump2 = tex2D( _BumpMap, i.bumpuv[1] ).rgb;
    25.     half3 bump = bump1 + bump2 - 1;
    26.    
    27.     half fresnel = dot( i.viewDir, bump );
    28.     half4 water = tex2D( _ColorControl, float2(fresnel,fresnel) );
    29.    
    30.     half4 col;
    31.     col.rgb = lerp( water.rgb, _horizonColor.rgb, water.a);
    32.     //col.a = water.a*2+0.2;
    33.     return col;
    34. }
    35. ENDCG
    36.         SetTexture [_BumpMap] {}
    37.         SetTexture [_ColorControl] {}
    38.     }
    39. }
    40.  
    41. // -----------------------------------------------------------
    42. // Radeon 9000
    43.  
    44. Subshader {
    45.     Tags { "Queue" = "Transparent" }
    46.     Blend SrcAlpha OneMinusSrcAlpha
    47.     ColorMask RGB
    48.    
    49.     Pass {
    50.  
    51. CGPROGRAM
    52. // vertex vert
    53. // just define 'vert' as a vertex shader, the code is included
    54. // from the section on top
    55. ENDCG
    56.  
    57.         Program "" {
    58.             SubProgram {
    59.                 Local 0, [_horizonColor]
    60.  
    61. "!!ATIfs1.0
    62. StartConstants;
    63.     CONSTANT c0 = program.local[0];
    64. EndConstants;
    65.  
    66. StartPrelimPass;
    67.     SampleMap r0, t0.str;
    68.     SampleMap r1, t1.str;
    69.     PassTexCoord r2, t2.str;
    70.    
    71.     ADD r1, r0.bias, r1.bias;   # bump = bump1 + bump2 - 1
    72.     DOT3 r2, r1, r2;            # fresnel: dot (bump, viewer-pos)
    73. EndPass;
    74.  
    75. StartOutputPass;
    76.     SampleMap r2, r2.str;
    77.  
    78.     LERP r0.rgb, r2.a, c0, r2;  # fade in reflection
    79.     MOV r0.a, r2.a;
    80. EndPass;
    81. "
    82. }
    83. }
    84.         SetTexture [_BumpMap] {}
    85.         SetTexture [_BumpMap] {}
    86.         SetTexture [_ColorControl] {}
    87.     }
    88. }
     
  2. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Wild guess: I think what you're seeing is not the water affecting the objects; it's water "occluding" the objects. The shader you are using does write into depth buffer, so it will occlude all objects that are "behind" the water (and that are drawn after the water is drawn).

    I'd suggest adding
    Code (csharp):
    1. ZWrite Off
    to the subshaders, for example after ColorMask RGB lines.
     
  3. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Thanks for the reply!
    After reading a bit I did start to suspect that it had something to do with queuing.

    However, is it really possible to solve the problem if it is related to Z-buffering, as I do want the water to be able to occlude other objects - but in a correct manner? The problem I have is solely with objects using an alpha shader, ordinary diffuse materials aren't affected.

    The shader in question is actually the one posted by...I think it was you..in response to a question about transparent indie water. Since I have no experience whatsoever with shaders, I would be very thankful if you would give an example of how to insert that code if in fact it will solve the problem?

    I'll give it a whirl, but I fear my computer will melt once I start tampering with the shader code... :)

    TIA!
    Dan
     
  4. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Ok, so you want the shader to be transparent, but also occlude objects that are below it? That are a bit conflicting goals :)
     
  5. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Hehe - I want it to be partially transparent, which it is. :) However, I don't want it to affect the transparency of transparent objects that are in front of it - which it does. ;)

    (I have the waterplane using the shader, and a mesh plane above it with a diffuse+alpha material - that mesh/material is being affected by the water shader in some way).

    I'm guessing it's related to which order the objects are being drawn in, but I have no idea how I can affect that..?
     
  6. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Oh, forgot to mention:
    I think I added ZWrite Off in the right places, but it made no difference.... :cry:
     
  7. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Ok, the hard part is (and the reason why builtin shaders are not partially transparent by default): it's hard to get transparent objects "right". Ever noticed how all games generally avoid transparent stuff, except for special effects? Yes, it has to do with sorting.

    Transparent objects (the ones in "transparent" render queue) are drawn starting from furthest one. Currently the sorting is based on object's bounding box center; so if you have a small transparent object and a big transparent water plane, their center points could be in any configuration, even if small object is actually always "in front" of the water.

    Now, "what to do?" is the hard question. Several possible ways:

    * Maybe you don't need transparent objects besides water? :roll: In some cases objects don't need to be partly transparent, and only have "cutout" transparency (think fences or tree leaves) - in that case it can be solved nicely.

    * Put water in transparent render queue, and turn off ZWrite. It should not make other transparent objects invisible; but it can appear that other objects are "behind" the water when in fact they are in front of it (and vice versa). Position the objects/water so that these cases are minimized and just live with it. Quite a lot of even big AAA games do that. Most players can't properly tell 2D from 3D anyways - would they notice sorting issues? right?

    * Don't put water in transparent render queue. This however can do other artifacs - for example if water happens to be drawn first, then you would see background color or skybox through it, instead of some ground underneath.

    And of course, if you have a small scene that shows these problems, with instructions on when they appear - just send it via Report Bug.app, I'll take look.
     
  8. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Exactly the case! A large waterplane with transparent objects in front. This would also explain why the effect differs based on the viewing angle rotated around the global Y-axis.

    I want the partially transparent objects in front. Mostly because the look nice. ;)
    The effect isn't that annoying if the water shader is set right, so I might get away with it.

    A final question - will I get around this problem when I upgrade to Pro, or does the Pro water introduce the same problem?

    Thanks for your help - especially on a Sunday! :)
     
  9. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    After a bit of fiddling around, I solved another problem I had been having with the transparent water!

    The shader as it was seemed to change the point in the fresnel gradient used based on not only the angle to the surface, but the distance. Zooming out would give the water a different look to the one up close. Since I'll be changing the camera altidue a fair bit, this didn't look pleasant.

    By some guessing and trial and error, I changed the following line
    Code (csharp):
    1. half fresnel = dot(i.viewDir, bump );
    to:
    Code (csharp):
    1.     half fresnel = dot(normalize(i.viewDir), bump );
    It seemed i.viewDir was a vector, and could be the reason the fresnel value changed as the camera position did the same. Normalizing it did the trick - although I'm not certain why. I would have thought it was normalized to start with, but I guess not.

    Anyway, I now have a great-looking shader that behaves the same way no matter where the camera is (or how large the water plane is). :)
     
  10. NicholasFrancis

    NicholasFrancis

    Joined:
    Apr 8, 2005
    Posts:
    1,587
    That makes perfect sense - looks like a bug we missed.

    I would recommend trying the normalization in the vertex program - so you normalize before setting the viewDir. That will perform a lot better!
     
  11. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    I'm in deep water here (ha...ha.. ;) ).
    I can't seem to find where in the vertex program to do the operation to get a similar result:

    Code (csharp):
    1. v2f vert(appdata v)
    2. {
    3.     v2f o;
    4.     float4 s;
    5.  
    6.     PositionFog( v.vertex, o.pos, o.fog );
    7.  
    8.     // scroll bump waves
    9.     float4 temp;
    10.     temp.xyzw = (v.vertex.xzxz + _Time.x * WaveSpeed.xyzw) * _WaveScale;
    11.     o.bumpuv[0] = temp.xy * float2(.4, .45);
    12.     o.bumpuv[1] = temp.wz;
    13.  
    14.     // object space view direction
    15.     o.viewDir.xzy = normalize( ObjSpaceViewDir(v.vertex) );
    16.     //o.viewDir = normalize(o.viewDir); // Tried this - didn't help.
    17.     return o;
    18. }
    19.  
    20. ENDCG
    21.  
    22. // -----------------------------------------------------------
    23. // ARB fragment program
    24.  
    25. Subshader {
    26.     Tags { "Queue" = "Transparent" }
    27.     Blend SrcAlpha OneMinusSrcAlpha
    28.     ColorMask RGB
    29.  
    30.     Pass {
    31.  
    32. CGPROGRAM
    33. // profiles arbfp1
    34. // vertex vert
    35. // fragment frag
    36. // fragmentoption ARB_precision_hint_fastest
    37. // fragmentoption ARB_fog_exp2
    38.  
    39. sampler2D _BumpMap : register(s0);
    40. sampler2D _ColorControl : register(s1);
    41.  
    42. half4 frag( v2f i ) : COLOR
    43. {
    44.     half3 bump1 = tex2D( _BumpMap, i.bumpuv[0] ).rgb;
    45.     half3 bump2 = tex2D( _BumpMap, i.bumpuv[1] ).rgb;
    46.     half3 bump = bump1 + bump2 - 1;
    47.    
    48.     half fresnel = dot(normalize(i.viewDir), bump ); // new way
    49.     //half fresnel = dot(i.viewDir, bump ); //old way
    50.     half4 water = tex2D( _ColorControl, float2(fresnel,fresnel) );
    51.    
    52.     half4 col;
    53.     col.rgb = lerp( water.rgb, _horizonColor.rgb, water.a );
    54.     col.a = water.a;
    55.     return col;
    56. }
    57. ENDCG
    58.         SetTexture [_BumpMap] {}
    59.         SetTexture [_ColorControl] {}
    60.     }
    61. }
    62.  
    Any hints? :)
    When I'm done with this I'll probably understand shader programming as well... :D
     
  12. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    It is normalized in the vertex shader; but not normalized in the fragment shader - and yes, it does change things a bit. In fragment shader (like you just did) looks nicer, but is slower; so there's a tradeoff.
     
  13. NicholasFrancis

    NicholasFrancis

    Joined:
    Apr 8, 2005
    Posts:
    1,587
    Hmm - it appears to already have been done in the vertex program.

    this means that the sole difference between your version and the official is that your version has slightly higher precision (at a performance penalty).

    How's your water mesh tesselated? You should not see a huge difference if the tesselation is "ok-ish".
     
  14. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    I've used the standard water mesh and scaled it up quite a bit, so I guess that could throw the shader off a bit. Didn't think of that actually as it was many moons ago since the last time that was a factor in a 3D animation/rendering package (which is my native home :) ).

    So basically I should be able to switch back to the faster/rougher way without normalization in the vertex program if I use a finer mesh?
     
  15. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Indeed - the better the mesh is, the smaller the difference.

    Which brings us to the final, final question:
    What incurs the bigger performance penalty?

    - a LARGE rough mesh, with the vertex program normalization
    - same size, but much finer mesh (1-2k polys) but the official shader with no vertex viewDir normalization

    Again, many thanks for your help!
     
  16. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    That depends on what hardware you're targeting. Just normalizing in the vertex shader makes fresnel dependent on the viewer position a bit, just like you realized; and the larger the triangles in the mesh, the larger the difference.

    Finer mesh is less difference, but more vertices to process (which is almost always a no issue... except on some integrated graphics cards).

    Normalizing in the fragment shader makes everything "correct" and is not an issue on most graphics cards (but then you have things like GeForce FX5200 or Intel GMA950... these are slow).

    Edit: ...1-2k triangles mesh is not very fine, I think, so go for that. I think the standard water mesh is 700 triangles or so. If you'd had 20-30k triangles in the mesh, then it's worth thinking about.
     
  17. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    OK - so if I shoot for slower hardware (like Mac Minis and the Macbooks - which use GMA950 IIRC) I would be better off with a slightly denser mesh and the shader as is, since those cards deal better with more triangles than a more detailed shader?

    I think I'll have to make this an option in-game, as I still think the variant with normalization is slightly better... :)
     
  18. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Resurrecting this old thread. :)

    Regarding the transparency sorting problems:
    - What if I know the exact order I want the semi-transparent objects to be rendered in? Can I force the sort order through code?

    My problem is that I have my partially transparent water, and some semi-transparent objecs above it. I know the water will always be "below" the other transparent objects. Currently, it still sometimes looks like that's not the case (due to the sorting going wrong - the objects are of very different sizes).

    Can I force it?
     
  19. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Not easily... you could use custom shaders for transparent objects, that don't use "transparent" render queue, but use "overlay" render queue (docs).
     
  20. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Alright! Will give it a whirl, even though it doesn't sound like the perfect solution.

    Thanks for your help! :)
     
  21. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Ressurecting again.
    Two questions:

    - How can I access the code for the built in unity shaders (if I want to modify one of them to use the overlay render queue)?

    - How exactly is the sorting order determined? It seems like it is based on the geometrical center of the objects. Any way I can modify or trick the engine to think the object is further away than it actually is? I tried modifying the axis center in C4D, but it made no difference...

    Many thanks in advance!
    Dan
     
  22. drJones

    drJones

    Joined:
    Oct 19, 2005
    Posts:
    1,351
  23. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    ARGH. I've been there recently, but my eyes drifted to the list of user shaders - I actually missed the built in shader source entry. Oh, well. Thanks! :)

    As for my second question, I've come up with a (less than elegant) way of solving the problem.

    As I mentioned, I had one big object (water plane) which basically messed up all alpha objects in the scene due to the big difference in size, which in turn makes it hard for the engine to sort the alpha objects correctly.

    Since I couldn't force the sorting order, and editing the object axis didn't affect the problem (seems like the engine automagically determines the geometrical center internally), I modified the actual geometry. I created a small polygon some distance below the water plane itself. Since the water plane now has a much "deeper" geometry, it will be (correctly) sorted below all other alpha objects. Problem solved, albeit not in a very nice way... :)
     
  24. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Hey, good suggestion...I remember seeing that before, but forgot about it. It fixed that spot in Storm City where the ground was being drawn on top of the rain, thanks!

    --Eric
     
  25. Foxxis

    Foxxis

    Joined:
    Jun 27, 2006
    Posts:
    1,108
    Cool - very happy it helped you as well! :)