filament: SurfaceOrientation doesn't duplicate vertices when generating flat normals

Describe the bug I am having two (separate I believe) issues rendering some meshes in Filament. I’m only opening one issue because they are both related to the same mesh type. We generate many meshes of this type, and the issue is not confined to this specific mesh, but all meshes of this type. First, this is what the mesh looks like in scenekit:

scenekit

Issue 1

The Filament directional lights only illuminate parts of the scene. This is clear if I light the scene with only a directional light. This issue appears in our viewer as well as the sample-gltf iOS viewer. As you can see a large chunk of the ground plane is not illuminated by the light (maybe there is a setting to fix this, but I played with all that seemed relevant)

filament_directional_only

At first I thought it was maybe an issue on our side with the triangulation, but as you can see in meshlab the ground plane only has two triangles and appear to be unrelated to the illumination boundaries

meshlab

*** Issue 2 ***

The colors for the mesh are pretty far off from what they are in all other viewers (SceneKit, MeshLab, ThreeJS). In particular they appear to be much less saturated. I have tried adjusting the colors and intensity of the lights but this did not fix it. Here is what it looks like in Filament when I adjusted the directional and indirect lights to be as good as I could make them

filament_directional_plus_indirect

To Reproduce

Load this .glb in filament and observe the issues:

house.glb.zip

These were my exact lighting settings:

    math::float3 harmonics[1];
    harmonics[0] = math::float3(1.0f, 1.0f, 1.0f);
    indirect_light_ = IndirectLight::Builder().irradiance(1, harmonics).intensity(24000).build(*engine_);
    if (indirect_light_ == nullptr) {
      throw std::runtime_error("Creating a filament indirect light failed!");
    }
    scene_->setIndirectLight(indirect_light_);

    // Lights and shadows for room mode captures
    utils::Entity light = utils::EntityManager::get().create();
    LightManager::ShadowOptions shadowOptions;
    shadowOptions.shadowCascades = 3;
    shadowOptions.mapSize = 4096;
    shadowOptions.vsm.blurWidth = 120;
    shadowOptions.vsm.msaaSamples = 16;

    LightManager::Builder(LightManager::Type::DIRECTIONAL)
        .color(Color::toLinear<ACCURATE>({0.98f, 0.9f, 0.86f}))
        .intensity(38000)
        .direction({0.2, -1, -0.3})
        .castShadows(true)
        .shadowOptions(shadowOptions)
        .build(*engine_, light);
    scene_->addEntity(light);

Expected behavior A clear and concise description of what you expected to happen.

Screenshots If applicable, add screenshots to help explain your problem.

Logs If applicable, copy logs from your console here. Please do not use screenshots of logs, copy them as text.

Desktop (please complete the following information):

  • OS: tested on iOS, Mac, Android and WebGL
  • GPU: All tested
  • Backend: Tested on OpenGL, Metal and WebGL

Smartphone (please complete the following information):

  • Device: [e.g. Pixel 2]
  • OS: [e.g. Android Pie 9.0]

Additional context Add any other context about the problem here.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (1 by maintainers)

Commits related to this issue

Most upvoted comments

I’ll reopen it so we can fix this issue on our end

This file contains a glb that isolates the issue: flat_normals_bug.zip

Flat normals are computed per triangle but we don’t duplicate vertices. So adjacent triangles erase each other’s normals in SurfaceOrientation.cpp/OrientationBuilderImpl::buildWithFlatNormals(), which in turns creates degenerate tangent frames in buildWithNormalsOnly() in the same file. The fix is to make buildWithFlatNormals() properly duplicate shared vertices when generating the flat normals.

The current workaround is to generate glTF files with normals in them.

The problem is that per the glTF spec we apply flat shading when no normals are provided. Unfortunately the code that does that does not duplicate shared vertices, which means we share normals and tangents across faces that are not coplanar.

If you are using the same tone mapper as in the other renderers, it should look very similar. The only other thing I can think about is that Filament also outputs in properly encoded sRGB (but it should be the case of those other renderers as well). Of course also making sure you use the same light and exposure setup (you can make FIlament behave like non-photometric based renderers by calling Camera.setExposure(1f) and then the light intensities are relative, so you’d set the directional light’s intensity to 1 for instance instead of 38,000. Also make sure your light colors are correct: all Filament APIs expect to be fed colors in “linear sRGB” but we provide conversion APIs (like you did in your sample).