|
6 | 6 | #define NB_PROBES 0 |
7 | 7 | #endif |
8 | 8 |
|
| 9 | +// Internal GGX stability floor. This does not change the user-facing |
| 10 | +// roughness convention or probe LOD mapping; it only prevents extremely sharp |
| 11 | +// microfacet lobes from reaching pathological peaks that can destabilize some |
| 12 | +// drivers and screenshot-test software renderers. |
| 13 | +const float MIN_GGX_ALPHA = 0.0064; |
| 14 | + |
9 | 15 | // BEGIN-@jhonkkk,Specular AA -------------------------------------------------------------- |
10 | 16 | // see:http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA(slides).pdf |
11 | 17 | // https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@15.0/manual/Geometric-Specular-Anti-Aliasing.html |
@@ -55,27 +61,27 @@ float Inner_PBR_ComputeDirectLight( |
55 | 61 | vec3 normal, vec3 halfVec, vec3 lightDir, vec3 viewDir, |
56 | 62 | vec3 lightColor, vec3 fZero, float alpha, float ndotv, |
57 | 63 | out vec3 outDiffuse, out vec3 outSpecular){ |
| 64 | + alpha = max(alpha, MIN_GGX_ALPHA); |
58 | 65 |
|
59 | 66 | // Compute ndotl, ndoth, vdoth terms which are needed later. |
60 | 67 | float ndotl = max( dot(normal, lightDir), 0.0); |
61 | 68 | float ndoth = max( dot(normal, halfVec), 0.0); |
62 | 69 | float hdotv = max( dot(viewDir, halfVec), 0.0); |
63 | 70 |
|
64 | | - // Compute diffuse using energy-conserving Lambert. |
65 | | - // Alternatively, use Oren-Nayar for really rough |
66 | | - // materials or if you have lots of processing power ... |
67 | | - outDiffuse = vec3(ndotl) * lightColor; |
68 | | - |
69 | 71 | //cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf |
70 | 72 |
|
71 | 73 | //D, GGX normal Distribution function |
72 | 74 | float alpha2 = alpha * alpha; |
73 | 75 | float sum = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0); |
74 | 76 | float denom = PI * sum * sum; |
75 | | - float D = alpha2 / denom; |
| 77 | + float D = alpha2 / denom; |
76 | 78 |
|
77 | 79 | // Compute Fresnel function via Schlick's approximation. |
78 | 80 | vec3 fresnel = F_Shlick(hdotv, fZero); |
| 81 | + // Lambert diffuse BRDF with a Fresnel-based energy split. |
| 82 | + // The caller multiplies this term by diffuseColor, which already encodes |
| 83 | + // the metallic workflow's (1 - metallic) factor. |
| 84 | + outDiffuse = vec3(ndotl / PI) * lightColor * (vec3(1.0) - fresnel); |
79 | 85 |
|
80 | 86 | //G Schlick GGX Geometry shadowing term, k = alpha/2 |
81 | 87 | float k = alpha * 0.5; |
@@ -136,22 +142,32 @@ vec3 integrateBRDFApprox( const in vec3 specular, float roughness, float NoV ){ |
136 | 142 | return specular * AB.x + AB.y; |
137 | 143 | } |
138 | 144 |
|
| 145 | +float computeSpecularAO(float ao, float roughness, float NoV) { |
| 146 | + float exponent = exp2(-16.0 * roughness - 1.0); |
| 147 | + return clamp(pow(NoV + ao, exponent) - 1.0 + ao, 0.0, 1.0); |
| 148 | +} |
| 149 | + |
139 | 150 | // from Sebastien Lagarde https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf page 69 |
140 | | -vec3 getSpecularDominantDir(const in vec3 N, const in vec3 R, const in float realRoughness){ |
| 151 | +// The Frostbite note's "real roughness" here is the microfacet alpha term, |
| 152 | +// not perceptual roughness. Callers should pass alpha = roughness * roughness. |
| 153 | +vec3 getSpecularDominantDir(const in vec3 N, const in vec3 R, const in float alpha){ |
141 | 154 | vec3 dominant; |
142 | 155 |
|
143 | | - float smoothness = 1.0 - realRoughness; |
144 | | - float lerpFactor = smoothness * (sqrt(smoothness) + realRoughness); |
| 156 | + float smoothness = 1.0 - alpha; |
| 157 | + float lerpFactor = smoothness * (sqrt(smoothness) + alpha); |
145 | 158 | // The result is not normalized as we fetch in a cubemap |
146 | 159 | dominant = mix(N, R, lerpFactor); |
147 | 160 |
|
148 | 161 | return dominant; |
149 | 162 | } |
150 | 163 |
|
151 | 164 | vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec, float nbMipMaps){ |
| 165 | + // The specular bake stores roughness quadratically across mip levels: |
| 166 | + // mipNorm = mip / (nbMipMaps - 1), roughness = mipNorm * mipNorm. |
| 167 | + // Runtime therefore samples with sqrt(roughness) to recover mipNorm. |
152 | 168 | float Lod = sqrt( Roughness ) * (nbMipMaps - 1.0); |
153 | 169 | vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz,Lod).rgb; |
154 | | - vec2 EnvBRDF = texture2D(integrateBRDF,vec2(Roughness, ndotv)).rg; |
| 170 | + vec2 EnvBRDF = texture2D(integrateBRDF,vec2(ndotv, Roughness)).rg; |
155 | 171 | return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y ); |
156 | 172 | } |
157 | 173 |
|
@@ -242,13 +258,10 @@ float renderProbe(vec3 viewDir, vec3 worldPos, vec3 normal, vec3 norm, float Rou |
242 | 258 | indirectSpecular *= vec3(horiz); |
243 | 259 | #endif |
244 | 260 |
|
245 | | - vec3 indirectLighting = (indirectDiffuse + indirectSpecular) * ao; |
| 261 | + float diffuseAO = clamp(ao.r, 0.0, 1.0); |
| 262 | + float specularAO = computeSpecularAO(diffuseAO, Roughness, ndotv); |
| 263 | + vec3 indirectLighting = indirectDiffuse * diffuseAO + indirectSpecular * specularAO; |
246 | 264 |
|
247 | 265 | color = indirectLighting * step( 0.0, probePos.w); |
248 | 266 | return ndf; |
249 | 267 | } |
250 | | - |
251 | | - |
252 | | - |
253 | | - |
254 | | - |
|
0 commit comments