godotengine / godot

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

Blender/GLTF material "specular" attribute is ignored #83320

Open lorddevereux opened 1 year ago

lorddevereux commented 1 year ago

Godot version

4.1.1 stable

System information

Linux Mint 21, AMDGPU, Forward+

Issue description

When using automatic Blender import (or GLB/GLTF export from Blender and import into Godot), material specular values are ignored and set to a default of 0.5.

This issue is also referenced in https://github.com/godotengine/collada-exporter/issues/57 and I can confirm that this issue still exists in Godot 4.1.1.

When using a large set of models imported from Blender, the only sensible way around this issue is to script every object load to correct the materials manually, which is quite annoying, or be stuck with the sheen on every object.

Steps to reproduce

  1. Create any model in Blender which has a material "Specular" set to any value other than 0.5
  2. Add the Blender file to a Godot project and open it for viewing
  3. The object will have its material Metallic > Specular set to 0.5

Minimal reproduction project

N/A

4CoDev commented 8 months ago

Is there any progress on fixing this problem?

clayjohn commented 8 months ago

This should be an easy first issue to tackle, here is where we set metallic in the GLTF importer:

https://github.com/godotengine/godot/blob/23191b834e4609baacf19855c3acb6a9f607b30f/modules/gltf/gltf_document.cpp#L4007-L4011

As you can see, we never touch specular. So an interested contributor just needs to check what the name of the specular component and then parse it the same as we do metallic.

cosparks commented 8 months ago

@lorddevereux @4CoDev which blender material properties do you want to be interpreted as metallic specular values in godot's material settings? Blender's principled BSDF material has a few different params for specular settings (IOR, tint, etc.) but I haven't seen just a single specular or metallic/specular value.

It would be useful to you provided an example material or gltf file and its expected specular value in godot.

According to this documentation, gltf format doesn't have specular values under pbrMetallicRoughness, but a specular value could be computed from KHR_material_specular and KHR_material_ior extensions.

lorddevereux commented 7 months ago

Thanks for the pointers folks, I'll have a look and see what might work best. I was looking at using this Specular value, as it seems to best represent the effect that would be caused by Specular in Godot

image

Scarface1809 commented 7 months ago

I managed to create a simple 3D scene to try and recreate the issue, and I can confirm that when you change the specular value in blender, the value does not change in godot.

lorddevereux commented 7 months ago

I think I can see why this wasn't implemented originally, it's not a straightforward read of a property.

The Blender GLTF exporter doesn't export that Specular value. Instead, it converts the Blender Specular properties (IOR/Specular and specular tint. See this (referred to in the gltf exporter code) https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33

I think the best way to implement this is to mirror the logic that is used in the Blender gltf IMPORTER to convert gltf material properties back to Blender Specular properties, but implemented in the Godot gltf importer.

I'll admit to being far from an expert in graphics terms but I could implement it so that Blender specular value became the Godot specular value. That would be a start

cosparks commented 7 months ago

I was looking at using this Specular value, as it seems to best represent the effect that would be caused by Specular in Godot

It looks like you're using blender 3.0. In blender 4.0, they've removed that specular property and added a number of fields for specularity which can be used to augment the base IOR. Below is a screenshot of some of the properties of the new principled BSDF material in blender 4.0

I don't have blender 3.0 installed. Could you have a look at the GLTF export to see what that specular value maps to? I'm assuming godot should still properly import blender 3 materials, and for blender 4 materials it might be worth looking into using the ior to compute an appropriate specular value.

edit: good to know that that specular value isn't being exported. I think the ior/specular and specular tint will show up in the gltf file as the KHR material extensions that I mentioned above.

lorddevereux commented 7 months ago

I've done some experimentation and it seems that the KHR material extensions section only gets generated if there is only 1 material present in the model. As soon as you add a second material, it is no longer generated and changes to the specular value in Blender do not result in any changes to the GLTF. Therefore, it would be impossible to support Specular in Blender 3.6 for a model with more than one material without modifying the gltf exporter.

edit 1: but you are right, the when there is only one material then the specular value does get mapped indirectly into values in KHR material extensions

I am downloading 4.0 and will look into what gets generated there.

edit 2: in blender 4.0 the exporter generates a KHR section per material, so I can work with that. It seems the "IOR Level" field under Specular maps to what older versions of blender just had labelled as "Specular".

edit 3: it seems, according to the GLTF spec, that "KHR_materials_specular" replaces the archived "KHR_materials_pbrSpecularGlossiness" item (which Godot does have support for). See the KHR_materials_specular documentation.

edit 4: this works. If the specular value is set higher than 0.5 it will be stored as specularColorFactor, less than 0.5 will be stored as specularFactor as long as the tint is RGB (1,1,1). As the comment notes - if the specular Tint (in Blender) is set to something other than 1,1,1 (white) then it will import wrong. The GLTF format stores colorFactor as an RGB value for which each channel is effectively: specular_tint.r * 2.0 * specular_factor. With 2 unknowns it's impossible to get that back to a factor and a tint image

In an ideal world, I think the specular tint would have an effect on the albedo colour of the material in Godot (which is loaded always as RGB 1,1,1 if a texture is applied to the material). As I say, I'm far from a CG expert, but to me Specular Tint in Blender seems to have a similar effect to Albedo colour in Godot.

If no-one has any strong objections I can PR this patch.

clayjohn commented 7 months ago

@lorddevereux I took a quick look at the Blender Principled BRDF docs https://docs.blender.org/manual/en/latest/render/shader_nodes/shader/principled.html

I don't think Godot supports anything close to "specular tint". Specular tint is meant to only modify the specular reflections and not diffuse. Albedo is always used for both. So tinting albedo with "specular tint" will result in wrong results.

That being said, it seems like what blender is calling "IOR level" is exactly what we call "specular".

What we call "specular" is unrelated to the old "specular/glossiness" workflow, its just a coincidence in naming.

Since we don't support specular tint, we should just ignore the specularColorFactor and specularColorTexture for now

Accordingly, I think you can probably just simplify the code you have in your last message to only contain the specularFactor part. Unless I am misunderstanding something about what Blender does?

lorddevereux commented 7 months ago

Makes sense on the colour tint, I will leave that alone.

The GLTF exporter is a bit weird (on the face of it, I'm sure there's a reason):

This behaviour is why I added the comment in about the three components of specularColorFactor being the same, and why the Godot importer needs to handle specularColorFactor (otherwise IOR level > 0.5 would not be imported).

It's not ideal, but I think hands are tied here by the gltf spec.

clayjohn commented 7 months ago

@lorddevereux When the result is more than 1.0, does it store anything in specularFactor?

lorddevereux commented 7 months ago

No, it does not create the specularFactor field at all in that case.