<p>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.</p>

<p>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 <a href="https://en.wikipedia.org/wiki/Total_internal_reflection" rel="noreferrer">total internal reflection</a> (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:</p>

<p><a href="https://i.sstatic.net/4lSRl.jpg" rel="noreferrer"><img src="https://i.sstatic.net/4lSRl.jpg" alt="total internal reflection"></a></p>

<p>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 <a href="https://en.wikipedia.org/wiki/Fresnel_equations" rel="noreferrer">Fresnel equations</a> 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:</p>

<p><a href="https://i.sstatic.net/7MOow.png" rel="noreferrer"><img src="https://i.sstatic.net/7MOow.png" alt="path-traced glass cube"></a></p>

<p>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.</p>

CC BY-SA 3.0
2015-09-03T02:53:30.507Z
`Here is what a cube looks like (...) using my own path tracer.`  
Do you happen to have open-sourced it by any chance ?
wip
CC BY-SA 3.0
2015-09-03T04:27:27.400Z
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.
MJP
CC BY-SA 3.0
2015-09-03T01:44:56.440Z
2015-09-03T01:44:56.440Z
MJP
<p>When I implement Beer's law (color absorption over distance through an object), it never looks very good for some reason.</p>

<p>When i have the color behind the object, I calculate the adjusted color like this:</p>

<pre><code>const vec3 c_absorb = vec3(0.2,1.8,1.8);
vec3 absorb = exp(-c_absorb * (distanceInObject));
behindColor *= absorb;
</code></pre>

<p>That will give me something that looks like this (note a little bit of refraction applied):
<a href="https://i.sstatic.net/XQbjV.png" rel="noreferrer"><img src="https://i.sstatic.net/XQbjV.png" alt="enter image description here"></a></p>

<p>And here it is without refraction:
<a href="https://i.sstatic.net/gOPZn.png" rel="noreferrer"><img src="https://i.sstatic.net/gOPZn.png" alt="enter image description here"></a></p>

<p>Note that this is implemented as a shader toy <a href="https://www.shadertoy.com/view/4lXSDf" rel="noreferrer">here</a>.</p>

<p>That fulfills the description of what Beer's law does, but it doesn't look very good, not when compared to shots like this:
<a href="https://i.sstatic.net/nchoX.png" rel="noreferrer"><img src="https://i.sstatic.net/nchoX.png" alt="enter image description here"></a></p>

<p>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?</p>

CC BY-SA 3.0
2015-09-01T16:19:11.247Z
I can't separate the refraction from the attenuation. Can you render the cube with IOR=1.0 please?
geometrian
CC BY-SA 3.0
2015-09-01T16:26:15.773Z
added imallett, also linked to the shadertoy
Alan Wolfe
CC BY-SA 3.0
2015-09-02T01:26:01.410Z
@AlanWolfe Your IOR=1 render looks exactly as I would expect it, and I skimmed the shadertoy impl and it looks good.
geometrian
CC BY-SA 3.0
2017-05-19T07:38:49.653Z
The path tracing example at the bottom is simulating total internal reflection also which adds a lot of complexity to the shading. Thin areas can appear both 'thicker' and more attenuated like the rims of the ears for example.
PaulHK
CC BY-SA 3.0
2015-08-20T15:15:03.147Z
You're comparing a cube with a more complex mesh however. Why not replicate the same scenario? The Susan model is easy to get.
Bart
CC BY-SA 3.0
2015-08-20T15:16:15.037Z
It's not so easy in a shadertoy implementation! (:
Alan Wolfe
CC BY-SA 3.0
2015-08-20T15:51:06.460Z
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.
yuriks
CC BY-SA 3.0
2015-08-20T16:37:42.147Z
ok fair enough.  i'll try it with better geometry, but i think you are right.  I'll accept it if you make an aswer!
Alan Wolfe
<3d><refraction><transparency>
Is this the correct way to implement Beer's Law?
Alan Wolfe
# Is this the correct way to implement Beer's Law?

(Asked by: username_0 on 2015-08-20)

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](https://i.sstatic.net/XQbjV.png)](https://i.sstatic.net/XQbjV.png)

And here it is without refraction:
[![enter image description here](https://i.sstatic.net/gOPZn.png)](https://i.sstatic.net/gOPZn.png)

Note that this is implemented as a shader toy [here](https://www.shadertoy.com/view/4lXSDf).

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](https://i.sstatic.net/nchoX.png)](https://i.sstatic.net/nchoX.png)

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?

**Tags:** 3d, refraction, transparency

> **Comments:**
> **username_3**: You're comparing a cube with a more complex mesh however. Why not replicate the same scenario? The Susan model is easy to get.  
> **username_0**: It's not so easy in a shadertoy implementation! (:  
> **username_4**: 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.  
> **username_0**: ok fair enough.  i'll try it with better geometry, but i think you are right.  I'll accept it if you make an aswer!  
> **username_1**: I can't separate the refraction from the attenuation. Can you render the cube with IOR=1.0 please?  
> **username_0**: added imallett, also linked to the shadertoy  
> **username_1**: @username_0 Your IOR=1 render looks exactly as I would expect it, and I skimmed the shadertoy impl and it looks good.  
> **username_2**: The path tracing example at the bottom is simulating total internal reflection also which adds a lot of complexity to the shading. Thin areas can appear both 'thicker' and more attenuated like the rims of the ears for example.  

---

## 1 answers

### Answer by username_5 on 2015-09-03. Score: 14 | Accepted answer

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](https://en.wikipedia.org/wiki/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](https://i.sstatic.net/4lSRl.jpg)](https://i.sstatic.net/4lSRl.jpg)

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](https://en.wikipedia.org/wiki/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](https://i.sstatic.net/7MOow.png)](https://i.sstatic.net/7MOow.png)

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.

> **Comments:**
> **username_6**: `Here is what a cube looks like (...) using my own path tracer.`   Do you happen to have open-sourced it by any chance ?  
> **username_5**: 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.  

---

