326 lines
11 KiB
HLSL
326 lines
11 KiB
HLSL
// Copyright(c) 2016, Michal Skalsky
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
// are permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT
|
|
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#define PI 3.14159265359
|
|
|
|
float _AtmosphereHeight;
|
|
float _PlanetRadius;
|
|
float2 _DensityScaleHeight;
|
|
|
|
float3 _ScatteringR;
|
|
float3 _ScatteringM;
|
|
float3 _ExtinctionR;
|
|
float3 _ExtinctionM;
|
|
|
|
float4 _IncomingLight;
|
|
float _MieG;
|
|
|
|
float4 _FrustumCorners[4];
|
|
|
|
float _SunIntensity;
|
|
|
|
sampler2D _ParticleDensityLUT;
|
|
sampler2D _RandomVectors;
|
|
|
|
sampler3D _SkyboxLUT;
|
|
sampler3D _SkyboxLUT2;
|
|
|
|
sampler3D _InscatteringLUT;
|
|
sampler3D _ExtinctionLUT;
|
|
|
|
float _Tenkoku_EclipseFactor;
|
|
float4 _MieColor;
|
|
float4 _GroundColor;
|
|
float _Tenkoku_MieAmt;
|
|
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// RaySphereIntersection
|
|
//-----------------------------------------------------------------------------------------
|
|
float2 RaySphereIntersection(float3 rayOrigin, float3 rayDir, float3 sphereCenter, float sphereRadius)
|
|
{
|
|
rayOrigin -= sphereCenter;
|
|
float a = dot(rayDir, rayDir);
|
|
float b = 2.0 * dot(rayOrigin, rayDir);
|
|
float c = dot(rayOrigin, rayOrigin) - (sphereRadius * sphereRadius);
|
|
float d = b * b - 4 * a * c;
|
|
if (d < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
d = sqrt(d);
|
|
return float2(-b - d, -b + d) / (2 * a);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// Sun
|
|
//-----------------------------------------------------------------------------------------
|
|
float Sun(float cosAngle)
|
|
{
|
|
float g = 0.98;
|
|
float g2 = g * g;
|
|
|
|
float sun = pow(1 - g, 2.0) / (4 * PI * pow(1.0 + g2 - 2.0*g*cosAngle, 1.5));
|
|
return (sun * 0.003);// 5;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// ApplyPhaseFunction
|
|
//-----------------------------------------------------------------------------------------
|
|
void ApplyPhaseFunction(inout float3 scatterR, inout float3 scatterM, float cosAngle)
|
|
{
|
|
// r
|
|
float phase = (3.0 / (16.0 * PI)) * (1 + (cosAngle * cosAngle));
|
|
scatterR *= phase;
|
|
|
|
// m
|
|
float g = _MieG;
|
|
float g2 = g * g;
|
|
phase = (1.0 / (4.0 * PI)) * ((3.0 * (1.0 - g2)) / (2.0 * (2.0 + g2))) * ((1 + cosAngle * cosAngle) / (pow((1 + g2 - 2 * g*cosAngle), 3.0 / 2.0)));
|
|
scatterM *= phase;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// ApplyPhaseFunctionElek
|
|
//-----------------------------------------------------------------------------------------
|
|
void ApplyPhaseFunctionElek(inout float3 scatterR, inout float3 scatterM, float cosAngle)
|
|
{
|
|
// r
|
|
float phase = (8.0 / 10.0) / (4 * PI) * ((7.0 / 5.0) + 0.5 * cosAngle);
|
|
scatterR *= phase;
|
|
|
|
// m
|
|
float g = _MieG;
|
|
float g2 = g * g;
|
|
phase = (1.0 / (4.0 * PI)) * ((3.0 * (1.0 - g2)) / (2.0 * (2.0 + g2))) * ((1 + cosAngle * cosAngle) / (pow((1 + g2 - 2 * g*cosAngle), 3.0 / 2.0)));
|
|
scatterM *= phase;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// RenderSun
|
|
//-----------------------------------------------------------------------------------------
|
|
float3 RenderSun(in float3 scatterM, float cosAngle)
|
|
{
|
|
return scatterM * Sun(cosAngle);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// GetAtmosphereDensity
|
|
//-----------------------------------------------------------------------------------------
|
|
void GetAtmosphereDensity(float3 position, float3 planetCenter, float3 lightDir, out float2 localDensity, out float2 densityToAtmTop)
|
|
{
|
|
float height = length(position - planetCenter) - _PlanetRadius;
|
|
localDensity = exp(-height.xx / _DensityScaleHeight.xy);
|
|
|
|
float cosAngle = dot(normalize(position - planetCenter), -lightDir.xyz);
|
|
|
|
densityToAtmTop = tex2D(_ParticleDensityLUT, float2(cosAngle * 0.5 + 0.5, (height / _AtmosphereHeight))).rg;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// ComputeLocalInscattering
|
|
//-----------------------------------------------------------------------------------------
|
|
void ComputeLocalInscattering(float2 localDensity, float2 densityPA, float2 densityCP, out float3 localInscatterR, out float3 localInscatterM)
|
|
{
|
|
float2 densityCPA = densityCP + densityPA;
|
|
|
|
float3 Tr = densityCPA.x * _ExtinctionR;
|
|
float3 Tm = densityCPA.y * _ExtinctionM;
|
|
|
|
float3 extinction = exp(-(Tr + Tm));
|
|
|
|
localInscatterR = localDensity.x * extinction;
|
|
localInscatterM = localDensity.y * extinction;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// ComputeOpticalDepth
|
|
//-----------------------------------------------------------------------------------------
|
|
float3 ComputeOpticalDepth(float2 density)
|
|
{
|
|
float3 Tr = density.x * _ExtinctionR;
|
|
float3 Tm = density.y * _ExtinctionM;
|
|
|
|
float3 extinction = exp(-(Tr + Tm));
|
|
|
|
return _IncomingLight.xyz * extinction;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// IntegrateInscattering
|
|
//-----------------------------------------------------------------------------------------
|
|
float4 IntegrateInscattering(float3 rayStart, float3 rayDir, float rayLength, float3 planetCenter, float distanceScale, float3 lightDir, float sampleCount, out float4 extinction)
|
|
{
|
|
float3 step = rayDir * (rayLength / sampleCount);
|
|
float stepSize = length(step) * distanceScale;
|
|
|
|
float2 densityCP = 0;
|
|
float3 scatterR = 0;
|
|
float3 scatterM = 0;
|
|
|
|
float2 localDensity;
|
|
float2 densityPA;
|
|
|
|
float2 prevLocalDensity;
|
|
float3 prevLocalInscatterR, prevLocalInscatterM;
|
|
GetAtmosphereDensity(rayStart, planetCenter, lightDir, prevLocalDensity, densityPA);
|
|
ComputeLocalInscattering(prevLocalDensity, densityPA, densityCP, prevLocalInscatterR, prevLocalInscatterM);
|
|
|
|
// P - current integration point
|
|
// C - camera position
|
|
// A - top of the atmosphere
|
|
[loop]
|
|
for (float s = 1.0; s < sampleCount; s += 1)
|
|
{
|
|
float3 p = rayStart + step * s;
|
|
|
|
GetAtmosphereDensity(p, planetCenter, lightDir, localDensity, densityPA);
|
|
densityCP += (localDensity + prevLocalDensity) * (stepSize / 2.0);
|
|
|
|
prevLocalDensity = localDensity;
|
|
|
|
float3 localInscatterR, localInscatterM;
|
|
ComputeLocalInscattering(localDensity, densityPA, densityCP, localInscatterR, localInscatterM);
|
|
|
|
scatterR += (localInscatterR + prevLocalInscatterR) * (stepSize / 2.0);
|
|
scatterM += (localInscatterM + prevLocalInscatterM) * (stepSize / 2.0);
|
|
|
|
prevLocalInscatterR = localInscatterR;
|
|
prevLocalInscatterM = localInscatterM;
|
|
}
|
|
|
|
float3 m = scatterM;
|
|
// phase function
|
|
ApplyPhaseFunction(scatterR, scatterM, dot(rayDir, -lightDir.xyz));
|
|
//scatterR = 0;
|
|
|
|
float3 lightInscatter = (scatterR * _ScatteringR + scatterM * _ScatteringM) * _IncomingLight.xyz;
|
|
lightInscatter += RenderSun(m, dot(rayDir, -lightDir.xyz)) * lerp(half3(1,1,1), _MieColor.rgb, _MieColor.a) * _SunIntensity * _Tenkoku_MieAmt * saturate(lerp(-5,1,_Tenkoku_EclipseFactor));
|
|
float3 lightExtinction = exp(-(densityCP.x * _ExtinctionR + densityCP.y * _ExtinctionM));
|
|
|
|
extinction = float4(lightExtinction, 0);
|
|
|
|
|
|
return float4(lightInscatter, 1);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// PrecomputeParticleDensity
|
|
//-----------------------------------------------------------------------------------------
|
|
float2 PrecomputeParticleDensity(float3 rayStart, float3 rayDir)
|
|
{
|
|
float3 planetCenter = float3(0, -_PlanetRadius, 0);
|
|
|
|
float stepCount = 250;
|
|
|
|
float2 intersection = RaySphereIntersection(rayStart, rayDir, planetCenter, _PlanetRadius);
|
|
if (intersection.x > 0)
|
|
{
|
|
// intersection with planet, write high density
|
|
return 1e+20;
|
|
}
|
|
|
|
intersection = RaySphereIntersection(rayStart, rayDir, planetCenter, _PlanetRadius + _AtmosphereHeight);
|
|
float3 rayEnd = rayStart + rayDir * intersection.y;
|
|
|
|
// compute density along the ray
|
|
float3 step = (rayEnd - rayStart) / stepCount;
|
|
float stepSize = length(step);
|
|
float2 density = 0;
|
|
|
|
for (float s = 0.5; s < stepCount; s += 1.0)
|
|
{
|
|
float3 position = rayStart + step * s;
|
|
float height = abs(length(position - planetCenter) - _PlanetRadius);
|
|
float2 localDensity = exp(-(height.xx / _DensityScaleHeight));
|
|
|
|
density += localDensity * stepSize;
|
|
}
|
|
|
|
return density;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// PrecomputeAmbientLight
|
|
//-----------------------------------------------------------------------------------------
|
|
float4 PrecomputeAmbientLight(float3 lightDir)
|
|
{
|
|
float startHeight = 0;
|
|
float3 rayStart = float3(0, startHeight, 0);
|
|
float3 planetCenter = float3(0, -_PlanetRadius + startHeight, 0);
|
|
|
|
float4 color = 0;
|
|
|
|
int sampleCount = 255;
|
|
|
|
[loop]
|
|
for (int ii = 0; ii < sampleCount; ++ii)
|
|
{
|
|
float3 rayDir = tex2D(_RandomVectors, float2(ii + (0.5 / 255.0), 0.5));
|
|
rayDir.y = abs(rayDir.y);
|
|
|
|
float2 intersection = RaySphereIntersection(rayStart, rayDir, planetCenter, _PlanetRadius + _AtmosphereHeight);
|
|
float rayLength = intersection.y;
|
|
|
|
intersection = RaySphereIntersection(rayStart, rayDir, planetCenter, _PlanetRadius);
|
|
if (intersection.x > 0)
|
|
rayLength = min(rayLength, intersection.x);
|
|
|
|
float sampleCount = 32;
|
|
float4 extinction;
|
|
|
|
float4 scattaring = (IntegrateInscattering(rayStart, rayDir, rayLength, planetCenter, 1, lightDir, sampleCount, extinction));
|
|
|
|
color += scattaring * dot(rayDir, float3(0, 1, 0));
|
|
}
|
|
|
|
return color * 2 * PI / sampleCount;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// PrecomputeDirLight
|
|
//-----------------------------------------------------------------------------------------
|
|
float4 PrecomputeDirLight(float3 rayDir)
|
|
{
|
|
float startHeight = 500;
|
|
|
|
float3 rayStart = float3(0, startHeight, 0);
|
|
|
|
float3 planetCenter = float3(0, -_PlanetRadius + startHeight, 0);
|
|
|
|
float2 localDensity;
|
|
float2 densityToAtmosphereTop;
|
|
|
|
GetAtmosphereDensity(rayStart, planetCenter, -rayDir, localDensity, densityToAtmosphereTop);
|
|
float4 color;
|
|
color.xyz = ComputeOpticalDepth(densityToAtmosphereTop);
|
|
color.w = 1;
|
|
return color;
|
|
} |