Skip to content

Commit a8418f3

Browse files
authored
WebGPURenderer: Fix furnace test energy loss for intermediate metalness values (#32201)
* WebGPURenderer: Fix furnace test energy loss for intermediate metalness values. * Removed unused vec4 import from MeshStandardNodeMaterial * PhysicalLightingModel: Use specularColorBlended for iridescence baseF0. * Updated screenshots.
1 parent 473ac04 commit a8418f3

File tree

7 files changed

+51
-23
lines changed

7 files changed

+51
-23
lines changed
1.84 KB
Loading
2.77 KB
Loading
482 Bytes
Loading

src/materials/nodes/MeshPhysicalNodeMaterial.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, specularColor, specularF90, diffuseColor, metalness, roughness, anisotropy, alphaT, anisotropyT, anisotropyB, ior, transmission, thickness, attenuationDistance, attenuationColor, dispersion } from '../../nodes/core/PropertyNode.js';
1+
import { clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, specularColor, specularColorBlended, specularF90, diffuseColor, metalness, roughness, anisotropy, alphaT, anisotropyT, anisotropyB, ior, transmission, thickness, attenuationDistance, attenuationColor, dispersion } from '../../nodes/core/PropertyNode.js';
22
import { materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialSpecularIntensity, materialSpecularColor, materialAnisotropy, materialIOR, materialTransmission, materialThickness, materialAttenuationDistance, materialAttenuationColor, materialDispersion } from '../../nodes/accessors/MaterialNode.js';
33
import { float, vec2, vec3, If } from '../../nodes/tsl/TSLBase.js';
44
import getRoughness from '../../nodes/functions/material/getRoughness.js';
@@ -350,7 +350,8 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
350350
const iorNode = this.iorNode ? float( this.iorNode ) : materialIOR;
351351

352352
ior.assign( iorNode );
353-
specularColor.assign( mix( min( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( materialSpecularColor ), vec3( 1.0 ) ).mul( materialSpecularIntensity ), diffuseColor.rgb, metalness ) );
353+
specularColor.assign( min( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( materialSpecularColor ), vec3( 1.0 ) ).mul( materialSpecularIntensity ) );
354+
specularColorBlended.assign( mix( specularColor, diffuseColor.rgb, metalness ) );
354355
specularF90.assign( mix( materialSpecularIntensity, 1.0, metalness ) );
355356

356357
}

src/materials/nodes/MeshStandardNodeMaterial.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import NodeMaterial from './NodeMaterial.js';
2-
import { diffuseColor, metalness, roughness, specularColor, specularF90 } from '../../nodes/core/PropertyNode.js';
2+
import { diffuseColor, diffuseContribution, metalness, roughness, specularColor, specularColorBlended, specularF90 } from '../../nodes/core/PropertyNode.js';
33
import { mix } from '../../nodes/math/MathNode.js';
44
import { materialRoughness, materialMetalness } from '../../nodes/accessors/MaterialNode.js';
55
import getRoughness from '../../nodes/functions/material/getRoughness.js';
66
import PhysicalLightingModel from '../../nodes/functions/PhysicalLightingModel.js';
77
import EnvironmentNode from '../../nodes/lighting/EnvironmentNode.js';
8-
import { float, vec3, vec4 } from '../../nodes/tsl/TSLBase.js';
8+
import { float, vec3 } from '../../nodes/tsl/TSLBase.js';
99

1010
import { MeshStandardMaterial } from '../MeshStandardMaterial.js';
1111

@@ -135,7 +135,8 @@ class MeshStandardNodeMaterial extends NodeMaterial {
135135

136136
const specularColorNode = mix( vec3( 0.04 ), diffuseColor.rgb, metalness );
137137

138-
specularColor.assign( specularColorNode );
138+
specularColor.assign( vec3( 0.04 ) );
139+
specularColorBlended.assign( specularColorNode );
139140
specularF90.assign( 1.0 );
140141

141142
}
@@ -166,7 +167,7 @@ class MeshStandardNodeMaterial extends NodeMaterial {
166167

167168
// DIFFUSE COLOR
168169

169-
diffuseColor.assign( vec4( diffuseColor.rgb.mul( metalnessNode.oneMinus() ), diffuseColor.a ) );
170+
diffuseContribution.assign( diffuseColor.rgb.mul( metalnessNode.oneMinus() ) );
170171

171172
}
172173

src/nodes/core/PropertyNode.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ export const varyingProperty = ( type, name ) => nodeObject( new PropertyNode( t
134134
*/
135135
export const diffuseColor = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec4', 'DiffuseColor' );
136136

137+
/**
138+
* TSL object that represents the shader variable `DiffuseContribution`.
139+
*
140+
* @tsl
141+
* @type {PropertyNode<vec3>}
142+
*/
143+
export const diffuseContribution = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec3', 'DiffuseContribution' );
144+
137145
/**
138146
* TSL object that represents the shader variable `EmissiveColor`.
139147
*
@@ -254,6 +262,14 @@ export const anisotropyB = /*@__PURE__*/ nodeImmutable( PropertyNode, 'vec3', 'A
254262
*/
255263
export const specularColor = /*@__PURE__*/ nodeImmutable( PropertyNode, 'color', 'SpecularColor' );
256264

265+
/**
266+
* TSL object that represents the shader variable `SpecularColorBlended`.
267+
*
268+
* @tsl
269+
* @type {PropertyNode<color>}
270+
*/
271+
export const specularColorBlended = /*@__PURE__*/ nodeImmutable( PropertyNode, 'color', 'SpecularColorBlended' );
272+
257273
/**
258274
* TSL object that represents the shader variable `SpecularF90`.
259275
*

src/nodes/functions/PhysicalLightingModel.js

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Schlick_to_F0 from './BSDF/Schlick_to_F0.js';
88
import BRDF_Sheen from './BSDF/BRDF_Sheen.js';
99
import { LTC_Evaluate, LTC_Uv } from './BSDF/LTC.js';
1010
import LightingModel from '../core/LightingModel.js';
11-
import { diffuseColor, specularColor, specularF90, roughness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, ior, thickness, transmission, attenuationDistance, attenuationColor, dispersion } from '../core/PropertyNode.js';
11+
import { diffuseColor, diffuseContribution, specularColor, specularColorBlended, specularF90, roughness, metalness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, ior, thickness, transmission, attenuationDistance, attenuationColor, dispersion } from '../core/PropertyNode.js';
1212
import { normalView, clearcoatNormalView, normalWorld } from '../accessors/Normal.js';
1313
import { positionViewDirection, positionView, positionWorld } from '../accessors/Position.js';
1414
import { Fn, float, vec2, vec3, vec4, mat3, If } from '../tsl/TSLBase.js';
@@ -502,7 +502,7 @@ class PhysicalLightingModel extends LightingModel {
502502
eta2: iridescenceIOR,
503503
cosTheta1: dotNVi,
504504
thinFilmThickness: iridescenceThickness,
505-
baseF0: specularColor
505+
baseF0: specularColorBlended
506506
} );
507507

508508
this.iridescenceF0 = Schlick_to_F0( { f: this.iridescenceFresnel, f90: 1.0, dotVH: dotNVi } );
@@ -521,8 +521,8 @@ class PhysicalLightingModel extends LightingModel {
521521
n,
522522
v,
523523
roughness,
524-
diffuseColor,
525-
specularColor,
524+
diffuseContribution,
525+
specularColorBlended,
526526
specularF90, // specularF90
527527
position, // positionWorld
528528
modelWorldMatrix, // modelMatrix
@@ -549,13 +549,13 @@ class PhysicalLightingModel extends LightingModel {
549549
// Approximates multi-scattering in order to preserve energy.
550550
// http://www.jcgt.org/published/0008/01/03/
551551

552-
computeMultiscattering( singleScatter, multiScatter, specularF90 ) {
552+
computeMultiscattering( singleScatter, multiScatter, specularF90, f0 ) {
553553

554554
const dotNV = normalView.dot( positionViewDirection ).clamp(); // @ TODO: Move to core dotNV
555555

556556
const fab = DFGApprox( { roughness, dotNV } );
557557

558-
const Fr = this.iridescenceF0 ? iridescence.mix( specularColor, this.iridescenceF0 ) : specularColor;
558+
const Fr = this.iridescenceF0 ? iridescence.mix( f0, this.iridescenceF0 ) : f0;
559559

560560
const FssEss = Fr.mul( fab.x ).add( specularF90.mul( fab.y ) );
561561

@@ -596,9 +596,9 @@ class PhysicalLightingModel extends LightingModel {
596596

597597
}
598598

599-
reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
599+
reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseContribution } ) ) );
600600

601-
reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX_Multiscatter( { lightDirection, f0: specularColor, f90: 1, roughness, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy } ) ) );
601+
reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX_Multiscatter( { lightDirection, f0: specularColorBlended, f90: 1, roughness, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy } ) ) );
602602

603603
}
604604

@@ -633,11 +633,11 @@ class PhysicalLightingModel extends LightingModel {
633633

634634
// LTC Fresnel Approximation by Stephen Hill
635635
// http://blog.selfshadow.com/publications/s2016-advances/s2016_ltc_fresnel.pdf
636-
const fresnel = specularColor.mul( t2.x ).add( specularColor.oneMinus().mul( t2.y ) ).toVar();
636+
const fresnel = specularColorBlended.mul( t2.x ).add( specularColorBlended.oneMinus().mul( t2.y ) ).toVar();
637637

638638
reflectedLight.directSpecular.addAssign( lightColor.mul( fresnel ).mul( LTC_Evaluate( { N, V, P, mInv, p0, p1, p2, p3 } ) ) );
639639

640-
reflectedLight.directDiffuse.addAssign( lightColor.mul( diffuseColor ).mul( LTC_Evaluate( { N, V, P, mInv: mat3( 1, 0, 0, 0, 1, 0, 0, 0, 1 ), p0, p1, p2, p3 } ) ) );
640+
reflectedLight.directDiffuse.addAssign( lightColor.mul( diffuseContribution ).mul( LTC_Evaluate( { N, V, P, mInv: mat3( 1, 0, 0, 0, 1, 0, 0, 0, 1 ), p0, p1, p2, p3 } ) ) );
641641

642642
}
643643

@@ -663,7 +663,7 @@ class PhysicalLightingModel extends LightingModel {
663663

664664
const { irradiance, reflectedLight } = builder.context;
665665

666-
reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
666+
reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseContribution } ) ) );
667667

668668
}
669669

@@ -705,16 +705,26 @@ class PhysicalLightingModel extends LightingModel {
705705
}
706706

707707
// Both indirect specular and indirect diffuse light accumulate here
708+
// Compute multiscattering separately for dielectric and metallic, then mix
708709

709-
const singleScattering = vec3().toVar( 'singleScattering' );
710-
const multiScattering = vec3().toVar( 'multiScattering' );
711-
const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
710+
const singleScatteringDielectric = vec3().toVar( 'singleScatteringDielectric' );
711+
const multiScatteringDielectric = vec3().toVar( 'multiScatteringDielectric' );
712+
const singleScatteringMetallic = vec3().toVar( 'singleScatteringMetallic' );
713+
const multiScatteringMetallic = vec3().toVar( 'multiScatteringMetallic' );
714+
715+
this.computeMultiscattering( singleScatteringDielectric, multiScatteringDielectric, specularF90, specularColor );
716+
this.computeMultiscattering( singleScatteringMetallic, multiScatteringMetallic, specularF90, diffuseColor.rgb );
712717

713-
this.computeMultiscattering( singleScattering, multiScattering, specularF90 );
718+
// Mix based on metalness
719+
const singleScattering = mix( singleScatteringDielectric, singleScatteringMetallic, metalness );
720+
const multiScattering = mix( multiScatteringDielectric, multiScatteringMetallic, metalness );
714721

715-
const totalScattering = singleScattering.add( multiScattering );
722+
// Diffuse energy conservation uses dielectric path
723+
const totalScatteringDielectric = singleScatteringDielectric.add( multiScatteringDielectric );
716724

717-
const diffuse = diffuseColor.mul( totalScattering.r.max( totalScattering.g ).max( totalScattering.b ).oneMinus() );
725+
const diffuse = diffuseContribution.mul( totalScatteringDielectric.r.max( totalScatteringDielectric.g ).max( totalScatteringDielectric.b ).oneMinus() );
726+
727+
const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
718728

719729
reflectedLight.indirectSpecular.addAssign( radiance.mul( singleScattering ) );
720730
reflectedLight.indirectSpecular.addAssign( multiScattering.mul( cosineWeightedIrradiance ) );

0 commit comments

Comments
 (0)