OpenGL: Wie zeichne ich einen Tunnel richtig?
-
Hallo zusammen!
Ich habe versucht ein Zylinder ohne Boden und Deckel zu zeichnen, aber irgendwie scheint es nicht so zu laufen. Da ich bestimmt irgendwo einen Denkfehler habe, würde ich mich freuen, wenn mir jemand eine "richtige" Methode posten könnte. - Wenn es geht, bitte nur mit GL_TRIANGLES! - Hier ist meine Funktion:
void srOpenGL::DrawTunnel(const unsigned int border) { //////////////////////////////////////////////////////////////////////////////// /// Ein Tunnel zeichnen //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// float angle[2], posY[2], posZ[2]; this->BindID(0); glBegin(GL_TRIANGLES); for(unsigned int c = 1; c<border; c++) { angle[0] = c*2*M_PI/border; angle[1] = (c-1)*2*M_PI/border; posY[0] = std::cos(angle[0]); posY[1] = std::cos(angle[1]); posZ[0] = std::sin(angle[0]); posZ[1] = std::sin(angle[1]); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.2f, 0.2f-posY[0], 0.2f-posZ[0]); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.2f, -0.2f-posY[1], 0.2f-posZ[1]); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.2f, 0.2f-posY[0], 0.2f-posZ[0]); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.2f, -0.2f-posY[1], 0.2f-posZ[1]); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.2f, -0.2f-posY[1], 0.2f-posZ[1]); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.2f, 0.2f-posY[0], 0.2f-posZ[0]); } glEnd(); //////////////////////////////////////// }
Danke für eure schnelle Hilfe!
Gruß,
littletux
-
Richtig? Dann mit Beziér Patches.
-
Kóyaánasqatsi schrieb:
Richtig? Dann mit Beziér Patches.
Richtig! Aber nicht mit OpenGL, sondern mit Direct3D 11, Tessellator und Shadern
-
DirectX kann ich leider nicht nutzen, da es nur für Microsoft Windows kozipiert ist. Ich bin ein Linuxanwender. Shader hört sich irgendwie interessant an. Was ist das eigentlich? Kann man Shader als eine Art "Assemblersprache für Graphikprozessoren" sehen? Muss man eine bestimmte Header-Datei einbinden, um Shader richtig nutzen zu können, oder bietet OpenGL auch Shader an? Wenn Shader von OpenGL unterstützt wird, wie muss eine Funktion aussehen, um ein einfaches Dreieck zu zeichen?
Der Grund warum ich immer auf die Dreiecke beharre ist, dass Dreicke angeblich schneller von der GPU bearbeitet werden als Vierecke. Ob das Gerücht stimmt, weiß ich leider nicht. Ich möchte schon eine Funktion schreiben die nicht nur funktioniert, sondern auch den GPU zu gute kommt - also nicht unnötige Speicher und Leistung verbraucht.
Könnt ihr mir ein einfaches Beispiel posten? Wenn es geht mit OpenGL! Wenn nicht, bitte ich euch wenigstens meine Methode nochmal anzuschauen und mir ein Tipp zu geben, wie ich es umändern kann, damit es einigermaßen funktioniert. Danke!
Viele Grüße,
littletux
-
angle[0] = c*2*M_PI/border; angle[1] = (c-1)*2*M_PI/border; posY[0] = std::cos(angle[0]); posY[1] = std::cos(angle[1]); posZ[0] = std::sin(angle[0]); posZ[1] = std::sin(angle[1]); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.2f, 0.2f-posY[0], 0.2f-posZ[0]); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.2f, -0.2f-posY[1], 0.2f-posZ[1]); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.2f, 0.2f-posY[0], 0.2f-posZ[0]); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.2f, -0.2f-posY[1], 0.2f-posZ[1]); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.2f, -0.2f-posY[1], 0.2f-posZ[1]); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.2f, 0.2f-posY[0], 0.2f-posZ[0]);
In der y-Komponente von
glVertex3f
verwendest du manchmal +0.2 und manchmal -0.2. Das macht keinen Sinn. Außerdem solltest du vllt. die Werte von posY und posZ ein wenig skalieren...posY[0] = std::cos(angle[0]) * 0.1; posY[1] = std::cos(angle[1]) * 0.1; posZ[0] = std::sin(angle[0]) * 0.1; posZ[1] = std::sin(angle[1]) * 0.1; glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.2f, posY[0], posZ[0]); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.2f, posY[1], posZ[1]); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.2f, posY[0], posZ[0]); glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.2f, posY[1], posZ[1]); glTexCoord2f(1.0f, 1.0f); glVertex3f(0.2f, posY[1], posZ[1]); glTexCoord2f(1.0f, 0.0f); glVertex3f(0.2f, posY[0], posZ[0]);
Wenn das nicht klappt, denke ich mal noch weiter darüber nach, ob mir da noch was seltsam drin vorkommt.
-
@ProgChild: Danke für den Hinweis! Der Grund warum ich +0.2f und -0.2f verwendet habe ist, dass ich mich zu sehr auf das Dreieck zeichen konzentriert habe. Mir ist noch ein Fehler augefallen:
for(unsigned int c = 1; c<border; c++)
wird zu
for(unsigned int c = 1; c<=border; c++)
Jetzt funktioniert alles
Danke!
-
Was wird eigentlich schneller vom GPU gezeichnet? GL_QUADS oder GL_TRIANGLES?
-
Normalerweise Triangles, da Quads innerhalb OpenGLs oder der Grafikkarte (bin mir da gerade nicht so sicher) in zwei Triangles umgerechnet werden.
Falls das umrechnen in der Grafikkarte geschieht kann es sein dass Quads trotzdem schneller sind weil du beim zeichnen zwei Vertexes weniger durch deine PCI-e Schnittstelle pressen musst.
Das sollte sich allerdings erübrigen wenn du mit Display Lists arbeitest. (Wies bei jeder statischen Geometrie empfehlenswert ist)
-
RedPuma schrieb:
Das sollte sich allerdings erübrigen wenn du mit Display Lists arbeitest. (Wies bei jeder statischen Geometrie empfehlenswert ist)
Nicht unbedingt. Je nach Implementierung sind die Vertex-Buffer schneller. Auch glInterleavedArrays in Kombination mit glDrawArrays, glDrawElements oder glDrawElement oder auch die gl*Pointer Funktionen in Kombination mit glDrawArrays, glDrawElements oder glDrawElement können schneller sein. Dann gibt es auch noch die Extension für Compiled Vertex Arrays...
Was am schnellsten ist, lässt sich also so nicht so leicht beantworten. Das muss man im Zweifel testen. Nur die glVertex*, glColor* usw. Funktionen sind definitiv die langsamste Möglichkeit.
-
Hier meine abgeänderte Variante:
GLuint srOpenGL::CreateTube(unsigned int border, float radius, float width) { //////////////////////////////////////////////////////////////////////////////// /// Ein Rohr erstellen //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// if(border>3) { float end, length, start; float angle[2], posX[2], posY[2], posZ[2]; length = 1.0f/static_cast<float>(border); width/=2; posY[0] = width; posY[1] = -width; GLuint tube = glGenLists(1); glNewList(tube, GL_COMPILE); glBegin(GL_TRIANGLES); for(unsigned int c = 1; c<=border; c++) { angle[0] = (c-1)*2*M_PI/border; angle[1] = c*2*M_PI/border; start = c*length; end = (c+1)*length; posX[0] = radius*std::cos(angle[0]); posX[1] = radius*std::cos(angle[1]); posZ[0] = radius*std::sin(angle[0]); posZ[1] = radius*std::sin(angle[1]); glTexCoord2f(start, 0.0f); glVertex3f(posX[0], posY[0], posZ[0]); glTexCoord2f(start, 1.0f); glVertex3f(posX[0], posY[1], posZ[0]); glTexCoord2f(end, 0.0f); glVertex3f(posX[1], posY[0], posZ[1]); glTexCoord2f(start, 1.0f); glVertex3f(posX[0], posY[1], posZ[0]); glTexCoord2f(end, 1.0f); glVertex3f(posX[1], posY[1], posZ[1]); glTexCoord2f(end, 0.0f); glVertex3f(posX[1], posY[0], posZ[1]); } glEnd(); glEndList(); return tube; } else { return 0; } ////////////////////////////////////////
Wie würde dies mit glDrawElements aussehen?
-
littletux schrieb:
Wie würde dies mit glDrawElements aussehen?
Google: glDrawElements example erster Eintrag...
-
Ich habe probiert mit glDrawElements zu arbeiten, aber irgendwie sehe ich nichts am Bildschirm. Habe ich was übersehen? Hier ist mein neuer Code:
void srOpenGL::DrawTube(unsigned border, float radius, float width) { //////////////////////////////////////////////////////////////////////////////// /// Ein Rohr zeichnen //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// unsigned int array = border*12, size = border*18; GLfloat end, length, start; GLfloat angle[2], posX[2], posY[2], posZ[2], textures[array], vertices[size]; GLubyte indices[size]; glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); for(unsigned int c = 0; c<size; c++) { indices[c] = static_cast<GLubyte>(c); } length = 1.0f/static_cast<GLfloat>(border); width/=2; posY[0] = width; posY[1] = -width; for(unsigned int c = 1, i = 0, j = 0; c<=border; c++, i+=18, j+=12) { angle[0] = (c-1)*2*M_PI/border; angle[1] = c*2*M_PI/border; start = c*length; end = (c+1)*length; posX[0] = radius*std::cos(angle[0]); posX[1] = radius*std::cos(angle[1]); posZ[0] = radius*std::sin(angle[0]); posZ[1] = radius*std::sin(angle[1]); textures[j] = start; textures[(j+1)] = 0.0f; textures[(j+2)] = start; textures[(j+3)] = 1.0f; textures[(j+4)] = end; textures[(j+5)] = 0.0f; textures[(j+6)] = start; textures[(j+7)] = 1.0f; textures[(j+8)] = end; textures[(j+9)] = 1.0f; textures[(j+10)] = end; textures[(j+11)] = 0.0f; vertices[i] = posX[0]; vertices[(i+1)] = posY[0]; vertices[(i+2)] = posZ[0]; vertices[(i+3)] = posX[0]; vertices[(i+4)] = posY[1]; vertices[(i+5)] = posZ[0]; vertices[(i+6)] = posX[1]; vertices[(i+7)] = posY[0]; vertices[(i+8)] = posZ[1]; vertices[(i+9)] = posX[0]; vertices[(i+10)] = posY[1]; vertices[(i+11)] = posZ[0]; vertices[(i+12)] = posX[1]; vertices[(i+13)] = posY[1]; vertices[(i+14)] = posZ[1]; vertices[(i+15)] = posX[1]; vertices[(i+16)] = posY[0]; vertices[(i+17)] = posZ[1]; } glTexCoordPointer(3, GL_FLOAT, sizeof(textures)/sizeof(GLfloat), textures); glVertexPointer(3, GL_FLOAT, sizeof(vertices)/sizeof(GLfloat), vertices); glPushMatrix(); glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices); glPopMatrix(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); //////////////////////////////////////// }
-
Deine Berechnungen mit
sizeof
sollten immer 1 liefern. (sizeof(textures)/sizeof(GLfloat))
-
Ich habe vergessen, meine Funktion aufzurufen. Jetzt kann ich was sehen. Meine Funktion zeichnet zwar ein Rohr aber irgendwie schaut von der seite eine große weiße Fläche raus und die Texturen werden auch nicht richtig angezeigt wie es vorher mit glTexCoord2f war. Woran liegt es?
-
Ich habe folgendes geändert:
glTexCoordPointer(2, GL_FLOAT, 0, &textures[0]); glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
Die Texturen werden zwar richtig dargestellt, aber irgendwie schauen aus der Seite des Rohrs lauter kleine senkrecht aufgestapelte Dreiecke raus. Ich weiß nicht warum, da dies eigentlich die selben Koordinaten sind wie in meine vorherigen Variante ohne glDrawElements.
-
Jetzt wird das Objekt richtig dargestellt! Der Fehler lag bei der Arraygröße. Es darf nicht maximal sein und muss wegen glVertex3f durch 3 geteit werden. Aber nun gibt es Probleme mit meine Textur, die nicht richtig auf die Oberfläche liegt. Kann mir bitte jemand weiter helfen?
const GLuint srOpenGL::CreateTube(unsigned int border, float radius, float width) { //////////////////////////////////////////////////////////////////////////////// /// Ein Rohr erstellen //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// float end, length, start; float angle[2], posX[2], posY[2], posZ[2]; GLfloat textures[(border*12)], vertices[(border*18)]; length = 1.0f/static_cast<float>(border); width/=2; posY[0] = width; posY[1] = -width; GLuint tube = glGenLists(1); for(unsigned int c=1, i=0, j=0; c<=border; c++, i+=12, j+=18) { angle[0] = (c-1)*2*M_PI/border; angle[1] = c*2*M_PI/border; start = c*length; end = (c+1)*length; posX[0] = radius*std::cos(angle[0]); posX[1] = radius*std::cos(angle[1]); posZ[0] = radius*std::sin(angle[0]); posZ[1] = radius*std::sin(angle[1]); textures[i] = start; textures[(i+1)] = 0.0f; textures[(i+2)] = start; textures[(i+3)] = 1.0f; textures[(i+4)] = end; textures[(i+5)] = 0.0f; textures[(i+6)] = start; textures[(i+7)] = 1.0f; textures[(i+8)] = end; textures[(i+9)] = 1.0f; textures[(+10)] = end; textures[(i+11)] = 0.0f; vertices[j] = posX[0]; vertices[(j+1)] = posY[0]; vertices[(j+2)] = posZ[0]; vertices[(j+3)] = posX[0]; vertices[(j+4)] = posY[1]; vertices[(j+5)] = posZ[0]; vertices[(j+6)] = posX[1]; vertices[(j+7)] = posY[0]; vertices[(j+8)] = posZ[1]; vertices[(j+9)] = posX[0]; vertices[(j+10)] = posY[1]; vertices[(j+11)] = posZ[0]; vertices[(j+12)] = posX[1]; vertices[(j+13)] = posY[1]; vertices[(j+14)] = posZ[1]; vertices[(j+15)] = posX[1]; vertices[(j+16)] = posY[0]; vertices[(j+17)] = posZ[1]; } glNewList(tube, GL_COMPILE); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, textures); glVertexPointer(3, GL_FLOAT, 0, vertices); glPushMatrix(); glDrawArrays(GL_TRIANGLES, 0, (6*border)); glPopMatrix(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glEndList(); return tube; //////////////////////////////////////// }
-
vielleicht bekommst du mehr antworten wenn die leute rausfinden wovon du immer sprichst, "nicht richtig" ist so waaaage ;).
mach ein screenshot und poste den link dazu (gibt ja genug free image hoster).
-
Wenn man sich nur ein Viereck (bestehend aus zwei Dreiecken) vom Mantel eines Zylinders vorstellt, so sieht es wie folgt aus:
1---3 _ 6
|.../ __ /|
|../ __ /.|
|./ __ /..|
2 _ 4---5Wenn man die Variable start mit 0.0f und end mit 1.0f setzt, so wird die Textur im Dreieck (1-2-3) richtig Dargestellt, aber im Dreieck (4-5-6) wird die Textur schreck dargestellt.
Also er zeichnet die Punktspalte von 6 in X-Richtung und die Punktzeile von Punkt 6 zu Punkt 4. Mit anderen Worten ist die Textur des zweiten Dreiecks um 45° geneigt.
-
Mir ist gerade aufgefallen, wenn ich ein Rohr zeichne (also den Mantel vom Zylinder), wird nur auf ein Viereck die Textur richtig dargestellt. Ich vermute mal, dass es der erste ist. Bei den anderen Vierecken sieht es wie oben beschrieben aus. Hat es etwas mit glTexCoordPointer(2, GL_FLOAT, 0, textures) zu tun? Ich Frage nur, weil der erste Parameter eine 2 ist und bei den anderen Funktionen glVertexPointer(3, GL_FLOAT, 0, vertices) eine 3 ist.
-
Der erste Parameter von gl*Pointer gibt an wieviel Komponenten das Vertexattribut hat.