Кругом сплошной обман

«Эффект Хитрого койота» в игре и его создание.

Шотландский инди-разработчик Мэтт Старк в своём Твиттере разместил видео, демонстрирующие то, что сам он назвал «эффектом Хитрого койота», в честь персонажа мультсериала Warner Bros. В нём койот, преследующий кукушку-подорожника по имени «Дорожный бегун», иногда врезался в стену, на которой был нарисован, например, сквозной туннель. Примерно такой же обман происходит и в ролике Старка. В своём блоге разработчик рассказал, как работает этот эффект.

В каждом дверном проёме на локации находится скрытая стена, которая становится видимой, когда персонаж игрока запускает триггер. В то же время, камера генерирует текстуру и применяет её к появившейся стене. Вот так это выглядит со стороны.

Чтобы эффект работал вне зависимости от того, куда смотрит сам игрок, разработчик добавил «временную камеру». Она находится там же, где и персонаж пользователя, но всегда направлена в на объект, который будет меняться. Подходящий угол поля зрения выбирается через итерации по углам ограничивающего параллелепипеда.

float maxAngle = 0; Vector3 min = meshFilter.sharedMesh.bounds.min; Vector3 max = meshFilter.sharedMesh.bounds.max; // Iterate through each of the 8 corners of the bounding box foreach (float bx in new float[] { min.x, max.x }) { foreach (float by in new float[] { min.y, max.y }) { foreach (float bz in new float[] { min.z, max.z }) { // Get the corner's position in camera space Vector3 cornerInCameraSpace = cam.transform.InverseTransformPoint(transform.TransformPoint(new Vector3(bx, by, bz))); // Find the horizontal and vertical angles between the camera's forward vector and the corner's position float horizontalAngle = Mathf.Abs(Mathf.Atan(cornerInCameraSpace.x / cornerInCameraSpace.z)); float verticalAngle = Mathf.Abs(Mathf.Atan(cornerInCameraSpace.y / cornerInCameraSpace.z)); // If either angle is greater than the stored value, replace it maxAngle = Mathf.Max(maxAngle, horizontalAngle, verticalAngle); } } } // Set the camera's field of view based on maxAngle. MaxAngle is in radians so must be converted to degrees. Maxangle also only represents the angle between forward and the edge of the view, but field of view is the angle between the top and bottom of the view, so it must be multiplied by two. cam.fieldOfView = maxAngle * Mathf.Rad2Deg * 2;

После этого камера рендерит нужную текстуру, однако важно правильно разместить её на объекте. Если сделать это с помощью UV-координат, то результат получится таким.

Кругом сплошной обман

Старк проецирует позиции вершин объекта в экранное пространство «временной камеры». Они используются для создания текстуры.

Перед удалением «временной камеры» просчитывается матрица, которая конвертирует значения по мировой системе координат в систему координат отсечения.

camMatrix = cam.projectionMatrix * cam.worldToCameraMatrix;

Эта матрица передаётся шейдеру вместе с текстурой. Позиции вершин проецируются сперва по мировой системе, а затем по системе отсечения «временной камеры».

// Project the vertex into world space float3 worldPos = mul(unity_ObjectToWorld, v.vertex); // Project the world space position into the temporary camera's clip space o.clipPos = mul(_WorldToCam, float4(worldPos, 1));

Во фрагментарном шейдере позиция по системе отсечения конвертируется в UV-координаты, которые используются для наложения текстуры.

// Get the UV coordinate float2 uv = i.clipPos.xy / i.clipPos.w; uv = (uv + float2(1, 1)) / 2; // Convert it from the range -1 to 1 to the range 0 to 1 // Sample the texture fixed4 col = tex2D(_CamTex, uv);
Кругом сплошной обман
384384
53 комментария

круть, игра Antichamber почти про такое

26

Да там вообще круто сделали

3

Особенно комната с звуками сверчков.

1

Винтажный мем, который полностью описывает мою степень недоумения.

14

Раскукож обратно мою перспективу, проклятый демон!

16

К тому и отсылался.

1