Developedia
Developedia
Virtuális kamerák

Virtuális kamerák

A virtuális kamerákat a számítógépes grafikában arra használjuk, hogy azok “szemén keresztül” ránézzünk a virtuális világra. Ezek a kamerák egy pixelgrafikus szeletet készítenek ezen virtuális világból, ez a szelet az, amit mi, mint felhasználók a monitoron látunk megjelenni.

Unity-ben egy virtuális kamerát úgy hozunk létre, hogy egy Camera komponenst adunk egy GameObject-hez. Ha csak egy ilyen kamera van jelen a Scene-ben, akkor annak képét fogja automatikusan megjeleníteni a motor. Több kamerás rendszerekkel egyelőre nem foglalkozunk.

Virtuális kamerák típusai

A virtuális világ megjelenítésére szolgáló kameráknak alapvetően 2 fajtája van:

  • Perspektivikus kamera
image
  • Ortografikus kamera
image

A perspektivikus kamerák a valós világból megszokott perspektív hatással jelenítik meg a virtuális világot. Ez azt jelenti, hogy egy objektum kamerától való távolságával arányosan a mérete is csökken. Ami messzebb van, azt kisebbnek látjuk. Ilyen egyszerű.

Perspektív kameranézet
Perspektív kameranézet
Tomb Raider (1996) Perspektív kameranézet
Tomb Raider (1996) Perspektív kameranézet

Ezzel szemben az ortografikus kamerán keresztül nézve a virtuális világra, ami közelebb van az nem tűnik nagyobbnak, mint a távolabbi verziói. A 3D térben futó párhuzamos vonalak az ortografikus kamera által renderelt képen is párhuzamosak lesznek.

Kamerák beállításai

A Unity Camera komponens egyik beállítása a “Projection”. Itt tudunk választani az Ortografikus és a Perspektív nézet közt.

Emellett természetesen sokat számít, hogy az adott kamera a térben hol helyezkedik el és mi az orientációja, (merre néz). Ezeket az információkat azonban nem a kamera, hanem a Transform komponens tartalmazza, ami minden GameObject-en kötelezően jelen van:

Lásd: Unity Engine alapjai és logikájaUnity Engine alapjai és logikája

Az ortografikus kamerát például gyakran használják úgy, hogy tökéletes felül- vagy oldalnézetből mutatja a világot. Ezt a transform komponens megfelelő beállításával érik el: (Pozicionálás forgatás)

Ortografikus -  Felülnézeti
Ortografikus - Felülnézeti
Ortografikus - Oldalnézeti
Ortografikus - Oldalnézeti

Egy másik jellemző módja az ortografikus kamera felhasználásának az izometrikus nézet. Ebben az esetben a függőleges tengely mentén 45 fokkal van elforgatva a kamera, a vízszintes tengelyen pedig körül belül 35-tel. Ezen értékek nem kőbe vésettek, játékonként részben eltérhetnek, úgy hogy azok nézete még mindig izometrikusnak nevezhető.

Izometrikus kameranézet (ortogrfikus)
Izometrikus kameranézet (ortogrfikus)
Hades (2018): Izometrikus kameranézet (ortogrfikus)
Hades (2018): Izometrikus kameranézet (ortogrfikus)

A virtuális kamerák mindig egy térrészt vágnak ki a világból. A valódi kamerákkal szemben egy virtuális kamerának mindig van egy legközelebbi és legtávolabbi távolsága, amit belát.

A kamerához legközelebbi látható síkot elülső, a legtávolabbi síkot pedig hátulsó vágósíknak hívjuk.

Ezáltal az ortografikus kamera egy téglatestet vág ki a térből, perspektív pedig egy csapott tetejű piramist. Ezutóbbi alakzat matematikai neve, Frusztum.

image

Ha egy test vagy annak csak bizonyos pontjai ezen alakzaton kívül helyezkednek el, akkor biztosak lehetünk benne, hogy nem kerülnek kirajzolásra.

Egy ortografikus kamerát az elülső és hátulsó vágósík távolságán kívül még az általa belátott magasság és szélesség ír le. Ezek adják meg a belátott téglatest dimenzióit. Ezzel szemben perspektivikus kamerát a vágósíkok távolságával és egy vízszintes és függőleges szög értékkel a nyílásszöggel, azaz Field of View-val írhatunk le.

Unity alatt perspektív kameránál mindig a függőleges nyílásszöget, ortografikus kameránál pedig a függőleges magasságot tudjuk csak állítani. A vízszintes nyílásszöget vagy ortografikus szélességet a játékot megjelenítő ablak képarányából és a függőleges értékekből számolja ki automatikusan a játékmotor.

(Unity kameránál perspektív esetben választhatjuk azt a beállítást is, hogy a vízszintes nyílásszöget állítjuk be. Ám ez ne tévesszen meg senkit! Ilyenkor is a függőlegeset menti el a Unity és amint változik a megjelenített képarány, egyből módosul a vízszintes nyílásszög vele együtt. Továbbra is a függőleges szög lesz állandó.)

A virtuális kamera nézetét tehát a következő beállítások írnak le:

Transform komponensen:

  • Pozíció - Position
  • Orientáció (elforgatás) - Rotation

Camera komponensen:

  • Projekció: Projection - Ortographic / Perspective
  • Elülső vágósík - Near clipping plane
  • Hátulsó vágósík - Far clipping plane

Ha Ortografikus a kamera:

  • Függőleges magasság - Size (Unity-ben fél magasságot kell beállítani)
  • Vízszintes szélesség: Automatikusan számolódik ki a képarány alapján.

Ha Perspektív a kamera:

  • Függőleges nyílásszög - Field of View
  • Vízszintes nyílásszög: Automatikusan számolódik ki a képarány alapján.

Ezen kívül több fontos beállítást tartalmaz egy Unity kamera, de ezeket itt most nem tárgyaljuk részletesen. Javaslom mindenkinek, hogy bátran kísérletezzen velük.

Vízszintes adatok beállása

Ha perspektív kamera a vízszintes látószögét vagy ortografikus kamera vízszintes mértét akarjuk fixre beállítani arra nincs kész lehetőség Unity-ben. Ehhez saját szkriptet kell írnunk ami mindig kiszámolja a függőleges értéket egy beállított vízszintes érték és a képarány alapján és ez alapján frissíti a kamerát folyamatosan.

Ezt teszi az alábbi kódrészlet, ami egy az egyben felhasználható bármilyen Unity projektben:

Logo

Főoldal

Blog

Elmélet

3D Studio

Adatvédelmi nyilatkozat

GY.I.K.

Házirend

Szerző: Marosi Csaba / marosi.csaba@3d-studio.hu

DiscordGitHubLinkedIn
using System;
using UnityEngine;

// A kamera méretét szabályozó komponens, amely lehetővé teszi a kamera látószögének fixálását a vízszintes irányban is.
// Perspektív és ortográfikus kamerák is támogatott.

[ExecuteAlways]   // Azért kell, hogy az Editor időben is működjön
[RequireComponent(typeof(Camera))]   // Kamera komponens kötelező
public class CameraSizeController : MonoBehaviour
{
    // Referencia látószög perspektív kamera esetén
    [SerializeField] float referenceVerticalFieldOfView = 60;
    [SerializeField] float referenceHorizontalFieldOfView = 60;

    [Space]
    // Referencia ortográfikus mérte kamera esetén
    [SerializeField] float referenceHorizontalOrthographicSize = 10;
    [SerializeField] float referenceVerticalOrthographicSize = 10;

    [Space]
    // Milyen mértékben legyen fix a vízszintes látószög vagy ortográfikus méret
    [SerializeField, Range(0, 1)] float fixHorizontal;

    new Camera camera;

    Vector2Int lastScreenSize = Vector2Int.zero;  // Legutóbbi képernyő méret

    void Awake() => UpdateCamera();   // Awake-ben végrehajtjuk a beállítást, ...
    void Update() => UpdateCamera();  // ...és minden Update-ben is.

    void UpdateCamera()
    {
        if (camera == null)
            camera = GetComponent<Camera>();  // Kamera komponens lekérése, ha szükséges

        Vector2Int screenSize = new Vector2Int(Screen.width, Screen.height);  // Képernyő méret

        if (screenSize == lastScreenSize)   // Ha nem változott a képernyő felbontása, akkor nincs semmi dolgunk
            return;

        lastScreenSize = screenSize;        // Eltároljuk a legutóbbi felbontást

        if (camera.orthographic)            // Ha ortográfikus kamera
            UpdateOrthographicCamera();
        else                                // Ha perspektív kamera
            UpdatePerspectiveCamera();

    }
    
    // Ortográfikus kamera frissítése
    void UpdateOrthographicCamera()
    {
        if (fixHorizontal <= 0)     // Ha a függőleges érték fix, akkor nincs sok dolgunk
        {
            camera.orthographicSize = referenceVerticalOrthographicSize;
            return;
        }
        
        // A vízszintes méret alapján számolt függőleges méret
        float verticalOrthographicSizeBasedOnHorizontal = referenceHorizontalOrthographicSize / camera.aspect;   // A vízszintes méret alapján számolt vertikális méret

        if (fixHorizontal >= 1)
            camera.orthographicSize = verticalOrthographicSizeBasedOnHorizontal;   // Ha teljsen fix a vízszintes látószög
        else
            camera.orthographicSize = Mathf.Lerp(referenceVerticalOrthographicSize, verticalOrthographicSizeBasedOnHorizontal, fixHorizontal);  // Interpolálás (Súlyozott átlagolás)

    }

    // Perspektív kamera frissítése
    void UpdatePerspectiveCamera()
    {
        if (fixHorizontal <= 0)   // Ha a függőleges érték fix, nincs sok dolgunk.
        {
            camera.fieldOfView = referenceVerticalFieldOfView;
            return;
        }
        
        // A vízszintes látószög alapján számolt függőleges látószög
        float verticalFieldOfViewBasedOnHorizontal = Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * referenceHorizontalFieldOfView / 2) / camera.aspect) * 2 * Mathf.Rad2Deg;

        if (fixHorizontal >= 1)
            camera.fieldOfView = verticalFieldOfViewBasedOnHorizontal;  // Ha teljsen fix a vízszintes látószög
        else
            camera.fieldOfView = Mathf.Lerp(referenceVerticalFieldOfView, verticalFieldOfViewBasedOnHorizontal, fixHorizontal); // Interpolálás (Súlyozott átlagolás)
    }

}