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

Diffuse Roughness support #16253

Open
wants to merge 24 commits into
base: master
Choose a base branch
from

Conversation

MiiBond
Copy link
Contributor

@MiiBond MiiBond commented Mar 5, 2025

This PR replaces #16183

This PR implements the base_diffuse_roughness parameter from the OpenPBR specification.

The diffuse roughness is implemented for analytic lights, realtime-filtered IBL, prefiltered IBL and spherical harmonics.
I've added a flag to a material to choose between Lambert, Burley and the new Energy Conserving Oren-Nayar (EON) model used by OpenPBR. The default is OpenPBR's EON model.

The previous behaviour was to use Burley diffuse for analytical lights and Lambert for IBL. Also, previously, specular roughness was applied to diffuse roughness for analytical lights while IBL didn't use it at all (because it was simply Lambertian). So, the new default slightly changes existing projects that used analytical lights but I question how noticeable that will be.

Analytical Light:
https://playground.babylonjs.com/?snapshot=refs/pull/16253/merge#MXACV7#3
roughnessCompare

Realtime IBL:
https://playground.babylonjs.com/?snapshot=refs/pull/16253/merge#MXACV7#5
image

The diffuse roughness models are heavily dependent on the light direction and view direction and are therefore difficult to handle with a prefiltered IBL. I came up with two methods for approximating roughness with prefiltered IBL's. The first, if we prefiltered using CDF, we generate a dominant light direction to use in the BRDF calculations. It works reasonably well.

Prefiltered IBL with CDF:
https://playground.babylonjs.com/?snapshot=refs/pull/16253/merge#MXACV7#9
image

The second approach, if you don't use CDF, is by approximating roughness by bending the surface normal towards the camera to add some of the retro-reflective behaviour that you get with EON. Because of this, you'll notice that Burley and EON are identical and the shadow terminator appears to move as diffuse roughness increases. In practice, however, this example uses an extreme IBL with a bright sunlight. With other IBL's, the effect tends to be more convincing.

Prefiltered IBL without CDF
https://playground.babylonjs.com/?snapshot=refs/pull/16253/merge#MXACV7#10
image

The default IBL lighting in Sandbox uses spherical harmonics so we need to approximate diffuse roughness with this as well. I'm using the same bent normal technique as with prefiltered IBL without CDF.

Spherical Harmonics IBL
https://playground.babylonjs.com/?snapshot=refs/pull/16253/merge#MXACV7#11
image

@MiiBond MiiBond marked this pull request as draft March 5, 2025 22:03
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 5, 2025

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 5, 2025

@@ -2321,6 +2374,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
}

ubo.updateFloat("baseWeight", this._baseWeight);
ubo.updateFloat("baseDiffuseRoughness", this._baseDiffuseRoughness ?? this._roughness ?? 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I put this logic here to fallback on the old behaviour if baseDiffuseRoughness wasn't used. However, this was inconsistent between IBL and analytic lights so I suggest we just remove this old behaviour. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed the fallback to the old behaviour now. How do you guys feel about this?
The roughness previously only had an effect on diffuse for analytical lights.

Copy link
Member

Choose a reason for hiding this comment

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

Would by default the rendering be impacted with the new code ? We had a few remarks/issues regarding the latest fixes in the PBR with ppl willing to use back the old behavior. If the impact is tiny it is ok but if it is more, we need at least a fallback flag

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, there is a change to the default behaviour. It only affects rough surfaces that use analytical lights (the default behaviour of IBL is unaffected). Previously, the surface roughness was used as input to the Burley diffuse model but this was only ever used for analytical lights (i.e. direct lighting). IBL didn't use Burley and so diffuse irradiance from IBL was unaffected by roughness. This inconsistency is one of the reasons I think we should change the default behaviour.

Here's an example of the most extreme case where the change would be most noticeable. The sphere on the right has roughness 1.0.
Before the change:
image

After the change:
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most cases won't be nearly this noticeable, of course. Diffuse roughness (for Burley or EON) is most noticeable when the view is aligned with the light direction, like in this example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another point to note is that, if we change the default diffuse roughness model to EON, rather than Burley, the default look of these cases is going to change anyway.

I'm seeing that in a lot of these visualization test failures.

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 5, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 5, 2025

1 similar comment
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 6, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 6, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 6, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 6, 2025

@MiiBond MiiBond force-pushed the openpbr/diffuse_roughness branch from 3580a82 to 55f34a0 Compare March 7, 2025 04:29
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 7, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 7, 2025

1 similar comment
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 7, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 7, 2025

Copy link
Contributor

@virtualzavie virtualzavie left a comment

Choose a reason for hiding this comment

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

This is only a partial review with a couple of comments.

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure everything is correct with the bottom row? It seems to have way too much energy.
Burley is not energy conserving, but I don't think it should be this bright.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I thought the same thing but it seems as though this retro-reflectivity is expected when viewing directly along the direction of the light. When viewing from the side, it gets a lot darker.
image
However, it is still slightly brighter than it should be and this is due to me post-multiplying the albedo rather than passing the albedo into the EON computation. For analytical lights and realtime filtering of the IBL, we can do EON correctly but I also want this to work for prefiltered IBL's so I want a solution where we can post-multiply the albedo. I'm working on improving this...

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this render black? Does it not include your IBL implementation yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because I didn't check the output before I pushed my changes :)

Thanks for pointing this out. It's fixed now.

"title": "OpenPBR Base Diffuse Roughness Realtime IBL",
"playgroundId": "#MXACV7#5",
"referenceImage": "openpbr_base_diffuse_roughness_realtime_ibl.png",
"excludedEngines": ["webgl1"]
Copy link
Contributor

@virtualzavie virtualzavie Mar 7, 2025

Choose a reason for hiding this comment

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

Why is webgl1 excluded?

Copy link
Contributor Author

@MiiBond MiiBond Mar 10, 2025

Choose a reason for hiding this comment

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

Good point. The CDF generator doesn't work in WebGL 1.0 but that shouldn't prevent this test from running. I've tested in WebGL 1.0 now and fixed a whole bunch of shader issues.
I think I can make the CDF generator work in WebGL 1.0 as well (as part of a future PR). I just have to get rid of all the texelFetch calls since that function doesn't exist in WebGL 1. Originally, the CDFGenerator was only for IBL Shadows which can't work in WebGL 1 because it doesn't support 3D textures.

Copy link
Contributor

@virtualzavie virtualzavie left a comment

Choose a reason for hiding this comment

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

Continued review.

Comment on lines 249 to 254
vec3 diffuseRoughnessTerm = vec3(1.0);
#if BASE_DIFFUSE_ROUGHNESS_MODEL == 1
diffuseRoughnessTerm = vec3(diffuseBRDF_Burley(NoL, NoV, VoH, diffuseRoughness) * PI);
#elif BASE_DIFFUSE_ROUGHNESS_MODEL == 2
diffuseRoughnessTerm = diffuseBRDF_EON(vec3(1.0), diffuseRoughness, NoL, NoV) * PI;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit surprised there's a ×π factor for Burley and EON but not for Lambert. The result looks correct for Lambert though, so when does it happen? Is there a discrepency regarding when it's factored in?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both diffuseBRDF_Burley and diffuseBRDF_EON contain an explicit 1.0 / PI.

Copy link
Contributor

Choose a reason for hiding this comment

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

It sounds like the engine assumes the ×π factor cancels out for Lambert, but keeps it explicit in the other two...

@@ -264,6 +266,7 @@
#else
vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
#endif
vec3 irradianceView = vec3(reflectionMatrix * vec4(viewDirectionW, 0)).xyz;
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't it be preferable to do:

vec3 irradianceView = mat3(reflectionMatrix) * viewDirectionW;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment on lines 44 to 49
float diffuseTerm = 1.0 / PI;
#if BASE_DIFFUSE_ROUGHNESS_MODEL == 1
diffuseTerm = diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.diffuseRoughness);
#elif BASE_DIFFUSE_ROUGHNESS_MODEL == 2
diffuseTerm = diffuseBRDF_EON(vec3(1.0), info.diffuseRoughness, info.NdotL, info.NdotV).x;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the scale ×1/π ; ×1 ; ×1 here, whereas it was ×1 ; ×π ; ×π in the other implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one is for direct lighting from an analytical light. The other was for the IBL filtering and I think the PI is already accounted for in the PDF.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. It makes sense, although that's not very consistent.

Comment on lines 73 to 78
float diffuseTerm = 1.0 / PI;
#if BASE_DIFFUSE_ROUGHNESS_MODEL == 1
diffuseTerm = diffuseBRDF_Burley(NdotL, info.NdotV, info.VdotH, info.diffuseRoughness);
#elif BASE_DIFFUSE_ROUGHNESS_MODEL == 2
diffuseTerm = diffuseBRDF_EON(vec3(1.0), info.diffuseRoughness, NdotL, info.NdotV).x;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Same remark here.

Comment on lines 38 to 43
var diffuseTerm: f32 = 1.0 / PI;
#if BASE_DIFFUSE_ROUGHNESS_MODEL == 1
diffuseTerm = diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.diffuseRoughness);
#elif BASE_DIFFUSE_ROUGHNESS_MODEL == 2
diffuseTerm = diffuseBRDF_EON(vec3(1.0), info.diffuseRoughness, info.NdotL, info.NdotV).x;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Same remark here.

Comment on lines 67 to 72
var diffuseTerm: f32 = 1.0 / PI;
#if BASE_DIFFUSE_ROUGHNESS_MODEL == 1
diffuseTerm = diffuseBRDF_Burley(NdotL, info.NdotV, info.VdotH, info.diffuseRoughness);
#elif BASE_DIFFUSE_ROUGHNESS_MODEL == 2
diffuseTerm = diffuseBRDF_EON(vec3(1.0), info.diffuseRoughness, NdotL, info.NdotV).x;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

And here.

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 10, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 10, 2025

1 similar comment
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 10, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 10, 2025

@MiiBond MiiBond force-pushed the openpbr/diffuse_roughness branch from 203a6cf to 5ee9410 Compare March 11, 2025 23:57
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 12, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 12, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 12, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 12, 2025

@MiiBond MiiBond marked this pull request as ready for review March 12, 2025 18:25
@MiiBond MiiBond force-pushed the openpbr/diffuse_roughness branch from 6b0f7a4 to 2f1cf0e Compare March 12, 2025 18:37
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 12, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 12, 2025

@MiiBond MiiBond force-pushed the openpbr/diffuse_roughness branch from 4bbbf90 to 8195f90 Compare March 17, 2025 21:55
@bjsplat
Copy link
Collaborator

bjsplat commented Mar 17, 2025

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 17, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 17, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 17, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 18, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 18, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 18, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 18, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 18, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 18, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 19, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 19, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 19, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Mar 19, 2025

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.

None yet

4 participants