Search Unity

Box projection reflection probes are inaccurate in 5.2

Discussion in 'General Graphics' started by Ante, Sep 9, 2015.

  1. Ante

    Ante

    Joined:
    Oct 21, 2010
    Posts:
    48

    You can see the inaccuracy of the reflection of the door here, as well as the bounds of the box projection in the editor window above.

    I've been using box projection reflection probes in Unity 5.1 to achieve a fairly accurate reflection of my rectangular room. In Unity 5.2, however, the reflections of the walls are offset 1 unit in every direction, perhaps due to the blend distance being 1. The blend distance attribute is disabled, so I can't modify it, but reducing the size of the box projection by 2 on one axis would achieve an accurate reflection on that axis. Unfortunately, reducing every axis by 2 to achieve accurate reflections would also put the floors, walls and ceiling outside of the reflection probe's area, leaving them with no reflections at all due to the new way reflections are handled and blended. The blending is a huge step forward, but this oversight makes box reflections pretty unusable for me. I'm pretty OCD and this sudden change is pretty annoying to me.

    I first noticed this issue on the arcade machine's monitor, where I use a box reflection to reflect the cabinet sides around the screen. While not perfect, the reflection was very convincing in VR. In 5.2, it seems to be reflecting a larger cabinet.

    I don't know if this was an oversight or a design decision, but the previous way of handling box projections seemed much more natural. The "box" you created was the box that was reflected. Now it seems there is no way to achieve that precise effect. Perhaps this should be independent from the blend distance (if that is the issue). If this was intentional, then if you were to be able to control the blend distance to extend out from or into the box projection (both positive or negative values), that should allow you to create the same effects as the current version as well as allow you more freedom.
     
  2. Ante

    Ante

    Joined:
    Oct 21, 2010
    Posts:
    48
    I just remembered seeing a new "Deferred Reflections" shader override in the graphics options, so I downloaded the shader source for 5.2 to see if their deferred reflections shader was in there, and it was. I was able to find and fix this issue pretty easily. I'm still not sure what the reasoning for this change was. The comments mention artifacts. I'll see if I run into any issues. Perhaps I'm using these new reflection probes incorrectly? Until then, I'm attaching my slight modification to Internal-DeferredReflections here. The changes are at line 67.

    Code (CSharp):
    1. Shader "Hidden/Internal-DeferredReflectionsAccurate" {
    2. Properties {
    3.     _SrcBlend ("", Float) = 1
    4.     _DstBlend ("", Float) = 1
    5. }
    6. SubShader {
    7.  
    8. // Calculates reflection contribution from a single probe (rendered as cubes) or default reflection (rendered as full screen quad)
    9. Pass {
    10.     ZWrite Off
    11.     ZTest LEqual
    12.     Blend [_SrcBlend] [_DstBlend]
    13. CGPROGRAM
    14. #pragma target 3.0
    15. #pragma vertex vert_deferred
    16. #pragma fragment frag
    17.  
    18. #include "UnityCG.cginc"
    19. #include "UnityDeferredLibrary.cginc"
    20. #include "UnityStandardUtils.cginc"
    21. #include "UnityStandardBRDF.cginc"
    22. #include "UnityPBSLighting.cginc"
    23.  
    24. sampler2D _CameraGBufferTexture0;
    25. sampler2D _CameraGBufferTexture1;
    26. sampler2D _CameraGBufferTexture2;
    27.  
    28. half3 distanceFromAABB(half3 p, half3 aabbMin, half3 aabbMax)
    29. {
    30.     return max(max(p - aabbMax, aabbMin - p), half3(0.0, 0.0, 0.0));
    31. }
    32.  
    33.  
    34. half4 frag (unity_v2f_deferred i) : SV_Target
    35. {
    36.     // Stripped from UnityDeferredCalculateLightParams, refactor into function ?
    37.     i.ray = i.ray * (_ProjectionParams.z / i.ray.z);
    38.     float2 uv = i.uv.xy / i.uv.w;
    39.  
    40.     // read depth and reconstruct world position
    41.     float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
    42.     depth = Linear01Depth (depth);
    43.     float4 viewPos = float4(i.ray * depth,1);
    44.     float3 worldPos = mul (_CameraToWorld, viewPos).xyz;
    45.  
    46.     half4 gbuffer0 = tex2D (_CameraGBufferTexture0, uv);
    47.     half4 gbuffer1 = tex2D (_CameraGBufferTexture1, uv);
    48.     half4 gbuffer2 = tex2D (_CameraGBufferTexture2, uv);
    49.  
    50.     half3 specColor = gbuffer1.rgb;
    51.     half oneMinusRoughness = gbuffer1.a;
    52.     half3 worldNormal = gbuffer2.rgb * 2 - 1;
    53.     worldNormal = normalize(worldNormal);
    54.     float3 eyeVec = normalize(worldPos - _WorldSpaceCameraPos);
    55.     half oneMinusReflectivity = 1 - SpecularStrength(specColor.rgb);
    56.     half occlusion = gbuffer0.a;
    57.  
    58.     half3 worldNormalRefl = reflect(eyeVec, worldNormal);
    59.     float blendDistance = unity_SpecCube1_ProbePosition.w; // will be set to blend distance for this probe
    60.     #if UNITY_SPECCUBE_BOX_PROJECTION
    61.         // For box projection, use expanded bounds as they are rendered; otherwise
    62.         // box projection artifacts when outside of the box.
    63.      
    64.         //This is where the reflections were becoming inaccurate by adding the blend distance.
    65.         //float4 boxMin = unity_SpecCube0_BoxMin - float4(blendDistance,blendDistance,blendDistance,0);
    66.         //float4 boxMax = unity_SpecCube0_BoxMax + float4(blendDistance,blendDistance,blendDistance,0);
    67.         float4 boxMin = unity_SpecCube0_BoxMin;
    68.         float4 boxMax = unity_SpecCube0_BoxMax;
    69.         half3 worldNormal0 = BoxProjectedCubemapDirection (worldNormalRefl, worldPos, unity_SpecCube0_ProbePosition, boxMin, boxMax);
    70.     #else
    71.         half3 worldNormal0 = worldNormalRefl;
    72.     #endif
    73.  
    74.     Unity_GlossyEnvironmentData g;
    75.     g.roughness        = 1 - oneMinusRoughness;
    76.     g.reflUVW        = worldNormal0;
    77.  
    78.     half3 env0 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, g);
    79.  
    80.     UnityLight light;
    81.     light.color = 0;
    82.     light.dir = 0;
    83.     light.ndotl = 0;
    84.  
    85.     UnityIndirect ind;
    86.     ind.diffuse = 0;
    87.     ind.specular = env0 * occlusion;
    88.  
    89.     half3 rgb = UNITY_BRDF_PBS (0, specColor, oneMinusReflectivity, oneMinusRoughness, worldNormal, -eyeVec, light, ind).rgb;
    90.  
    91.     // Calculate falloff value, so reflections on the edges of the probe would gradually blend to previous reflection.
    92.     // Also this ensures that pixels not located in the reflection probe AABB won't
    93.     // accidentally pick up reflections from this probe.
    94.     half3 distance = distanceFromAABB(worldPos, unity_SpecCube0_BoxMin.xyz, unity_SpecCube0_BoxMax.xyz);
    95.     half falloff = saturate(1.0 - length(distance)/blendDistance);
    96.     return half4(rgb, falloff);
    97. }
    98.  
    99. ENDCG
    100. }
    101.  
    102. // Adds reflection buffer to the lighting buffer
    103. Pass
    104. {
    105.     ZWrite Off
    106.     ZTest Always
    107.     Blend [_SrcBlend] [_DstBlend]
    108.  
    109.     CGPROGRAM
    110.         #pragma target 3.0
    111.         #pragma vertex vert
    112.         #pragma fragment frag
    113.         #pragma multi_compile ___ UNITY_HDR_ON
    114.  
    115.         #include "UnityCG.cginc"
    116.  
    117.         sampler2D _CameraReflectionsTexture;
    118.  
    119.         struct v2f {
    120.             float2 uv : TEXCOORD0;
    121.             float4 pos : SV_POSITION;
    122.         };
    123.  
    124.         v2f vert (float4 vertex : POSITION)
    125.         {
    126.             v2f o;
    127.             o.pos = mul(UNITY_MATRIX_MVP, vertex);
    128.             o.uv = ComputeScreenPos (o.pos).xy;
    129.             return o;
    130.         }
    131.  
    132.         half4 frag (v2f i) : SV_Target
    133.         {
    134.             half4 c = tex2D (_CameraReflectionsTexture, i.uv);
    135.             #ifdef UNITY_HDR_ON
    136.             return c;
    137.             #else
    138.             return exp2(-c);
    139.             #endif
    140.  
    141.         }
    142.     ENDCG
    143. }
    144.  
    145. }
    146. Fallback Off
    147. }
    148.  
    Edit: Yeah, there are definitely blending issues with this. I'll keep messing with it, but I'm no shader expert. Is there any way to revert to the old way of doing reflections, if there is no foreseeable fix for this? Most of the objects in my game don't need to blend nicely and were set to only use one cubemap anyway.
     
    Last edited: Sep 9, 2015
  3. Ante

    Ante

    Joined:
    Oct 21, 2010
    Posts:
    48
    So changing the blend distance to 0 or close to 0 fixes the inaccuracy issue, but the new issue is, why is it disabled? The player is set to use deferred rendering and so is the main camera, yet I can't edit this value unless I'm in edit mode.