godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.58k stars 20.44k forks source link

Incorrect tangents (Normal Map Shading) on mesh at import glTF #71221

Open dr3muchiy opened 1 year ago

dr3muchiy commented 1 year ago

Godot version

4.0 beta10, beta11, beta12

System information

Windows 10, Nvidia GeForce GTX 1060 (528.02) / Vulkan linux mint Nvidia GeForce GTX 1060 / Vulkan

Issue description

remark: the material used on the models has seamless textures, u,v coordinates of individual vertices can be <0 or >1.

export method 1 (blender to glTF - tangents checkbox off)

When exporting models from a blender through GLTF with default settings(tangents checkbox off), with some(not all) 3d model, such a problem is observed: NormalFlip

The bug flip Normal Shading Line not lie on the edge, and located inside the triangle. NormalFlipInsideTriangle

models on which this situation occurs look equally wrong as in Godot 4.0 beta10, beta11, so in the Godot 3.4

export method 2 (blender to glTF - tangents checkbox on)

a: for Godot 3.4 - completely treated the situation and gave a model with the right Normal Shading

b: for Godot 4.0 beta10-12 flip NormalShading lines inside the triangles disappear, but now Normal Shading is buggy in a different way. On one side of the wall, the Normal Shading is correct, on the other side wrong. The surface of the bricks that should be illuminated turn out to be darkened, and vice versa. The picture demonstrates the side with wrong Shading:

wrongNormShad2

export method 3 (blender to obj):

Godot 4.0 beta10-12 When import models through OBJ format, we get a mesh with the right Normal Shading. NormShad2

Steps to reproduce

Open the project / testScene and see three mesh obtained by three different import methods.

First mesh from global world origin, has a glitch at the top, where the red arrow points. Second mesh has sides with incorrect normal shading - bricks are darkened from above and lightened from below. Third mesh (from global world origin) has no glitches - example of correct normal shading.

Minimal reproduction project

glTF_BugNormalShading.zip

model blender file ruinD300.zip

Eisendroid commented 1 year ago

This bug appeared for me since Alpha 15. Every version before that worked fine. Maybe someone can confirm if Alpha 14 works for them.

clayjohn commented 1 year ago

The three meshes all look correct to me when running the MRP.

Screenshot from 2023-01-11 11-44-19

This bug appeared for me since Alpha 15. Every version before that worked fine. Maybe someone can confirm if Alpha 14 works for them.

@Eisendroid Your issue is likely unrelated. Between Alpha 14 and Alpha 15 we merged a PR switching meshes to using Octahedral normals. Accordingly, you need to re-import the meshes that you initially imported in Alpha 14 and earlier.

dr3muchiy commented 1 year ago

@clayjohn

The three meshes all look correct to me when running the MRP

First mesh from global world origin, has a glitch where the red arrow points. Take a closer look at the red arrow zone, you can see it on your screen. reAr

Second mesh (from global world origin) - bricks are darkened from above and lightened from below - this is not right. You can see it on your screen.

Third mesh (from global world origin) not included in your screen, has no glitches. Third mesh shown as an example of correct shading, obtained by importing from OBJ format

dr3muchiy commented 1 year ago

@Eisendroid

This bug appeared for me since Alpha 15.

In my case it looks like there are 2 bugs.

blender to gltf (tangents off) - catch one bug - flip Normal Shading not lie on the edges, and located inside the triangles.

blender to gltf (tangents on) - catch another - flip Normal Shading lie on edges

Eisendroid commented 1 year ago

@clayjohn

Between Alpha 14 and Alpha 15 we merged a PR switching meshes to using Octahedral normals. Accordingly, you need to re-import the meshes that you initially imported in Alpha 14 and earlier.

Re-import doesn't change anything. I created a new project in different Godot versions, imported the model the first time and still had this bug. FBX works fine (beside some other issues but these are unrelated to this topic).

@dr3muchiy Yes, you are right, there are two bugs. I also have them. It seems like the color channels of the normalmap are randomly "switched".

dr3muchiy commented 1 year ago

@Eisendroid

It seems like the color channels of the normalmap are randomly "switched".

In my case it's not a texture import problem.
I import blender model into the godot without textures and materials, in three different ways.

Normal map texture import into the godot separately Create one material, using this texture - for all three variants.

And as a result, we get two variants with incorrect shading. And one variant(through obj format) is correct.

For case (blender to glTF - tangents off) vertex A tangent directed downward, vertex B tangent directed upward. As a result, in the middle have flip Normal Shading tang_2

dr3muchiy commented 1 year ago

Checked the situation for godot 4.0 beta12. After re-import gltf files, described glitches are still present.

stoofin commented 1 year ago

I ran into what I think is the same issue. Alpha15 introduced octahedral compression for normals and tangents, and I think that octahedron_tangent_encode/decode combined with being stored as 2xu16 is not robustly roundtripping the previously independent tangent_sign used for the binormal.

In my case, checking the tangents with MeshDataTool and comparing across versions (alpha13 vs rc1), I see it happening only when the tangent is very close to (0, 0, -1). e.g. (-1e-6, -1e-6, -1, 1) becomes (-0.000031, 0, -1, -1)

My crappy model: godot-4 0alpha15-cropped Showing binormal: godot-4 0rc1 binormal

I tried ticking all the flags, triangulating before export, but only using a version before alpha15 or overwriting the binormal in the vertex shader with something like BINORMAL = cross(NORMAL, TANGENT); has it look right.

Here's a quick script showing the 4th component flipping:

extends Node3D

# Called when the node enters the scene tree for the first time.
func _ready():
    var vertices = PackedVector3Array()
    vertices.push_back(Vector3(0, 0, 1))
    vertices.push_back(Vector3(1, 0, 0))
    vertices.push_back(Vector3(1, 0, 1))

    var uvs = PackedVector2Array()
    uvs.push_back(Vector2(0, 1))
    uvs.push_back(Vector2(1, 0))
    uvs.push_back(Vector2(1, 1))

    var normals = PackedVector3Array()
    normals.push_back(Vector3(0, 1, 0))
    normals.push_back(Vector3(0, 1, 0))
    normals.push_back(Vector3(0, 1, 0))

    var tangents = PackedFloat32Array()
    # (0, 0, 1, 1)
    tangents.append(0)
    tangents.append(0)
    tangents.append(1)
    tangents.append(1)

    # (0, 0, -1, 1)
    tangents.append(0)
    tangents.append(0)
    tangents.append(-1)
    tangents.append(1)

    # ~(-1e-6, -1e-6, -1, 1)
    var evil = Vector3(-1e-6, -1e-6, -1).normalized()
    tangents.append(evil.x)
    tangents.append(evil.y)
    tangents.append(evil.z)
    tangents.append(1)

    # Initialize the ArrayMesh.
    var arr_mesh = ArrayMesh.new()
    var arrays = []
    arrays.resize(Mesh.ARRAY_MAX)
    arrays[Mesh.ARRAY_VERTEX] = vertices
    arrays[Mesh.ARRAY_TEX_UV] = uvs
    arrays[Mesh.ARRAY_NORMAL] = normals
    arrays[Mesh.ARRAY_TANGENT] = tangents

    # Create the Mesh.
    arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
    var m = MeshInstance3D.new()
    m.mesh = arr_mesh

    var mdt = MeshDataTool.new()
    mdt.create_from_surface(arr_mesh, 0)
    for i in range(3):
        print(mdt.get_vertex_tangent(i))

in rc1 prints

[N: (-0.000015, -0.000015, 1), D: 1]
[N: (0, 0, -1), D: 1]
[N: (-0.000031, 0, -1), D: -1]

but in alpha13 printed

[N: (-0.000978, -0.000978, 1), D: 1]
[N: (-0.000978, -0.000978, -1), D: 1]
[N: (-0.000978, -0.000978, -1), D: 1]
clayjohn commented 1 year ago

I took another look at this today. I ended up loading the glb (default, no tangents) into blender and re-exporting and the new mesh looks fine. I used Blender 3.4.1. I have a feeling that this was an issue with the blender GLTF2 exporter that has since been fixed.

@dr3muchiy Can you confirm the same on your end?

dr3muchiy commented 1 year ago

@clayjohn Sorry I haven't looked at git for a long time

export from Blender 3.6.4 to gltf2 godot 4.1.1

bug flip Normal Shading Line still here image

WickedInsignia commented 10 months ago

I'm experiencing similar issues. Tangents generated on export from Blender don't seem to come through properly. In the following examples I'm using a trimsheet with bevels and details to create the smooth edges. This means the UV map faces are usually rotated around many different ways.

Blender: TangentIssues01

Godot, Ensure Tangents Off: TangentIssues02

Godot, Ensure Tangents On: TangentIssues03

Unfortunately Ensure Tangents has caused some issues in mesh shading in other projects of mine, so it can't always be used to resolve this problem.

Blender file and Godot project: Godot Project Blender File Trim Normals

clayjohn commented 10 months ago

@WickedInsignia What version of Blender are you using? When I attempt to open your blend file in Blender 3.4.1 it crashes Blender. Maybe I need to upgrade to the newest version?

WickedInsignia commented 10 months ago

@clayjohn It was built in Blender 4.0.0 so an upgrade may be needed.

clayjohn commented 10 months ago

@WickedInsignia I converted to the .GLB into a .GLTF and inspected it and the file doesn't contain tangents at all.

Doing some internet searching I stumbled on this https://projects.blender.org/blender/blender-addons/issues/81746

So I tried adding the "triangulate" modifier to your mesh in the .blend file. And then I ensured that "apply modifiers" and "export tangents" were both checked off at export time. After doing so, the mesh imports into Godot correctly, even with "ensure tangents" set to off.

WickedInsignia commented 10 months ago

@clayjohn The issue disappeared for me recently for this particular file and I was trying to work out why, I think you've tracked it down here. I have validated this works on my side. I was having some issues related to UV2 tangents as well but couldn't track them down. This will help there, I'll fiddle around a bit and see if I can't get a report going for that if it still exists.

jitspoe commented 8 months ago

So I seem to have a stupidly simple repro case for this. I have a door that's literally just a box, and on one side, the tangents/bitangents are correct. On the other they are not:

image image

test_bad_tangent.zip

Usually, when run into this issue, I can disable tangents on the export (since it seems there's a bug with the Blender exporter) and enable ensure tangents on the import, but this particular mesh seems to be broken with both. I even tried the triangulate modifier. Just tried with blender 4 and it still seems broken.

Not sure if this is at all related, but it seems there might be some issues with calculations on very specific angles (like perfect 90 degrees) that causes this? https://github.com/godotengine/godot/issues/81446

jitspoe commented 8 months ago

Seems it has something to do with compression. If I check "Force Disable Compression", the issue goes away (in my case, anyway).

Calinou commented 8 months ago

Does your mesh use mirrorred UVs anywhere?

jitspoe commented 8 months ago

Indeed I am. Looks like it might be the issue you linked not the one shown here (or maybe they're both connected?)

jitspoe commented 8 months ago

Ok, I think there's an issue beyond the gltf importer. I just ran into this on my BSP importer as well. One specific area had bad tangents/inverted normal maps. I'm using surf_tool.generate_tangents(). Full source here: https://github.com/jitspoe/godot_bsp_importer

The map I'm importing is kind of massive and complex, but I might see if I can isolate just the problem area later.

jitspoe commented 6 months ago

Is this fixed with https://github.com/godotengine/godot/pull/88725?

clayjohn commented 6 months ago

Is this fixed with #88725?

I don't think so. I believe I tested the OP and it wasn't fixed. It would be worth testing with the lastest dev release though.

WickedInsignia commented 6 months ago

It'd be worth mentioning that meshes utilizing 2 UV maps don't have their tangents calculated properly either. Could be related to this issue or tangent basis for UV1 simply isn't calculated properly if face orientations are different to UV0. Unreal has a material function called "Derive Tangent Basis" that can derive new tangent vectors using the specified UVs so that you can use a normal map with any UV channel. A similar functionality in Godot's Visual Shader nodes would be great if possible.

The tangent issues I've had with Godot are so frequent and unpredictable I haven't yet managed to understand them well enough to make a comprehensive bug report. It would probably require cranking open Godot in a manner I'm not equipped for.

clayjohn commented 6 months ago

It'd be worth mentioning that meshes utilizing 2 UV maps don't have their tangents calculated properly either. Could be related to this issue or tangent basis for UV1 simply isn't calculated properly if face orientations are different to UV0. Unreal has a material function called "Derive Tangent Basis" that can derive new tangent vectors using the specified UVs so that you can use a normal map with any UV channel. A similar functionality in Godot's Visual Shader nodes would be great if possible.

The tangent issues I've had with Godot are so frequent and unpredictable I haven't yet managed to understand them well enough to make a comprehensive bug report. It would probably require cranking open Godot in a manner I'm not equipped for.

I added an import step that checks to see if generated tangents are acceptable. Right now it is only used to disable mesh compression, but it could be used to warn users about improper tangents as well.

An option to generate tangents from other UV channels could be really interesting if it is common for users to have materials where the normal_map uses alternative UV channels

WickedInsignia commented 6 months ago

An option to generate tangents from other UV channels could be really interesting if it is common for users to have materials where the normal_map uses alternative UV channels

It's becoming more common since it allows for a great deal of artistic authoring with less texture usage than individually baked assets. For example it's used in material workflows that use texture-driven blending to achieve grime/scratches/etc. Unreal added the aforementioned material function in 2016. Godot presents some issues to using multiple UVs beyond the tangent problem: it reserves UV1 for lightmapping and doesn't easily allow the use of UV2 (there's no node for this in Visual Shader).

One efficient method for hard-surface assets is to have trimsheet normal textures on one UV set, and tiling A/R/M/N maps on another UV set. For example in Halo Infinite, the panel lines are driven by a normal map that is frequently stretched and rotated on one UV while the tiling metal materials are on a separate one (judging by my investigations). Opening up the opportunity to use/blend normal maps across both UVs also allows for deferred-decal style techniques that are modeled straight into the surface rather than as floating geo.

neil-mcknight-pod-06 neil-mcknight-pod-07

pineapplemachine commented 2 months ago

I've just run into this myself, with v4.2.1.stable.mono.official [b09f793f5]

Here's the mesh that would not import correctly until I checked "Force Disable Compression" in the blend file import options:

repro-godot-issue-71221.zip

image

Broken, with compression (first image) vs. fixed, without compression (second image):

image
image

clayjohn commented 2 months ago

@pineapplemachine I just tested your mesh locally. The issue you are running into has already been fixed in 4.2.2. Just reimport the mesh using 4.2.2 and the issue will go away

rafrafek commented 1 month ago

I accidentally commented on a closed issue, I think my comment should be here instead:

This model: https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/Fox/glTF-Binary In the screenshot: on the left the model imported to Blender and then .blend to Godot, on the right directly to Godot. image

I'm on MacOS (M1) and on the Mobile renderer.

Moved from: https://github.com/godotengine/godot/issues/85406#issuecomment-2308549894

Edit: Exporting to Android was broken for me with this model. Even after re-exporting from Blender, the shading on Android was still broken. I managed to fix the Android build somehow, and although I thought I had identified the solution, I'm no longer able to reproduce the buggy behavior on Android. However, the issue with importing the Fox.glb file directly into Godot persists.

Edit: Moved to https://github.com/godotengine/godot/issues/96484