Search Unity

Render far objects without increasing the clipping plane

Discussion in 'General Graphics' started by MikeyJY, Jul 10, 2020.

  1. MikeyJY

    MikeyJY

    Joined:
    Mar 2, 2018
    Posts:
    530
    I have an cloud sphere that's very far from the Player. I want to make the camera to render the clouds without increasing the near clipping planes to improve performance and to avoid the player to see to much in the horizon. Just 1000-2000 units. I want to tell camera to render the clouds that are 7000 units far, but to avoid rendering other objects that are more than 2000 far.

    P.S: I would like to not use the 2 cameras method(1 camera with 7000 clipping plane that render only the clouds layer and 1 camera with 2000 clipping plane that render everything else)
     
  2. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,569
    You could try using a custom procedural skybox shader.

    How to write it depends on which version and rendering pipeline you're using.

    Additionally you could attach a SMALL 1 unit sphere directly to the camera, and render it with depth write disabled, in Background queue. It will look as if it was infinitely far away. Just because an object looks like it is far away, that doesn't mean it is actually there.

    Once again, this is for "built-in renderer"
    https://docs.unity3d.com/Manual/SL-SubShaderTags.html

    Basically, your clouds do not need to be up to scale, it can be just smoke and mirrors around the camera.
     
    Kiwasi and angrypenguin like this.
  3. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
  4. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    From the docs it sounds like this can be used to reduce culling distance for layers, but not to increase it.

    From a pure math perspective you can get the desired result by setting the far clip to just larger than the sky sphere's distance and reducing the layer clip distances for everything else. However, when you take the depth buffer into account that could give you issues, as you're reducing the depth precision available to the majority of the stuff you're rendering. That may or may not matter, depending on what you're doing.

    The first solution that comes to my mind, for conceptual simplicity, is to simply have two cameras with matching settings and transforms, except for the clear flags, clip planes and render order. Render your far camera first with clear flags set to "Skybox", then your normal camera with your clear flags set to "depth only". It does seem overkill to have a whole extra camera to render just one thing, though.

    The second solution that comes to my mind has already been explained by @neginfinity. Tweak the shader and render order within a single camera so that your skybox is rendered first without z-write, at any scale that makes it visible. Then render the rest of your scene, which will ignore it because it's not in the z-buffer. I'm no rendering expert but this strikes me as far more efficient.

    If you look into how skyboxes are generally rendered in Unity there might be options there. Replace the skybox material with one that gives the behaviour you want, then assign that to your cameras.
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Yes, but you just approach it from the other direction. Set your camera to 7000, then use layerCullDistances to reduce all layers to 2000 except for the cloud layer.
     
  6. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    In the very next paragraph... ;)
     
  7. MikeyJY

    MikeyJY

    Joined:
    Mar 2, 2018
    Posts:
    530
    Thank you a lot for your answers
     
    angrypenguin likes this.
  8. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    I was just thinking about this problem.
    I'm using HDRP.
    I have got some mountains very far away (3d models), far beyond my camera clipping plane that I would not like to change.

    I'm bumping since somebody might know the "official" way to do it.
     
  9. Depends on what you're actually doing. You can put them on a cube map and render them with the sky or you can make impostors out of them and render them at the far clipping plane. For example. You can't render 3D objects beyond your far clipping plane, obviously.
     
  10. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    I was trying to cheat reality.
     
    NotaNaN likes this.
  11. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,569
    Since HDRP does not support camera stacking, use render target and small scene. You can also prerender this to a skybox.

    Long time ago I recorded this:


    This is life-sized earth and moon. It is done by using camera stacking and progressively smaller scenes set on different layers. So, you'd have a visual layers with objects that are nearby, a visual layer with surroundings scaled down by factor of, say 1000, one more layer with surroundings scaled by factor of 1000000 and so on. This can be stored in the same scene, although managing it all in the same spot is messy.

    The cameras woudl first render the largest scene, then smaller scene on top of it, with the "nearby objects" scene being rendered last.
     
    Silentor666 and koirat like this.
  12. usernameHed

    usernameHed

    Joined:
    Apr 5, 2016
    Posts:
    93

    I've created a tiny tool, that change every Layer with a default Far value (the maximum is the default far of the camera)
    Planning to add it to the asset store, for anyone wanting it for free before I release it, ping me ;)
     
  13. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Just be aware this scenario will give you precision issues if there is a large difference between your depths. Might be worth testing some extreme scenarios so that you can provide accurate details of the precision limits to your buyers.
     
  14. JoeMan500

    JoeMan500

    Joined:
    Jul 26, 2022
    Posts:
    4
    I have had this problem too with a sky dome and custom shader to make clouds.
    I used this code
    o.pos.z = o.pos.w - 1.0e-6f;
    , which rendered the clouds in front of all the other geometry in the scene, so then I used
    "Queue" = "Geometry-1"
    to render before any scene objects.
    Note this is extracted code from a custom sky / cloud shader, so I haven't tested it.

    Code (CSharp):
    1. Shader "Custom/NoSkyClippingShader" {
    2.     Properties {
    3.             _MainTex ("Texture", 2D) = "white" {}
    4.         // your properties here
    5.     }
    6.  
    7.     SubShader {
    8.         // we want to render the clouds always before the geometry
    9.         Tags { "Queue" = "Geometry-1" "RenderType"="Transparent" }
    10.         cull front // we only want to render the inside of the dome
    11.         zwrite off
    12.         Blend SrcAlpha OneMinusSrcAlpha
    13.  
    14.         Pass {
    15.  
    16.             CGPROGRAM
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.  
    20.             #include "UnityCG.cginc"
    21.  
    22.             struct v2f {
    23.                 fixed4 pos : SV_POSITION;
    24.                 fixed4 uv0 : TEXCOORD0;
    25.                 fixed4 uv1 : TEXCOORD1;
    26.             };
    27.  
    28.             v2f vert (appdata v) {
    29.                 v2f o;
    30.                 o.pos = UnityObjectToClipPos(v.vertex);
    31.                 // set the z pos of every vert of the dome
    32.                 o.pos.z = o.pos.w - 1.0e-6f;
    33.                                // rest of your shader code here
    34.                         }
    35.  
    36.             fixed4 frag (v2f i) : SV_Target { return tex2D(_MainTex, i.uv); }
    37.             ENDCG
    38.  
     
  15. Jon2mil

    Jon2mil

    Joined:
    May 10, 2017
    Posts:
    5
    Can I get it?
     
  16. usernameHed

    usernameHed

    Joined:
    Apr 5, 2016
    Posts:
    93
    Hello! well I haven't had the time to implement it yet, but the code to make it work is really really simple:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. namespace UnityEssentials.CameraPerLayerFarDist
    6. {
    7.     /// <summary>
    8.     ///
    9.     /// </summary>
    10.     [ExecuteInEditMode]
    11.     public class PerLayerFarDistance : MonoBehaviour
    12.     {
    13.         [SerializeField] private Camera _camera; public Camera Camera { get { return (_camera); } }
    14.         [SerializeField] private float[] _distances = new float[32];
    15.         [SerializeField] private bool _useRoundFar = false;
    16.  
    17.         private void OnEnable()
    18.         {
    19.             SetupCullings();
    20.         }
    21.  
    22.         private void SetupCullings()
    23.         {
    24.             if (_camera == null)
    25.             {
    26.                 return;
    27.             }
    28.             _camera.layerCullDistances = _distances;
    29.             _camera.layerCullSpherical = _useRoundFar;
    30.         }
    31.        
    32.         private void OnValidate()
    33.         {
    34.             SetupCullings();
    35.         }
    36.     }
    37. }
    The editor stuff is secondary
     
  17. Kolyn090

    Kolyn090

    Joined:
    May 4, 2022
    Posts:
    1
    I made this version with custom editor based on usernameHed's answer.
    Notice that in order for this to behave correctly, you need to set your camera's far clipping planes to something like 1000. Here, I have set all layers' clipping distance to 80. If you wish to always render far objects like sky domes, set their distances to 1000 or some higher values. Edit: Added "#if UNITY_EDITOR" and "#endif".

    Code (CSharp):
    1. using System;
    2. using System.Linq;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6.  
    7. namespace UnityEssentials.CameraPerLayerFarDist
    8. {
    9.     /// <summary>
    10.     /// * Limits clipping plane distance for each layer
    11.     /// </summary>
    12.     [ExecuteInEditMode]
    13.     public class PerLayerFarDistance : MonoBehaviour
    14.     {
    15.         [SerializeField]
    16.         private Camera _camera;
    17.  
    18.         [SerializeField]
    19.         private LayerWithDistance[] _settings = new LayerWithDistance[32];
    20.  
    21.         [Serializable]
    22.         public struct LayerWithDistance
    23.         {
    24.             public LayerMask layer;
    25.  
    26.             public float distance;
    27.         }
    28.  
    29.         public void OnValidate()
    30.         {
    31.             if (_settings.Length != 32)
    32.                 GetLayers();
    33.         }
    34.  
    35.         private void Awake()
    36.         {
    37.             SetCameraCullingMode();
    38.         }
    39.  
    40.         private void SetCameraCullingMode()
    41.         {
    42.             _camera.layerCullSpherical = false;
    43.             _camera.layerCullDistances =
    44.                 _settings.ToList().Select(x => x.distance).ToArray();;
    45.         }
    46.  
    47.         [ContextMenu("GetLayers")]
    48.         public void GetLayers()
    49.         {
    50.             for (int i = 0; i <= 31; i++)
    51.             {
    52.                 _settings[i].layer = 1 << i;
    53.                 // * Set far clipping plane distance for layer to 80
    54.                 _settings[i].distance = 80f;
    55.             }
    56.         }
    57.  
    58. #if UNITY_EDITOR
    59.         [CustomPropertyDrawer(typeof(LayerWithDistance))]
    60.         public class LayerWithDistanceDrawerUIE: PropertyDrawer
    61.         {
    62.             public override void OnGUI(Rect position,
    63.                                         SerializedProperty property,
    64.                                         GUIContent label)
    65.             {
    66.                 EditorGUI.BeginProperty(position, label, property);
    67.  
    68.                 position = EditorGUI.PrefixLabel(position,
    69.                                         GUIUtility
    70.                                             .GetControlID(FocusType.Passive),
    71.                                         label);
    72.  
    73.                 var indent = EditorGUI.indentLevel;
    74.                 EditorGUI.indentLevel = 0;
    75.  
    76.                 Rect layerRect = new(position.x,
    77.                                             position.y,
    78.                                             120,
    79.                                             position.height);
    80.                 Rect distanceRect = new(position.x + 125,
    81.                                         position.y, 30,
    82.                                         position.height);
    83.      
    84.                 EditorGUI.PropertyField(layerRect,
    85.                                         property
    86.                                             .FindPropertyRelative("layer"),
    87.                                         GUIContent.none);
    88.                 EditorGUI.PropertyField(distanceRect,
    89.                                         property
    90.                                             .FindPropertyRelative("distance"),
    91.                                         GUIContent.none);
    92.          
    93.                 EditorGUI.indentLevel = indent;
    94.  
    95.                 EditorGUI.EndProperty();
    96.             }
    97.         }
    98. #endif
    99.     }
    100. }
    101.  
     
    Last edited: Jan 17, 2024