Search Unity

Blending problem in Deferred - Physically Based Rendering

Discussion in 'Shaders' started by LeoTS, Oct 31, 2013.

  1. LeoTS

    LeoTS

    Joined:
    Jan 1, 2013
    Posts:
    18
    Greetings Unity forums!

    I've been working on a realtime image based lighting shader and it went pretty good! (as you can see in this image)


    But I'd like to combine this with a bumped specular surface shader I made that also works fine:


    The problem happens when i try to combine the two, using Blend One One, gives me this result:


    As you can see, in the preview window it works nicely, that is in forward rendering path. The scene is being rendered in deferred and this strange behavior happens.
    I tried many things but I can't find a solution for that, I don't know if it's a unity bug or something, could some one help me?

    Btw, i'm using a equirectangular cubemap rendering for down sampling it and avoiding seams in the faces corners, and generating/setting it with an other script, it works perfectly, the only problem is actually the blending.

    This is the complete shader:
    Code (csharp):
    1. Shader "Reflective/DiffuseTest" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _ShininessForce ("Shininess Force (0-5)", Range (0, 5)) = 1
    5.         _GlossForce ("Gloss Force (0-5)", Range (0, 5)) = 1
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.         _SpecMap ("Gloss(RGB) Shininess(A)", 2D) = "gray" {}
    8.         _BumpMap ("Normalmap", 2D) = "bump" {}
    9.         _AmbMip ("Ambient MipLevel (0-10)", Range(0,10)) = 1
    10.         _SpecMip ("Spec MipLevel (0-10)", Range(0,10)) = 1
    11.         _Fresnel ("Fresnel (0-10)", Range(0,10)) = 1
    12.     }
    13.  
    14.     SubShader{
    15.         Tags { "RenderType" = "Opaque" }
    16.        
    17.         Pass {
    18.             Tags { "LightMode" = "Always" }
    19.            
    20.             CGPROGRAM
    21.                 #pragma vertex vert
    22.                 #pragma fragment frag
    23.                 #pragma fragmentoption ARB_precision_hint_fastest
    24.                 #pragma glsl
    25.                 #pragma target 3.0
    26.  
    27.                 #include "UnityCG.cginc"
    28.  
    29.                 struct appdata {
    30.                     float4 vertex : POSITION;
    31.                     float4 tangent : TANGENT;
    32.                     float3 normal : NORMAL;
    33.                     float4 texcoord : TEXCOORD0;
    34.                 };
    35.  
    36.                 struct v2f{
    37.                     float4  pos : SV_POSITION;
    38.                     float2  uv : TEXCOORD0;
    39.                     float3  normal : TEXCOORD2;
    40.                     float3  tangent : TEXCOORD3;
    41.                     float3  binormal : TEXCOORD4;
    42.                     float3  viewDir : TEXCOORD5;
    43.                 };
    44.  
    45.                 v2f vert (appdata v){
    46.                     v2f o;
    47.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    48.                     o.uv = v.texcoord.xy;
    49.                     o.normal = v.normal;
    50.                     o.tangent = v.tangent;
    51.                     o.binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
    52.                     o.viewDir = WorldSpaceViewDir(v.vertex);
    53.                     return o;
    54.                 }
    55.  
    56.                 sampler2D _MainTex, _SpecMap, _BumpMap;
    57.                 sampler2D _Equirectangular;
    58.                 float _EquirectangularBlur,_AmbMip,_SpecMip,_Fresnel;
    59.  
    60.                 #define PI 3.141592653589793
    61.  
    62.                 inline float3 TangentToWorld(float3 tSpace, float3 normal, float3 tangent, float3 binormal){
    63.                     float3 normalO = (tangent * tSpace.x) + (binormal * tSpace.y) + (normal * tSpace.z);
    64.                     return normalize(mul((float3x3)_Object2World, normalO));
    65.                 }
    66.  
    67.                 inline float2 RadialCoords(float3 a_coords){
    68.                     float lon = atan2(a_coords.z, a_coords.x);
    69.                     float lat = acos(a_coords.y);
    70.                     float2 sphereCoords = float2(lon, lat) * (1.0 / PI);
    71.                     return float2(sphereCoords.x * 0.5 + 0.5, 1 - sphereCoords.y);
    72.                 }
    73.  
    74.                 float4 frag(v2f IN) : COLOR {
    75.                     IN.viewDir = normalize (IN.viewDir);
    76.  
    77.                     fixed3 albedo = tex2D(_MainTex, IN.uv).rgb;
    78.                     float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv));
    79.                     float3 specular = tex2D(_SpecMap, IN.uv).rgb;
    80.  
    81.                     float3 normalW = TangentToWorld ( normal, IN.normal, IN.tangent, IN.binormal);
    82.                     float2 ambCoords = RadialCoords(normalW);
    83.                     float3 refl = -reflect(IN.viewDir, normalW);
    84.                     float2 specCoords = RadialCoords(refl);
    85.  
    86.                     float3 amb = tex2Dlod(_Equirectangular, float4(ambCoords.xy, 0, _AmbMip)).rgb;
    87.                     float3 spec = tex2Dlod(_Equirectangular, float4(specCoords.xy, 0, _SpecMip)).rgb;
    88.                    
    89.                     float VdotN = dot( IN.viewDir, normalW );
    90.                     float fresnel = pow( abs(1.0 - VdotN), 5.0 );
    91.                     fresnel += _Fresnel * ( 1.0 - fresnel );
    92.  
    93.                     float4 c;
    94.                     c.rgb = (albedo * amb) + (spec * specular.rgb * fresnel);
    95.                     c.a = 1.0;
    96.                     return c;
    97.                 }
    98.             ENDCG
    99.         }
    100.        
    101.         Blend One One
    102.        
    103.         CGPROGRAM
    104.             #pragma surface surf ColoredSpecular noambient
    105.             #pragma target 3.0
    106.             #pragma exclude_renderers d3d11_9x
    107.            
    108.             struct Input {
    109.                 float2 uv_MainTex;
    110.             };
    111.            
    112.             struct MySurfaceOutput {
    113.                 half3 Albedo;
    114.                 half3 Normal;
    115.                 half3 Emission;
    116.                 half Specular;
    117.                 half3 GlossColor;
    118.                 half Alpha;
    119.             };
    120.            
    121.             inline half4 LightingColoredSpecular (MySurfaceOutput s, half3 lightDir, half3 viewDir, half atten){
    122.                 half3 h = normalize (lightDir + viewDir);
    123.                 half diff = max (0, dot (s.Normal, lightDir));
    124.                 float nh = max (0, dot (s.Normal, h));
    125.                 float spec = pow(nh,32.0);
    126.                 half3 specCol = spec * s.GlossColor;
    127.                 half4 c;
    128.                 c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * specCol) * (atten * 2);
    129.                 c.a = s.Alpha;
    130.                 return c;
    131.             }
    132.             inline half4 LightingColoredSpecular_PrePass (MySurfaceOutput s, half4 light){
    133.                 half3 spec = light.a * s.GlossColor;
    134.                 half4 c;
    135.                 c.rgb = (s.Albedo * light.rgb + light.rgb * spec);
    136.                 c.a = s.Alpha;
    137.                 return c;
    138.             }
    139.            
    140.             sampler2D _MainTex,_SpecMap,_BumpMap;
    141.            
    142.             half4 _Color;
    143.             half _ShininessForce;
    144.             half _GlossForce;
    145.            
    146.             void surf (Input IN, inout MySurfaceOutput o){
    147.                 half4 diff = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    148.                 half4 spec_tex = tex2D (_SpecMap, IN.uv_MainTex);
    149.                
    150.                 o.Albedo = diff.rgb;
    151.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    152.                 o.GlossColor = spec_tex.rgb*_GlossForce;
    153.                 o.Specular = _ShininessForce*spec_tex.a;
    154.                 o.Alpha = _Color.a;
    155.             }
    156.         ENDCG
    157.        
    158.     }
    159.     FallBack "VertexLit"
    160. }
     
  2. LeoTS

    LeoTS

    Joined:
    Jan 1, 2013
    Posts:
    18
    After some tests, if I set it as Transparent, it will be rendered with forward rendering, so it works, but won't receive shadows :/
     
  3. cician

    cician

    Joined:
    Dec 10, 2012
    Posts:
    233
    I haven't read all of it, but...
    You have to understand that the second part, which is a surface shader generates different passes that get rendered in different moments.
    Instead of just setting blend mode before CGPROGRAM block you can try decal:add in #surf directive.
    That being said, since you're targeting SM3 anyway, you're probably better off combining both in a single surface shader for better performance.
     
  4. LeoTS

    LeoTS

    Joined:
    Jan 1, 2013
    Posts:
    18
    Yea i think thats the way to go, its actually turning out well, except for one problem. For some reason, the WorldRelfectionVector(IN,o.Normal) is varying with the pixel distance from the screen, messing up the Equirectangular lookup. But when calculated using the interpolated vertex normal/tangent/binormal/viewdir in the fragment shader it's perfectly fine. What could be causing it to vary with distance?

    Here is how the reflection vector looks: (note that using the surface function it changes with distance, while in the fragment it keeps constant)


    What could be causing it?
    Or how could I fix that? :|

    Btw, thanks for helping
     
    Last edited: Nov 4, 2013
  5. WGermany

    WGermany

    Joined:
    Jun 27, 2013
    Posts:
    78
    I know its a bit offtopic but I am working in the same thing. I kinda jumped into shaders not knowing a lot of things while still being able to pull off some pretty crazy things. I find it difficult to dive into the nitty gritty of shaders because of the scarce sources of information on the web. You can add me on Skype if you want to talk from there I can PM you my username.

    Debugging
    I am curious how does one know if their view/normal/tangent etc reflection vectors and whatnot are correct when debugging them?

    Equirectangular Cubemapping
    I know equirectangular cubemap rendering is useful so one doesn't have to deal with edge seams from normal cubemapping procedures. But how do you actually do equirectangular cubemap rendering?
    Does it allow HDR cubemapping?
    Does the equirectangular cubemap work in realtime (I know you've said real time IBL but how does it blur in realtime if so)?

    Specular
    What type of specular term are you going to be using? Blinn Phong, Cook Torrance, Anisotropic ones?

    Multi-Layered BRDF
    One last question have you thought about layered BRDF's (see link below)?
    http://www.cescg.org/CESCG-2010/papers/ElekOskar.pdf
     
  6. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Just curious, did you manage to fix you problem?
     
  7. LeoTS

    LeoTS

    Joined:
    Jan 1, 2013
    Posts:
    18
    Hey WGermany,

    what I do for the debug is simply apply those vectors as emission color of the material, so we can "see" the vector and interpret it as red=x green=y blue=z, so it helps to know if something is wrong

    The Equirectangular mapping its fully realtime, a camera renders 6 cube faces (one each frame) and a material replacement shader renders it to a equirectangular 2d texture, btw it also supports HDR
    I'm using a modified blinn phong to support a colored specular texture, and using a fresnel term for the glossy reflections

    I don't think the multi-layered BRDF will be useful for me, since i'm using the PBR only for some special objects in my scene, but you could implement it if you need, as a different lighting model in the surface shader, but if you want a even more complex shader, you could add refraction on the object, but you would need a grabpass+distortion, and for blending the passes the object needs to be rendered as transparent (in forward) so it wouldn't support shadows in deferred too.

    So i think it depends of how you want to use it, a complete physically based material would be too heavy and unpractical for using in every object
     
    Last edited: Nov 6, 2013
  8. LeoTS

    LeoTS

    Joined:
    Jan 1, 2013
    Posts:
    18
    After some tests, it seems like this inconstancy of the world reflection vector don't make difference when using a cube map, so i think there is no practical way to fix it :/
    I have no idea how the calculation can be fixed now :(
    Here is a screenshot of the distortion occurred: (surface shader, using the WorldReflectionVector(IN,o.Normal) to get the coord in the function RadialCoords)
    Code (csharp):
    1. inline float2 RadialCoords(float3 a_coords){
    2.      float lon = atan2(a_coords.z, a_coords.x);
    3.      float lat = acos(a_coords.y);
    4.      float2 sphereCoords = float2(lon, lat) * (1.0 / PI);
    5.      return float2(sphereCoords.x * 0.5 + 0.5, 1 - sphereCoords.y);
    6. }


    Now the same calculations but in a fragment program instead of the surface program:



    Any idea of how that could be fixed?
     
  9. LeoTS

    LeoTS

    Joined:
    Jan 1, 2013
    Posts:
    18
    Hey guys! I finally managed to fix the shader!
    I converted the pass entirely in to the surface shader, but instead of using unity's worldNormal and worldRefl, I calculated the normal/tangent/binormal/viewDir manually in a vertex program. Its working wonderfully now!



    The final shader:
    Code (csharp):
    1. CGPROGRAM
    2.             #pragma surface surf ColoredSpecular noambient vertex:vert
    3.             #pragma target 3.0
    4.             #pragma exclude_renderers d3d11_9x
    5.             #pragma glsl
    6.            
    7.             struct Input {
    8.                 float2 uv_MainTex;
    9.                 float3 viewDir2;
    10.                 half3 normal;
    11.                 half3 tangent;
    12.                 half3 binormal;
    13.             };
    14.            
    15.             void vert(inout appdata_full v,out Input o){
    16.                 half4 p_normal = mul(float4(v.normal,0.0f),_World2Object);
    17.                 half4 p_tangent = mul(_Object2World,v.tangent);
    18.  
    19.                 half3 normal_input = normalize(p_normal.xyz);
    20.                 half3 tangent_input = normalize(p_tangent.xyz);
    21.                 half3 binormal_input = cross(p_normal.xyz,p_tangent.xyz);
    22.  
    23.                 o.normal = normal_input;
    24.                 o.tangent = tangent_input;
    25.                 o.binormal = binormal_input;
    26.                
    27.                 o.viewDir2 = WorldSpaceViewDir(v.vertex);  // I'm using viewDir2 instead of viewDir, because it conflicts with the unity's viewDir
    28.             }
    29.            
    30.             struct MySurfaceOutput {
    31.                 half3 Albedo;
    32.                 half3 Normal;
    33.                 half3 Emission;
    34.                 half Specular;
    35.                 half3 GlossColor;
    36.                 half Alpha;
    37.             };
    38.            
    39.             inline half4 LightingColoredSpecular (MySurfaceOutput s, half3 lightDir, half3 viewDir, half atten){
    40.                 half3 h = normalize (lightDir + viewDir);
    41.                 half diff = max (0, dot (s.Normal, lightDir));
    42.                 float nh = max (0, dot (s.Normal, h));
    43.                 float spec = pow(nh,32.0);
    44.                 half3 specCol = spec * s.GlossColor;
    45.                 half4 c;
    46.                 c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * specCol) * (atten * 2);
    47.                 c.a = s.Alpha;
    48.                 return c;
    49.             }
    50.             inline half4 LightingColoredSpecular_PrePass (MySurfaceOutput s, half4 light){
    51.                 half3 spec = light.a * s.GlossColor;
    52.                 half4 c;
    53.                 c.rgb = (s.Albedo * light.rgb + light.rgb * spec);
    54.                 c.a = s.Alpha;
    55.                 return c;
    56.             }
    57.            
    58.             sampler2D _MainTex,_SpecMap,_BumpMap,_Equirectangular;
    59.            
    60.             half4 _Color;
    61.             half _ShininessForce;
    62.             half _GlossForce;
    63.             float _AmbMip,_SpecMip,_Fresnel;
    64.            
    65.             #define PI 3.141592653589793
    66.  
    67.             inline float3 TangentToWorld(float3 tSpace, float3 normal, float3 tangent, float3 binormal){
    68.                 float3 normalO = (tangent * tSpace.x) + (binormal * tSpace.y) + (normal * tSpace.z);
    69.                 return normalize(mul((float3x3)_Object2World, normalO));
    70.             }
    71.  
    72.             inline float2 RadialCoords(float3 a_coords){
    73.                 float lon = atan2(a_coords.z, a_coords.x);
    74.                 float lat = acos(a_coords.y);
    75.                 float2 sphereCoords = float2(lon, lat) * (1.0 / PI);
    76.                 return float2(sphereCoords.x * 0.5 + 0.5, 1 - sphereCoords.y);
    77.             }
    78.            
    79.             void surf (Input IN, inout MySurfaceOutput o){
    80.                 half4 diff = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    81.                 half4 spec_tex = tex2D (_SpecMap, IN.uv_MainTex);
    82.                
    83.                 o.Albedo = diff.rgb;
    84.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    85.                 o.GlossColor = spec_tex.rgb*_GlossForce;
    86.                 o.Specular = _ShininessForce*spec_tex.a;
    87.                
    88.                 IN.viewDir2 = normalize (IN.viewDir2);
    89.                
    90.                 float3 normalW = TangentToWorld ( o.Normal, IN.normal, IN.tangent, IN.binormal);
    91.                 float2 ambCoords = RadialCoords(normalW);
    92.                 float3 refl = -reflect(IN.viewDir2, normalW);
    93.                 float2 specCoords = RadialCoords(refl);
    94.  
    95.                 float3 amb = tex2Dlod(_Equirectangular, float4(ambCoords.xy, 0, _AmbMip)).rgb;
    96.                 float3 spec = tex2Dlod(_Equirectangular, float4(specCoords.xy, 0, _SpecMip)).rgb;
    97.                
    98.                 float VdotN = dot( IN.viewDir2, normalW );
    99.                 float fresnel = pow( abs(1.0 - VdotN), 5.0 );
    100.                 fresnel += _Fresnel * ( 1.0 - fresnel );
    101.                
    102.                 float4 c;
    103.                 c.rgb = (o.Albedo * amb) + (spec * spec_tex.rgb * fresnel);
    104.                 c.a = 1.0;
    105.                
    106.                 o.Emission = c.rgb;
    107.             }
    108.         ENDCG
    I'll just work a bit more on it to add HDR support