[OpenGL] Probleme mit Transparenz
-
Hi,
ich habe hier ein ziemlich konkretes Problem bei der Umsetzung eines kleinen OpenGL-Schachspiels. Ich möchte beim Markieren eines Steins diesen transparent darstellen. Dabei bekomme ich aber eine unerwartete Darstellung.
Bei dem markierten Stein wird die Zeichnungsstruktur sichtbar. Jetzt weiß ich nicht, was ich tun soll, damit der Stein schön gleichmäßig transparent dargestellt wird.Hier die wichtigsten Codeauszüge, wenn euch noch Infos fehlen, sagt Bescheid:
Initialisierung
// Standardkram glClearColor (0.0f, 0.28f, 0.648f, 1.0f); glClearDepth (1.0f); glDepthFunc (GL_LEQUAL); glEnable (GL_DEPTH_TEST); glShadeModel (GL_SMOOTH); glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Transparenz glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Licht glLightfv (GL_LIGHT1, GL_AMBIENT, beleuchtung); glLightfv (GL_LIGHT1, GL_DIFFUSE, lichtquelle); glLightfv (GL_LIGHT1, GL_POSITION, lichtpos); glEnable (GL_LIGHT1); glEnable (GL_LIGHTING); glEnable (GL_COLOR_MATERIAL);Zeichnungsstruktur
void Figur::Zeichnen (float x, float y) { float winkel = 0.0f, xAkt[2], yAkt[2]; int i; for (i = 0; i < ANZAHL; i++) { xAkt[0] = (float) cos (winkel) * RADIUS + x; yAkt[0] = (float) sin (winkel) * RADIUS + y; winkel += 2 * PI / ANZAHL; xAkt[1] = (float) cos (winkel) * RADIUS + x; yAkt[1] = (float) sin (winkel) * RADIUS + y; // ----- Boden der Figur ----- // glBegin (GL_TRIANGLES); glNormal3f (0.0f, 0.0f, -1.0f); glVertex3f (x, y, 0.01f); glVertex3f (xAkt[0], yAkt[0], 0.01f); glVertex3f (xAkt[1], yAkt[1], 0.01f); // ----- obere Seite ----- // glNormal3f (0.0f, 0.0f, 1.0f); glVertex3f (x, y, 0.2f); glVertex3f (xAkt[0], yAkt[0], 0.2f); glVertex3f (xAkt[1], yAkt[1], 0.2f); glEnd (); // ----- Mantel des Zylinders ----- // glBegin (GL_QUADS); glNormal3f ((xAkt[1] - xAkt[0]) / 2 + xAkt[0] - x, (yAkt[1] - yAkt[0]) / 2 + yAkt[0] - y, 0.0f); glVertex3f (xAkt[0], yAkt[0], 0.01f); glVertex3f (xAkt[1], yAkt[1], 0.01f); glVertex3f (xAkt[1], yAkt[1], 0.2f); glVertex3f (xAkt[0], yAkt[0], 0.2f); glEnd (); } }Darstellung der Markierung
void Figur::Faerben (float r, float g, float b) { if (markiert) glColor4f (r, g, b, 0.5f); else glColor4f (r, g, b, 1.0f); }Für eure Hilfe bedanke ich mich schon mal im voraus. Viele Grüße
-
Bei den transparenten Steinen musst du das Backface Culling deaktivieren, dass beide Seiten der Dreiecke sichtbar sind. Dann noch Depth Test deaktvieren und für undurchsichtige wieder aktivieren.
-
Ah, das ist schon mal gut, ich hatte das mit dem Depth Test schon versucht, aber eben für alle Steine. Ist ja eigentlich klar, dass man da unterscheiden muss.
Ich weiß noch nicht, wie ich das Backface Culling deaktivieren kann. Ich habe ein bisschen gegoogelt und gefunden, dass man es damit aktiviert:glEnable(GL_CULL_FACE);Bedeutet das, dass es standardmäßig deaktiviert ist, wenn ich den Befehl noch gar nicht benutzt habe, also dass ich mich nur um den Depth Test kümmern muss?
-
xindon schrieb:
Bei den transparenten Steinen musst du das Backface Culling deaktivieren, dass beide Seiten der Dreiecke sichtbar sind. Dann noch Depth Test deaktvieren und für undurchsichtige wieder aktivieren.
Nein, Depth Test nicht deaktivieren! Dann würden transparente Objekte vor allen anderen erscheinen (wenn man sie zuletzt malt) bzw. von allen anderen verdeckt werden (wenn man sie zuerst malt).
Du musst Depth Write deaktivieren. Depth Test einschalten, aber Schreiben in den Z-Buffer abschalten. Dann die transparenten Objekte zuletzt rendern.
-
Hm, Depth Test und Backface Culling auszuschalten hat tatsächlich nichts gebracht.
Sorry, dass ich nochmal frage, aber ich habe nirgendwo gefunden, wie ich denn das Schreiben in den Depth Buffer abschalten kann. Und mir ist auch noch nicht ganz klar, warum ich das machen muss. Bin jetzt leider leicht verwirrt.
Und muss ich die Reihenfolge des Zeichnens tatsächlich beachten, wenn alle Steine auf der Z-Achse dieselben Koordinaten haben (also dieselbe "Tiefe")? Bisher gehe ich nämlich nach dem Zeichnen des Schachbretts einfach die Felder des Bretts (einfaches 2-dimensionales Array) der Reihe nach durch und zeichne dort evtl. befindliche Steine.
-
Tlyman schrieb:
Sorry, dass ich nochmal frage, aber ich habe nirgendwo gefunden, wie ich denn das Schreiben in den Depth Buffer abschalten kann. Und mir ist auch noch nicht ganz klar, warum ich das machen muss. Bin jetzt leider leicht verwirrt.
Das Ein- und Abschalten vom Z-Write kannst du mit glDepthMask erreichen.
Warum das notwendig ist? Ist doch logisch: transparente Objekte sollen transparent sein. Sie verdecken das, was hinter ihnen liegt, nicht.Tlyman schrieb:
Und muss ich die Reihenfolge des Zeichnens tatsächlich beachten, wenn alle Steine auf der Z-Achse dieselben Koordinaten haben (also dieselbe "Tiefe")? Bisher gehe ich nämlich nach dem Zeichnen des Schachbretts einfach die Felder des Bretts (einfaches 2-dimensionales Array) der Reihe nach durch und zeichne dort evtl. befindliche Steine.
Transparente Objekte sollte man zuletzt zeichnen. Du solltest die Objekte auch nach ihrer Tiefe (aber aus der Sicht der Kamera!) sortieren, weil du ja nicht in den Z-Buffer schreibst. Normalerweise ist der Z-Buffer ja dafür zuständig, dass Objekte, die weiter vorne liegen, die hinteren verdecken. Aber da man ihn hier ja abschalten muss, sollte man schon die Objekte sortieren.
-
TomasRiker schrieb:
Ist doch logisch: transparente Objekte sollen transparent sein.
Hm, ja eigentlich klar, ich hatte irgendwie angenommen, dass das "automatisch" geregelt wird. Ich war vorhin irgendwie zu verwirrt wegen dem Hin und Her mit Depth Test, Depth Write, Backface Culling... ich bin immer noch dabei, mir die Sachen anzueignen, daher dauert das alles noch etwas

TomasRiker schrieb:
Du solltest die Objekte auch nach ihrer Tiefe (aber aus der Sicht der Kamera!) sortieren [...]
Oje, verstehe ich das richtig, dass ich bei jeder Drehung des Spielbretts (bei mir geht das mit allen Achsen) berechnen muss, wie die Steine zueinander liegen und in welcher Reihenfolge sie gezeichnet werden müssen? Ich hatte gehofft, dass man das nur anhand der Lage auf der Z-Achse bestimmen muss und ich somit fein raus bin.
Aber vielen Dank für deine Mühe!
Edit:
Wenn ich folgendes ändere:
void Figur::Faerben (float r, float g, float b) { if (markiert) { glDepthMask (GL_FALSE); glColor4f (r, g, b, 0.5f); } else { glDepthMask (GL_TRUE); glColor4f (r, g, b, 1.0f); } }bekomme ich fast dasselbe Ergebnis wie vorher.
Wenn ich mal experimentell das glDepthMask (GL_TRUE) weglasse, was ja nicht gehen kann, bekomme ich beim ersten Markieren das hier und beim Anklicken eines zweiten Steins schon mal durchgehende Transparenz, auch wenn der Hintergrund durchscheint und das Schachbrett "ignoriert" wird (obwohl es ja zuerst gezeichnet wird).
Ich habe zwar noch nicht die Reihenfolge beachtet, aber bei den markierten Steinen dürfte das doch keine Rolle spielen, weil sich kein Stein überdeckt.Klappt es jetzt wegen der falschen Reihenfolge nicht, oder mach ich etwas anderes falsch?

Entschuldigt bitte, wenn ich immer noch rumfrage, aber mich ärgert, dass so etwas an sich Simples solche Probleme bereitet. *frustrated*
-
Mach mal Folgendes:
Zuerst nur die Rückseiten rendern, dann nur die Vorderseiten (bei jedem Stein), mit Culling-Modus. Da ein Zylinder ein konvexes Objekt ist, sollte das prima funktionieren.
-
Moin,
Versuch mal folgende Kombination. So hat es bei mir geklappt.glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_COLOR); //... evtl. weiter OpenGL Funktionen glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); //... Figuren zeichnen glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); //... Figuren zeichnenHier noch mal was aus dem Internet.
OpenGL doesn't support a direct interface for rendering translucent (partially opaque) primitives. However, you can create a transparency effect with the blend feature and carefully ordering your primitive data. You might also consider using screen door transparency.
An OpenGL application typically enables blending as follows:
glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
After blending is enabled, as shown above, the incoming primitive color is blended with the color already stored in the framebuffer. glBlendFunc() controls how this blending occurs. The typical use described above modifies the incoming color by its associated alpha value and modifies the destination color by one minus the incoming alpha value. The sum of these two colors is then written back into the framebuffer.
The primitive’s opacity is specified using glColor4*(). RGB specifies the color, and the alpha parameter specifies the opacity.
When using depth buffering in an application, you need to be careful about the order in which you render primitives. Fully opaque primitives need to be rendered first, followed by partially opaque primitives in back-to-front order. If you don't render primitives in this order, the primitives, which would otherwise be visible through a partially opaque primitive, might lose the depth test entirely.
-
Danke euch beiden für die Tipps und Internetrecherchen. Ärgerlich, dass das Problem gar nicht so trivial ist, wie man glauben könnte.
-
Es gibt auch noch eine Alternative: Screen Door Transparency. Such mal danach. Sieht allerdings nicht ganz so toll aus, hat allerdings einige Vorteile.
-
wird in hardware als alpha to coverage seit einigerzeit gemacht.
-
*** Unsinn wegeditiert ***