1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-01 17:36:21 +03:00
occt/resources/Shaders/PBREnvBaking.fs
Pasukhin Dmitry df4b931988
Configuration - Resource structure reorganization #429
Reorganized resources to keep source part in src and real-time scripts in resource folder.
For the installation result no changes, still installed to src for windows.
2025-03-18 22:54:43 +00:00

227 lines
7.5 KiB
GLSL

THE_SHADER_IN vec3 ViewDirection; //!< direction of fetching from environment cubemap
#if (__VERSION__ >= 120)
uniform int uSamplesNum; //!< number of samples in Monte-Carlo integration
#else
const int uSamplesNum = 256;
#endif
uniform samplerCube uEnvMap; //!< source of baking (environment cubemap)
#ifdef THE_TO_BAKE_DIFFUSE
uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap
uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap
#endif
#ifdef THE_TO_BAKE_SPECULAR
uniform int uCurrentLevel; //!< current level of specular IBL map (ignored in case of diffuse map's processing)
uniform float uEnvSolidAngleSource; //!< source solid angle sample computed from one edge's size of source environment map's zero mipmap level
#endif
//! Returns coordinates of point theNumber from Hammersley point set having size theSize.
vec2 hammersley (in int theNumber,
in int theSize)
{
int aDenominator = 2;
int aNumber = theNumber;
float aVanDerCorput = 0.0;
for (int i = 0; i < 32; ++i)
{
if (aNumber > 0)
{
aVanDerCorput += mod(float(aNumber), 2.0) / float(aDenominator);
aNumber /= 2;
aDenominator *= 2;
}
}
return vec2(float(theNumber) / float(theSize), aVanDerCorput);
}
//! This function does importance sampling on hemisphere surface using GGX normal distribution function
//! in tangent space (positive z axis is surface normal direction).
vec3 importanceSample (in vec2 theHammersleyPoint,
in float theRoughness)
{
float aPhi = PI_2 * theHammersleyPoint.x;
theRoughness *= theRoughness;
theRoughness *= theRoughness;
float aCosTheta = sqrt((1.0 - theHammersleyPoint.y) / (1.0 + (theRoughness - 1.0) * theHammersleyPoint.y));
float aSinTheta = sqrt(1.0 - aCosTheta * aCosTheta);
return vec3(aSinTheta * cos(aPhi),
aSinTheta * sin(aPhi),
aCosTheta);
}
//! This function uniformly generates samples on whole sphere.
vec3 sphereUniformSample (in vec2 theHammersleyPoint)
{
float aPhi = PI_2 * theHammersleyPoint.x;
float aCosTheta = 2.0 * theHammersleyPoint.y - 1.0;
float aSinTheta = sqrt(1.0 - aCosTheta * aCosTheta);
return vec3(aSinTheta * cos(aPhi),
aSinTheta * sin(aPhi),
aCosTheta);
}
//! Transforms resulted sampled direction from tangent space to world space considering the surface normal.
vec3 fromTangentSpace (in vec3 theVector,
in vec3 theNormal)
{
vec3 anUp = (abs(theNormal.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 anX = normalize(cross(anUp, theNormal));
vec3 anY = cross(theNormal, anX);
return anX * theVector.x + anY * theVector.y + theNormal * theVector.z;
}
#ifdef THE_TO_BAKE_DIFFUSE
#if (__VERSION__ >= 120)
const float aSHBasisFuncCoeffs[9] = float[9]
(
0.282095 * 0.282095,
0.488603 * 0.488603,
0.488603 * 0.488603,
0.488603 * 0.488603,
1.092548 * 1.092548,
1.092548 * 1.092548,
1.092548 * 1.092548,
0.315392 * 0.315392,
0.546274 * 0.546274
);
const float aSHCosCoeffs[9] = float[9]
(
3.141593,
2.094395,
2.094395,
2.094395,
0.785398,
0.785398,
0.785398,
0.785398,
0.785398
);
#else
uniform float aSHBasisFuncCoeffs[9];
uniform float aSHCosCoeffs[9];
#endif
//! Bakes diffuse IBL map's spherical harmonics coefficients.
vec3 bakeDiffuseSH()
{
int anId = int(gl_FragCoord.x);
float aCoef;
#if (__VERSION__ >= 120)
aCoef = aSHCosCoeffs[anId] * aSHBasisFuncCoeffs[anId];
#else
if (anId == 0) { aCoef = aSHCosCoeffs[0] * aSHBasisFuncCoeffs[0]; }
else if (anId == 1) { aCoef = aSHCosCoeffs[1] * aSHBasisFuncCoeffs[1]; }
else if (anId == 2) { aCoef = aSHCosCoeffs[2] * aSHBasisFuncCoeffs[2]; }
else if (anId == 3) { aCoef = aSHCosCoeffs[3] * aSHBasisFuncCoeffs[3]; }
else if (anId == 4) { aCoef = aSHCosCoeffs[4] * aSHBasisFuncCoeffs[4]; }
else if (anId == 5) { aCoef = aSHCosCoeffs[5] * aSHBasisFuncCoeffs[5]; }
else if (anId == 6) { aCoef = aSHCosCoeffs[6] * aSHBasisFuncCoeffs[6]; }
else if (anId == 7) { aCoef = aSHCosCoeffs[7] * aSHBasisFuncCoeffs[7]; }
else { aCoef = aSHCosCoeffs[8] * aSHBasisFuncCoeffs[8]; }
#endif
vec3 aRes = vec3 (0.0);
for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter)
{
vec2 aHammersleyPoint = hammersley (aSampleIter, uSamplesNum);
vec3 aDir = sphereUniformSample (aHammersleyPoint);
vec3 aVal = occTextureCube (uEnvMap, cubemapVectorTransform (aDir, uYCoeff, uZCoeff)).rgb;
#if (__VERSION__ >= 120)
float aFunc[9];
aFunc[0] = 1.0;
aFunc[1] = aDir.x;
aFunc[2] = aDir.y;
aFunc[3] = aDir.z;
aFunc[4] = aDir.x * aDir.z;
aFunc[5] = aDir.y * aDir.z;
aFunc[6] = aDir.x * aDir.y;
aFunc[7] = 3.0 * aDir.z * aDir.z - 1.0;
aFunc[8] = aDir.x * aDir.x - aDir.y * aDir.y;
aRes += aVal * aFunc[anId];
#else
if (anId == 0) { aRes += aVal * 1.0; }
else if (anId == 1) { aRes += aVal * aDir.x; }
else if (anId == 2) { aRes += aVal * aDir.y; }
else if (anId == 3) { aRes += aVal * aDir.z; }
else if (anId == 4) { aRes += aVal * (aDir.x * aDir.z); }
else if (anId == 5) { aRes += aVal * (aDir.y * aDir.z); }
else if (anId == 6) { aRes += aVal * (aDir.x * aDir.y); }
else if (anId == 7) { aRes += aVal * (3.0 * aDir.z * aDir.z - 1.0); }
else { aRes += aVal * (aDir.x * aDir.x - aDir.y * aDir.y); }
#endif
}
return 4.0 * aRes * aCoef / float(uSamplesNum);
}
#endif
#ifdef THE_TO_BAKE_SPECULAR
//! Computes a single sample for specular IBL map.
vec4 specularMapSample (in vec3 theNormal,
in float theRoughness,
in int theNumber,
in int theSize)
{
vec2 aHammersleyPoint = hammersley (theNumber, theSize);
vec3 aHalf = importanceSample (aHammersleyPoint, occRoughness (theRoughness));
float aHdotV = aHalf.z;
aHalf = fromTangentSpace (aHalf, theNormal);
vec3 aLight = -reflect (theNormal, aHalf);
float aNdotL = dot (aLight, theNormal);
if (aNdotL > 0.0)
{
float aSolidAngleSample = 1.0 / (float(theSize) * (occPBRDistribution (aHdotV, theRoughness) * 0.25 + 0.0001) + 0.0001);
float aLod = (theRoughness == 0.0) ? 0.0 : 0.5 * log2 (aSolidAngleSample / uEnvSolidAngleSource);
return vec4 (occTextureCubeLod (uEnvMap, aLight, aLod).rgb * aNdotL, aNdotL);
}
return vec4 (0.0);
}
//! Bakes specular IBL map.
vec3 bakeSpecularMap (in vec3 theNormal,
in float theRoughness)
{
vec4 aResult = vec4(0.0);
if (theRoughness == 0.0)
{
aResult = specularMapSample (theNormal, theRoughness, 0, 1);
}
else
{
for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter)
{
aResult += specularMapSample (theNormal, theRoughness, aSampleIter, uSamplesNum);
}
}
return aResult.xyz / aResult.w;
}
#endif
void main()
{
vec3 aViewDirection = normalize (ViewDirection);
#ifdef THE_TO_BAKE_DIFFUSE
vec4 aRes = vec4 (bakeDiffuseSH(), 1.0);
#ifdef THE_TO_PACK_FLOAT
int aCompIndex = int(gl_FragCoord.y);
float aComp = aCompIndex == 0 ? aRes.x : (aCompIndex == 1 ? aRes.y : aRes.z);
int aFixedPrec = int(aComp * 2147483647.0);
int aFixedDiv1 = aFixedPrec / 256;
int aFixedDiv2 = aFixedDiv1 / 256;
int aFixedDiv3 = aFixedDiv2 / 256;
vec4 aPacked = vec4(float(aFixedPrec), float(aFixedDiv1), float(aFixedDiv2), float(aFixedDiv3));
aRes = fract (aPacked * (1.0 / 256.0));
#endif
occFragColor = aRes;
#else
float aRoughness = float(uCurrentLevel) / float(occNbSpecIBLLevels - 1);
occFragColor = vec4 (bakeSpecularMap (aViewDirection, aRoughness), 1.0);
#endif
}