Bläddra i källkod

First push GLTF clearcoat and specular Extension

sebavan 5 år sedan
förälder
incheckning
55d265af1d

+ 2 - 0
dist/preview release/what's new.md

@@ -97,6 +97,8 @@
 - Added support for using HTTP range requests when loading `MSFT_lod` extension from a glTF binary. ([bghgary](https://github.com/bghgary))
 - Added a flag to enable/disable creation of instances for glTF loader. ([bghgary](https://github.com/bghgary))
 - Added an order property to glTF loader extensions to support reordering. ([bghgary](https://github.com/bghgary))
+- Added support for GLTF clearcoat extension [Sebavan](https://github.com/sebavan/)
+- Added support for GLTF specular extension [Sebavan](https://github.com/sebavan/)
 
 ### Materials
 

+ 5 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx

@@ -109,6 +109,7 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "Surface Albedo", value: 60 },
             { label: "Reflectance 0", value: 61 },
             { label: "Metallic", value: 62 },
+            { label: "Metallic F0", value: 71 },
             { label: "Roughness", value: 63 },
             { label: "AlphaG", value: 64 },
             { label: "NdotV", value: 65 },
@@ -142,6 +143,10 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="METALLIC WORKFLOW">
                     <SliderLineComponent label="Metallic" target={material} propertyName="metallic" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Metallic F0" target={material} propertyName="metallicF0" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Metallic F0 From Map" target={material} propertyName="useMetallicF0FactorFromMetallicTexture"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Roughness" target={material} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="CLEAR COAT">

+ 117 - 0
loaders/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts

@@ -0,0 +1,117 @@
+import { Nullable } from "babylonjs/types";
+import { PBRMaterial } from "babylonjs/Materials/PBR/pbrMaterial";
+import { Material } from "babylonjs/Materials/material";
+
+import { ITextureInfo, IMaterial } from "../glTFLoaderInterfaces";
+import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
+import { GLTFLoader } from "../glTFLoader";
+import { IMaterialNormalTextureInfo } from 'babylonjs-gltf2interface';
+
+const NAME = "KHR_materials_clearcoat";
+
+interface IKHR_materials_clearcoat {
+    clearcoatFactor: number;
+    clearcoatTexture: ITextureInfo;
+    clearcoatRoughnessFactor: number;
+    clearcoatRoughnessTexture: ITextureInfo;
+    clearcoatNormalTexture: IMaterialNormalTextureInfo;
+}
+
+/**
+ * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1677)
+ */
+export class KHR_materials_clearcoat implements IGLTFLoaderExtension {
+    /**
+     * The name of this extension.
+     */
+    public readonly name = NAME;
+
+    /**
+     * Defines whether this extension is enabled.
+     */
+    public enabled: boolean;
+
+    /**
+     * Defines a number that determines the order the extensions are applied.
+     */
+    public order = 220;
+
+    private _loader: GLTFLoader;
+
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
+        this.enabled = this._loader.isExtensionUsed(NAME);
+    }
+
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
+    }
+
+    /** @hidden */
+    public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
+        return GLTFLoader.LoadExtensionAsync<IKHR_materials_clearcoat>(context, material, this.name, (extensionContext, extension) => {
+            const promises = new Array<Promise<any>>();
+            promises.push(this._loader.loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
+            promises.push(this._loadClearCoatPropertiesAsync(extensionContext, material, extension, babylonMaterial));
+            this._loader.loadMaterialAlphaProperties(context, material, babylonMaterial);
+            return Promise.all(promises).then(() => { });
+        });
+    }
+
+    private _loadClearCoatPropertiesAsync(context: string, material: IMaterial, properties: IKHR_materials_clearcoat, babylonMaterial: Material): Promise<void> {
+        if (!(babylonMaterial instanceof PBRMaterial)) {
+            throw new Error(`${context}: Material type not supported`);
+        }
+
+        const promises = new Array<Promise<any>>();
+
+        babylonMaterial.clearCoat.isEnabled = true;
+
+        if (properties.clearcoatFactor) {
+            babylonMaterial.clearCoat.intensity = properties.clearcoatFactor;
+        }
+        else {
+            babylonMaterial.clearCoat.intensity = 0;
+        }
+
+        if (properties.clearcoatTexture) {
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/clearcoatTexture`, properties.clearcoatTexture, (texture) => {
+                texture.name = `${babylonMaterial.name} (ClearCoat Intensity)`;
+                babylonMaterial.clearCoat.texture = texture;
+            }));
+        }
+
+        if (properties.clearcoatRoughnessFactor) {
+            babylonMaterial.clearCoat.roughness = properties.clearcoatRoughnessFactor;
+        }
+        else {
+            babylonMaterial.clearCoat.roughness = 0;
+        }
+
+        if (properties.clearcoatRoughnessTexture) {
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/clearcoatRoughnessTexture`, properties.clearcoatRoughnessTexture, (texture) => {
+                texture.name = `${babylonMaterial.name} (ClearCoat Roughness)`;
+                babylonMaterial.clearCoat.texture = texture;
+            }));
+        }
+
+        if (properties.clearcoatNormalTexture) {
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/clearcoatNormalTexture`, properties.clearcoatNormalTexture, (texture) => {
+                texture.name = `${babylonMaterial.name} (ClearCoat Normal)`;
+                babylonMaterial.clearCoat.bumpTexture = texture;
+            }));
+
+            babylonMaterial.invertNormalMapX = !babylonMaterial.getScene().useRightHandedSystem;
+            babylonMaterial.invertNormalMapY = babylonMaterial.getScene().useRightHandedSystem;
+            if (properties.clearcoatNormalTexture.scale != undefined) {
+                babylonMaterial.clearCoat.bumpTexture!.level = properties.clearcoatNormalTexture.scale;
+            }
+        }
+
+        return Promise.all(promises).then(() => { });
+    }
+}
+
+GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_materials_clearcoat(loader));

+ 76 - 0
loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts

@@ -0,0 +1,76 @@
+import { Nullable } from "babylonjs/types";
+import { PBRMaterial } from "babylonjs/Materials/PBR/pbrMaterial";
+import { Material } from "babylonjs/Materials/material";
+
+import { IMaterial } from "../glTFLoaderInterfaces";
+import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
+import { GLTFLoader } from "../glTFLoader";
+
+const NAME = "KHR_materials_specular";
+
+interface IKHR_materials_specular {
+    specularFactor: number;
+}
+
+/**
+ * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1677)
+ */
+export class KHR_materials_specular implements IGLTFLoaderExtension {
+    /**
+     * The name of this extension.
+     */
+    public readonly name = NAME;
+
+    /**
+     * Defines whether this extension is enabled.
+     */
+    public enabled: boolean;
+
+    /**
+     * Defines a number that determines the order the extensions are applied.
+     */
+    public order = 230;
+
+    private _loader: GLTFLoader;
+
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
+        this.enabled = this._loader.isExtensionUsed(NAME);
+    }
+
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
+    }
+
+    /** @hidden */
+    public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
+        return GLTFLoader.LoadExtensionAsync<IKHR_materials_specular>(context, material, this.name, (extensionContext, extension) => {
+            const promises = new Array<Promise<any>>();
+            promises.push(this._loader.loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
+            promises.push(this._loadSpecularPropertiesAsync(extensionContext, material, extension, babylonMaterial));
+            this._loader.loadMaterialAlphaProperties(context, material, babylonMaterial);
+            return Promise.all(promises).then(() => { });
+        });
+    }
+
+    private _loadSpecularPropertiesAsync(context: string, material: IMaterial, properties: IKHR_materials_clearcoat, babylonMaterial: Material): Promise<void> {
+        if (!(babylonMaterial instanceof PBRMaterial)) {
+            throw new Error(`${context}: Material type not supported`);
+        }
+
+        if (properties.specularFactor) {
+            babylonMaterial.metallicF0Factor = properties.specularFactor;
+        }
+
+        if (properties.specularTexture) {
+            // This does not allow a separate sampler for it at the moment but is currently under discussion.
+            babylonMaterial.useMetallicF0FactorFromMetallicTexture = true;
+        }
+
+        return Promise.resolve();
+    }
+}
+
+GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_materials_specular(loader));

+ 2 - 0
loaders/src/glTF/2.0/Extensions/index.ts

@@ -3,6 +3,8 @@ export * from "./KHR_draco_mesh_compression";
 export * from "./KHR_lights_punctual";
 export * from "./KHR_materials_pbrSpecularGlossiness";
 export * from "./KHR_materials_unlit";
+export * from "./KHR_materials_clearcoat";
+export * from "./KHR_materials_specular";
 export * from "./KHR_texture_transform";
 export * from "./MSFT_audio_emitter";
 export * from "./MSFT_lod";

+ 25 - 1
src/Materials/PBR/pbrBaseMaterial.ts

@@ -99,6 +99,8 @@ export class PBRMaterialDefines extends MaterialDefines
     public ROUGHNESSSTOREINMETALMAPGREEN = false;
     public METALLNESSSTOREINMETALMAPBLUE = false;
     public AOSTOREINMETALMAPRED = false;
+    public METALLICF0FACTORFROMMETALLICMAP = false;
+
     public ENVIRONMENTBRDF = false;
     public ENVIRONMENTBRDF_RGBD = false;
 
@@ -399,6 +401,21 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _roughness: Nullable<number> = null;
 
     /**
+     * Specifies the an F0 factor to help configuring the material F0.
+     * Instead of the default 4%, 8% * factor will be used. As the factor is defaulting
+     * to 0.5 the previously hard coded value stays the same.
+     * Can also be used to scale the F0 values of the metallic texture.
+     */
+    protected _metallicF0Factor = 0.5;
+
+    /**
+     * Specifies whether the F0 factor can be fetched from the mettalic texture.
+     * If set to true, please adapt the metallicF0Factor to ensure it fits with
+     * your expectation as it multiplies with the texture data.
+     */
+    protected _useMetallicF0FactorFromMetallicTexture = false;
+
+    /**
      * Used to enable roughness/glossiness fetch from a separate channel depending on the current mode.
      * Gray Scale represents roughness in metallic mode and glossiness in specular mode.
      */
@@ -1418,6 +1435,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         defines.ROUGHNESSSTOREINMETALMAPGREEN = !this._useRoughnessFromMetallicTextureAlpha && this._useRoughnessFromMetallicTextureGreen;
                         defines.METALLNESSSTOREINMETALMAPBLUE = this._useMetallnessFromMetallicTextureBlue;
                         defines.AOSTOREINMETALMAPRED = this._useAmbientOcclusionFromMetallicTextureRed;
+                        defines.METALLICF0FACTORFROMMETALLICMAP = this._useMetallicF0FactorFromMetallicTexture;
                     }
                     else if (this._reflectivityTexture) {
                         MaterialHelper.PrepareDefinesForMergedUV(this._reflectivityTexture, defines, "REFLECTIVITY");
@@ -1792,7 +1810,13 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 if (defines.METALLICWORKFLOW) {
                     TmpColors.Color3[0].r = (this._metallic === undefined || this._metallic === null) ? 1 : this._metallic;
                     TmpColors.Color3[0].g = (this._roughness === undefined || this._roughness === null) ? 1 : this._roughness;
-                    ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], 0);
+
+                    // We are here deriving our default reflectance from a common value for none metallic surface.
+                    // Default specular reflectance at normal incidence.
+                    // 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
+                    // We then use 8% combined with a factor of 0.5 to allow some variations around the 0.04 default value.
+                    const metallicF0 = 0.08 * this._metallicF0Factor;
+                    ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], metallicF0);
                 }
                 else {
                     ubo.updateColor4("vReflectivityColor", this._reflectivityColor, this._microSurface);

+ 19 - 0
src/Materials/PBR/pbrMaterial.ts

@@ -166,6 +166,25 @@ export class PBRMaterial extends PBRBaseMaterial {
     public roughness: Nullable<number>;
 
     /**
+     * Specifies the an F0 factor to help configuring the material F0.
+     * Instead of the default 4%, 8% * factor will be used. As the factor is defaulting
+     * to 0.5 the previously hard coded value stays the same.
+     * Can also be used to scale the F0 values of the metallic texture.
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    protected metallicF0Factor = 0.5;
+
+    /**
+     * Specifies whether the F0 factor can be fetched from the mettalic texture.
+     * If set to true, please adapt the metallicF0Factor to ensure it fits with
+     * your expectation as it multiplies with the texture data.
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    protected useMetallicF0FactorFromMetallicTexture = false;
+
+    /**
      * Used to enable roughness/glossiness fetch from a separate channel depending on the current mode.
      * Gray Scale represents roughness in metallic mode and glossiness in specular mode.
      */

+ 2 - 0
src/Shaders/ShadersInclude/pbrDebug.fx

@@ -110,6 +110,8 @@
         #define DEBUGMODE_GAMMA
     #elif DEBUGMODE == 62 && defined(METALLICWORKFLOW)
         gl_FragColor.rgb = vec3(metallicRoughness.r);
+    #elif DEBUGMODE == 71 && defined(METALLICWORKFLOW)
+        gl_FragColor.rgb = metallicF0;
     #elif DEBUGMODE == 63
         gl_FragColor.rgb = vec3(roughness);
     #elif DEBUGMODE == 64

+ 8 - 7
src/Shaders/pbr.fragment.fx

@@ -211,17 +211,18 @@ void main(void) {
             // Compute the converted reflectivity.
             surfaceReflectivityColor = mix(0.16 * reflectance * reflectance, baseColor, metallicRoughness.r);
         #else
-            // we are here fixing our default reflectance to a common value for none metallic surface.
-
-            // Default specular reflectance at normal incidence.
-            // 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
-            const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
+            vec3 metallicF0 = vec3(vReflectivityColor.a);
+            #ifdef METALLICF0FACTORFROMMETALLICMAP
+                #ifdef REFLECTIVITY
+                    metallicF0 *= surfaceMetallicColorMap.a;
+                #endif
+            #endif
 
             // Compute the converted diffuse.
-            surfaceAlbedo = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
+            surfaceAlbedo = mix(baseColor.rgb * (1.0 - metallicF0.r), vec3(0., 0., 0.), metallicRoughness.r);
 
             // Compute the converted reflectivity.
-            surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);
+            surfaceReflectivityColor = mix(metallicF0, baseColor, metallicRoughness.r);
         #endif
     #else
         #ifdef REFLECTIVITY