<p>I think what you're describing is what happens when you mix colours in gamma space. You should be using linear colour space when mixing.</p>

<p>When reading colours of lights and materials, convert them from gamma->linear using the inverse gamma function.</p>

<pre><code>colourLin = pow(colourLight, 2.2);
... do lambert calculation here and accumulate in 'colour' variable ..

// On output (linear-&gt;gamma conversion)
gl_FragColor = pow(colour, 1.0/2.2);
</code></pre>

<p>There is an in depth chapter in GPU Gems on this topic: <a href="https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch24.html" rel="nofollow noreferrer">The Importance of Being Linear</a>.</p>

CC BY-SA 4.0
2019-11-25T09:42:57.417Z
A couple of *very minor* points: 1) The monitor/display system is probably using sRGB which is a slightly different curve to the pow(colour, gamma_val)   (BUT you might be able to avoid that with https://stackoverflow.com/q/56204242/626644 )   2) if you are concerned about the performance, you *might* be able to get away with the approximation of using "2" for the gamma power. You can then use squaring and square roots which are relatively cheap.
Simon F
CC BY-SA 3.0
2016-08-22T04:27:47.463Z
Done it. It looks like the problem is really with hamma. But I still has a question. There are to results of my ray tracing. In the first one I
Valerii Boldakov
CC BY-SA 3.0
2016-08-22T04:40:27.343Z
Sorry for kind of spamming. Didn't use to sending commentary with Enter button. There is continuation. In the left one I make at first gamma to linear conversion and then multiplying it on the max(0, scalar multiplication of light and normal) and after that summing results in pixel. And convert results into gamma space again. At the right one I convert colour into linear space only after multiplying it on on the max(0, scalar multiplication of light and normal). I don't quite get the reason why  should I multiply in the gamma space. ![img](http://i.imgur.com/7WRCFb0.png)
Valerii Boldakov
CC BY-SA 3.0
2016-08-22T04:50:26.380Z
The most important thing is the output is converted to gamma (meaning your light accumulation is linear). Because your inputs are constant colours it doesn't affect shading as much as the output conversion.
PaulHK
CC BY-SA 3.0
2016-08-22T04:51:01.107Z
I'd be interested to see your new image output if you can post an update
PaulHK
CC BY-SA 3.0
2016-08-22T04:54:22.533Z
Don't forget, you picked the orange/green colours by eye (presumably) using a gamma-space monitor, so likely these are the correct/intended colours.
PaulHK
CC BY-SA 3.0
2016-08-22T05:05:02.593Z
I understand it. Don't have any complaints about color. Just about dark parts at the left part of the second image in the updated post. The right one is good without them. But why should I make transformation to the linear space after multiplication.
Valerii Boldakov
CC BY-SA 3.0
2016-08-22T07:43:07.343Z
I think the left image is the correct one looking at the function, although you may need to adjust your light colours to something a bit darker.
PaulHK
CC BY-SA 3.0
2016-08-23T02:44:07.993Z
Could your colors be overflowing?
Alan Wolfe
CC BY-SA 3.0
2016-08-23T05:18:39.280Z
Nope, I clamp to 1.0f every component when it becomes more than 1.0f. I thought about this case that's why I specially check it in my tests.
Valerii Boldakov
CC BY-SA 4.0
2016-08-22T02:30:35.690Z
2019-11-25T12:11:19.520Z
2019-11-25T12:11:19.520Z
PaulHK
<p>I'm writing my implementation of ray tracer and I've got result which you can see at this image.</p>

<p><img src="https://i.sstatic.net/tFCEf.png" alt="img">
So you can see that some dark parts are overlapping. And I have them even in the part where it should be bright in my opinion. There are two lights one from above and the second one from right-bottom corner.</p>

<p>And I have two questions. Is it right? I really tried to google some references but failed. I'm not still sure that results in my imagination are correct ones.</p>

<p>And the second one where does it come from?</p>

<p>I use usual lambertian shading model and have something like this in my code:</p>

<pre><code>pixel = (0, 0, 0)
normal = get normal of sphere in point of intersection;
normal = unit vector of normal;
for each light from light_set {
    light = unit vector of light;
    pixel = pixel + diffuseColor * max(0, scalar multiplication of light and normal);
}
</code></pre>

<p>I'm really struggling to find out the reason where this strange thing is coming from. But couldn't find it.</p>

<p>UPD0: added some of my real code. The part which is described here is next:</p>

<pre><code>Material mat = surfaces[closestSurfase]-&gt;getMaterial();
Color pixel = Color(0, 0, 0);
Vector3f normal = surfaces[closestSurfase]-&gt;getNormal(point).getUnitVector();
Vector3f view = -ray.getDirection().getUnitVector();
for (size_t i = 0; i &lt; lights.size(); ++i) {
    Vector3f normal_light = lights[i]-&gt;getDirection().getUnitVector();
    Vector3f specVect = (normal_light + view).getUnitVector();
    float intenst = lights[i]-&gt;getIntensity();
    pixel = pixel + mat.getDiffuse() * std::max(0.0f, normal_light.dotMultiply(normal)) * intenst;
}
result-&gt;setPixel(x, y, pixel);
</code></pre>

<p>Or you can find more here: <a href="https://github.com/minebv/ray_tracer/tree/lambertian_shading_fix/src" rel="nofollow noreferrer">https://github.com/minebv/ray_tracer/tree/lambertian_shading_fix/src</a></p>

<p>Described above is from Camera's method getRender.</p>

<p>UPD1: It looks like the problem in the mixing colors in gamma space instead linear one, as answered @PaulHK .</p>

<p>But I've still some kind of problem. Here you can see 2 renders.
<img src="https://i.sstatic.net/HNclL.png" alt="img1">
At the left one I make all work after transformation all colors into linear space, I mean it's like this in my code:</p>

<pre><code>pixel = pixel + mat.getDiffuse().toLinear() * std::max(0.0f, normal_light.dotMultiply(normal)) * intenst;
</code></pre>

<p>At the right one I make transformation after multiplying:</p>

<pre><code>pixel = pixel + (mat.getDiffuse() * std::max(0.0f, normal_light.dotMultiply(normal)) * intenst).toLinear();
</code></pre>

<p>And I didn't forget to make pixel into gamma space again after all job.</p>

<p>As for me, the right one is more correct. But then there is a question: why should I make multiplication on angle into gamma space? Or is it my incorrect way to think about what is right?</p>

<p>UPD3:it looks like the whole thing really was about mixing colors in gamma space instead of linear one. Here is result with ambient light.
<img src="https://i.sstatic.net/DOjAR.png" alt="img3"></p>

<p>Also about overlapping lights, it was my incorrect way of thinking. I finally found reference material where they have the same thing. Found it at blender <a href="https://docs.blender.org/manual/en/2.79/render/blender_render/materials/properties/specular_shaders.html#cook-torrance" rel="nofollow noreferrer">docs</a>.</p>

<p>Thank everyone for answers.</p>

CC BY-SA 3.0
2016-08-22T01:59:10.037Z
It does look weird. Could you put some of your actual code for us to see? Pseudo code shows your intent, which looks decent, but the problem might be in some subtle issue of the math or a typo (:
Alan Wolfe
CC BY-SA 3.0
2016-08-22T04:25:42.377Z
@AlanWolfe updated my question, added some real code.
Valerii Boldakov
<raytracing>
Ray tracing scene with 2 lights and sphere
Valerii Boldakov
# Ray tracing scene with 2 lights and sphere

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

I'm writing my implementation of ray tracer and I've got result which you can see at this image.

![img](https://i.sstatic.net/tFCEf.png)
So you can see that some dark parts are overlapping. And I have them even in the part where it should be bright in my opinion. There are two lights one from above and the second one from right-bottom corner.

And I have two questions. Is it right? I really tried to google some references but failed. I'm not still sure that results in my imagination are correct ones.

And the second one where does it come from?

I use usual lambertian shading model and have something like this in my code:

```
pixel = (0, 0, 0)
normal = get normal of sphere in point of intersection;
normal = unit vector of normal;
for each light from light_set {
    light = unit vector of light;
    pixel = pixel + diffuseColor * max(0, scalar multiplication of light and normal);
}

```

I'm really struggling to find out the reason where this strange thing is coming from. But couldn't find it.

UPD0: added some of my real code. The part which is described here is next:

```
Material mat = surfaces[closestSurfase]->getMaterial();
Color pixel = Color(0, 0, 0);
Vector3f normal = surfaces[closestSurfase]->getNormal(point).getUnitVector();
Vector3f view = -ray.getDirection().getUnitVector();
for (size_t i = 0; i < lights.size(); ++i) {
    Vector3f normal_light = lights[i]->getDirection().getUnitVector();
    Vector3f specVect = (normal_light + view).getUnitVector();
    float intenst = lights[i]->getIntensity();
    pixel = pixel + mat.getDiffuse() * std::max(0.0f, normal_light.dotMultiply(normal)) * intenst;
}
result->setPixel(x, y, pixel);

```

Or you can find more here: <https://github.com/minebv/ray_tracer/tree/lambertian_shading_fix/src>

Described above is from Camera's method getRender.

UPD1: It looks like the problem in the mixing colors in gamma space instead linear one, as answered @username_2 .

But I've still some kind of problem. Here you can see 2 renders.
![img1](https://i.sstatic.net/HNclL.png)
At the left one I make all work after transformation all colors into linear space, I mean it's like this in my code:

```
pixel = pixel + mat.getDiffuse().toLinear() * std::max(0.0f, normal_light.dotMultiply(normal)) * intenst;

```

At the right one I make transformation after multiplying:

```
pixel = pixel + (mat.getDiffuse() * std::max(0.0f, normal_light.dotMultiply(normal)) * intenst).toLinear();

```

And I didn't forget to make pixel into gamma space again after all job.

As for me, the right one is more correct. But then there is a question: why should I make multiplication on angle into gamma space? Or is it my incorrect way to think about what is right?

UPD3:it looks like the whole thing really was about mixing colors in gamma space instead of linear one. Here is result with ambient light.
![img3](https://i.sstatic.net/DOjAR.png)

Also about overlapping lights, it was my incorrect way of thinking. I finally found reference material where they have the same thing. Found it at blender [docs](https://docs.blender.org/manual/en/2.79/render/blender_render/materials/properties/specular_shaders.html#cook-torrance).

Thank everyone for answers.

**Tags:** raytracing

> **Comments:**
> **username_1**: It does look weird. Could you put some of your actual code for us to see? Pseudo code shows your intent, which looks decent, but the problem might be in some subtle issue of the math or a typo (:  
> **username_0**: @username_1 updated my question, added some real code.  

---

## 1 answers

### Answer by username_2 on 2016-08-22. Score: 6 | Accepted answer

I think what you're describing is what happens when you mix colours in gamma space. You should be using linear colour space when mixing.

When reading colours of lights and materials, convert them from gamma->linear using the inverse gamma function.

```
colourLin = pow(colourLight, 2.2);
... do lambert calculation here and accumulate in 'colour' variable ..

// On output (linear->gamma conversion)
gl_FragColor = pow(colour, 1.0/2.2);

```

There is an in depth chapter in GPU Gems on this topic: [The Importance of Being Linear](https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch24.html).

> **Comments:**
> **username_0**: Done it. It looks like the problem is really with hamma. But I still has a question. There are to results of my ray tracing. In the first one I  
> **username_0**: Sorry for kind of spamming. Didn't use to sending commentary with Enter button. There is continuation. In the left one I make at first gamma to linear conversion and then multiplying it on the max(0, scalar multiplication of light and normal) and after that summing results in pixel. And convert results into gamma space again. At the right one I convert colour into linear space only after multiplying it on on the max(0, scalar multiplication of light and normal). I don't quite get the reason why  should I multiply in the gamma space. ![img](http://i.imgur.com/7WRCFb0.png)  
> **username_2**: The most important thing is the output is converted to gamma (meaning your light accumulation is linear). Because your inputs are constant colours it doesn't affect shading as much as the output conversion.  
> **username_2**: I'd be interested to see your new image output if you can post an update  
> **username_2**: Don't forget, you picked the orange/green colours by eye (presumably) using a gamma-space monitor, so likely these are the correct/intended colours.  
> **username_0**: I understand it. Don't have any complaints about color. Just about dark parts at the left part of the second image in the updated post. The right one is good without them. But why should I make transformation to the linear space after multiplication.  
> **username_2**: I think the left image is the correct one looking at the function, although you may need to adjust your light colours to something a bit darker.  
> **username_1**: Could your colors be overflowing?  
> **username_0**: Nope, I clamp to 1.0f every component when it becomes more than 1.0f. I thought about this case that's why I specially check it in my tests.  
> **username_3**: A couple of *very minor* points: 1) The monitor/display system is probably using sRGB which is a slightly different curve to the pow(colour, gamma_val)   (BUT you might be able to avoid that with https://stackoverflow.com/q/56204242/626644 )   2) if you are concerned about the performance, you *might* be able to get away with the approximation of using "2" for the gamma power. You can then use squaring and square roots which are relatively cheap.  

---

