2021-09-09 20:42:29 -04:00

301 lines
10 KiB
Plaintext

Shader "Hidden/HDRP/Sky/PbrSky"
{
HLSLINCLUDE
#pragma vertex Vert
// #pragma enable_d3d11_debug_symbols
#pragma editor_sync_compilation
#pragma target 4.5
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightDefinition.cs.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/PhysicallyBasedSky/PhysicallyBasedSkyCommon.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyUtils.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/AtmosphericScattering/AtmosphericScattering.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/CookieSampling.hlsl"
int _HasGroundAlbedoTexture; // bool...
int _HasGroundEmissionTexture; // bool...
int _HasSpaceEmissionTexture; // bool...
int _RenderSunDisk; // bool...
float _GroundEmissionMultiplier;
float _SpaceEmissionMultiplier;
// Sky framework does not set up global shader variables (even per-view ones),
// so they can contain garbage. It's very difficult to not include them, however,
// since the sky framework includes them internally in many header files.
// Just don't use them. Ever.
float3 _WorldSpaceCameraPos1;
float4x4 _ViewMatrix1;
#undef UNITY_MATRIX_V
#define UNITY_MATRIX_V _ViewMatrix1
// 3x3, but Unity can only set 4x4...
float4x4 _PlanetRotation;
float4x4 _SpaceRotation;
TEXTURECUBE(_GroundAlbedoTexture);
TEXTURECUBE(_GroundEmissionTexture);
TEXTURECUBE(_SpaceEmissionTexture);
struct Attributes
{
uint vertexID : SV_VertexID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings Vert(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID, UNITY_RAW_FAR_CLIP_VALUE);
return output;
}
float4 RenderSky(Varyings input)
{
const float R = _PlanetaryRadius;
// TODO: Not sure it's possible to precompute cam rel pos since variables
// in the two constant buffers may be set at a different frequency?
const float3 O = _WorldSpaceCameraPos1 - _PlanetCenterPosition.xyz;
const float3 V = GetSkyViewDirWS(input.positionCS.xy);
bool renderSunDisk = _RenderSunDisk != 0;
float3 N; float r; // These params correspond to the entry point
float tEntry = IntersectAtmosphere(O, V, N, r).x;
float tExit = IntersectAtmosphere(O, V, N, r).y;
float NdotV = dot(N, V);
float cosChi = -NdotV;
float cosHor = ComputeCosineOfHorizonAngle(r);
bool rayIntersectsAtmosphere = (tEntry >= 0);
bool lookAboveHorizon = (cosChi >= cosHor);
float tFrag = FLT_INF;
float3 radiance = 0;
if (renderSunDisk)
{
// Intersect and shade emissive celestial bodies.
// Unfortunately, they don't write depth.
for (uint i = 0; i < _DirectionalLightCount; i++)
{
DirectionalLightData light = _DirectionalLightDatas[i];
// Use scalar or integer cores (more efficient).
bool interactsWithSky = asint(light.distanceFromCamera) >= 0;
// Celestial body must be outside the atmosphere (request from Pierre D).
float lightDist = max(light.distanceFromCamera, tExit);
if (interactsWithSky && asint(light.angularDiameter) != 0 && lightDist < tFrag)
{
// We may be able to see the celestial body.
float3 L = -light.forward.xyz;
float LdotV = -dot(L, V);
float rad = acos(LdotV);
float radInner = 0.5 * light.angularDiameter;
float cosInner = cos(radInner);
float cosOuter = cos(radInner + light.flareSize);
float solidAngle = TWO_PI * (1 - cosInner);
if (LdotV >= cosOuter)
{
// Sun flare is visible. Sun disk may or may not be visible.
// Assume uniform emission.
float3 color = light.color.rgb;
float scale = rcp(solidAngle);
if (LdotV >= cosInner) // Sun disk.
{
tFrag = lightDist;
if (light.surfaceTextureScaleOffset.x > 0)
{
// The cookie code de-normalizes the axes.
float2 proj = float2(dot(-V, normalize(light.right)), dot(-V, normalize(light.up)));
float2 angles = HALF_PI - acos(proj);
float2 uv = angles * rcp(radInner) * 0.5 + 0.5;
color *= SampleCookie2D(uv, light.surfaceTextureScaleOffset);
// color *= SAMPLE_TEXTURE2D_ARRAY(_CookieTextures, s_linear_clamp_sampler, uv, light.surfaceTextureIndex).rgb;
}
color *= light.surfaceTint;
}
else // Flare region.
{
float r = max(0, rad - radInner);
float w = saturate(1 - r * rcp(light.flareSize));
color *= light.flareTint;
scale *= SafePositivePow(w, light.flareFalloff);
}
radiance += color * scale;
}
}
}
}
if (rayIntersectsAtmosphere && !lookAboveHorizon) // See the ground?
{
float tGround = tEntry + IntersectSphere(R, cosChi, r).x;
if (tGround < tFrag)
{
// Closest so far.
// Make it negative to communicate to EvaluatePbrAtmosphere that we intersected the ground.
tFrag = -tGround;
radiance = 0;
float3 gP = O + tGround * -V;
float3 gN = normalize(gP);
if (_HasGroundEmissionTexture)
{
float4 ts = SAMPLE_TEXTURECUBE(_GroundEmissionTexture, s_trilinear_clamp_sampler, mul(gN, (float3x3)_PlanetRotation));
radiance += _GroundEmissionMultiplier * ts.rgb;
}
float3 albedo = _GroundAlbedo.xyz;
if (_HasGroundAlbedoTexture)
{
albedo *= SAMPLE_TEXTURECUBE(_GroundAlbedoTexture, s_trilinear_clamp_sampler, mul(gN, (float3x3)_PlanetRotation)).rgb;
}
float3 gBrdf = INV_PI * albedo;
// Shade the ground.
for (uint i = 0; i < _DirectionalLightCount; i++)
{
DirectionalLightData light = _DirectionalLightDatas[i];
// Use scalar or integer cores (more efficient).
bool interactsWithSky = asint(light.distanceFromCamera) >= 0;
float3 L = -light.forward.xyz;
float3 intensity = light.color.rgb;
float3 irradiance = SampleGroundIrradianceTexture(dot(gN, L));
radiance += gBrdf * (irradiance * intensity); // Scale from unit intensity to light's intensity
}
}
}
else if (tFrag == FLT_INF) // See the stars?
{
if (_HasSpaceEmissionTexture)
{
// V points towards the camera.
float4 ts = SAMPLE_TEXTURECUBE(_SpaceEmissionTexture, s_trilinear_clamp_sampler, mul(-V, (float3x3)_SpaceRotation));
radiance += _SpaceEmissionMultiplier * ts.rgb;
}
}
float3 skyColor = 0, skyOpacity = 0;
if (rayIntersectsAtmosphere)
{
float distAlongRay = tFrag;
EvaluatePbrAtmosphere(_WorldSpaceCameraPos1, V, distAlongRay, renderSunDisk, skyColor, skyOpacity);
}
skyColor += radiance * (1 - skyOpacity);
skyColor *= _IntensityMultiplier;
return float4(skyColor, 1.0);
}
float4 FragBaking(Varyings input) : SV_Target
{
return RenderSky(input); // The cube map is not pre-exposed
}
float4 FragRender(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
float4 value = RenderSky(input);
value.rgb *= GetCurrentExposureMultiplier(); // Only the full-screen pass is pre-exposed
return value;
}
float4 FragBlack(Varyings input) : SV_Target
{
return 0;
}
ENDHLSL
SubShader
{
Pass
{
ZWrite Off
ZTest Always
Blend Off
Cull Off
HLSLPROGRAM
#pragma fragment FragBaking
ENDHLSL
}
Pass
{
ZWrite Off
ZTest Always
Blend Off
Cull Off
HLSLPROGRAM
#pragma fragment FragBlack
ENDHLSL
}
Pass
{
ZWrite Off
ZTest LEqual
Blend Off
Cull Off
HLSLPROGRAM
#pragma fragment FragRender
ENDHLSL
}
Pass
{
ZWrite Off
ZTest LEqual
Blend Off
Cull Off
HLSLPROGRAM
#pragma fragment FragBlack
ENDHLSL
}
}
Fallback Off
}