Skip to content

Instantly share code, notes, and snippets.

@aras-p
Last active January 9, 2024 00:28
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aras-p/3d8218ef5d96d5984019 to your computer and use it in GitHub Desktop.
Save aras-p/3d8218ef5d96d5984019 to your computer and use it in GitHub Desktop.
Unity flat skybox picture shader
// Skybox shader that just draws flat texture in the background
Shader "Skybox/Background Texture"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
Cull Off ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
void vert (float4 pos : POSITION, out float4 outUV : TEXCOORD0, out float4 outPos : SV_POSITION)
{
outPos = mul(UNITY_MATRIX_MVP, pos);
outUV = ComputeScreenPos(outPos);
}
sampler2D _MainTex;
fixed4 frag (float4 uv : TEXCOORD0) : SV_Target
{
fixed4 col = tex2Dproj(_MainTex, uv);
return col;
}
ENDCG
}
}
}
@IgorAherne
Copy link

IgorAherne commented Dec 3, 2023

Thank you, here is variant that maintains aspect of an image:

//Helps to display a flat 2D image that always remains on screen, 
//regardless of where the camera is looking.
//from https://gist.github.com/aras-p/3d8218ef5d96d5984019
// Maintains aspect of the image (outter-envelops the viewport)

Shader "Skybox/Background Texture Aspect"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
		Cull Off ZWrite Off

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			void vert (float4 pos : POSITION, out float4 outUV : TEXCOORD0, out float4 outPos : SV_POSITION)
			{				
				outPos = UnityObjectToClipPos(pos);
				outUV = ComputeScreenPos(outPos);
			}

			sampler2D _MainTex;
			float4 _MainTex_TexelSize;
			
			fixed4 frag (float4 uv : TEXCOORD0) : SV_Target{
				// Compute aspect ratio of the texture and the screen
				float textureAspect = _MainTex_TexelSize.z / _MainTex_TexelSize.w; // Use z and w for texture width and height
				float screenAspect = _ScreenParams.x / _ScreenParams.y;

				uv /= uv.w;

				// Calculate scale and offset for UVs to keep the image centered
				float scale, offset;

				if (screenAspect < textureAspect){
					// Screen is narrower than texture - adjust UV.x
					scale = screenAspect / textureAspect;
					offset = (1.0f - scale) * 0.5f;
					uv.x = uv.x * scale + offset;
				} else {// Screen is less tall than texture - adjust UV.y
					scale = textureAspect / screenAspect;
					offset = (1.0f - scale) * 0.5f;
					uv.y = uv.y * scale + offset;
				} 
				fixed4 col = tex2D(_MainTex, uv);
				return col;
			}

			ENDCG
		}
	}
}

@IgorAherne
Copy link

IgorAherne commented Jan 8, 2024

Here is a more advanced variant.
Needed if you want to toggle between envelope/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:

  void Update(){
        if (_envelopeTheViewport){ _skyboxMaterial.EnableKeyword("ENVELOPE_THE_VIEWPORT"); }
        else { _skyboxMaterial.DisableKeyword("ENVELOPE_THE_VIEWPORT"); }

        if (_colorNothing) { _skyboxMaterial.EnableKeyword("NOTHING_IF_OUTSIDE_UV"); }
        else { _skyboxMaterial.DisableKeyword("NOTHING_IF_OUTSIDE_UV"); }

        Vector2 wh = new Vector2(1024,512);//<---you can dynamically change this.
        _skyboxMaterial.SetVector("_Inner_WidthHeight", new Vector4(wh.x, wh.y, 0,0));
    }
//Helps to display a flat 2D image that always remains on screen, 
//regardless of where the camera is looking.
//from https://gist.github.com/aras-p/3d8218ef5d96d5984019

Shader "Skybox/Background Texture (Advanced)"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "black" {}//Background(skybox)

		//size of whatever is to be squeezed into viewport.
		//only used if ENVELOPE_THE_VIEWPORT is off
		_Inner_WidthHeight("Inner WidthHeight", Vector) = (512,512,0,0)

		//only used if keyword NOTHING_IF_OUTSIDE_UV is ON
                //typically, you also want to have ENVELOPE_THE_VIEWPORT as OFF
		_ColorNothing("Color of 'Nothing'", Color) = (0.1,0.1,0.1,1)
	}
	SubShader
	{
		Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
		Cull Off ZWrite Off

		Pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile _ ENVELOPE_THE_VIEWPORT  NOTHING_IF_OUTSIDE_UV
			#include "UnityCG.cginc"

			sampler2D _MainTex;
			float4 _MainTex_TexelSize;

			float4 _Inner_WidthHeight;

			float4 _ColorNothing;


			void vert (float4 pos : POSITION, out float4 outUV : TEXCOORD0, out float4 outPos : SV_POSITION){				
				outPos = UnityObjectToClipPos(pos);
				outUV = ComputeScreenPos(outPos);
			}


            #include "ShaderEffects.cginc"

			 
			fixed4 frag (float4 uv : TEXCOORD0) : SV_Target{
				uv /= uv.w;

				//'width/height' of the image squeezed inside (into) the viewport:
				float inner_aspect = _Inner_WidthHeight.x / _Inner_WidthHeight.y;

				float screenRealAspect = _ScreenParams.x / _ScreenParams.y;

				float ratios = inner_aspect/screenRealAspect;

				#ifdef NOTHING_IF_OUTSIDE_UV
					float2 uv_fromCenter = uv-0.5f;
					float2 uv_adjusted   = uv_fromCenter;
						if(ratios>1){
							uv_adjusted.y*= ratios;
						}else{
							uv_adjusted.x/= ratios;
						}
					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
					float isFullyInside  = isInside01.x*isInside01.y;
				#endif

				// Compute aspect ratio of the texture and the screen
				float textureAspect = _MainTex_TexelSize.z / _MainTex_TexelSize.w; // Use z and w for texture width and height

				// Calculate scale and offset for UVs to keep the image centered
				float scale, offset;

				#ifdef ENVELOPE_THE_VIEWPORT
					bool is_adjust_horizontal =  screenRealAspect < textureAspect;
				#else//fully fit inside the viewport:
					bool is_adjust_horizontal =  screenRealAspect > textureAspect;
				#endif

				if (is_adjust_horizontal){
					// Screen is narrower than texture - adjust UV.x
					scale = screenRealAspect / textureAspect;
					offset = (1.0f - scale) * 0.5f;
					uv.x = uv.x * scale + offset;
				} else {// Screen is less tall than texture - adjust UV.y
					scale = textureAspect / screenRealAspect;
					offset = (1.0f - scale) * 0.5f;
					uv.y = uv.y * scale + offset;
				} 
				fixed4 col = tex2D(_MainTex, uv);
				
				#ifdef NOTHING_IF_OUTSIDE_UV
				    col =  lerp(col, _ColorNothing, 1-isFullyInside);
				#endif

				return col;
			}

			ENDCG
		}
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment