Gå til indholdet

Tutorial 4: Perspektivisk Projektion

Perspektivisk projektion er en teknik inden for 3D-grafik, der bruges til at skabe en illusion af dybde på en 2D-skærm. Metoden simulerer, hvordan objekter ser mindre ud, jo længere de er fra kameraet. Denne tutorial forklarer, hvordan 4×4 matricer benyttes til at implementere perspektivisk projektion, og gennemgår de underliggende principper og matematikken bag.


Grundlæggende Begreber

  • Definition: Perspektivisk projektion er en metode til at projicere 3D-punkter på et 2D-plan. Her mødes projektionslinjerne (linjerne fra de originale 3D-punkter til de projicerede 2D-punkter) i et enkelt punkt, kaldet projektionscenteret (center of projection).

  • Perspektivisk Forkortning (Foreshortening):
    Objekter, der er længere væk fra projektionsplanet, vises mindre, hvilket efterligner, hvordan det menneskelige øje opfatter dybde.

  • Pinhole Kamera-model:
    En simpel model til at forklare perspektivisk projektion. Et pinhole-kamera består af en kasse med et lille hul, hvorigennem lysstråler passerer og projiceres på den modsatte side – billedet bliver inverteret. Her fungerer pinhole’et som projektionscenter.

  • Sammenligning med Ortografisk Projektion:

    • Ortografisk Projektion: Projektionslinjerne er parallelle, og objekters størrelse forbliver konstant uanset afstand.
    • Perspektivisk Projektion: Projektionslinjerne konvergerer mod et enkelt punkt, hvilket medfører, at objekter, der er længere væk, bliver forminskede.
Ortografisk Projektion

Ortografisk Projektion


Perspektivisk Projektion

Perspektivisk Projektion


Geometrien bag Perspektivisk Projektion

Vi antager et 3D-koordinatsystem med:

  • Origo: Placeret i projektionscenteret.
  • z-aksen: Vinkelret på projektionsplanet.
  • x- og y-akser: Parallelle med projektionsplanet.

Projektionsplanet er placeret i en afstand \(d\) fra origo langs z-aksen. Ligningen for planet er:

\[ z = d. \]

Det betyder, at jo større \(z\)-værdien (dvs. afstanden fra origo) er, desto mere "komprimeres" billedet, og derfor bliver objekterne mindre.

Den tættest på fremstår størrre på planet


Perspektivprojektion fra origo til planerne (rækkevektorer)

Nedenfor er de tre \(4\times 4\)-matricer (for rækkevektorer) til at lave en perspektivprojektion fra origo ned på henholdsvis planerne

  1. \(x = d\)
  2. \(y = d\)
  3. \(z = d\).

Fælles idé:

  • Man ønsker, at et vilkårligt punkt \(\bigl(x,y,z\bigr)\) “skydes” langs linjen fra \((0,0,0)\) igennem \((x,y,z)\) til skæringen med planet.
  • I homogene koordinater \([x\;\;y\;\;z\;\;1]\) skal resultatet af matrixmultiplikationen (før division) være sådan, at når man dividerer de første tre koordinater med den fjerde, fås netop skæringspunktet på planet.

Projektion på \(x = d\)

\[ M_{x=d} =\begin{bmatrix} 1 & 0 & 0 & \frac{1}{d}\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 \end{bmatrix}. \]

Projektion på \(y = d\)

\[ M_{y=d} =\begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & \frac{1}{d}\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 \end{bmatrix}. \]

Projektion på \(z = d\)

\[ M_{z=d} =\begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & \frac{1}{d}\\ 0 & 0 & 0 & 0 \end{bmatrix}. \]

Eksempel

Lad os sige, at \(z = d = 2\). Vi har et punkt:

\[ \mathbf{p} = \begin{bmatrix} 4 & 3 & 8 \end{bmatrix}. \]

Multiplicer med \(\mathbf{P}\):

\[ \begin{bmatrix} 4 & 3 & 8 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & \frac{1}{2} \\ 0 & 0 & 0 & 0 \end{bmatrix} = \begin{bmatrix} 4 & 3 & 8 & \frac{8}{2} \end{bmatrix} = \begin{bmatrix} 4 & 3 & 8 & 4 \end{bmatrix}. \]

Efter homogen deling:

\[ x_{\text{proj}} = \frac{4}{4} = 1,\quad y_{\text{proj}} = \frac{3}{4} = 0.75,\quad z_{\text{proj}} = \frac{8}{4} = 2. \]

Det projicerede punkt bliver således \((1,\,0.75,\,2)\).


💻 Python

Her er et eksempel i Python, som demonstrerer oprettelsen af en perspektivisk projektionmatrix (på \(z=d\)) og anvendelsen på et 3D-punkt:

import numpy as np

def perspective_projection_matrix(d):
    """
    Returnerer en 4x4 perspektivisk projektionmatrix for rækkevektorer.
    d: Afstanden til projektionsplanet.
    """
    P = np.array([[1, 0, 0, 0],
                  [0, 1, 0, 0],
                  [0, 0, 1, 1/d],
                  [0, 0, 0, 0]])
    return P

# Afstand til projektionsplanet
d = 2

# Perspektivisk projektionmatrix
P = perspective_projection_matrix(d)
print("Perspektivisk projektionmatrix:\n", P)

# Eksempelpunkt (rækkevektor notation)
p = np.array([4, 3, 8, 1])
print("\nOriginalt punkt:\n", p)

# Anvend projektion
p_proj = p @ P
print("\nPunkt efter projektion (før homogen deling):\n", p_proj)

# Homogen deling (hvor w = z/d)
if p_proj[3] != 0:
    p_ndc = p_proj / p_proj[3]
else:
    p_ndc = p_proj
print("\nProjiceret punkt efter homogen deling (NDC):\n", p_ndc)

Forklaring af koden:

  • Funktionen perspective_projection_matrix:
    Denne funktion opretter en 4×4 matrix, der projekterer et 3D-punkt til clip space, så \(w = \frac{z}{d}\).

  • Anvendelse af matricen:
    Punktet \(p\) multipliceres med matrixen, hvorefter vi udfører homogen deling for at få de endelige Normalized Device Coordinates (NDC).


Vigtige Overvejelser

  • Matrixkonkatenering:
    Perspektivisk projektion kan kombineres med andre transformationer (rotation, skalering, translation) ved at multiplicere de respektive \(4 \times 4\) matricer sammen.

  • Homogen deling:
    Det er afgørende at huske på den homogene deling efter projektionen, da den konverterer det 4D punkt til et 3D punkt (eller 2D for skærmprojektion).

  • Normalisering af \(w\):
    I mange grafiske systemer normaliseres \(w\)-værdien, så den passer til den forventede dybdeværdi, f.eks. i intervallet \([-1, 1]\) eller \([0, 1]\).

  • Field of View (FOV):
    I mere avancerede perspektiviske projektioner (f.eks. de, der bruges i spil og grafik-API’er) inkluderer projektionmatricen også skalering af \(x\) og \(y\) i henhold til kameraets synsfelt og aspektforhold.

  • Clip Matrix:
    Ofte omtales projektionmatricen også som en "clip matrix", fordi den forbereder punkterne til clipping i grafikpipeline-processen.