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

Dynamic Blinders (Eagle Flight)

Discussion in 'General Graphics' started by matazematratze, Feb 15, 2018.

  1. matazematratze

    matazematratze

    Joined:
    May 3, 2015
    Posts:
    9
    So I'm working on a VR game that is basically Eagle Flight (Ubisoft) and since people are experiencing motion sickness from it, I'm in the process of copying some of the techniques Eagle Flight uses to mitigate that - namely what they call Dynamic Blinders. See what that is here



    What they do is detect if an object is close to the player and dynamically black out a portion of the screen depending on the relative direction to the player and how close it is. They also black out parts if the player turns around very tightly but that's not important here.

    The way I've implemented this is by parenting a plane with a custom cutout shader to my camera, the way you would when creating a simple fade to black effect, where a black plane is right in front of the camera facing it and you lerp the alpha value over time. Only my plane is transparent and the shader will dynamically render black pixels where they need to be.

    What the shader does is it has MaterialToggle integers that get set via SendMessage from hitboxes around the player and work as booleans. "Is there an object close to the right? -> set int = 1". Then it does some math to determine the pixels that need to be rendered black.
    It works fine, I'm happy with the result considering this is my first attempt in manipulating shaders.

    The problem:

    (if the gif won't show, here's a link https://cl.ly/1W0m000V0M2z)

    When flying really close to objects, they get drawn on top of the plane, which shouldn't be happening since the plane is as close to the camera as it can be before disappearing behind the near clipping plane (which I cannot fiddle with since this is a VR game and the VR headsets prevent Unity from making changes to things like the clipping planes, FOV etc.)

    How do I fix this? Any ideas?



    If it helps, find the shader and some screenshots here:

    Code (CSharp):
    1. Shader "Unlit/Unlit_Transparent_Cutout_Custom"
    2. {
    3.     Properties {
    4.     _Color ("Main Color", Color) = (1,1,1,1)
    5.     _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    6.     _TransitionTex("Transition Texture", 2D) = "white" {}
    7.     //_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
    8.     /*_Cutoff_b("Cutoff_b", Range(0.01, 0.08)) = 0.01
    9.     _Cutoff_r("Cutoff_r", Range(0, 1)) = 0
    10.     _Cutoff_g("Cutoff_g", Range(0, 1)) = 0*/
    11.  
    12.         [HideInInspector]
    13.     minTop ("MinTop", Float) = 0.6
    14.         [HideInInspector]
    15.     maxTop ("MaxTop", Float) = 0.9
    16.     _Intensity_Top ("_Intensity_Top", Range(0.6, 0.9)) = 0.9
    17.  
    18.         [HideInInspector]
    19.     minBot("MinBot", Float) = 0.09
    20.         [HideInInspector]
    21.     maxBot("MaxBot", Float) = 0.4
    22.     _Intensity_Bottom ("_Intensity_Bottom", Range(0.09, 0.4)) = 0.09
    23.  
    24.         [HideInInspector]
    25.     minRight("MinRight", Float) = 0.1
    26.         [HideInInspector]
    27.     maxRight("MaxRight", Float) = 0.53
    28.     _Intensity_Right ("_Intensity_Right", Range(0.1, 0.53)) = 0.53
    29.  
    30.         [HideInInspector]
    31.     minLeft("MinLeft", Float) = 0.47
    32.         [HideInInspector]
    33.     maxLeft("MaxLeft", Float) = 0.9
    34.     _Intensity_Left ("_Intensity_Left", Range(0.47, 0.9)) = 0.47
    35.  
    36.         [HideInInspector]
    37.     minTopRight("MinTopRight", Float) = 0.01
    38.         [HideInInspector]
    39.     maxTopRight("MaxTopRight", Float) = 0.08
    40.     _Intensity_TopRight("Intensity_TopRight", Range(0.01, 0.08)) = 0.01
    41.  
    42.         [HideInInspector]
    43.     minTopLeft("MinTopLeft", Float) = 0.01
    44.         [HideInInspector]
    45.     maxTopLeft("MaxTopLeft", Float) = 0.08
    46.     _Intensity_TopLeft("Intensity_TopLeft", Range(0.01, 0.08)) = 0.01
    47.  
    48.         [HideInInspector]
    49.     minBotRight("MinBotRight", Float) = 0.01
    50.         [HideInInspector]
    51.     maxBotRight("MaxBotRight", Float) = 0.08
    52.     _Intensity_BottomRight("Intensity_BottomRight", Range(0.01, 0.08)) = 0.01
    53.  
    54.         [HideInInspector]
    55.     minBotLeft("MinBotLeft", Float) = 0.01
    56.         [HideInInspector]
    57.     maxBotLeft("MaxBotLeft", Float) = 0.08
    58.     _Intensity_BottomLeft("Intensity_BottomLeft", Range(0.01, 0.08)) = 0.01
    59.  
    60.     [MaterialToggle] _Top("Top", int) = 0
    61.     [MaterialToggle] _Bottom("Bottom", int) = 0      
    62.     [MaterialToggle] _Right("Right", int) = 0
    63.     [MaterialToggle] _Left("Left", int) = 0      
    64.     [MaterialToggle] _TopRight("TopRight", int) = 0
    65.     [MaterialToggle] _TopLeft("TopLeft", int) = 0
    66.     [MaterialToggle] _BottomRight("BottomRight", int) = 0
    67.     [MaterialToggle] _BottomLeft("BottomLeft", int) = 0
    68. }
    69. SubShader {
    70.     Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    71.     LOD 200
    72. CGPROGRAM
    73. #pragma surface surf Lambert alpha
    74. sampler2D _MainTex;
    75. sampler2D _TransitionTex;
    76. fixed4 _Color;
    77. float _Cutoff;
    78. //float _Cutoff_b;
    79. //float _Cutoff_r;
    80. //float _Cutoff_g;
    81.  
    82. float _Intensity_Top;
    83. float _Intensity_Bottom;
    84. float _Intensity_Right;
    85. float _Intensity_Left;
    86. float _Intensity_TopRight;
    87. float _Intensity_TopLeft;
    88. float _Intensity_BottomRight;
    89. float _Intensity_BottomLeft;
    90.  
    91. int _Top;
    92. int _Bottom;
    93. int _Right;
    94. int _Left;
    95. int _TopRight;
    96. int _TopLeft;
    97. int _BottomRight;
    98. int _BottomLeft;
    99.  
    100. struct Input {
    101.      float2 uv_MainTex;
    102. };
    103. void surf (Input IN, inout SurfaceOutput o) {
    104.     fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    105.     fixed4 transit = tex2D(_TransitionTex, IN.uv_MainTex);
    106.     o.Albedo = c.rgb;
    107.    
    108.     if (_Top > 0)
    109.         if (IN.uv_MainTex.y > -pow(0.5 * IN.uv_MainTex.x - 0.25, 2) + _Intensity_Top)
    110.             o.Alpha = c.a;
    111.     if (_Bottom > 0)
    112.         if (IN.uv_MainTex.y < pow(0.5 * IN.uv_MainTex.x - 0.25, 2) + _Intensity_Bottom)
    113.             o.Alpha = c.a;
    114.     if (_Right > 0)
    115.         if (IN.uv_MainTex.x > -pow(0.5 * IN.uv_MainTex.y - 0.25, 2) + 0.5 + _Intensity_Right)
    116.             o.Alpha = c.a;
    117.     if (_Left > 0)
    118.         if (IN.uv_MainTex.x < pow(0.5 * IN.uv_MainTex.y -0.25, 2) -0.5 + _Intensity_Left)
    119.             o.Alpha = c.a;
    120.     if (_TopRight > 0)
    121.         if (IN.uv_MainTex.y > _Intensity_TopRight/(IN.uv_MainTex.x - 1) + 1) // 0.02 bis 0.08
    122.             o.Alpha = c.a;
    123.     if (_TopLeft > 0)
    124.         if (IN.uv_MainTex.y > _Intensity_TopLeft/(-IN.uv_MainTex.x) + 1) // 0.02 bis 0.08
    125.             o.Alpha = c.a;
    126.     if (_BottomRight > 0)
    127.         if (IN.uv_MainTex.y < _Intensity_BottomRight/(-IN.uv_MainTex.x + 1)) // 0.02 bis 0.08
    128.             o.Alpha = c.a;
    129.     if (_BottomLeft > 0)
    130.         if (IN.uv_MainTex.y < _Intensity_BottomLeft/(IN.uv_MainTex.x)) // 0.02 bis 0.08
    131.             o.Alpha = c.a;
    132.      
    133.     /*if (_BottomLeft > 0)
    134.         if (transit.g > _Cutoff_g && transit.b > _Cutoff_b)
    135.             return col = lerp(col, _Color, _Fade);
    136.         */  
    137. }
    138. ENDCG
    139. }
    140. Fallback "Transparent/VertexLit"
    141. }

     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    First step: Don't use a surface shader. Surface shaders should be used only if you need it to interact with Unity's lighting system. A vertex & fragment shader is much more appropriate for this.

    Second step: Change the queue to "Overlay" and set ZWrite False and ZTest Always. As you said you're getting very close to objects and they're clipping through the camera's near plane and the blinders plane. The solution is to make sure the blinders are drawn last and ignore the depth buffer entirely. You can do this with a surface shader if you want to just get it working.

    An alternative would be to set the queue to "Background" so it renders first and have your fragment shader write directly to the depth buffer exactly at the near plane. There's some potential performance benefits to that, though this can't be done with a surface shader at all.
     
    richardkettlewell likes this.