@openhps/core
Version:
Open Hybrid Positioning System - Core component
746 lines (690 loc) • 27.7 kB
JavaScript
"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;