PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Stencil-Buffer


Früchtemüsli
2013-11-07, 21:52:30
Hi :)

Ich habe mich heute mal mit dem Thema Stencil-Buffer beschäftigt und überlegt, wie man das objektorientiert programmieren könnte. Dazu habe ich mir die OpenGL-Befehle glStencilFunc und glStencilOp angeschaut.

Den Sinn dieser Maske verstehe ich irgendwie noch nicht. Wann braucht man das? Habt ihr zufällig ein Beispiel bei der Hand, wo der Sinn dieser Maske klar wird?

Mit dem Depth-Test hab ich auch noch Probleme. Weiß nicht, wie ich das programmieren könnte. Vielleicht dem Konstruktor meiner StencilBuffer-Klasse ein Argument übergeben, das null oder ein DepthTest-Objekt ist? Und nur, wenn es ein DepthTest-Objekt ist, dann mache ich eine Tiefenkontrolle?

So schaut übrigens mein aktueller Stand aus:

<?php
error_reporting(-1);

/**
* Hier wird kein Anzahl erlaubter Bitplanes berücksichtigt.
* Es wird angenommen, dass so viele Bitplanes zur Verfügung stehen, dass man den Integer-Bereich
* voll ausnutzen kann.
*/
abstract class AStencilFunc
{
private $iRefValue;

/**
* Maske wurde hier noch keine berücksichtigt. TODO: ?
* @param int $refValue
*/
function __construct($refValue)
{
$this->setRefValue($refValue);
}
/**
* @param int $refValue
* @throws Exception
* not type int
* < 0
*/
private function setRefValue($refValue)
{
if
(
!is_int($refValue) ||
$refValue < 0
)
throw new Exception('ref value');

$this->iRefValue = $refValue;
}
/**
* @return int
*/
function getRefValue()
{
return $this->iRefValue;
}
abstract function pass($value, $currentValue);
}

/**
* Test schlägt immer fehl.
*/
class StencilFuncNever extends AStencilFunc
{
function __construct()
{
}
function pass($value, $currentValue)
{
return false;
}
}

/**
* Test erfolgreich, wenn neuer wert < alter wert
*/
class StencilFuncLess extends AStencilFunc
{
function pass($value, $currentValue)
{
if($value < $currentValue)
{
return true;
}
return false;
}
}

/**
* Test erfolgreich, wenn neuer Wert > alter Wert
*/
class StencilFuncGreater extends AStencilFunc
{
function pass($value, $currentValue)
{
if($value > $currentValue)
{
return true;
}
return false;
}
}

interface IStencilOp
{
function getValue($value);
}

/**
* Wert belassen.
*/
class StencilOpKeep implements IStencilOp
{
function getValue($value)
{
return $value;
}
}

/**
* Wert auf 0 setzen.
*/
class StencilOpZero implements IStencilOp
{
function getValue($value)
{
return 0;
}
}

/**
* Wert inkrementieren.
* PHP_INT_MAX wird nicht überschritten.
*/
class StencilOpIncr implements IStencilOp
{
function getValue($value)
{
if($value == PHP_INT_MAX)
{
return $value;
}
return ++$value;
}
}

/**
* Wert inkrementieren.
* 0, falls PHP_INT_MAX überschritten wird.
*/
class StencilOpIncrWrap implements IStencilOp
{
function getValue($value)
{
if($value == PHP_INT_MAX)
{
return 0;
}
return ++$value;
}
}

/**
* Wert durch Referenz-Wert ersetzen.
*/
class StencilOpReplace implements IStencilOp
{
protected $oFunc;

function __construct(AStencilFunc $oFunc)
{
$this->oFunc = $oFunc;
}
function getValue($value)
{
return $this->oFunc->getRefValue();
}
}

/**
* Ist wie ein Color-Buffer, nur dass seine Daten nicht auf dem Bildschirma ausgegeben werden.
* TODO: Tiefen-Test einbauen!
*/
class StencilBuffer
{
private $iWidth;
private $iHeight;
private $aValue;

private $oFunc;

private $oOpSFail;
private $oOpDPFail;
private $oOpDPPass;

/**
* @param Window $oWindow Gibt Breite und Höhe des Buffers vor.
*/
function __construct
(
Window $oWindow,
AStencilFunc $oFunc,
IStencilOp $oOpSFail,
IStencilOp $oOpDPFail,
IStencilOp $oOpDPPass
)
{
$this->initValues($oWindow);
$this->oFunc = $oFunc;
$this->oOpSFail = $oOpSFail;
$this->oOpDPFail = $oOpDPFail;
$this->oOpDPPass = $oOpDPPass;
}
/**
* @param Window $oWindow
*/
private function initValues(Window $oWindow)
{
$this->iWidth = $oWindow->getWidth();
$this->iHeight = $oWindow->getHeight();
for($iX = 0; $iX < $this->iWidth; ++$iX)
{
for($iY = 0; $iY < $this->iHeight; ++$iY)
{
$this->aValue[$iX][$iY] = 0;
}
}
}
/**
* Gibt einen Wert aus dem Buffer zurück.
* @param int $x
* @param int $y
* @return number
* @throws Exception
* x or y not type int
* x or y < 0
* x >= width
* y >= height
*/
function getValue($x, $y)
{
if
(
!is_int($x) ||
$x < 0 ||
$x >= $this->iWidth
)
throw new Exception('x');

if
(
!is_int($y) ||
$y < 0 ||
$y >= $this->iHeight
)
throw new Exception('y');

return $this->aValue[$x][$y];
}
/**
* @param int $x Speicher in Spalte X
* @param int $y Speicher in Zeile Y
* @param int $value
*/
function setValue($x, $y, $value)
{
if
(
!is_int($value) ||
$value < 0
)
throw new Exception('value');

$iCurrentValue = $this->getValue($x, $y);
$bPass = $this->oFunc->pass($value, $iCurrentValue);
if($bPass)
{
$this->aValue[$x][$y] = $this->oOpDPPass->getValue($value, $iCurrentValue);
}
else
{
$this->aValue[$x][$y] = $this->oOpSFail->getValue($value, $iCurrentValue);
}
}
/**
* @return array
*/
function getValues()
{
return $this->aValue;
}
}

// hier nur zum Testen die Klasse Window
class Window
{
private $width;
private $height;
function __construct($left, $top, $right, $bottom)
{
$this->width = $right - $left + 1;
$this->height = $bottom - $top + 1;
}
function getWidth()
{
return $this->width;
}
function getHeight()
{
return $this->height;
}
}

$oW = new Window(0, 0, 3, 2); // links, oben, rechts, unten
$oFunc = new StencilFuncGreater(5); // ref-wert
$oSB = new StencilBuffer
(
$oW,
$oFunc,
new StencilOpReplace($oFunc), // sFail
new StencilOpIncr(), // dpFail
new StencilOpZero() // dpPass
);
$oSB->setValue(0, 0, 100); // x, y, wert
var_dump($oSB->getValues());
?>

Früchtemüsli
2013-11-10, 10:28:10
Bzgl. Depth-Test:

Muss ich im Konstruktor ein z-Buffer-Objekt und bei setValue statt x und y einen vollständigen Vektor übergeben? Und im setValue vergleiche ich dann das z mitdem Eintrag im z-Buffer.

Ist das richtig so?

Coda
2013-11-10, 11:02:28
Ich verstehe nicht was du damit in PHP willst aber die Stencil Ops anzuwenden gehört meiner Meinung nach in den Renderer und nicht ins Stencil-Buffer-Objekt.

Um ehrlich zu sein tut es dafür auch ein Array ohne Klasse. Die Stencil-Ops mit Vererbung sind okay, allerdings wäre das nicht schnell wenn man das tatsächlich einsetzen wollte.

Früchtemüsli
2013-11-10, 11:09:06
allerdings wäre das nicht schnell wenn man das tatsächlich einsetzen wollte

Ja, das ist eh klar. Ich will nur Programmieren und PHP lernen :)

die Stencil Ops anzuwenden gehört meiner Meinung nach in den Renderer und nicht ins Stencil-Buffer-Objekt

http://www.opengl.org/sdk/docs/man/xhtml/glStencilFunc.xml
http://www.opengl.org/sdk/docs/man/xhtml/glStencilOp.xml

Nachdem das alles glStencilXXX-Funktionen sind, dachte ich mir, ich pack das alles irgendwie zusammen. Also alles in StencilXXX-Klassen.

Wäre bei dir die StencilBuffer-Klasse (sofern man überhaupt eine Klasse dafür verwendet) eine Klasse, die nur für die Datenhaltung zuständig ist? Also ganz ohne Geschäftslogik?

Wo definiert man eigentlich im OpenGL, wie groß (Breite, Höhe) der Buffer sein soll?

Coda
2013-11-10, 13:22:59
Ja, ich würde die Buffer als reine Daten sehen. Die würde ich dann einem Renderer-Objekt übergeben dass die Operationen darauf ausführt.

Alternativ könntest du die Operationen wie du vorschlägst im Objekt kapseln und dann Methoden machen wie "SetValue(x, y, depth)". Den Depth-Buffer übergeben finde ich irgendwie unschön. Aber ich würde das ehrlich gesagt ganz sein lassen. Es ist eh gut datenorientiert zu programmieren wenn du irgendwann performanten Code schreiben willst.

Buffer werden bei OpenGL mit einer entsprechenden Größe erzeugt und dann gebunden. Dabei gibt es natürlich Einschränkungen, beispielsweise müssen Depth- und Stencil-Buffer normal auch ein Buffer sein und gleiche Dimensionen wie der gebundene Color-Buffer haben.

Früchtemüsli
2013-11-13, 20:14:05
Aber ich würde das ehrlich gesagt ganz sein lassen.
Ich glaube, du hast recht :)

Also ich würde sagen, es reicht eine Klasse StencilBuffer. Im Kontstruktor übergebe ich ein Window-Objekt, das die Größe (Breite, Höhe) des Buffers vorgibt. Dann gibt es noch einen setter, wo ich einen int-Wert setzen kann. Und schließlich noch einen getter, wo ich einen Wert abfragen kann.

Ich glaube, mehr muss so eine Stencil-Buffer-Klasse gar nicht können.

Einzig diese Maske (http://www.opengl.org/sdk/docs/man/xhtml/glStencilFunc.xml) interessiert mich noch. Wozu braucht man die?