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

Depth of Field changes based on screen resolution

Discussion in 'Image Effects' started by User340, Sep 18, 2017.

  1. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    I noticed that the Depth Of Field effect gets less blurry the smaller I shrink my screen. Is there a setting for this to make it not do that?
     
  2. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    823
  3. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Thanks, I’ll give it a try.
     
  4. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    It's still an issue and that solution only works for some.
     
  5. cloverme

    cloverme

    Joined:
    Apr 6, 2018
    Posts:
    193
    If you only need distance blur, another workaround is to fake it by creating a plane (no collider and layer to ignore raycast) with a gaussian blur shader/material applied to the plane. Since it's a gameobject, it's not impacted by screen resolution changes. If your character moves/rotates, you can just rotate the object and/or move the object to keep the same distance from the player/camera without any hit to fps. You can get fancy and create multiple "panels" to stack the effect with different levels of blurs or colors for special effects. This wont work for tilt shift or other lens-type simulation effects.
     
    astracat111 likes this.
  6. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    ty clover, I'm going to just try this with a sphere around the active camera
     
  7. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    @cloverme Now if only I knew how to actually make a shader that I could stick on a sphere that works as a blurry lens... : (

    EDIT: Alright here's a shader that may or may not work for you, create a sphere that stays around the camera and basically grow or shrink it for distance in a custom cs script:

    Save this first as Blur.cginc:
    Code (CSharp):
    1. // https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson5
    2. float4 gaussianBlur(float2 dir, float4 grabPos, float res, sampler2D tex, float radius)
    3. {
    4.     //this will be our RGBA sum
    5.     float4 sum = float4(0, 0, 0, 0);
    6.  
    7.     //the amount to blur, i.e. how far off center to sample from
    8.     //1.0 -> blur by one pixel
    9.     //2.0 -> blur by two pixels, etc.
    10.     float blur = radius / res;
    11.  
    12.     //the direction of our blur
    13.     //(1.0, 0.0) -> x-axis blur
    14.     //(0.0, 1.0) -> y-axis blur
    15.     float hstep = dir.x;
    16.     float vstep = dir.y;
    17.  
    18.     //apply blurring, using a 9-tap filter with predefined gaussian weights
    19.  
    20.     sum += tex2Dproj(tex, float4(grabPos.x - 4*blur*hstep, grabPos.y - 4.0*blur*vstep, grabPos.zw)) * 0.0162162162;
    21.     sum += tex2Dproj(tex, float4(grabPos.x - 3.0*blur*hstep, grabPos.y - 3.0*blur*vstep, grabPos.zw)) * 0.0540540541;
    22.     sum += tex2Dproj(tex, float4(grabPos.x - 2.0*blur*hstep, grabPos.y - 2.0*blur*vstep, grabPos.zw)) * 0.1216216216;
    23.     sum += tex2Dproj(tex, float4(grabPos.x - 1.0*blur*hstep, grabPos.y - 1.0*blur*vstep, grabPos.zw)) * 0.1945945946;
    24.  
    25.     sum += tex2Dproj(tex, float4(grabPos.x, grabPos.y, grabPos.zw)) * 0.2270270270;
    26.  
    27.     sum += tex2Dproj(tex, float4(grabPos.x + 1.0*blur*hstep, grabPos.y + 1.0*blur*vstep, grabPos.zw)) * 0.1945945946;
    28.     sum += tex2Dproj(tex, float4(grabPos.x + 2.0*blur*hstep, grabPos.y + 2.0*blur*vstep, grabPos.zw)) * 0.1216216216;
    29.     sum += tex2Dproj(tex, float4(grabPos.x + 3.0*blur*hstep, grabPos.y + 3.0*blur*vstep, grabPos.zw)) * 0.0540540541;
    30.     sum += tex2Dproj(tex, float4(grabPos.x + 4.0*blur*hstep, grabPos.y + 4.0*blur*vstep, grabPos.zw)) * 0.0162162162;
    31.  
    32.     return float4(sum.rgb, 1.0);
    33. }
    Save this as Blur.shader:
    Code (CSharp):
    1. Shader "Custom/Blur"
    2. {
    3.     Properties
    4.     {
    5.         _ClearColor ("Clear Color", Color) = (1,1,1,1)
    6.         _FogColor ("Fog Color", Color) = (1,1,1,1)
    7.         _BlurRadius ("Blur Radius", float) = 3
    8.         _MaxAge("Max Age", float) = 3
    9.     }
    10.  
    11.     SubShader
    12.     {
    13.         Tags
    14.         {
    15.             "Queue" = "Transparent"
    16.         }
    17.  
    18.        Cull Front
    19.  
    20.         // Grab the screen behind the object into _BGTex
    21.         GrabPass
    22.         {
    23.             "_BGTex"
    24.         }
    25.  
    26.         Pass
    27.         {
    28.             CGPROGRAM
    29.             #pragma vertex vert
    30.             #pragma fragment frag
    31.             #include "UnityCG.cginc"
    32.             #include "Blur.cginc"
    33.             /**/
    34.             // Properties
    35.             // set in material
    36.             uniform float4 _ClearColor;
    37.             uniform float4 _FogColor;
    38.             uniform float _BlurRadius;
    39.             uniform float _MaxAge;
    40.             // grab pass
    41.             uniform sampler2D _BGTex;
    42.             uniform float4 _BGTex_TexelSize;
    43.             // set by script
    44.             uniform sampler2D _MouseMap;
    45.             uniform float _MaxSeconds;
    46.  
    47.             struct vertexInput
    48.             {
    49.                 float4 vertex : POSITION;
    50.                 float3 texCoord : TEXCOORD0;
    51.             };
    52.  
    53.             struct vertexOutput
    54.             {
    55.                 float4 pos : SV_POSITION;
    56.                 float3 texCoord : TEXCOORD0;
    57.                 float4 grabPos : TEXCOORD1;
    58.             };
    59.  
    60.             vertexOutput vert(vertexInput input)
    61.             {
    62.                 vertexOutput output;
    63.                 output.pos = UnityObjectToClipPos(input.vertex);
    64.                 output.grabPos = ComputeGrabScreenPos(output.pos);
    65.                 output.texCoord = input.texCoord;
    66.                 return output;
    67.             }
    68.  
    69.             float4 frag(vertexOutput input) : COLOR
    70.             {
    71.                 float4 bg = tex2Dproj(_BGTex, input.grabPos);
    72.                 // younger = redder
    73.                 float timeDrawn = tex2D(_MouseMap, input.texCoord.xy).r;
    74.                 float age = clamp(_Time.y - timeDrawn, 0.0001, _Time.y);
    75.                 float percentMaxAge = saturate(age / _MaxAge);
    76.                 //return float4(percentMaxAge, 0, 0, 1);
    77.            
    78.                 // older = higher percentMaxAge = more blur
    79.                 float blurRadius = _BlurRadius * percentMaxAge;
    80.                 float4 color = (1-percentMaxAge)*_ClearColor + percentMaxAge*_FogColor;
    81.  
    82.                 float4 blurX = gaussianBlur(float2(1,0), input.grabPos, _BGTex_TexelSize.z, _BGTex, blurRadius);
    83.                 float4 blurY = gaussianBlur(float2(0,1), input.grabPos, _BGTex_TexelSize.w, _BGTex, blurRadius);
    84.                 return (blurX + blurY) * color;
    85.  
    86.                 // TEST
    87.                 //float blurRadius = floor(_BlurRadius * mouseSample.r);
    88.                 //return mouseSample; // test mouse map
    89.                 //float noSmudge = mouseSample.r != 0; // test erasing blur
    90.             }
    91.  
    92.             ENDCG
    93.         }
    94.     }
    95. }
    I added in the 'Cull Front' part of this shader so that your sphere shades the inside.

    Create a material and set fog color to RGBA 0.5, 0.5, 0.5 and alpha can be anything. It should create a blur.
     
    Last edited: Jan 2, 2020
  8. lczyzycki7lvls

    lczyzycki7lvls

    Joined:
    Sep 10, 2018
    Posts:
    8
    To anyone that ends up here looking for solution and gets frustrated over GitHub link not working, like me:

    We've ended up compensating for this issue by scaling the offset of texture sampling in the Bokeh pass by the ratio of current to the reference resolution.

    So

    float2 duv = float2(disp.x * _RcpAspect, disp.y);
    became

    float2 duv = float2(disp.x * _RcpAspect, disp.y) * _ResolutionCompensation;


    This isn't perfect. You might get slight artefacts noticeable in higher resolutions, but for us, it did the job of making DoF look close enough in 4K, 1080p and 720p.
     
    Liderangel likes this.
  9. Sabrino

    Sabrino

    Joined:
    Aug 8, 2015
    Posts:
    35
    Thank you @lczyzycki7lvls for posting a simple solution that works.
     
  10. Magistrix93

    Magistrix93

    Joined:
    Mar 17, 2017
    Posts:
    4
    Hi, how did you calculated the resolution compensation?
     
  11. lczyzycki7lvls

    lczyzycki7lvls

    Joined:
    Sep 10, 2018
    Posts:
    8
    It's the height value in the current rendering resolution divided by the height of the resolution for which the effect was initially set and tweaked. For us it was 1920x1080, so y / 1080. You can set it where all the other DoF parameters are set, in Render function in DepthOfField.cs:
    sheet.properties.SetFloat("_ResolutionCompensation", context.height / 1080f);
     
  12. Renardjojo

    Renardjojo

    Joined:
    May 15, 2019
    Posts:
    11
    The algorithm change, so you can handle it in BokehDepthOfField.shader
    change line from
    Code (CSharp):
    1. half4 samp = SAMPLE_TEXTURE2D_X(_SourceTex, sampler_LinearClamp, uv + disp.wy);
    to
    Code (CSharp):
    1. half4 samp = SAMPLE_TEXTURE2D_X(_SourceTex, sampler_LinearClamp, uv + disp.wy * _ResolutionCompensation);
    This can also be compute in PrepareBokehKernel function of DepthOfFieldPass.cs