@openhps/core
Version:
Open Hybrid Positioning System - Core component
150 lines (134 loc) • 5.57 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _LightingModel = _interopRequireDefault(require("../core/LightingModel.js"));
var _PropertyNode = require("../core/PropertyNode.js");
var _TSLBase = require("../tsl/TSLBase.js");
var _Position = require("../accessors/Position.js");
var _Camera = require("../accessors/Camera.js");
var _LoopNode = require("../utils/LoopNode.js");
var _ViewportDepthNode = require("../display/ViewportDepthNode.js");
var _ModelNode = require("../accessors/ModelNode.js");
var _LTC = require("./BSDF/LTC.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const scatteringDensity = (0, _PropertyNode.property)('vec3');
const linearDepthRay = (0, _PropertyNode.property)('vec3');
const outgoingRayLight = (0, _PropertyNode.property)('vec3');
/**
* VolumetricLightingModel class extends the LightingModel to implement volumetric lighting effects.
* This model calculates the scattering and transmittance of light through a volumetric medium.
* It dynamically adjusts the direction of the ray based on the camera and object positions.
* The model supports custom scattering and depth nodes to enhance the lighting effects.
*
* @augments LightingModel
*/
class VolumetricLightingModel extends _LightingModel.default {
constructor() {
super();
}
start(builder) {
const {
material,
context
} = builder;
const startPos = (0, _PropertyNode.property)('vec3');
const endPos = (0, _PropertyNode.property)('vec3');
// This approach dynamically changes the direction of the ray,
// prioritizing the ray from the camera to the object if it is inside the mesh, and from the object to the camera if it is far away.
(0, _TSLBase.If)(_Camera.cameraPosition.sub(_Position.positionWorld).length().greaterThan(_ModelNode.modelRadius.mul(2)), () => {
startPos.assign(_Camera.cameraPosition);
endPos.assign(_Position.positionWorld);
}).Else(() => {
startPos.assign(_Position.positionWorld);
endPos.assign(_Camera.cameraPosition);
});
//
const viewVector = endPos.sub(startPos);
const steps = (0, _TSLBase.uniform)('int').onRenderUpdate(({
material
}) => material.steps);
const stepSize = viewVector.length().div(steps).toVar();
const rayDir = viewVector.normalize().toVar(); // TODO: toVar() should be automatic here ( in loop )
const distTravelled = (0, _TSLBase.float)(0.0).toVar();
const transmittance = (0, _TSLBase.vec3)(1).toVar();
if (material.offsetNode) {
// reduce banding
distTravelled.addAssign(material.offsetNode.mul(stepSize));
}
(0, _LoopNode.Loop)(steps, () => {
const positionRay = startPos.add(rayDir.mul(distTravelled));
const positionViewRay = _Camera.cameraViewMatrix.mul((0, _TSLBase.vec4)(positionRay, 1)).xyz;
if (material.depthNode !== null) {
linearDepthRay.assign((0, _ViewportDepthNode.linearDepth)((0, _ViewportDepthNode.viewZToPerspectiveDepth)(positionViewRay.z, _Camera.cameraNear, _Camera.cameraFar)));
context.sceneDepthNode = (0, _ViewportDepthNode.linearDepth)(material.depthNode).toVar();
}
context.positionWorld = positionRay;
context.shadowPositionWorld = positionRay;
context.positionView = positionViewRay;
scatteringDensity.assign(0);
let scatteringNode;
if (material.scatteringNode) {
scatteringNode = material.scatteringNode({
positionRay
});
}
super.start(builder);
if (scatteringNode) {
scatteringDensity.mulAssign(scatteringNode);
}
// beer's law
const falloff = scatteringDensity.mul(.01).negate().mul(stepSize).exp();
transmittance.mulAssign(falloff);
// move along the ray
distTravelled.addAssign(stepSize);
});
outgoingRayLight.addAssign(transmittance.saturate().oneMinus());
}
scatteringLight(lightColor, builder) {
const sceneDepthNode = builder.context.sceneDepthNode;
if (sceneDepthNode) {
(0, _TSLBase.If)(sceneDepthNode.greaterThanEqual(linearDepthRay), () => {
scatteringDensity.addAssign(lightColor);
});
} else {
scatteringDensity.addAssign(lightColor);
}
}
direct({
lightNode,
lightColor
}, builder) {
// Ignore lights with infinite distance
if (lightNode.light.distance === undefined) return;
// TODO: We need a viewportOpaque*() ( output, depth ) to fit with modern rendering approaches
const directLight = lightColor.xyz.toVar();
directLight.mulAssign(lightNode.shadowNode); // it no should be necessary if used in the same render pass
this.scatteringLight(directLight, builder);
}
directRectArea({
lightColor,
lightPosition,
halfWidth,
halfHeight
}, builder) {
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 P = builder.context.positionView;
const directLight = lightColor.xyz.mul((0, _LTC.LTC_Evaluate_Volume)({
P,
p0,
p1,
p2,
p3
})).pow(1.5);
this.scatteringLight(directLight, builder);
}
finish(builder) {
builder.context.outgoingLight.assign(outgoingRayLight);
}
}
var _default = exports.default = VolumetricLightingModel;