Skip to content

Commit 8ccaa64

Browse files
committed
WIP KHR_materials_volume_scatter import
1 parent f60fe75 commit 8ccaa64

File tree

8 files changed

+481
-53
lines changed

8 files changed

+481
-53
lines changed

packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class KHR_materials_diffuse_transmission implements IGLTFLoaderExtension
7979
const adapter = this._loader._getOrCreateMaterialAdapter(babylonMaterial);
8080
adapter.configureSubsurface();
8181
adapter.subsurfaceWeight = extension.diffuseTransmissionFactor ?? 0;
82-
adapter.subsurfaceColor = extension.diffuseTransmissionColorFactor !== undefined ? Color3.FromArray(extension.diffuseTransmissionColorFactor) : Color3.White();
82+
adapter.subsurfaceConstantTint = extension.diffuseTransmissionColorFactor !== undefined ? Color3.FromArray(extension.diffuseTransmissionColorFactor) : Color3.White();
8383

8484
const promises = new Array<Promise<any>>();
8585

@@ -97,7 +97,7 @@ export class KHR_materials_diffuse_transmission implements IGLTFLoaderExtension
9797
promises.push(
9898
this._loader.loadTextureInfoAsync(`${context}/diffuseTransmissionColorTexture`, extension.diffuseTransmissionColorTexture).then((texture: BaseTexture) => {
9999
texture.name = `${babylonMaterial.name} (Diffuse Transmission Color)`;
100-
adapter.subsurfaceColorTexture = texture;
100+
adapter.subsurfaceConstantTintTexture = texture;
101101
})
102102
);
103103
}

packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_volume.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { IGLTFLoaderExtension } from "../glTFLoaderExtension";
88
import { GLTFLoader } from "../glTFLoader";
99
import type { IKHRMaterialsVolume } from "babylonjs-gltf2interface";
1010
import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry";
11+
import { Vector3 } from "core/Maths";
1112

1213
const NAME = "KHR_materials_volume";
1314

@@ -89,9 +90,16 @@ export class KHR_materials_volume implements IGLTFLoaderExtension {
8990
return Promise.resolve();
9091
}
9192

92-
adapter.transmissionDepth = extension.attenuationDistance !== undefined ? extension.attenuationDistance : Number.MAX_VALUE;
93-
adapter.transmissionColor =
94-
extension.attenuationColor !== undefined && extension.attenuationColor.length == 3 ? Color3.FromArray(extension.attenuationColor) : Color3.White();
93+
const attenuationDistance = extension.attenuationDistance !== undefined ? extension.attenuationDistance : Number.MAX_VALUE;
94+
const attenuationColor = extension.attenuationColor !== undefined && extension.attenuationColor.length == 3 ? Color3.FromArray(extension.attenuationColor) : Color3.White();
95+
// Calculate the attenuation coefficient (i.e. extinction coefficient)
96+
const extinctionCoefficient = new Vector3(-Math.log(attenuationColor.r), -Math.log(attenuationColor.g), -Math.log(attenuationColor.b));
97+
extinctionCoefficient.scaleInPlace(1 / Math.max(attenuationDistance, 0.001));
98+
adapter.extinctionCoefficient = extinctionCoefficient;
99+
100+
adapter.transmissionDepth = attenuationDistance;
101+
adapter.transmissionColor = attenuationColor;
102+
95103
adapter.volumeThickness = extension.thicknessFactor ?? 0;
96104

97105
const promises = new Array<Promise<any>>();
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
import type { Nullable } from "core/types";
3+
import type { Material } from "core/Materials/material";
4+
import { Color3 } from "core/Maths/math.color";
5+
import { Vector3 } from "core/Maths/math.vector";
6+
import type { IMaterial } from "../glTFLoaderInterfaces";
7+
import type { IGLTFLoaderExtension } from "../glTFLoaderExtension";
8+
import { GLTFLoader } from "../glTFLoader";
9+
import type { IKHRMaterialsVolumeScatter } from "babylonjs-gltf2interface";
10+
import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry";
11+
12+
const NAME = "KHR_materials_volume_scatter";
13+
14+
declare module "../../glTFFileLoader" {
15+
// eslint-disable-next-line jsdoc/require-jsdoc, @typescript-eslint/naming-convention
16+
export interface GLTFLoaderExtensionOptions {
17+
/**
18+
* Defines options for the KHR_materials_volume_scatter extension.
19+
*/
20+
// NOTE: Don't use NAME here as it will break the UMD type declarations.
21+
["KHR_materials_volume_scatter"]: {};
22+
}
23+
}
24+
25+
function multiScatterToSingleScatterAlbedo(multiScatter: Color3): Vector3 {
26+
const multiScatterAlbedo = new Vector3(multiScatter.r, multiScatter.g, multiScatter.b);
27+
const s: Vector3 = new Vector3(4.09712, 4.09712, 4.09712);
28+
s.multiplyInPlace(new Vector3(4.20863, 4.20863, 4.20863).multiplyInPlace(multiScatterAlbedo));
29+
30+
const p: Vector3 = new Vector3(9.59217, 9.59217, 9.59217);
31+
p.addInPlace(new Vector3(41.6808, 41.6808, 41.6808).multiplyInPlace(multiScatterAlbedo));
32+
p.addInPlace(new Vector3(17.7126, 17.7126, 17.7126).multiplyInPlace(multiScatterAlbedo.multiply(multiScatterAlbedo)));
33+
s.subtractInPlace(new Vector3(Math.sqrt(p.x), Math.sqrt(p.y), Math.sqrt(p.z)));
34+
return new Vector3(1.0, 1.0, 1.0).subtract(s.multiply(s));
35+
}
36+
37+
/**
38+
* [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume_scatter/README.md)
39+
* @since 5.0.0
40+
*/
41+
// eslint-disable-next-line @typescript-eslint/naming-convention
42+
export class KHR_materials_volume_scatter implements IGLTFLoaderExtension {
43+
/**
44+
* The name of this extension.
45+
*/
46+
public readonly name = NAME;
47+
48+
/**
49+
* Defines whether this extension is enabled.
50+
*/
51+
public enabled: boolean;
52+
53+
/**
54+
* Defines a number that determines the order the extensions are applied.
55+
*/
56+
public order = 174;
57+
58+
private _loader: GLTFLoader;
59+
60+
/**
61+
* @internal
62+
*/
63+
constructor(loader: GLTFLoader) {
64+
this._loader = loader;
65+
this.enabled = this._loader.isExtensionUsed(NAME);
66+
if (this.enabled) {
67+
// We need to disable instance usage because the attenuation factor depends on the node scale of each individual mesh
68+
this._loader._disableInstancedMesh++;
69+
}
70+
}
71+
72+
/** @internal */
73+
public dispose() {
74+
if (this.enabled) {
75+
this._loader._disableInstancedMesh--;
76+
}
77+
(this._loader as any) = null;
78+
}
79+
80+
/**
81+
* @internal
82+
*/
83+
// eslint-disable-next-line no-restricted-syntax
84+
public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
85+
return GLTFLoader.LoadExtensionAsync<IKHRMaterialsVolumeScatter>(context, material, this.name, async (extensionContext, extension) => {
86+
const promises = new Array<Promise<any>>();
87+
promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial));
88+
promises.push(this._loadVolumePropertiesAsync(extensionContext, material, babylonMaterial, extension));
89+
// eslint-disable-next-line github/no-then
90+
return await Promise.all(promises).then(() => {});
91+
});
92+
}
93+
94+
// eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
95+
private _loadVolumePropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsVolumeScatter): Promise<void> {
96+
const adapter = this._loader._getOrCreateMaterialAdapter(babylonMaterial);
97+
98+
// If transparency isn't enabled already, this extension shouldn't do anything.
99+
// i.e. it requires either the KHR_materials_transmission or KHR_materials_diffuse_transmission extensions.
100+
if (adapter.transmissionWeight === 0 && adapter.subsurfaceWeight === 0) {
101+
return Promise.resolve();
102+
}
103+
104+
const scatterColor = extension.multiscatterColor !== undefined && extension.multiscatterColor.length == 3 ? Color3.FromArray(extension.multiscatterColor) : Color3.Black();
105+
const scatterAnisotropy = extension.scatterAnisotropy !== undefined ? extension.scatterAnisotropy : 0;
106+
107+
// In glTF, both the translucency volume and subsurface volume use the same input parameters.
108+
// We'll apply them to both, as appropriate.
109+
if (adapter.transmissionWeight > 0) {
110+
const singleScatterAlbedo = multiScatterToSingleScatterAlbedo(scatterColor);
111+
const absorptionCoefficient = adapter.extinctionCoefficient.multiplyByFloats(1.0 - singleScatterAlbedo.x, 1.0 - singleScatterAlbedo.y, 1.0 - singleScatterAlbedo.z);
112+
const scatteringCoefficient = adapter.extinctionCoefficient.multiply(singleScatterAlbedo);
113+
114+
const maxVal = Math.max(absorptionCoefficient.x, absorptionCoefficient.y, absorptionCoefficient.z);
115+
const absorptionDistance = maxVal !== 0.0 ? 1.0 / maxVal : 1.0;
116+
117+
adapter.transmissionColor = new Color3(
118+
Math.exp(-absorptionCoefficient.x * absorptionDistance),
119+
Math.exp(-absorptionCoefficient.y * absorptionDistance),
120+
Math.exp(-absorptionCoefficient.z * absorptionDistance)
121+
);
122+
adapter.transmissionDepth = absorptionDistance;
123+
adapter.transmissionScatter = scatteringCoefficient;
124+
adapter.transmissionScatterAnisotropy = scatterAnisotropy;
125+
}
126+
// Subsurface volume
127+
if (adapter.subsurfaceWeight > 0) {
128+
adapter.subsurfaceScatterAnisotropy = scatterAnisotropy;
129+
adapter.subsurfaceColor = scatterColor;
130+
131+
const mfp = new Vector3(
132+
adapter.extinctionCoefficient.x !== 0 ? 1.0 / adapter.extinctionCoefficient.x : 1.0,
133+
adapter.extinctionCoefficient.y !== 0 ? 1.0 / adapter.extinctionCoefficient.y : 1.0,
134+
adapter.extinctionCoefficient.z !== 0 ? 1.0 / adapter.extinctionCoefficient.z : 1.0
135+
);
136+
137+
adapter.subsurfaceRadius = Math.max(mfp.x, mfp.y, mfp.z);
138+
adapter.subsurfaceRadiusScale = new Color3(mfp.x / adapter.subsurfaceRadius, mfp.y / adapter.subsurfaceRadius, mfp.z / adapter.subsurfaceRadius);
139+
}
140+
141+
return Promise.resolve();
142+
}
143+
}
144+
145+
unregisterGLTFExtension(NAME);
146+
registerGLTFExtension(NAME, true, (loader) => new KHR_materials_volume_scatter(loader));

packages/dev/loaders/src/glTF/2.0/Extensions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export * from "./KHR_materials_variants";
2222
export * from "./KHR_materials_transmission";
2323
export * from "./KHR_materials_diffuse_transmission";
2424
export * from "./KHR_materials_volume";
25+
export * from "./KHR_materials_volume_scatter";
2526
export * from "./KHR_materials_dispersion";
2627
export * from "./KHR_materials_diffuse_roughness";
2728
export * from "./KHR_mesh_quantization";

0 commit comments

Comments
 (0)