⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 radgen.cpp

📁 使用stl技术,(还没看,是听说的)
💻 CPP
📖 第 1 页 / 共 4 页
字号:
		}
	}

	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 + -