UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

216 lines (205 loc) 7.14 kB
import { Fn, float, mat3, vec3, If } from '../tsl/TSLBase.js'; import { select } from '../math/ConditionalNode.js'; import { clamp, log2, max, min, pow, mix } from '../math/MathNode.js'; import { mul, sub, div } from '../math/OperatorNode.js'; /** * Linear tone mapping, exposure only. * * @tsl * @function * @param {Node<vec3>} color - The color that should be tone mapped. * @param {Node<float>} exposure - The exposure. * @return {Node<vec3>} The tone mapped color. */ export const linearToneMapping = /*@__PURE__*/Fn(([color, exposure]) => { return color.mul(exposure).clamp(); }).setLayout({ name: 'linearToneMapping', type: 'vec3', inputs: [{ name: 'color', type: 'vec3' }, { name: 'exposure', type: 'float' }] }); /** * Reinhard tone mapping. * * Reference: {@link https://www.cs.utah.edu/docs/techreports/2002/pdf/UUCS-02-001.pdf} * * @tsl * @function * @param {Node<vec3>} color - The color that should be tone mapped. * @param {Node<float>} exposure - The exposure. * @return {Node<vec3>} The tone mapped color. */ export const reinhardToneMapping = /*@__PURE__*/Fn(([color, exposure]) => { color = color.mul(exposure); return color.div(color.add(1.0)).clamp(); }).setLayout({ name: 'reinhardToneMapping', type: 'vec3', inputs: [{ name: 'color', type: 'vec3' }, { name: 'exposure', type: 'float' }] }); /** * Cineon tone mapping. * * Reference: {@link http://filmicworlds.com/blog/filmic-tonemapping-operators/} * * @tsl * @function * @param {Node<vec3>} color - The color that should be tone mapped. * @param {Node<float>} exposure - The exposure. * @return {Node<vec3>} The tone mapped color. */ export const cineonToneMapping = /*@__PURE__*/Fn(([color, exposure]) => { // filmic operator by Jim Hejl and Richard Burgess-Dawson color = color.mul(exposure); color = color.sub(0.004).max(0.0); const a = color.mul(color.mul(6.2).add(0.5)); const b = color.mul(color.mul(6.2).add(1.7)).add(0.06); return a.div(b).pow(2.2); }).setLayout({ name: 'cineonToneMapping', type: 'vec3', inputs: [{ name: 'color', type: 'vec3' }, { name: 'exposure', type: 'float' }] }); // source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs const RRTAndODTFit = /*@__PURE__*/Fn(([color]) => { const a = color.mul(color.add(0.0245786)).sub(0.000090537); const b = color.mul(color.add(0.4329510).mul(0.983729)).add(0.238081); return a.div(b); }); /** * ACESFilmic tone mapping. * * Reference: {@link https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs} * * @tsl * @function * @param {Node<vec3>} color - The color that should be tone mapped. * @param {Node<float>} exposure - The exposure. * @return {Node<vec3>} The tone mapped color. */ export const acesFilmicToneMapping = /*@__PURE__*/Fn(([color, exposure]) => { // sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT const ACESInputMat = mat3(0.59719, 0.35458, 0.04823, 0.07600, 0.90834, 0.01566, 0.02840, 0.13383, 0.83777); // ODT_SAT => XYZ => D60_2_D65 => sRGB const ACESOutputMat = mat3(1.60475, -0.53108, -0.07367, -0.10208, 1.10813, -0.00605, -0.00327, -0.07276, 1.07602); color = color.mul(exposure).div(0.6); color = ACESInputMat.mul(color); // Apply RRT and ODT color = RRTAndODTFit(color); color = ACESOutputMat.mul(color); // Clamp to [0, 1] return color.clamp(); }).setLayout({ name: 'acesFilmicToneMapping', type: 'vec3', inputs: [{ name: 'color', type: 'vec3' }, { name: 'exposure', type: 'float' }] }); const LINEAR_REC2020_TO_LINEAR_SRGB = /*@__PURE__*/mat3(vec3(1.6605, -0.1246, -0.0182), vec3(-0.5876, 1.1329, -0.1006), vec3(-0.0728, -0.0083, 1.1187)); const LINEAR_SRGB_TO_LINEAR_REC2020 = /*@__PURE__*/mat3(vec3(0.6274, 0.0691, 0.0164), vec3(0.3293, 0.9195, 0.0880), vec3(0.0433, 0.0113, 0.8956)); const agxDefaultContrastApprox = /*@__PURE__*/Fn(([x_immutable]) => { const x = vec3(x_immutable).toVar(); const x2 = vec3(x.mul(x)).toVar(); const x4 = vec3(x2.mul(x2)).toVar(); return float(15.5).mul(x4.mul(x2)).sub(mul(40.14, x4.mul(x))).add(mul(31.96, x4).sub(mul(6.868, x2.mul(x))).add(mul(0.4298, x2).add(mul(0.1191, x).sub(0.00232)))); }); /** * AgX tone mapping. * * @tsl * @function * @param {Node<vec3>} color - The color that should be tone mapped. * @param {Node<float>} exposure - The exposure. * @return {Node<vec3>} The tone mapped color. */ export const agxToneMapping = /*@__PURE__*/Fn(([color, exposure]) => { const colortone = vec3(color).toVar(); const AgXInsetMatrix = mat3(vec3(0.856627153315983, 0.137318972929847, 0.11189821299995), vec3(0.0951212405381588, 0.761241990602591, 0.0767994186031903), vec3(0.0482516061458583, 0.101439036467562, 0.811302368396859)); const AgXOutsetMatrix = mat3(vec3(1.1271005818144368, -0.1413297634984383, -0.14132976349843826), vec3(-0.11060664309660323, 1.157823702216272, -0.11060664309660294), vec3(-0.016493938717834573, -0.016493938717834257, 1.2519364065950405)); const AgxMinEv = float(-12.47393); const AgxMaxEv = float(4.026069); colortone.mulAssign(exposure); colortone.assign(LINEAR_SRGB_TO_LINEAR_REC2020.mul(colortone)); colortone.assign(AgXInsetMatrix.mul(colortone)); colortone.assign(max(colortone, 1e-10)); colortone.assign(log2(colortone)); colortone.assign(colortone.sub(AgxMinEv).div(AgxMaxEv.sub(AgxMinEv))); colortone.assign(clamp(colortone, 0.0, 1.0)); colortone.assign(agxDefaultContrastApprox(colortone)); colortone.assign(AgXOutsetMatrix.mul(colortone)); colortone.assign(pow(max(vec3(0.0), colortone), vec3(2.2))); colortone.assign(LINEAR_REC2020_TO_LINEAR_SRGB.mul(colortone)); colortone.assign(clamp(colortone, 0.0, 1.0)); return colortone; }).setLayout({ name: 'agxToneMapping', type: 'vec3', inputs: [{ name: 'color', type: 'vec3' }, { name: 'exposure', type: 'float' }] }); /** * Neutral tone mapping. * * Reference: {@link https://modelviewer.dev/examples/tone-mapping} * * @tsl * @function * @param {Node<vec3>} color - The color that should be tone mapped. * @param {Node<float>} exposure - The exposure. * @return {Node<vec3>} The tone mapped color. */ export const neutralToneMapping = /*@__PURE__*/Fn(([color, exposure]) => { const StartCompression = float(0.8 - 0.04); const Desaturation = float(0.15); color = color.mul(exposure); const x = min(color.r, min(color.g, color.b)); const offset = select(x.lessThan(0.08), x.sub(mul(6.25, x.mul(x))), 0.04); color.subAssign(offset); const peak = max(color.r, max(color.g, color.b)); If(peak.lessThan(StartCompression), () => { return color; }); const d = sub(1, StartCompression); const newPeak = sub(1, d.mul(d).div(peak.add(d.sub(StartCompression)))); color.mulAssign(newPeak.div(peak)); const g = sub(1, div(1, Desaturation.mul(peak.sub(newPeak)).add(1))); return mix(color, vec3(newPeak), g); }).setLayout({ name: 'neutralToneMapping', type: 'vec3', inputs: [{ name: 'color', type: 'vec3' }, { name: 'exposure', type: 'float' }] });