123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <viewsrg.srgi>
- #include <Atom/Features/SphericalHarmonicsUtility.azsli>
- #include <Atom/RPI/Math.azsli>
- // RGB per row
- // first 3 bands for Grace Cathedral probe available at here:
- // https://www.pauldebevec.com/Probes/
- // calculated as c_l_m = integral{ Radiance(s) * SHBasis(l, m, s) } for all sampled direction s on sphere S
- // which runs for each color channel separately (c_l_m_r, c_l_m_g, c_l_m_b)
- // the lower hemi sphere radiances under the surface are filtered by lambertian cosine term
- static const float GraceCathedralSH[27] = {
- // R
- 2.53951384635872,
- 1.0775523857285165, 1.239229205484427 , 0.9458659067630454 ,
- 1.782942445353695 , 0.8193052111265798, -0.3359348352648975, 0.3709499352580087, 0.7844904694566587,
-
- // G
- 1.405939440477567,
- 0.5737359819468685, 1.0978087141208104, 0.18506274053816416,
- 0.6811688402789957, 0.7023431983214563, 0.3056738462214631 , 0.1753565473744576, 0.1778660985880454,
-
- // B
- 1.748271935016665,
- 0.8526870003819748, 1.9101990711004575, -0.025668773449626654,
- 0.4438016469839106, 1.5102998945629529, 1.093993744201323, 0.4109905263687058, -0.07419514044613942
- };
- // SH coefficients for Eucalyptus Grove probe
- static const float RNLSH[27] = {
- // R
- 3.766055529641367,
- -0.40406140148206865, 2.877179048074897, 1.0167815002471956,
- -0.5853821006554704, -0.09935292918366617, 0.28423834881566207, 0.6222773907470484, 0.8885327769488396,
-
- // G
- 4.2447416994629465,
- -0.3207608858594331, 3.5761459663568447, 1.014234018050483,
- -0.5164785598628846, 0.12420621153110219, 0.6557821044358743, 0.556331752796699, 1.0410020790843508,
-
- // B
- 4.491401630981911,
- -0.12215412094184247, 4.136091787179279, 0.8658157727284175,
- -0.377989948123863, 0.4461962278000395, 1.1430202258191537, 0.4006585445306217, 1.0740016349582413
- };
- // SH coefficients for St Peter's Basilica probe
- static const float StPeterSH[27] = {
- // R
- 3.5803230858525286,
- 0.266039684453623, 1.6925803298077324, -0.35934100317952267,
- 0.04656037807347018, 0.4358724537888737, 1.2687743552404271, -0.18570076550676343, 0.33246625615466635,
-
- // G
- 2.5747666746640654,
- 0.10749801820011659, 1.3790985820712267, -0.2352465711852622,
- 0.037522972624484646, 0.23694792243134288, 0.7304912926110536, -0.1382374891588147, 0.46604897024599046,
-
- // B
- 2.2780129003295295,
- 0.013810896524256528, 1.2053005811787343, -0.10786941964227027,
- 0.001560121273374082, 0.10741082229332044, 0.3891549382974423, -0.038006070134113876, 0.6299315230288755
- };
- ShaderResourceGroup SphericalHarmonicsInstanceSrg : SRG_PerObject
- {
- int m_presetIndex;
- float m_exposure;
- bool m_enableGammaCorrection;
- float3 m_rotationAngle;
- column_major float4x4 m_objectMatrix;
- row_major float4x4 m_fakeLightSH;
- }
- struct VSInput
- {
- float3 m_position : POSITION;
- float2 m_uv : UV0;
- };
- struct VSOutput
- {
- float4 m_position : SV_Position;
- float2 m_uv : UV0;
- };
- VSOutput MainVS(VSInput vsInput)
- {
- VSOutput OUT;
- OUT.m_position = mul(float4(vsInput.m_position, 1.0), SphericalHarmonicsInstanceSrg::m_objectMatrix);
- OUT.m_uv = vsInput.m_uv;
- return OUT;
- }
- struct PSOutput
- {
- float4 m_color : SV_Target0;
- };
- float3 CalFakeLight(float3 dir)
- {
- float3 radiance = float3(0.0, 0.0, 0.0);
- // RGB, band 0
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][0] * SHBasisPoly3(0, 0, dir);
- // RGB, band 1
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][1] * SHBasisPoly3(1, -1, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][2] * SHBasisPoly3(1, 0, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][3] * SHBasisPoly3(1, 1, dir);
- // RGB, band 2
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][0] * SHBasisPoly3(2, -2, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][1] * SHBasisPoly3(2, -1, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][2] * SHBasisPoly3(2, 0, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][3] * SHBasisPoly3(2, 1, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[2][0] * SHBasisPoly3(2, 2, dir);
- // RGB, band 3
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[2][1] * SHBasisPoly3(3, -3, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[2][2] * SHBasisPoly3(3, -2, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[2][3] * SHBasisPoly3(3, 1, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[3][0] * SHBasisPoly3(3, 0, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[3][1] * SHBasisPoly3(3, 1, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[3][2] * SHBasisPoly3(3, 2, dir);
- radiance.xyz += SphericalHarmonicsInstanceSrg::m_fakeLightSH[3][3] * SHBasisPoly3(3, 3, dir);
-
- return radiance;
- }
- float3 CalFakeLightOriginal(float3 dir)
- {
- float theta = acos(dir.y);
- float phi = atan2(dir.z, dir.x);
-
- float3 res = float3(0.0, 0.0, 0.0);
- res = 5.0 * (max(0.0, 5.0 * cos(theta) - 4.0) +
- max(0.0, -4.0 * sin(theta - PI) * cos(phi - 2.5) - 3.0));
- return res;
- }
- float3x3 EulerToRotMatrix(float3 angle)
- {
- row_major float3x3 rotationMat;
- row_major float3x3 Rx = {
- 1.0, 0.0, 0.0,
- 0.0, cos(angle.x), -sin(angle.x),
- 0.0, sin(angle.x), cos(angle.x)
- };
-
- row_major float3x3 Ry = {
- cos(angle.y), 0.0, sin(angle.y),
- 0.0, 1.0, 0.0,
- -sin(angle.y), 0.0, cos(angle.y)
- };
-
- row_major float3x3 Rz = {
- cos(angle.z), -sin(angle.z), 0.0,
- sin(angle.z), cos(angle.z), 0.0,
- 0.0, 0.0, 1.0
- };
-
- rotationMat = mul(Rz, mul(Ry, Rx));
- return rotationMat;
- }
- float3 CalFakeLightRotated(float3 dir, float3 angle)
- {
- float3 radiance = float3(0.0, 0.0, 0.0);
- // include first 3 bands only since the rotation function only support 3,
- // rotation of higher band is recommended to use standard WignerD provided on CPU side
- row_major float3x3 Rot = EulerToRotMatrix(angle);
- float sh[9] = {
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][0],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][1],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][2],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[0][3],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][0],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][1],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][2],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[1][3],
- SphericalHarmonicsInstanceSrg::m_fakeLightSH[2][0]
- };
-
- float shRot[9];
- SHRotationZHF3(Rot, sh, shRot);
-
- // RGB, band 0
- radiance.xyz += shRot[0] * SHBasisPoly3(0, 0, dir);
- // RGB, band 1
- radiance.xyz += shRot[1] * SHBasisPoly3(1, -1, dir);
- radiance.xyz += shRot[2] * SHBasisPoly3(1, 0, dir);
- radiance.xyz += shRot[3] * SHBasisPoly3(1, 1, dir);
- // RGB, band 2
- radiance.xyz += shRot[4] * SHBasisPoly3(2, -2, dir);
- radiance.xyz += shRot[5] * SHBasisPoly3(2, -1, dir);
- radiance.xyz += shRot[6] * SHBasisPoly3(2, 0, dir);
- radiance.xyz += shRot[7] * SHBasisPoly3(2, 1, dir);
- radiance.xyz += shRot[8] * SHBasisPoly3(2, 2, dir);
-
- return radiance;
- }
- // The name of variable follows following resource:
- // https://www.gdcvault.com/play/1012351/Uncharted-2-HDR
- float3 Uncharted2ToneMapping(float3 color, float exposure)
- {
- float A = 0.15;
- float B = 0.50;
- float C = 0.10;
- float D = 0.20;
- float E = 0.02;
- float F = 0.30;
- float W = 11.2;
- color *= exposure;
- color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
- float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
- color /= white;
- return color;
- }
- // Ray marcher for SH visualisation, based on https://www.shadertoy.com/view/lsfXWH
- PSOutput MainPS(VSOutput psInput)
- {
- // all following calculations and coordinates are in world space
- PSOutput OUT;
- float3 hdrColor = float3(0.0, 0.0, 0.0);
- // inverse of rotation matrix is its transpose due to orthogonality
- // position of translation part won't affect result since it's not used here
- float4x4 invView = transpose(ViewSrg::m_viewMatrix);
-
- float2 recenteredUV = psInput.m_uv - float2(0.5, 0.5);
-
- // -1 because -z forward frame is used
- float3 viewSpaceRayDir = float3(recenteredUV.x, recenteredUV.y, -1.0);
-
- // world space ray origin & direction
- float3 rayDir = normalize(mul(invView, float4(viewSpaceRayDir, 0.0)).xyz);
- float3 rayOrigin = ViewSrg::m_worldPosition.xyz;
- // length of ray at intersection point, -1 if miss
- float t = RaySphereClosestHitWS(float3(0, 0, 0), 0.5, rayOrigin, rayDir);
-
- // only do shading if hit anything
- if(t > 0.0)
- {
- float3 pos = rayOrigin + rayDir * t;
-
- // the normal of points on unit sphere at the origin is its position
- float3 normal = normalize(pos);
- float3 outRadiance = float3(0.0, 0.0, 0.0);
- switch(SphericalHarmonicsInstanceSrg::m_presetIndex)
- {
- case 0: outRadiance = EvalSH2RadianceRGB(normal, GraceCathedralSH); break;
- case 1: outRadiance = EvalSH2RadianceRGB(normal, RNLSH ); break;
- case 2: outRadiance = EvalSH2RadianceRGB(normal, StPeterSH ); break;
- case 3: outRadiance = CalFakeLight(normal); break;
- case 4: outRadiance = CalFakeLightOriginal(normal); break;
- case 5: outRadiance = CalFakeLightRotated(normal, SphericalHarmonicsInstanceSrg::m_rotationAngle);
- }
- hdrColor = Uncharted2ToneMapping(max(outRadiance, 0.0), SphericalHarmonicsInstanceSrg::m_exposure);
- }
- if(SphericalHarmonicsInstanceSrg::m_enableGammaCorrection)
- {
- hdrColor = sqrt(hdrColor);
- }
-
- OUT.m_color = float4(hdrColor, 1.0);
- return OUT;
- }
|