Browse Source

First take at PBR blocks

Popov72 5 năm trước cách đây
mục cha
commit
9492a059ac

+ 7 - 1
nodeEditor/src/blockTools.ts

@@ -62,6 +62,8 @@ import { DerivativeBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/deriva
 import { RefractBlock } from 'babylonjs/Materials/Node/Blocks/refractBlock';
 import { RefractBlock } from 'babylonjs/Materials/Node/Blocks/refractBlock';
 import { ReflectBlock } from 'babylonjs/Materials/Node/Blocks/reflectBlock';
 import { ReflectBlock } from 'babylonjs/Materials/Node/Blocks/reflectBlock';
 import { DesaturateBlock } from 'babylonjs/Materials/Node/Blocks/desaturateBlock';
 import { DesaturateBlock } from 'babylonjs/Materials/Node/Blocks/desaturateBlock';
+import { PBRMetallicRoughnessBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/pbrMetallicRoughnessBlock';
+import { SheenBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/sheenBlock';
 
 
 export class BlockTools {
 export class BlockTools {
     public static GetBlockFromString(data: string, scene: Scene, nodeMaterial: NodeMaterial) {
     public static GetBlockFromString(data: string, scene: Scene, nodeMaterial: NodeMaterial) {
@@ -432,7 +434,11 @@ export class BlockTools {
 
 
                 return transformBlock;
                 return transformBlock;
             }              
             }              
-        }
+            case "PBRMetallicRoughnessBlock":
+                return new PBRMetallicRoughnessBlock("PBRMetallicRoughness");
+            case "SheenBlock":
+                return new SheenBlock("Sheen");
+            }
 
 
         return null;
         return null;
     }
     }

+ 4 - 1
nodeEditor/src/components/nodeList/nodeListComponent.tsx

@@ -116,7 +116,9 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
         "WorleyNoise3DBlock": "Creates a random pattern resembling cells.",
         "WorleyNoise3DBlock": "Creates a random pattern resembling cells.",
         "ReflectBlock": "Outputs the direction of the input vector reflected across the surface normal.",
         "ReflectBlock": "Outputs the direction of the input vector reflected across the surface normal.",
         "RefractBlock": "Outputs a direction simulating a deflection of the input vector.", 
         "RefractBlock": "Outputs a direction simulating a deflection of the input vector.", 
-        "Rotate2dBlock": "Rotates UV coordinates around the W axis."
+        "Rotate2dBlock": "Rotates UV coordinates around the W axis.",
+        "PBRMetallicRoughnessBlock": "PBR metallic/roughness material",
+        "SheenBlock": "Sheen block"
     }
     }
 
 
     constructor(props: INodeListComponentProps) {
     constructor(props: INodeListComponentProps) {
@@ -145,6 +147,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
             Mesh: ["InstancesBlock", "PositionBlock", "UVBlock", "ColorBlock", "NormalBlock", "PerturbNormalBlock", "NormalBlendBlock" , "TangentBlock", "MatrixIndicesBlock", "MatrixWeightsBlock", "WorldPositionBlock", "WorldNormalBlock", "WorldTangentBlock", "FrontFacingBlock"], 
             Mesh: ["InstancesBlock", "PositionBlock", "UVBlock", "ColorBlock", "NormalBlock", "PerturbNormalBlock", "NormalBlendBlock" , "TangentBlock", "MatrixIndicesBlock", "MatrixWeightsBlock", "WorldPositionBlock", "WorldNormalBlock", "WorldTangentBlock", "FrontFacingBlock"], 
             Noises: ["RandomNumberBlock", "SimplexPerlin3DBlock", "WorleyNoise3DBlock"],
             Noises: ["RandomNumberBlock", "SimplexPerlin3DBlock", "WorleyNoise3DBlock"],
             Output_Nodes: ["VertexOutputBlock", "FragmentOutputBlock", "DiscardBlock"],
             Output_Nodes: ["VertexOutputBlock", "FragmentOutputBlock", "DiscardBlock"],
+            PBR: ["PBRMetallicRoughnessBlock", "SheenBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             Scene: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ViewDirectionBlock"],
             Scene: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ViewDirectionBlock"],

+ 3 - 1
src/Materials/Node/Blocks/Fragment/index.ts

@@ -4,4 +4,6 @@ export * from "./imageProcessingBlock";
 export * from "./perturbNormalBlock";
 export * from "./perturbNormalBlock";
 export * from "./discardBlock";
 export * from "./discardBlock";
 export * from "./frontFacingBlock";
 export * from "./frontFacingBlock";
-export * from "./derivativeBlock";
+export * from "./derivativeBlock";
+export * from "./pbrMetallicRoughnessBlock";
+export * from "./sheenBlock";

+ 514 - 0
src/Materials/Node/Blocks/Fragment/pbrMetallicRoughnessBlock.ts

@@ -0,0 +1,514 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { MaterialHelper } from '../../../materialHelper';
+import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { NodeMaterialSystemValues } from '../../Enums/nodeMaterialSystemValues';
+import { InputBlock } from '../Input/inputBlock';
+import { Light } from '../../../../Lights/light';
+import { Nullable } from '../../../../types';
+import { _TypeStore } from '../../../../Misc/typeStore';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { Effect, IEffectCreationOptions } from '../../../effect';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Scene } from '../../../../scene';
+
+export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
+    private _lightId: number;
+
+    /**
+     * Gets or sets the light associated with this block
+     */
+    public light: Nullable<Light>;
+
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.VertexAndFragment);
+
+        this._isUnique = true;
+
+        this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
+        this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("perturbedNormal", NodeMaterialBlockConnectionPointTypes.Vector4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("cameraPosition", NodeMaterialBlockConnectionPointTypes.Vector3, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("baseColor", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("baseTexture", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("metallic", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("roughness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("metalRoughText", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("opacityTexture", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("ambientOcclusion", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("reflection", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("sheen", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("clearCoat", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("subSurface", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("anisotropy", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+
+        this.registerOutput("ambient", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("diffuse", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("specular", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("sheen", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("clearcoat", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("diffuseInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("specularInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("sheenInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("clearcoatInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("refraction", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("lighting", NodeMaterialBlockConnectionPointTypes.Color4, NodeMaterialBlockTargets.Fragment);
+        this.registerOutput("shadow", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Fragment);
+
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "PBRMetallicRoughnessBlock";
+    }
+
+    public get worldPosition(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    public get worldNormal(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    public get perturbedNormal(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    public get cameraPosition(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    public get baseColor(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    public get baseTexture(): NodeMaterialConnectionPoint {
+        return this._inputs[5];
+    }
+
+    public get metallic(): NodeMaterialConnectionPoint {
+        return this._inputs[6];
+    }
+
+    public get roughness(): NodeMaterialConnectionPoint {
+        return this._inputs[7];
+    }
+
+    public get metalRoughTexture(): NodeMaterialConnectionPoint {
+        return this._inputs[8];
+    }
+
+    public get opacityTexture(): NodeMaterialConnectionPoint {
+        return this._inputs[9];
+    }
+
+    public get ambientOcclusionParams(): NodeMaterialConnectionPoint {
+        return this._inputs[10];
+    }
+
+    public get reflectionParams(): NodeMaterialConnectionPoint {
+        return this._inputs[11];
+    }
+
+    public get sheenParams(): NodeMaterialConnectionPoint {
+        return this._inputs[12];
+    }
+
+    public get clearcoatParams(): NodeMaterialConnectionPoint {
+        return this._inputs[13];
+    }
+
+    public get subSurfaceParams(): NodeMaterialConnectionPoint {
+        return this._inputs[14];
+    }
+
+    public get anisotropyParams(): NodeMaterialConnectionPoint {
+        return this._inputs[15];
+    }
+
+    public get ambient(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    public get diffuse(): NodeMaterialConnectionPoint {
+        return this._outputs[1];
+    }
+
+    public get specular(): NodeMaterialConnectionPoint {
+        return this._outputs[2];
+    }
+
+    public get sheen(): NodeMaterialConnectionPoint {
+        return this._outputs[3];
+    }
+
+    public get clearcoat(): NodeMaterialConnectionPoint {
+        return this._outputs[4];
+    }
+
+    public get diffuseIndirect(): NodeMaterialConnectionPoint {
+        return this._outputs[5];
+    }
+
+    public get specularIndirect(): NodeMaterialConnectionPoint {
+        return this._outputs[6];
+    }
+
+    public get sheenIndirect(): NodeMaterialConnectionPoint {
+        return this._outputs[7];
+    }
+
+    public get clearcoatIndirect(): NodeMaterialConnectionPoint {
+        return this._outputs[8];
+    }
+
+    public get refraction(): NodeMaterialConnectionPoint {
+        return this._outputs[9];
+    }
+
+    public get lighting(): NodeMaterialConnectionPoint {
+        return this._outputs[10];
+    }
+
+    public get shadow(): NodeMaterialConnectionPoint {
+        return this._outputs[11];
+    }
+
+    public autoConfigure(material: NodeMaterial) {
+        if (!this.cameraPosition.isConnected) {
+            let cameraPositionInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.CameraPosition);
+
+            if (!cameraPositionInput) {
+                cameraPositionInput = new InputBlock("cameraPosition");
+                cameraPositionInput.setAsSystemValue(NodeMaterialSystemValues.CameraPosition);
+            }
+            cameraPositionInput.output.connectTo(this.cameraPosition);
+        }
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (!defines._areLightsDirty) {
+            return;
+        }
+
+        const scene = mesh.getScene();
+
+        if (!this.light) {
+            // Lights
+            MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, nodeMaterial.maxSimultaneousLights);
+            defines._needNormals = true;
+
+            // Multiview
+            //MaterialHelper.PrepareDefinesForMultiview(scene, defines);
+        } else {
+            let state = {
+                needNormals: false,
+                needRebuild: false,
+                lightmapMode: false,
+                shadowEnabled: false,
+                specularEnabled: false
+            };
+
+            MaterialHelper.PrepareDefinesForLight(scene, mesh, this.light, this._lightId, defines, true, state);
+
+            if (state.needRebuild) {
+                defines.rebuild();
+            }
+        }
+
+        if (this.baseTexture.isConnected) {
+            defines.setValue("ALBEDO", true);
+        }
+
+        if (this.opacityTexture.isConnected) {
+            defines.setValue("OPACITY", true);
+        }
+    }
+
+    public updateUniformsAndSamples(state: NodeMaterialBuildState, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines, uniformBuffers: string[]) {
+        MaterialHelper.PrepareUniformsAndSamplersList(<IEffectCreationOptions>{
+            uniformsNames: state.uniforms,
+            uniformBuffersNames: uniformBuffers,
+            samplers: state.samplers,
+            defines: defines,
+            maxSimultaneousLights: nodeMaterial.maxSimultaneousLights
+        });
+    }
+
+    public isReady() {
+        /*if (this.texture && !this.texture.isReadyOrNotBlocking()) {
+            return false;
+        }*/
+
+        return true;
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        if (!mesh) {
+            return;
+        }
+
+        const scene = mesh.getScene();
+
+        if (!this.light) {
+            MaterialHelper.BindLights(scene, mesh, effect, true, nodeMaterial.maxSimultaneousLights);
+        } else {
+            MaterialHelper.BindLight(this.light, this._lightId, scene, effect, true);
+        }
+
+        /*if (!mesh || !this.texture) {
+            return;
+        }
+
+        effect.setMatrix(this._reflectionMatrixName, this.texture.getReflectionTextureMatrix());
+
+        if (this.texture.isCube) {
+            effect.setTexture(this._cubeSamplerName, this.texture);
+        } else {
+            effect.setTexture(this._2DSamplerName, this.texture);
+        }*/
+    }
+
+    private _injectVertexCode(state: NodeMaterialBuildState) {
+        let worldPos = this.worldPosition;
+        let comments = `//${this.name}`;
+
+        // Declaration
+        if (!this.light) { // Emit for all lights
+            state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, {
+                repeatKey: "maxSimultaneousLights"
+            });
+            this._lightId = 0;
+
+            state.sharedData.dynamicUniformBlocks.push(this);
+        } else {
+            this._lightId = (state.counters["lightCounter"] !== undefined ? state.counters["lightCounter"] : -1) + 1;
+            state.counters["lightCounter"] = this._lightId;
+
+            state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, {
+                replaceStrings: [{ search: /{X}/g, replace: this._lightId.toString() }]
+            }, this._lightId.toString());
+        }
+
+        // Inject code in vertex
+        let worldPosVaryingName = "v_" + worldPos.associatedVariableName;
+        if (state._emitVaryingFromString(worldPosVaryingName, "vec4")) {
+            state.compilationString += `${worldPosVaryingName} = ${worldPos.associatedVariableName};\r\n`;
+        }
+
+        if (this.light) {
+            state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, {
+                replaceStrings: [
+                    { search: /{X}/g, replace: this._lightId.toString() },
+                    { search: /worldPos/g, replace: worldPos.associatedVariableName }
+                ]
+            });
+        } else {
+            state.compilationString += `vec4 worldPos = ${worldPos.associatedVariableName};\r\n`;
+            state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, {
+                repeatKey: "maxSimultaneousLights"
+            });
+        }
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        if (state.target !== NodeMaterialBlockTargets.Fragment) {
+            // Vertex
+            this._injectVertexCode(state);
+
+            return;
+        }
+
+        // Fragment
+        state.sharedData.bindableBlocks.push(this);
+        state.sharedData.blocksWithDefines.push(this);
+
+        let comments = `//${this.name}`;
+        let worldPos = this.worldPosition;
+        let normalShading = this.perturbedNormal;
+
+        //
+        // Includes
+        //
+        if (!this.light) { // Emit for all lights
+            state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, {
+                repeatKey: "maxSimultaneousLights"
+            });
+        } else {
+            state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightUboDeclaration" : "lightFragmentDeclaration", comments, {
+                replaceStrings: [{ search: /{X}/g, replace: this._lightId.toString() }]
+            }, this._lightId.toString());
+        }
+
+        state._emitFunctionFromInclude("helperFunctions", comments);
+        state._emitFunctionFromInclude("pbrHelperFunctions", comments);
+
+        state._emitFunctionFromInclude("shadowsFragmentFunctions", comments, {
+            replaceStrings: [
+                { search: /vPositionW/g, replace: "v_" + worldPos.associatedVariableName + ".xyz" }
+            ]
+        });
+
+        state._emitFunctionFromInclude("harmonicsFunctions", comments);
+
+        state._emitFunctionFromInclude("pbrDirectLightingSetupFunctions", comments, {
+            replaceStrings: [
+                { search: /vPositionW/g, replace: "v_" + worldPos.associatedVariableName + ".xyz" }
+            ]
+        });
+
+        state._emitFunctionFromInclude("pbrDirectLightingFalloffFunctions", comments);
+        state._emitFunctionFromInclude("pbrBRDFFunctions", comments);
+
+        state._emitFunctionFromInclude("pbrDirectLightingFunctions", comments, {
+            replaceStrings: [
+                { search: /vPositionW/g, replace: "v_" + worldPos.associatedVariableName + ".xyz" }
+            ]
+        });
+
+        state._emitFunctionFromInclude("pbrIBLFunctions", comments);
+
+        state._emitFunctionFromInclude("pbrBlockAlbedoOpacity", comments);
+        state._emitFunctionFromInclude("pbrBlockReflectivity", comments);
+        state._emitFunctionFromInclude("pbrBlockAmbientOcclusion", comments);
+        state._emitFunctionFromInclude("pbrBlockAlphaFresnel", comments);
+        state._emitFunctionFromInclude("pbrBlockAnisotropic", comments);
+        state._emitFunctionFromInclude("pbrBlockReflection", comments);
+        state._emitFunctionFromInclude("pbrBlockSheen", comments);
+        state._emitFunctionFromInclude("pbrBlockClearcoat", comments);
+        state._emitFunctionFromInclude("pbrBlockSubSurface", comments);
+
+        //
+        // code
+        //
+        if (this._lightId === 0) {
+            // _____________________________ Geometry Information ____________________________
+            if (state._registerTempVariable("viewDirectionW")) {
+                state.compilationString += `vec3 viewDirectionW = normalize(${this.cameraPosition.associatedVariableName} - ${"v_" + worldPos.associatedVariableName}.xyz);\r\n`;
+            }
+
+            state.compilationString += `vec3 geometricNormalW = ${this.worldNormal.associatedVariableName}.xyz;\r\n`;
+
+            state.compilationString += `vec3 normalW = ${normalShading.isConnected ? normalShading.associatedVariableName + ".xyz" : "geometricNormalW"};\r\n`;
+
+            state.compilationString += state._emitCodeFromInclude("pbrBlockNormalFinal", comments, {
+                replaceStrings: [
+                    { search: /vPositionW/g, replace: worldPos.associatedVariableName },
+                    { search: /vEyePosition.w/g, replace: "1." },
+                ]
+            });
+
+            // _____________________________ Albedo & Opacity ______________________________
+            state.compilationString += `albedoOpacityOutParams albedoOpacityOut;\r\n`;
+
+            const albedoColor = this.baseColor.isConnected ? this.baseColor.associatedVariableName : "vec4(1., 1., 1., 1.)";
+            const albedoTexture = this.baseTexture.isConnected ? this.baseTexture.associatedVariableName : "";
+            const opacityTexture = this.opacityTexture.isConnected ? this.opacityTexture.associatedVariableName : "";
+
+            state.compilationString += `albedoOpacityBlock(
+                    ${albedoColor},
+                #ifdef ALBEDO
+                    ${albedoTexture},
+                    vec2(1., 1.),
+                #endif
+                #ifdef OPACITY
+                    ${opacityTexture},
+                    vec2(1., 1.),
+                #endif
+                    albedoOpacityOut
+                );
+
+                vec3 surfaceAlbedo = albedoOpacityOut.surfaceAlbedo;
+                float alpha = albedoOpacityOut.alpha;\r\n`;
+
+            // _____________________________ AO  _______________________________
+            state.compilationString += `ambientOcclusionOutParams aoOut;\r\n`;
+
+            const aoBlock = this.ambientOcclusionParams.connectedPoint?.ownerBlock;
+            const aoTexture = "";//this.ambientOcclusion.isConnected ? this.aoTexture.associatedVariableName : "";
+
+            state.compilationString += `ambientOcclusionBlock(
+                    #ifdef AMBIENT
+                        ${aoTexture},
+                        vec2(1., 1.),
+                    #endif
+                        aoOut
+                    );\r\n`;
+
+            // _____________________________ Direct Lighting Info __________________________________
+            //state.compilationString += state._emitCodeFromInclude("pbrBlockDirectLighting", comments);
+        }
+
+        /*if (this.light) {
+            state.compilationString += state._emitCodeFromInclude("lightFragment", comments, {
+                replaceStrings: [
+                    { search: /{X}/g, replace: this._lightId.toString() }
+                ]
+            });
+        } else {
+            state.compilationString += state._emitCodeFromInclude("lightFragment", comments, {
+                repeatKey: "maxSimultaneousLights"
+            });
+        }
+
+        // _____________________________ Compute Final Lit and Unlit Components ________________________
+        state.compilationString += state._emitCodeFromInclude("pbrBlockFinalLitComponents", comments);
+
+        state.compilationString += state._emitCodeFromInclude("pbrBlockFinalUnlitComponents", comments);
+
+        state.compilationString += state._emitCodeFromInclude("pbrBlockFinalColorComposition", comments);*/
+
+        if (this.lighting.hasEndpoints) {
+            state.compilationString += this._declareOutput(this.lighting, state) + ` = vec4(surfaceAlbedo, alpha);\r\n`;
+        }
+
+        if (this.shadow.hasEndpoints) {
+            state.compilationString += this._declareOutput(this.shadow, state) + ` = shadow;\r\n`;
+        }
+
+        console.log(this.sheenParams);
+        (window as any).sheenParams = this.sheenParams;
+        return this;
+    }
+
+    public serialize(): any {
+        let serializationObject = super.serialize();
+
+        if (this.light) {
+            serializationObject.lightId = this.light.id;
+        }
+
+        /*if (this.texture) {
+            serializationObject.texture = this.texture.serialize();
+        }*/
+
+        return serializationObject;
+    }
+
+    public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
+        super._deserialize(serializationObject, scene, rootUrl);
+
+        if (serializationObject.lightId) {
+            this.light = scene.getLightByID(serializationObject.lightId);
+        }
+
+        /*if (serializationObject.texture) {
+            rootUrl = serializationObject.texture.url.indexOf("data:") === 0 ? "" : rootUrl;
+            if (serializationObject.texture.isCube) {
+                this.texture = CubeTexture.Parse(serializationObject.texture, scene, rootUrl);
+            } else {
+                this.texture = Texture.Parse(serializationObject.texture, scene, rootUrl);
+            }
+        }*/
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.PBRMetallicRoughnessBlock"] = PBRMetallicRoughnessBlock;

+ 61 - 0
src/Materials/Node/Blocks/Fragment/sheenBlock.ts

@@ -0,0 +1,61 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
+import { Nullable } from '../../../../types';
+import { _TypeStore } from '../../../../Misc/typeStore';
+
+import { BaseTexture } from '../../../Textures/baseTexture';
+
+export class SheenBlock extends NodeMaterialBlock {
+
+    public texture: Nullable<BaseTexture>;
+
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this.registerInput("intensity", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("roughness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("albedoScaling", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+
+        this.registerOutput("sheen", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Fragment);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "SheenBlock";
+    }
+
+    public get intensity(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    public get color(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    public get roughness(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    public get albedoScaling(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    public get sheen(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        return this;
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.SheenBlock"] = SheenBlock;