godotengine / godot

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

Importing .blend file results in Roughness texture being used as Metallic texture #82455

Open tobloef opened 1 year ago

tobloef commented 1 year ago

Godot version

v4.1.1.stable.official [bd6af8e0e]

System information

Godot v4.1.1.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3070 (NVIDIA; 31.0.15.3713) - AMD Ryzen 7 5800X 8-Core Processor (16 Threads)

Issue description

When importing .blend files, the Roughness texture is used for both the Roughness and the Metallic properties of the imported material. I would expect the imported model to use the Metallic texture that was set up in Blender.

image

image

image

Steps to reproduce

In Blender (3.6.3):

In Godot (4.1.1):

Minimal reproduction project

Godot project: MetallicImportBugRepro_Godot.zip

Blender project: MetallicImportBugRepro_Blender.zip

Calinou commented 1 year ago

Can you reproduce this when exporting a .gltf file and importing it in Godot? (Godot internally tells Blender to export a .gltf file.)

This may be a bug in Blender's glTF exporter, especially if you can't reproduce this with another tool that exports glTF files.

tobloef commented 1 year ago

When exporting a .gltf file from Blender and importing it in Godot, I get a combined Metallic/Roughness texture file. This is loaded into the material correctly as the two textures are indeed the same file, just different channels.

image

I assume this "combine into one texture" behavior is the intention for the .blend import as well?

As an aside, I personally find it somewhat unintuitive that the textures are automatically combined instead of just using the two image textures I set up in Blender. I wonder, what's the reason for this being the default?

Calinou commented 1 year ago

I assume this "combine into one texture" behavior is the intention for the .blend import as well?

Yes; I don't know why it's not happening when Godot imports a .blend file directly.

As an aside, I personally find it somewhat unintuitive that the textures are automatically combined instead of just using the two image textures I set up in Blender. I wonder, what's the reason for this being the default?

Using this approach results in better performance (fewer textures need to be used for the material), as you can use ORMMaterial3D instead of StandardMaterial3D. See also https://github.com/godotengine/godot-proposals/issues/2955.

tobloef commented 1 year ago

Using this approach results in better performance (fewer textures need to be used for the material), as you can use ORMMaterial3D instead of StandardMaterial3D. See also godotengine/godot-proposals#2955.

Makes sense. Do you know if it's possible to disable this behavior? I can't seem to find something like that in the import settings. If not, that's totally fine. It's not what this issue is about anyway πŸ˜…

Regarding the actual issue, if someone could point me to the right corner of the codebase I would also be happy to look into the issue myself.

Calinou commented 1 year ago

Makes sense. Do you know if it's possible to disable this behavior?

Not that I know of.

Regarding the actual issue, if someone could point me to the right corner of the codebase I would also be happy to look into the issue myself.

The Blender importer code is here: https://github.com/godotengine/godot/blob/ec62b8a3ee1d731387a440b4d2abb7961aa28322/modules/gltf/editor/editor_import_blend_runner.cpp

As you can see, Godot executes Blender with a specific Python payload designed to make it export a glTF file.

tobloef commented 1 year ago

I have been looking into this more over the weekend. This will be a somewhat long post with the details of my findings. I hope it can serve as a jumping-off point for any potential fixes.

Forgive me if some of this is obvious to veterans of the engine, I'm a very recent immigrant from the Unity fiasco. If you have corrections, please let me know!

Blender glTF export

Basic theory

Blender has 3 main modes of exporting gLTF files: Binary (.glb), Embedded (.gltf), and Separate (.gltf + .bin + textures).

Note that by default, whether using embedded textures or not, Blender will combine the Metallic and Roughness into the texture we previously talked about in this thread. It is possible to disable this behavior by toggling the Keep original option when using the Separate mode, but this does come with the following very relevant warning:

⚠️ If you use more than one texture, where PBR requires only one, only one texture will be used. This can lead to unexpected results.

Godot's settings

When importing a .blend file, Godot will behind the scenes call Blender and perform a gLTF export, just like Calinou said. This export uses the Separate mode, along with Keep original set to True.

To me, this explains the faulty behavior originally described in this issue. Godot seemingly assumes that the Metallic and Roughness textures will be combined, while explicitly telling Blender not to do that.

Testing different formats

So perhaps we can solve the issue by changing export settings. The table below shows the results of importing gLTF files manually exported from Blender, both for projects where the texture files have and have not been packed into the .blend file.

Packed Unpacked
Binary βœ… † βœ…
Embedded βœ… † βœ…
Separate βœ… ‑ βœ… ‑
Separate + Keep original ❌ ❌

Legend

βœ… Imported with Metallic and Roughness correctly assigned

❌ Imported with Metallic incorrectly assigned to the same texture file as Roughness

† If Godot's Embedded Image Handling is set to Extract Textures in the scene import settings, you will have to trigger a second import. Otherwise, the imported scene will error due to missing images. I believe this to be a bug.

‑ You of course have to import the exported textures and the .bin file alongside the .gltf file, as they are not embedded inside the project with this export setting.

Attempted fix by changing Godot's export settings

At this point, I thought the fix would be to simply change Godot's Blender export settings to one of the working formats from the table above. This can be easily done here: https://github.com/godotengine/godot/blob/6de34fde27f650f738a1e46992f1b783f2cf9e76/modules/gltf/editor/editor_scene_importer_blend.cpp#L77-L78

However, all approaches I tried had issues.

Separate and Embedded

For Separate and Embedded exports, the scene has no Metallic/Roughness textures assigned at all and Godot produces the following error:

πŸ”΄ Can't find file 'res://.godot/imported/Unpacked-ef043bcaf77b3602acfd1d86d08410e3_Metallic-Roughness.png'.
πŸ”΄ No loader found for resource: res://.godot/imported/Unpacked-ef043bcaf77b3602acfd1d86d08410e3_Metallic-Roughness.png (expected type: Texture2D)
🟑 modules/gltf/gltf_document.cpp:3233 - glTF: Image index '0' couldn't be loaded with the name: Metallic-Roughness. Skipping it.

After the import has run, .godot/imported will actually contain the combined Metallic/Roughness texture in question, but Godot will not import it due to it being in the .godot folder. I assume this is on purpose to prevent an infinite loop:https://github.com/godotengine/godot/blob/7aff59f0b6920b970454c7608148845619d6cd60/editor/editor_file_system.cpp#L1369-L1371

Additionally, if the Blender project is not packed and you import the textures as well, .godot/imported will also contain a combined Metallic-Roughness.png texture. Note that this will happen despite the export mode being Embedded. There will also be the following error in the log:

πŸ”΄ No loader found for resource: res://.godot/imported/Metallic-Roughness.png (expected type: )

Binary

For both packed and unpacked Blender files, the imported scene will completely fail and get a little ❌ icon. The following will be printed in the log:

πŸ”΄ modules/gltf/gltf_document.cpp:7580 - Condition "err != OK" is true. Returning: ERR_FILE_CANT_OPEN
πŸ”΄ Error importing 'res://Unpacked.blend'.

What now?

Barring abandoning the issue, I see two potential actions to take from here:

A) Make Godot support importing gLTF files with separate textures for Metallic and Roughness. This would help not only with gLTF support in general but also allow the existing Blender file importing to Just Workβ„’.

B) Figure out what goes wrong when Godot uses other export settings in the Blender import process. With this, Godot would not have to concern itself with separate Metallic/Roughness textures, as they would be combined during the Blender export.

I'm not sure either of these fixes would be easy for me to execute, but I would be willing to take a look if we knew what approach we wanted to go with. I'm also very open to someone more experienced with the engine taking it from here.

If you've read all this, I thank you. I would love to hear what you think. Perhaps someone would be willing to see if they get similar results and that it's not just me messing something up.

Cheers ✌️

scurest commented 1 year ago

glTF does not support separate textures for metal and roughness, they have to be combined into one texture. (Same for base color and alpha.)

tobloef commented 1 year ago

glTF does not support separate textures for metal and roughness, they have to be combined into one texture. (Same for base color and alpha.)

Makes sense. So something like approach B from my previous post (quoted below) would be the way to go?

B) Figure out what goes wrong when Godot uses other export settings in the Blender import process. With this, Godot would not have to concern itself with separate Metallic/Roughness textures, as they would be combined during the Blender export.

(I've updated the wording in the wording slightly, to not emphasize using Binary mode for the export)

aaronfranke commented 2 months ago

@tobloef As an aside, I personally find it somewhat unintuitive that the textures are automatically combined instead of just using the two image textures I set up in Blender. I wonder, what's the reason for this being the default?

This happens because glTF does not support specifying different textures for metallic as roughness, it specifies only a metallicRoughnessTexture for both: https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/material.pbrMetallicRoughness.schema.json#L43 Therefore, "A) Make Godot support importing gLTF files with separate textures for Metallic and Roughness" is not possible. ...uh, sorry, I missed the last part of the discussion where this was mentioned already, but I'll keep this text around instead of deleting it in case the details and link helps.

Anyway, it seems that Blender does not have a way to "unpack" the converted roughness+metallic texture. I've made two PRs to fix this problem: #96778 and #96782. However, note that you will need to uncheck this box:

Screenshot 2024-09-10 at 2 32 47β€―AM
etherealxx commented 1 month ago

Reproducible in Godot v4.3.stable and Blender 4.1.1/4.2.2 Here's the model i use to test: The Cartoon Knight_anim.zip