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

Correctly interpret material roughness as perceptual and convert to material #34

Merged
merged 4 commits into from
Jul 8, 2017

Conversation

snagy
Copy link
Contributor

@snagy snagy commented Jun 20, 2017

The roughness value stored in the PBR textures is perceptual linear roughness, and needs to be converted to material roughness. Most folks square the linear roughness to get a good material roughness estimate, so we'll do that as well.

This changes the look of surfaces but is more aligned with how other renderers display the same content.

@emackey
Copy link
Member

emackey commented Jun 20, 2017

Looks good to me. Will the name alpha be confusing? Could we call it roughnessSquared or something like that?

pr_34

@emackey emackey requested a review from moneimne June 20, 2017 18:59
@snagy
Copy link
Contributor Author

snagy commented Jun 20, 2017

I was back and forth on the naming; I feel like roughnessSq doesn't properly convey the conceptual difference between the perceptual roughness in the texture and the transformed material roughness.

If anything, I could just name one "perceptualRoughness" and the other "materialRoughness" or something.

@moneimne
Copy link
Contributor

Oh, this is interesting. I had no idea that the roughness from the textures wasn't the same as the material roughness discussed in papers. @snagy, do you have a reference to confirm this?

I do agree that the branch's results captures the specular falloffs more nicely.

As for naming, I think that being explicit and naming the variables linearRoughness for the texture read, roughness for material roughness, and alpha for roughness squared might be a good idea.

@javagl
Copy link
Contributor

javagl commented Jun 20, 2017

Isn't that just a ... how to call it ... "implementation detail" or "convention" that was introduced by Disney? See https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf#page=15 , penultimate paragraph:

For roughness, we found that mapping α = roughness² results in a more perceptually linear change
in the roughness. Without this remapping, very small and non-intuitive values were required for
matching shiny materials. Also, interpolating between a rough and smooth material would always
produce a rough result.

Not sure what's "right" and "wrong" here, though....

@snagy
Copy link
Contributor Author

snagy commented Jun 20, 2017

It's definitely a convention, @javagl, but one that's currently followed in Unreal, Unity, and Frostbite, so I feel like we should side with the masses.

Edit: as well as Babylon.js and three.js!

@javagl
Copy link
Contributor

javagl commented Jun 20, 2017

Sure! It doesn't make sense to propose a technique that will look different from all other PBR renderers.

I also wanted to add the link here to give a reference (as moneimne asked for this), and maybe this explains where the name "alpha" comes from - although I also think that this is a highly unfortunate name, as "alpha" in rendering usually refers to opacity (24 letters, and they had to pick this one... however, roughnessSquared, maybe with a comment like // This is called alpha in [ref to disney] may be an option)

@moneimne
Copy link
Contributor

Well I guess my call for reference is less about the theory and more about the glTF 2.0 spec here. Roughness will be a value between 0.0 and 1.0 regardless of whether we square it or not, but which visual results would artists expect when creating a model?

Right now, the spec only mentions that roughness is "the roughness of the material" and that α = roughness ^ 2. If α refers to materialRoughness squared, then isn't roughness in the spec materialRoughness and not perceptualRoughness?

@emackey
Copy link
Member

emackey commented Jun 21, 2017

one that's currently followed in Unreal, Unity, and Frostbite, ...

as well as Babylon.js and three.js!

Given that the other implementations are already doing this, our behavior should follow theirs. It was already demonstrated with the "Y flip" issue that at this late stage we would rather clarify (or even edit) the spec to match existing implementations, rather than ask all of the implementations to change.

@snagy
Copy link
Contributor Author

snagy commented Jun 21, 2017

The spec already describes the behavior represented in this PR:

The following equations show how to calculate bidirectional reflectance distribution function (BRDF) inputs (cdiff, F0, α) from the metallic-roughness material properties.
[...]
α = roughness ^ 2

I don't think an update to the spec is required, and I don't really think that we need to call this out specifically in the shader code as to why we're doing it (there's a lot of conventional assumptions being made all over the place!)

For me, the main question is naming; do we stick with 'alpha' because that's what the spec calls it, or should we call it something else?

@emackey
Copy link
Member

emackey commented Jun 21, 2017

For me, the main question is naming; do we stick with 'alpha' because that's what the spec calls it

Formulas in the spec tend to use single-letter variable names, but implementations would do well to use longer, clearer names where possible.

alpha is such an over-used term in graphics development. Can we call it roughnessAlpha or roughnessSquared or anything like that?

@moneimne
Copy link
Contributor

Ah, I'm sorry I should have read the line changes more carefully in the shader. I assumed we were already taking into account that alpha is roughness squared, so I thought we were squaring it again for some reason.

I didn't know that alpha becomes the new value of roughness for calculations outside of the NDF, GGX, though. Does this mean we should also use alpha when reading the BRDF LUT and in the final scale based on roughness?

@@ -106,7 +107,7 @@ float geometricOcclusionCookTorrance(PBRInfo pbrInputs)
// implementation of microfacet occlusion from “An Inexpensive BRDF Model for Physically based Rendering” by Christophe Schlick
float geometricOcclusionSchlick(PBRInfo pbrInputs)
{
float k = pbrInputs.roughness * 0.79788; // 0.79788 = sqrt(2.0/3.1415);
float k = pbrInputs.alpha * 0.79788; // 0.79788 = sqrt(2.0/3.1415);
// alternately, k can be defined with
// float k = (pbrInputs.roughness + 1)*(pbrInputs.roughness + 1)/8;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be wrong here, but in Schlick's paper, he presents k=sqrt(2*m*m / PI) (Equation 19). Does that suggest that we should keep pbrInputs.roughness here?

Also, I noticed in the Epic paper that they only did this alpha remapping (roughness + 1)/2) * (roughness + 1)/2), for analytic light sources and that with IBL the results at glancing angles is too dark.

@abwood
Copy link
Contributor

abwood commented Jun 26, 2017

@moneimne I don't think we should use alpha for the brdf lookup. In their paper, they state the input is Roughness (with no mention to alpha).

@moneimne
Copy link
Contributor

@abwood, yeah I think you're right. The sample code in UE4's paper seems to distinguish between alpha and roughness, too.

@emackey
Copy link
Member

emackey commented Jun 29, 2017

@moneimne @abwood Thoughts on this update when you get a chance?

// alternately, k can be defined with
// float k = (pbrInputs.roughness + 1)*(pbrInputs.roughness + 1)/8;
// float k = (pbrInputs.alphaRoughness + 1)*(pbrInputs.alphaRoughness + 1)/8;
Copy link
Contributor

@abwood abwood Jun 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Epic's implementation notes, this commented-out alternative should use pbrInputs.perceptualRoughness rather than alphaRoughness:
float k = (pbrInputs.perceptualRoughness + 1)*(pbrInputs.perceptualRoughness + 1)/8;

See Equation 4 in http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my impression, too.

We also chose to use Disney’s modification to reduce “hotness” by remapping roughness using (Roughness+1)/2 before squaring.

@abwood
Copy link
Contributor

abwood commented Jun 30, 2017

Just that one comment from me. From a visual standpoint this is a great improvement 👍

Copy link
Contributor

@moneimne moneimne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall! Just echoing @abwood on his review.

// alternately, k can be defined with
// float k = (pbrInputs.roughness + 1)*(pbrInputs.roughness + 1)/8;
// float k = (pbrInputs.alphaRoughness + 1)*(pbrInputs.alphaRoughness + 1)/8;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my impression, too.

We also chose to use Disney’s modification to reduce “hotness” by remapping roughness using (Roughness+1)/2 before squaring.

@emackey
Copy link
Member

emackey commented Jul 8, 2017

Since the only remaining review comment is on a line that's commented out, I'm going to merge this.

@abwood can you open a PR with the proposed change that didn't get included here? Thanks!

@emackey emackey merged commit 53a30f8 into KhronosGroup:master Jul 8, 2017
@abwood abwood mentioned this pull request Jul 8, 2017
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.

5 participants