@@ -119,8 +119,7 @@ float perceptualRoughnessToLod(float perceptualRoughness) {
119119 return frameUniforms.iblRoughnessOneLevel * perceptualRoughness * (2.0 - perceptualRoughness);
120120}
121121
122- vec3 prefilteredRadiance(const vec3 r, float perceptualRoughness) {
123- float lod = perceptualRoughnessToLod(perceptualRoughness);
122+ vec3 prefilteredRadiance(const vec3 r, float lod) {
124123 return decodeDataForIBL(textureLod(sampler0_iblSpecular, r, lod));
125124}
126125
@@ -391,7 +390,8 @@ void evaluateSheenIBL(const PixelParams pixel, float diffuseAO,
391390 vec3 reflectance = pixel.sheenDFG * pixel.sheenColor;
392391 reflectance *= specularAO(shading_NoV, diffuseAO, pixel.sheenRoughness, cache);
393392
394- Fr += reflectance * prefilteredRadiance(shading_reflected, pixel.sheenPerceptualRoughness);
393+ Fr += reflectance * prefilteredRadiance(shading_reflected,
394+ perceptualRoughnessToLod(pixel.sheenPerceptualRoughness));
395395#endif
396396#endif
397397}
@@ -422,7 +422,8 @@ void evaluateClearCoatIBL(const PixelParams pixel, float diffuseAO,
422422
423423 // TODO: Should we apply specularAO to the attenuation as well?
424424 float specularAO = specularAO(clearCoatNoV, diffuseAO, pixel.clearCoatRoughness, cache);
425- Fr += prefilteredRadiance(clearCoatR, pixel.clearCoatPerceptualRoughness) * (specularAO * Fc);
425+ Fr += prefilteredRadiance(clearCoatR,
426+ perceptualRoughnessToLod(pixel.clearCoatPerceptualRoughness)) * (specularAO * Fc);
426427 }
427428#endif
428429}
@@ -448,7 +449,7 @@ struct Refraction {
448449};
449450
450451void refractionSolidSphere(float etaIR, float etaRI, float thickness,
451- const vec3 n, vec3 r, out Refraction ray) {
452+ const vec3 n, vec3 r, out Refraction ray) {
452453 r = refract (r, n, etaIR);
453454 float NoR = dot (n, r);
454455 float d = thickness * - NoR;
@@ -459,7 +460,7 @@ void refractionSolidSphere(float etaIR, float etaRI, float thickness,
459460}
460461
461462void refractionSolidBox(float etaIR, float thickness,
462- const vec3 n, vec3 r, out Refraction ray) {
463+ const vec3 n, vec3 r, out Refraction ray) {
463464 vec3 rr = refract (r, n, etaIR);
464465 float NoR = dot (n, rr);
465466 float d = thickness / max (- NoR, 0.001 );
@@ -474,7 +475,7 @@ void refractionSolidBox(float etaIR, float thickness,
474475}
475476
476477void refractionThinSphere(float etaIR, float uThickness,
477- const vec3 n, vec3 r, out Refraction ray) {
478+ const vec3 n, vec3 r, out Refraction ray) {
478479 float d = 0.0 ;
479480#if defined(MATERIAL_HAS_MICRO_THICKNESS)
480481 // note: we need the refracted ray to calculate the distance traveled
@@ -490,90 +491,94 @@ void refractionThinSphere(float etaIR, float uThickness,
490491 ray.d = d;
491492}
492493
493- vec3 evaluateRefraction(
494- const PixelParams pixel,
495- const vec3 n0, vec3 E) {
494+ vec3 evaluateRefraction(const PixelParams pixel, const vec3 n0, const float lod,
495+ const float etaIR, const float etaRI) {
496496
497- #if REFRACTION_TYPE == REFRACTION_TYPE_THIN
498- // For thin surfaces, the light will bounce off at the second interface in the direction of
499- // the reflection, effectively adding to the specular, but this process will repeat itself.
500- // Each time the ray exits the surface on the front side after the first bounce,
501- // it's multiplied by E^2, and we get: E + E(1-E)^2 + E^3(1-E)^2 + ...
502- // This infinite series converges and is easy to simplify.
503- // Note: we calculate these bounces only on a single component,
504- // since it's a fairly subtle effect.
505- E *= 1.0 + pixel.transmission * (1.0 - E.g) / (1.0 + E.g);
506- #endif
507-
508- vec3 Ft;
497+ Refraction ray;
509498
510- #if defined(MATERIAL_HAS_DISPERSION) && ( REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
511- for ( int i = 0 ; i < 3 ; i ++ ) {
512- float etaIR = pixel.etaIR[i];
513- float etaRI = pixel.etaRI[i] ;
499+ #if REFRACTION_TYPE == REFRACTION_TYPE_SOLID
500+ refractionSolidSphere(etaIR, etaRI, pixel.thickness, n0, - shading_view, ray);
501+ #elif REFRACTION_TYPE == REFRACTION_TYPE_THIN
502+ refractionThinSphere(etaIR, pixel.uThickness, n0, - shading_view, ray) ;
514503#else
515- float etaIR = pixel.etaIR;
516- float etaRI = pixel.etaRI;
504+ # error invalid REFRACTION_TYPE
517505#endif
518506
519- Refraction ray;
520-
521- #if REFRACTION_TYPE == REFRACTION_TYPE_SOLID
522- refractionSolidSphere(etaIR, etaRI, pixel.thickness, n0, - shading_view, ray);
523- #elif REFRACTION_TYPE == REFRACTION_TYPE_THIN
524- refractionThinSphere(etaIR, pixel.uThickness, n0, - shading_view, ray);
507+ /* sample the cubemap or screen-space */
508+ #if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
509+ // when reading from the cubemap, we are not pre-exposed so we apply iblLuminance
510+ // which is not the case when we'll read from the screen-space buffer
511+ vec3 t = prefilteredRadiance(ray.direction, lod) * frameUniforms.iblLuminance;
525512#else
526- #error invalid REFRACTION_TYPE
513+ // compute the point where the ray exits the medium, if needed
514+ highp vec4 p = mulMat4x4Float3(getClipFromWorldMatrix(), ray.position);
515+ vec2 uv = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5 );
516+ vec3 t = textureLod(sampler0_ssr, vec3 (uv, 0.0 ), lod).rgb;
527517#endif
528518
529- // compute transmission T
519+ // apply absorption
530520#if defined(MATERIAL_HAS_ABSORPTION)
531- vec3 T = min (vec3 (1.0 ), exp (- pixel.absorption * ray.d));
521+ // compute transmission T
522+ vec3 T = saturate(exp (- pixel.absorption * ray.d));
523+ t *= T;
532524#endif
533525
534- // Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
535- // of 1.5 has full microfacet refraction
536- float perceptualRoughness = mix (pixel.perceptualRoughnessUnclamped, 0.0 ,
537- saturate(etaIR * 3.0 - 2.0 ));
538-
539- /* sample the cubemap or screen-space */
540- #if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
541- // when reading from the cubemap, we are not pre-exposed so we apply iblLuminance
542- // which is not the case when we'll read from the screen-space buffer
543- vec3 t = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance;
544- #else
545- // compute the point where the ray exits the medium, if needed
546- vec4 p = vec4 (getClipFromWorldMatrix() * vec4 (ray.position, 1.0 ));
547- p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5 );
526+ return t;
527+ }
548528
549- // distance to camera plane
550- const float invLog2sqrt5 = 0.8614 ;
551- float lod = max (0.0 , (2.0 * log2 (perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
552- vec3 t = textureLod(sampler0_ssr, vec3 (p.xy, 0.0 ), lod).rgb;
553- #endif
529+ vec3 evaluateRefraction(const PixelParams pixel, const vec3 n0, vec3 E) {
530+ vec3 Ft;
554531
555- // base color changes the amount of light passing through the boundary
556- t *= pixel.diffuseColor;
532+ // Note: We use the average IOR for the roughness lod calculation.
557533
558- // fresnel from the first interface
559- t *= 1.0 - E;
534+ // Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
535+ // of 1.5 has full microfacet refraction
536+ #if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
537+ float perceptualRoughness = mix (pixel.perceptualRoughnessUnclamped, 0.0 , saturate(pixel.etaIR[1 ] * 3.0 - 2.0 ));
538+ #else
539+ float perceptualRoughness = mix (pixel.perceptualRoughnessUnclamped, 0.0 , saturate(pixel.etaIR * 3.0 - 2.0 ));
540+ #endif
560541
561- // apply absorption
562- #if defined(MATERIAL_HAS_ABSORPTION)
563- t *= T;
542+ #if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
543+ float lod = perceptualRoughnessToLod(perceptualRoughness);
544+ #else
545+ // distance to camera plane
546+ const float invLog2sqrt5 = 0.8614 ;
547+ float lod = max (0.0 , (2.0 * log2 (perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
564548#endif
565549
566550#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
551+ for (int i = 0 ; i < 3 ; i++ ) {
552+ vec3 t = evaluateRefraction(pixel, n0, lod, pixel.etaIR[i], pixel.etaRI[i]);
567553 Ft[i] = t[i];
568554 }
569555#else
570- Ft = t;
556+ Ft = evaluateRefraction(pixel, n0, lod, pixel.etaIR, pixel.etaRI);
557+ #endif
558+
559+
560+ #if REFRACTION_TYPE == REFRACTION_TYPE_THIN
561+ // For thin surfaces, the light will bounce off at the second interface in the direction of
562+ // the reflection, effectively adding to the specular, but this process will repeat itself.
563+ // Each time the ray exits the surface on the front side after the first bounce,
564+ // it's multiplied by E^2, and we get: E + E(1-E)^2 + E^3(1-E)^2 + ...
565+ // This infinite series converges and is easy to simplify.
566+ // Note: we calculate these bounces only on a single component,
567+ // since it's a fairly subtle effect.
568+ E *= 1.0 + pixel.transmission * (1.0 - E.g) / (1.0 + E.g);
571569#endif
572570
571+ // fresnel from the first interface
572+ Ft *= 1.0 - E;
573+
574+ // base color changes the amount of light passing through the boundary
575+ Ft *= pixel.diffuseColor;
576+
573577 return Ft;
574578}
575579#endif
576580
581+
577582void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout vec3 color) {
578583 // specular layer
579584 vec3 Fr = vec3 (0.0 );
@@ -615,7 +620,7 @@ void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout v
615620 vec3 E = specularDFG(pixel);
616621 if (ssrFr.a < 1.0 ) { // prevent reading the IBL if possible
617622 vec3 r = getReflectedVector(pixel, shading_normal);
618- Fr = E * prefilteredRadiance(r, pixel.perceptualRoughness);
623+ Fr = E * prefilteredRadiance(r, perceptualRoughnessToLod( pixel.perceptualRoughness) );
619624 }
620625#elif IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING
621626 vec3 E = vec3 (0.0 ); // TODO: fix for importance sampling
0 commit comments