You can say that i''m crazy or something, but i got bored reading all that stuff, and i quitted for a while!!! So, i rewritten my radiosity calculator, to have something to work with. Also i made some changes, from the knowledge i got so far.
So, here is how it works so far. I don''t know if this is a full implementation of an already suggested algorithm, but i think that it is a mix from some of them. The steps for calculating radiosity for the Cornel Box (a scene i mean) is :
1) Calculate lightmap UVs for every polygon in the scene and pack them(!!!).
2) Calculate lumel/patches properties given a constant density factor for U and V axis. I mean, a square which is 1x1, with a density in U equal to 2.0f and density V equal 2.0f will have total 4 lumels (that is, its lightmap will be 2x2). Density is variable before starting calculations.
3) Start radiosity loop, by finding the brightest patch/lumel in the scene. (Source)
4) Calculate potential receivers
5) For every receiver (dest), calculate a form factor from source to dest.
6) Calculate the amount of radiance arrives at dest depending on patches'' areas, dest reflectivity form factor etc.
7) Store this ff in a table for later usage (when dest is the source, and source is the dest, you dont have to recalculate this ff!!)
8) When finished with all receivers for this source, update lightmaps and render the scene (to see the progress)
9) Repeat steps 3-8 until the maximum radiosity leaving from the source is less than a given value.
here is the source for that (i hope i pasted it all!!!)
GELumel* FindBrightestPatch(void){ GELumel* tmp = NULL; float fEnergy; // initial energy must me a non negative small value, // so we set it to 0.0f!!! fEnergy = 0.0f; // for every lumel in the scene... for(int i=0;i<PatchList.size();i++) { // calculate the energy it has to give... float tempEnergy = PatchList[i]->DeltaRad[0] + PatchList[i]->DeltaRad[1] + PatchList[i]->DeltaRad[2]; // if it is greater the the minimum energy so far, and if it // belongs to some polygon, set its energy to be the minimum, and brightest patch to // itself!!! // IMPORTANT : Only lumels inside a polygon can become sources. // Lumels outside polygons, are only for receiving!!!! if(tempEnergy > fEnergy && PatchList[i]->InsidePolygon) { tmp = PatchList[i]; fEnergy = tempEnergy; } } // if the final minimum energy if less than a given limit, then // no source found!!! if(fEnergy < MinRad) return NULL; // else, return the source... return tmp;}int ClassifyPolygon(GEPlane *plane, GEPolygon *poly){ int numPos = 0; int numNeg = 0; int i; for(i=0;i<poly->NumVertices;i++) { float side = plane->ClassifyPointf(&poly->Vertex[i].GetVector()); if(side > 0.0f) numPos++; else if(side < 0.0f) numNeg++; } if(numPos > 0 && numNeg == 0) return GE_IN_FRONT_OF; else if(numPos == 0 && numNeg > 0) return GE_IN_BACK_OF; else if(numPos == 0 && numNeg == 0) { if(plane->Normal * poly->Plane.Normal > 0.0f) return GE_IN_FRONT_OF; else return GE_IN_BACK_OF; } return GE_SPLIT;}void SetupReceivers(GELumel* src){ // Clear receiver list, for a new calculation... Receiver.clear(); if(!src) return; // Count number of lightmaps... int i, numMaps = LightmapList.size(); int a, numTris; bool triFound; GELightmap* lm; GEPolygon* dstPoly; int j, k; for(i=0;i<numMaps;i++) { lm = LightmapList[i]; // if the source lumel if behind first triangle''s plane (= lightmap''s plane // then this lightmap is not a receiver. if(lm->PolyList[0]->Plane.ClassifyPointf(&src->Pos) < 0.0f) continue; // Count number of triangles in current lightmap numTris = lm->PolyList.size(); triFound = false; for(a=0;a<numTris;a++) { dstPoly = lm->PolyList[a]; // if current triangle is behind source''s plane, // then we can''t say if this lightmap is a receiver. if(ClassifyPolygon(&src->Parent->Plane,dstPoly) == GE_IN_BACK_OF) continue; // else this lightmap has some lumels visible from source. // so set triFound to true, and break... triFound = true; break; } // If we actually found a triangle that has visible lumels // from source then... if(triFound) { // push every lightmap''s lumel to Receiver list... for(j=0;j<lm->Tex.Width;j++) { for(k=0;k<lm->Tex.Height;k++) if(lm->Lumel[j][k].Parent) Receiver.push_back(&lm->Lumel[j][k]); } } }}int CastRay(GEPolygon* tri,GEVector3D *cStart, GEVector3D *cEnd, GEVector3D *cOut){ float t, t0, t1; float dist; GEVector3D normal = tri->Plane.Normal; dist = tri->Plane.D; t0 = - (normal.x * cStart->x + normal.y * cStart->y + normal.z * cStart->z + dist ); t1 = normal * (*cEnd); if (!t1) return 0; t = t0 / t1; if(t <= (GE_FLOAT_PRECISION)) return 0; if(t >= (1.0f - GE_FLOAT_PRECISION)) return 0; cOut->x = cStart->x + cEnd->x * t; cOut->y = cStart->y + cEnd->y * t; cOut->z = cStart->z + cEnd->z * t; return 1;}/*int TriangleIntersect(GEVector3D *origin, GEVector3D *dir, GEVector3D *a, GEVector3D *b, GEVector3D *c){ GEVector3D edge1, edge2, tvec, pvec, qvec; float det,inv_det; float t, u, v; edge1 = *b - *a; edge2 = *c - *a; pvec = (*dir) | edge2; det = edge1 * pvec; if (det > -EPSILON && det < EPSILON) return 0; inv_det = 1.0 / det; tvec = *origin - *a; u = (tvec * pvec) * inv_det; if (u < 0.0 || u > 1.0) return 0; qvec = tvec | edge1; v = ((*dir) * qvec) * inv_det; if (v < 0.0 || u + v > 1.0) return 0; t = (edge2 * qvec) * inv_det; return 1;}*/int Visible(GEPolygon* tri,GEVector3D *cStart, GEVector3D *cEnd){ GEVector3D c; if(CastRay(tri, cStart, cEnd, &c)) { if(tri->IsPointInside(&c,GE_FLOAT_PRECISION))// IntersectLineSegment(&line[0])) return 0; } return 1;}float CanSeePatch(GELumel* cSrc,GELumel *cDest){ // if src is behind dst plane, or if dst is behind src''s plane // then there is no chance to see each other!!!! if( cSrc->Parent->Plane.ClassifyPointf(&cDest->Pos) < 0.0f || cDest->Parent->Plane.ClassifyPointf(&cSrc->Pos) < 0.0f) return 0.0f; int i; GEVector3D vect, tmp; vect = cDest->Pos - cSrc->Pos; // see if a triangle is in the way between the source and the destination lumel... for(i = 0; i < PolygonList.size(); i++) { if(!Visible(PolygonList[i],&cSrc->Pos, &vect) ) return 0.0; } return 1.0;}float FormFactor(GELumel *cDest, GELumel *cSrc ) { float R, cos0i, cos0j, FF, H; if(!cDest->Parent || !cSrc->Parent) return 0.0f; FF = 0.0f; H = CanSeePatch(cSrc, cDest); if(H == 0.0f) return 0.0f; if(cDest->Pos == cSrc->Pos) return 0.0f; GEVector3D vect; vect = cDest->Pos - cSrc->Pos; R = vect.Normalize(); cos0i = vect * cSrc->Parent->Plane.Normal; cos0j = - (vect * cDest->Parent->Plane.Normal); FF = (cos0j * cos0i * cDest->Area * H) / (PI * R * R); return FF;}void DoPass(void){ GELumel *pSrc, *pDest; float ff, deltaRad[3]; // search for the brightest of the patches... pSrc = FindBrightestPatch(); // if no patch has delta rad greater than the given limit // then we are done we calculations... until we lower the limit!!! if(pSrc == NULL) { DoRadiosity = false; AutoStep = false;// CheckMenuItem(RadMenu,ID_RAD_AUTOSTEP,MF_UNCHECKED); // we re-save all lightmaps, because the exposure can be changed!!! UpdateAllLightmaps(); return; } // setup the receiver list, for the brightest patch... SetupReceivers(pSrc); // count number of receivers... int numPatches = Receiver.size(); // if there is no lumel to receive radiance, then reset the source // and return... if(numPatches <= 0) { pSrc->DeltaRad[0] = 0.0f; pSrc->DeltaRad[1] = 0.0f; pSrc->DeltaRad[2] = 0.0f; return; } PassesDone++; CurrentDeltaRad = pSrc->DeltaRad[0] + pSrc->DeltaRad[1] + pSrc->DeltaRad[2]; CurrentSrc = pSrc->Parent; // For every receiver... for(int i=0;i<numPatches;i++) { pDest = Receiver[i]; // calculate a form factor between the source and the receiver... if(FFTable[pSrc->Index][pDest->Index] == -1.0f) { ff = FormFactor(pSrc, pDest); FFTable[pSrc->Index][pDest->Index] = ff; FFTable[pDest->Index][pSrc->Index] = ff; } // if the form factor equals zero, then there is nothing to give // to that lumel, so continue with the next... if(ff <= 0.0f) continue; // we precalculate this because it is needed a few times... float mult = pDest->Reflect * ff * pSrc->Area / pDest->Area; deltaRad[0] = pSrc->DeltaRad[0] * mult; deltaRad[1] = pSrc->DeltaRad[1] * mult; deltaRad[2] = pSrc->DeltaRad[2] * mult; pDest->DeltaRad[0] += deltaRad[0]; pDest->DeltaRad[1] += deltaRad[1]; pDest->DeltaRad[2] += deltaRad[2]; pDest->Rad[0] += deltaRad[0]; pDest->Rad[1] += deltaRad[1]; pDest->Rad[2] += deltaRad[2]; } // Pass completed!!! // Reset the brightest patch, to be the darkest... pSrc->DeltaRad[0] = 0.0f; pSrc->DeltaRad[1] = 0.0f; pSrc->DeltaRad[2] = 0.0f; // save all lightmaps, so we can see the difference!!! UpdateAllLightmaps();}
There are many problems with it. First of all, color bleeding doesn''t seam to work. I''ve modeled the Cornel Box in Lightwave, and i used the colors described in its specifications (link at Yann''s post). I put colors as described, and i used textures colored in the same way (in case to use multitexturing).
Problems so far.
1) Color bleeding doesn''t seam to work!
2) Form Factors are veeeeery small. With this i want to say that, when i start calculations, light''s radiosity are very big (Luminosity = 100% * Color.RGB = (255, 255, 0)), but when light give all its power, the remaining radiosity in the scene is very small. From 600 (a randome value) when light is still alive, to 50 or less when the light is done!
3) What must lumel/patch density be to make my Cornell Box look like the real thing??? I think a value of 100 lumels/world unit would be great.
Any suggestions for making it better acceptable.
I''m shutting down now. I have reading in front of me.
Thanks for the attention!!!
HellRaiZer