Реализация карт теней на массовом графическом аппаратном обеспечении

Опубликовано: 23 октября 2007 г. | Последние Изменения: 28 августа 2008 г.
Введение
Автор: Дэйв Букаут (Dave Bookout)

В статье рассматривается использование карт теней в массовых системах с графическим аппаратным обеспечением на базе набора микросхем Intel® 915G Express. Включает примеры кода для реализации карт теней, которые можно использовать в примере проекта EmptyProject из комплекта Microsoft DirectX* 9.0 для разработчиков.

Тени оказывают существенное влияние на зрительное восприятие окружающего мира. Путем анализа тени, отбрасываемой объектом, можно получить о нем дополнительную информацию.1 По расположению и форме тени можно судить о поверхности, на которую падает тень, о форме объекта и о положении источника света относительно объекта (см. Рис. 1). Соответственно, правильная прорисовка теней способствует увеличению реалистичности сцен трехмерных графических приложений. Если же тени на изображении, созданном на компьютере, прорисованы неверное, это сразу же становится заметно.2 В данной статье рассматриваются следующие вопросы:

  • реализация теней с использованием карт теней в массовых системах с графическим аппаратным обеспечением на базе набора микросхем Intel® 915G Express;
  • организация поточной обработки и некоторые проблемы с точностью вычислений в системах, аппаратное обеспечение которых не поддерживает текстуры с использованием чисел с плавающей запятой;
  • пример кода для реализации карт теней, которые можно использовать в примере проекта EmptyProject из комплекта Microsoft DirectX* 9.0 для разработчиков.


Рис. 1. Рендеринг сцен с использованием и без использования теней. По тени, отбрасываемой объектом, можно судить о положении источника света и расстоянии между объектом и плоскостью, на которую падает тень.

1 Franklin C. Crow (Франклин С. Кроу). Shadow Algorithms for Computer Graphics (Алгоритмы построения теней в компьютерной графике). Computer Graphics, том 11, №2. Июль 1977 г. Стр. 242—248.

2 Франклин С. Кроу. Алгоритмы построения теней в компьютерной графике. Computer Graphics, том 11, №2. Июль 1977 г. Стр. 242—248.

Описание
Объект, который расположен на пути от источника света до плоскости, на которую падает свет, отбрасывает тень. Для простоты рассмотрим модель с точечным источником. В этом случае свет либо полностью дойдет до плоскости, либо будет остановлен объектом, находящимся на пути распространения света. В этой модели сформируется отчетливая тень, которая не очень реалистична из-за резкой границы между освещенной и затененной частью плоскости падения. В отличие от точечных, объемные источники света создадут размытые тени, а плоскость падения будет частично освещена источником света (см. Рис. 2).



Рис. 2. Один точечный источник света создает резкую тень. Объемный источник света создает области полутени и тени. При этом область тени меньше, чем в случае с точечным источником света.

Карты теней были впервые представлены Лансом Уильямсом (Lance Williams) в 1978 г.3 Основной принцип построения карт теней: источник света освещает только объекты, расположенные в зоне его обзора. Для расчета освещения необходимо определить, какие точки сцены видны со стороны источника света. Расстояния до точек, которые находятся ближе всего к источнику света, записываются в карты теней. При расчете освещения точки расстояние от нее до источника света сравнивается со значением, записанным в карте теней. Если эти значения совпадают, значит, точка освещена, в противном случае она находится в тени, а освещена будет другая точка.

Для создания карт теней необходимо провести рендеринг сцены относительно положения источника света и сохранить значения глубины цвета каждого пикселя. Обычно карта теней сохраняется в виде текстуры. С помощью стандартного рендеринга можно быстро упорядочить объекты сцены, независимо от ее геометрии. При рендеринге каждая точка сцены переводится в плоскость проекции света, а затем рассчитывается расстояние от этой точки до источника света. Вычисленное расстояние сравнивается со значением, которое сохранено в карте теней. Если расстояние от точки до источника света больше значения, сохраненного в карте теней, значит, эта точка находится в тени какого-то объекта, расположенного на пути распространения света.

Простота создания делает карты теней весьма практичным инструментом. Если можно провести рендеринг объектов сцены, значит, можно и создать карты теней. Следовательно, нет никаких ограничений геометрии сцены. Количество карт теней (а следовательно, и объем занимаемой ими памяти) прямо пропорционально количеству источников света и не зависит от сложности сцены. Для современных оптимизированных вычислительных систем создание карт теней не составляет труда. Кроме того, так как тени не зависят от положения камеры, то при движении источников света или объектов сцены нужно всего лишь провести новые вычисления карт теней.

Карты теней имеют ограниченную точность: возрастание суммарной ошибки многократных вычислений в формате с плавающей запятой может привести к неверной прорисовке теней. Проблемы возможны и с моделями, имеющими низкое разрешение, вследствие которого они могут отбросить тень на самих себя. Еще одной проблемой является ступенчатость, которая проявляется при пиксельных вычислениях. Проблемы со ступенчатостью возникают из-за шаблонов, используемых для создания карт теней. Карта теней создается в плоскости проекции света, однако оно используется для определения освещения с противоположной стороны – в плоскости камеры. При неточном совпадении этих факторов множество пикселей в плоскости камеры будет соответствовать одной и той же точке в карте теней.

Краткое описание метода создания карт теней:
  • создание карты теней
    • проведите рендеринг сцены относительно положения источника света
    • для каждого пикселя
      • сохраните значение глубины цвета

  • рендеринг сцены
    • проведите обычный рендеринг сцены
    • для каждого пикселя
      • переведите точку плоскости камеры в плоскость проекции света
      • определите расстояние от точки до источника света
      • сравните расстояние со значением для данной точки из карты теней
      • если расстояние больше значения, записанного в карте теней
        • пиксель затенен
      • в противном случае
        • пиксель не затенен, используйте обычные расчеты освещения.

3 Ланс Уильямс. Casting Curved Shadows on Curved Surfaces (Отбрасывание теней на неровные поверхности). Computer Graphics. SIGGRAPH, 1978 г. Том 12, №3. Стр. 39—42.

Реализация
Поскольку карты теней хранятся в виде текстур, создайте текстуру для карты теней, полученной в результате рендеринга. Графические технологии Intel® поддерживают различные форматы текстур для карт теней, кроме текстур с плавающей запятой. Наиболее оптимальными являются 32-разрядные текстуры.






D3DXCreateTexture(pd3dDevice, SHADOWMAP_SIZE, SHADOWMAP_SIZE, 1,
 			D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, 
D3DPOOL_DEFAULT, &g_pShadowMap);

IDirect3DSurface9* pShadowSurface;
IDirect3DSurface9* pOriginalBackBuffer;

g_pShadowMap->GetSurfaceLevel(0, &pShadowSurface);
pd3dDevice->GetRenderTarget(0, &pOriginalBackBuffer);
pd3dDevice->SetRenderTarget(0, pShadowSurface);
SAFE_RELEASE(pShadowSurface);

//render the scene, then restore the settings

pd3dDevice->SetRenderTarget(0, &pOriginalBackBUffer);

Подпрограмма построения теней (шейдер) для создания карты теней чрезвычайно проста. Для вершинных шейдеров переведите координаты вершин в плоскость источника света. Передайте новые значения координат текстуры пиксельному шейдеру, чтобы он вычислил координаты каждого пикселя. Пиксельный шейдер возвратит значения глубины цвета каждого пикселя.

void VertexGenerateShadowMap(float4 iPosition : POSITION, 
				 out float2 oDepth : TEXCOORD0, 
 				 out float4 oPosition : POSITION)
{
 oPosition = mul(iPosition, g_mWorldViewProjection);
 oDepth.xy = oPosition.zw;
}

void PixelGenerateShadowMap(float2 Depth : TEXCOORD0, 
 out float4 oColor : COLOR)
{
	oColor = Depth.x / Depth.y;
}



Рис. 3. Рендеринг карты теней. Значения глубины цвета меняются от 0,0 до 1,0, где 0,0 обозначает цвета точек, которые находятся ближе всего к источнику света. Таким образом, точки, которые находятся ближе к источнику света, имеют более темный цвет.

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

void VertexShadowMap(float4 iPosition : POSITION,
			 float3 iNormal : NORMAL,
			 out float4 oPosition : POSITION,
			 out float4 oViewSpacePosition : TEXCOORD0,
			 out float3 oNormal : TEXCOORD1,
			 out float4 oLightSpacePosition : TEXCOORD2)
{
oViewSpacePosition = mul(iPosition, g_mWorldView);
oPosition = mul(iPosition, g_mWorldViewProjection);
 	oNormal = mul( iNormal, (float3x3)g_mWorldView);
oLightSpacePosition = mul(oViewSpacePosition, 
				g_mViewToLightProjectionSpace);
}

Рассчитайте освещение для каждого пикселя. Используйте координаты каждого пикселя в плоскости проекции света в качестве координат текстур для карты теней. Координаты меняются от -1 до 1, поэтому для создания шаблона переведите их в значения от 0 до 1,0. Пиксель будет освещен, если его координаты меньше значений, записанных в карте теней. Для компенсации суммарной ошибки вычислений в формате с плавающей запятой необходимо учесть смещение.

void PixelShadowMap(float4 vViewSpacePosition : TEXCOORD0,
 float3 vNormal : TEXCOORD1,
 float4 vLightSpacePosition : TEXCOORD2,
 out float4 oColor : COLOR)
{
	// determine the depth value stored in the shadow map
	float2 ShadowMapCoord = 0.5 * vLightSpacePosition.xy /
 				vLightSpacePosition.w 
+ float2(0.5, 0.5);
ShadowMapCoord.y = 1.0f - ShadowMapCoord.y;
	float fShadowMapDistance = tex2D(ShadowMapSampler, 
						 ShadowMapCoord);
 
 	float fDistanceToLight = vLightSpacePosition.z 
					/ vLightSpacePosition.w;

	// compare the distance to the light with the shadow map value
	// the bias factor reduced impact of floating point errors
	float fShadow = fDistanceToLight - fShadowMapDistance 
> g_fShadowMapBias
				? 0.0f : 1.0f;

	float3 vLight = (float3)normalize(vViewSpacePosition – 
						 g_vPositionOfLight);
float4 vDiffuse = fShadow * g_vLightDiffuse
				* dot(-vLight, normalize(vNormal));
	oColor = saturate((vDiffuse + g_vLightAmbient) 
* g_vMaterialColor);
}



Рис. 4. Сцена, рендеринг которой был произведен с формированием теней.


Многопоточная обработка
DWORD dwThreadid;
g_hRenderMutex = CreateMutex(NULL, FALSE, NULL);
g_hShadowThread = 
	(HANDLE) _beginthreadex(NULL, 0, 
			GenerateShadowMap,(void*) pd3dDevice, 0, 
			(unsigned int*)&dwThreadid );
Эта функция создает карту теней:

unsigned __stdcall GenerateShadowMap(void* pd3dDevice)
{
	while (pd3dDevice)
	{
		// Acquire the lock before attempting to create the // shadow map.

		DWORD dwResult = 
WaitForSingleObject(g_hRenderMutex, INFINITE);

		// Create the shadow map here.
		// Release the lock after the shadow map is complete. 
ReleaseMutex(g_hRenderMutex);
		
// Make the thread sleep until the shadow map should
// be updated. The sleep time is specified in 
// milliseconds.
Sleep(g_iShadowMapUpdateMS);
	}
		_endthread();
	return 0;
}

Подобную блокировку необходимо предусмотреть для главной функции рендеринга.

Неофициальные тесты показали незначительное увеличение частоты кадров при запуске отдельного потока для создания растрового изображения теней при близких частотах обновления. Многопоточная версия обеспечивает повышенную частоту кадров и при синтетических нагрузках на процессор.


Точность вычислений
Информация о глубине цвета карты теней хранится в одном цветовом канале 32-разрядной текстуры. При создании карты теней необходимо ввести ограничения на значения данных, поскольку для их записи предназначен лишь один байт памяти. Так как глубина цвета варьируется в пределах от 0 до 1,0, ее точность в карте теней будет увеличиваться с уменьшением разности между предельными значениями. Рассчитывая освещение, увеличивайте значение смещения до тех пор, пока ошибки шейдера не перестанут влиять на организацию теней. Правильно выбранное смещение поможет избежать проблемы с самозатенением, которое может возникнуть, когда значения глубины цвета рассчитаны неточно или модель недостаточно детализирована, а шейдер рассчитал, что объект отбрасывает тень на самого себя (см. Рис. 5).



Рис. 5. Неправильное затенение из-за неточного сравнения значений глубины цвета, возникшее вследствие нескольких факторов: ограниченной емкости буфера, низкого разрешения модели и низкого разрешения карты теней.

Карта теней использует для хранения значений глубины цвета 8 бит из 32-х. Для хранения более точных значений используйте дополнительные биты.

float4 pack(float f, int iRange) 
{
	float4 fReturn = 0.0f;
	float fRange = iRange * 1.0f;
	int i = (int) (f * iRange);
	fReturn.r = i / fRange;
	f = (f - fReturn.r) * fRange;
	i = (int) (f * iRange);
	fReturn.g = i / fRange;
	f = (f - fReturn.g) * fRange;
	i = (int) (f * iRange);
	fReturn.b = i / fRange;
	fReturn.a = 0.0f;
	return fReturn;
}

float unpack(float4 f, int iRange)
{
	float fRange = iRange * 1.0f;
	return f.r + f.g / fRange + f.b / (fRange * fRange);
}



Рис. 6. Выходные данные из карты теней. Глубина цвета хранится в трех цветовых каналах (красный, зеленый, синий). Красный цветовой канал занимает 8 старших битов, зеленый – следующие 8 битов, синий – 8 младших битов.


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

Выше отмечалось, что преимуществом карты теней является их независимость от положения камеры, однако если плоскости камеры и источника света сильно отличаются, то шаблоны, записанные в карты теней, могут оказаться бесполезными. Один из методов решения этой проблемы заключается в создании карт теней в плоскости камеры4. Он позволяет снизить эффект ступенчатости благодаря тому, что размер шаблона карты теней более точно соответстует к размеру пиксельного шаблона, поскольку объекты, находящиеся вблизи камеры, требуют большей детализации в карте теней. Из-за необходимости перевода точек плоскости источника света в плоскость камеры применение перспективных карт теней повышает сложность расчетов затенения.

4 Марк Стаммингер (Marc Stamminger) и Джордж Дреттакис (George Drettakis). Perspective Shadow Maps (Перспективные карты теней). ACM Transaction on Graphics, SIGGRAPH 2002 г. Том 21, №3. Стр. 557—562.

Выводы
Карты теней являются эффективным и быстрым средством для создания затенения. Ограничения графических технологий Intel можно легко обойти с помощью управления сценой и простейших операциями в формате с плавающей запятой. Карты теней повышают реалистичность трехмерных приложений и несут важную для зрительного восприятия дополнительную информацию. К данной статье приложены примеры кода для реализации карт теней, которые можно использовать в проекте-примере EmptyProject из комплекта Microsoft DirectX* 9.0 для разработчиков.


Ссылки
Франклин С. Кроу. Алгоритмы построения теней в компьютерной графике. Computer Graphics, июль 1977 г. Том 11, №2. Стр. 242—248.

Ланс Уильямс. Отбрасывание теней на неровные поверхности. Computer Graphics. SIGGRAPH, 1978 г. Том 12, №3. Стр. 39—42.

Марк Стаммингер и Джордж Дреттакис. Перспективные карты теней. ACM Transaction on Graphics, SIGGRAPH, 2002 г. Том 21, №3. Стр. 557—562.


Об авторе
Дэвид Букаут – разработчик программного обеспечения, уже несколько лет занимается трехмерной графикой. Он трижды публиковал свои статьи в сети Intel® Software Network. Во время работы в Intel Architecture Labs он занимался технологией Shockwave3D*, разрабатывал пиксельные и вершинные шейдеры. В настоящее время он учится в аспирантуре по специальности "Вычислительная техника" в Университете последипломного образования штата Орегон и планирует поступать в магистратуру в мае 2005 г. Он также получил степень бакалавра в области английского языка Университета Пердью.


Post a comment If you have any questions, please contact our support team.