2011-10-07 15 views
68

Czy są tam jakieś tutoriale wyjaśniające, w jaki sposób mogę narysować kulę w OpenGL bez konieczności korzystania z gluSphere()?Rysowanie sferyczne w OpenGL bez użycia gluSphere()?

Wiele samouczków 3D dla OpenGL znajduje się tylko na kostkach. Przeszukałem, ale większość rozwiązań do rysowania kuli ma używać gluSphere(). Istnieje również strona, która ma kod do rysowania kuli przy this site, ale nie wyjaśnia ona matematyki za rysowaniem kuli. Mam też inne wersje tego, jak narysować kulę w wielokącie zamiast quadów w tym łączu. Ale znowu nie rozumiem, w jaki sposób sfera jest narysowana za pomocą kodu. Chcę móc wizualizować, aby w razie potrzeby zmodyfikować kulę.

+2

wyszukać współrzędne sferyczne dla wyjaśnienia matematycznego (w szczególności przekształcenie ze współrzędnych sferycznych na współrzędne kartezjańskie). –

Odpowiedz

222

Jednym ze sposobów, w jaki można to zrobić, jest rozpoczęcie od bryły platońskiej o trójkątnych bokach - na przykład octahedron. Następnie wziąć każdy trójkąt i rekursywnie podzielić go na mniejsze trójkąty, tak jak poniżej:

recursively drawn triangles

Gdy masz wystarczającą ilość punktów, to unormować ich wektory tak, że wszystkie są w stałej odległości od centrum ciała stałego. Powoduje to, że boki wybrzuszają się do kształtu przypominającego kulę, ze wzrostem gładkości w miarę zwiększania liczby punktów.

Normalizacja oznacza tutaj przesuwanie punktu tak, aby jego kąt w stosunku do innego punktu był taki sam, ale odległość między nimi jest różna. Oto dwuwymiarowy przykład.

enter image description here

A i B są 6 jednostek siebie. Ale załóżmy, że chcemy znaleźć punkt na linii AB, który jest 12 jednostek od A.

enter image description here

Można powiedzieć, że C jest znormalizowana forma B względem A, z odległością 12. Możemy uzyskania C z kodem tak:

#returns a point collinear to A and B, a given distance away from A. 
function normalize(a, b, length): 
    #get the distance between a and b along the x and y axes 
    dx = b.x - a.x 
    dy = b.y - a.y 
    #right now, sqrt(dx^2 + dy^2) = distance(a,b). 
    #we want to modify them so that sqrt(dx^2 + dy^2) = the given length. 
    dx = dx * length/distance(a,b) 
    dy = dy * length/distance(a,b) 
    point c = new point 
    c.x = a.x + dx 
    c.y = a.y + dy 
    return c 

Jeśli robimy ten proces normalizacji na dużo punktów, wszystkie w odniesieniu do tego samego punktu a iz tą samą odległość R, a następnie znormalizowane punkty zostaną wszystkie leżą na łuku koła o środku A i promieniu R.

bulging line segment

Tutaj czarne punkty zaczynają się na linii i "wybrzuszają się" w łuk.

Proces ten można rozszerzyć na trzy wymiary, w którym to przypadku otrzymujemy kulę, a nie okrąg. Wystarczy dodać komponent dz do funkcji znormalizowanej.

normalized polygons

level 1 bulging octahedron level 3 bulging octahedron

Jeśli spojrzeć na sferze w Epcot można zobaczyć ten rodzaj techniki w pracy. jest dwunastościanem z wybrzuszonymi twarzami, aby wyglądał na bardziej okrągły.

+1

Wolę usunąć link do sfery Epcot. Może wprowadzać w zakłopotanie początkujących, ponieważ każdy trójkąt jest ponownie podzielony na trzy trójkąty równoramienne (podobne do pierwszej części sqrt (3) - podległej). Jestem pewien, że znajdziesz lepszy przykład. –

+0

Mam ładne wdrożenie tego na moim komputerze domowym. Z przyjemnością edytuję niektóre zrzuty ekranu po pracy. – Kevin

+0

Dzięki za pomysł. Ale nie rozumiem, w jaki sposób, normalizując wektory, mogłem wypchnąć boki do kształtu przypominającego kulę? Jak wypychać boki? – Carven

2

Kod w próbce zostanie szybko wyjaśniony. Powinieneś zajrzeć do funkcji void drawSphere(double r, int lats, int longs).Parametry lat określają, ile linii poziomych chcesz mieć w swojej sferze i ile pionowych linii. r to promień twojej sfery.

Teraz jest podwójna iteracja nad lat/lon, a współrzędne wierzchołka są obliczane za pomocą prostej trygonometrii.

Obliczone wierzchołki są teraz wysyłane do procesora graficznego przy użyciu glVertex...() jako GL_QUAD_STRIP, co oznacza, że ​​wysyłane są dwa wierzchołki, które tworzą kwadrat z dwoma wcześniej wysłanymi.

Wszystko, co musisz teraz zrozumieć, to jak działają funkcje trygonometryczne, ale domyślam się, że możesz to łatwo zrozumieć.

1

Jeśli chcesz być przebiegły jak lis, możesz o połowę wyciąć kod z GLU. Sprawdź kod źródłowy MesaGL (http://cgit.freedesktop.org/mesa/mesa/).

+3

Chociaż rozumiałem znaczenie "pół cala" w w tym kontekście, myślę, że mógłbyś chcieć go edytować dla innych 95% czytelników, którzy nie mówią płynnie w [rytmie rymów cockneya] (http://en.wikipedia.org/wiki/Rhyming_slang)! – Flexo

+1

Aha! Punkt zaczerpnięty :-) Miałem na myśli "szczyptę" jak w "uczyć się od" ;-) – blockchaindev

12

będę dalej tłumaczyć się popularnym sposobem generowania kulę za pomocą szerokości i długości geograficznej (inny sposób, icospheres został już wyjaśniono w odpowiedzi najpopularniejszego w chwili pisania tego tekstu).

A kula może być wyrażony za pomocą następującego równania parametrycznego:

F (u, v) = [cos (U) * sin (V) * R, cos (V) * R sin (u) * sin (v) * r]

gdzie:

  • R jest promieniem;
  • u to długość geograficzna w zakresie od 0 do 2 π; i
  • v to szerokość geograficzna w zakresie od 0 do π.

Generowanie kuli polega następnie na ocenie funkcji parametrycznej w ustalonych odstępach czasu.

Na przykład, w celu wytworzenia 16 linii na długości, będzie 17 linii siatki wzdłuż kształcie U osi, przy czym etap π/8 (2 π/16) (17-linia otacza).

Poniższy pseudokod generuje siatki trójkątów oceniając funkcję parametryczną w regularnych odstępach czasu (to działa na żadnego funkcję parametryczną powierzchni, nie tylko sfery).

W poniższym pseudokod, UResolution oznacza liczbę punktów siatki wzdłuż osi U (tu linie długości) i VResolution oznacza liczbę punktów siatki wzdłuż osi V (w tym przypadku, linie od szerokości geograficznej)

var startU=0 
var startV=0 
var endU=PI*2 
var endV=PI 
var stepU=(endU-startU)/UResolution // step size between U-points on the grid 
var stepV=(endV-startV)/VResolution // step size between V-points on the grid 
for(var i=0;i<UResolution;i++){ // U-points 
for(var j=0;j<VResolution;j++){ // V-points 
var u=i*stepU+startU 
var v=j*stepV+startV 
var un=(i+1==UResolution) ? EndU : (i+1)*stepU+startU 
var vn=(j+1==VResolution) ? EndV : (j+1)*stepV+startV 
// Find the four points of the grid 
// square by evaluating the parametric 
// surface function 
var p0=F(u, v) 
var p1=F(u, vn) 
var p2=F(un, v) 
var p3=F(un, vn) 
// NOTE: For spheres, the normal is just the normalized 
// version of each vertex point; this generally won't be the case for 
// other parametric surfaces. 
// Output the first triangle of this grid square 
triangle(p0, p2, p1) 
// Output the other triangle of this grid square 
triangle(p3, p1, p2) 
} 
} 
+0

Głosowanie w dół wydaje się nieco trudne. Jest to jedna z niewielu odpowiedzi, która podaje przykład dyskretnej konstrukcji za pomocą równania parametrycznego kuli. Łatwiej też zrozumieć, że kulę można uznać za stos okręgów, które kurczą się w pobliżu biegunów. –

+1

Witam, chciałem tylko wskazać, że druga z każdej wartości p0, p1, p2, p3 powinna być v lub vn, w przeciwieństwie do u lub un. – nicole

+0

@nicole: Dzięki za poprawkę. –

0

moim przykładzie, jak wykorzystać „pasmo trójkąt” narysować „polarny” sferę, polega ona punktów poboru w parach:

const float PI = 3.141592f; 
GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles   
GLfloat radius = 60.0f; 
int gradation = 20; 

for (alpha = 0.0; alpha < GL_PI; alpha += PI/gradation) 
{   
    glBegin(GL_TRIANGLE_STRIP); 
    for (beta = 0.0; beta < 2.01*GL_PI; beta += PI/gradation)    
    {    
     x = radius*cos(beta)*sin(alpha); 
     y = radius*sin(beta)*sin(alpha); 
     z = radius*cos(alpha); 
     glVertex3f(x, y, z); 
     x = radius*cos(beta)*sin(alpha + PI/gradation); 
     y = radius*sin(beta)*sin(alpha + PI/gradation); 
     z = radius*cos(alpha + PI/gradation);    
     glVertex3f(x, y, z);    
    }   
    glEnd(); 
} 

Wprowadzony pierwszy punkt (glVertex3f) jest równaniem parametrycznym, a drugi jest przesunięty o jeden krok kąta alfa (od następnego równoległego).

0

Jednym ze sposobów jest zrobienie kwadratu, który stoi przed kamerą i napisanie fragmentu i fragmentu shadera, który renderuje coś, co wygląda jak kula. Można użyć równań dla okręgu/sfery, które można znaleźć w Internecie.

Jedną miłą rzeczą jest to, że sylwetka kuli wygląda tak samo pod każdym kątem. Jeśli jednak kula nie znajduje się w centrum widoku perspektywicznego, wydaje się być może bardziej jak elipsa. Możesz opracować równania do tego i umieścić je w cieniowaniu fragmentu. Wtedy jasne cieniowanie musi się zmienić, gdy gracz się porusza, jeśli rzeczywiście gracz porusza się w przestrzeni 3D wokół kuli.

Czy ktoś może skomentować, czy próbował tego, czy byłby zbyt drogi, aby być praktycznym?

+0

To tylko prawda pod równoległą projekcją. Jeśli używasz rzutowania perspektywicznego, sylwetka kuli na wyjściu renderowania to ** nie ** ogólnie okrąg. –

0

Chociaż zaakceptowana odpowiedź rozwiązuje pytanie, na końcu jest trochę nieporozumień. Dwunastościany są (lub mogą być) regularnymi wielościanami, w których wszystkie twarze mają ten sam obszar. Wydaje się, że tak jest w przypadku Epcot (który, nawiasem mówiąc, wcale nie jest dwunastościanem). Ponieważ rozwiązanie zaproponowane przez @Kevin nie zapewnia tej cechy, pomyślałem, że mogę dodać podejście, które to robi.

Dobrym sposobem na uzyskanie N PYSKU wielościan, gdzie wszystkie wierzchołki leży w tym samym zakresie i wszystkie jego powierzchnie mają ten sam obszar zaczyna się na dwudziestościanu i iteracyjnie grupą rozdzielającą i normalizowanie jej trójkątne powierzchnie (w sugerowane w przyjętej odpowiedzi). Dwunastościany, na przykład, są w rzeczywistości truncated icosahedrons.

Regular icosahedrons ma 20 twarzy (12 wierzchołków) i można go łatwo zbudować z 3 złotych prostokątów; to tylko kwestia tego, że jest to punkt wyjścia zamiast ośmiościanu. Możesz znaleźć przykład here.

Wiem, że jest to trochę nie na temat, ale wierzę, że może to pomóc, jeśli ktoś dostanie się tutaj w poszukiwaniu tego konkretnego przypadku.