<p>Now it looks like I have to try to figure it out myself... since I've waited for days without an answer. So I decided to dig into the code of <code>pbrt-v3</code> and I found what I need:</p>
<p><code>Spectrum BSDF::Sample_f</code>: first we convert the world direction vector into the local frame. This can be found in <code>line 714 of src/core/reflection.cpp</code>:</p>
<pre class="lang-cpp prettyprint-override"><code>Vector3f wi, wo = WorldToLocal(woWorld);

// and the WorldToLocal function just uses shading normal:
// This can be found in line 178 of src/core/reflection.h:
Vector3f WorldToLocal(const Vector3f &amp;v) const {
    return Vector3f(Dot(v, ss), Dot(v, ts), Dot(v, ns));
}
</code></pre>
<p>And then, we start to sample the BSDF. We take the typical BSDF used in glass material <code>FresnelSpecular</code> for an example (refer to <code>line 487 of src/core/reflection.cpp</code>).</p>
<pre class="lang-cpp prettyprint-override"><code>Spectrum f = bxdf-&gt;Sample_f(wo, &amp;wi, uRemapped, pdf, sampledType);
</code></pre>
<ul>
<li><p>First, sampling according to Fresnel's law, and we can see that we are using shading normal here:</p>
<pre class="lang-cpp prettyprint-override"><code>Float F = FrDielectric(CosTheta(wo), etaA, etaB);
</code></pre>
<p>since <code>wo</code> is converted to local frame (defined by shading normal)</p>
</li>
<li><p>Reflection sampling has nothing to do with ROI therefore we just use shading normal to calculate reflection. This is kinda trivial but I think I need to get back to this later and explain this with the picture posted in my question since I think there are some nasty situations. So we skip the if branch and focus the else branch.</p>
</li>
<li><p>Obviously, shading normal is used to decide the interior and exterior (ROI) for the medium:</p>
<pre class="lang-cpp prettyprint-override"><code>bool entering = CosTheta(wo) &gt; 0;
Float etaI = entering ? etaA : etaB;
Float etaT = entering ? etaB : etaA;
</code></pre>
</li>
</ul>
<p>Actually, for specular reflection/transmission materials, we have no more thing to explain: <code>line 767</code>:</p>
<pre class="lang-cpp prettyprint-override"><code>if (!(bxdf-&gt;type &amp; BSDF_SPECULAR)) {
</code></pre>
<p>skip the rest of the <code>f</code> part and even if we need to use <code>f</code>, due to being specular, all we would get is zero anyway, so why bother? Therefore for the situation shown in the figure below, if we are using simple specular reflection &amp; transmission material, maybe we only have to stick with <span class="math-container">$n_s$</span>.</p>
<p><a href="https://i.sstatic.net/U5c7X.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/U5c7X.png" alt="enter image description here" /></a></p>
<p>But what about non-specular situation? For example, what if I use <code>MicrofacetReflection</code> / <code>MicrofacetTransmission</code> (the former requires no ROI, so I use this as a simple example, the latter does, though)? I present two examples below, and the logic we should pay attention to in <code>pbrt-v3</code> is (starting from <code>line 766</code>):</p>
<pre class="lang-cpp prettyprint-override"><code>// Compute value of BSDF for sampled direction
if (!(bxdf-&gt;type &amp; BSDF_SPECULAR)) {
    bool reflect = Dot(*wiWorld, ng) * Dot(woWorld, ng) &gt; 0;
    f = 0.;
    for (int i = 0; i &lt; nBxDFs; ++i)
        if (bxdfs[i]-&gt;MatchesFlags(type) &amp;&amp;
            ((reflect &amp;&amp; (bxdfs[i]-&gt;type &amp; BSDF_REFLECTION)) ||
             (!reflect &amp;&amp; (bxdfs[i]-&gt;type &amp; BSDF_TRANSMISSION))))
            f += bxdfs[i]-&gt;f(wo, wi);
}
</code></pre>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th>Reflection</th>
<th>Transmission (Refraction)</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://i.sstatic.net/fTdT1.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/fTdT1.png" alt="enter image description here" /></a></td>
<td><a href="https://i.sstatic.net/8MIHt.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/8MIHt.png" alt="enter image description here" /></a></td>
</tr>
</tbody>
</table>
</div>
<ul>
<li><p>Reflection example. This is weird, it has the conflicts I mentioned in the question: <span class="math-container">$n_g$</span> and <span class="math-container">$n_s$</span> propose differently. However, if I understand correctly,  in this situation we have two directions on the different sides of the geometric surface (<span class="math-container">$n_g$</span>), therefore <code>bool reflect</code> is <code>false</code> here. During the for loop  <code>reflect &amp;&amp; (bxdfs[i]-&gt;type &amp; BSDF_REFLECTION</code> will be <code>false </code> and <code>!reflect &amp;&amp; (bxdfs[i]-&gt;type &amp; BSDF_TRANSMISSION)</code>  will be <code>false</code> again since <code>reflect = false</code> and <code>MicrofacetReflection</code> has type <code>BSDF_REFLECTION</code>... so <code>f</code> will actually be zero. I think it is reasonable, since we can't let the light leak beneath the geometric surface.</p>
</li>
<li><p>Transmission example. We use <code>MicrofacetTransmission</code> and sampled a transmission event, great. However, <code>bool reflect </code> here will be <code>true</code> since both directions are on the same side about <span class="math-container">$n_g$</span>. Again, the for loop results in a zero, since there is again a miss match (BSDF has transmission type, we sampled a transmission according to <span class="math-container">$n_s$</span> but <span class="math-container">$n_g$</span> tells us we are having a reflection event). So the result is again invalidated, in case we mistook the same side as fictional interior and exterior. I have to admit that this example feels kinda weird to me..., it's just a feeling and I can't actually put it accurately, but basically I think this is the case.</p>
</li>
</ul>
<p>To wrap up:</p>
<ul>
<li>Specular reflection &amp; transmission: stick with <span class="math-container">$n_s$</span> will be okay.</li>
<li>Non-specular: check if <span class="math-container">$n_g$</span> and <span class="math-container">$n_s$</span> agree with each other on which event should happen, and if there is a conflict, the spectrum should be made zero.</li>
</ul>
<p>These are just my understanding, I don't know if these are correct or not. Maybe I'll just implement these in my own renderer for testing. Notify me if anything feels wrong.</p>

CC BY-SA 4.0
2023-06-22T17:59:36.067Z
2023-06-22T17:59:36.067Z
Enigmatisms
<p>I got confused when implementing my own renderer. I read this from the PBR-book:</p>
<blockquote>
<p>Fortunately, there is an elegant solution to these problems. When evaluating the BSDF, we can use the geometric normal to decide if we should be evaluating reflection or transmission: if <span class="math-container">$w_i$</span> and <span class="math-container">$w_o$</span> lie in the same hemisphere with respect to <span class="math-container">$n_g$</span>, we evaluate the BRDFs, and otherwise we evaluate the BTDFs. In evaluating the scattering equation, however, the dot product of the normal and the incident direction is still taken with the shading normal rather than the geometric normal.</p>
</blockquote>
<p>But when it comes to the calculation of refraction, I just can't find myself convinced, about when to use <span class="math-container">$n_g$</span> and when to use <span class="math-container">$n_s$</span>. Here is thing, if I have an object like this:</p>
<p><a href="https://i.sstatic.net/5Qd7i.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/5Qd7i.png" alt="enter image description here" /></a></p>
<p>and when the light beam tries to enter the medium from the outside, I need to know:</p>
<ul>
<li>Whether I should have reflection or refraction (depending on sampling the Fresnel term)</li>
<li>The IOR for outside and the inside of the interface, which is confusing since normally I use the dot product of ray direction and geometric normal to determine whether the ray points inwards or outwards.However, when geometric normal tells me the ray points inwards, the shading normal tells me otherwise, so I can't actually know the IOR for both sides.</li>
</ul>
<p>I don't quite see how this corresponds to things said in the PBR-book, since if we decide to have a transmission event according to <span class="math-container">$n_g$</span>, the final calculate according to <span class="math-container">$n_s$</span> might make it look like it's having a reflection event... Any help would be much appreciated, I got totally confused here.</p>

<pbr><shading><normal-mapping><refraction>
Shading normal and geometric normal for refractive surface rendering
Enigmatisms
# Shading normal and geometric normal for refractive surface rendering

(Asked by: username_0 on 2023-06-19)

I got confused when implementing my own renderer. I read this from the PBR-book:

> Fortunately, there is an elegant solution to these problems. When evaluating the BSDF, we can use the geometric normal to decide if we should be evaluating reflection or transmission: if $w\_i$ and $w\_o$ lie in the same hemisphere with respect to $n\_g$, we evaluate the BRDFs, and otherwise we evaluate the BTDFs. In evaluating the scattering equation, however, the dot product of the normal and the incident direction is still taken with the shading normal rather than the geometric normal.

But when it comes to the calculation of refraction, I just can't find myself convinced, about when to use $n\_g$ and when to use $n\_s$. Here is thing, if I have an object like this:

[![enter image description here](https://i.sstatic.net/5Qd7i.png)](https://i.sstatic.net/5Qd7i.png)

and when the light beam tries to enter the medium from the outside, I need to know:

* Whether I should have reflection or refraction (depending on sampling the Fresnel term)
* The IOR for outside and the inside of the interface, which is confusing since normally I use the dot product of ray direction and geometric normal to determine whether the ray points inwards or outwards.However, when geometric normal tells me the ray points inwards, the shading normal tells me otherwise, so I can't actually know the IOR for both sides.

I don't quite see how this corresponds to things said in the PBR-book, since if we decide to have a transmission event according to $n\_g$, the final calculate according to $n\_s$ might make it look like it's having a reflection event... Any help would be much appreciated, I got totally confused here.

**Tags:** pbr, shading, normal-mapping, refraction


---

## 1 answers

### Answer by username_0 on 2023-06-22. Score: 0 | Accepted answer

Now it looks like I have to try to figure it out myself... since I've waited for days without an answer. So I decided to dig into the code of `pbrt-v3` and I found what I need:

`Spectrum BSDF::Sample_f`: first we convert the world direction vector into the local frame. This can be found in `line 714 of src/core/reflection.cpp`:

```cpp
Vector3f wi, wo = WorldToLocal(woWorld);

// and the WorldToLocal function just uses shading normal:
// This can be found in line 178 of src/core/reflection.h:
Vector3f WorldToLocal(const Vector3f &v) const {
    return Vector3f(Dot(v, ss), Dot(v, ts), Dot(v, ns));
}

```

And then, we start to sample the BSDF. We take the typical BSDF used in glass material `FresnelSpecular` for an example (refer to `line 487 of src/core/reflection.cpp`).

```cpp
Spectrum f = bxdf->Sample_f(wo, &wi, uRemapped, pdf, sampledType);

```

* First, sampling according to Fresnel's law, and we can see that we are using shading normal here:

  ```cpp
  Float F = FrDielectric(CosTheta(wo), etaA, etaB);

  ```

  since `wo` is converted to local frame (defined by shading normal)
* Reflection sampling has nothing to do with ROI therefore we just use shading normal to calculate reflection. This is kinda trivial but I think I need to get back to this later and explain this with the picture posted in my question since I think there are some nasty situations. So we skip the if branch and focus the else branch.
* Obviously, shading normal is used to decide the interior and exterior (ROI) for the medium:

  ```cpp
  bool entering = CosTheta(wo) > 0;
  Float etaI = entering ? etaA : etaB;
  Float etaT = entering ? etaB : etaA;

  ```

Actually, for specular reflection/transmission materials, we have no more thing to explain: `line 767`:

```cpp
if (!(bxdf->type & BSDF_SPECULAR)) {

```

skip the rest of the `f` part and even if we need to use `f`, due to being specular, all we would get is zero anyway, so why bother? Therefore for the situation shown in the figure below, if we are using simple specular reflection & transmission material, maybe we only have to stick with $n\_s$.

[![enter image description here](https://i.sstatic.net/U5c7X.png)](https://i.sstatic.net/U5c7X.png)

But what about non-specular situation? For example, what if I use `MicrofacetReflection` / `MicrofacetTransmission` (the former requires no ROI, so I use this as a simple example, the latter does, though)? I present two examples below, and the logic we should pay attention to in `pbrt-v3` is (starting from `line 766`):

```cpp
// Compute value of BSDF for sampled direction
if (!(bxdf->type & BSDF_SPECULAR)) {
    bool reflect = Dot(*wiWorld, ng) * Dot(woWorld, ng) > 0;
    f = 0.;
    for (int i = 0; i < nBxDFs; ++i)
        if (bxdfs[i]->MatchesFlags(type) &&
            ((reflect && (bxdfs[i]->type & BSDF_REFLECTION)) ||
             (!reflect && (bxdfs[i]->type & BSDF_TRANSMISSION))))
            f += bxdfs[i]->f(wo, wi);
}

```

| Reflection | Transmission (Refraction) |
| --- | --- |
| [enter image description here](https://i.sstatic.net/fTdT1.png) | [enter image description here](https://i.sstatic.net/8MIHt.png) |

* Reflection example. This is weird, it has the conflicts I mentioned in the question: $n\_g$ and $n\_s$ propose differently. However, if I understand correctly, in this situation we have two directions on the different sides of the geometric surface ($n\_g$), therefore `bool reflect` is `false` here. During the for loop `reflect && (bxdfs[i]->type & BSDF_REFLECTION` will be `false`  and `!reflect && (bxdfs[i]->type & BSDF_TRANSMISSION)` will be `false` again since `reflect = false` and `MicrofacetReflection` has type `BSDF_REFLECTION`... so `f` will actually be zero. I think it is reasonable, since we can't let the light leak beneath the geometric surface.
* Transmission example. We use `MicrofacetTransmission` and sampled a transmission event, great. However, `bool reflect`  here will be `true` since both directions are on the same side about $n\_g$. Again, the for loop results in a zero, since there is again a miss match (BSDF has transmission type, we sampled a transmission according to $n\_s$ but $n\_g$ tells us we are having a reflection event). So the result is again invalidated, in case we mistook the same side as fictional interior and exterior. I have to admit that this example feels kinda weird to me..., it's just a feeling and I can't actually put it accurately, but basically I think this is the case.

To wrap up:

* Specular reflection & transmission: stick with $n\_s$ will be okay.
* Non-specular: check if $n\_g$ and $n\_s$ agree with each other on which event should happen, and if there is a conflict, the spectrum should be made zero.

These are just my understanding, I don't know if these are correct or not. Maybe I'll just implement these in my own renderer for testing. Notify me if anything feels wrong.


---

