3D-tiles get very dark textures


I am creating 3D-tiles via FME:s writer Cesium 3D Tiles. The origin data are .obj(with .mlt and .png)
When I load these tilesets in Cesium the buildings appear very dark. Is there a way I can change that with CesiumJS?

If I write KML data via FME, the texture is preserved as it should be. If I upload KMZ and let ion tile it for me the texture is preserved.

FME workflow
OBJ–>3Dtiles (dark texture)
OBJ–>KMZ–>3Dtiles (dark texture)
OBJ–>KMZ–> let ion tile -->3Dtiles (preserved texture)

I’ve read multple post about dark textures, but not really found a working solution.
I tried to use this GitHub - PrincessGod/objTo3d-tiles: Convert obj model file to 3d tiles But it’s no longer maintained so I did not get it to work even tho the transformation went through but noting showed up when I uploaded it to ion. I’ve also tried this GitHub - CesiumGS/obj2gltf: Convert OBJ assets to glTF which preserved the textures but not the location data. When I upload it to ion I had to set its position and they were flipped 90* to add, they also shake every time I rotate the camera…

Does anyone have a working tranformation process from .obj to 3Dtiles? I have also made a post in FMEs Community. FME Community

Ideally you would want to tile from obj to 3d tiles yourself, but I’m afraid that requires the on prem plan? We are planning to go for the commercial plan.


maybe it’s the metallicFactor (again)? default value = 1.0 gives dark buildings White 3d tiles very dark - #2 by Marco13

It may be? May I ask how you created your 3D-tileset? @bertt

see https://github.com/Geodan/pg2b3dm/blob/master/getting_started.md , I recently added metallicFactor=0 to prevent dark buildings

Thanks, but that won’t let me go from .obj(.mlt and .png) to 3Dtiles right?
When I check my tileset.json file that is created from fme when writing tiles, I do not have a
So how could I set metallicFactor and roughnessFactor then?

it’s inside the gltf the metallicFactor and roughnessFactor

Yes, now I remember I recently changed that for my trees which was dark in my tree placement tool. But that tool load .gltf models.
When I create building in FME it gives a tileset.json file and then a folder “data” which contain .b3dm files. Maybe I’m ignorant but where is the .gltf? inside the .b3dm?

yeah it’s inside the b3dm, to unpack see b3dm2glb GitHub - CesiumGS/3d-tiles-tools

Any idea what caused this?

Update: I rewrote the file from FME and now it worked

Am I supposed to open the .glb with notepad?
I did this and changed the value to 0 from 1:

Then I converted it back to b3dm and but nothing happend. What am I missing :thinking:

yeah it’s just boxes in boxes in boxes… I always use glTF Tools - Visual Studio Marketplace to get from glb to gltf, there you can visualize the glTF as well

Okay thanks, this as a whole seems too time consuming for + 40 000 buildings in the end maybe.

have you tried GitHub - OpenDroneMap/Obj2Tiles: Converts OBJ files to OGC 3D tiles by performing splitting, decimation and conversion ?

I have not, would you recommend this tool? Your dataset here: White 3d tiles very dark was that one b3dm model for all buildings?

First a short note: In post #9, you posted a screenshot of a call to the 3D Tiles Tools b3dmToGlb command, saying

Error: Expected version “1” but got "538976257

I think that I know where this error is coming from, but not why it appears. Could it be that this input file (i.e. the myTiles/data/data1.b3dm) is broken or corrupted in some way?
(If you could pack that file into a ZIP and attach it here, I might be able to take a closer look…)

Regarding the actual issue here:

I think that it’s important to analyze the “root cause” and identify the actual issue that causes the problem. From the posts so far, it looks like bertt was right, and the problem is caused by a metallicFactor:1 at some place.

The question is how this can be avoided or fixed.

Unfortunately, I’m not familiar with FME or KMZ, and may not have fully understood the intended workflow. And importantly: I’m not even sure which of the workflows (mentioned in the first post) may have been attempted workarounds for problems (and where it might be possible to solve the original problem in another way).

When you say

OBJ–>3Dtiles (dark texture)
OBJ–>KMZ–>3Dtiles (dark texture)

Then I have to ask about some details:

For the first one:
Is the input here a single OBJ file, or is the input a list of OBJ (+MTL) files? When you load this (one file or many files) into FME, and and export it as 3DTiles, then what is the output? From the remaining conversation here, it sounds like the output is a tileset.json and a bunch of .b3dm files, is that correct?

For the second one:
Is it right that the intermediate step of using KMZ was an attempted workaround for the dark textures?

Just to confirm:
In the last screenshot, you showed a Test.glb file opened in a text editor. Is it correct that

  • This GLB was extracted from one of the B3DM files that was generated by FME?
  • The value for the metallicFactor there was originally a ‘1’, but you changed it to ‘0’


In any case:

Iff FME does not offer any functions for configuring the output in a way so that it writes the proper metallicFactor, then there certainly are solutions for this.

The 3D Tiles Tools may offer some options here. It would be relatively easy to create a small script that

  • Takes an input B3DM file
  • Extracts the GLB file from that
  • Changes the metallicFactor to 0 in all materials
  • Creates a new B3DM file from the modified GLB

This script could then be applied to the tileset, to fix the materials for all 40000 buildings in some sort of “batch run”.

(NOTE: The 3D Tiles Tools currently don’t have an official (public) API. This would only be a workaround to quickly solve the issue for a given data set. It would be strongly preferable to fix FME, so that it writes sensible metallicFactor values to begin with. If they don’t do that, and there is a noticable demand for this operation, then we could even consider options for offering this as a predefined functionality in the 3D Tiles tools)

Hi Marco13, thanks for taking the time!

  1. Yes, I have packed it and will message you. I tried again today with data0.b3dm and got the same error.

  2. Yes. The filestructure is:
    Main folder
    Next folder
    -same layout but different number

The obj reader reads each folder in the main folder and treats it as a separate object.
FMEs 3Dtiles writer let you choose from two geometry types: cesium_3d_object or cesium_point_cloud. I use cesium_3d_object. The output is one tileset.json and a folder called data contain an amount of b3dm. Each corresponding to one building.

Yes I figured, if KMZ keeps the texture look when uploaded to Ion, maybe I can go first via FME: obj → kmz, then again kmz → 3Dtiles. But that did not work.

  1. Yes that GLB was extracted from the B3DM files generated by FME.
    The metallicFactor was indeed at 1 without modification. I changed it to 0 in the GLB, Exported it back to b3dm and replaced the that specific b3dm. But nothing improved. I changed the value in notepad.

Yes Ideally would be for FME to fix this, but I find their forum not as active as this forum. So that probably some time away. Your suggestion about a script that could batch sound very sound.

I have now successfully changed the value using glTF Tools in VS code that @bertt suggested.
The problem to solve now is how can I batch this process, maybe a script as you said.
Because I cannot open every single building a change the value manually that’s not time efficient. I’m open to help from the community.


I changed the value in notepad.

Binary files (like B3DM) cannot be modified with a text editor. They can be modified with a hex editor, as long as the size of the file does not change (and in this particular example, the modification would only be changing a 1 to a 0, so that would be possible very easily).

But for the task of modifying this value in thousands of files, one will need some sort of batch processing.

Note: The following description is intended as a workaround. It should work in this particular case, but it might not work for other tilesets that show the same problem. It is not intended a long-term solution.

  • Clone the current state of the 3D Tiles Tools (i.e. the current main state, which is this state at the time of writing this)
  • run npm install
  • Add the code that is shown below as a file called ModifyMaterials.ts in the project root directory
  • Run npx ts-node ModifyMaterials.ts

The code that does the modification is shown here (adjust the tilesetSourceName and tilesetTargetName according to your input/output directories):

// NOTE: None of the functionality that is shown here is part of the
// public API of the 3D Tiles tools. The functions that are shown here
// use an INTERNAL API that may change at any point in time.
import { TileContentProcessing } from "./src/tilesetProcessing/TileContentProcessing";
import { GltfUtilities } from "./src/contentProcessing/GtlfUtilities";
import { GltfTransform } from "./src/contentProcessing/GltfTransform";
import { TileFormats } from "./src/tileFormats/TileFormats";
import { ContentDataTypes } from "./src/contentTypes/ContentDataTypes";

// Read a glTF-Transform document from the given input GLB buffer,
// set the 'metallicFactor' of all materials to 0, and return
// a new buffer that was created from the modified document
async function modifyMaterialsInGlb(inputGlb: Buffer) {
  const io = await GltfTransform.getIO();
  const document = await io.readBinary(inputGlb);
  const root = document.getRoot();
  const materials = root.listMaterials();
  for (const material of materials) {
  const outputGlb = await io.writeBinary(document);
  return outputGlb;

// Read the tile data from the given input B3DM buffer,
// extract the payload (GLB data), modify it by calling
// `modifyMaterialsInGlb`, and create a new B3DM buffer
// with the modified GLB data
async function modifyMaterialsInB3dm(inputB3dm: Buffer) {
  const inputTileData = TileFormats.readTileData(inputB3dm);
  const inputGlb = inputTileData.payload;
  const glb = await GltfUtilities.replaceCesiumRtcExtension(inputGlb);
  const outputGlb = await modifyMaterialsInGlb(glb);
  const outputTileData = TileFormats.createB3dmTileDataFromGlb(
  const outputB3dm = TileFormats.createTileDataBuffer(outputTileData);
  return outputB3dm;

async function runConversion() {
  const tilesetSourceName = "./input/tileset.json";
  const tilesetTargetName = "./output/tileset.json";
  const overwrite = true;

  // Create a `TileContentProcessor` that calls modifyMaterialsInB3dm
  // for all B3DM files
  const tileContentProcessor = async (
    content: Buffer,
    type: string | undefined
  ) => {
    if (type !== ContentDataTypes.CONTENT_TYPE_B3DM) {
      return content;
    // A pragmatic try-catch block for the actual modification.
    // In a real application, different error handling could
    // be used.
    console.log("Modifying materials...");
    try {
      const modifiedB3dm = await modifyMaterialsInB3dm(content);
      console.log("Modifying materials... DONE");
      return modifiedB3dm;
    } catch (e) {
      console.log(`ERROR: ${e}`);
      return content;
  // Process the tileset source, and write it to the tileset target,
  // applying the `TileContentProcessor` to all tile contents
  await TileContentProcessing.process(


This should fix the wrong metallicFactor that seems to be written by FME in this case. If similar problems with tilesets come up more frequently in the future, then this might become part of an official functionality, but for now, it is only a temporary workaround.