I’ve created the following custom material by using the babylon node material editor: https://nme.babylonjs.com/#8F45L0#1
By importing a few meshes (.glb) into my babylon scene and assigning them the aforementioned material everything works ok except for the global ambient occlusion texture (see image).
I set it up in order to be mapped according the 2nd uv set (uv2) but it doesn’t seem to work at all.
Here’s a screenshot of the model in Blender (I’m exporting the glb files from an FBX model, 2 separated uv set) and the final babylon result (lower-right part of the image).
What am I doing wrong?
Welcome aboard!
It’s hard to tell because the node material seems ok as the AO texture takes its inputs from uv2: have you checked that the uv2 channel has the right data after the object is loaded in Babylon? What happens if you take your inputs from the uv channel instead?
Also, are you able to setup a repro in the Playground as it would be easier to diagnose?
You can use this doc if you need to host your assets: Using External Assets In the Playground | Babylon.js Documentation
mrcointreau:
I’m sorry but I’m not currently able to replicate the problem in the babylon playground at the moment.
Is it that you can’t create the PG by itself or that you can but the problem does not show up there?
mrcointreau:
I’m trying to get rid of the root parent element,
You can’t really throw it away, it holds some important transformation for the right/left handed handling. If you still want to remove it, you should set scene.useRightHandedSystem = true
so that this transformation is the identity matrix.
mrcointreau:
How can I check that the 2nd uv channel has the “right data”?
You can display in the console mesh.getVerticesData("uv2")
and see if the data “seem right”: meaning, values are around the [0,1] range and not all 0 for eg.
What if you try to display your file in the sandbox (https://sandbox.babylonjs.com/ )?
mrcointreau:
if I toggle the root .scaling the ambient map is mapped differently
By removing the root object you remove a mirror on Z, so it may be your problem. Try to either remove your code that tamper with the root node or set scene.useRightHandedSystem = true
.
Here’s the PG example: Babylon.js Playground
The funny thing is that in the PG the ambient occlusion texture works as expected, I even copied the PG code in my application and it works like a charm.
The main difference between the PG and my application is that in the node material pointed by the PG the textures are embedded in the node, while in my application I use the node material generated code (with some addition in order to handle the dynamic texture change).
Here’s the function I use in order to create a material:
const createPBRMaterial = (
alphaMapTextureUrl,
baseMapTextureUrl,
metalnessMapTextureUrl,
roughnessMapTextureUrl,
normalMapTextureUrl,
gridMapTextureUrl,
globalAmbientOcclusionTextureUrl
) => {
const nodeMaterial = new BABYLON.NodeMaterial('node')
// InputBlock
const position = new BABYLON.InputBlock('position')
position.setAsAttribute('position')
// TransformBlock
const WorldPos = new BABYLON.TransformBlock('WorldPos')
WorldPos.complementZ = 0
WorldPos.complementW = 1
// InputBlock
const World = new BABYLON.InputBlock('World')
World.setAsSystemValue(BABYLON.NodeMaterialSystemValues.World)
// TransformBlock
const Worldnormal = new BABYLON.TransformBlock('World normal')
Worldnormal.complementZ = 0
Worldnormal.complementW = 0
// InputBlock
const normal = new BABYLON.InputBlock('normal')
normal.setAsAttribute('normal')
// TransformBlock
const Worldnormal1 = new BABYLON.TransformBlock('World normal')
Worldnormal1.complementZ = 0
Worldnormal1.complementW = 0
// PerturbNormalBlock
const Perturbnormal = new BABYLON.PerturbNormalBlock('Perturb normal')
Perturbnormal.invertX = false
Perturbnormal.invertY = false
// TransformBlock
const Worldposition = new BABYLON.TransformBlock('World position')
Worldposition.complementZ = 0
Worldposition.complementW = 1
// TransformBlock
const Worldtangent = new BABYLON.TransformBlock('World tangent')
Worldtangent.complementZ = 0
Worldtangent.complementW = 0
// InputBlock
const tangent = new BABYLON.InputBlock('tangent')
tangent.setAsAttribute('tangent')
// InputBlock
const uv = new BABYLON.InputBlock('uv')
uv.setAsAttribute('uv')
// TextureBlock
const normalmap = new BABYLON.TextureBlock('normal map')
normalmap.texture = new BABYLON.Texture(normalMapTextureUrl, null)
normalmap.texture.wrapU = 1
normalmap.texture.wrapV = 1
normalmap.texture.uAng = 0
normalmap.texture.vAng = 0
normalmap.texture.wAng = 0
normalmap.texture.uOffset = 0
normalmap.texture.vOffset = 0
normalmap.texture.uScale = 1
normalmap.texture.vScale = 1
normalmap.convertToGammaSpace = false
normalmap.convertToLinearSpace = false
// TextureBlock
const basemap = new BABYLON.TextureBlock('base map')
basemap.texture = new BABYLON.Texture(baseMapTextureUrl, null)
basemap.texture.wrapU = 1
basemap.texture.wrapV = 1
basemap.texture.uAng = 0
basemap.texture.vAng = 0
basemap.texture.wAng = 0
basemap.texture.uOffset = 0
basemap.texture.vOffset = 0
basemap.texture.uScale = 1
basemap.texture.vScale = 1
basemap.convertToGammaSpace = false
basemap.convertToLinearSpace = false
// LerpBlock
const Lerp = new BABYLON.LerpBlock('Lerp')
Lerp.visibleInInspector = false
Lerp.visibleOnFrame = false
// MultiplyBlock
const Multiply = new BABYLON.MultiplyBlock('Multiply')
Multiply.visibleInInspector = false
Multiply.visibleOnFrame = false
// TextureBlock
const gridmap = new BABYLON.TextureBlock('grid map')
gridmap.texture = new BABYLON.Texture(gridMapTextureUrl, null)
gridmap.texture.wrapU = 1
gridmap.texture.wrapV = 1
gridmap.texture.uAng = 0
gridmap.texture.vAng = 0
gridmap.texture.wAng = 0
gridmap.texture.uOffset = 0
gridmap.texture.vOffset = 0
gridmap.texture.uScale = 1
gridmap.texture.vScale = 1
gridmap.convertToGammaSpace = false
gridmap.convertToLinearSpace = false
// VectorMergerBlock
const VectorMerger = new BABYLON.VectorMergerBlock('VectorMerger')
VectorMerger.visibleInInspector = false
VectorMerger.visibleOnFrame = false
// AddBlock
const Add = new BABYLON.AddBlock('Add')
Add.visibleInInspector = false
Add.visibleOnFrame = false
// MultiplyBlock
const Multiply1 = new BABYLON.MultiplyBlock('Multiply')
Multiply1.visibleInInspector = false
Multiply1.visibleOnFrame = false
// InputBlock
const gridhorizontalshiftspeed = new BABYLON.InputBlock('grid horizontal shift speed')
gridhorizontalshiftspeed.value = 0.1
gridhorizontalshiftspeed.min = 0
gridhorizontalshiftspeed.max = 0
gridhorizontalshiftspeed.isBoolean = false
gridhorizontalshiftspeed.matrixMode = 0
gridhorizontalshiftspeed.animationType = BABYLON.AnimatedInputBlockTypes.None
gridhorizontalshiftspeed.isConstant = false
// InputBlock
const Time = new BABYLON.InputBlock('Time')
Time.value = 0
Time.min = 0
Time.max = 0
Time.isBoolean = false
Time.matrixMode = 0
Time.animationType = BABYLON.AnimatedInputBlockTypes.Time
Time.isConstant = false
// MultiplyBlock
const Multiply2 = new BABYLON.MultiplyBlock('Multiply')
Multiply2.visibleInInspector = false
Multiply2.visibleOnFrame = false
// InputBlock
const gridverticalshiftspeed = new BABYLON.InputBlock('grid vertical shift speed')
gridverticalshiftspeed.value = 0.1
gridverticalshiftspeed.min = 0
gridverticalshiftspeed.max = 0
gridverticalshiftspeed.isBoolean = false
gridverticalshiftspeed.matrixMode = 0
gridverticalshiftspeed.animationType = BABYLON.AnimatedInputBlockTypes.None
gridverticalshiftspeed.isConstant = false
// AddBlock
const Add1 = new BABYLON.AddBlock('Add')
Add1.visibleInInspector = false
Add1.visibleOnFrame = false
// VectorSplitterBlock
const VectorSplitter = new BABYLON.VectorSplitterBlock('VectorSplitter')
VectorSplitter.visibleInInspector = false
VectorSplitter.visibleOnFrame = false
// InputBlock
const gridcolor = new BABYLON.InputBlock('grid color')
gridcolor.value = new BABYLON.Color3(1, 1, 1)
gridcolor.isConstant = false
// PBRMetallicRoughnessBlock
const PBRMetallicRoughness = new BABYLON.PBRMetallicRoughnessBlock('PBRMetallicRoughness')
PBRMetallicRoughness.lightFalloff = 0
PBRMetallicRoughness.useAlphaTest = false
PBRMetallicRoughness.alphaTestCutoff = 0.5
PBRMetallicRoughness.useAlphaBlending = true
PBRMetallicRoughness.useRadianceOverAlpha = true
PBRMetallicRoughness.useSpecularOverAlpha = true
PBRMetallicRoughness.enableSpecularAntiAliasing = false
PBRMetallicRoughness.realTimeFiltering = false
PBRMetallicRoughness.realTimeFilteringQuality = 8
PBRMetallicRoughness.useEnergyConservation = true
PBRMetallicRoughness.useRadianceOcclusion = true
PBRMetallicRoughness.useHorizonOcclusion = true
PBRMetallicRoughness.unlit = false
PBRMetallicRoughness.forceNormalForward = false
PBRMetallicRoughness.debugMode = 0
PBRMetallicRoughness.debugLimit = 0
PBRMetallicRoughness.debugFactor = 1
// InputBlock
const view = new BABYLON.InputBlock('view')
view.setAsSystemValue(BABYLON.NodeMaterialSystemValues.View)
// InputBlock
const cameraPosition = new BABYLON.InputBlock('cameraPosition')
cameraPosition.setAsSystemValue(BABYLON.NodeMaterialSystemValues.CameraPosition)
// TextureBlock
const metalnessmap = new BABYLON.TextureBlock('metalness map')
metalnessmap.texture = new BABYLON.Texture(metalnessMapTextureUrl, null)
metalnessmap.texture.wrapU = 1
metalnessmap.texture.wrapV = 1
metalnessmap.texture.uAng = 0
metalnessmap.texture.vAng = 0
metalnessmap.texture.wAng = 0
metalnessmap.texture.uOffset = 0
metalnessmap.texture.vOffset = 0
metalnessmap.texture.uScale = 1
metalnessmap.texture.vScale = 1
metalnessmap.convertToGammaSpace = false
metalnessmap.convertToLinearSpace = false
// TextureBlock
const roughnessmap = new BABYLON.TextureBlock('roughness map')
roughnessmap.texture = new BABYLON.Texture(roughnessMapTextureUrl, null)
roughnessmap.texture.wrapU = 1
roughnessmap.texture.wrapV = 1
roughnessmap.texture.uAng = 0
roughnessmap.texture.vAng = 0
roughnessmap.texture.wAng = 0
roughnessmap.texture.uOffset = 0
roughnessmap.texture.vOffset = 0
roughnessmap.texture.uScale = 1
roughnessmap.texture.vScale = 1
roughnessmap.convertToGammaSpace = false
roughnessmap.convertToLinearSpace = false
// TextureBlock
const GlobalAO = new BABYLON.TextureBlock('Global AO')
GlobalAO.texture = new BABYLON.Texture(globalAmbientOcclusionTextureUrl, null)
GlobalAO.texture.wrapU = 1
GlobalAO.texture.wrapV = 1
GlobalAO.texture.uAng = 0
GlobalAO.texture.vAng = 0
GlobalAO.texture.wAng = 0
GlobalAO.texture.uOffset = 0
GlobalAO.texture.vOffset = 0
GlobalAO.texture.uScale = 1
GlobalAO.texture.vScale = 1
GlobalAO.convertToGammaSpace = false
GlobalAO.convertToLinearSpace = false
// InputBlock
const uv1 = new BABYLON.InputBlock('uv2')
uv1.setAsAttribute('uv2')
// TextureBlock
const alphamap = new BABYLON.TextureBlock('alpha map')
alphamap.texture = new BABYLON.Texture(alphaMapTextureUrl, null)
alphamap.texture.wrapU = 1
alphamap.texture.wrapV = 1
alphamap.texture.uAng = 0
alphamap.texture.vAng = 0
alphamap.texture.wAng = 0
alphamap.texture.uOffset = 0
alphamap.texture.vOffset = 0
alphamap.texture.uScale = 1
alphamap.texture.vScale = 1
alphamap.convertToGammaSpace = false
alphamap.convertToLinearSpace = false
// ReflectionBlock
const Reflection = new BABYLON.ReflectionBlock('Reflection')
Reflection.useSphericalHarmonics = true
Reflection.forceIrradianceInFragment = false
// InputBlock
const Color = new BABYLON.InputBlock('Color3')
Color.value = new BABYLON.Color3(1, 1, 1)
Color.isConstant = false
// FragmentOutputBlock
const FragmentOutput = new BABYLON.FragmentOutputBlock('FragmentOutput')
FragmentOutput.visibleInInspector = false
FragmentOutput.visibleOnFrame = false
// InputBlock
const normalstrength = new BABYLON.InputBlock('normal strength')
normalstrength.value = 1
normalstrength.min = 0
normalstrength.max = 0
normalstrength.isBoolean = false
normalstrength.matrixMode = 0
normalstrength.animationType = BABYLON.AnimatedInputBlockTypes.None
normalstrength.isConstant = false
// TransformBlock
const WorldPosViewProjectionTransform = new BABYLON.TransformBlock('WorldPos * ViewProjectionTransform')
WorldPosViewProjectionTransform.complementZ = 0
WorldPosViewProjectionTransform.complementW = 1
// InputBlock
const ViewProjection = new BABYLON.InputBlock('ViewProjection')
ViewProjection.setAsSystemValue(BABYLON.NodeMaterialSystemValues.ViewProjection)
// VertexOutputBlock
const VertexOutput = new BABYLON.VertexOutputBlock('VertexOutput')
VertexOutput.visibleInInspector = false
VertexOutput.visibleOnFrame = false
// Connections
position.output.connectTo(WorldPos.vector)
World.output.connectTo(WorldPos.transform)
WorldPos.output.connectTo(WorldPosViewProjectionTransform.vector)
ViewProjection.output.connectTo(WorldPosViewProjectionTransform.transform)
WorldPosViewProjectionTransform.output.connectTo(VertexOutput.vector)
WorldPos.output.connectTo(PBRMetallicRoughness.worldPosition)
normal.output.connectTo(Worldnormal.vector)
World.output.connectTo(Worldnormal.transform)
Worldnormal.output.connectTo(PBRMetallicRoughness.worldNormal)
view.output.connectTo(PBRMetallicRoughness.view)
cameraPosition.output.connectTo(PBRMetallicRoughness.cameraPosition)
position.output.connectTo(Worldposition.vector)
World.output.connectTo(Worldposition.transform)
Worldposition.output.connectTo(Perturbnormal.worldPosition)
normal.output.connectTo(Worldnormal1.vector)
World.output.connectTo(Worldnormal1.transform)
Worldnormal1.output.connectTo(Perturbnormal.worldNormal)
tangent.output.connectTo(Worldtangent.vector)
World.output.connectTo(Worldtangent.transform)
Worldtangent.output.connectTo(Perturbnormal.worldTangent)
uv.output.connectTo(Perturbnormal.uv)
uv.output.connectTo(normalmap.uv)
normalmap.rgb.connectTo(Perturbnormal.normalMapColor)
normalstrength.output.connectTo(Perturbnormal.strength)
Perturbnormal.output.connectTo(PBRMetallicRoughness.perturbedNormal)
uv.output.connectTo(basemap.uv)
basemap.rgb.connectTo(Lerp.left)
gridhorizontalshiftspeed.output.connectTo(Multiply1.left)
Time.output.connectTo(Multiply1.right)
Multiply1.output.connectTo(Add.left)
uv.output.connectTo(VectorSplitter.xyIn)
VectorSplitter.x.connectTo(Add.right)
Add.output.connectTo(VectorMerger.x)
VectorSplitter.y.connectTo(Add1.left)
Time.output.connectTo(Multiply2.left)
gridverticalshiftspeed.output.connectTo(Multiply2.right)
Multiply2.output.connectTo(Add1.right)
Add1.output.connectTo(VectorMerger.y)
VectorMerger.xy.connectTo(gridmap.uv)
gridmap.rgb.connectTo(Multiply.left)
gridcolor.output.connectTo(Multiply.right)
Multiply.output.connectTo(Lerp.right)
gridmap.a.connectTo(Lerp.gradient)
Lerp.output.connectTo(PBRMetallicRoughness.baseColor)
uv.output.connectTo(metalnessmap.uv)
metalnessmap.r.connectTo(PBRMetallicRoughness.metallic)
uv.output.connectTo(roughnessmap.uv)
roughnessmap.r.connectTo(PBRMetallicRoughness.roughness)
uv1.output.connectTo(GlobalAO.uv)
GlobalAO.r.connectTo(PBRMetallicRoughness.ambientOcc)
uv.output.connectTo(alphamap.uv)
alphamap.r.connectTo(PBRMetallicRoughness.opacity)
position.output.connectTo(Reflection.position)
World.output.connectTo(Reflection.world)
Color.output.connectTo(Reflection.color)
Reflection.reflection.connectTo(PBRMetallicRoughness.reflection)
PBRMetallicRoughness.lighting.connectTo(FragmentOutput.rgb)
PBRMetallicRoughness.alpha.connectTo(FragmentOutput.a)
// Output nodes
nodeMaterial.addOutputNode(VertexOutput)
nodeMaterial.addOutputNode(FragmentOutput)
nodeMaterial.build()
return nodeMaterial
}
You can try to use the generated code in the PG and see if that works.
Yes, I’ll definitely do this, thank you!
I generated the code of the material with the embedded textures from here https://nme.babylonjs.com/#8F45L0#3
Updated PG: Babylon.js Playground
I get the ERR_INVALID_URL while trying to load the embdedded textures. The texture url has been generated by the node material editor, is it a bug?
If I replaced the data:octet/stream generated urls with public images urls it should still work, right?
I fixed the urls by replacing the data:octet/stream with dropbox-served resources but I get the same problem I have on the application.
ParseFromSnippetAsync PG: https://playground.babylonjs.com/#3XTXYR#2
Generated code PG: Babylon.js Playground
It looks like the material returned by the ParseFromSnippetAsync function is somehow different from the one created by the node material generated code.
Could you generate a glb file from Blender with the AOMap in? that way we would have a point of comparison with something that actually works.
Unfortunately I can’t use Blender yet, but I’ll ask for a friend of mine who knows it very well.
That said, this https://playground.babylonjs.com/#3XTXYR#2 is the desired result while this Babylon.js Playground is what I get instead.
Is there a way to compare two different babylon javascript material objects? I tried the JSON.stringify way but it tells me that the material are circular objects.
Some parameters of the texture and reflection blocks were not correctly serialized when generating code, this PR should fix the problem:
BabylonJS:master
← Popov72:fix-nme-reflectionblock-gencode
opened 11:28AM - 18 Jan 21 UTC
1 Like
Thank you very much Evgeni!!!