Część I
Część II
Część III
Część IV
Czym będziemy się zajmować?
Wyprowadzimy wzory na przecięcie promienia ze sferą i trójkątem.
Zaprogramujemy te wzory poznając pułapki jakie czekają na nas
z powodu ograniczonej precyzji liczb zmiennoprzecinkowych.
Na koniec dowiemy się nieco o tym,
co ma największy wpływ na szybkość działania naszego silnika graficznego.
Zaczniemy od kilku definicji
Punkty na trasie przebywanej przez promień
Dowolny punkt na trasie jaką pokonuje promień możemy opisać wzorać wzorem:
Przyjmijmy oznaczenia:
Definicja sfery i trójkąta
Sfera opisana jest przez punkt będący jej środkiem ciężkości,
promień oraz wskaźnik na materiał.
1
2
3
4
5
6
7
8
| struct sphere {
typedef const sphere& i;
vec3 center; // środek sfery
float radius; // promień sfery
material* mat; // wskaźnik na materiał
// konstruktory
}; |
Trójkąt naturalnie opisujemy poprzez trzy wierzchołki.
Dodatkowo przechowujemy położenie wierzchołków trójkąta w przestrzeni tekstury oraz
równanie płaszczyzny, na której leży trójkąt oraz wskaźnik na materiał.
1
2
3
4
5
6
7
8
9
10
11
12
| // trójkąt
struct triangle {
typedef const triangle& i; // input ref
vec3 v1, v2, v3; // wierzchołki trójkąta
vec2 uv1, uv2, uv3; // wierzchołki trójkąta w przestrzeni tekstury
vec3 N; // wektor normalny do płaszczyzny trójkąta
float d; // równanie płaszczyzny: dotProd(N,(x,y,z)) + d = 0
material* mat; // wskaźnik na materiał
// konstruktory
}; |
Przy okazji zobaczmy jak wyznaczać wektory normalne do naszych prymitywów geometrycznych.
Wektor normalny w dowolnym punkcie na sferze można łatwo obliczyć
odejmując od punktu środek sfery. Wektor ten można też taniej
znormalizować - wystarczy go podzielić przez promień sfery.
1
2
| inline vec3 normal(sphere::i s, vec3::i p)
{ return (p - s.center) / s.radius; } |
Wektor normalny do powierzchni trójkąta mamy zapamiętany w trójkącie,
więc go po prostu wyciągamy.
1
2
| inline vec3 normal(triangle::i t)
{ return t.N; } |
Definicja struktury zawierającej informacje o zderzeniu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| struct hit {
float t; // odległość trafienia od źródła promienia
vec3 ip; // punkt przecięcia
vec3 N; // wektor normalny do powierzchni trafionego obiektu
vec3 Ex; // wektor na osi X w przestrzeni rozpiętej na wektorach Ex, N, Ez
vec3 Ez; // wektor na osi Z w przestrzeni rozpiętej na wektorach Ex, N, Ez
vec2 uv; // współrzędne trafienia w przestrzeni tekstury
material* mat; // wskaźnik na materiał
hit() : t(undefined) {}
hit(sphere::i S, float t, ray::i r) : t(t) {
if (t == undefined) return;
ip = r.origin + r.aheadDir * t; // punkt przecięcia
mat = S.mat; // wskaźnik na materiał
N = normal(S, ip); // normalna do powierzchni
if ( ! mat -> hasTextures ) return;
uv = mapSurfaceToTexture(S, ip); // punkt ip w przestrzeni tekstury
if ( ! mat -> hasNormalMap ) return;
Ex = mapTextureToSurface(S, vec2(uv.u + epsilon3, uv.v)) - ip;
Ex = unitise(Ex);
Ez = mapTextureToSurface(S, vec2(uv.u, uv.v + epsilon3)) - ip;
Ez = unitise(Ez);
}
hit(triangle::i T, float t, ray::i r) : t(t) {
if (t == undefined) return;
ip = r.origin + r.aheadDir * t; // punkt przecięcia
mat = T.mat; // wskaźnik na materiał
N = normal(T); // normalna do powierzchni
if ( ! mat -> hasTextures ) return;
uv = mapSurfaceToTexture(T, ip); // punkt ip w przestrzeni tekstury
if ( ! mat -> hasNormalMap ) return;
Ex = mapTextureToSurface(T, vec2(uv.u + epsilon3, uv.v)) - ip;
Ex = unitise(Ex);
Ez = mapTextureToSurface(T, vec2(uv.u, uv.v + epsilon3)) - ip;
Ez = unitise(Ez);
}
}; |