UNPKG

@babylonjs/loaders

Version:

For usage documentation please visit https://doc.babylonjs.com/features/featuresDeepDive/importers/loadingFileTypes/.

116 lines 6.71 kB
import { Color3 } from "@babylonjs/core/Maths/math.color.js"; import { Vector3 } from "@babylonjs/core/Maths/math.vector.js"; import { GLTFLoader } from "../glTFLoader.js"; import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry.js"; const NAME = "KHR_materials_volume_scatter"; function multiScatterToSingleScatterAlbedo(multiScatter) { const multiScatterAlbedo = new Vector3(multiScatter.r, multiScatter.g, multiScatter.b); const s = new Vector3(4.09712, 4.09712, 4.09712); s.addInPlace(new Vector3(4.20863, 4.20863, 4.20863).multiplyInPlace(multiScatterAlbedo)); const p = new Vector3(9.59217, 9.59217, 9.59217); p.addInPlace(new Vector3(41.6808, 41.6808, 41.6808).multiplyInPlace(multiScatterAlbedo)); p.addInPlace(new Vector3(17.7126, 17.7126, 17.7126).multiplyInPlace(multiScatterAlbedo.multiply(multiScatterAlbedo))); s.subtractInPlace(new Vector3(Math.sqrt(p.x), Math.sqrt(p.y), Math.sqrt(p.z))); return new Vector3(1.0, 1.0, 1.0).subtract(s.multiply(s)); } /** * TODO: In-progress specification * [Specification](https://github.com/KhronosGroup/glTF/blob/7ea427ed55d44427e83c0a6d1c87068b1a4151c5/extensions/2.0/Khronos/KHR_materials_volume_scatter/README.md) * @experimental * @since 9.0.0 */ // eslint-disable-next-line @typescript-eslint/naming-convention export class KHR_materials_volume_scatter { /** * @internal */ constructor(loader) { /** * The name of this extension. */ this.name = NAME; /** * Defines a number that determines the order the extensions are applied. */ this.order = 172; this._loader = loader; this.enabled = this._loader.isExtensionUsed(NAME); if (this.enabled) { // We need to disable instance usage because the attenuation factor depends on the node scale of each individual mesh this._loader._disableInstancedMesh++; } } /** @internal */ dispose() { if (this.enabled) { this._loader._disableInstancedMesh--; } this._loader = null; } /** * @internal */ // eslint-disable-next-line no-restricted-syntax loadMaterialPropertiesAsync(context, material, babylonMaterial) { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); promises.push(this._loadVolumePropertiesAsync(extensionContext, material, babylonMaterial, extension)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => { }); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax _loadVolumePropertiesAsync(context, material, babylonMaterial, extension) { const adapter = this._loader._getOrCreateMaterialAdapter(babylonMaterial); // If transparency isn't enabled already, this extension shouldn't do anything. // i.e. it requires either the KHR_materials_transmission or KHR_materials_diffuse_transmission extensions. // Additionally, this already needs to be a volumetric material to apply this extension. if ((adapter.transmissionWeight === 0 && adapter.subsurfaceWeight === 0) || adapter.geometryThinWalled) { return Promise.resolve(); } const scatterColor = extension.multiscatterColorFactor !== undefined && extension.multiscatterColorFactor.length == 3 ? Color3.FromArray(extension.multiscatterColorFactor) : Color3.Black(); const scatterAnisotropy = extension.scatterAnisotropy !== undefined ? extension.scatterAnisotropy : 0; // If diffuse_transmission, apply props to subsurface // If transmission, apply props to transmission // Load texture if present let texturePromise = Promise.resolve(null); if (extension.multiscatterColorTexture) { extension.multiscatterColorTexture.nonColorData = true; texturePromise = this._loader.loadTextureInfoAsync(`${context}/multiscatterColorTexture`, extension.multiscatterColorTexture, (texture) => { texture.name = `${babylonMaterial.name} (Scatter Color)`; if (adapter.transmissionWeight > 0) { adapter.transmissionScatterTexture = texture; } if (adapter.subsurfaceWeight > 0) { adapter.subsurfaceColorTexture = texture; } }); } const extinctionCoefficient = new Vector3(-Math.log(adapter.transmissionColor.r), -Math.log(adapter.transmissionColor.g), -Math.log(adapter.transmissionColor.b)); extinctionCoefficient.scaleInPlace(1 / Math.max(adapter.transmissionDepth, 0.000001)); // In glTF, both the translucency volume and subsurface volume use the same input parameters. // We'll apply them to both, as appropriate. if (adapter.transmissionWeight > 0) { const singleScatterAlbedo = multiScatterToSingleScatterAlbedo(scatterColor); const scatteringCoefficient = extinctionCoefficient.multiply(singleScatterAlbedo); // The scattering coefficient in OpenPBR is defined as per unit distance, so we need to scale it back up by the depth. scatteringCoefficient.scaleInPlace(adapter.transmissionDepth); adapter.transmissionScatter.set(scatteringCoefficient.x, scatteringCoefficient.y, scatteringCoefficient.z); adapter.transmissionScatterAnisotropy = scatterAnisotropy; } // Subsurface volume if (adapter.subsurfaceWeight > 0) { adapter.subsurfaceScatterAnisotropy = scatterAnisotropy; adapter.subsurfaceColor = scatterColor; const mfp = new Vector3(extinctionCoefficient.x !== 0 ? 1.0 / extinctionCoefficient.x : 1.0, extinctionCoefficient.y !== 0 ? 1.0 / extinctionCoefficient.y : 1.0, extinctionCoefficient.z !== 0 ? 1.0 / extinctionCoefficient.z : 1.0); adapter.subsurfaceRadius = Math.max(mfp.x, mfp.y, mfp.z); adapter.subsurfaceRadiusScale = new Color3(mfp.x / adapter.subsurfaceRadius, mfp.y / adapter.subsurfaceRadius, mfp.z / adapter.subsurfaceRadius); } // eslint-disable-next-line github/no-then return texturePromise.then(() => { }); } } unregisterGLTFExtension(NAME); registerGLTFExtension(NAME, true, (loader) => new KHR_materials_volume_scatter(loader)); //# sourceMappingURL=KHR_materials_volume_scatter.js.map