Heat haze

Сделал эффект “искажения пространства”, похожий на марево от жары – думаю применить его где-нибудь в игре:

Новые скриншоты

Несколько свежих скринов со всеми эффектами, которые используются в Atrium (bump/parallax mapping + glow + тени):

Эффект затенения

«Честные» динамические тени в аркадных 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;
}