When I implement Beer's law (color absorption over distance through an object), it never looks very good for some reason.

When i have the color behind the object, I calculate the adjusted color like this:

const vec3 c_absorb = vec3(0.2,1.8,1.8);
vec3 absorb = exp(-c_absorb * (distanceInObject));
behindColor *= absorb;

That will give me something that looks like this (note a little bit of refraction applied): enter image description here

And here it is without refraction: enter image description here

Note that this is implemented as a shader toy here.

That fulfills the description of what Beer's law does, but it doesn't look very good, not when compared to shots like this: enter image description here

Specular highlights aside, I'm trying to figure out the difference. Could it just be that my geometry is too simple to really show it off very well? Or am I implementing it incorrectly?

  • 2
    $\begingroup$ You're comparing a cube with a more complex mesh however. Why not replicate the same scenario? The Susan model is easy to get. $\endgroup$
    – Bart
    Aug 20, 2015 at 15:15
  • $\begingroup$ It's not so easy in a shadertoy implementation! (: $\endgroup$
    – Alan Wolfe
    Aug 20, 2015 at 15:16
  • 2
    $\begingroup$ You cube looks correct to me: Get more transparent as it approaches the edges. If you can do full blown Suzanne the a sphere should at least give a better approximation of the look in the other picture. $\endgroup$
    – yuriks
    Aug 20, 2015 at 15:51
  • 1
    $\begingroup$ I can't separate the refraction from the attenuation. Can you render the cube with IOR=1.0 please? $\endgroup$
    – geometrian
    Sep 1, 2015 at 16:19
  • 1
    $\begingroup$ @AlanWolfe Your IOR=1 render looks exactly as I would expect it, and I skimmed the shadertoy impl and it looks good. $\endgroup$
    – geometrian
    Sep 2, 2015 at 1:26

1 Answer 1


Your image definitely does not look correct, and it appears that you are not correctly computing the internal path of light rays as they travel through your mesh. From the looks of it, I would say that you are computing the distance between the point where the view ray first enters the cube and where it first hits the interior wall, and using that as your absorption distance. This essentially assumes that light will always exit the glass the first time it hits a wall, which is a poor assumption.

In reality, When light enters glass from air, it often does not immediately exit the glass. This is because when the light strikes the glass/air interface, a phenomenon known as total internal reflection (TIR) can occur. TIR occurs when light travels from a medium with a higher index of refraction (IOR) to one with a lower IOR, which is precisely what happens in the case of light hitting the interior wall of a glass object. This image from Wikipedia is a good visual demonstration of what it looks like when it occurs:

total internal reflection

In basic terms, what it means is that if the light hits at a shallow angle, the light will completely reflect off of the interior of the medium. To account for this, you need to evaluate the Fresnel equations every time that your light ray hits a glass/air interface (AKA the interior surface of your mesh). The Fresnel equations will tell you the ratio of reflected light to the amount of refracted light, while will be 1 in the case of TIR. You can then compute the appropriate reflected and refracted light directions, and continue to trace the light's path either through the medium or outside of it. If you assume a simple convex mesh with a uniform scattering coefficient, then the the distance to use for Beer's law will be the sum of all internal path lengths before exiting the medium. Here is what a cube looks like with your scattering coefficients and IOR of 1.526 (soda lime glass), rendered using my own path tracer that accounts for both internal and external reflections and refractions:

path-traced glass cube

Ultimately the internal reflections and refractions are a major part of what makes glass look like glass. Simple approximations really don't cut it, as you've already found out. It gets even worse if you add multiple meshes and/or non-convex meshes, as you not not only have to account for internal reflections but must also account for rays that leave the medium and enter it at a different point.

  • 1
    $\begingroup$ Here is what a cube looks like (...) using my own path tracer. Do you happen to have open-sourced it by any chance ? $\endgroup$
    – wip
    Sep 3, 2015 at 2:53
  • 2
    $\begingroup$ No, not yet. I was planning on finishing and releasing this particular variant along with a blog post about glass rendering, but it's been on my backlog for a while. $\endgroup$
    – MJP
    Sep 3, 2015 at 4:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.