Babylon and PBR material assets
  • 27 Feb 2023
  • 7 Minutes to read
  • Contributors
  • Dark
    Light

Babylon and PBR material assets

  • Dark
    Light

Article Summary

Basic workflow

1. Upload babylon file

You can upload a babylon file like any other asset. The system automatically extracts material definitions from the uploaded babylon file and creates new material assets inside a folder called Materials3d.

If a material asset with the given name already exists within folder Materials3d, the existing material will not be overwritten and no new material asset will be created in this scenario but the existing asset will be linked to the uploaded babylon asset.

image.png

We only support PBR Materials at the moment. The editor warns the user if he tries to upload a babylon file which contains materials of other types and rejects the uploaded data completely.

2. View nodes and adjust tags in babylon asset

The babylon asset UI shows the node tree of the model, which helps the user to get an impression of the babylon file content.

When selecting a certain node, you will see the tags of this node.
"Tags" are a concept of Babylon.js for grouping nodes, see Babylon.js docs for detailed information.

The tag assignment can be adjusted by adding, removing and creating new tags.
These tags can then be used by the viewers tag manager, which is the preferred way to alter node data, like visibility, appearance and transformations.

Tags are extracted from the babylon file only on the first upload, which happens when creating a new babylon asset.

Tags from each subsequent file upload will be ignored.
However the existing node to tag assignments will be restored in the asset when uploading new babylon files.
For example, if the new babylon file contains a node that is already associated with a tag, this tag will be set on the node of the updated babylon asset as well.

3. Upload texture images & use them in materials

Upload texture images into folder Textures3d and link material textures to uploaded images using the material editor UI:

Only images within folder Textures3dcan be linked to material assets. The texture comboboxes in the material editor UI do not show images from other folders at all.

image.png

If material assets with references to local images have been created, those references are lost on upload but are automatically restored when creating image assets with the referenced name in folder Textures3d:

2022-09-26_15h56_40.gif

4. Assign bundle to configurator

See How to assign bundles to configurators for detailed description.

5. Use babylon & material assets in configurator

Use AssetUtils.getBabylonUrl to get the URL of the babylon asset which can be used in the glTF property of the viewers variant spec.

All materials within all bundles which are assigned to the configurator can be used in the viewer spec or in the parameter system of the viewer without any further configuration.

6. Upload & use viewer environment file

Upload environment files which provide IBL lighting as file asset.


See viewer docs for more info on environment files in general.

Create a component which provides the URL of the uploaded environment file e.g. using the following Hive rule:

// "Cups3d" = Name (or alias) of bundle assigned to configurator
// "env" = Folder name in which "studio" is located
// "studio" = Asset name
Cups3d.env.studio.url

Usage example in viewer

The following shows an exemplary viewer spec using babylon & material assets from assigned bundle Cups3d:

/**
 * @return {StructureJson}
 */
export function createSpec() {
  return {
    scene: {
      parameters: {
        // Component `EnvUrl` provides the URL e.g. using Hive rule `Cups3d.env.studio.url`
        environment: CmpUtils.getTextCmpValue("EnvUrl"),
      },
      // ...
    },
    setup: {
      instances: [ /* Nothing special here in regards to 3d assets */ ],
    },
    variants: {
      Cups: {
        // Get URL of asset named "cup_togo" within folder "models" of assigned bundle "Cups3d"
        glTF: AssetUtils.getBabylonUrl("Cups3d", "models.cup_togo"),
        elements: {
          Main: {
            paths: {
              include: ["ToGoCup"],
              exclude: [ "ToGoCup.toGoCup_leatherRing" ],
            },
          },
          LeatherRing: ["ToGoCup.toGoCup_leatherRing"],
        },
        parameters: {
          // Apply material defined in asset "leather_smooth" (within folder `Materials3d`)
          "LeatherRing.material": "leather_smooth",
        },
      },
    },
    // ...
  };
}
  • Example of how to toggle materials using the viewers parameter systemat runtime:
    // Apply material defined in asset "leather_rough" (within folder `Materials3d`) at runtime
    viewer.variantInstances.commitParameters({ 'LeatherRing.material': 'leather_rough' });
    

Material editor expert mode

The material editors expert mode can be used to add or adjust material settings which are not provided by the UI editor by directly editing the materials definition as JSON text.

JSON syntax

When editing raw JSON, one needs to be very careful to not break the syntax as an invalid syntax would make the material unusable for the viewer.

Some common mistakes which can break the JSON syntax:

  • Not having 2 " around the property names
  • Not closing every opening brackets ({, [, ...)
  • Having too many closing brackets (}, ], ...)
  • Omitting , where needed (e.g. when seperating 2 properties)
  • Having , in places where they are not allowed (e.g. after the last property of an object)
  • ...

The editor recognizes such invalid JSON, disables the Save changes button and provides a Restore last valid object button which simply restores to the last known valid JSON:

image.png

There are good external tools & services which can help identify & fixing syntax errors in JSON.
One example is the online JSON Formatter & Validator into which you can paste broken JSON and which sometimes fixes issues automatically or at least gives you a better idea about where the JSON is actually broken etc.

Material & base texture JSON examples

The following examples show different JSONs using all possible properties of Babylon.js's PBRMaterial & BaseTexture.

Data Types

  • string = text between two double quotes (e.g. state: "ready")
  • boolean = true or false
  • number = Any kind of number. Floating point numbers are separated with a ., not a ,! E.g. 0.5, 3.14, 13, ...
  • BaseTexture = An object as shown in the Base texture JSON example below
  • Nullable<xxx> = Either null or the type expressed by xxx
  • any = Can be anything, string, number, boolean, object, array, ...
  • Color3 = Array with 3 color values for red, green & blue, each having a value between 0 and 1. E.g. [ 1, 0, 0 ] = "Full" red, [ 1, 1, 0 ] = "Full" yellow (red + green) etc.

Property names

The properties allowed in the JSON and the ones mentioned in the Babylon.js docs at PBRMaterial & BaseTexture are not always the same. The Babylon.js docs describe available properties of the runtime material & texture objects, which are only indirectly related to the available properties in the serialized JSON format.

Also, not all properties mentioned in the Babylon.js docs are even available in the serialized JSON format.

Unfortunately, there is no official documentation or specification regarding the serialized JSON format, so it is best to always stick to the names used in the examples below which should be an exhaustive list of available properties.

Material JSON

{
  "id": string,
  "name": string,
  "metadata": any,
  "checkReadyOnEveryCall": boolean,
  "checkReadyOnlyOnce": boolean,
  "state": string,
  "alpha": number,
  "backFaceCulling": boolean,
  "cullBackFaces": boolean,
  "sideOrientation": integer,
  "alphaMode": integer,
  "disableDepthWrite": boolean,
  "disableColorWrite": boolean,
  "forceDepthWrite": boolean,
  "depthFunction": boolean,
  "separateCullingPass": boolean,
  "fogEnabled": boolean,
  "pointSize": number,
  "zOffset": number,
  "zOffsetUnits": number,
  "pointsCloud": boolean,
  "fillMode": number,
  "transparencyMode": Nullable<number>,
  "useLogarithmicDepth": boolean,
  "directIntensity": number,
  "emissiveIntensity": number,
  "environmentIntensity": number,
  "specularIntensity": number,
  "disableBumpMap": boolean,
  "albedoTexture": Nullable<BaseTexture>,
  "ambientTexture": Nullable<BaseTexture>,
  "ambientTextureStrength": number,
  "ambientTextureImpactOnAnalyticalLights": number,
  "opacityTexture": Nullable<BaseTexture>,
  "reflectionTexture": Nullable<BaseTexture>,
  "emissiveTexture": Nullable<BaseTexture>,
  "reflectivityTexture": Nullable<BaseTexture>,
  "metallicTexture": Nullable<BaseTexture>,
  "metallic": Nullable<number>,
  "roughness": Nullable<number>,
  "metallicF0Factor": number,
  "metallicReflectanceColor": Color3,
  "useOnlyMetallicFromMetallicReflectanceTexture": boolean,
  "metallicReflectanceTexture": Nullable<BaseTexture>,
  "reflectanceTexture": Nullable<BaseTexture>,
  "microSurfaceTexture": Nullable<BaseTexture>,
  "bumpTexture": Nullable<BaseTexture>,
  "lightmapTexture": Nullable<BaseTexture>,
  "ambient": Color3,
  "albedo": Color3,
  "reflectivity": Color3,
  "reflection": Color3,
  "emissive": Color3,
  "microSurface": number,
  "useLightmapAsShadowmap": boolean,
  "useAlphaFromAlbedoTexture": boolean,
  "forceAlphaTest": boolean,
  "alphaCutOff": number,
  "useSpecularOverAlpha": boolean,
  "useMicroSurfaceFromReflectivityMapAlpha": boolean,
  "useRoughnessFromMetallicTextureAlpha": boolean,
  "useRoughnessFromMetallicTextureGreen": boolean,
  "useMetallnessFromMetallicTextureBlue": boolean,
  "useAmbientOcclusionFromMetallicTextureRed": boolean,
  "useAmbientInGrayScale": boolean,
  "useAutoMicroSurfaceFromReflectivityMap": boolean,
  "usePhysicalLightFalloff": boolean,
  "useGLTFLightFalloff": boolean,
  "useRadianceOverAlpha": boolean,
  "useObjectSpaceNormalMap": boolean,
  "useParallax": boolean,
  "useParallaxOcclusion": boolean,
  "parallaxScaleBias": number,
  "disableLighting": boolean,
  "forceIrradianceInFragment": boolean,
  "maxSimultaneousLights": number,
  "invertNormalMapX": boolean,
  "invertNormalMapY": boolean,
  "twoSidedLighting": boolean,
  "useAlphaFresnel": boolean,
  "useLinearAlphaFresnel": boolean,
  "environmentBRDFTexture": Nullable<BaseTexture>,
  "forceNormalForward": boolean,
  "enableSpecularAntiAliasing": boolean,
  "useHorizonOcclusion": boolean,
  "useRadianceOcclusion": boolean,
  "unlit": boolean,
  "customType": string,
  "clearCoat": {
    "isEnabled": boolean,
    "intensity": number,
    "roughness": number,
    "indexOfRefraction": number,
    "texture": Nullable<BaseTexture>,
    "useRoughnessFromMainTexture": boolean,
    "textureRoughness": Nullable<BaseTexture>,
    "remapF0OnInterfaceChange": boolean,
    "bumpTexture": Nullable<BaseTexture>,
    "isTintEnabled": boolean,
    "tintColor": Color3,
    "tintColorAtDistance": number,
    "tintThickness": number,
    "tintTexture": Nullable<BaseTexture>
  },
  "anisotropy": {
    "isEnabled": boolean,
    "intensity": number,
    "direction": number[2], // Vector2
    "texture": Nullable<BaseTexture>,
  },
  "brdf": {
    "useEnergyConservation": boolean,
    "useSmithVisibilityHeightCorrelated": boolean,
    "useSphericalHarmonics": boolean,
    "useSpecularGlossinessInputEnergyConservation": boolean
  },
  "sheen": {
    "isEnabled": boolean,
    "linkSheenWithAlbedo": boolean,
    "intensity": number,
    "color": Color3,
    "texture": Nullable<BaseTexture>,
    "useRoughnessFromMainTexture": boolean,
    "roughness": Nullable<number>,
    "textureRoughness": Nullable<BaseTexture>,
    "albedoScaling": boolean
  },
  "subSurface": {
    "isRefractionEnabled": boolean,
    "isTranslucencyEnabled": boolean,
    "isScatteringEnabled": boolean,
    "refractionIntensity": number,
    "translucencyIntensity": number,
    "useAlbedoToTintRefraction": boolean,
    "useAlbedoToTintTranslucency": boolean,
    "thicknessTexture": Nullable<BaseTexture>,
    "refractionTexture": Nullable<BaseTexture>,
    "indexOfRefraction": number,
    "invertRefractionY": boolean,
    "linkRefractionWithTransparency": boolean,
    "minimumThickness": number,
    "maximumThickness": number,
    "useThicknessAsDepth": boolean,
    "tintColor": Color3,
    "tintColorAtDistance": number,
    "diffusionDistance": Color3,
    "useMaskFromThicknessTexture": boolean,
    "refractionIntensityTexture": Nullable<BaseTexture>,
    "translucencyIntensityTexture": Nullable<BaseTexture>,
    "useGltfStyleTextures": boolean
  },
  "iridescence": {
    "isEnabled": boolean,
    "intensity": number,
    "minimumThickness": number,
    "maximumThickness": number,
    "indexOfRefraction": number,
    "texture": Nullable<BaseTexture>,
    "thicknessTexture": Nullable<BaseTexture>
  }
}

Base texture JSON

{
  "uniqueId": number,
  "name": string, // Usually overwritten by server
  "metadata": any,
  "hasAlpha": boolean,
  "getAlphaFromRGB": boolean,
  "level": number,
  "coordinatesIndex": number,
  "coordinatesMode": number,
  "wrapU": number,
  "wrapV": number,
  "wrapR": number,
  "anisotropicFilteringLevel": number,
  "isCube": boolean,
  "is3D": boolean,
  "is2DArray": boolean,
  "gammaSpace": boolean,
  "invertZ": boolean,
  "lodLevelInAlpha": boolean,
  "lodGenerationOffset": boolean,
  "lodGenerationScale": number,
  "linearSpecularLOD": boolean,
  "irradianceTexture": Nullable<BaseTexture>,
  "isRenderTarget": boolean
}

Was this article helpful?

What's Next