Beispiel #1
0
Datei: Ray.cpp Projekt: 7kia/CG
bool CPlane::Hit(const CRay &ray, SRayIntersection &intersection) const
{
    // Величина, меньше которой модуль скалярного произведения вектора направления луча и
    // нормали плоскости означает параллельность луча и плоскости
    const float EPSILON = std::numeric_limits<float>::epsilon();

    // Нормаль к плоскости в системе координат объекта
    const glm::vec3 normalInObjectSpace(m_planeEquation);

    // Скалярное произведение направления луча и нормали к плоскости
    const float normalDotDirection = glm::dot(ray.GetDirection(), normalInObjectSpace);

    // Если скалярное произведение близко к нулю, луч параллелен плоскости
    if (fabs(normalDotDirection) < EPSILON)
    {
        return false;
    }

    /*
    Находим время пересечения луча с плоскостью, подставляя в уравнение плоскости точку испускания луча
    и деление результата на ранее вычисленное сканярное произведение направления луча и нормали к плоскости
    */
    const float hitTime = -glm::dot(glm::vec4(ray.GetStart(), 1), m_planeEquation) / normalDotDirection;

    // Нас интересует только пересечение луча с плоскостью в положительный момент времени,
    // поэтому находящуюся "позади" точки испускания луча точку пересечения мы за точку пересечения не считаем
    // Сравнение с величиной EPSILON, а не с 0 нужно для того, чтобы позволить
    // лучам, испущенным с плоскости, оторваться от нее.
    // Это необходимо при обработке вторичных лучей для построения теней и отражений и преломлений
    if (hitTime <= EPSILON)
    {
        return false;
    }

    // Вычисляем точку столкновения с лучом в системе координат сцены в момент столкновения
    const glm::vec3 hitPoint = ray.GetPointAtTime(hitTime);
    intersection.m_time = hitTime;
    intersection.m_point = hitPoint;

    return true;
}
bool CTriangleMesh::Hit(CRay const& ray, CIntersection & intersection) const
{
	// Вычисляем обратно преобразованный луч (вместо вполнения прямого преобразования объекта)
	CRay invRay = Transform(ray, GetInverseTransform());
	CVector3d const& invRayStart = invRay.GetStart();
	CVector3d const& invRayDirection = invRay.GetDirection();

	//////////////////////////////////////////////////////////////////////////
	// Здесь следует выполнить проверку на пересечение луча с ограничивающим
	// объемом (bounding volume) полигональной сетки.
	// При отсутствии такого пересечения луч гарантированно не будет пересекать
	// ни одну из граней сетки, что позволит избежать лишних проверок:
	// if (!ray_intesects_bounding_volume)
	//     return false;
	//////////////////////////////////////////////////////////////////////////
	
	// Получаем информацию о массиве треугольников сетки
	CTriangle const* const triangles = m_pMeshData->GetTriangles();
	const size_t numTriangles = m_pMeshData->GetTriangleCount();

	// Массив найденных пересечений луча с гранями сетки.
	std::vector<FaceHit> faceHits;

	//////////////////////////////////////////////////////////////////////////
	// Используется поиск пересечения луча со всеми гранями сетки.
	// Данный подход является очень неэффективным уже на полигональных сетках,
	// содержащих более нескольких десятков граней, а, тем более, сотни и тысячи граней.
	//
	// Для эффективного поиска столкновений следует представить полигональную сетку
	// не в виде массива граней, а в виде древовидной структуры (Oct-Tree или BSP-Tree),
	// что уменьшит вычислительную сложность поиска столкновений с O(N) до O(log N)
	//////////////////////////////////////////////////////////////////////////
	FaceHit hit;
	for (size_t i = 0; i < numTriangles; ++i)
	{
		CTriangle const& triangle = triangles[i];

		// Проверка на пересечение луча с треугольной гранью
		if (triangle.HitTest(invRayStart, invRayDirection, hit.hitTime, hit.hitPointInObjectSpace, hit.w0, hit.w1, hit.w2))
		{
			// Сохраняем индекс грани и добавляем информацию в массив найденных пересечений
			hit.faceIndex = i;

			if (faceHits.empty())
			{
				// При обнаружени первого пересечения резервируем
				// память сразу под 8 пересечений (для уменьшения количества операций выделения памяти)
				faceHits.reserve(8);
			}
			faceHits.push_back(hit);
		}
	}

	// При отсутствии пересечений выходим
	if (faceHits.empty())
	{
		return false;
	}

	//////////////////////////////////////////////////////////////////////////
	// Упорядочиваем найденные пересечения по возрастанию времени столкновения
	//////////////////////////////////////////////////////////////////////////
	size_t const numHits = faceHits.size();
	std::vector<FaceHit const *> hitPointers(numHits);
	{
		// Инициализируем массив указателей на точки пересечения
		for (size_t i = 0; i < numHits; ++i)
		{
			hitPointers[i] = &faceHits[i];
		}

		// Сортируем массив указателей по возрастанию времени столкновения
		// Сортируются указатели, а не сами объекты, чтобы сократить
		// издержки на обмен элементов массива:
		// На 32 битной платформе размер структуры FaceHit равен 64 байтам,
		// а размер указателя - всего 4 байта.
		if (numHits > 1)
		{
			std::sort(hitPointers.begin(), hitPointers.end(), HitPointerComparator());
			// Теперь указатели в массиве hitPointers отсортированы
			// в порядке возрастания времени столкновения луча с гранями
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Возвращаем информацию о найденных пересечениях
	//////////////////////////////////////////////////////////////////////////
	for (size_t i = 0; i < numHits; ++i)
	{
		// Получаем информацию о столкновении
		FaceHit const& faceHit = *hitPointers[i];

		// Сохраняем информацию только о первом пересечении
		if (i == 0)
		{
			ExtendedHitData *hd = (ExtendedHitData*)&m_lastHitData;
			hd->faceIndex = faceHit.faceIndex;
			hd->vc[0] = static_cast<float>(faceHit.w0);
			hd->vc[1] = static_cast<float>(faceHit.w1);
			hd->vc[2] = static_cast<float>(faceHit.w2);
		}

		// Точка столкновения в мировой системе координат
		CVector3d hitPoint = ray.GetPointAtTime(faceHit.hitTime);

		// Грань, с которой произошло столкновение
		CTriangle const& triangle = triangles[faceHit.faceIndex];

		// Нормаль "плоской грани" во всех точках столкновения равна нормали самой грани
		CVector3d normalInObjectSpace = triangle.GetPlaneEquation();

		if (!triangle.IsFlatShaded())
		{
			// Для неплоских граней выполняется интерполяция нормалей вершин треугольника
			// с учетом их весовых коэффициентов в точке пересечения
			Vertex const& v0 = triangle.GetVertex0();
			Vertex const& v1 = triangle.GetVertex1();
			Vertex const& v2 = triangle.GetVertex2();

			// Взвешенный вектор нормали
			normalInObjectSpace = 
				faceHit.w0 * v0.normal + 
				faceHit.w1 * v1.normal +
				faceHit.w2 * v2.normal;
		}

		// Нормаль в мировой системе координат
		CVector3d normal = GetNormalMatrix() * normalInObjectSpace;

		// Добавляем информацию о точке пересечения в объект intersection
		intersection.AddHit(
			CHitInfo(
				faceHit.hitTime, *this, 
				hitPoint,
				faceHit.hitPointInObjectSpace,
				normal, normalInObjectSpace
				)
			);
	}

	return true;
}