# Calculating transparency in a raytracer

This topic is 3102 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi there, I'm building a ray tracer and a crucial requirement is that it is able to generate transparent images. So if the rays hit nothing, it should return transparent as a color I'm having trouble calculating the final color of my transparent textures. It seems a trivial problem but I can't get it right Example. Imagine a plane that has a texture with color1: ARGB 0.5,1,1,1 (white, half transparent). The raytracer sees it is transparent, and continues tracing after which it hits nothing anymore so returns color2: ARGB 0,0,0,0 (black, transparent). My first thought: sourceAlpha = color1.A; finalcolor.Red = sourceAlpha * color1.R + (1-sourceAlpha ) * color2.A finalcolor.A = color1.A + (1-sourceAlpha ) * color2.A; Unfortunately this doesnt work correctly. The alpha is calculated fine, but it returns 0.5 as red, which is wrong (darker). Can anyone help me out here?

##### Share on other sites
if the plane is 0.5 transparent, it seems to be correct if it gets blended with the background, I mean, from the theory point of view.

if you dont want to blend with the empty background, just dont blend, really. check if there was a hit, if not, dont blend. if you rely on the color+alpha of the object that you hit, you invert the blend formular. you could get it right with just one transparent plane and the background, but as soon as you'll have several transparent planes, you'll get in trouble.

not just return a color, but also if you hit something at all, and if not, dont blend.

sorry if that might sound like a stupid advice ;)

##### Share on other sites

thanks for the help,

1. Yea, that extra plane behind the first one is exactly my problem. I have lots of half transparent textures and objects. that overlap.

2. secondly, no that method does not return the correct result. It averages the colors, so with the provided example you get ARGB 0.5, 0.5,0.5,05 which is half transparent grey instead of half transparent white.

I put the whole thing in an analytical equation that seems to be working and is almost finished, I'll post it here tomorrow. Its more complicated than one would think. It appears the alpha calculation is not correct yet.

##### Share on other sites
Quote:
 Original post by garma83Example. Imagine a plane that has a texture with color1: ARGB 0.5,1,1,1 (white, half transparent). The raytracer sees it is transparent, and continues tracing after which it hits nothing anymore so returns color2: ARGB 0,0,0,0 (black, transparent). Unfortunately this doesnt work correctly. The alpha is calculated fine, but it returns 0.5 as red, which is wrong (darker). Can anyone help me out here?

I have a question: what would be in your example the correct result? Because if I didn't misunderstand your problem, a 0.5 red seems correct to me:
The 'color' of a surface is the light it reflects towards your eyes (diffuse component). If your surface is half transparent, then only half of the incident light will be reflected, and this means that only half of the light reaches your eyes. And this results in a darker color, unless there is somthing bright behind the plane.
Am I missing something?

##### Share on other sites
hi there,

yes those were my first thoughts as well. But note that there is _nothing_ behind the plane. Not a color, just transparency. The result of the blending operation of the color of the plane with the background color (which is nothing, full transparency) should be the original color.

What you are saying is correct if the background color would have no transparency channel. In that case, the background color is black, and red mixed with black indeed gives 0.5. But note that the background color has '0' in the alpha channel! The darker red (0.5) instead of full red is therefore wrong. It should stay full red but half transparent. What you are referring to as 'darker' is only valid when whatever is behind it is darker. If the background would be white, the red would be lighter (washed)

Let me put it differently. If you would have a glass window, fully transparent, and you have a sheet of white paper behind it, the color one sees would be white, right? Now if you hold a sheet of red paper behind it, you would see red. Now imagine you would hold a sheet of _transparent_ paper behind it. The final color would depend on whatever is behind the sheet of paper. In the real world, there is always something behind it. However, in my scenes, there is just transparency. So note how the final color would be fully _transparent_, and not a color. If you introduce a half transparent, red window, you got my problem.

This problem is solved in DirectX and OpenGL by the fact that it is impossible to render transparent render targets. You always specify a background color.

##### Share on other sites
Quote:
 Original post by garma83hi there,yes those were my first thoughts as well. But note that there is _nothing_ behind the plane. Not a color, just transparency. The result of the blending operation of the color of the plane with the background color (which is nothing, full transparency) should be the original color.What you are saying is correct if the background color would have no transparency channel. In that case, the background color is black, and red mixed with black indeed gives 0.5. But note that the background color has '0' in the alpha channel! The darker red (0.5) instead of full red is therefore wrong. It should stay full red but half transparent. What you are referring to as 'darker' is only valid when whatever is behind it is darker. If the background would be white, the red would be lighter (washed)

Yes, I understand this, but still, I don't get how you could have both full color and transparency. That is, if you need that for compositing, the first equations should still be valid.
Unless your requirements are there for other reasons, and in that case I can't help, sorry :-)

##### Share on other sites
Quote:
 What you are saying is correct if the background color would have no transparency channel. In that case, the background color is black, and red mixed with black indeed gives 0.5. But note that the background color has '0' in the alpha channel! The darker red (0.5) instead of full red is therefore wrong. It should stay full red but half transparent. What you are referring to as 'darker' is only valid when whatever is behind it is darker. If the background would be white, the red would be lighter (washed)

No, the darker red is STILL the correct result.

##### Share on other sites
no it is REALLY not. it would ONLY be correct if there is a black, nontransparent background.

Compare:
ARGB (0.5,1,0,0) + ARGB (0,0,0,0) = ARGB(0.5,0.5,0,0) -> darkened red
ARGB (0.5,1,0,0) + ARGB (0,1,1,1) = ARGB(0.5,1,0.5,0.5) -> washed out red

The fact that these results DIFFER is wrong, because the background-colors are fully transparent. How can something that is fully transparent, change the final color in ANY way? It shouldn't. If I put a fully transparent bunny in front of the plane, it wouldn't change the final color either, wouldn't it?

Try it in photoshop if you still don't believe me. Make a layer, fill it with red, put the opacity on 50%, disable the background-layer and tell me if the red got darker.

##### Share on other sites
I think that you should use the formulas wich better describe what you want the result to be. That said, I'm not really sure you're looking it from the correct perspective:
If you talk about blending modes, then yes, there are many formulas. Some of them are used also by DX/OGL, and you can find them in the manuals (this should help you finding the one you like most). But you are talking about raytracing: from a physical point of view the diffuse color indicates how the incident light is reflected. If you set a transparency value of 50%, you are just saying that half the incident light will go through (so only half of the light will bounce).
You think in terms of colors, while it is more correct to talk about reflectance function: that red is not 'red', only a function that relates incident and reflected (diffuse, in this case) light.

You said that a transparent background should not change the surface color. It seems to me that a transparent background should not change the amount of light that your surface reflects.

You can't really compare a graphic tool like photoshop to a raytracer, because in the first case there are only pixels wich must be combined with other pixes. In a raytracer it is a matter of light/surface interactions.

Then again, you must implement what you need, and I can be completely wrong :-)

##### Share on other sites
You're way over thinking this... Simply return .5, 1,1,1. Then, whatever application displays the rendered image is responsible for blending it with IT's background color, since you have no background color, it doesn't make a change at all, so you do nothing with the 0,0,0,0. You aren't taking into account DestAlpha, you are only calculating using SrcAlpha. I will try to work out a formula for this today if you'd like. I came up with a few samples and what the results *should* be, just need to find an equation to fit:

(r,g,b,a)
1,1,1,0.5
0,0,0,0
Result = 1,1,1,0.5

1,1,1,.5
0,0,0,.5
Result = .5,.5,.5,0.75

1,1,1,.75
0,0,0,.25
Result = .75, .75, .75, 0.8125

1,0,0,.5
0,1,1,.5
Result = .5,.5,.5,0.75

I will try to get back later (after work) and try to get you a formula if you can't figure something out.

##### Share on other sites
thanks guys, appreciate the help

cignox1: you're right, but the photoshop example is only to illustrate the transparency algorithm, and regardless of rays or pixels, the blending stays the same process

Ready4Dis: The same problem applies to the situation where there are halftransparent objects sitting in front of each other. So applying the blend operation based on whether something has been hit is not good enough.

Indeed I think the destAlpha is the way to go. I made a table like you did for a whole bunch of values and I extracted a formula from that that seemed to work. Unfortunately I dont have it here right now, but Ill try to reproduce it here:

Color.R = Color1.R * (1- sourceAlpha) * (1-destAlpha) + Color2.R * (..)
can't remember the (..) part. Maybe 'destAlpha * (1-sourceAlpha)'

##### Share on other sites
Ok, I think I have it:

DstCol.a = sqrt(Col1.a*Col1.a+Col2.a*Col2.a);
DstCol.r = (Col1.r*Col1.a + Col2.r*Col2.a)/DstCol.a;
DstCol.g = (Col1.g*Col1.a + Col2.g*Col2.a)/DstCol.a;
DstCol.b = (Col1.b*Col1.a + Col2.b*Col2.a)/DstCol.a;

There you are ;). You can apply as many times as you find objects in it's path. Should work as you expect... let me know.

*** Edit ***
See next response, it has my corrections, this is incorrect!!!!

[Edited by - Ready4Dis on June 10, 2009 3:39:50 PM]

##### Share on other sites
After thinking about it some more, my formula is incorrect... this one works much better

Alpha2 = (1-Col1.a)*Col2.a; //How much do we have left?
DstCol.a = Col1.a + Alpha2;
DstCol.r = (Col1.r*Col1.a + Col2.r*Alpha2)/DstCol.a;
DstCol.g = (Col1.g*Col1.a + Col2.g*Alpha2)/DstCol.a;
DstCol.b = (Col1.b*Col1.a + Col2.b*Alpha2)/DstCol.a;

This works how you think it should (or at least how I think it should).
First it calculates the percentage each color value effects the final color, then calculates the final alpha value. So, if there is already .5 (1/2 opacity), there is only .5 translucency left that it could possibly effect. It works for all values, where the first one would end up with values > 1 in certain cases. This formula insures no values > 1 (unless values >1 are inputed), and will tend towards full opacity until a value of either 1 is input (in either col1 or col2). Hope this helps, let me know how the results look, I haven't done any testing with it except in my head :).

*** Edit ***
Made it more correct... again :P.

[Edited by - Ready4Dis on June 12, 2009 7:13:53 AM]

##### Share on other sites
Hey there,

This is what I got, it looks the same as what you have

// intermediates in range 0-1
float first = sourceAlpha + sourceAlphaInv * destAlphaInv;
float second = sourceAlphaInv * destAlpha;

// colors, colors are in 255 range
int r = (int)(first * color1.R + second * color2.R);
int g = (int)(first * color1.G + second * color2.G);
int b = (int)(first * color1.B + second * color2.B);

// alpha.
int a = (int)((sourceAlpha + (sourceAlphaInv * destAlpha)) * 255);

I have the same calculation for alpha, but for some reason the alpha is not correct. The colors are perfect now. I havent figured out yet what the problem is

Thanks for the help so far!

##### Share on other sites
My DstCol.a value is correct in my formula. Our formula's are similar but not exact (mine assumes all values are 0->1 floating point). Try out mine, it gives correct color and alpha results.

For example, if you use 0,0,0,.5 and 1,1,1,.5, they would combine as such:
.25,.25,.25,.75
If you passed 1,1,1,.5 THEN 0,0,0,.5, it would combine as such:
.75,.75,.75,.75. So, the order of transparency makes a big difference (which is correct). The returned alpha value (in DstCol) is correct, if you have something that is 50% translucent, and another behind it, then 50% of what was remaining is considered (so, 50% of 50% = 25%).

What values are you expecting for alpha that you say are incorrect? If the first color is 50%, then the second is 50% alpha, that should be 75% total...

50% for the first, then 50% of the leftover 50% (25%) for the second, which totals 75% opacity, (or 25% translucency leftover). After looking and playing with a few numbers, my formula should work correctly in all cases as expected, I will play around with it some more later (make a spreadsheet up with both formula's and compare results and see which one is 'more' correct). My formula also only uses a single intermediate variable (as long as they start as float's)

##### Share on other sites
I'm gonna focus on the alpha here because both our formulas appear to return correct colors, I checked most circumstances.

The alpha should indeed also be correct, so I'm suspecting it must be a bug somewhere else in my code. I'm hunting it down as we speak, I'll keep you posted.

##### Share on other sites
Geez, it turned out that my formula was incorrect after all :( So I implemented yours and after an hour of bughunting for a typo, I got it and it works flawless! Thanks so much!

##### Share on other sites
so, just in case you would like a similar problem to crack your head on :) You know, for those lonely evenings.. I have similar trouble calculating anti-alias values.

As you know, aa is usually calculated by averaging multiple rays that have different angles. Now imaging you have 4 rays, 3 of them fully transparent (0,0,0,0) and one of them white (1,1,1,1), how would you go about calculating the final values here?

##### Share on other sites
In case your still working on this problem, the compositing scheme you're looking for is called "source over" using pre-multiplied alpha color components. You can google for "Porter-Duff compositing" if you want to know more.

##### Share on other sites
Meh, it's much more fun coming up with solutions than just searching online for it ;). I see what you're saying about anti-aliasing... they need to be equal parts.

I will see what I can come up with, lol. I am thinking something like .25,.25,.25,.25 should be the solution to your problem (3x 0,0,0,0, and 1x 1,1,1,1). So, I will start thinking about it some today and see what I can come up with. Glad my formula works perfectly for you, send me some screen shots when you get it working good :). I will see what I can come up with for your anti-aliasing problem, they all need to be treated equally, but their color can only modify based on their alpha.

##### Share on other sites
hi there!

I'll post the screens when I'm back at work

I gave the aa-problem some thought already, and I'm thinking it'll be much easier than the previous one.

imagine:
Color1 ARGB 0.2,1,0,0
Color2 ARGB 0.4,0,1,0
Color3 ARGB 0.0,1,1,1
Color4 ARGB 0.0,1,1,1

If im not mistaken, the result would be:
ARGB 0.15, 0.3, 0.6,0

now I didn't check with too many results, but this algorithm would do the trick:

float totalalpha = Color1.A + Color2.A + Color3.A + Color4.A;
Result.A = totalalpha / numberofcolors; // numberofcolors = 4 of course in this case

float factor1 = Color1.A / totalalpha; // 1/3
float factor2 = Color2.A / totalalpha; // 2/3
float factor3 = Color3.A / totalalpha; // 0
float factor4 = Color4.A / totalalpha; // 0

// same for G/B, you get the point
Result.R = Color1.R * factor1 + Color2.R * factor2 + Color3.R * factor3 + Color4.R * factor4;

I'll implement it tomorrow.

##### Share on other sites

Are you guys complicating things? Transparency first of all has different models and the most used one is very simple. Just like in OpenGL you can use updatedColor=bufferColor*(1-alpha)+fragmentColor*alpha bufferColor=updatedColor alpha is going from 0 to 1 and is the transparency factor of the fragment being added on top of the previous color which is called bufferColor. When alpha is 0 then the fragment is fully transparent and does not add itself to the updatedColor. It does require that all fragments are sorted back-to-front so that the most far away fragment is processed first and the closest one last. To use additive transparency put alpha to 0.5. Hope it's more clear now,

Edited by spinningcube
Restored post contents from history.

##### Share on other sites
Pablo,

Thanks for the effort of writing your answer but it appears you didn't really read my posts very well. It has been explained multiple times in this thread what the problem is with your approach. Please read my fourth reply especially. It contains an example.

Regards,

##### Share on other sites

.

Edited by spinningcube

##### Share on other sites
Quote:
 This is my interpretation and not an attack on you.

No problem. No offense pablo, but all of this has been explained before. That was my point.

Quote:
 You always have to start with some background color such as blue, white, grey, black or what have you.

That is exactly the problem. You say you always have to start with some background color. In my case, the background color can be fully or partially transparent. That is NOT the same as NO background. That completely changes the issue.

##### Share on other sites

This topic is 3102 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628688
• Total Posts
2984243

• 16
• 13
• 13
• 10
• 10