• Advertisement

Archived

This topic is now archived and is closed to further replies.

Radiosity for Beginners: Concerning form factors

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, i´m just reading a while about Radiosity Lighting and i have decided to implement it in my indoor rendering engine for static lighting, since my current lighting method handles shadows very badly. Although im still reading and need to get more knowledge, i have a question concerning form factors: Which approach is esier to implement? Im very confused by the Hemicube algorithm (i found a link here to a very good tutorial, but i have read it several times, the more foten i read about the hemicube stuff, the more i get confused about it ) So i think about determining the form factors by raytracing. what are the performance and accurracy issues compared to the hemicube algorithm. can someone tell me which method produces better quality? and what about the matrix solution? how is it actually performed? thanks for any help! greets Gammastrahler

Share this post


Link to post
Share on other sites
Advertisement
quote:
Original post by Gammastrahler
Which approach is esier to implement?

Raytraced FFs.

quote:

So i think about determining the form factors by raytracing. what are the performance and accurracy issues compared to the hemicube algorithm. can someone tell me which method produces better quality?

Raytracing achieves better quality (more precise), but is slower (but not always, depends on how you implement it). Hemicubes are an image space method, and suffer from the usual quality tradeoffs. Raytracing is object space, but you''ll need to shoot/gather lots of rays to get good soft shadows.

quote:

and what about the matrix solution? how is it actually performed?

It''s a theoretical model, used to derive the more advanced radiosity algorithms, but is unusable in pratice. You could solve it with a Gauss Seidel relaxation or something similar, but the memory requirements for using matrix radiosity directly are totally insane.

Share this post


Link to post
Share on other sites
I have coded a very simple app in which i "tried" to implement radiosity. I render a hollow cube.

I´think i post my code here (i know that it is not correct), but maybe you could give me the right formulas.

The form factor calculation is very cheap and slow... it is currently only for testing purposes. I also don´t take into account the patch´s differential area since each of my patch has the same size.

Another problem in my form factor calculation is: i get negative form factors! I dont´know why... therefore, i use fabs to flip the sign.

The rad_calc function should then calculate the radiosity for each patch, but i have problems solving the solution... how is p calculated? and how is E updated... i just don´t get agrip on it...

i would be very happy if someone could guide me !

thanks
Gammastrahler


void calc_form_factors()
{
for (int i = 0; i < patches.size(); i++)
{
float ff = 0.0f;

for (int j = 0; j < patches.size(); j++)
{
if (i != j)
{
vec3 ci = patches[i].center();
vec3 cj = patches[j].center();

vec3 r = ci - cj;

float l = r.length();

r.normalize();

float cosA = r.dot(patches[i].p._normal);
float cosB = r.dot(patches[j].p._normal);

ff += fabs((cosA * cosB) / 3.1415926 * l*l);
}
}

patches[i].formFactor = ff * 0.00015f;
}
}

void rad_calc()
{
for (int k = 0; k < 3; k++)
{
for (int i = 0; i < patches.size(); i++)
{
float incident = 0.0f;

for (int j = 0; j < patches.size(); j++)
{
if (i != j)
{
incident += patches[j].radiosity * patches[j].formFactor;
}
}

patches[i].radiosity = patches[i].emission + incident;
}
}
}


[edited by - Gammastrahler on April 13, 2004 8:26:36 AM]

Share this post


Link to post
Share on other sites
Here is the output of my crappy code...


greets
Gammastrahler


[edited by - Gammastrahler on April 13, 2004 8:32:20 AM]

Share this post


Link to post
Share on other sites
Here is my code to get the formfactors between two patches (S = sender, R = receiver). It''s slow, but it works great.



// Make sure sender and receiver aren''t the same

if( S == R ) continue;

// get vector V, pointing from the center of S to the center of R

V = R->position - S->position;

// get length(V) and normalize it

r = V.length();
V.normalize();

// Compute shadow factor

// TODO: multisampling

Hij = Raytracer->TestShadowRay(S->position, R->position);
if( Hij == 0 ) continue;

// compute geometric formfactor between S and R (range: 0..1)

phi_p = S->normal * V;
phi_r = -(R->normal * V);

if( phi_p <= 0 || phi_r <= 0 ) continue;

double ff = (phi_p*phi_r) / (pi*r*r);

// Take differential area and shadow factor into account

ff *= Hij * S->area;

// Make sure we are using sane values

assert( ff <= 1.0 && ff >= 0.0 );

// Consider monochromatic energy absorption

ff *= GeneralPatchReflectance;

// shoot energy to R->energy and R->radiance, modulated by the receiving patch colour

RGB<double> d = S->energy * ff * R->colour;
R->energy += d;
R->radiance += d;

// Reset sender energy

S->energy = 0;

Share this post


Link to post
Share on other sites
Thanks for your answer

I have oriented my code on your´s and as well took some pseudo code from "Computer Graphics & Principles" to use it in my current application:

My radiosity processor now looks like this:


void do_radiosity()
{
for (int i = 0; i < patches.size(); i++)
{
for (int j = 0; j < patches.size(); j++)
{
if (i == j) continue;

vec3 ci = patches[i].center();
vec3 cj = patches[j].center();

vec3 r = ci - cj;

float l = r.length();
r.normalize();

float cosA = -r.dot(patches[i].p._normal);
float cosB = r.dot(patches[j].p._normal);

if (cosA <= 0 || cosB <= 0) continue;

patches[j].formFactor = (cosA * cosB) /(3.14159265358979323846 * l*l);
}

for (int j = 0; j < patches.size(); j++)
{
float Radiosity = patches[i].deltaB * patches[j].formFactor * 0.3;

patches[j].deltaB += Radiosity;
patches[j].B += Radiosity;
}

patches[i].deltaB = 0.0;
}
}


But the output looks somewhat wired!



Can someone tell me the error?

Thanks
Gammastrahler

[edited by - Gammastrahler on April 13, 2004 2:51:33 PM]

Share this post


Link to post
Share on other sites
Weird. Are you sure that your patch centers and normals are correctly computed ? It looks like something is wrong with them, especially on the back wall.

Share this post


Link to post
Share on other sites
I have checked all patch normals and patch centers... they seem to be okay.

Can you find any potentional errors in my code?

Or is the problem with the scaling of my final radiosity values to RGB values?

i do this

glColor3f(patches.radiosity * 0.000025, patches[i].radiosity * 0.000025, patches[i].radiosity * 0.000025);

to get acceptable results.

thanks
Gammastrahler


[edited by - Gammastrahler on April 14, 2004 6:53:07 AM]

Share this post


Link to post
Share on other sites
Is it point sampled?? It looks like a lightmap of 16x16 pixels (count the rasters). Try switching linear point sampling on.

Share this post


Link to post
Share on other sites
I wonder why people even speak of form factors in the context of hemicube radiosity.

The classic hemicube radiosity simply computes the radiosity at each patch in a relaxation-like manner.

Sure, you can also figure out the actual form factors from the hemicube data, but it''s surely done much more rarely than simply computing the radiosity.

- Mikko

Share this post


Link to post
Share on other sites
I have rewritten my radisoity code and implemented progressive refinement.

Here is a picture rendered with the new code, after doing 10 passes, it looks much better than the previous image i posted, thugh still there are fatal errors. The most obvious error is, that ner the light source, everything is DARK!!!



Here, again is my source code:


void do_radiosity()
{
int i, j;
float max = 0.0f;
int maxIndex = 0;

// select patch i with highest energy


for (int i = 0; i < patches.size(); i++)
{
if (patches[i].emission > max)
{
maxIndex = i;
max = patches[i].emission;
}
}

i = maxIndex;

for (j = 0; j < patches.size(); j++)
{
if (i == j) continue;

vec3 V = patches[j].center() - patches[i].center();

float r = V.length();

V.normalize();

float phi_j = -V.dot(patches[j].p._normal);
float phi_i = V.dot(patches[i].p._normal);

if (phi_j <= 0.0f || phi_i <= 0.0f) continue;

patches[j].formFactor = (phi_j * phi_i) / (3.14159265358979323846 * r * r);
}

for (j = 0; j < patches.size(); j++)
{
if (i == j) continue;

float rad = 0.1 * patches[i].emission * patches[j].formFactor;

patches[j].radiosity += rad;
patches[j].emission += rad;
}

patches[i].emission = 0.0;
}


if someone could help me again, i would be very happy.

thanks
Gammastrahler

Share this post


Link to post
Share on other sites
This:

is perfectly fine. Your radiosity system is working as it should.

the black ceiling is correct. It is only lit by indirect light, but the reflected light from the walls is not strong enough to light it in your case. But that''s normal behaviour.

To get a good image, you need some adjustments:

1) Don''t use a linear scale (patches.radiosity * 0.000025), but use an exponential exposure function instead.

2) Either your area light intensity, or the reflectance of the walls seem a little low.

3) If you want to recreate the effect of the cornell box, you need to add the two boxes in the middle. The taller box almost reaches the ceiling, its top is very near to the light. It will reflect a lot of indirect light onto the ceiling. Without that box, your ceiling will be much darker. Less light is reflected onto it.

Share this post


Link to post
Share on other sites
Okay, i detected the problem with the last image: the lightsource was to near to the top, i think.

Here again are two images with a cube placed in the room as a light source, were only the bottom side of the cube has initial energy.

The images are closer to the desired result as the last ones, but there is still a problem, as you can obviously see: The left and right walls get too dark at lower posititions.

what could be wrong here?



thanks
Gammastrahler

Share this post


Link to post
Share on other sites
quote:
Original post by Gammastrahler
what could be wrong here?


Nothing. That''s the way radiosity works. the dark areas are parts only lit by indirect light. If you want good indirect illumination, then you need an exponential response curve, first and foremost. That''s the most important part.

Then, the number of passes. By 10 passes, you mean 10 patches shot their energy ? If that''s the case, then you need many more passes, before the effect of indirect illumination really kicks in. The amounts of energy in indirect light are tiny, compared to direct light. They will only emerge at higher passes, and will only be visible using an exponential curve.

Share this post


Link to post
Share on other sites
This is the scene after shooting radisity for 35 patches:

Is it still normal that there are some very areas at the bottom?

I think, i should first learn more about heat transfer... maybe then i get a grip on it... but could you explain me how i can implement an exponentional response curve?

thanks anyway



Share this post


Link to post
Share on other sites
No, that''s not normal. The black borders are a sign of inaccurate patch centers. Since you evaluate the lighting only once per patch, the point of evaluation should more or less represent the patch (an area is represented by a single point). This will introduce errors, so a slight dark border is normal (and usually not noticeable). The only way to even solve that problem, is sampling several points per patch. And average the result. In your case, there is a problem. The border is too black.

There is also a problem with ceiling and floor visible on the last screenshot. Both are totally saturated, unlike the walls. The ceiling is too white, it doesn''t receive direct light, so it should be much darker.

Your code seem to not respect energy conservation. Adding passes will redistribute energy, but it will never add energy. In your case, it seems that lighting gets whiter with every pass, so energy is added to the system.

The exponential response curve is done like this (copy and pasted from code YannL posted in some old thread):

Your high dynamic range light value = v
The remapped light value used to render = c

c = (1.0 - exp(-v * exposure)) * 255.0

Share this post


Link to post
Share on other sites
Thanks @ ALX a lot, i think now i´m closer to the desired result.

Here is an image rendered with my latest code, after a few passes, including the exponetional response curve for color conversion and fixed some little bugs:



The problem i still have, that when rendering with too many passes, the scene still gets brighter and brighter until it is completely white.

I have checked my code, but it looks the same as your´s as well as the pseudo code from some books and papers.

The problem i have with those books and papers is, that they are all english and my english is far from good... so understanding radiosity is actual a language problem for me...

I initially set B and delta_B of all patches to E. I use 1.0 for lightsources, and 0.0 for all other patches.

Then i enter the passes loop. After each pass, i render with the resulting pathces.radiosity (B) as the color.

but what is wrong with this code? can you see some error?

thanks
Gammastrahler


[edited by - Gammastrahler on April 15, 2004 10:48:49 AM]

Share this post


Link to post
Share on other sites
Your lighting model looks awsome! Thankyou for putting up screenshots.

~Wave

Share this post


Link to post
Share on other sites
quote:
Original post by Gammastrahler
Here is an image rendered with my latest code, after a few passes, including the exponetional response curve for color conversion and fixed some little bugs:


Doesn''t look too bad.

quote:

The problem i still have, that when rendering with too many passes, the scene still gets brighter and brighter until it is completely white.


Are you resetting the shot patch energy after each pass ?

quote:

The problem i have with those books and papers is, that they are all english and my english is far from good... so understanding radiosity is actual a language problem for me...


Yeah, many are badly written and error prone on top of that. If you have specific questions, feel free to email me. I can try to explain it in either German or French, if that helps.

Share this post


Link to post
Share on other sites
quote:

Are you resetting the shot patch energy after each pass ?


Yes, after each pass, i set the shooted energy = 0, (defined as delta in my code)

quote:

Yeah, many are badly written and error prone on top of that. If you have specific questions, feel free to email me. I can try to explain it in either German or French, if that helps.


I would really appreciate that German would be great, but i´ve never learned french Could you give me your email address?

/EDIT
have overseen the "mail" link i have sent you a mail...

[edited by - Gammastrahler on April 17, 2004 8:59:48 AM]

Share this post


Link to post
Share on other sites
In ALX''s code, he has:

// Take differential area and shadow factor into account
ff *= Hij * S->area;

I know you have not computed any shadow factors, but in the code you posted, I can''t see where you are multiplying by the area of the sending patch. Are you in fact doing this?

Share this post


Link to post
Share on other sites

  • Advertisement