Version: 2018.1
Размер усечённой пирамиды на данном расстоянии от камеры
Лучи из камеры

Dolly Zoom (эффект “Trombone”)

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

Объект, который точно помещается в пирамиду видимости (frustum ) вертикально, будет занимать всю высоту экрана на сцене. Это верно для любого расстояния от объекта до камеры и для любого угла обзора, главное, чтобы объект точно помещается в пирамиду видимости вертикально. Только этот объект будет казаться неизменным, хотя все остальное поменяет как размер и расстояние, так и угол обзора. На этом базируется эффект dolly zoom.

Чтобы воссоздать эффект, нужно измерить высоту основания пирамиды видимости (frustum) (основание совпадает с позицией объекта съемки) в начале изменения фокусного расстояния (zoom). После, когда камера начинает двигаться, нужно подобрать угол обзора, так чтобы высота оставалась неизменной. Этого можно добиться следующим кодом:-

using UnityEngine;
using System.Collections;

public class ExampleScript : MonoBehaviour {
    public Transform target;
    public Camera camera;
    
    private float initHeightAtDist;
    private bool dzEnabled;

    // Calculate the frustum height at a given distance from the camera.
    void FrustumHeightAtDistance(float distance) {
        return 2.0f * distance * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
    }

    // Calculate the FOV needed to get a given frustum height at a given distance.
    void FOVForHeightAndDistance(float height, float distance) {
        return 2.0f * Mathf.Atan(height * 0.5f / distance) * Mathf.Rad2Deg;
    }

    // Start the dolly zoom effect.
    void StartDZ() {
        var distance = Vector3.Distance(transform.position, target.position);
        initHeightAtDist = FrustumHeightAtDistance(distance);
        dzEnabled = true;
    }
    
    // Turn dolly zoom off.
    void StopDZ() {
        dzEnabled = false;
    }
    
    void Start() {
        StartDZ();
    }

    void Update () {
        if (dzEnabled) {
            // Measure the new distance and readjust the FOV accordingly.
            var currDistance = Vector3.Distance(transform.position, target.position);
            camera.fieldOfView = FOVForHeightAndDistance(initHeightAtDist, currDistance);
        }
        
        // Simple control to allow the camera to be moved in and out using the up/down arrows.
        transform.Translate(Input.GetAxis("Vertical") * Vector3.forward * Time.deltaTime * 5f);
    }
}

C# script example

var target: Transform;

private var initHeightAtDist: float;
private var dzEnabled: boolean;


// Calculate the frustum height at a given distance from the camera.
function FrustumHeightAtDistance(distance: float) {
    return 2.0 * distance * Mathf.Tan(camera.fieldOfView * 0.5 * Mathf.Deg2Rad);
}


// Calculate the FOV needed to get a given frustum height at a given distance.
function FOVForHeightAndDistance(height: float, distance: float) {
    return 2 * Mathf.Atan(height * 0.5 / distance) * Mathf.Rad2Deg;
}


// Start the dolly zoom effect.
function StartDZ() {
    var distance = Vector3.Distance(transform.position, target.position);
    initHeightAtDist = FrustumHeightAtDistance(distance);
    dzEnabled = true;
}


// Turn dolly zoom off.
function StopDZ() {
    dzEnabled = false;
}


function Start() {
    StartDZ();
}


function Update () {
    if (dzEnabled) {
        // Measure the new distance and readjust the FOV accordingly.
        var currDistance = Vector3.Distance(transform.position, target.position);
        camera.fieldOfView = FOVForHeightAndDistance(initHeightAtDist, currDistance);
    }
    
    // Simple control to allow the camera to be moved in and out using the up/down arrows.
    transform.Translate(Input.GetAxis("Vertical") * Vector3.forward * Time.deltaTime * 5);
}

JS script example

Размер усечённой пирамиды на данном расстоянии от камеры
Лучи из камеры