# How to properly implement Phong material and importance sample it in a path tracer?

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.

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.

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?

• 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 May 22 '16 at 13:05
• 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 May 22 '16 at 13:20
• 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 May 22 '16 at 15:49
• 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 Sep 27 '16 at 13:32