Własny silnik graficzny. Część III: teksturowanie.

30.11.2010 - Robert Kraus
TrudnośćTrudnośćTrudnośćTrudność

Proces teksturowania

Zwykle mówi się o teksturowaniu obiektu lub nakładaniu tekstury na obiekt, w rzeczywistości jednak obliczenia związane z teksturowaniem przypominają nakładanie obiektu na teksturę. Punkt leżący na obiekcie mapujemy na układ współrzędnych tekstury (będzie to kwadrat $ [0,1]^2 $).

Teksturowanie sfery

Reprezentacja sfery $ ({S}_k) $ w kartezjańskim układzie współrzędnych:
$ c \in R^3 $ - środek sfery o współrzędnych $ x_c, y_c, z_c $
$ r \in R_{+} $ - promień sfery

Reprezentacja sfery $ ({S}_s) $ w sferycznym (biegunowym) układzie współrzędnych:
$ \theta $ - kąt względem osi OX (tj. długość geograficzna)
$ \phi $ - kąt względem osi OY (tj. szerokość geograficzna)
$ r \in R_{+} $ - promień sfery
Więcej o współrzędnych sferycznych możesz przeczytać na wikipedii

Reprezentacje te są wzajemnie jednoznaczne. Niech $ (x, y, z) $ będzie punktem (w układzie kartezjańskim) na powierzchni sfery, a $ (\theta, \phi, r) $ będą jego współrzędnymi biegunowymi. Wtedy prawdziwa jest zależność:
x = $ r \cdot sin(\phi)cos(\theta) = r \cdot sin(\pi v)cos(2\pi u), gdzie \hspace{0.5cm} v = \frac{\phi}{\pi} (0.0 \leq v \leq 1.0)  $
z = $ r \cdot sin(\phi)sin(\theta) = r \cdot sin(\pi v)sin(2\pi u), gdzie \hspace{0.5cm} u = \frac{\theta}{2\pi} (0.0 \leq u \leq 1.0)  $
y = $ r \cdot cos(\phi) = r \cdot cos(\pi v)  $

$$ u = \frac{arccos(\frac{x}{r \cdot sin(\phi)})}{2\pi} $$

$$ v = \frac{arccos(\frac{y}{r})}{\pi} $$


W ten sposób otrzymaliśmy mapowanie $ {S}_k $ na $ [0, 1]^2 $ oraz mapowanie $ [0, 1]^2 $ na $ {S}_k $.

Mając współrzędne $ (x,y,z) $ możemy wyznaczyć współrzędne $ (u,v) $ stosując powyższe wzory. Musimy pamiętać, że aby użyć wzorów opisanych powyżej sefra musi mieć środek w początku układu współrzędnych. Gwarantujemy to poprzez odjęcie środka sfery od punktu na sferze, dla którego obliczamy współrzędne w przestrzeni tekstury. Dodatkowo trzeba uwzględnić w kodzie osobliwości we wzorach (miejsca, w których mogą wystąpić dzielenia przez 0 oraz miejsca, w których funkcja $ arccos $ jest nieokreślona).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// przekształcenie punktu z przestrzeni sfery na przestrzeń tekstury
inline vec2 mapSurfaceToTexture(sphere::i s, vec3::i p) {
  vec3 lp = p - s.center;
 
  float yDIVr = lp.y / s.radius;
  float v = 
    ( abs(yDIVr - 1.0f) < epsilon5 ) ? 0.0f : // v = 0 (górny biegun)
    ( abs(yDIVr + 1.0f) < epsilon5 ) ? 1.0f : // v = 1 (dolny biegun)
    acos(yDIVr) / Pi;                         // bez osobliwości
 
  float rMULsin = s.radius * sin(Pi*v);
  float u = // 1,2 -> biegun // 3,4 -> |x/rMULsin|=1 -> acos nieokreślony
    ( abs(v) < epsilon5 ) ? 0.0f :               // 1) (u,v) = (0.0, 0)
    ( abs(v - 1.0f) < epsilon5 ) ? 0.0f :        // 2) (u,v) = (0.0, 1)
    ( abs(lp.x - rMULsin) < epsilon5 ) ? 0.0f :  // 3) (u,v) = (0.0, v) 
    ( abs(lp.x + rMULsin) < epsilon5 ) ? 0.5f :  // 4) (u,v) = (0.5, v)
    acos(lp.x / rMULsin) / PiMul2;               // bez osobliwości
 
  return vec2(u, v);
}

Mając współrzędne $ (u,v) $ możemy również obliczyć współrzędne $ (x,y,z) $. Musimy jedynie pamiętać, że po zastosowaniu wzorów do wyniku należy dodać środek sfery.

1
2
3
4
5
6
7
8
// przekształcenie punktu z przestrzeni tekstury na przestrzeń sfery
inline vec3 mapTextureToSurface(sphere::i s, vec2::i p) {
  return vec3(
    s.center.x + s.radius * sin(Pi * p.v) * cos(PiMul2 * p.u),
    s.center.y + s.radius * cos(Pi * p.v),
    s.center.z + s.radius * sin(Pi * p.v) * sin(PiMul2 * p.u)
  );
}

Teksturowanie trójkąta

Trójkąt reprezentowany jest przez trójkę wierzchołków $ A $, $ B $, $ C $ $ \in \mathbb{R}^3 $. Dla wierzchołków tych musimy wyznaczyć punkty $ A' $, $ B' $, $ C' $ $ \in [0,1]^2 $, będące wierzchołkami naszego trójkąta w przestrzeni tekstury. Dokonujemy tego zgodnie z naszymi upodobaniami co to tego gdzie powinien się znajdować nasz trójkąt w przestrzeni tekstury.

Następną rzeczą jaką potrzebujemy jest przepis na mapowanie dowolnego punktu trójkąta $ ABC $ na odpowiadający mu punkt w trójkącie $ A'B'C' $. W tym celu wykorzystamy współrzędne barycentryczne. Niech $ (x, y, z) $ będą współrzędnymi kartezjańskimi punktu $ P $ leżącego na trójkącie $ ABC $. Obliczymy współrzędne $ (r, s, q) $ o właśności $ (x, y, z) = r \cdot A + s \cdot B + q \cdot C $, $ r + s + q = 1 $.

$$r = \frac{pole(PBC)}{pole(ABC)}$$
$$s = \frac{pole(APC)}{pole(ABC)}$$
$$q = \frac{pole(ABP)}{pole(ABC)}$$

Używając współrzędnych barycentrycznych możemy w identyczny sposób przekształcać współrzędne punktu na trójkącie pomiędzy przestrzenią trójwymiarową, a przestrzenią tekstury. Wystarczy jedynie zastosować je do wierzchołków trójkąta w odpowiedniej przestrzeni.

1
2
3
4
5
6
7
8
// przekształcenie punktu z przestrzeni trójkąta na przestrzeń tekstury
inline vec2 mapSurfaceToTexture(triangle::i t, vec3::i p) {
  float abc = area(t.v1 , t.v2 , t.v3);
  float   r = area(  p  , t.v2 , t.v3) / abc;
  float   s = area(t.v1 ,   p  , t.v3) / abc;
  float   q = 1.0f - r - s;
  return t.uv1*r + t.uv2*s + t.uv3*q;
}

1
2
3
4
5
6
7
8
// przekształcenie punktu z przestrzeni tekstury na przestrzeń trójkąta
inline vec3 mapTextureToSurface(triangle::i t, vec2::i p) {
  float abc = area(t.uv1 , t.uv2 , t.uv3);
  float   r = area(  p   , t.uv2 , t.uv3) / abc;
  float   s = area(t.uv1 ,   p   , t.uv3) / abc;
  float   q = 1.0f - r - s;
  return t.v1*r + t.v2*s + t.v3*q;
}

Poniżej pomocnicze w teksturowaniu funkcje obliczające pole trójkąta.

1
2
3
4
5
6
7
8
9
// pole trójkąta rozpiętego w przestrzeni 3d na wierzchołkach v1, v2, v3
// implementacja wzoru Herona
inline float area(vec3::i v1, vec3::i v2, vec3::i v3) {
  float a = length(v2 - v1);
  float b = length(v3 - v1);
  float c = length(v3 - v2);
  float p = (a + b + c) / 2.0f;
  return sqrt(p*(p-a)*(p-b)*(p-c));
}

1
2
3
4
5
6
7
8
9
// pole trójkąta rozpiętego w przestrzeni 2d na wierzchołkach v1, v2, v3
// implementacja wzoru Herona
inline float area(vec2::i v1, vec2::i v2, vec2::i v3) {
  float a = length(v2 - v1);
  float b = length(v3 - v1);
  float c = length(v3 - v2);
  float p = (a + b + c) / 2.0f;
  return sqrt(p*(p-a)*(p-b)*(p-c));
}

5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com