Closed Goshido closed 1 year ago
I found that reference implementation does not follow rule of conservation of energy. Long story short I found the case when point light with brightness of 11 units produces specular reflection with brightness of 26 units. You could validate it by plug in the following numbers into implementation:
albedo = (1.0, 1.0, 1.0, 1.0)
viewer location = (0.0, 0.0, 0.0)
fragment location = (45.0901, 1.4374, 34.7226)
fragment normal = (-0.957000, 0.014640, 0.288080)
light location = (23.59422, 4.62833, 88.95494)
light intensity = 11.31312
roughness = 0.2
metallic = 1.0
specular part will be (26.767, 26.767, 26.767)
which is impossible. Result brightness is bigger that light source brightness. The fragment IS NOT LOCATED closer that 1 unit from the source. So attenuation is fine.
If you calculate all in float16
types you even get INF
values.
I was suggested to try this old paper from 2012. I saw this before. I gonna give it second chance.
The problem source: BRDF by design should be used with integration math. Calculation of the direct value from viewer direction, fragment normal and light position does not make much sense. D
and G
component is a normal-like distributions. There are peaks at infinity with extreme input values.
The volume under the distribution graph makes real sense.
Of course direct integration per each fragment is too computation heavy for runtime. The solution is pre-integrate D
and GGXShick
terms as LUT
table. Such LUT will be used in runtime at directional lighting stage.
After some research and asking in "Graphics Programming" Discord server I found solution. All materials could be found in questions-forum
category. The name Cook-Torrance don't conserve energy for direct lighting
So it turns out that only problem is in D
term. There are several general solutions and combinations:
The clamped graph of D
looks like this
In case of G
term turns out it's already in range of 0-1:
G - term, roughness 0.000, max 1.000
G - term, roughness 0.143, max 1.000
G - term, roughness 0.286, max 1.000
G - term, roughness 0.429, max 1.000
G - term, roughness 0.571, max 1.000
G - term, roughness 0.714, max 1.000
G - term, roughness 0.857, max 1.000
G - term, roughness 1.000, max 1.000
After clamping D
term all INF
values gone from HDR image. And there are no any pixel which will be brighter than light source. My last concern is only about discontinuity. It could introduce new artifacts on different scenes.
But result is good enough at this scene.
Idea about LUT and pre-integrating is incorrect for point light source. It's still valid to directly plug values into BRDF equation and get final pixel color without any loops.
It was found that point light pass produces
INF
values. It's needed to fix.Steps
master
branch