📄 radgen.cpp
字号:
}
}
if (directLightOnly()) reflectedEnergy = geom::Color3(0,0,0);
energyEscapedThisIteration() = shootingEnergy - receivedEnergy;
energyAbsorbedThisIteration() = shootingEnergy - energyEscapedThisIteration() - reflectedEnergy;
}
// ---------------------------------------------------------------------------------------------------------------------------------
void RadGen::emitPointLight(RadPatch & emitter, const RadPrimPointerListGrainy & potentialReceivers, RadLMapArray & storedEnergy)
{
// What's visible from this emitter?
RadPrimListGrainy visiblePieces;
calcVisiblePieces(emitter, potentialReceivers, visiblePieces);
// We're emitting from a point light source. This isn't necessarily how Radiosity works, so we need to be
// able to translate from "energy transmitted from a point light source" to "energy transmitted from an area
// light source" -- and here it is....
float pointToAreaScaleFactor = pointLightMultiplier() / areaLightMultiplier();
// Clear out the emitter's energy before we shoot. Theoretically, we never should need to do this, because an emitter
// should never be able to emit energy onto itself, but it won't hurt to do this for completeness.
geom::Color3 shootingEnergy = emitter.energy();
geom::Color3 reflectedEnergy(0,0,0);
geom::Color3 receivedEnergy(0,0,0);
emitter.energy() = geom::Color3(0,0,0);
// Light up the visible pieces
{
for (RadPrimListGrainy::node * i = visiblePieces.head(); i; i = i->next())
{
RadPrim & prim = i->data();
RadPrim & originalPrim = *i->data().originalPrimitive();
RadLMap & se = storedEnergy[originalPrim.textureID()];
prim.prepareNoPatches();
unsigned int fIndex = 0;
float totalArea = (prim.uXFormVector() % prim.vXFormVector()).length();
geom::Point3 elementCenter = prim.minXYZ() + prim.uXFormVector() * 0.5 + prim.vXFormVector() * 0.5;
// PDNDEBUG -- could this code be responsible for the reason why we get strange edges at the lightmap-clipped limits? Not using floor()?
unsigned int minU = static_cast<unsigned int>(prim.minUV().u());
unsigned int minV = static_cast<unsigned int>(prim.minUV().v());
unsigned int maxU = static_cast<unsigned int>(prim.maxUV().u());
unsigned int maxV = static_cast<unsigned int>(prim.maxUV().v());
unsigned int ominU = static_cast<unsigned int>(originalPrim.minUV().u());
unsigned int omaxU = static_cast<unsigned int>(originalPrim.maxUV().u());
unsigned int ominV = static_cast<unsigned int>(originalPrim.minUV().v());
for (unsigned int v = minV; v <= maxV; ++v)
{
geom::Point3 ec = elementCenter;
for (unsigned int u = minU; u <= maxU; ++u, ++fIndex, ec += prim.uXFormVector())
{
// Receiver must have area
float area_j = prim.elementAreas()[fIndex];
if (area_j <= 0) continue;
// Get the whole area of this patch from the original polygon
unsigned int pu = u - ominU;
unsigned int pv = v - ominV;
unsigned int elementIndex = pv * (omaxU-ominU+1) + pu;
// Here we calculate a hacked radiosity equation for a point light. It's just this:
//
// 1 / (distance_to_element * distance_to_element)
// Vector from j to i
#if 0 // Quake-like lighting
geom::Vector3 jiVect = emitter.origin() - ec;
float distanceSquared = jiVect.lengthSquared() / (8*8); // these 8's are specific to Jason's OCT files
jiVect.normalize();
float Hij = area_j / totalArea;
float Fij = (1.0f / distanceSquared) * (jiVect^prim.plane().normal()) * Hij;
#else
geom::Vector3 jiVect = emitter.origin() - ec;
float distanceSquared = jiVect.lengthSquared();
jiVect.normalize();
float cosTheta_j = prim.plane().normal() ^ jiVect;
float Hij = area_j / totalArea;
float dAj = totalArea;
// Our differential form factor
float Fij = cosTheta_j / (fstl::pi<float>() * distanceSquared) * Hij * dAj;
#endif
// The energy received
geom::Color3 energy = shootingEnergy * Fij;
geom::Color3 reflected = energy * originalPrim.reflectanceColor();
// Keep track of the energy going back into the scene
receivedEnergy += energy;
reflectedEnergy += reflected;
// Store the energy in the element
se.data()[v*se.width()+u] += reflected * pointLightMultiplier();
// Transfer the energy up to the patch
if (!directLightOnly())
{
unsigned int patchIndex = pv/subdivisionV() * originalPrim.uPatches() + pu/subdivisionU();
RadPatch & patch = originalPrim.patches()[patchIndex];
patch.energy() += reflected * pointToAreaScaleFactor;
}
}
elementCenter += prim.vXFormVector();
}
}
}
// Notice how we do not subtract the reflected energy from this value like we do for the area light sources...
//
// We don't do this for point lights because it's pretty much uselss... they don't act like normal lights, and have a
// different scale for lighting alltogether. So, we treat them separately, and give them a separate illumination pass.
//
// Therefore, we don't need to (or want to) track the energy reflected back into the scene in the statistics.
energyAbsorbedThisIteration() = shootingEnergy * pointToAreaScaleFactor;
}
// ---------------------------------------------------------------------------------------------------------------------------------
RadPatch * RadGen::findBrightestEmitter()
{
RadPatch * brightestPatch = static_cast<RadPatch *>(0);
float brightestEmission = 0.0f;
// Light sources come first...
if (lightsToProcess())
{
for(RadPatchList::node *i = geometry().lights().head(); i; i = i->next())
{
RadPatch & p = i->data();
// Ignore polygons that don't illuminate
if (p.energy() == geom::Color3(0,0,0)) continue;
// Is this the brightest (or only one) so far?
float thisEmission = p.energy().r() + p.energy().g() + p.energy().b();
if (thisEmission > brightestEmission)
{
brightestEmission = thisEmission;
brightestPatch = &p;
}
}
}
// If there is a light, use it first
if (brightestPatch) return brightestPatch;
// If we get here, we have no more lights to process
lightsToProcess() = false;
for(RadPrimList::node * i = geometry().polys().head(); i; i = i->next())
{
RadPrim & prim = i->data();
for(unsigned int j = 0; j < prim.patches().size(); ++j)
{
RadPatch & p = prim.patches()[j];
// Ignore polygons that don't illuminate
if (p.energy() == geom::Color3(0,0,0)) continue;
// Ignore unused patches
if (!p.area()) continue;
// Is this the brightest (or only one) so far?
float thisEmission = p.energy().r() + p.energy().g() + p.energy().b();
if (thisEmission > brightestEmission)
{
brightestEmission = thisEmission;
brightestPatch = &p;
}
}
}
return brightestPatch;
}
// ---------------------------------------------------------------------------------------------------------------------------------
bool RadGen::processEnergy(SOctree & node)
{
// Bounce some light around
bool processingLights = lightsToProcess();
bool atMaxSubdivision = false;
unsigned int iterationsToPerform = maxIterationsCount();
if (!maxIterations()) iterationsToPerform = 0xffffffff;
while(iterationsToPerform)
{
// Find the brightest emitter
RadPatch * emitter = findBrightestEmitter();
if (!emitter) break;
energyThisPass() = emitter->energy().r() + emitter->energy().g() + emitter->energy().b();
// What are we doing?
if (lightsToProcess())
{
progress()->setCurrentStatus("Distributing energy from light sources");
energyThisPass() *= pointLightMultiplier() / areaLightMultiplier();
}
else
{
if (processingLights)
{
processingLights = false;
totalAbsorbedEnergy() = geom::Color3(0,0,0);
totalEscapedEnergy() = geom::Color3(0,0,0);
initialEnergy() = calcTotalEnergy();
}
progress()->setCurrentStatus("Distributing reflected energy");
}
// Can we merge patches?
unsigned int newSubdivisionU = subdivisionU() * 2;
unsigned int newSubdivisionV = subdivisionV() * 2;
if (!atMaxSubdivision && adaptivePatchSubdivision() && newSubdivisionU <= adaptiveMaxSubdivisionU() && newSubdivisionV <= adaptiveMaxSubdivisionV())
{
// Have we reached the adaptive subdivision threshold?
geom::Color3 temp = calcRemainingEnergy();
float remaining = temp.r() + temp.g() + temp.b();
if (energyThisPass() * areaLightMultiplier() < adaptiveThreshold())
{
// Merge patches
bool merged = false;
for(RadPrimList::node * i = geometry().polys().head(); i; i = i->next())
{
RadPrim & prim = i->data();
if (prim.mergePatches2x2()) merged = true;
}
// Was anything merged?
if (merged)
{
// Our new subdivision levels
subdivisionU() = newSubdivisionU;
subdivisionV() = newSubdivisionV;
// Update our elements/patch count
countPatchesAndElements();
// Back to the top and try again...
continue;
}
// Nothing could be merged, so stop trying
else
{
atMaxSubdivision = true;
}
}
}
// Update the user
if (!updateStats(*progress())) return false;
// Find potential receivers
RadPrimPointerListGrainy potentialReceivers;
node.bsp().getOrderedList(emitter->origin(), potentialReceivers);
// Emit the energy
{
energyAbsorbedThisIteration() = geom::Color3(0,0,0);
if (emitter->isPointLight()) emitPointLight(*emitter, potentialReceivers, geometry().lightmaps());
else if (useNusselt()) emitNusselt(*emitter, potentialReceivers, geometry().lightmaps());
else emit(*emitter, potentialReceivers, geometry().lightmaps());
// Track the total absorbed and escaped energy
totalAbsorbedEnergy() += energyAbsorbedThisIteration();
totalEscapedEnergy() += energyEscapedThisIteration();
}
// Track iterations
iterationsToPerform--;
iterationsProcessed()++;
}
return true;
}
// ---------------------------------------------------------------------------------------------------------------------------------
geom::Color3 RadGen::calcTotalEnergy()
{
geom::Color3 remain(0,0,0);
// Lights
{
float pointToAreaScaleFactor = pointLightMultiplier() / areaLightMultiplier();
for(RadPatchList::node * i = geometry().lights().head(); i; i = i->next())
{
remain += i->data().energy() * pointToAreaScaleFactor;
}
}
// Patches
{
for(RadPrimList::node * i = geometry().polys().head(); i; i = i->next())
{
RadPrim & prim = i->data();
for(unsigned int j = 0; j < prim.patches().size(); ++j)
{
remain += prim.patches()[j].energy();
}
}
}
return remain;
}
// ---------------------------------------------------------------------------------------------------------------------------------
geom::Color3 RadGen::calcRemainingEnergy()
{
return initialEnergy() - totalAbsorbedEnergy() - totalEscapedEnergy();
}
// ---------------------------------------------------------------------------------------------------------------------------------
bool RadGen::updateStats(ProgressDlg & prog, const bool checkConvergence)
{
const float displayMultiplier = static_cast<float>(areaLightMultiplier());
geom::Color3 ie = initialEnergy();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -