[OpenGL] Der Stencilbuffer - eine Schablone fürs Zeichnen

Übersicht BlitzMax, BlitzMax NG FAQs und Tutorials

Neue Antwort erstellen

Lobby

Betreff: [OpenGL] Der Stencilbuffer - eine Schablone fürs Zeichnen

BeitragSa, März 30, 2013 17:21
Antworten mit Zitat
Benutzer-Profile anzeigen
In diesem Tutorial wird es darum gehen wie man den sogenannten Stencilbuffer in OpenGL verwendet. Mit Hilfe dieses Buffers lässt sich pixelgenau bestimmen, wo andere Zeichenbefehle zeichnen dürfen. Es werden keinerlei OpenGL-Kenntnisse vorausgesetzt. Man könnte damit beispielsweise so etwas realisieren:
user posted image
Es handelt sich hierbei um die runde Minimap eines Weltraumspiels, dabei ist zu sehen, dass die Asteroiden die sich am Rand der Minimap befinden nur innerhalb dieser gezeichnet werden.

Was ist der Stencilbuffer?
Der Stencilbuffer selbst ist wie ein Bildbuffer der für jeden Pixel des Grafikbereichs mindestens 8 Bit, also ein Byte, an Speicher zur Verfügung stellt. Jeder Pixel des Stencilbuffers kann beliebig mit einem Wert, der in eben dieses Byte passt, beschrieben werden. Abhängig von den im Stencilbuffer gespeicherten Werten kann man nun festlegen, welche Pixel durch Zeichnenoperationen (wie zum Beispiel DrawRect, DrawLine, DrawImage und DrawText) in ihrer Farbe beeinflusst werden können.
Will man, dass nur innerhalb eines ungedrehten, rechteckigen Bereiches gezeichnert werden kann, so kann man das max2d eigene SetViewport(x, y, w, h) verwenden. Für beliebige Formen aber eignet sich der hier vorgestellte Stencilbuffer.

Eine kleine Hilfsklasse für den Stencilbuffer in OpenGL
Da reines OpenGL nicht immer gut lesbar oder nachvollziehbar ist, habe ich die wichtigsten Funktionen für den Stencilbuffer in einer eigenen Klasse TGLStencil gekapselt. BlitzMax: [AUSKLAPPEN]
Rem
This is a little class to use the stencil buffer in BlitzMax max2d.
Please make sure that you created your graphics context at least with flag GRAPHICS_STENCILBUFFER.

Written by Lobby Divinus
end rem


SuperStrict

Import brl.glmax2d

Type TGLStencil
Function Enable()
glEnable(GL_STENCIL_TEST)
End Function

Function Disable()
glDisable(GL_STENCIL_TEST)
End Function

Function BeginMask(ref:Int = 1)
SetBlend(MASKBLEND)
glColorMask(False, False, False, False)
glStencilFunc(GL_ALWAYS, ref, %11111111)
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
End Function

Function EndMask(mode:Int = GL_EQUAL, ref:Int = 1, mask:Int = %11111111)
SetBlend(AlphaBlend)
glColorMask(True, True, True, True)
glStencilFunc(mode, ref, mask)
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
End Function

Function clearMask()
glClear(GL_STENCIL_BUFFER_BIT)
End Function
End Type

Diese Klasse stellt folgende Methoden zur Verfügung:
-Enable()
Aktiviert den Stencilbuffer, dies sollte immer gemacht werden, bevor man den Stencilbuffer benutzt.

-Disable()
Sobald man mit der Verwendung des Stencilbuffers fertig ist, sollte man diese Methode aufrufen.

-BeginMask(ref:Int = 1)
Zeichnenoperationen die nach Aufruf dieser Methode aufgerufen werden, setzen den Wert aller Pixel, die dabei beschrieben werden würden, im Stencilbuffer auf den Wert ref. Der aktuelle Grafikbuffer (in der Regel der Backbuffer) wird hierbei nicht beschrieben.

-EndMask(mode:Int = GL_EQUAL, ref:Int = 1, mask:Int = %11111111)
Nachdem man alle Werte im Stencilbuffer wie gewünscht gesetzt hat, kann man mit dieser Methode festlegen, wo folgende Zeichnenoperationen im Abhängigkeit von den im Stencilbuffer gespeicherten Werten auf den aktuellen Grafikbuffer schreiben dürfen.
Mode gibt dabei an, in welcher Beziehung ref zu den Werten im Stencilbuffer stehen muss, damit ein Pixel beschrieben werden darf. Es gibt folgende Werte, wobei stencil im Folgenden der jeweilige Wert im Stencilbuffer sei, an dem gezeichnet werden soll:
  • GL_EQUAL - Es muss ref = stencil gelten.
  • GL_NEVER - Kein Pixel darf beschrieben werden.
  • GL_ALWAYS - Alle Pixel dürfen beschrieben werden.
  • GL_NOTEQUAL - Es muss ref <> stencil (ungleich) gelten.
  • GL_LESS - Es muss ref < stencil gelten.
  • GL_LEQUAL - Es muss ref <= stencil gelten.
  • GL_GEQUAL - Es muss ref >= stencil gelten.
  • GL_GREATER - Es muss ref > stencil gelten.
Mask ist eine Maske die vor der Vergleichsoperation mit mode auf ref sowie stencil angewendet wird, also (mode & mask) OP (stencil & mask). Mask = %11111111 beachtet dabei also alle Bits, während %00000001 nur das mit der niedrigsten Wertigkeit berücksichtigt.

-clearMask()
Setzt alle im Stencilbuffer gespeicherten Werte auf 0 zurück. Wenn man sich nicht auf vorherige Werte beziehen will, sollte man diese Methode immer aufrufen, bevor man den Stencilbuffer neu beschreiben will.

Wie sieht die konkrete Anwendung aus?
Die Dokumentation der Klasse TGLStencil ist etwas lang ausgefallen, weil der Stencilbuffer eben all diese Möglichkeiten (und eigentlich noch mehr) bietet. Aber in der tatsächlichen Anwendung sollten meist die Standardwerte für die Methoden vollkommen ausreichen. Daher auch im folgenden ein kleiner, lauffähiger Beispielcode der zeigt wie man ein Rechteck so zeichnet, dass es nur innerhalb eines Kreises angezeigt wird. BlitzMax: [AUSKLAPPEN]
SuperStrict

'Prapare graphics driver so that we use OpenGL with back buffer and stencil buffer.
SetGraphicsDriver(GLMax2DDriver(), GRAPHICS_BACKBUFFER | GRAPHICS_STENCILBUFFER)

'Create graphics and timer.
Graphics(400, 300, 0)
Local timer:TTimer = ttimer.Create(60)

'Main loop that will break if escape-key or close button is pressed.
While(Not(KeyHit(KEY_ESCAPE) Or AppTerminate()))
Cls()

'Clear stencil buffer.
TGLStencil.clearMask()
'Start using stencil buffer.
TGLStencil.Enable()
'Draw onto the stencil buffer.
TGLStencil.BeginMask()
DrawOval(0, 0, 300, 300)

TGLStencil.EndMask()
'Now draw a rect that will only be drawn within the oval.
DrawRect(20, 20, 260, 260)
TGLStencil.Disable()

'Show the result and wait for timer.
Flip()
timer.Wait()
Wend


Type TGLStencil
Function Enable()
glEnable(GL_STENCIL_TEST)
End Function

Function Disable()
glDisable(GL_STENCIL_TEST)
End Function

Function BeginMask(ref:Int = 1)
SetBlend(MASKBLEND)
glColorMask(False, False, False, False)
glStencilFunc(GL_ALWAYS, ref, %11111111)
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
End Function

Function EndMask(mode:Int = GL_EQUAL, ref:Int = 1, mask:Int = %11111111)
SetBlend(AlphaBlend)
glColorMask(True, True, True, True)
glStencilFunc(mode, ref, mask)
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
End Function

Function clearMask()
glClear(GL_STENCIL_BUFFER_BIT)
End Function
End Type

Der Beispielcode sollte zu folgendem Ergebnis führen:
user posted image
Im Code fällt vielleicht diese Zeile auf. BlitzMax: [AUSKLAPPEN]
SetGraphicsDriver(GLMax2DDriver(), GRAPHICS_BACKBUFFER | GRAPHICS_STENCILBUFFER)

Sie ist notwendig um einerseits den Grafiktreiber auf OpenGL zu setzen (siehe brl.glmax2d), und andererseits um festzulegen, dass Graphics den Grafikkontext mit Stencilbuffer erstellen soll (ich habe festgestellt, dass das auf modernen Grafikkarten nicht selten automatisch geschieht, auch wenn der Flag nicht gesetzt wurde, aber darauf kann man sich im Allgemeinen nicht verlassen).

Wofür kann man den Stencilbuffer denn verwenden?
Ein simpler Anwendungsbereich ist die zu Anfang gezeigte Minimap, bei der man mit Hilfe des Stencilbuffers genau bestimmen kann, was noch gezeichnet werden soll und was nicht. Generell lässt sich der Stencilbuffer immer dann verwenden, wenn man etwas nur an bestimmten Pixeln zeichnen möchte.

Wissenswertes
Sehr mit dem Stencilbuffer verwandt ist der Z-Buffer beziehungsweise Tiefenbuffer. Er verfügt heutzutage oftmals über mehr als 8 Bit Speicher für jeden einzelnen Pixel, wobei in diesem jeweils gespeichert wird, wie weit der Pixel vom Betrachter entfernt ist (bzw. der Punkt eines Dreiecks, der dem Pixel seine Farbe gegeben hat). Beim Zeichnen ein jedes Dreiecks einer 3D-Szene wird ein Pixel nur dann überschrieben, wenn der neue Wert kleiner wäre als der vorherige - so erscheinen nachher alle Dreiecke in der richtigen Reihenfolge gezeichnet worden zu sein. Die Range der Kamera wird auf die Werte des Z-Buffers projiziert, daher nehmen mit steigender Range auch die Bildfehler bezüglich der Zeichenreihenfolge nahe beieinander liegender Dreiecke zu, da die Abstandsangaben im Z-Buffer zu ungenau werden.

Der Stencilbuffer ist nicht OpenGL-only, ich beschränke mich hier vor allem der Einfachheit wegen auf OpenGL. Aber es gäbe da auch noch ein Stencilbuffer-Modul von btbn das neben OpenGL auch DirectX9 unterstützt.

Dieses Tutorial erhebt keinen Anspruch auf Richtigkeit, Vollständigkeit, Qualität und Funktionsweise des bereitgestellten Codes. Ich wünsche noch viel Spaß beim selbst Experimentieren.
Lobby Divinus
TheoTown - Eine Stadtaufbausimulation für Android, iOS, Windows, Mac OS und Linux

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG FAQs und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group