David Catuhe 6 年之前
父节点
当前提交
51cef54279

+ 22 - 0
dist/preview release/babylon.d.ts

@@ -53589,6 +53589,28 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Block used to pertub normals based on a normal map
+     */
+    export class PertubNormalBlock extends NodeMaterialBlock {
+        /**
+         * Create a new PertubNormalBlock
+         * @param name defines the block name
+         */
+        constructor(name: string);
+        /**
+         * Gets the current class name
+         * @returns the class name
+         */
+        getClassName(): string;
+        /**
+         * Gets the output component
+         */
+        readonly output: NodeMaterialConnectionPoint;
+        protected _buildBlock(state: NodeMaterialBuildState): this;
+    }
+}
+declare module BABYLON {
+    /**
      * Block used to multiply 2 values
      */
     export class MultiplyBlock extends NodeMaterialBlock {

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/babylon.js


文件差异内容过多而无法显示
+ 137 - 50
dist/preview release/babylon.max.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 48 - 0
dist/preview release/babylon.module.d.ts

@@ -56142,11 +56142,37 @@ declare module "babylonjs/Materials/Node/Blocks/Dual/lightBlock" {
         _deserialize(serializationObject: any, scene: Scene, rootUrl: string): void;
     }
 }
+declare module "babylonjs/Materials/Node/Blocks/Dual/pertubNormalBlock" {
+    import { NodeMaterialBlock } from "babylonjs/Materials/Node/nodeMaterialBlock";
+    import { NodeMaterialBuildState } from "babylonjs/Materials/Node/nodeMaterialBuildState";
+    import { NodeMaterialConnectionPoint } from "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint";
+    /**
+     * Block used to pertub normals based on a normal map
+     */
+    export class PertubNormalBlock extends NodeMaterialBlock {
+        /**
+         * Create a new PertubNormalBlock
+         * @param name defines the block name
+         */
+        constructor(name: string);
+        /**
+         * Gets the current class name
+         * @returns the class name
+         */
+        getClassName(): string;
+        /**
+         * Gets the output component
+         */
+        readonly output: NodeMaterialConnectionPoint;
+        protected _buildBlock(state: NodeMaterialBuildState): this;
+    }
+}
 declare module "babylonjs/Materials/Node/Blocks/Dual/index" {
     export * from "babylonjs/Materials/Node/Blocks/Dual/fogBlock";
     export * from "babylonjs/Materials/Node/Blocks/Dual/lightBlock";
     export * from "babylonjs/Materials/Node/Blocks/Dual/textureBlock";
     export * from "babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock";
+    export * from "babylonjs/Materials/Node/Blocks/Dual/pertubNormalBlock";
 }
 declare module "babylonjs/Materials/Node/Blocks/Input/index" {
     export * from "babylonjs/Materials/Node/Blocks/Input/inputBlock";
@@ -119090,6 +119116,28 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Block used to pertub normals based on a normal map
+     */
+    export class PertubNormalBlock extends NodeMaterialBlock {
+        /**
+         * Create a new PertubNormalBlock
+         * @param name defines the block name
+         */
+        constructor(name: string);
+        /**
+         * Gets the current class name
+         * @returns the class name
+         */
+        getClassName(): string;
+        /**
+         * Gets the output component
+         */
+        readonly output: NodeMaterialConnectionPoint;
+        protected _buildBlock(state: NodeMaterialBuildState): this;
+    }
+}
+declare module BABYLON {
+    /**
      * Block used to multiply 2 values
      */
     export class MultiplyBlock extends NodeMaterialBlock {

+ 4 - 1
nodeEditor/src/blockTools.ts

@@ -37,6 +37,7 @@ import { ViewDirectionBlock } from 'babylonjs/Materials/Node/Blocks/viewDirectio
 import { LightInformationBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/lightInformationBlock';
 import { MaxBlock } from 'babylonjs/Materials/Node/Blocks/maxBlock';
 import { MinBlock } from 'babylonjs/Materials/Node/Blocks/minBlock';
+import { PertubNormalBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/pertubNormalBlock';
 
 export class BlockTools {
     public static GetBlockFromString(data: string) {
@@ -110,7 +111,9 @@ export class BlockTools {
             case "MaxBlock":
                 return new MaxBlock("Max");       
             case "MinBlock":
-                return new MinBlock("Min");                                                  
+                return new MinBlock("Min");        
+            case "PertubNormalBlock":                                          
+                return new PertubNormalBlock("Perturb normal");        
             case "CosBlock": {
                 let cosBlock = new TrigonometryBlock("Cos");
                 cosBlock.operation = TrigonometryBlockOperations.Cos;

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

@@ -35,7 +35,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
             Output_Blocks: ["VertexOutputBlock", "FragmentOutputBlock", "AlphaTestBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Round: ["StepBlock", "RoundBlock", "CeilingBlock", "FloorBlock"],
-            Scene_Attributes: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ReflectionTextureBlock", "ViewDirectionBlock"],
+            Scene_Attributes: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ReflectionTextureBlock", "ViewDirectionBlock", "PertubNormalBlock"],
             Trigonometry: ["CosBlock", "SinBlock", "AbsBlock", "ExpBlock", "Exp2Block"],
             Vector_Math: ["CrossBlock", "DotBlock", "TransformBlock", "FresnelBlock"],
         }

+ 1 - 1
src/Materials/Node/Blocks/Dual/fogBlock.ts

@@ -121,9 +121,9 @@ export class FogBlock extends NodeMaterialBlock {
     protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
-        state.sharedData.blocksWithDefines.push(this);
 
         if (state.target === NodeMaterialBlockTargets.Fragment) {
+            state.sharedData.blocksWithDefines.push(this);
             state.sharedData.bindableBlocks.push(this);
 
             state._emitFunctionFromInclude("fogFragmentDeclaration", `//${this.name}`, {

+ 2 - 9
src/Materials/Node/Blocks/Dual/lightBlock.ts

@@ -34,7 +34,7 @@ export class LightBlock extends NodeMaterialBlock {
         super(name, NodeMaterialBlockTargets.VertexAndFragment);
 
         this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
-        this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
+        this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Fragment);
 
         this.registerInput("cameraPosition", NodeMaterialBlockConnectionPointTypes.Vector3, false, NodeMaterialBlockTargets.Fragment);
         this.registerOutput("diffuseOutput", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
@@ -147,8 +147,6 @@ export class LightBlock extends NodeMaterialBlock {
 
     private _injectVertexCode(state: NodeMaterialBuildState) {
         let worldPos = this.worldPosition;
-        let worldNormal = this.worldNormal;
-
         let comments = `//${this.name}`;
 
         // Declaration
@@ -175,11 +173,6 @@ export class LightBlock extends NodeMaterialBlock {
             state.compilationString += `${worldPosVaryingName} = ${worldPos.associatedVariableName};\r\n`;
         }
 
-        let worldNormalVaryingName = "v_" + worldNormal.associatedVariableName;
-        if (state._emitVaryingFromString(worldNormalVaryingName, "vec4")) {
-            state.compilationString += `${worldNormalVaryingName} = ${worldNormal.associatedVariableName};\r\n`;
-        }
-
         if (this.light) {
             state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, {
                 replaceStrings: [
@@ -249,7 +242,7 @@ export class LightBlock extends NodeMaterialBlock {
             state.compilationString += `float glossiness = 0.;\r\n`;
             state.compilationString += `vec3 diffuseBase = vec3(0., 0., 0.);\r\n`;
             state.compilationString += `vec3 specularBase = vec3(0., 0., 0.);\r\n`;
-            state.compilationString += `vec3 normalW = v_${this.worldNormal.associatedVariableName}.xyz;\r\n`;
+            state.compilationString += `vec3 normalW = ${this.worldNormal.associatedVariableName}.xyz;\r\n`;
         }
 
         if (this.light) {

+ 2 - 0
src/Materials/Node/Blocks/Dual/textureBlock.ts

@@ -109,6 +109,8 @@ export class TextureBlock extends NodeMaterialBlock {
     }
 
     public get target() {
+        // TextureBlock has a special optimizations for uvs that come from the vertex shaders as they can be packed into a single varyings.
+        // But we need to detect uvs coming from fragment then
         if (!this.uv.isConnected) {
             return NodeMaterialBlockTargets.VertexAndFragment;
         }

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

@@ -1,4 +1,5 @@
 
 export * from "./fragmentOutputBlock";
 export * from "./alphaTestBlock";
-export * from "./imageProcessingBlock";
+export * from "./imageProcessingBlock";
+export * from "./pertubNormalBlock"

+ 139 - 0
src/Materials/Node/Blocks/Fragment/pertubNormalBlock.ts

@@ -0,0 +1,139 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { _TypeStore } from '../../../../Misc/typeStore';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { InputBlock } from '../Input/inputBlock';
+
+/**
+ * Block used to pertub normals based on a normal map
+ */
+export class PertubNormalBlock extends NodeMaterialBlock {
+    /**
+     * Create a new PertubNormalBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        // Vertex
+        this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Fragment);        
+        this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Fragment);        
+        this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("normalMapColor", NodeMaterialBlockConnectionPointTypes.Color3, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("strength", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
+
+        // Fragment
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4, NodeMaterialBlockTargets.Fragment);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "PertubNormalBlock";
+    }
+
+    /**
+     * Gets the world position input component
+     */
+    public get worldPosition(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }    
+
+    /**
+     * Gets the world normal input component
+     */
+    public get worldNormal(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the uv input component
+     */
+    public get uv(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }    
+    
+    /**
+    * Gets the normal map color input component
+    */
+    public get normalMapColor(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+    * Gets the strength input component
+    */
+    public get strength(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    /**
+     * Gets the output component
+     */
+    public get output(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        defines.setValue("BUMP", true);
+    }
+
+    public autoConfigure(material: NodeMaterial) {
+        if (!this.uv.isConnected) {
+            let uvInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "uv");
+
+            if (!uvInput) {
+                uvInput = new InputBlock("uv");
+                uvInput.setAsAttribute();
+            }
+            uvInput.output.connectTo(this.uv);
+        }
+
+        if (!this.strength.isConnected) {
+            let strengthInput = new InputBlock("strength");
+            strengthInput.value = 1.0;
+            strengthInput.output.connectTo(this.strength);
+        }
+    }    
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let comments = `//${this.name}`;        
+        let uv = this.uv;
+        let worldPosition = this.worldPosition;
+        let worldNormal = this.worldNormal;
+
+        state.sharedData.blocksWithDefines.push(this);
+
+        state._emitExtension("bump", "#extension GL_OES_standard_derivatives : enable");
+        state._emitFunctionFromInclude("bumpFragmentFunctions", comments, {
+            replaceStrings: [
+                { search: /vBumpInfos.y/g, replace: `1.0 / ${this.strength.associatedVariableName}`},
+                { search: /vTangentSpaceParams/g, replace: "vec2(1.0, 1.0)"},
+                { search: /vPositionW/g, replace: worldPosition.associatedVariableName + ".xyz"}
+            ]
+        });      
+        state.compilationString += this._declareOutput(this.output, state) + " = vec4(0.);\r\n";    
+        state.compilationString += state._emitCodeFromInclude("bumpFragment", comments, {
+            replaceStrings: [
+                { search: /perturbNormal\(TBN,vBumpUV\+uvOffset\)/g, replace: `perturbNormal(TBN, ${this.normalMapColor.associatedVariableName})` },
+                { search: /vBumpInfos.y/g, replace: `1.0 / ${this.strength.associatedVariableName}`},
+                { search: /vBumpUV/g, replace: uv.associatedVariableName},
+                { search: /vPositionW/g, replace: worldPosition.associatedVariableName + ".xyz"},
+                { search: /normalW=/g, replace: this.output.associatedVariableName + ".xyz = " },
+                { search: /normalW/g, replace: worldNormal.associatedVariableName + ".xyz" }
+            ]
+        });
+
+        return this;
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.PertubNormalBlock"] = PertubNormalBlock;

+ 1 - 1
src/Materials/Node/nodeMaterialBlock.ts

@@ -399,7 +399,7 @@ export class NodeMaterialBlock {
         const otherBlockWasGeneratedInVertexShader = block._buildTarget === NodeMaterialBlockTargets.Vertex && block.target !== NodeMaterialBlockTargets.VertexAndFragment;
 
         if (localBlockIsFragment && (
-            ((block.target & this.target) === 0) ||
+            ((block.target & input.target) === 0) ||
             (this.target !== NodeMaterialBlockTargets.VertexAndFragment && otherBlockWasGeneratedInVertexShader)
             )) { // context switch! We need a varying
             if ((!block.isInput && state.target !== block._buildTarget) // block was already emitted by vertex shader

+ 19 - 0
src/Materials/Node/nodeMaterialBuildState.ts

@@ -31,6 +31,11 @@ export class NodeMaterialBuildState {
      */
     public functions: { [key: string]: string } = {};
     /**
+     * Gets the list of emitted extensions
+     */
+    public extensions: { [key: string]: string } = {};
+
+    /**
      * Gets the target of the compilation state
      */
     public target: NodeMaterialBlockTargets;
@@ -103,6 +108,11 @@ export class NodeMaterialBuildState {
             this.compilationString = `\r\n${emitComments ? "//Attributes\r\n" : ""}${this._attributeDeclaration}\r\n${this.compilationString}`;
         }
 
+        for (var extensionName in this.extensions) {
+            let extension = this.extensions[extensionName];
+            this.compilationString = `${extension}\r\n${this.compilationString}`;
+        }
+
         this._builtCompilationString = this.compilationString;
     }
 
@@ -170,6 +180,15 @@ export class NodeMaterialBuildState {
     }
 
     /** @hidden */
+    public _emitExtension(name: string, extension: string) {
+        if (this.extensions[name]) {
+            return;
+        }
+
+        this.extensions[name] = extension;
+    }    
+
+    /** @hidden */
     public _emitFunction(name: string, code: string, comments: string) {
         if (this.functions[name]) {
             return;

+ 3 - 0
src/Materials/Node/nodeMaterialBuildStateSharedData.ts

@@ -126,6 +126,9 @@ export class NodeMaterialBuildStateSharedData {
         this.variableNames["specularBase"] = 0;
         this.variableNames["worldPos"] = 0;
 
+        // Exclude known varyings
+        this.variableNames["vTBN"] = 0;
+
         // Exclude defines
         this.defineNames["MAINUV0"] = 0;
         this.defineNames["MAINUV1"] = 0;

+ 5 - 0
src/Shaders/ShadersInclude/bumpFragmentFunctions.fx

@@ -61,6 +61,11 @@
 		return perturbNormal(cotangentFrame, texture2D(bumpSampler, uv).xyz, vBumpInfos.y);
 	}
 
+	vec3 perturbNormal(mat3 cotangentFrame, vec3 color)
+	{
+		return perturbNormal(cotangentFrame, color, vBumpInfos.y);
+	}
+
 	// Thanks to http://www.thetenthplanet.de/archives/1180
 	mat3 cotangent_frame(vec3 normal, vec3 p, vec2 uv)
 	{

二进制
tests/validation/LogoV3.png


二进制
tests/validation/ReferenceImages/node-material3.png


+ 5 - 0
tests/validation/config.json

@@ -12,6 +12,11 @@
             "referenceImage": "node-material2.png"
         },
         {
+            "title": "Node material #3",
+            "playgroundId": "#LWGVT0#1",
+            "referenceImage": "node-material3.png"
+        },          
+        {
             "title": "Basis loader",
             "playgroundId": "#4RN0VF#0",
             "referenceImage": "basis.png"

+ 1 - 1
tests/validation/index.css

@@ -7,7 +7,7 @@
 }
 
 body {
-    background: url("LogoV3.png");    
+    background: url("Logo.png");    
     background-position: center center;
     background-repeat: no-repeat;
 }

二进制
tests/validation/logo.png