PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : phong-shader


Früchtemüsli
2013-08-08, 09:19:32
Hi! :)

Bei mir haben sich mittlerweile einige Fragen zur 3D-Programmierung angesammelt. Ich hoffe, hier kann mir wer weiterhelfen. :)

Ich will einen eigenen Renderer programmieren - unabhängig von irgendwelchen Bibliotheken. Einiges habe ich schon. Jetzt überlege ich, wie ich einen Phong-Shader programmieren könnte.

Habt ihr das schon mal gemacht? Ich würde gerne mal so eine Phong-Klasse anschauen. Im Internet finde ich nämlich nix.

Früchtemüsli
2013-10-18, 17:58:50
Ich hab jetzt etwas programmiert (noch keine Zeile getestet!), was folgendermaßen ausschaut:

/**
* Dieser Phong-Shader erwartet:
* - 1 ambientes Licht
* - mindestens 1 Lichtquelle (für diffuses und speculares Licht)
* - Materialeigenschaften für die 3 Lichtarten
* - 1 Betrachter
*/
class Phong implements IShaderElement
{
private $oAmbientLightColor; // Farbe ambientes Licht
private $aLightSetEntry = []; // Daten zu Lichtquellen (Farbe und Position der Lichtquelle)

// Materialeigenschaften!
private $nAmbientCoefficient;
private $nDiffuseCoefficient;
private $nSpecularCoefficient;
private $nSpecularReflectionCoefficient;

private $oCameraPosition;

/**
* @param Vector $oAmbientLightColor Farbe des ambienten Lichts
* @param LightSet $oLightSet Set an Lichtquellen. Zu jeder Lichtquelle die Farbe und Position
* @param number $ambientCoefficient Koeffizient für ambientes Licht [0, 1]
* @param number $diffuseCoefficient Koeffizient für diffuses Licht [0, 1]
* @param number $specularCoefficient Koeffizient für spekulares Licht [0, 1]
* @param number $specularReflectionCoefficient Reflexions-Koeffizient für spekulares Licht, Wert >= 0
* @param Vector $oCameraPosition Position der Kamera
*/
function __construct
(
Vector $oAmbientLightColor,
LightSet $oLightSet,
$ambientCoefficient, $diffuseCoefficient, $specularCoefficient,
$specularReflectionCoefficient,
Vector $oCameraPosition
)
{
$this->oAmbientLightColor = $oAmbientLightColor;
$this->setLightSetEntries($oLightSet);
$this->setCoefficients($ambientCoefficient, $diffuseCoefficient, $specularCoefficient);
$this->setSpecularReflectionCoefficient($specularReflectionCoefficient);
$this->oCameraPosition = $oCameraPosition;
}
/**
* Es muss mindestens 1 Lichtquelle definiert sein.
* @param LightSet $oLightSet
* @throws Exception
*/
private function setLightSetEntries(LightSet $oLightSet)
{
$aLightSetEntry = $oLightSet->getEntries();
if(count($aLightSetEntry) < 1)
throw new Exception('lights');

$this->aLightSetEntry = $aLightSetEntry;
}
/**
* Jeder Koeffizient muss >=0 und <= 1 sein.
* @param number $coefficient
* @throws Exception
*/
private function checkCoefficient($coefficient)
{
if
(
!is_numeric($coefficient) ||
$coefficient < 0 ||
$coefficient > 1
)
throw new Exception('coefficient');
}
/**
* @param number $ambientCoefficient
* @param number $diffuseCoefficient
* @param number $specularCoefficient
* @throws Exception
*/
private function setCoefficients($ambientCoefficient, $diffuseCoefficient, $specularCoefficient)
{
$this->checkCoefficient($ambientCoefficient);
$this->checkCoefficient($diffuseCoefficient);
$this->checkCoefficient($specularCoefficient);

// Die Summe der Koeffizienten muss im Bereich [0, 1] liegen.
$nSumCoefficients = $ambientCoefficient + $diffuseCoefficient + $specularCoefficient;
if
(
$nSumCoefficients < 0 ||
$nSumCoefficients > 1
)
throw new Exception('coefficients');

$this->nAmbientCoefficient = $ambientCoefficient;
$this->nDiffuseCoefficient = $diffuseCoefficient;
$this->nSpecularCoefficient = $specularCoefficient;
}
/**
* @param number $specularReflectionCoefficient
* @throws Exception
*/
private function setSpecularReflectionCoefficient($specularReflectionCoefficient)
{
if
(
!is_numeric($specularReflectionCoefficient) ||
$specularReflectionCoefficient < 0
)
throw new Exception('specular reflection coefficient');

$this->nSpecularReflectionCoefficient = $specularReflectionCoefficient;
}
/**
* @param Vector $oPosition Position von Punkt P
* @param Vector $oNormal Normale auf einen Punkt P
* @return Color
*/
function getColor(Vector $oPosition, Vector $oNormal)
{
// L(ambient) = K(ambient) * I(ambient):
// dieser Teil ist bei jedem getColor-Aufruf gleich!
$oColor = $this->ambient->multiplyByScalar($this->nAmbientCoefficient);

// je Lichtquelle:
foreach($this->aLightSetEntry as $oLightSetEntry)
{
// Einheits-Vektor von Position zum Light:
$oLightDirection = $oLightSetEntry->getPosition()->sub($oPosition)->normalized();

// Einheits-Vektor von Position zur Kamera:
$oViewDirection = $oCameraPosition->sub($oPosition)->normalized();

// diffuse und specular:
// L(diffuse) = K(diffus) * (normal dot light)
// L(specular) = K(specular) * (normal dot half)^p mit half = normalize(light + view)
$oHalfLightView = $this->oViewDirection->add($oLightDirection)->normalized();
if
(
($nCosDiffuse = $oNormal->dot($oLightDirection)) > 0 &&
($nCosSpecular = $oNormal->dot($oHalfLightView)) > 0
)
{
$nFactorDiffuse = $this->nDiffuseCoefficient * $nCosDiffuse;
$nFactorSpecular = pow($this->nSpecularCoefficient * $nCosSpecular, $this->nSpecularReflectionCoefficient);

// Farbe der Lichtquelle:
$oLightType = $oLightSetEntry->getLight()->getTechniqueCommon();

// diffuse und specular Licht hinzufügen:
$oColor->add($oLightType->multiplyByScalar($nFactorDiffuse + $nFactorSpecular));
}
}

return $oColor;
}
}

Ich übergebe hier NICHT die Einheits-Vektoren von einem zu shadernden Punkt P in Richtung Lichtquellen bzw. in Richtung Kamera, sondern übergebe nur deren Positionen. Die für den Shader benötigten Einheits-Vektoren berechne ich INNERHALB vom Shader.

Ich dachte mir folgendes dabei:

1)

Ich muss je Berechnung zu einem Punkt P weniger Daten übergeben: nur die Position von P und seine Normale darauf.
Alle anderen Daten zu Lichtern oder zur Kamera übergebe ich einmalig im Konstruktor.

2)

Die Vektoren von P in Richtung zu den Lichtern und zur Kamera berechne ich innerhalb meiner shader-Methode.
Dadurch ist gleich sichergestellt, dass ich dann tatsächlich Einheits-Vektoren habe.

Ist das OK so? Was ist eure Meinung dazu? :)

EDIT: noch ein paar Kleinigkeiten an den Code-Kommentaren geändert :)