Search Unity

Resolved skybox that displays a flat image

Discussion in 'General Graphics' started by laurentlavigne, Oct 16, 2020.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,331
    The goal is to display a RenderTexture as flat instead of the sphere skybox.
    Currently I'm getting a sphere projection of that rendertexture if I use a flat shader as skybox material so I was wondering if it's even possible to have a skybox render as a flat plane, and if it's even more efficient than having a second camera look at a flat plane with the render texture on it.
    upload_2020-10-15_17-33-29.png
     

    Attached Files:

  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Unity always uses a sphere mesh for the skybox, unless it’s the 6 sided skybox which probably requires a skybox with 6 passes for it to know to not use the sphere mesh.

    You could certainly write a shader to use the screen coordinates to display a flat texture on the skybox sphere regardless of where you’re looking, but it’d probably be easier to not use a skybox at all and use a custom shader with a queue of 2499 on a quad attached to the camera. You can also ensure it always renders at the far plane with this bit of code:
    https://aras-p.info/blog/2019/02/01/Infinite-sky-shader-for-Unity/
     
    BrandyStarbrite likes this.
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,331
    Thanks!
    Here is the script if someone needs.

    Code (CSharp):
    1. Shader "flat infinity"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags
    10.         {
    11.             "RenderType"="Opaque"
    12.         }
    13.  
    14.         Pass
    15.         {
    16.             CGPROGRAM
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.             #include "UnityCG.cginc"
    20.  
    21.             struct appdata
    22.             {
    23.                 float4 vertex : POSITION;
    24.                 float2 uv : TEXCOORD0;
    25.             };
    26.  
    27.             struct v2f
    28.             {
    29.                 float2 uv : TEXCOORD0;
    30.                 UNITY_FOG_COORDS(1)
    31.                 float4 vertex : SV_POSITION;
    32.             };
    33.  
    34.             sampler2D _MainTex;
    35.             float4 _MainTex_ST;
    36.  
    37.             v2f vert(appdata v)
    38.             {
    39.                 v2f o;
    40.                 o.vertex = UnityObjectToClipPos(v.vertex);
    41.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    42.                 o.vertex.z = 1.0e-9f;
    43.                 return o;
    44.             }
    45.  
    46.             fixed4 frag(v2f i) : SV_Target
    47.             {
    48.                 // sample the texture
    49.                 fixed4 col = tex2D(_MainTex, i.uv);
    50.                 return col;
    51.             }
    52.             ENDCG
    53.         }
    54.     }
    55. }
     
  4. Sunwer91

    Sunwer91

    Joined:
    Jul 13, 2016
    Posts:
    5
    THANKS! I was searching this type of solution for hours!
     
    BrandyStarbrite likes this.
  5. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    Here is a skybox shader for that, without a need for a quad.
    It will also maintain correct aspect of the image, ensuring it won't stretch
    https://gist.github.com/aras-p/3d8218ef5d96d5984019?permalink_comment_id=4780287#gistcomment-4780287

    Code (CSharp):
    1. //Helps to display a flat 2D image that always remains on screen,
    2. //regardless of where the camera is looking.
    3. //from https://gist.github.com/aras-p/3d8218ef5d96d5984019
    4. // Maintains aspect of the image (outter-envelops the viewport)
    5.  
    6. Shader "Skybox/Background Texture Aspect (Advanced)"
    7. {
    8.     Properties
    9.     {
    10.         _MainTex ("Texture", 2D) = "white" {}
    11.     }
    12.     SubShader
    13.     {
    14.         Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    15.         Cull Off ZWrite Off
    16.  
    17.         Pass
    18.         {
    19.             CGPROGRAM
    20.             #pragma vertex vert
    21.             #pragma fragment frag
    22.             #include "UnityCG.cginc"
    23.  
    24.             void vert (float4 pos : POSITION, out float4 outUV : TEXCOORD0, out float4 outPos : SV_POSITION)
    25.             {    
    26.                 outPos = UnityObjectToClipPos(pos);
    27.                 outUV = ComputeScreenPos(outPos);
    28.             }
    29.  
    30.             sampler2D _MainTex;
    31.             float4 _MainTex_TexelSize;
    32.  
    33.             fixed4 frag (float4 uv : TEXCOORD0) : SV_Target{
    34.                 // Compute aspect ratio of the texture and the screen
    35.                 float textureAspect = _MainTex_TexelSize.z / _MainTex_TexelSize.w; // Use z and w for texture width and height
    36.                 float screenAspect = _ScreenParams.x / _ScreenParams.y;
    37.  
    38.                 uv /= uv.w;
    39.  
    40.                 // Calculate scale and offset for UVs to keep the image centered
    41.                 float scale, offset;
    42.  
    43.                 if (screenAspect < textureAspect){
    44.                     // Screen is narrower than texture - adjust UV.x
    45.                     scale = screenAspect / textureAspect;
    46.                     offset = (1.0f - scale) * 0.5f;
    47.                     uv.x = uv.x * scale + offset;
    48.                 } else {// Screen is less tall than texture - adjust UV.y
    49.                     scale = textureAspect / screenAspect;
    50.                     offset = (1.0f - scale) * 0.5f;
    51.                     uv.y = uv.y * scale + offset;
    52.                 }
    53.                 fixed4 col = tex2D(_MainTex, uv);
    54.                 return col;
    55.             }
    56.  
    57.             ENDCG
    58.         }
    59.     }
    60. }

    -------------------------------------------------------------

    Also, here is a more advanced variant.
    Needed if you want to toggle between envelopeTheViewport and fitInsideViewport.
    Or if you want to ensure there is "empty" space when squeezing the image into viewport.
    Notice, to make this variant work you need to feed it the size of your image. For example:

    Code (CSharp):
    1.   void Update(){
    2.         if (_envelopeTheViewport){ _skyboxMaterial.EnableKeyword("ENVELOPE_THE_VIEWPORT"); }
    3.         else { _skyboxMaterial.DisableKeyword("ENVELOPE_THE_VIEWPORT"); }
    4.         if (_colorNothing) { _skyboxMaterial.EnableKeyword("NOTHING_IF_OUTSIDE_UV"); }
    5.         else { _skyboxMaterial.DisableKeyword("NOTHING_IF_OUTSIDE_UV"); }
    6.         Vector2 wh = new Vector2(1024,512);//<---you can dynamically change this.
    7.         _skyboxMaterial.SetVector("_Inner_WidthHeight", new Vector4(wh.x, wh.y, 0,0));
    8.     }
    Code (CSharp):
    1. //Helps to display a flat 2D image that always remains on screen,
    2. //regardless of where the camera is looking.
    3. //from https://gist.github.com/aras-p/3d8218ef5d96d5984019
    4.  
    5. Shader "Skybox/Background Texture"
    6. {
    7.     Properties
    8.     {
    9.         _MainTex ("Texture", 2D) = "black" {}//Background(skybox)
    10.  
    11.         //size of whatever is to be squeezed into viewport.
    12.         //only used if ENVELOPE_THE_VIEWPORT is off
    13.         _Inner_WidthHeight("Inner WidthHeight", Vector) = (512,512,0,0)
    14.  
    15.         //only used if keyword NOTHING_IF_OUTSIDE_UV is ON
    16.         //typically, you also want to have ENVELOPE_THE_VIEWPORT as OFF
    17.         _ColorNothing("Color of 'Nothing'", Color) = (0.1,0.1,0.1,1)
    18.     }
    19.     SubShader
    20.     {
    21.         Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    22.         Cull Off ZWrite Off
    23.  
    24.         Pass{
    25.             CGPROGRAM
    26.             #pragma vertex vert
    27.             #pragma fragment frag
    28.             #pragma multi_compile _ ENVELOPE_THE_VIEWPORT
    29.             #pragma multi_compile _ NOTHING_IF_OUTSIDE_UV
    30.             #include "UnityCG.cginc"
    31.  
    32.             sampler2D _MainTex;
    33.             float4 _MainTex_TexelSize;
    34.  
    35.             float4 _Inner_WidthHeight;
    36.  
    37.             float4 _ColorNothing;
    38.  
    39.  
    40.             void vert (float4 pos : POSITION, out float4 outUV : TEXCOORD0, out float4 outPos : SV_POSITION){    
    41.                 outPos = UnityObjectToClipPos(pos);
    42.                 outUV = ComputeScreenPos(outPos);
    43.             }
    44.  
    45.  
    46.             #include "ShaderEffects.cginc"
    47.  
    48.  
    49.             fixed4 frag (float4 uv : TEXCOORD0) : SV_Target{
    50.                 uv /= uv.w;
    51.  
    52.                 //'width/height' of the image squeezed inside (into) the viewport:
    53.                 float inner_aspect = _Inner_WidthHeight.x / _Inner_WidthHeight.y;
    54.  
    55.                 float screenRealAspect = _ScreenParams.x / _ScreenParams.y;
    56.  
    57.                 float ratios = inner_aspect/screenRealAspect;
    58.  
    59.                 #ifdef NOTHING_IF_OUTSIDE_UV
    60.                     float2 uv_fromCenter = uv-0.5f;
    61.                     float2 uv_adjusted   = uv_fromCenter;
    62.                         if(ratios>1){
    63.                             uv_adjusted.y*= ratios;
    64.                         }else{
    65.                             uv_adjusted.x/= ratios;
    66.                         }
    67.                     float2 isInside01    = 1-step(0.5f, abs(uv_adjusted)); //[0,1] --> [-0.5, 0.5]  and then checking if absolute val is more than 0.5
    68.                     float isFullyInside  = isInside01.x*isInside01.y;
    69.                 #endif
    70.  
    71.                 // Compute aspect ratio of the texture and the screen
    72.                 float textureAspect = _MainTex_TexelSize.z / _MainTex_TexelSize.w; // Use z and w for texture width and height
    73.  
    74.                 // Calculate scale and offset for UVs to keep the image centered
    75.                 float scale, offset;
    76.  
    77.                 #ifdef ENVELOPE_THE_VIEWPORT
    78.                     bool is_adjust_horizontal =  screenRealAspect < textureAspect;
    79.                 #else//fully fit inside the viewport:
    80.                     bool is_adjust_horizontal =  screenRealAspect > textureAspect;
    81.                 #endif
    82.  
    83.                 if (is_adjust_horizontal){
    84.                     // Screen is narrower than texture - adjust UV.x
    85.                     scale = screenRealAspect / textureAspect;
    86.                     offset = (1.0f - scale) * 0.5f;
    87.                     uv.x = uv.x * scale + offset;
    88.                 } else {// Screen is less tall than texture - adjust UV.y
    89.                     scale = textureAspect / screenRealAspect;
    90.                     offset = (1.0f - scale) * 0.5f;
    91.                     uv.y = uv.y * scale + offset;
    92.                 }
    93.                 fixed4 col = tex2D(_MainTex, uv);
    94.      
    95.                 #ifdef NOTHING_IF_OUTSIDE_UV
    96.                     col =  lerp(col, _ColorNothing, 1-isFullyInside);
    97.                 #endif
    98.  
    99.                 return col;
    100.             }
    101.  
    102.             ENDCG
    103.         }
    104.     }
    105. }
     
    Last edited: Jan 9, 2024
    laurentlavigne likes this.