0030700: Visualization, TKOpenGl - support PBR Metallic-Roughness shading model
[occt.git] / src / Shaders / PBREnvBaking.fs
1 THE_SHADER_IN vec3 ViewDirection; //!< direction of fetching from environment cubemap
2
3 uniform int uSamplesNum;     //!< number of samples in Monte-Carlo integration
4 uniform int uCurrentLevel;   //!< current level of specular IBL map (ignored in case of diffuse map's processing)
5 uniform int uEnvMapSize;     //!< one edge's size of source environtment map's zero mipmap level
6 uniform int uYCoeff;         //!< coefficient of Y controlling horizontal flip of cubemap
7 uniform int uZCoeff;         //!< coefficient of Z controlling vertical flip of cubemap
8 uniform samplerCube uEnvMap; //!< source of baking (environment cubemap)
9
10 //! Returns coordinates of point theNumber from Hammersley point set having size theSize.
11 vec2 hammersley (in int theNumber,
12                  in int theSize)
13 {
14   int aDenominator = 2;
15   int aNumber = theNumber;
16   float aVanDerCorput = 0.0;
17   for (int i = 0; i < 32; ++i)
18   {
19     if (aNumber > 0)
20     {
21       aVanDerCorput += float(aNumber % 2) / float(aDenominator);
22       aNumber /= 2;
23       aDenominator *= 2;
24     }
25   }
26   return vec2(float(theNumber) / float(theSize), aVanDerCorput);
27 }
28
29 //! This function does importance sampling on hemisphere surface using GGX normal distribution function
30 //! in tangent space (positive z axis is surface normal direction).
31 vec3 importanceSample (in vec2  theHammersleyPoint,
32                        in float theRoughness)
33 {
34   float aPhi = PI_2 * theHammersleyPoint.x;
35   theRoughness *= theRoughness;
36   theRoughness *= theRoughness;
37   float aCosTheta = sqrt((1.0 - theHammersleyPoint.y) / (1.0 + (theRoughness - 1.0) * theHammersleyPoint.y));
38   float aSinTheta = sqrt(1.0 - aCosTheta * aCosTheta);
39   return vec3(aSinTheta * cos(aPhi),
40               aSinTheta * sin(aPhi),
41               aCosTheta);
42 }
43
44 //! This function uniformly generates samples on whole sphere.
45 vec3 sphereUniformSample (in vec2 theHammersleyPoint)
46 {
47   float aPhi = PI_2 * theHammersleyPoint.x;
48   float aCosTheta = 2.0 * theHammersleyPoint.y - 1.0;
49   float aSinTheta = sqrt(1.0 - aCosTheta * aCosTheta);
50   return vec3(aSinTheta * cos(aPhi),
51               aSinTheta * sin(aPhi),
52               aCosTheta);
53 }
54
55 //! Transforms resulted sampled direction from tangent space to world space considering the surface normal.
56 vec3 fromTangentSpace (in vec3 theVector,
57                        in vec3 theNormal)
58 {
59   vec3 anUp = (abs(theNormal.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
60   vec3 anX = normalize(cross(anUp, theNormal));
61   vec3 anY = cross(theNormal, anX);
62   return anX * theVector.x + anY * theVector.y + theNormal * theVector.z;
63 }
64
65 const float aSHBasisFuncCoeffs[9] = float[9]
66 (
67   0.282095 * 0.282095,
68   0.488603 * 0.488603,
69   0.488603 * 0.488603,
70   0.488603 * 0.488603,
71   1.092548 * 1.092548,
72   1.092548 * 1.092548,
73   1.092548 * 1.092548,
74   0.315392 * 0.315392,
75   0.546274 * 0.546274
76 );
77
78 const float aSHCosCoeffs[9] = float[9]
79 (
80   3.141593,
81   2.094395,
82   2.094395,
83   2.094395,
84   0.785398,
85   0.785398,
86   0.785398,
87   0.785398,
88   0.785398
89 );
90
91 //! Bakes diffuse IBL map's spherical harmonics coefficients.
92 vec3 bakeDiffuseSH()
93 {
94   int anIndex = int(gl_FragCoord.x);
95   vec3 aResult = vec3 (0.0);
96   for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter)
97   {
98     vec2 aHammersleyPoint = hammersley (aSampleIter, uSamplesNum);
99     vec3 aDirection = sphereUniformSample (aHammersleyPoint);
100
101     vec3 aValue = occTextureCube (uEnvMap, cubemapVectorTransform (aDirection, uYCoeff, uZCoeff)).rgb;
102
103     float aBasisFunc[9];
104     aBasisFunc[0] = 1.0;
105
106     aBasisFunc[1] = aDirection.x;
107     aBasisFunc[2] = aDirection.y;
108     aBasisFunc[3] = aDirection.z;
109
110     aBasisFunc[4] = aDirection.x * aDirection.z;
111     aBasisFunc[5] = aDirection.y * aDirection.z;
112     aBasisFunc[6] = aDirection.x * aDirection.y;
113
114     aBasisFunc[7] = 3.0 * aDirection.z * aDirection.z - 1.0;
115     aBasisFunc[8] = aDirection.x * aDirection.x - aDirection.y * aDirection.y;
116
117     aResult += aValue * aBasisFunc[anIndex];
118   }
119
120   aResult *= 4.0 * aSHCosCoeffs[anIndex] * aSHBasisFuncCoeffs[anIndex] / float(uSamplesNum);
121   return aResult;
122 }
123
124 //! Bakes specular IBL map.
125 vec3 bakeSpecularMap (in vec3  theNormal,
126                       in float theRoughness)
127 {
128   vec3 aResult = vec3(0.0);
129   float aWeightSum = 0.0;
130   int aSamplesNum = (theRoughness == 0.0) ? 1 : uSamplesNum;
131   float aSolidAngleSource = 4.0 * PI / (6.0 * float(uEnvMapSize * uEnvMapSize));
132   for (int aSampleIter = 0; aSampleIter < aSamplesNum; ++aSampleIter)
133   {
134     vec2 aHammersleyPoint = hammersley (aSampleIter, aSamplesNum);
135     vec3 aHalf = importanceSample (aHammersleyPoint, occRoughness (theRoughness));
136     float aHdotV = aHalf.z;
137     aHalf = fromTangentSpace (aHalf, theNormal);
138     vec3  aLight = -reflect (theNormal, aHalf);
139     float aNdotL = dot (aLight, theNormal);
140     if (aNdotL > 0.0)
141     {
142       float aSolidAngleSample = 1.0 / (float(aSamplesNum) * (occPBRDistribution (aHdotV, theRoughness) * 0.25 + 0.0001) + 0.0001);
143       float aLod = (theRoughness == 0.0) ? 0.0 : 0.5 * log2 (aSolidAngleSample / aSolidAngleSource);
144       aResult += occTextureCubeLod (uEnvMap, aLight, aLod).rgb * aNdotL;
145       aWeightSum += aNdotL;
146     }
147   }
148   return aResult / aWeightSum;
149 }
150
151 void main()
152 {
153   vec3 aViewDirection = normalize (ViewDirection);
154   if (occNbSpecIBLLevels == 0)
155   {
156     occSetFragColor (vec4 (bakeDiffuseSH (), 1.0));
157   }
158   else
159   {
160     occSetFragColor (vec4 (bakeSpecularMap (aViewDirection, float(uCurrentLevel) / float(occNbSpecIBLLevels - 1)), 1.0));
161   }
162 }