Advertisement

PBR - casting shadows with a blue tint

Started by October 08, 2018 02:30 PM
4 comments, last by Aceticon 6 years, 3 months ago

So, there is one thing that i don't quite understand. (Probably because i didn't dive that deep into PBR lighting in the first place.)

Currently, i implemented a very basic PBR renderer (with the BRDF microfaced shading model) into my engine. The lighting system i have is pretty basic (1 directional/sun light, deffered point lights and 1 ambient light)

I don't have a GI solution yet. (Only a very basic world-space Ambient occlusion technique).

Here is how it looks like:

Spoiler

shadow_bw.thumb.png.6ebad5dc4c5804ce0863134857b749ee.png

Now, what i would like to do is to give the shadows a slightly blueish tint. (To simulate the blueish light from the sky.)

Unreal seems to implement this too which gives the scene a much more natural look:

Spoiler

unreal_shadow.PNG.e17660e2431eea7f4d984c8c5e6cfd12.PNG

Now, my renderer does render in HDR and i use exposure/tonemapping to bring this down to LDR.

The first image used an indirect light with a RGB value of (40,40,40) and an indirect light of (15,15,15).
Here is the same picture but with an ambient light of (15,15,15) * ((109, 162, 255) / (255,255,255)) which should give us this blueish tint.

The problem is it looks like this:

Spoiler

shadow_blue.thumb.png.ddb2d3b088000063b025e03dfe6213b5.png

The shadows do get the desired color (more or less) the issue is that all lit pixels also get affected giving the scene a blue tint.

Reducing the ambient light intensity results in way too dark shadows. Increase the intensity and the shadows look alright but then the whole scene gets affected way too much.

In the shader i basically have:


color = directionalLightColor * max(dot(normal,sunNormal),0.0) + ambientLight;

The result is that the blue component of the color will be always higher than the other two.

I could of course fix it by faking it (only adding the ambient light if the pixel is in shadow) but i want to stay as close to PBR as possible and avoid adding hacks like that.

My question is: How is this effect done properly (with PBR / proper physically based lighting)?

The whole point of PBR is to do Physically (that's what that P stands for) Realistic Illumination, so what you just did was make the entire space around everything emit blue light, something impossible in the real world.

If you had GI you would naturally use the skybox to provide the environmental illumination (which is what replaces ambient light in PBR), just like people do in 3D renders, as real world illumination comes mostly from the sky and surrounding surfaces.

From a PBR point of view you're already hacking it by using ambient light, so your solution if you really want to make the shadows look as you expect without doing it in a proper PBR way is to hack it further, IMHO.

Advertisement

If I understand correctly, you want fully lit pixels to have zero ambient color and fully unlit pixels to use the ambient color. This way the fully lit pixels should match that first screenshot. If this is true, then making this happen isn't a "hack" but simply your particular requirement. However, I agree with Aceticon that your PBR is already hacked so it doesn't really matter.

Something like the following could work:


float factor = max(dot(normal,sunNormal), 0.0);
color = vec4(mix(ambientLight, directionalLightColor, factor), 1.0);

 

I'm fully aware that PBR does require proper illumination in order to achieve the best results

I don't have the ability to do that yet though. (I neither have realtime nor pre-beaked GI). So what i'm trying to do is to approximate the illumination as best as possible without deviating from PBR as much.

The only thing i have is a very basic implementation of baked AO but even then it's not even single bounce:

Spoiler

AO.thumb.png.46b5234210ca134df1130e6ddda6281f.png

inside.thumb.png.2e5ffd37e79edde532fb60316afeb169.png

(Note that the floor is still unaffected by the shading as i didn't include the floor mesh into the baking process)

The ambient lighting in this case does represent the lighting of the sky (areas which are occluded get progressively darker the less they are lit by the sky).

So what i tried to do is to have only 1 lightsource (the sun) and simulate lighting from the sky/GI by having an ambient light which gets filled into the occluded areas. In order to darken interiors a baked AO texture is implemented by multiplying it with the AO value.

So the shader code looks roughly like this:


vec3 color  = texture2D(sLight, vTexcoord).rgb;//this is the accumulated light on the given fragment (from the directional light)

float shadow = texture2D(sShadowMap, vTexcoord).r;//tells us if the pixel is in shadow. 0 = fragment is occluded by the sun, 1 = pixel is lit by the sun

color*=shadow;//occluded pixels are set to 0 (completely dark)

//now approximate skydome-lighting
vec3 ambient = uAmbientLightColor*texture2D(gAlbedo, vTexcoord).rgb;//ambient light (fixed value) multiplied with albedo texture
float AO =  texture2D(sAmbientOcclusion, vTexcoord).r;//baked World-space Ambient Occlusion

color+=ambient;//...

//apply AO only if pixel is in SunShadow (if shadow == 0)
color *= mix(AO,1.0,shadow);

outputF = color;//write to screen

I'm fully aware that this isn't a physically correct solution.

So in my case it's either implementing a proper GI solution or hacking the PBR to achieve my desired results?

How is the ambient lighting in Unreal (seen in my first post) implemented? 

Well, in the real world the diffuse light shinning on an object is sun + sky + light reflected and refracted from the surrounding objects.

Because of just how much higher the light intensity coming from the sun is than that coming from the sky (If I remember it correctly, something like 100x stronger), that light bouncing from surrounding objects itself is mostly sun light altered by those objects' characteristics (reflected, refracted and such).

However, no GI there (yet) so no fancy pantsy "shadows next to the yellow pillar are yellowed because of the light bouncing off the pillar is yellow" stuff and such, so how about simplifying by just considering that your ambient light is made up of a bounced-sunlight component plus a sky-illumination one, while (and this is the important bit) maintaining a realistic ratio of light intensity between both?

So instead of using the 6, 9, 15 (overwhelmingly blue and darker) color value that your ambient color equation is producing, have you considered making your ambient light ever so slightly blue (start with 15, 15, 16) to reflect just how disproportionally much of the ambient light is bounced sun-light rather than sky-illumination?

After all, in your shader the color value of a pixel which maps to an area of an object in the shadow will roughly be black +  (that ambient color * albedo) thus any of-white in that color will stand out a lot, whilst in the areas illuminated directly by your sun most of the color will come from the refraction of that sun light off the object so the of-white in the ambient color stands out much less. If you look at your Unreal example, the non-shadow areas do have a slight blue tint too, so a similar effect is probably happening there.

 

This topic is closed to new replies.

Advertisement