Использование Z буфера и сортировка полигонов по глубине

Загрузить архив с примерами ЗДЕСЬ.

На примере как осветить модель торуса (использовалась функция DrawDibDraw, демонстрация positional light source, gouraud shading) мы изучим использование Z буфера. Проект можно загрузить /src/03.001-z_buff/Lighting_Soft_Torus_Z_Buff.

Освещение поверхностей - Интерполяция цветов

В проекте освещения торуса использовался Z буфер, потому что отбрасывания задних поверхностей не хватает, т.к. у торуса в центре просвечиваются треугольники. Попробуйте закоментировать код работы с Z буфером и вы увидите следующую картину.

Освещение поверхностей - Интерполяция цветов

В чем заключем смысл использования Z буфера. Z буфер в приложении это двумерный массив такого же размера как экран приложения. Например у нас приложение имеет окно размером 800 на 600 пикселей. Такого же размера надо делать Z буфер, если мы хотим использовать Z буфер. Сначала мы определяем указатель на массив в классе CMeshManager.


	float **m_fZBuff;
	

Наше приложение имеет размеры окна 800 на 600 пикселей, и мы могли бы определить массив Z буфера так:


	float m_fZBuff[800][600];
	

Но размер окна приложения может менятся по желанию пользователя, поэтому мы ввели динамический массив для Z буфера. Сначала в функции Init_Torus мы создаем этот массив для Z буфера, то есть выделяем место нужного размера, под размеры нашего окна приложения:


	//создаем Z буфер
	m_fZBuff = new float*[m_nViewHeight];

	for (UINT i = 0; i < m_nViewHeight; i++)
	{
		m_fZBuff[i] = new float[m_nViewWidth];
	}
	

Далее в самом начале функции Draw_Torus перед вызовом Clear_BackBuffer мы вызываем код очистки Z буфера, очистка производится в каждом кадре перед рисованием. Для очистки Z буфера мы взяли максимально возможную глубину для сцены 25000.0f.

	
	//очищаем Z-Buffer
	for (UINT i = 0; i < m_nViewHeight; i++)
	{
		for(UINT j = 0; j < m_nViewWidth; j++)
		{
			//дальняя плоскость отсечения 25000.0f
			//это максимальная глубина Z
			//мы этой глубиной очищаем Z-Buffer
			m_fZBuff[i][j] = 25000.0f;
		}
	}

Далее в функции Draw_Color_Poly есть такой код, который и показывает работу Z буфера:


		for (int xi=(int)m_xl; xi<m_xr; xi++)
		{
			float fZVal = m_fZBuff[yi][xi];

			//если глубина fZVal в Z буфере меньше
			//чем глубина пикселя Z
			//не рисуем пиксель пропускаем его
			if( fZVal < zi )
			{	
				
				ri+=dr;
				gi+=dg;
				bi+=db;
				zi+=dz;

				continue;
			}

			m_fZBuff[yi][xi] = zi;
	

Как видим этот код расположен в цикле интеполяции по x. Сначала мы получаем значение из Z буфера:


	float fZVal = m_fZBuff[yi][xi];
	

Далее если это значение что мы получили из Z буфера меньше чем то что мы имеем сейчас по Z - мы продолжаем цикл continue- не меняем значение в Z буфере и не рисуем пиксель. Но если значение из Z буфера больше чем Z, мы заносим значение в Z буфер и рисуем пиксель. Таким образом Z буфер нам говорит- рисовать пиксель на экране или пропусить, так как на экране уже есть наприсованный пиксель с меньшей глубиной, т.е. ближе к зрителю.

	
			if( fZVal < zi )
			{	
				
				ri+=dr;
				gi+=dg;
				bi+=db;
				zi+=dz;

				continue;
			}

			m_fZBuff[yi][xi] = zi;


В следующем примере мы сортируем треугольники по глубине и выводим на экран начиная из самого дальнего. Проект без использования Z буффера. Что бы не усложнять алгоритм был использован способ сортировки пузырьком. Проект можно загрузить /src/03.001-z_buff/Lighting_Soft_Torus_Sorted.