Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add specular to MeshPhysicalMaterial and GLTFLoader KHR_materials_specular support #22156

Merged
merged 8 commits into from
Jul 28, 2021

Conversation

takahirox
Copy link
Collaborator

This PR adds shininess and specular to MeshPhysicalMaterial and KHR_materials_specular extension support to GLTFLoader.

Description

KHR_materials_specular is a glTF extension which enables to control specular of PBR material.

Link: KHR_materials_specular extension specification

The extension adds four specular related properties to PBR material

  • specularFactor: The strength of the specular reflection.
  • specularTexture: The Strength of the specular reflection, stored in the A channel.
  • specularColorFactor: The F0 color of the specular reflection (linear RGB).
  • specularColorTexture: The F0 color of the specular reflection, stored in the RGB channels and encoded in sRGB.

The Fresnel term F will be calculated as the following formulas.

dielectricSpecularF0  = min(0.04 * specularColorFactor * specularColorTexture.rgb, float3(1.0)) *
                        specularFactor * specularTexture.a
dielectricSpecularF90 = specularFactor * specularTexture.a

F0  = lerp(dielectricSpecularF0, baseColor.rgb, metallic)
F90 = lerp(dielectricSpecularF90, 1, metallic)

F = F0 + (F90 - F0) * (1 - VdotH)^5

This PR adds shininess, shininessMap, specular, and specularMap properties corresponding to the four KHR_materials_specular extension properties to MeshPhysicalMaterial and updates the shader to follow the above formulas.

Screenshot

Left: With this PR, Right without this PR. The left specular is a bit brighter because the model has specularColor [2,2,2] or [3,3,3] (default is [1,1,1]).

image

Model: glTF IridescentDishWithOlives KhronosGroup/glTF-Sample-Models#319

Changes

  • Add shininess, shininessMap, specular, and specularMap properties corresponding to the four KHR_materials_specular extension properties to MeshPhysicalMaterial
  • Add KHR_materials_specular extension support to GLTFLoader
  • Update shader codes
  • Add shininess and specular to GUI in the webgl_materials_physical_transmission example to check the specular
  • Replace the environment in webgl_loader_gltf_transmission example with another one because the glTF IridescentDishWithOlives will look nicer
  • Replaced the glTF IridescentDishWithOlives file with the better compressed one (Thanks @donmccurdy)

Notes

  • This change introduces F90 controls in the shader. Currently F_Schlick() and other functions in the shader code are optimized by assuming F90 is 1.0. In this PR I updated only F_Schlick() for the parameterized F90 so far. Perhaps I should have updated other functions, e.g. F_Schlick_RoughnessDependent(), for the parameterized F90, too, but I was unsure how to apply F90 to them so I didn't touch them so far. (I'm still new to PBR shader code). Following up is very welcome.

@WestLangley
Copy link
Collaborator

@takahirox Thanks for this!

The KHR_materials_specular specification is neither consistent nor precise the nomenclature it uses, but I believe the following is correct:

  • specular (a float) scales the amount of specular reflection for non-metals only. When set to zero, the model is effectively Lambertian.

  • specularColor (a Color) tints the specular reflection at normal incidence for non-metals only.

These properties are for artistic control, and are neither physically-based nor physically plausible.

Consequently, I do not think they are appropriate as properties of MeshPhysicalMaterial directly.

I think they should only be used to extend MeshPhysicalMaterial as needed in the GLTFLoader -- just as is done with pbrSpecularGlossinessMaterial.

@WestLangley
Copy link
Collaborator

WestLangley commented Jul 20, 2021

The KHR_materials_specular specification uses these terms, although sometimes with "Factor" added:

specular <float> in [ 0, 1 ]
specularTexture

specularColor <Color>
specularColorTexture

You have renamed these terms to:

shininess <float>
shininessMap

specular <Color>
specularMap

I do not think shininess is appropriate. I suggest:

specularStrength <float> in [ 0, 1 ]
specularStrengthMap

specularTint <Color>
specularTintMap

The so-called specularColor is applied as a tint, not as an absolute color. Hence "tint" is more accurate. Tint is the term used in the Dassault model.

@donmccurdy
Copy link
Collaborator

Many thanks @takahirox!

I agree with @WestLangley's suggestions on renaming to specularStrength and specularTint, if these properties are added to MeshPhysicalMaterial. I would like to think a bit more about the tradeoffs of using a custom ShaderMaterial vs. MeshPhysicalMaterial though.

@mrdoob
Copy link
Owner

mrdoob commented Jul 20, 2021

@WestLangley

How about:

specular <float> in [ 0, 1 ]
specularMap

specularTint <Color>
specularTintMap

Just like metalness, metalnessMap, roughnes, roughnessMap, etc

@mrdoob mrdoob added this to the r131 milestone Jul 20, 2021
@donmccurdy
Copy link
Collaborator

donmccurdy commented Jul 20, 2021

One other thought on naming. The glTF extension defines specularTexture A and specularColorTexture RGB with the intention that they can (and often should) be packed into a single RGBA texture. For a few reasons Khronos does not require that packing, so they can be separated, but if we (three.js) would prefer to support only a shorter list of properties...

  • specularMap: RGBA texture
  • specular: color
  • specularStrength: scalar

... we could certainly do that, and just require users to pack their textures accordingly.

@takahirox
Copy link
Collaborator Author

takahirox commented Jul 21, 2021

Thanks for the renaming suggestion! I was very unsure if the naming is good, so the suggestion is very helpful.

I don't have any big preference about the names but one thing I can say is it may be better to avoid to use the name specular for scalar because we already use specular for color. It may be confusing to users.

@WestLangley
Copy link
Collaborator

WestLangley commented Jul 21, 2021

specularStrength, specularTint would be OK.

I like even better specularIntensity, specularTint.

//

I would not add these as properties of the material directly -- only apply them as extensions.

EDIT: On second thought, maybe it would be OK to add properties to MeshPhysicalMaterial which are compatible with the metalness-roughness workflow, and leave as extensions properties compatible with the specular-glossiness workflow. I guess I don't feel that strongly about it after all... :-)

//

Then, in an example, have a GUI checkbox, that allows the user to toggle on various extensions -- in varying combinations.

The GUI should also allow the user the alter the primary material parameters.

For the geometry, use a sphere -- or polyhedron, if you need to demonstrate flat shading.

That should be sufficient to demonstrate the code is working correctly.

Anything more complex is a "showcase".

@donmccurdy
Copy link
Collaborator

donmccurdy commented Jul 21, 2021

EDIT: On second thought, maybe it would be OK to add properties to MeshPhysicalMaterial which are compatible with the metalness-roughness workflow, and leave as extensions properties compatible with the specular-glossiness workflow.

I think this would be my preference, perhaps supporting only a packed RGBA texture if we are concerned about API complexity.

As a bit of context, the spec/gloss PBR workflow is less preferred for glTF moving forward. There was some reasonable concern that the new PBR extensions (transmission, volume, IOR, sheen, ...) × (2x PBR workflows) would create double implementation complexity for little gain. With KHR_materials_specular and KHR_materials_ior, the metal/rough workflow can handle anything the spec/gloss workflow can, and tooling support for metal/rough glTF is far better. For that reason, none of the new KHR_materials_* extensions are supported with spec/gloss workflows.

That being the case, we could actually remove the custom ShaderMaterial that GLTFLoader currently generates for spec/gloss PBR materials, and set up MeshPhysicalMaterial with appropriate specular and IOR properties instead. This comes at a cost (inverting a texture channel, and a more expensive material than the ShaderMaterial) but it would be a nice simplification of GLTFLoader, and help to steer users toward the core materials.

@WestLangley
Copy link
Collaborator

Unfortunately, this implementation is not working... It does not attenuate indirect specular lighting from environment maps properly -- or attenuate it all for that matter.

This feature is for artist control, and is not physically-based.

This is a reason not to add it to MeshPhysicalMaterial directly.

@donmccurdy
Copy link
Collaborator

This feature is for artist control, and is not physically-based.

As spec/gloss is one of the two dominant PBR workflows, I'm not sure I understand this distinction with specular properties in the metal/rough workflow. The Dassault and Autodesk metal/rough workflows similarly provide specular intensity and tint in some form. My understanding is that it's possible to break the law of conservation with incorrect specular values, or to create physically-implausible materials. But the workflow is still physically-based, expands the range of real materials that the metal/rough workflow can represent, and certainly not all physical materials have dielectric specular F0 == 0.04.

@WestLangley
Copy link
Collaborator

TDLR;

Users can change any parameter in MeshStandard/PhysicalMaterial and the model remains physically feasible or plausible. With this PR, that is not longer the case. That is a good reason to implement it as a glTF "extension".

//

This feature is for artist control, and is not physically-based.

I would like to avoid a discussion about semantics, if possible. Unfortunately, that is partly what this is about. :-)

The illumination model attempts to model physical phenomenon. Once you modify that model for artistic control, it no longer models physical phenomenon. That is why I prefer to add parameters for artistic controls via an "extension", rather than add them directly to the material.

As spec/gloss is one of the two dominant PBR workflows, I'm not sure I understand this distinction with specular properties in the metal/rough workflow.

There is a distinction between this PR and pbrSpecularGlossinessMaterial. This is not that.

The Dassault and Autodesk metal/rough workflows similarly provide specular intensity and tint in some form.

Right. And Dassault makes clear it is for artistic control.

My understanding is that it's possible to break the law of conservation with incorrect specular values, or to create physically-implausible materials.

Right. With this PR it is possible -- but not without it. We try to be careful to ensure that.

But the workflow is still physically-based,

I would say an illumination model can be physically-based. A workflow is just a reference to the model parameters.

With this PR, the workflow remains the same, but the modified model can violate laws of physics.

expands the range of real materials that the metal/rough workflow can represent,

I do not see the addition of these parameters as expanding the range of physical materials the model can represent.

and certainly not all physical materials have dielectric specular F0 == 0.04.

We can already handle that.

MeshStandardMaterial models certain plastics for which 0.04 is a reasonable value for specular reflectance.

MeshPhysicalMaterial allows that value to be changed via the reflectivity parameter.

Also, a glTF extension allows ior to be set, and that serves the same purpose.

@elalish
Copy link
Contributor

elalish commented Jul 23, 2021

@WestLangley You make good points, and in fact these very points were debated at length in the glTF PBRNext working group (which I attend) as this extension was being hammered out. This extension grew out of compromise, which I will briefly summarize here (for the record, I mostly defended your argument during the proceedings).

  1. There are lots of Specular Glossiness material workflows in the world that we don't want to abandon, but we do want to deprecate the Specular Glossiness extension.

  2. The physical world is complicated and every parameterization is to some extent arbitrary and incomplete (including things like metalness and how we define its specular behavior). Some materials have specularity that doesn't quite match the metalness parameterization, and this extension allows a more flexible one. Some combinations of parameters may result in material properties that don't exist in the real world, but that is already true of our existing parameterizations (how does everyone feel about transparent aluminum?).

If this is super important to you, I'd recommend you join the glTF Khronos group. There are a lot of smart people there, and once they set the standard, it becomes hard to push back against.

@WestLangley
Copy link
Collaborator

Thank you, @elalish, for your comments and explanation. :-)

but we do want to deprecate the Specular Glossiness extension.

Ah, I was not aware of that. So we need to accommodate the specular-glossiness workflow in another way...

//

So it remains:

  1. Should specularIntensity and specularTint (and their maps) be added as properties to MeshPhysicalMaterial -- or only as-needed to comply with the KHR_materials_specular extension? /ping @donmccurdy

  2. To do: IBLs need to be handled correctly in three.js under this extension.

@elalish
Copy link
Contributor

elalish commented Jul 23, 2021

The renders are looking good; is there a reason @WestLangley thinks IBLs are not handled properly in this PR? edit: wait, I see the comment now. Yeah, worth looking into.

All the other glTF PBR Next extensions seem to be added directly to MeshPhysicalMaterial, right? I don't think this one is really any different. Is there a cost associated?

@WestLangley
Copy link
Collaborator

WestLangley commented Jul 23, 2021

All the other glTF PBR Next extensions seem to be added directly to MeshPhysicalMaterial, right? I don't think this one is really any different.

The spec-gloss properties were not added. And for good reason: total user confusion.

As I understand it, this PR is a replacement for that.

Also see my comments above regarding ior and reflectivity. We can now add tint to the list. Having all three simultaneously would be a terrible API.

@WestLangley
Copy link
Collaborator

@WestLangley said

Unfortunately, this implementation is not working... It does not attenuate indirect specular lighting from environment maps properly -- or attenuate it all for that matter.

I have fixes for this PR that appear to be working nicely. IBLs now attenuate. I have not addressed area lights, yet, nor clearcoat, so there is still more to do...

@takahirox
Copy link
Collaborator Author

takahirox commented Jul 27, 2021

@WestLangley

To do: IBLs need to be handled correctly in three.js under this extension.

Yes, that's the plan. Sorry I didn't clearly write but the PR comment implied that IBL and other parts needs to be updated later.

In this PR I updated only F_Schlick() for the parameterized F90 so far. Perhaps I should have updated other functions, e.g. F_Schlick_RoughnessDependent(), for the parameterized F90, too, but I was unsure how to apply F90 to them so I didn't touch them so far. (I'm still new to PBR shader code). Following up is very welcome.

I have fixes for this PR that appear to be working nicely. IBLs now attenuate.

I'm happy if you make following up PRs after this PR is merged.

@mrdoob
Copy link
Owner

mrdoob commented Jul 27, 2021

We currently have lightMapIntensity, aoMapIntensity, emissiveIntensity and envMapIntensity...

So I think specularStrength should be specularIntensity too.

@WestLangley
Copy link
Collaborator

#22156 (comment)

specularIntensity, specularTint

@donmccurdy
Copy link
Collaborator

Also see my comments above regarding ior and reflectivity. We can now add tint to the list. Having all three simultaneously would be a terrible API.

Yes, this would likely be confusing. I'm not sure the docs are really clear (even now) that ior and reflectivity are just different ways of setting the same thing. The specularIntensity and specularTint properties added by this PR are distinct, so (IMO) the way to improve this would be to deprecate either ior or reflectivity, since they are equivalent? Could be done in a separate PR.

@WestLangley
Copy link
Collaborator

so (IMO) the way to improve this would be to deprecate either ior or reflectivity,

When @bhouston and I discussed the implementation of the Physical material when it was introduced, we wanted all parameters to take [0, 1] values. Hence, reflectivity is a re-parameterization of ior.

However, it is fine with me to deprecate reflectivity.

ior is nominally in [1, 2+].

@mrdoob
Copy link
Owner

mrdoob commented Jul 27, 2021

@WestLangley Just to clarify...

The current names in this PR are these:

this.specularStrength = 1.0;
this.specularStrengthMap = null;
this.specular = new Color( 1, 1, 1 );
this.specularMap = null;

What names should we use instead?

@WestLangley
Copy link
Collaborator

this.specularIntensity = 1.0;
this.specularIntensityMap = null;
this.specularTint = new Color( 1, 1, 1 );
this.specularTintMap = null;

@bhouston
Copy link
Contributor

bhouston commented Jul 27, 2021 via email

@takahirox
Copy link
Collaborator Author

I have renamed specularStrength and specular of MeshPhysicalMaterial to specularIntensity and specularTint.

Regarding the reflectivity deprecation, I want it to be in another PR because it's a breaking change, I think it would be easier for devs to track the change in a separated PR.

@mrdoob mrdoob merged commit dc5aafe into mrdoob:dev Jul 28, 2021
@mrdoob
Copy link
Owner

mrdoob commented Jul 28, 2021

Thanks!

@chubei-oppen
Copy link
Contributor

Looks like the documentation of specular* properties of MeshPhysicalMaterial is missing. If no one is working on that, I can make a PR.

@WestLangley
Copy link
Collaborator

@chubei-oppen Please do. :-)

@3D-Dev
Copy link

3D-Dev commented Feb 1, 2022

Thanks for your upgrade!
I used the r125 version of threejs in my project, how do I update for KHR-material?
If I used only three.js, three.min.js, three.module.js and components(js and jsm), need the rewrite with the new version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants