<p>I was comparing images on different max bounce counts. My image was generated on 2 bounces, whereas reference image was generated on 3 bounces.</p>

CC BY-SA 4.0
2022-11-22T08:07:20.673Z
so by changing to same amount of bounces, you get the same result?
Thomas
CC BY-SA 4.0
2022-11-22T21:05:31.607Z
Yes, exactly the same.
RafalMaziejuk
CC BY-SA 4.0
2022-11-21T20:08:32.700Z
2022-11-21T20:08:32.700Z
RafalMaziejuk
<h2>Problem description</h2>
<p>When comparing Cornell Box image generated by my path tracer I noticed that colors are much more sharp/saturated (don't know specific term) than they should be. Comparison (on the left mine and on the right how it should look - shadows are darker and color bleeding looks unnatural, mainly because of clear green line on the floor next to small box):</p>
<p><a href="https://i.sstatic.net/5XW69.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/5XW69.jpg" alt="enter image description here" /></a></p>
<h2>Implementation (OpenCL/C)</h2>
<h3>Main logic</h3>
<pre><code>const float3 intersectionPoint = ray-&gt;origin + hit.tNearest * ray-&gt;direction;
        const float3 normal = interpolate3(triangle-&gt;v0.normal, triangle-&gt;v1.normal, triangle-&gt;v2.normal, hit.u, hit.v);
        
        // direct lighting
        {
            const float y = 1.985f;
            const float factor = 0.15f;
            const float3 v1 = (float3)( factor, y,  factor);
            const float3 v2 = (float3)(-factor, y, -factor);
            
            Light light;
            light.position = (float3)(factor, y, -factor);
            light.emission = (float3)(17.0f, 12.0f, 4.0f);
            light.u        = v1 - light.position;
            light.v        = v2 - light.position;
            light.area     = length(cross(light.u, light.v));
            
            float3 scatterPosition = intersectionPoint + normal * FLT_EPSILON;
            LightSample lightSample = sample_rectangle_light(&amp;light, scatterPosition, &amp;seed);

            float3 Ld = 0.0f;
            float3 Li = lightSample.emission;
            if (dot(lightSample.direction, lightSample.normal) &lt; 0.0f) {
                Ray shadowRay;
                shadowRay.origin = scatterPosition;
                shadowRay.direction = lightSample.direction;
                Hit shadowHit = intersect_ray_bvh(&amp;shadowRay, bvhNodes, triangles);

                if (shadowHit.tNearest &gt; lightSample.distance - FLT_EPSILON) {
                    ScatterSample scatterSample = evaluate_lambert(material, normal, lightSample.direction);
                    if (scatterSample.pdf &gt; 0.0f) {
                        Ld = Ld + ((Li * scatterSample.f) / lightSample.pdf);
                    }
                }
            }
        
            radiance[index] += Ld * throughput;
        }
        
        ScatterSample scatterSample;
        scatterSample = sample_lambert(material, normal, &amp;seed);
        if (scatterSample.pdf &gt; 0.0f) {
            throughput = (throughput * scatterSample.f) / scatterSample.pdf;
        }

        ray-&gt;direction = scatterSample.L;
        ray-&gt;origin = intersectionPoint + ray-&gt;direction * FLT_EPSILON;
</code></pre>
<h3>Rectangle light sampling</h3>
<pre><code>LightSample sample_rectangle_light(const Light *light, const float3 scatterPosition, uint2 *seed) {
    const uint lightsCount = 1u;
    const float2 random = get_random_numbers(seed);
    const float3 lightSurfacePosition = light-&gt;position + light-&gt;u * random.x + light-&gt;v * random.y;

    LightSample result;
    result.normal = normalize(cross(light-&gt;u, light-&gt;v));
    result.emission = light-&gt;emission * (float)lightsCount;
    result.direction = lightSurfacePosition - scatterPosition;
    result.distance = length(result.direction);
    result.direction /= result.distance;

    float distanceSquared = result.distance * result.distance;
    result.pdf = distanceSquared / (light-&gt;area * fabs(dot(result.normal, result.direction)));

    return result;
}
</code></pre>
<h3>Lambert BSDF</h3>
<pre><code>ScatterSample evaluate_lambert(const Material *material, const float3 normal, const float3 L) {
    ScatterSample result;
    result.pdf = dot(normal, L) * M_1_PI_F;
    result.f = M_1_PI_F * material-&gt;diffuse * dot(normal, L);

    return result;
}

ScatterSample sample_lambert(const Material *material, const float3 normal, uint2 *seed) {
    const float2 random = get_random_numbers(seed);

    ScatterSample result;
    result.L = sample_hemisphere(normal, random);
    result.f = M_1_PI_F * material-&gt;diffuse * dot(normal, result.L);
    result.pdf = dot(normal, result.L) * M_1_PI_F;
    
    return result;
}
</code></pre>
<h3>Gamma correction</h3>
<pre><code>float3 color = (radiance[index] + read_imagef(imagePlane, (int2)(x, y)).xyz) / (float)sampleCount;
color = pow(color, (float3)(1.0f / 2.2f));
</code></pre>
<h2>Culprit</h2>
<p>It might be the hardcoded rectangle light that causes this effect but I'm not sure because manipulating its position/area didn't change much.</p>
<h2>Edit</h2>
<p>Turns out my model had wrong normal vectors on certain meshes. That's why certain areas appeared darker.</p>
<p><a href="https://i.sstatic.net/DYhrr.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/DYhrr.jpg" alt="enter image description here" /></a></p>
<p>I still have no idea why for example box's corners are much darker than in reference image.</p>

CC BY-SA 4.0
2022-11-21T12:22:14.970Z
Haven't had a deeper look into your code, but did you apply [gamma correction](https://learnopengl.com/Advanced-Lighting/Gamma-Correction)?
wychmaster
CC BY-SA 4.0
2022-11-21T12:22:38.547Z
Yes, sorry forgot to mention that. Edited my question.
RafalMaziejuk
CC BY-SA 4.0
2022-11-21T18:09:15.993Z
It honestly looks like just a difference in brightness/contrast, which could be explained by a difference in exposure, tone mapping, gamma correction, etc. between the two images. Where is your reference image coming from? If you have access to that renderer, is it possible to extract the linear pixel values (prior to any display processing) and compare them to those of your renderer?
Nathan Reed
CC BY-SA 4.0
2022-11-21T18:22:56.990Z
I don't think it's a difference in exposure. Compare for example short box top and look at the floor. In my image top is darker and floor is lighter whereas on the reference image top is lighter and floor is darker. It's knightcrawler25's GLSL PathTracer.
RafalMaziejuk
CC BY-SA 4.0
2022-11-21T18:24:51.890Z
Also look at the shadows, mine are much more sharp and not as soft. It looks unnatural imo.
RafalMaziejuk
<pathtracing><lighting><opencl>
Path tracing - colors/shadows sharper than they should be
RafalMaziejuk
# Path tracing - colors/shadows sharper than they should be

(Asked by: username_0 on 2022-11-21)

## Problem description

When comparing Cornell Box image generated by my path tracer I noticed that colors are much more sharp/saturated (don't know specific term) than they should be. Comparison (on the left mine and on the right how it should look - shadows are darker and color bleeding looks unnatural, mainly because of clear green line on the floor next to small box):

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

## Implementation (OpenCL/C)

### Main logic

```
const float3 intersectionPoint = ray->origin + hit.tNearest * ray->direction;
        const float3 normal = interpolate3(triangle->v0.normal, triangle->v1.normal, triangle->v2.normal, hit.u, hit.v);
        
        // direct lighting
        {
            const float y = 1.985f;
            const float factor = 0.15f;
            const float3 v1 = (float3)( factor, y,  factor);
            const float3 v2 = (float3)(-factor, y, -factor);
            
            Light light;
            light.position = (float3)(factor, y, -factor);
            light.emission = (float3)(17.0f, 12.0f, 4.0f);
            light.u        = v1 - light.position;
            light.v        = v2 - light.position;
            light.area     = length(cross(light.u, light.v));
            
            float3 scatterPosition = intersectionPoint + normal * FLT_EPSILON;
            LightSample lightSample = sample_rectangle_light(&light, scatterPosition, &seed);

            float3 Ld = 0.0f;
            float3 Li = lightSample.emission;
            if (dot(lightSample.direction, lightSample.normal) < 0.0f) {
                Ray shadowRay;
                shadowRay.origin = scatterPosition;
                shadowRay.direction = lightSample.direction;
                Hit shadowHit = intersect_ray_bvh(&shadowRay, bvhNodes, triangles);

                if (shadowHit.tNearest > lightSample.distance - FLT_EPSILON) {
                    ScatterSample scatterSample = evaluate_lambert(material, normal, lightSample.direction);
                    if (scatterSample.pdf > 0.0f) {
                        Ld = Ld + ((Li * scatterSample.f) / lightSample.pdf);
                    }
                }
            }
        
            radiance[index] += Ld * throughput;
        }
        
        ScatterSample scatterSample;
        scatterSample = sample_lambert(material, normal, &seed);
        if (scatterSample.pdf > 0.0f) {
            throughput = (throughput * scatterSample.f) / scatterSample.pdf;
        }

        ray->direction = scatterSample.L;
        ray->origin = intersectionPoint + ray->direction * FLT_EPSILON;

```

### Rectangle light sampling

```
LightSample sample_rectangle_light(const Light *light, const float3 scatterPosition, uint2 *seed) {
    const uint lightsCount = 1u;
    const float2 random = get_random_numbers(seed);
    const float3 lightSurfacePosition = light->position + light->u * random.x + light->v * random.y;

    LightSample result;
    result.normal = normalize(cross(light->u, light->v));
    result.emission = light->emission * (float)lightsCount;
    result.direction = lightSurfacePosition - scatterPosition;
    result.distance = length(result.direction);
    result.direction /= result.distance;

    float distanceSquared = result.distance * result.distance;
    result.pdf = distanceSquared / (light->area * fabs(dot(result.normal, result.direction)));

    return result;
}

```

### Lambert BSDF

```
ScatterSample evaluate_lambert(const Material *material, const float3 normal, const float3 L) {
    ScatterSample result;
    result.pdf = dot(normal, L) * M_1_PI_F;
    result.f = M_1_PI_F * material->diffuse * dot(normal, L);

    return result;
}

ScatterSample sample_lambert(const Material *material, const float3 normal, uint2 *seed) {
    const float2 random = get_random_numbers(seed);

    ScatterSample result;
    result.L = sample_hemisphere(normal, random);
    result.f = M_1_PI_F * material->diffuse * dot(normal, result.L);
    result.pdf = dot(normal, result.L) * M_1_PI_F;
    
    return result;
}

```

### Gamma correction

```
float3 color = (radiance[index] + read_imagef(imagePlane, (int2)(x, y)).xyz) / (float)sampleCount;
color = pow(color, (float3)(1.0f / 2.2f));

```

## Culprit

It might be the hardcoded rectangle light that causes this effect but I'm not sure because manipulating its position/area didn't change much.

## Edit

Turns out my model had wrong normal vectors on certain meshes. That's why certain areas appeared darker.

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

I still have no idea why for example box's corners are much darker than in reference image.

**Tags:** pathtracing, lighting, opencl

> **Comments:**
> **username_1**: Haven't had a deeper look into your code, but did you apply [gamma correction](https://learnopengl.com/Advanced-Lighting/Gamma-Correction)?  
> **username_0**: Yes, sorry forgot to mention that. Edited my question.  
> **username_2**: It honestly looks like just a difference in brightness/contrast, which could be explained by a difference in exposure, tone mapping, gamma correction, etc. between the two images. Where is your reference image coming from? If you have access to that renderer, is it possible to extract the linear pixel values (prior to any display processing) and compare them to those of your renderer?  
> **username_0**: I don't think it's a difference in exposure. Compare for example short box top and look at the floor. In my image top is darker and floor is lighter whereas on the reference image top is lighter and floor is darker. It's knightcrawler25's GLSL PathTracer.  
> **username_0**: Also look at the shadows, mine are much more sharp and not as soft. It looks unnatural imo.  

---

## 1 answers

### Answer by username_0 on 2022-11-21. Score: 1 | Accepted answer

I was comparing images on different max bounce counts. My image was generated on 2 bounces, whereas reference image was generated on 3 bounces.

> **Comments:**
> **username_3**: so by changing to same amount of bounces, you get the same result?  
> **username_0**: Yes, exactly the same.  

---

