<p>I'm recently implementing Phong materials in my path tracer. My implementation of a randomly sampled Phong material works and looks fine. But it doesn't work well if the shininess(aka power) is too high. So I tried to implement an importance sampled Phong material. It also looks fine. <br>
However, when I compared the random sampled Phong material to the importance sampled one. Both material looks the same if the shininess is high, but the importance sampled one looks darker if the shininess is low. Why? Where am I doing wrong?</p>

<p>Picture 1. Random sampled Phong surface(left)/importance sampled Phong surface(right), both shininess ware set to 0 (3500 SPP, naive path tracing). The importance sampled  surface is too dark.<br>
<a href="https://i.sstatic.net/fqgRS.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/fqgRS.png" alt="Random sampled vs Importance sampled, power = 0"></a></p>

<p>Picture 2. andom sampled Phong surface(left)/importance sampled Phong surface(right), both shininess ware set to 23 (6700 SPP, naive path tracing). Both surface looks identical.<br>
<a href="https://i.sstatic.net/KW0ng.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/KW0ng.png" alt="Random sampled vs Importance sampled, power = 23"></a></p>

<p>The code.<br>
1.The code that samples the Phong BRDF randomly</p>

<pre><code>void PhongMaterial::createSample(const Ray&amp; ray, float4 normal, float4 intersection, Ray&amp; sampleRay, float4&amp; coefficient)
{
    sampleRay = createRandomReflect(normal,intersection);
    float4 reflectDirection = reflect(ray.direction,normal);
    coefficient = reflectColor * pow(dot(sampleRay.direction,reflectDirection),power+1)
        *(float)((power+1));
}
</code></pre>

<p>2.The code that importance samples the Phong BRDF</p>

<pre><code>void PhongMaterial::createImportanceSample(const Ray&amp; ray, float4 normal, float4 intersection, Ray&amp; sampleRay, float4&amp; coefficient)
{
    float4 reflectDirection = reflect(ray.direction,normal);
    float2 rnd(rand1(),rand1());
    float phi = 2.0f*M_PI*rnd.x;
    float theta = acos(pow(rnd.y,1.0f/(power+1.f)));
    float4 rotX,rotY;
    ons(reflectDirection,rotX,rotY);
    float4 result = cos(phi)*sin(theta)*rotX
        + sin(phi)*sin(theta)*rotY
        + cos(theta)*reflectDirection;
    sampleRay.origin = intersection + result*0.00001f;
    sampleRay.direction = result;

    coefficient = reflectColor*(float)((power+1)/(power+2));
}
</code></pre>

<p>Where <code>ray</code> is the incoming ray, <code>normal</code> is the surface normal, <code>intersection</code> is where the ray and the surface intersects, <code>coefficient</code> is light transmit coefficient and <code>power</code> is the shininess.<br>
Also, <code>ons</code> creates two vector perpendicular to the rest from a single vector(the first parameter).</p>

<p>Where am I doing wrong?</p>

CC BY-SA 3.0
2016-05-22T13:05:05.427Z
What do you mean by "random sampling"? Does it mean "uniform random sampling" or "cosine-weighted random sampling"? What does `createRandomReflect` do? How do you compute the PDF of the generated sample? How does your Monte Carlo estimator look like?
ivokabel
CC BY-SA 3.0
2016-05-22T13:20:25.433Z
Ohh... Sorry,
"random sampling" means "uniform random sampling". createRandomReflect creates sample on a hemisphere for uniform random sampling. As for the PDF, I did some integral(on paper) and google/wolfram|alpha search. I'm using a basic Monte Carlo estimator which could be found in any basic Path Tracer(eg. smallpt/smallpaint). It's a loop that throws samples at the scene and averages whatever I get back from the samples.
Mary Chang
CC BY-SA 3.0
2016-05-22T15:49:06.213Z
Of course, but in a naive path tracer you usually use a simple Monte Carlo estimation, which consists of picking the direction, evaluating the BRDF for that direction and dividing the result by probability density of generating the direction. You showed us the sampling and evaluation of the BRDF, but we cannot see how you compute the PDF and how you use the generated sample later on. We need more code to understand your implementation.
ivokabel
CC BY-SA 3.0
2016-09-27T13:32:36.983Z
Are you implementing the original Phong IS or modified (cos weighted) Phong IS? The multiplier (pow+1)/(pow+2) indicates the latter but you seem to be missing the cos weighting and it should be (pow+2)/(pow+1). Didn't check if the rotation of the IS to reflection vector is correct but I assume so.
JarkkoL
<brdf><pathtracing><sampling><importance-sampling>
How to properly implement Phong material and importance sample it in a path tracer?
Mary Chang
# How to properly implement Phong material and importance sample it in a path tracer?

(Asked by: username_0 on 2016-05-21)

I'm recently implementing Phong materials in my path tracer. My implementation of a randomly sampled Phong material works and looks fine. But it doesn't work well if the shininess(aka power) is too high. So I tried to implement an importance sampled Phong material. It also looks fine.   
However, when I compared the random sampled Phong material to the importance sampled one. Both material looks the same if the shininess is high, but the importance sampled one looks darker if the shininess is low. Why? Where am I doing wrong?

Picture 1. Random sampled Phong surface(left)/importance sampled Phong surface(right), both shininess ware set to 0 (3500 SPP, naive path tracing). The importance sampled surface is too dark.  
[![Random sampled vs Importance sampled, power = 0](https://i.sstatic.net/fqgRS.png)](https://i.sstatic.net/fqgRS.png)

Picture 2. andom sampled Phong surface(left)/importance sampled Phong surface(right), both shininess ware set to 23 (6700 SPP, naive path tracing). Both surface looks identical.  
[![Random sampled vs Importance sampled, power = 23](https://i.sstatic.net/KW0ng.png)](https://i.sstatic.net/KW0ng.png)

The code.  
1.The code that samples the Phong BRDF randomly

```
void PhongMaterial::createSample(const Ray& ray, float4 normal, float4 intersection, Ray& sampleRay, float4& coefficient)
{
    sampleRay = createRandomReflect(normal,intersection);
    float4 reflectDirection = reflect(ray.direction,normal);
    coefficient = reflectColor * pow(dot(sampleRay.direction,reflectDirection),power+1)
        *(float)((power+1));
}

```

2.The code that importance samples the Phong BRDF

```
void PhongMaterial::createImportanceSample(const Ray& ray, float4 normal, float4 intersection, Ray& sampleRay, float4& coefficient)
{
    float4 reflectDirection = reflect(ray.direction,normal);
    float2 rnd(rand1(),rand1());
    float phi = 2.0f*M_PI*rnd.x;
    float theta = acos(pow(rnd.y,1.0f/(power+1.f)));
    float4 rotX,rotY;
    ons(reflectDirection,rotX,rotY);
    float4 result = cos(phi)*sin(theta)*rotX
        + sin(phi)*sin(theta)*rotY
        + cos(theta)*reflectDirection;
    sampleRay.origin = intersection + result*0.00001f;
    sampleRay.direction = result;

    coefficient = reflectColor*(float)((power+1)/(power+2));
}

```

Where `ray` is the incoming ray, `normal` is the surface normal, `intersection` is where the ray and the surface intersects, `coefficient` is light transmit coefficient and `power` is the shininess.  
Also, `ons` creates two vector perpendicular to the rest from a single vector(the first parameter).

Where am I doing wrong?

**Tags:** brdf, pathtracing, sampling, importance-sampling

> **Comments:**
> **username_1**: What do you mean by "random sampling"? Does it mean "uniform random sampling" or "cosine-weighted random sampling"? What does `createRandomReflect` do? How do you compute the PDF of the generated sample? How does your Monte Carlo estimator look like?  
> **username_0**: Ohh... Sorry, "random sampling" means "uniform random sampling". createRandomReflect creates sample on a hemisphere for uniform random sampling. As for the PDF, I did some integral(on paper) and google/wolfram|alpha search. I'm using a basic Monte Carlo estimator which could be found in any basic Path Tracer(eg. smallpt/smallpaint). It's a loop that throws samples at the scene and averages whatever I get back from the samples.  
> **username_1**: Of course, but in a naive path tracer you usually use a simple Monte Carlo estimation, which consists of picking the direction, evaluating the BRDF for that direction and dividing the result by probability density of generating the direction. You showed us the sampling and evaluation of the BRDF, but we cannot see how you compute the PDF and how you use the generated sample later on. We need more code to understand your implementation.  
> **username_2**: Are you implementing the original Phong IS or modified (cos weighted) Phong IS? The multiplier (pow+1)/(pow+2) indicates the latter but you seem to be missing the cos weighting and it should be (pow+2)/(pow+1). Didn't check if the rotation of the IS to reflection vector is correct but I assume so.  

---

## 0 answers

