UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

746 lines (690 loc) 27.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _BRDF_Lambert = _interopRequireDefault(require("./BSDF/BRDF_Lambert.js")); var _BRDF_GGX = _interopRequireDefault(require("./BSDF/BRDF_GGX.js")); var _DFGApprox = _interopRequireDefault(require("./BSDF/DFGApprox.js")); var _EnvironmentBRDF = _interopRequireDefault(require("./BSDF/EnvironmentBRDF.js")); var _F_Schlick = _interopRequireDefault(require("./BSDF/F_Schlick.js")); var _Schlick_to_F = _interopRequireDefault(require("./BSDF/Schlick_to_F0.js")); var _BRDF_Sheen = _interopRequireDefault(require("./BSDF/BRDF_Sheen.js")); var _LTC = require("./BSDF/LTC.js"); var _LightingModel = _interopRequireDefault(require("../core/LightingModel.js")); var _PropertyNode = require("../core/PropertyNode.js"); var _Normal = require("../accessors/Normal.js"); var _Position = require("../accessors/Position.js"); var _TSLBase = require("../tsl/TSLBase.js"); var _ConditionalNode = require("../math/ConditionalNode.js"); var _MathNode = require("../math/MathNode.js"); var _OperatorNode = require("../math/OperatorNode.js"); var _Camera = require("../accessors/Camera.js"); var _ModelNode = require("../accessors/ModelNode.js"); var _ScreenNode = require("../display/ScreenNode.js"); var _ViewportTextureNode = require("../display/ViewportTextureNode.js"); var _TextureBicubic = require("../accessors/TextureBicubic.js"); var _LoopNode = require("../utils/LoopNode.js"); var _constants = require("../../constants.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // // Transmission // const getVolumeTransmissionRay = /*@__PURE__*/(0, _TSLBase.Fn)(([n, v, thickness, ior, modelMatrix]) => { // Direction of refracted light. const refractionVector = (0, _TSLBase.vec3)((0, _MathNode.refract)(v.negate(), (0, _MathNode.normalize)(n), (0, _OperatorNode.div)(1.0, ior))); // Compute rotation-independent scaling of the model matrix. const modelScale = (0, _TSLBase.vec3)((0, _MathNode.length)(modelMatrix[0].xyz), (0, _MathNode.length)(modelMatrix[1].xyz), (0, _MathNode.length)(modelMatrix[2].xyz)); // The thickness is specified in local space. return (0, _MathNode.normalize)(refractionVector).mul(thickness.mul(modelScale)); }).setLayout({ name: 'getVolumeTransmissionRay', type: 'vec3', inputs: [{ name: 'n', type: 'vec3' }, { name: 'v', type: 'vec3' }, { name: 'thickness', type: 'float' }, { name: 'ior', type: 'float' }, { name: 'modelMatrix', type: 'mat4' }] }); const applyIorToRoughness = /*@__PURE__*/(0, _TSLBase.Fn)(([roughness, ior]) => { // Scale roughness with IOR so that an IOR of 1.0 results in no microfacet refraction and // an IOR of 1.5 results in the default amount of microfacet refraction. return roughness.mul((0, _MathNode.clamp)(ior.mul(2.0).sub(2.0), 0.0, 1.0)); }).setLayout({ name: 'applyIorToRoughness', type: 'float', inputs: [{ name: 'roughness', type: 'float' }, { name: 'ior', type: 'float' }] }); const viewportBackSideTexture = /*@__PURE__*/(0, _ViewportTextureNode.viewportMipTexture)(); const viewportFrontSideTexture = /*@__PURE__*/(0, _ViewportTextureNode.viewportMipTexture)(); const getTransmissionSample = /*@__PURE__*/(0, _TSLBase.Fn)(([fragCoord, roughness, ior], { material }) => { const vTexture = material.side === _constants.BackSide ? viewportBackSideTexture : viewportFrontSideTexture; const transmissionSample = vTexture.sample(fragCoord); //const transmissionSample = viewportMipTexture( fragCoord ); const lod = (0, _MathNode.log2)(_ScreenNode.screenSize.x).mul(applyIorToRoughness(roughness, ior)); return (0, _TextureBicubic.textureBicubic)(transmissionSample, lod); }); const volumeAttenuation = /*@__PURE__*/(0, _TSLBase.Fn)(([transmissionDistance, attenuationColor, attenuationDistance]) => { (0, _TSLBase.If)(attenuationDistance.notEqual(0), () => { // Compute light attenuation using Beer's law. const attenuationCoefficient = (0, _MathNode.log)(attenuationColor).negate().div(attenuationDistance); const transmittance = (0, _MathNode.exp)(attenuationCoefficient.negate().mul(transmissionDistance)); return transmittance; }); // Attenuation distance is +∞, i.e. the transmitted color is not attenuated at all. return (0, _TSLBase.vec3)(1.0); }).setLayout({ name: 'volumeAttenuation', type: 'vec3', inputs: [{ name: 'transmissionDistance', type: 'float' }, { name: 'attenuationColor', type: 'vec3' }, { name: 'attenuationDistance', type: 'float' }] }); const getIBLVolumeRefraction = /*@__PURE__*/(0, _TSLBase.Fn)(([n, v, roughness, diffuseColor, specularColor, specularF90, position, modelMatrix, viewMatrix, projMatrix, ior, thickness, attenuationColor, attenuationDistance, dispersion]) => { let transmittedLight, transmittance; if (dispersion) { transmittedLight = (0, _TSLBase.vec4)().toVar(); transmittance = (0, _TSLBase.vec3)().toVar(); const halfSpread = ior.sub(1.0).mul(dispersion.mul(0.025)); const iors = (0, _TSLBase.vec3)(ior.sub(halfSpread), ior, ior.add(halfSpread)); (0, _LoopNode.Loop)({ start: 0, end: 3 }, ({ i }) => { const ior = iors.element(i); const transmissionRay = getVolumeTransmissionRay(n, v, thickness, ior, modelMatrix); const refractedRayExit = position.add(transmissionRay); // Project refracted vector on the framebuffer, while mapping to normalized device coordinates. const ndcPos = projMatrix.mul(viewMatrix.mul((0, _TSLBase.vec4)(refractedRayExit, 1.0))); const refractionCoords = (0, _TSLBase.vec2)(ndcPos.xy.div(ndcPos.w)).toVar(); refractionCoords.addAssign(1.0); refractionCoords.divAssign(2.0); refractionCoords.assign((0, _TSLBase.vec2)(refractionCoords.x, refractionCoords.y.oneMinus())); // webgpu // Sample framebuffer to get pixel the refracted ray hits. const transmissionSample = getTransmissionSample(refractionCoords, roughness, ior); transmittedLight.element(i).assign(transmissionSample.element(i)); transmittedLight.a.addAssign(transmissionSample.a); transmittance.element(i).assign(diffuseColor.element(i).mul(volumeAttenuation((0, _MathNode.length)(transmissionRay), attenuationColor, attenuationDistance).element(i))); }); transmittedLight.a.divAssign(3.0); } else { const transmissionRay = getVolumeTransmissionRay(n, v, thickness, ior, modelMatrix); const refractedRayExit = position.add(transmissionRay); // Project refracted vector on the framebuffer, while mapping to normalized device coordinates. const ndcPos = projMatrix.mul(viewMatrix.mul((0, _TSLBase.vec4)(refractedRayExit, 1.0))); const refractionCoords = (0, _TSLBase.vec2)(ndcPos.xy.div(ndcPos.w)).toVar(); refractionCoords.addAssign(1.0); refractionCoords.divAssign(2.0); refractionCoords.assign((0, _TSLBase.vec2)(refractionCoords.x, refractionCoords.y.oneMinus())); // webgpu // Sample framebuffer to get pixel the refracted ray hits. transmittedLight = getTransmissionSample(refractionCoords, roughness, ior); transmittance = diffuseColor.mul(volumeAttenuation((0, _MathNode.length)(transmissionRay), attenuationColor, attenuationDistance)); } const attenuatedColor = transmittance.rgb.mul(transmittedLight.rgb); const dotNV = n.dot(v).clamp(); // Get the specular component. const F = (0, _TSLBase.vec3)((0, _EnvironmentBRDF.default)({ // n, v, specularColor, specularF90, roughness dotNV, specularColor, specularF90, roughness })); // As less light is transmitted, the opacity should be increased. This simple approximation does a decent job // of modulating a CSS background, and has no effect when the buffer is opaque, due to a solid object or clear color. const transmittanceFactor = transmittance.r.add(transmittance.g, transmittance.b).div(3.0); return (0, _TSLBase.vec4)(F.oneMinus().mul(attenuatedColor), transmittedLight.a.oneMinus().mul(transmittanceFactor).oneMinus()); }); // // Iridescence // // XYZ to linear-sRGB color space const XYZ_TO_REC709 = /*@__PURE__*/(0, _TSLBase.mat3)(3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, -0.4985314, 0.0415560, 1.0572252); // Assume air interface for top // Note: We don't handle the case fresnel0 == 1 const Fresnel0ToIor = fresnel0 => { const sqrtF0 = fresnel0.sqrt(); return (0, _TSLBase.vec3)(1.0).add(sqrtF0).div((0, _TSLBase.vec3)(1.0).sub(sqrtF0)); }; // ior is a value between 1.0 and 3.0. 1.0 is air interface const IorToFresnel0 = (transmittedIor, incidentIor) => { return transmittedIor.sub(incidentIor).div(transmittedIor.add(incidentIor)).pow2(); }; // Fresnel equations for dielectric/dielectric interfaces. // Ref: https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html // Evaluation XYZ sensitivity curves in Fourier space const evalSensitivity = (OPD, shift) => { const phase = OPD.mul(2.0 * Math.PI * 1.0e-9); const val = (0, _TSLBase.vec3)(5.4856e-13, 4.4201e-13, 5.2481e-13); const pos = (0, _TSLBase.vec3)(1.6810e+06, 1.7953e+06, 2.2084e+06); const VAR = (0, _TSLBase.vec3)(4.3278e+09, 9.3046e+09, 6.6121e+09); const x = (0, _TSLBase.float)(9.7470e-14 * Math.sqrt(2.0 * Math.PI * 4.5282e+09)).mul(phase.mul(2.2399e+06).add(shift.x).cos()).mul(phase.pow2().mul(-4.5282e+09).exp()); let xyz = val.mul(VAR.mul(2.0 * Math.PI).sqrt()).mul(pos.mul(phase).add(shift).cos()).mul(phase.pow2().negate().mul(VAR).exp()); xyz = (0, _TSLBase.vec3)(xyz.x.add(x), xyz.y, xyz.z).div(1.0685e-7); const rgb = XYZ_TO_REC709.mul(xyz); return rgb; }; const evalIridescence = /*@__PURE__*/(0, _TSLBase.Fn)(({ outsideIOR, eta2, cosTheta1, thinFilmThickness, baseF0 }) => { // Force iridescenceIOR -> outsideIOR when thinFilmThickness -> 0.0 const iridescenceIOR = (0, _MathNode.mix)(outsideIOR, eta2, (0, _MathNode.smoothstep)(0.0, 0.03, thinFilmThickness)); // Evaluate the cosTheta on the base layer (Snell law) const sinTheta2Sq = outsideIOR.div(iridescenceIOR).pow2().mul(cosTheta1.pow2().oneMinus()); // Handle TIR: const cosTheta2Sq = sinTheta2Sq.oneMinus(); (0, _TSLBase.If)(cosTheta2Sq.lessThan(0), () => { return (0, _TSLBase.vec3)(1.0); }); const cosTheta2 = cosTheta2Sq.sqrt(); // First interface const R0 = IorToFresnel0(iridescenceIOR, outsideIOR); const R12 = (0, _F_Schlick.default)({ f0: R0, f90: 1.0, dotVH: cosTheta1 }); //const R21 = R12; const T121 = R12.oneMinus(); const phi12 = iridescenceIOR.lessThan(outsideIOR).select(Math.PI, 0.0); const phi21 = (0, _TSLBase.float)(Math.PI).sub(phi12); // Second interface const baseIOR = Fresnel0ToIor(baseF0.clamp(0.0, 0.9999)); // guard against 1.0 const R1 = IorToFresnel0(baseIOR, iridescenceIOR.toVec3()); const R23 = (0, _F_Schlick.default)({ f0: R1, f90: 1.0, dotVH: cosTheta2 }); const phi23 = (0, _TSLBase.vec3)(baseIOR.x.lessThan(iridescenceIOR).select(Math.PI, 0.0), baseIOR.y.lessThan(iridescenceIOR).select(Math.PI, 0.0), baseIOR.z.lessThan(iridescenceIOR).select(Math.PI, 0.0)); // Phase shift const OPD = iridescenceIOR.mul(thinFilmThickness, cosTheta2, 2.0); const phi = (0, _TSLBase.vec3)(phi21).add(phi23); // Compound terms const R123 = R12.mul(R23).clamp(1e-5, 0.9999); const r123 = R123.sqrt(); const Rs = T121.pow2().mul(R23).div((0, _TSLBase.vec3)(1.0).sub(R123)); // Reflectance term for m = 0 (DC term amplitude) const C0 = R12.add(Rs); const I = C0.toVar(); // Reflectance term for m > 0 (pairs of diracs) const Cm = Rs.sub(T121).toVar(); (0, _LoopNode.Loop)({ start: 1, end: 2, condition: '<=', name: 'm' }, ({ m }) => { Cm.mulAssign(r123); const Sm = evalSensitivity((0, _TSLBase.float)(m).mul(OPD), (0, _TSLBase.float)(m).mul(phi)).mul(2.0); I.addAssign(Cm.mul(Sm)); }); // Since out of gamut colors might be produced, negative color values are clamped to 0. return I.max((0, _TSLBase.vec3)(0.0)); }).setLayout({ name: 'evalIridescence', type: 'vec3', inputs: [{ name: 'outsideIOR', type: 'float' }, { name: 'eta2', type: 'float' }, { name: 'cosTheta1', type: 'float' }, { name: 'thinFilmThickness', type: 'float' }, { name: 'baseF0', type: 'vec3' }] }); // // Sheen // // This is a curve-fit approximation to the "Charlie sheen" BRDF integrated over the hemisphere from // Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF". The analysis can be found // in the Sheen section of https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing const IBLSheenBRDF = /*@__PURE__*/(0, _TSLBase.Fn)(({ normal, viewDir, roughness }) => { const dotNV = normal.dot(viewDir).saturate(); const r2 = roughness.pow2(); const a = (0, _ConditionalNode.select)(roughness.lessThan(0.25), (0, _TSLBase.float)(-339.2).mul(r2).add((0, _TSLBase.float)(161.4).mul(roughness)).sub(25.9), (0, _TSLBase.float)(-8.48).mul(r2).add((0, _TSLBase.float)(14.3).mul(roughness)).sub(9.95)); const b = (0, _ConditionalNode.select)(roughness.lessThan(0.25), (0, _TSLBase.float)(44.0).mul(r2).sub((0, _TSLBase.float)(23.7).mul(roughness)).add(3.26), (0, _TSLBase.float)(1.97).mul(r2).sub((0, _TSLBase.float)(3.27).mul(roughness)).add(0.72)); const DG = (0, _ConditionalNode.select)(roughness.lessThan(0.25), 0.0, (0, _TSLBase.float)(0.1).mul(roughness).sub(0.025)).add(a.mul(dotNV).add(b).exp()); return DG.mul(1.0 / Math.PI).saturate(); }); const clearcoatF0 = (0, _TSLBase.vec3)(0.04); const clearcoatF90 = (0, _TSLBase.float)(1); /** * Represents the lighting model for a PBR material. * * @augments LightingModel */ class PhysicalLightingModel extends _LightingModel.default { /** * Constructs a new physical lighting model. * * @param {boolean} [clearcoat=false] - Whether clearcoat is supported or not. * @param {boolean} [sheen=false] - Whether sheen is supported or not. * @param {boolean} [iridescence=false] - Whether iridescence is supported or not. * @param {boolean} [anisotropy=false] - Whether anisotropy is supported or not. * @param {boolean} [transmission=false] - Whether transmission is supported or not. * @param {boolean} [dispersion=false] - Whether dispersion is supported or not. */ constructor(clearcoat = false, sheen = false, iridescence = false, anisotropy = false, transmission = false, dispersion = false) { super(); /** * Whether clearcoat is supported or not. * * @type {boolean} * @default false */ this.clearcoat = clearcoat; /** * Whether sheen is supported or not. * * @type {boolean} * @default false */ this.sheen = sheen; /** * Whether iridescence is supported or not. * * @type {boolean} * @default false */ this.iridescence = iridescence; /** * Whether anisotropy is supported or not. * * @type {boolean} * @default false */ this.anisotropy = anisotropy; /** * Whether transmission is supported or not. * * @type {boolean} * @default false */ this.transmission = transmission; /** * Whether dispersion is supported or not. * * @type {boolean} * @default false */ this.dispersion = dispersion; /** * The clear coat radiance. * * @type {?Node} * @default null */ this.clearcoatRadiance = null; /** * The clear coat specular direct. * * @type {?Node} * @default null */ this.clearcoatSpecularDirect = null; /** * The clear coat specular indirect. * * @type {?Node} * @default null */ this.clearcoatSpecularIndirect = null; /** * The sheen specular direct. * * @type {?Node} * @default null */ this.sheenSpecularDirect = null; /** * The sheen specular indirect. * * @type {?Node} * @default null */ this.sheenSpecularIndirect = null; /** * The iridescence Fresnel. * * @type {?Node} * @default null */ this.iridescenceFresnel = null; /** * The iridescence F0. * * @type {?Node} * @default null */ this.iridescenceF0 = null; } /** * Depending on what features are requested, the method prepares certain node variables * which are later used for lighting computations. * * @param {NodeBuilder} builder - The current node builder. */ start(builder) { if (this.clearcoat === true) { this.clearcoatRadiance = (0, _TSLBase.vec3)().toVar('clearcoatRadiance'); this.clearcoatSpecularDirect = (0, _TSLBase.vec3)().toVar('clearcoatSpecularDirect'); this.clearcoatSpecularIndirect = (0, _TSLBase.vec3)().toVar('clearcoatSpecularIndirect'); } if (this.sheen === true) { this.sheenSpecularDirect = (0, _TSLBase.vec3)().toVar('sheenSpecularDirect'); this.sheenSpecularIndirect = (0, _TSLBase.vec3)().toVar('sheenSpecularIndirect'); } if (this.iridescence === true) { const dotNVi = _Normal.transformedNormalView.dot(_Position.positionViewDirection).clamp(); this.iridescenceFresnel = evalIridescence({ outsideIOR: (0, _TSLBase.float)(1.0), eta2: _PropertyNode.iridescenceIOR, cosTheta1: dotNVi, thinFilmThickness: _PropertyNode.iridescenceThickness, baseF0: _PropertyNode.specularColor }); this.iridescenceF0 = (0, _Schlick_to_F.default)({ f: this.iridescenceFresnel, f90: 1.0, dotVH: dotNVi }); } if (this.transmission === true) { const position = _Position.positionWorld; const v = _Camera.cameraPosition.sub(_Position.positionWorld).normalize(); // TODO: Create Node for this, same issue in MaterialX const n = _Normal.transformedNormalWorld; const context = builder.context; context.backdrop = getIBLVolumeRefraction(n, v, _PropertyNode.roughness, _PropertyNode.diffuseColor, _PropertyNode.specularColor, _PropertyNode.specularF90, // specularF90 position, // positionWorld _ModelNode.modelWorldMatrix, // modelMatrix _Camera.cameraViewMatrix, // viewMatrix _Camera.cameraProjectionMatrix, // projMatrix _PropertyNode.ior, _PropertyNode.thickness, _PropertyNode.attenuationColor, _PropertyNode.attenuationDistance, this.dispersion ? _PropertyNode.dispersion : null); context.backdropAlpha = _PropertyNode.transmission; _PropertyNode.diffuseColor.a.mulAssign((0, _MathNode.mix)(1, context.backdrop.a, _PropertyNode.transmission)); } super.start(builder); } // Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting" // Approximates multi-scattering in order to preserve energy. // http://www.jcgt.org/published/0008/01/03/ computeMultiscattering(singleScatter, multiScatter, specularF90) { const dotNV = _Normal.transformedNormalView.dot(_Position.positionViewDirection).clamp(); // @ TODO: Move to core dotNV const fab = (0, _DFGApprox.default)({ roughness: _PropertyNode.roughness, dotNV }); const Fr = this.iridescenceF0 ? _PropertyNode.iridescence.mix(_PropertyNode.specularColor, this.iridescenceF0) : _PropertyNode.specularColor; const FssEss = Fr.mul(fab.x).add(specularF90.mul(fab.y)); const Ess = fab.x.add(fab.y); const Ems = Ess.oneMinus(); const Favg = _PropertyNode.specularColor.add(_PropertyNode.specularColor.oneMinus().mul(0.047619)); // 1/21 const Fms = FssEss.mul(Favg).div(Ems.mul(Favg).oneMinus()); singleScatter.addAssign(FssEss); multiScatter.addAssign(Fms.mul(Ems)); } /** * Implements the direct light. * * @param {Object} lightData - The light data. * @param {NodeBuilder} builder - The current node builder. */ direct({ lightDirection, lightColor, reflectedLight }) { const dotNL = _Normal.transformedNormalView.dot(lightDirection).clamp(); const irradiance = dotNL.mul(lightColor); if (this.sheen === true) { this.sheenSpecularDirect.addAssign(irradiance.mul((0, _BRDF_Sheen.default)({ lightDirection }))); } if (this.clearcoat === true) { const dotNLcc = _Normal.transformedClearcoatNormalView.dot(lightDirection).clamp(); const ccIrradiance = dotNLcc.mul(lightColor); this.clearcoatSpecularDirect.addAssign(ccIrradiance.mul((0, _BRDF_GGX.default)({ lightDirection, f0: clearcoatF0, f90: clearcoatF90, roughness: _PropertyNode.clearcoatRoughness, normalView: _Normal.transformedClearcoatNormalView }))); } reflectedLight.directDiffuse.addAssign(irradiance.mul((0, _BRDF_Lambert.default)({ diffuseColor: _PropertyNode.diffuseColor.rgb }))); reflectedLight.directSpecular.addAssign(irradiance.mul((0, _BRDF_GGX.default)({ lightDirection, f0: _PropertyNode.specularColor, f90: 1, roughness: _PropertyNode.roughness, iridescence: this.iridescence, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy }))); } /** * This method is intended for implementing the direct light term for * rect area light nodes. * * @param {Object} input - The input data. * @param {NodeBuilder} builder - The current node builder. */ directRectArea({ lightColor, lightPosition, halfWidth, halfHeight, reflectedLight, ltc_1, ltc_2 }) { const p0 = lightPosition.add(halfWidth).sub(halfHeight); // counterclockwise; light shines in local neg z direction const p1 = lightPosition.sub(halfWidth).sub(halfHeight); const p2 = lightPosition.sub(halfWidth).add(halfHeight); const p3 = lightPosition.add(halfWidth).add(halfHeight); const N = _Normal.transformedNormalView; const V = _Position.positionViewDirection; const P = _Position.positionView.toVar(); const uv = (0, _LTC.LTC_Uv)({ N, V, roughness: _PropertyNode.roughness }); const t1 = ltc_1.sample(uv).toVar(); const t2 = ltc_2.sample(uv).toVar(); const mInv = (0, _TSLBase.mat3)((0, _TSLBase.vec3)(t1.x, 0, t1.y), (0, _TSLBase.vec3)(0, 1, 0), (0, _TSLBase.vec3)(t1.z, 0, t1.w)).toVar(); // LTC Fresnel Approximation by Stephen Hill // http://blog.selfshadow.com/publications/s2016-advances/s2016_ltc_fresnel.pdf const fresnel = _PropertyNode.specularColor.mul(t2.x).add(_PropertyNode.specularColor.oneMinus().mul(t2.y)).toVar(); reflectedLight.directSpecular.addAssign(lightColor.mul(fresnel).mul((0, _LTC.LTC_Evaluate)({ N, V, P, mInv, p0, p1, p2, p3 }))); reflectedLight.directDiffuse.addAssign(lightColor.mul(_PropertyNode.diffuseColor).mul((0, _LTC.LTC_Evaluate)({ N, V, P, mInv: (0, _TSLBase.mat3)(1, 0, 0, 0, 1, 0, 0, 0, 1), p0, p1, p2, p3 }))); } /** * Implements the indirect lighting. * * @param {NodeBuilder} builder - The current node builder. */ indirect(builder) { this.indirectDiffuse(builder); this.indirectSpecular(builder); this.ambientOcclusion(builder); } /** * Implements the indirect diffuse term. * * @param {NodeBuilder} builder - The current node builder. */ indirectDiffuse(builder) { const { irradiance, reflectedLight } = builder.context; reflectedLight.indirectDiffuse.addAssign(irradiance.mul((0, _BRDF_Lambert.default)({ diffuseColor: _PropertyNode.diffuseColor }))); } /** * Implements the indirect specular term. * * @param {NodeBuilder} builder - The current node builder. */ indirectSpecular(builder) { const { radiance, iblIrradiance, reflectedLight } = builder.context; if (this.sheen === true) { this.sheenSpecularIndirect.addAssign(iblIrradiance.mul(_PropertyNode.sheen, IBLSheenBRDF({ normal: _Normal.transformedNormalView, viewDir: _Position.positionViewDirection, roughness: _PropertyNode.sheenRoughness }))); } if (this.clearcoat === true) { const dotNVcc = _Normal.transformedClearcoatNormalView.dot(_Position.positionViewDirection).clamp(); const clearcoatEnv = (0, _EnvironmentBRDF.default)({ dotNV: dotNVcc, specularColor: clearcoatF0, specularF90: clearcoatF90, roughness: _PropertyNode.clearcoatRoughness }); this.clearcoatSpecularIndirect.addAssign(this.clearcoatRadiance.mul(clearcoatEnv)); } // Both indirect specular and indirect diffuse light accumulate here const singleScattering = (0, _TSLBase.vec3)().toVar('singleScattering'); const multiScattering = (0, _TSLBase.vec3)().toVar('multiScattering'); const cosineWeightedIrradiance = iblIrradiance.mul(1 / Math.PI); this.computeMultiscattering(singleScattering, multiScattering, _PropertyNode.specularF90); const totalScattering = singleScattering.add(multiScattering); const diffuse = _PropertyNode.diffuseColor.mul(totalScattering.r.max(totalScattering.g).max(totalScattering.b).oneMinus()); reflectedLight.indirectSpecular.addAssign(radiance.mul(singleScattering)); reflectedLight.indirectSpecular.addAssign(multiScattering.mul(cosineWeightedIrradiance)); reflectedLight.indirectDiffuse.addAssign(diffuse.mul(cosineWeightedIrradiance)); } /** * Implements the ambient occlusion term. * * @param {NodeBuilder} builder - The current node builder. */ ambientOcclusion(builder) { const { ambientOcclusion, reflectedLight } = builder.context; const dotNV = _Normal.transformedNormalView.dot(_Position.positionViewDirection).clamp(); // @ TODO: Move to core dotNV const aoNV = dotNV.add(ambientOcclusion); const aoExp = _PropertyNode.roughness.mul(-16.0).oneMinus().negate().exp2(); const aoNode = ambientOcclusion.sub(aoNV.pow(aoExp).oneMinus()).clamp(); if (this.clearcoat === true) { this.clearcoatSpecularIndirect.mulAssign(ambientOcclusion); } if (this.sheen === true) { this.sheenSpecularIndirect.mulAssign(ambientOcclusion); } reflectedLight.indirectDiffuse.mulAssign(ambientOcclusion); reflectedLight.indirectSpecular.mulAssign(aoNode); } /** * Used for final lighting accumulations depending on the requested features. * * @param {NodeBuilder} builder - The current node builder. */ finish({ context }) { const { outgoingLight } = context; if (this.clearcoat === true) { const dotNVcc = _Normal.transformedClearcoatNormalView.dot(_Position.positionViewDirection).clamp(); const Fcc = (0, _F_Schlick.default)({ dotVH: dotNVcc, f0: clearcoatF0, f90: clearcoatF90 }); const clearcoatLight = outgoingLight.mul(_PropertyNode.clearcoat.mul(Fcc).oneMinus()).add(this.clearcoatSpecularDirect.add(this.clearcoatSpecularIndirect).mul(_PropertyNode.clearcoat)); outgoingLight.assign(clearcoatLight); } if (this.sheen === true) { const sheenEnergyComp = _PropertyNode.sheen.r.max(_PropertyNode.sheen.g).max(_PropertyNode.sheen.b).mul(0.157).oneMinus(); const sheenLight = outgoingLight.mul(sheenEnergyComp).add(this.sheenSpecularDirect, this.sheenSpecularIndirect); outgoingLight.assign(sheenLight); } } } var _default = exports.default = PhysicalLightingModel;