|
@@ -1,13 +1,17 @@
|
|
import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
|
|
import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
|
|
import BRDF_GGX from './BSDF/BRDF_GGX.js';
|
|
import BRDF_GGX from './BSDF/BRDF_GGX.js';
|
|
import DFGApprox from './BSDF/DFGApprox.js';
|
|
import DFGApprox from './BSDF/DFGApprox.js';
|
|
|
|
+import EnvironmentBRDF from './BSDF/EnvironmentBRDF.js';
|
|
|
|
+import F_Schlick from './BSDF/F_Schlick.js';
|
|
import { lightingModel } from '../core/LightingModel.js';
|
|
import { lightingModel } from '../core/LightingModel.js';
|
|
-import { temp } from '../core/VarNode.js';
|
|
|
|
-import { diffuseColor, specularColor, roughness } from '../core/PropertyNode.js';
|
|
|
|
-import { transformedNormalView } from '../accessors/NormalNode.js';
|
|
|
|
|
|
+import { diffuseColor, specularColor, roughness, clearcoat, clearcoatRoughness } from '../core/PropertyNode.js';
|
|
|
|
+import { transformedNormalView, transformedClearcoatNormalView } from '../accessors/NormalNode.js';
|
|
import { positionViewDirection } from '../accessors/PositionNode.js';
|
|
import { positionViewDirection } from '../accessors/PositionNode.js';
|
|
import { ShaderNode, float, vec3 } from '../shadernode/ShaderNode.js';
|
|
import { ShaderNode, float, vec3 } from '../shadernode/ShaderNode.js';
|
|
|
|
|
|
|
|
+const clearcoatF0 = vec3( 0.04 );
|
|
|
|
+const clearcoatF90 = vec3( 1 );
|
|
|
|
+
|
|
// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
|
|
// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
|
|
// Approximates multiscattering in order to preserve energy.
|
|
// Approximates multiscattering in order to preserve energy.
|
|
// http://www.jcgt.org/published/0008/01/03/
|
|
// http://www.jcgt.org/published/0008/01/03/
|
|
@@ -28,14 +32,53 @@ const computeMultiscattering = ( singleScatter, multiScatter, specularF90 = floa
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
-const RE_IndirectSpecular_Physical = new ShaderNode( ( inputs ) => {
|
|
|
|
|
|
+const LM_Init = new ShaderNode( ( context, stack, builder ) => {
|
|
|
|
+
|
|
|
|
+ if ( builder.includes( clearcoat ) ) {
|
|
|
|
+
|
|
|
|
+ context.clearcoatRadiance = vec3().temp();
|
|
|
|
+ context.reflectedLight.clearcoatSpecular = vec3().temp();
|
|
|
|
+
|
|
|
|
+ const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
|
|
|
|
+
|
|
|
|
+ const Fcc = F_Schlick.call( {
|
|
|
|
+ dotVH: dotNVcc,
|
|
|
|
+ f0: clearcoatF0,
|
|
|
|
+ f90: clearcoatF90
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ const outgoingLight = context.reflectedLight.total;
|
|
|
|
+ const clearcoatLight = outgoingLight.mul( clearcoat.mul( Fcc ).oneMinus() ).add( context.reflectedLight.clearcoatSpecular.mul( clearcoat ) );
|
|
|
|
+
|
|
|
|
+ outgoingLight.assign( clearcoatLight );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+} );
|
|
|
|
+
|
|
|
|
+const RE_IndirectSpecular_Physical = new ShaderNode( ( context ) => {
|
|
|
|
|
|
- const { radiance, iblIrradiance, reflectedLight } = inputs;
|
|
|
|
|
|
+ const { radiance, iblIrradiance, reflectedLight } = context;
|
|
|
|
+
|
|
|
|
+ if ( reflectedLight.clearcoatSpecular ) {
|
|
|
|
+
|
|
|
|
+ const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
|
|
|
|
+
|
|
|
|
+ const clearcoatEnv = EnvironmentBRDF.call( {
|
|
|
|
+ dotNV: dotNVcc,
|
|
|
|
+ specularColor: clearcoatF0,
|
|
|
|
+ specularF90: clearcoatF90,
|
|
|
|
+ roughness: clearcoatRoughness
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ reflectedLight.clearcoatSpecular.addAssign( context.clearcoatRadiance.mul( clearcoatEnv ) );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
// Both indirect specular and indirect diffuse light accumulate here
|
|
// Both indirect specular and indirect diffuse light accumulate here
|
|
|
|
|
|
- const singleScattering = temp( vec3() );
|
|
|
|
- const multiScattering = temp( vec3() );
|
|
|
|
|
|
+ const singleScattering = vec3().temp();
|
|
|
|
+ const multiScattering = vec3().temp();
|
|
const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
|
|
const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
|
|
|
|
|
|
computeMultiscattering( singleScattering, multiScattering );
|
|
computeMultiscattering( singleScattering, multiScattering );
|
|
@@ -51,9 +94,9 @@ const RE_IndirectSpecular_Physical = new ShaderNode( ( inputs ) => {
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
-const RE_IndirectDiffuse_Physical = new ShaderNode( ( inputs ) => {
|
|
|
|
|
|
+const RE_IndirectDiffuse_Physical = new ShaderNode( ( context ) => {
|
|
|
|
|
|
- const { irradiance, reflectedLight } = inputs;
|
|
|
|
|
|
+ const { irradiance, reflectedLight } = context;
|
|
|
|
|
|
reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor } ) ) );
|
|
reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor } ) ) );
|
|
|
|
|
|
@@ -66,15 +109,26 @@ const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
|
|
const dotNL = transformedNormalView.dot( lightDirection ).clamp();
|
|
const dotNL = transformedNormalView.dot( lightDirection ).clamp();
|
|
const irradiance = dotNL.mul( lightColor );
|
|
const irradiance = dotNL.mul( lightColor );
|
|
|
|
|
|
|
|
+ if ( reflectedLight.clearcoatSpecular ) {
|
|
|
|
+
|
|
|
|
+ const dotNLcc = transformedClearcoatNormalView.dot( lightDirection ).clamp();
|
|
|
|
+ const ccIrradiance = dotNLcc.mul( lightColor );
|
|
|
|
+
|
|
|
|
+ reflectedLight.clearcoatSpecular.addAssign( ccIrradiance.mul( BRDF_GGX.call( { lightDirection, f0: clearcoatF0, f90: clearcoatF90, roughness: clearcoatRoughness, normalView: transformedClearcoatNormalView } ) ) );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor: diffuseColor.rgb } ) ) );
|
|
reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor: diffuseColor.rgb } ) ) );
|
|
|
|
|
|
reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX.call( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
|
|
reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX.call( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
-const RE_AmbientOcclusion_Physical = new ShaderNode( ( { ambientOcclusion, reflectedLight } ) => {
|
|
|
|
|
|
+const RE_AmbientOcclusion_Physical = new ShaderNode( ( context ) => {
|
|
|
|
+
|
|
|
|
+ const { ambientOcclusion, reflectedLight } = context;
|
|
|
|
|
|
- const dotNV = transformedNormalView.dot( positionViewDirection ).clamp();
|
|
|
|
|
|
+ const dotNV = transformedNormalView.dot( positionViewDirection ).clamp(); // @ TODO: Move to core dotNV
|
|
|
|
|
|
const aoNV = dotNV.add( ambientOcclusion );
|
|
const aoNV = dotNV.add( ambientOcclusion );
|
|
const aoExp = roughness.mul( - 16.0 ).oneMinus().negate().exp2();
|
|
const aoExp = roughness.mul( - 16.0 ).oneMinus().negate().exp2();
|
|
@@ -88,6 +142,6 @@ const RE_AmbientOcclusion_Physical = new ShaderNode( ( { ambientOcclusion, refle
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
-const physicalLightingModel = lightingModel( RE_Direct_Physical, RE_IndirectDiffuse_Physical, RE_IndirectSpecular_Physical, RE_AmbientOcclusion_Physical );
|
|
|
|
|
|
+const physicalLightingModel = lightingModel( LM_Init, RE_Direct_Physical, RE_IndirectDiffuse_Physical, RE_IndirectSpecular_Physical, RE_AmbientOcclusion_Physical );
|
|
|
|
|
|
export default physicalLightingModel;
|
|
export default physicalLightingModel;
|