Gibt es Tutorials, die erklären, wie ich eine Kugel in OpenGL zeichnen kann, ohne sie verwenden zu müssen gluSphere()
?
Viele der 3D-Tutorials für OpenGL befinden sich nur in Cubes. Ich habe gesucht, aber die meisten Lösungen zum Zeichnen einer Kugel sind zu verwenden gluSphere()
. Es gibt auch eine Site, die den Code zum Zeichnen einer Kugel an dieser Site enthält, aber die Mathematik hinter dem Zeichnen der Kugel nicht erklärt. Ich habe auch andere Versionen, wie man die Kugel in Polygon anstelle von Quads in diesem Link zeichnet. Aber auch hier verstehe ich nicht, wie die Kugeln mit dem Code gezeichnet werden. Ich möchte in der Lage sein, zu visualisieren, damit ich die Kugel bei Bedarf ändern kann.
Antworten:
Eine Möglichkeit besteht darin, mit einem platonischen Körper mit dreieckigen Seiten zu beginnen - beispielsweise einem Oktaeder . Nehmen Sie dann jedes Dreieck und teilen Sie es rekursiv in kleinere Dreiecke auf:
Sobald Sie eine ausreichende Anzahl von Punkten haben, normalisieren Sie ihre Vektoren so, dass sie alle einen konstanten Abstand vom Zentrum des Volumenkörpers haben. Dies führt dazu, dass sich die Seiten in eine Form wölben, die einer Kugel ähnelt, wobei die Glätte zunimmt, wenn Sie die Anzahl der Punkte erhöhen.
Normalisierung bedeutet hier, einen Punkt so zu verschieben, dass sein Winkel in Bezug auf einen anderen Punkt gleich ist, der Abstand zwischen ihnen jedoch unterschiedlich ist. Hier ist ein zweidimensionales Beispiel.
A und B sind 6 Einheiten voneinander entfernt. Angenommen, wir möchten einen Punkt auf der Linie AB finden, der 12 Einheiten von A entfernt ist.
Wir können sagen, dass C die normalisierte Form von B in Bezug auf A mit Abstand 12 ist. Wir können C mit Code wie folgt erhalten:
#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
Wenn wir diesen Normalisierungsprozess an vielen Punkten durchführen, alle in Bezug auf denselben Punkt A und mit demselben Abstand R, dann liegen die normalisierten Punkte alle auf dem Kreisbogen mit Mittelpunkt A und Radius R.
Hier beginnen die schwarzen Punkte auf einer Linie und "wölben" sich in einen Bogen.
Dieser Vorgang kann in drei Dimensionen erweitert werden. In diesem Fall erhalten Sie eher eine Kugel als einen Kreis. Fügen Sie der Normalisierungsfunktion einfach eine dz-Komponente hinzu.
Wenn Sie sich die Kugel bei Epcot ansehen , können Sie diese Technik bei der Arbeit sehen. Es ist ein Dodekaeder mit ausgebauchten Gesichtern, damit es runder aussieht.
quelle
Ich werde eine beliebte Methode zur Erzeugung einer Kugel anhand von Längen- und Breitengraden näher erläutern (eine andere Methode, Icosphären , wurde bereits in der beliebtesten Antwort zum Zeitpunkt dieses Schreibens erläutert.)
Eine Kugel kann durch die folgende parametrische Gleichung ausgedrückt werden:
F ( u , v ) = [cos (u) · sin (v) · r, cos (v) · r, sin (u) · sin (v) · r]
Wo:
Das Erzeugen der Kugel beinhaltet dann das Auswerten der parametrischen Funktion in festen Intervallen.
Um beispielsweise 16 Längengrade zu erzeugen, gibt es 17 Gitterlinien entlang der u- Achse mit einem Schritt von π / 8 (2π / 16) (die 17. Linie wird umbrochen).
Der folgende Pseudocode erzeugt ein Dreiecksnetz, indem eine parametrische Funktion in regelmäßigen Abständen ausgewertet wird (dies funktioniert für jede parametrische Oberflächenfunktion, nicht nur für Kugeln).
Im folgenden Pseudocode ist UResolution die Anzahl der Gitterpunkte entlang der U-Achse (hier Längengradlinien) und VResolution die Anzahl der Gitterpunkte entlang der V-Achse (hier Breitengradlinien).
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) } }
quelle
Der Code im Beispiel wird schnell erklärt. Sie sollten in die Funktion schauen
void drawSphere(double r, int lats, int longs)
:void drawSphere(double r, int lats, int longs) { int i, j; for(i = 0; i <= lats; i++) { double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats); double z0 = sin(lat0); double zr0 = cos(lat0); double lat1 = M_PI * (-0.5 + (double) i / lats); double z1 = sin(lat1); double zr1 = cos(lat1); glBegin(GL_QUAD_STRIP); for(j = 0; j <= longs; j++) { double lng = 2 * M_PI * (double) (j - 1) / longs; double x = cos(lng); double y = sin(lng); glNormal3f(x * zr0, y * zr0, z0); glVertex3f(r * x * zr0, r * y * zr0, r * z0); glNormal3f(x * zr1, y * zr1, z1); glVertex3f(r * x * zr1, r * y * zr1, r * z1); } glEnd(); } }
Die Parameter
lat
definieren, wie viele horizontale Linien Sie in Ihrer Kugel haben möchten undlon
wie viele vertikale Linien.r
ist der Radius Ihrer Kugel.Jetzt gibt es eine doppelte Iteration über
lat
/lon
und die Scheitelpunktkoordinaten werden mithilfe einfacher Trigonometrie berechnet.Die berechneten Scheitelpunkte werden jetzt mit
glVertex...()
als an Ihre GPU gesendet. DiesGL_QUAD_STRIP
bedeutet, dass Sie jeweils zwei Scheitelpunkte senden, die mit den zuvor beiden gesendeten Scheitelpunkten ein Quad bilden.Jetzt müssen Sie nur noch verstehen, wie die Trigonometrie funktioniert, aber ich denke, Sie können es leicht herausfinden.
quelle
Wenn Sie schlau wie ein Fuchs sein wollen, können Sie den Code von GLU einen halben Zoll groß machen. Überprüfen Sie den MesaGL-Quellcode (http://cgit.freedesktop.org/mesa/mesa/).
quelle
Siehe das rote OpenGL-Buch: http://www.glprogramming.com/red/chapter02.html#name8. Es löst das Problem durch Polygonunterteilung.
quelle
Mein Beispiel für die Verwendung eines Dreiecksstreifens zum Zeichnen einer "polaren" Kugel besteht darin, Punkte paarweise zu zeichnen:
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(); }
Der erste eingegebene Punkt (glVertex3f) folgt der parametrischen Gleichung, und der zweite Punkt wird um einen einzelnen Schritt des Alpha-Winkels (von der nächsten Parallele) verschoben.
quelle
Obwohl die akzeptierte Antwort die Frage löst, gibt es am Ende ein kleines Missverständnis. Dodekaeder sind (oder könnten) reguläre Polyeder sein, bei denen alle Flächen die gleiche Fläche haben. Dies scheint beim Epcot der Fall zu sein (der übrigens überhaupt kein Dodekaeder ist ). Da die von @Kevin vorgeschlagene Lösung diese Eigenschaft nicht bietet, dachte ich, ich könnte einen Ansatz hinzufügen, der dies tut.
Ein guter Weg, um ein Polyeder mit N-Gesicht zu erzeugen, bei dem alle Eckpunkte in derselben Kugel liegen und alle Flächen eine ähnliche Fläche / Oberfläche haben, beginnt mit einem Ikosaeder und der iterativen Unterteilung und Normalisierung seiner dreieckigen Flächen (wie in der akzeptierten Antwort vorgeschlagen) ). Dodekaeder zum Beispiel sind tatsächlich abgeschnittene Ikosaeder .
Normale Ikosaeder haben 20 Flächen (12 Eckpunkte) und können leicht aus 3 goldenen Rechtecken konstruiert werden. Es geht nur darum, dies als Ausgangspunkt anstelle eines Oktaeders zu haben. Ein Beispiel finden Sie hier .
Ich weiß, dass dies ein wenig vom Thema abweicht, aber ich glaube, es kann hilfreich sein, wenn jemand hierher kommt und nach diesem speziellen Fall sucht.
quelle
Eine Möglichkeit besteht darin, ein Quad zu erstellen, das der Kamera zugewandt ist, und einen Scheitelpunkt- und Fragment-Shader zu schreiben, der etwas rendert, das wie eine Kugel aussieht. Sie können Gleichungen für einen Kreis / eine Kugel verwenden, die Sie im Internet finden.
Eine schöne Sache ist, dass die Silhouette einer Kugel aus jedem Winkel gleich aussieht. Befindet sich die Kugel jedoch nicht in der Mitte einer perspektivischen Ansicht, erscheint sie möglicherweise eher wie eine Ellipse. Sie könnten die Gleichungen dafür ausarbeiten und sie in die Fragmentschattierung einfügen. Dann muss sich die Lichtschattierung ändern, wenn sich der Spieler bewegt, wenn sich tatsächlich ein Spieler im 3D-Raum um die Kugel bewegt.
Kann jemand kommentieren, ob er dies versucht hat oder ob es zu teuer wäre, um praktisch zu sein?
quelle
Python-Adaption von @Constantinius Antwort:
lats = 10 longs = 10 r = 10 for i in range(lats): lat0 = pi * (-0.5 + i / lats) z0 = sin(lat0) zr0 = cos(lat0) lat1 = pi * (-0.5 + (i+1) / lats) z1 = sin(lat1) zr1 = cos(lat1) glBegin(GL_QUAD_STRIP) for j in range(longs+1): lng = 2 * pi * (j+1) / longs x = cos(lng) y = sin(lng) glNormal(x * zr0, y * zr0, z0) glVertex(r * x * zr0, r * y * zr0, r * z0) glNormal(x * zr1, y * zr1, z1) glVertex(r * x * zr1, r * y * zr1, r * z1) glEnd()
quelle
void draw_sphere() { // z // | // __ // /| // | // | // | * \ // | _ _| _ _ _ | _y // / \c |n / p3 --- p2 // / \o |i | | // / \s|s z=sin(v) p0 --- p1 // |/__ y=cos(v) *sin(u) // x=cos(v) *cos(u) // / // x // double pi = 3.141592; double di =0.02; double dj =0.04; double du =di*2*pi; double dv =dj*pi; for (double i = 0; i < 1.0; i+=di) //horizonal for (double j = 0; j < 1.0; j+=dj) //vertical { double u = i*2*pi; //0 to 2pi double v = (j-0.5)*pi; //-pi/2 to pi/2 double p[][3] = { cos(v) * cos(u) ,cos(v) * sin(u) ,sin(v), cos(v) * cos(u + du) ,cos(v) * sin(u + du) ,sin(v), cos(v + dv)* cos(u + du) ,cos(v + dv)* sin(u + du) ,sin(v + dv), cos(v + dv)* cos(u) ,cos(v + dv)* sin(u) ,sin(v + dv)}; //normal glNormal3d(cos(v+dv/2)*cos(u+du/2),cos(v+dv/2)*sin(u+du/2),sin(v+dv/2)); glBegin(GL_POLYGON); glTexCoord2d(i, j); glVertex3dv(p[0]); glTexCoord2d(i+di,j); glVertex3dv(p[1]); glTexCoord2d(i+di,j+dj); glVertex3dv(p[2]); glTexCoord2d(i, j+dj); glVertex3dv(p[3]); glEnd(); } }
quelle