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.
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/
Thanks!
Here is the script if someone needs.
Shader "flat infinity"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.vertex.z = 1.0e-9f;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
THANKS! I was searching this type of solution for hours!
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
background texture-aspect skybox
//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 (Advanced)"
{
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
}
}
}
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:
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));
}
EnvelopeParent/FitInsideParent + empty space
//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"
{
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
#pragma multi_compile _ 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
}
}
}