<p>I found my error which was quite hard to understand but I think it is misleading on it's explanations in books. The visibility term is denoted most of the time as <em>V(x,x')</em>. However, if visibility is taken account as form one point to another, it gives a wrong answer. The correct fix for that is to get the intersection point &quot;x&quot; to direction of &quot; x' &quot;. In that case visibility is changed as <em>V(x,(x'-x).normalized())</em>. So, a direction should be used, not a point.</p>

CC BY-SA 4.0
2022-01-08T12:52:39.160Z
It doesn't give a wrong answer if you implement it correctly, your code was simply not computing visibility between two points but rather expected a point and direction. There is a difference between the ray-tracing function $r(x, \omega)$ and the visibility function $V(x,y)$. Another pitfall you may run into is floating point precision. So typically one uses instead $V(x+\epsilon N_x, y + \epsilon N_y)$ instead of $V(x,y)$ in order to avoid self-intersection. $N_x, N_y$ being the normals such that $N_x\cdot (y-x)>0$ and $N_y \cdot (x-y)>0$.
lightxbulb
CC BY-SA 4.0
2024-01-10T17:11:32.140Z
i tried to use this but it still doesn't work after your recommended fix, I have a lot of 'white' pixels - which makes me think there's division by zero somewhere
rajasaurus
CC BY-SA 4.0
2022-01-08T12:50:17.283Z
2022-01-08T12:50:17.283Z
Metalian
<p>I have a question regarding the NEE for light sources that are composed from triangles. I implemented the next event estimation (explicit light source sampling) for spherical light sources but after all of the trials, the output is always noisy and not even close to spherical luminaries.</p>
<pre><code>        else if(const Triangle* tr=dynamic_cast&lt;const Triangle*&gt;(prim))
        {
            double ran1=rand01();
            double ran2=rand01();

            double alpha=1-sqrt(ran1);
            double beta=(1-ran2)*sqrt(ran1);
            double gamma=ran2*sqrt(ran1);

            while(alpha+beta&gt;1)
            {
                ran1=rand01();
                ran2=rand01();
                alpha=1-sqrt(ran1);
                beta=(1-ran2)*sqrt(ran1);
            }

            Vec firstEdge=tr-&gt;vertex1-tr-&gt;vertex0;
            Vec secondEdge=tr-&gt;vertex2-tr-&gt;vertex0;
            Vec nPrime=secondEdge.cross(firstEdge);
            double A=nPrime.length()*1/2;

            Vec xPrime = tr-&gt;vertex0*alpha+tr-&gt;vertex1*beta+tr-&gt;vertex2*gamma;

            Vec omega=(xPrime-x).normalize();
            Vec inverseOmega=(x-xPrime).normalize();

            double cosTheta=clamp(((n-x).normalize()).dot(inverseOmega))/(n-x).length()*inverseOmega.length();
            double cosThetaPrime=abs(((nPrime-xPrime).normalize()).dot(omega))/(nPrime-xPrime).length()*omega.length();
            double magOfxPrimeToX=(xPrime-x).dot(xPrime-x);
            
            int idShadowRay=0;
            double tShadowRay;
            Vec xShadowRay;
            Vec nShadowRay;

            if(intersect(Ray(x,xPrime),idShadowRay,tShadowRay,xShadowRay,nShadowRay) &amp;&amp; idShadowRay==i)
            {
                L_direct = L_direct + (f.mult(tr-&gt;e))*(cosTheta)*(cosThetaPrime/magOfxPrimeToX) /(1/A);
            }
        
        }
</code></pre>
<p>After the loop ends for the light sources, I add direct contribution and indirect contribution in the return statement:
<code>return obj.e * emissive + L_direct + f.mult(radiance(Ray(x, d), depth, 0));</code></p>
<p>The result with 150 sample with 750x500 resolution:
<a href="https://i.sstatic.net/cabsi.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/cabsi.jpg" alt="enter image description here" /></a></p>
<p>However spherical light sources are quite succesful in same specifications (150 sample with 750x500 resolution):</p>
<p><a href="https://i.sstatic.net/xa9I6.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/xa9I6.jpg" alt="enter image description here" /></a></p>
<p>What could be wrong in this case ? Thanks a lot.</p>

<raytracing><pathtracing><lighting><importance-sampling>
Next Event Estimation for Triangle-based Light Sources
Metalian
# Next Event Estimation for Triangle-based Light Sources

(Asked by: username_0 on 2022-01-04)

I have a question regarding the NEE for light sources that are composed from triangles. I implemented the next event estimation (explicit light source sampling) for spherical light sources but after all of the trials, the output is always noisy and not even close to spherical luminaries.

```
        else if(const Triangle* tr=dynamic_cast<const Triangle*>(prim))
        {
            double ran1=rand01();
            double ran2=rand01();

            double alpha=1-sqrt(ran1);
            double beta=(1-ran2)*sqrt(ran1);
            double gamma=ran2*sqrt(ran1);

            while(alpha+beta>1)
            {
                ran1=rand01();
                ran2=rand01();
                alpha=1-sqrt(ran1);
                beta=(1-ran2)*sqrt(ran1);
            }

            Vec firstEdge=tr->vertex1-tr->vertex0;
            Vec secondEdge=tr->vertex2-tr->vertex0;
            Vec nPrime=secondEdge.cross(firstEdge);
            double A=nPrime.length()*1/2;

            Vec xPrime = tr->vertex0*alpha+tr->vertex1*beta+tr->vertex2*gamma;

            Vec omega=(xPrime-x).normalize();
            Vec inverseOmega=(x-xPrime).normalize();

            double cosTheta=clamp(((n-x).normalize()).dot(inverseOmega))/(n-x).length()*inverseOmega.length();
            double cosThetaPrime=abs(((nPrime-xPrime).normalize()).dot(omega))/(nPrime-xPrime).length()*omega.length();
            double magOfxPrimeToX=(xPrime-x).dot(xPrime-x);
            
            int idShadowRay=0;
            double tShadowRay;
            Vec xShadowRay;
            Vec nShadowRay;

            if(intersect(Ray(x,xPrime),idShadowRay,tShadowRay,xShadowRay,nShadowRay) && idShadowRay==i)
            {
                L_direct = L_direct + (f.mult(tr->e))*(cosTheta)*(cosThetaPrime/magOfxPrimeToX) /(1/A);
            }
        
        }

```

After the loop ends for the light sources, I add direct contribution and indirect contribution in the return statement:
`return obj.e * emissive + L_direct + f.mult(radiance(Ray(x, d), depth, 0));`

The result with 150 sample with 750x500 resolution:
[![enter image description here](https://i.sstatic.net/cabsi.jpg)](https://i.sstatic.net/cabsi.jpg)

However spherical light sources are quite succesful in same specifications (150 sample with 750x500 resolution):

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

What could be wrong in this case ? Thanks a lot.

**Tags:** raytracing, pathtracing, lighting, importance-sampling


---

## 1 answers

### Answer by username_0 on 2022-01-08. Score: 1 | Accepted answer

I found my error which was quite hard to understand but I think it is misleading on it's explanations in books. The visibility term is denoted most of the time as *V(x,x')*. However, if visibility is taken account as form one point to another, it gives a wrong answer. The correct fix for that is to get the intersection point "x" to direction of " x' ". In that case visibility is changed as *V(x,(x'-x).normalized())*. So, a direction should be used, not a point.

> **Comments:**
> **username_1**: It doesn't give a wrong answer if you implement it correctly, your code was simply not computing visibility between two points but rather expected a point and direction. There is a difference between the ray-tracing function $r(x, \omega)$ and the visibility function $V(x,y)$. Another pitfall you may run into is floating point precision. So typically one uses instead $V(x+\epsilon N_x, y + \epsilon N_y)$ instead of $V(x,y)$ in order to avoid self-intersection. $N_x, N_y$ being the normals such that $N_x\cdot (y-x)>0$ and $N_y \cdot (x-y)>0$.  
> **username_2**: i tried to use this but it still doesn't work after your recommended fix, I have a lot of 'white' pixels - which makes me think there's division by zero somewhere  

---

