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());
?>
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());
?>