Эффект затенения
«Честные» динамические тени в аркадных 3D-играх не всегда бывают уместны. Как правило, разработчики ограничиваются статическими предрассчитанными тенями от неподвижных объектов и простым темным кружочком на земле под персонажем – дешево и сердито =) Я решил дополнить этот нехитрый метод одной простой, но важной деталью: изменение яркости персонажа в зависимости от его местоположения – в тени или на свету.
Цвет для яркости будет считываться из карты освещения, в которой «запечены» все статические тени на карте.
Допустим, у вас есть некий блок кода, в котором вы находите точку на поверхности полигона под ногами персонажа:
// Проверяем факт пересечения
IntrStatus istatus = character.downRay.intersectTriangle(tri);
if (istatus.hit)
{
// Извлекаем точку пересечения
Vector3f ipt = istatus.intersectionPoint;
if (ipt.y > currentFloorHeight)
{
currentFloorHeight = ipt.y;
// Берем материал полигона
Material mat = materialByIndex[tri.matIndex];
// Если нет карты освещения (текстура 1), то ничего не делаем
if (mat.textures[1])
{
// Берем изображение карты освещения
Image lightmap = imageByIndex[mat.textures[1].imgIndex];
// Находим текстурные координаты точки пересечения
Vector2f tc = triObjSpaceToTexSpace(tri.vertices, tri.texCoords2, ipt);
// Конвертируем текстурные координаты в дискретные
uint imgX = cast(uint)(tc.x * lightmap.width - 0.5f);
uint imgY = cast(uint)(tc.y * lightmap.height - 0.5f);
// Считываем цвет пикселя c карты освещения
Color lumel = lightmap[imgX, imgY];
// Применяем полученный цвет к материалу персонажа
chMaterial.ambientColor = lumel;
chMaterial.diffuseColor = lumel;
chMaterial.specularColor = lumel;
}
}
}
Осталось определить функцию triObjSpaceToTexSpace. Она будет использовать барицентрические координаты:
Vector2f triObjSpaceToTexSpace(
Vector3f[3] vertices,
Vector2f[3] texCoords,
Vector3f point)
{
Vector3f v0 = vertices[2] - vertices[0];
Vector3f v1 = vertices[1] - vertices[0];
Vector3f v2 = point - vertices[0];
float dot00 = dot(v0, v0);
float dot01 = dot(v0, v1);
float dot02 = dot(v0, v2);
float dot11 = dot(v1, v1);
float dot12 = dot(v1, v2);
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
Vector2f t2 = texCoords[1] - texCoords[0];
Vector2f t1 = texCoords[2] - texCoords[0];
return texCoords[0] + t1 * u + t2 * v;
}