Is this the correct way to implement Beer's Law? 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):


And here it is without refraction:


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:


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?
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:



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:



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.