Shader "Builtin Skybox" { Properties { _Tint ("Tint", Color) = (1,1,1,1) [Semantic(Tint)] _Exposure ("Exposure", Float) = 1.0 [Semantic(Exposure)] _Rotation ("Rotation", Float) = 0.0 [Semantic(Rotation)] _MainTex ("Panoramic", 2D) = "white" [Semantic(SkyboxPanoramicTexture)] _Tex ("Cubemap (HDR)", Cube) = "white" [Semantic(SkyboxTexture)] } HLSLINCLUDE cbuffer EnvironmentConstants { float4 gSkyboxTopColor; float4 gSkyboxHorizonColor; float4 gSkyboxBottomColor; float4 gCameraRightAndTanHalfFov; float4 gCameraUpAndAspect; float4 gCameraForwardAndUnused; }; cbuffer MaterialConstants { float4 gSkyboxTintAndExposure; float4 gSkyboxRotationAndMode; }; Texture2D SkyboxPanoramicTexture; TextureCube SkyboxTexture; SamplerState LinearClampSampler; struct VSOutput { float4 position : SV_POSITION; float2 ndc : TEXCOORD0; }; static const float XC_PI = 3.14159265358979323846f; static const float XC_INV_PI = 0.31830988618379067154f; static const float XC_INV_TWO_PI = 0.15915494309189533577f; VSOutput MainVS(uint vertexId : SV_VertexID) { const float2 positions[3] = { float2(-1.0f, -1.0f), float2(-1.0f, 3.0f), float2( 3.0f, -1.0f) }; VSOutput output; output.position = float4(positions[vertexId], 1.0f, 1.0f); output.ndc = positions[vertexId]; return output; } float3 EvaluateProceduralSkybox(float3 viewRay) { const float vertical = clamp(viewRay.y, -1.0f, 1.0f); float3 color = gSkyboxHorizonColor.rgb; if (vertical >= 0.0f) { color = lerp(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(saturate(vertical), 0.65f)); } else { color = lerp(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(saturate(-vertical), 0.55f)); } return color; } float3 RotateAroundY(float3 viewRay) { const float rotation = gSkyboxRotationAndMode.x; const float sinTheta = sin(rotation); const float cosTheta = cos(rotation); return normalize(float3( viewRay.x * cosTheta - viewRay.z * sinTheta, viewRay.y, viewRay.x * sinTheta + viewRay.z * cosTheta)); } float2 ComputePanoramicUv(float3 viewRay) { const float3 rotatedRay = RotateAroundY(viewRay); const float u = frac(atan2(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5f); const float v = acos(clamp(rotatedRay.y, -1.0f, 1.0f)) * XC_INV_PI; return float2(u, saturate(v)); } float4 MainPS(VSOutput input) : SV_TARGET { const float tanHalfFov = gCameraRightAndTanHalfFov.w; const float aspect = gCameraUpAndAspect.w; const float3 viewRay = normalize( gCameraForwardAndUnused.xyz + input.ndc.x * aspect * tanHalfFov * gCameraRightAndTanHalfFov.xyz + input.ndc.y * tanHalfFov * gCameraUpAndAspect.xyz); float3 color = EvaluateProceduralSkybox(viewRay); if (gSkyboxRotationAndMode.y > 1.5f) { color = SkyboxTexture.Sample(LinearClampSampler, RotateAroundY(viewRay)).rgb * gSkyboxTintAndExposure.rgb * gSkyboxTintAndExposure.w; } else if (gSkyboxRotationAndMode.y > 0.5f) { color = SkyboxPanoramicTexture.Sample(LinearClampSampler, ComputePanoramicUv(viewRay)).rgb * gSkyboxTintAndExposure.rgb * gSkyboxTintAndExposure.w; } return float4(color, 1.0f); } ENDHLSL SubShader { Pass { Name "Skybox" Tags { "LightMode" = "Skybox" } Cull Off ZWrite Off ZTest LEqual Blend Off HLSLPROGRAM #pragma target 4.5 #pragma vertex MainVS #pragma fragment MainPS ENDHLSL } } }