From e7dca4d8cc8f65a7045aa368e7011dbb47843722 Mon Sep 17 00:00:00 2001 From: Nikolai Prokoschenko Date: Wed, 14 Apr 2021 19:08:48 +0200 Subject: [PATCH] Allow skipping texture level multiplication for NodeMaterial's TextureBlock Reference discussion: https://forum.babylonjs.com/t/nme-make-texture-level-work-for-normal-maps-as-it-does-in-other-materials/19443/ --- dist/preview release/what's new.md | 1 + .../texturePropertyTabComponent.tsx | 13 ++++++++- .../Node/Blocks/Dual/textureBlock.ts | 28 +++++++++++++++++-- .../Blocks/Fragment/perturbNormalBlock.ts | 10 +++---- src/Shaders/ShadersInclude/bumpFragment.fx | 6 ++-- .../ShadersInclude/bumpFragmentFunctions.fx | 20 +------------ 6 files changed, 47 insertions(+), 31 deletions(-) diff --git a/dist/preview release/what's new.md b/dist/preview release/what's new.md index aa53719685a..f098dafebe6 100644 --- a/dist/preview release/what's new.md +++ b/dist/preview release/what's new.md @@ -78,6 +78,7 @@ - Increased float precision to 4 ([msDestiny14](https://github.com/msDestiny14)) - Added ability to make input node's properties visible in the properties of a custom frame ([msDestiny14](https://github.com/msDestiny14)) +- NME `TextureBlock`: add an output for the texture level and a switch to disable the internal multiplication (level * texture) ([#10192](https://github.com/BabylonJS/Babylon.js/pull/10192)) ([rassie](https://github.com/rassie)) ### GUIEditor diff --git a/nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx b/nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx index 4242b5adce9..0063d31060c 100644 --- a/nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx +++ b/nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx @@ -234,6 +234,17 @@ export class TexturePropertyTabComponent extends React.Component } + { + texture && + { + this.props.globalState.onUpdateRequiredObservable.notifyObservers(); + }} + /> + } { texture && texture.updateSamplingMode && { @@ -355,4 +366,4 @@ export class TexturePropertyTabComponent extends React.Component ); } -} \ No newline at end of file +} diff --git a/src/Materials/Node/Blocks/Dual/textureBlock.ts b/src/Materials/Node/Blocks/Dual/textureBlock.ts index 463aea01f6b..507d2ad9b2e 100644 --- a/src/Materials/Node/Blocks/Dual/textureBlock.ts +++ b/src/Materials/Node/Blocks/Dual/textureBlock.ts @@ -73,6 +73,11 @@ export class TextureBlock extends NodeMaterialBlock { */ public convertToLinearSpace = false; + /** + * Gets or sets a boolean indicating if multiplication of texture with level should be disabled + */ + public disableLevelMultiplication = false; + /** * Create a new TextureBlock * @param name defines the block name @@ -91,6 +96,8 @@ export class TextureBlock extends NodeMaterialBlock { this.registerOutput("b", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral); this.registerOutput("a", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral); + this.registerOutput("level", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Neutral); + this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Vector3); this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Vector4); @@ -154,6 +161,13 @@ export class TextureBlock extends NodeMaterialBlock { return this._outputs[5]; } + /** + * Gets the level output component + */ + public get level(): NodeMaterialConnectionPoint { + return this._outputs[6]; + } + public get target() { if (this._fragmentOnly) { return NodeMaterialBlockTargets.Fragment; @@ -291,6 +305,8 @@ export class TextureBlock extends NodeMaterialBlock { this._textureTransformName = state._getFreeVariableName("textureTransform"); this._textureInfoName = state._getFreeVariableName("textureInfoName"); + this.level.associatedVariableName = this._textureInfoName; + state._emitVaryingFromString(this._transformedUVName, "vec2", this._defineName); state._emitVaryingFromString(this._mainUVName, "vec2", this._mainUVDefineName); @@ -309,7 +325,7 @@ export class TextureBlock extends NodeMaterialBlock { this._writeTextureRead(state, true); for (var output of this._outputs) { - if (output.hasEndpoints) { + if (output.hasEndpoints && output.name !== "level") { this._writeOutput(state, output, output.name, true); } } @@ -371,8 +387,11 @@ export class TextureBlock extends NodeMaterialBlock { this._generateConversionCode(state, output, swizzle); return; } + let complement = ""; - const complement = ` * ${this._textureInfoName}`; + if (!this.disableLevelMultiplication) { + complement = ` * ${this._textureInfoName}`; + } state.compilationString += `${this._declareOutput(output, state)} = ${this._tempTextureRead}.${swizzle}${complement};\r\n`; this._generateConversionCode(state, output, swizzle); @@ -425,7 +444,7 @@ export class TextureBlock extends NodeMaterialBlock { this._writeTextureRead(state); for (var output of this._outputs) { - if (output.hasEndpoints) { + if (output.hasEndpoints && output.name !== "level") { this._writeOutput(state, output, output.name); } } @@ -438,6 +457,7 @@ export class TextureBlock extends NodeMaterialBlock { codeString += `${this._codeVariableName}.convertToGammaSpace = ${this.convertToGammaSpace};\r\n`; codeString += `${this._codeVariableName}.convertToLinearSpace = ${this.convertToLinearSpace};\r\n`; + codeString += `${this._codeVariableName}.disableLevelMultiplication = ${this.disableLevelMultiplication};\r\n`; if (!this.texture) { return codeString; @@ -464,6 +484,7 @@ export class TextureBlock extends NodeMaterialBlock { serializationObject.convertToGammaSpace = this.convertToGammaSpace; serializationObject.convertToLinearSpace = this.convertToLinearSpace; serializationObject.fragmentOnly = this._fragmentOnly; + serializationObject.disableLevelMultiplication = this.disableLevelMultiplication; if (this.texture && !this.texture.isRenderTarget && this.texture.getClassName() !== "VideoTexture") { serializationObject.texture = this.texture.serialize(); } @@ -477,6 +498,7 @@ export class TextureBlock extends NodeMaterialBlock { this.convertToGammaSpace = serializationObject.convertToGammaSpace; this.convertToLinearSpace = !!serializationObject.convertToLinearSpace; this._fragmentOnly = !!serializationObject.fragmentOnly; + this.disableLevelMultiplication = !!serializationObject.disableLevelMultiplication; if (serializationObject.texture && !NodeMaterial.IgnoreTexturesAtLoadTime && serializationObject.texture.url !== undefined) { rootUrl = serializationObject.texture.url.indexOf("data:") === 0 ? "" : rootUrl; diff --git a/src/Materials/Node/Blocks/Fragment/perturbNormalBlock.ts b/src/Materials/Node/Blocks/Fragment/perturbNormalBlock.ts index 9f8c14b79e2..3b47dc2410f 100644 --- a/src/Materials/Node/Blocks/Fragment/perturbNormalBlock.ts +++ b/src/Materials/Node/Blocks/Fragment/perturbNormalBlock.ts @@ -177,18 +177,18 @@ export class PerturbNormalBlock extends NodeMaterialBlock { state._emitFunctionFromInclude("bumpFragmentFunctions", comments, { replaceStrings: [ { search: /vBumpInfos.y/g, replace: replaceForBumpInfos}, - { search: /vTangentSpaceParams/g, replace: this._tangentSpaceParameterName}, { search: /vPositionW/g, replace: worldPosition.associatedVariableName + ".xyz"}, { search: /varying vec2 vBumpUV;/g, replace: ""}, - { search: /uniform sampler2D bumpSampler;[\s\S]*?\}/g, replace: ""}, + { search: /uniform sampler2D bumpSampler;/g, replace: ""}, ] }); 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: replaceForBumpInfos}, + { search: /perturbNormal\(TBN,texture2D\(bumpSampler,vBumpUV\+uvOffset\).xyz,vBumpInfos.y\)/g, replace: `perturbNormal(TBN, ${this.normalMapColor.associatedVariableName}, vBumpInfos.y)` }, + { search: /vTangentSpaceParams/g, replace: this._tangentSpaceParameterName}, + { search: /vBumpInfos.y/g, replace: replaceForBumpInfos }, { search: /vBumpUV/g, replace: uv.associatedVariableName}, { search: /vPositionW/g, replace: worldPosition.associatedVariableName + ".xyz"}, { search: /normalW=/g, replace: this.output.associatedVariableName + ".xyz = " }, @@ -226,4 +226,4 @@ export class PerturbNormalBlock extends NodeMaterialBlock { } } -_TypeStore.RegisteredTypes["BABYLON.PerturbNormalBlock"] = PerturbNormalBlock; \ No newline at end of file +_TypeStore.RegisteredTypes["BABYLON.PerturbNormalBlock"] = PerturbNormalBlock; diff --git a/src/Shaders/ShadersInclude/bumpFragment.fx b/src/Shaders/ShadersInclude/bumpFragment.fx index b1bc9954f7f..bbda64afdeb 100644 --- a/src/Shaders/ShadersInclude/bumpFragment.fx +++ b/src/Shaders/ShadersInclude/bumpFragment.fx @@ -14,7 +14,7 @@ #elif defined(BUMP) // flip the uv for the backface vec2 TBNUV = gl_FrontFacing ? vBumpUV : -vBumpUV; - mat3 TBN = cotangent_frame(normalW * normalScale, vPositionW, TBNUV); + mat3 TBN = cotangent_frame(normalW * normalScale, vPositionW, TBNUV, vTangentSpaceParams); #else // flip the uv for the backface vec2 TBNUV = gl_FrontFacing ? vDetailUV : -vDetailUV; @@ -52,7 +52,7 @@ normalW = normalize(texture2D(bumpSampler, vBumpUV).xyz * 2.0 - 1.0); normalW = normalize(mat3(normalMatrix) * normalW); #elif !defined(DETAIL) - normalW = perturbNormal(TBN, vBumpUV + uvOffset); + normalW = perturbNormal(TBN, texture2D(bumpSampler, vBumpUV + uvOffset).xyz, vBumpInfos.y); #else vec3 bumpNormal = texture2D(bumpSampler, vBumpUV + uvOffset).xyz * 2.0 - 1.0; // Reference for normal blending: https://blog.selfshadow.com/publications/blending-in-detail/ @@ -70,4 +70,4 @@ #elif defined(DETAIL) detailNormal.xy *= vDetailInfos.z; normalW = perturbNormalBase(TBN, detailNormal, vDetailInfos.z); -#endif \ No newline at end of file +#endif diff --git a/src/Shaders/ShadersInclude/bumpFragmentFunctions.fx b/src/Shaders/ShadersInclude/bumpFragmentFunctions.fx index d4846d048f4..29cf60660c4 100644 --- a/src/Shaders/ShadersInclude/bumpFragmentFunctions.fx +++ b/src/Shaders/ShadersInclude/bumpFragmentFunctions.fx @@ -7,11 +7,6 @@ varying vec2 vBumpUV; #endif uniform sampler2D bumpSampler; - - vec3 perturbNormal(mat3 cotangentFrame, vec2 uv) - { - return perturbNormal(cotangentFrame, texture2D(bumpSampler, uv).xyz, vBumpInfos.y); - } #endif #if defined(DETAIL) @@ -25,19 +20,6 @@ uniform sampler2D detailSampler; #endif -#if defined(BUMP) - 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) - { - return cotangent_frame(normal, p, uv, vTangentSpaceParams); - } -#endif - #if defined(BUMP) && defined(PARALLAX) const float minSamples = 4.; const float maxSamples = 15.; @@ -96,4 +78,4 @@ vec2 texCoordOffset = heightScale * viewDir.xy * height; return -texCoordOffset; } -#endif \ No newline at end of file +#endif