GLSL Shader – Schnittstelle zu der in den OpenGL-Tutorials verwendeten Mathematik Bibliothek

In unserem heutigen Artikel gehen wir der Frage nach, wie sich Vektoren, Matrizen (einzeln und als Arrays) an ein Shader Programm übergeben lassen. Mit den Vektor- und Matrizen-Klassen der von uns verwendeten Mathematik Bibliothek (D3DXVECTOR3, D3DXVECTOR4, D3DXMATRIXA16, usw.) können die Shader natürlich nichts anfangen. Kurzum, wir benötigen eine Schnittstelle zwischen den GLSL Shader Programmen auf der einen und der verwendeten Mathematik Bibliothek auf der anderen Seite. Realisiert wird dies mithilfe einer Reihe von Helper-Funktionen, welche die bibliotheksspezifischen Vektor- und Matrizen-Klassenobjekte in ein für die Shader verständliches Format konvertieren.



Zunächst einmal müssen wir natürlich klären, wie sich ein Vektor oder eine Matrix an ein Shader Programm übergeben lässt. Nun, die Antwort ist einfach – als simples float-Array:

// dreidimensionaler Vektor:
float g_ShaderVector3[3];

// vierdimensionaler Vektor:
float g_ShaderVector4[4];


Hinweis:
In unseren Programmbeispielen verwenden wir global definierte float-Arrays. Auf diese Weise können die Methoden der CGLSLShader-Klasse genauso darauf zugreifen wie die einzelnen Schnittstellen-Helper-Funktionen.

Zum Speichern einer 2x2-Matrix mit ihren 4 Komponenten benötigen wir ein float[4]-Array. Für eine 3x3-Matrix mit ihren 9 Komponenten benötigen wir dementsprechend ein float[9]-Array und für eine 4x4-Matrix mit ihren 16 Komponenten ein float[16]-Array:

float g_ShaderMatrix2X2[4];
float g_ShaderMatrix3X3[9];
float g_ShaderMatrix4X4[16];


Wollen wir einem Shader nun anstelle eines einzelnen Vektors oder einer einzelnen Matrix ein Vektoren- bzw. Matrizen-Array übergeben, so müssen wir unsere float-Arrays dementsprechend dimensionieren:

// Arrays für bis zu 256 Vektoren:
float g_ShaderVector3Array[768]; // 256 * 3
float g_ShaderVector4Array[1024]; // 256 * 4

// Arrays für bis zu 256 Matrizen:
float g_ShaderMatrix4X4Array[4096]; // 256 * 16
float g_ShaderMatrix3X3Array[2304]; // 256 * 9
float g_ShaderMatrix2X2Array[1024]; // 256 * 4


Kommen wir nun zu den einzelnen Helper-Funktionen. Allen Funktionen ist gemein, dass sie als Rückgabewert einen Zeiger auf die Speicheradresse des an den Shader zu übergebenden float-Arrays liefern.

Die ersten beiden Funktionen konvertieren einen D3DXVECTOR3- bzw. D3DXVECTOR4-Vektor in ein für die Shader verständliches Format:

INLINE float* Build_ShaderVector3(D3DXVECTOR3* pVector)
{
    g_ShaderVector3[0] = pVector->x;
    g_ShaderVector3[1] = pVector->y;
    g_ShaderVector3[2] = pVector->z;

    return g_ShaderVector3;
}


INLINE float* Build_ShaderVector4(D3DXVECTOR4* pVector)
{
    g_ShaderVector4[0] = pVector->x;
    g_ShaderVector4[1] = pVector->y;
    g_ShaderVector4[2] = pVector->z;
    g_ShaderVector4[3] = pVector->w;

    return g_ShaderVector4;
}


Die nachfolgenden Funktionen erwarten als Parameter die Adresse eines D3DXMATRIXA16-Matrix-Objekts und konvertieren dieses wahlweise in eine 2x2-, 3x3- bzw. 4x4-Matrix.

Hinweis:
Für die Transformation von Vertex Normalen im Vertex Shader benötigt man lediglich Rotationsmatrizen. Da für die Beschreibung von Rotationen 3x3-Matrizen vollkommen ausreichend sind, ist es völlig unnötig und eine Verschwendung von Bandbreite, stattdessen eine 4x4-Matrix an den entsprechenden Shader zu senden.

INLINE float* Build_ShaderMatrix2X2(D3DXMATRIXA16* pMatrix)
{
    g_ShaderMatrix2X2[0] = pMatrix->_11;
    g_ShaderMatrix2X2[1] = pMatrix->_12;

    g_ShaderMatrix2X2[2] = pMatrix->_21;
    g_ShaderMatrix2X2[3] = pMatrix->_22;

    return g_ShaderMatrix2X2;
}


INLINE float* Build_ShaderMatrix3X3(D3DXMATRIXA16* pMatrix)
{
    g_ShaderMatrix3X3[0] = pMatrix->_11;
    g_ShaderMatrix3X3[1] = pMatrix->_12;
    g_ShaderMatrix3X3[2] = pMatrix->_13;

    g_ShaderMatrix3X3[3] = pMatrix->_21;
    g_ShaderMatrix3X3[4] = pMatrix->_22;
    g_ShaderMatrix3X3[5] = pMatrix->_23;

    g_ShaderMatrix3X3[6] = pMatrix->_31;
    g_ShaderMatrix3X3[7] = pMatrix->_32;
    g_ShaderMatrix3X3[8] = pMatrix->_33;

    return g_ShaderMatrix3X3;
}


INLINE float* Build_ShaderMatrix4X4(D3DXMATRIXA16* pMatrix)
{
    g_ShaderMatrix4X4[0] = pMatrix->_11;
    g_ShaderMatrix4X4[1] = pMatrix->_12;
    g_ShaderMatrix4X4[2] = pMatrix->_13;
    g_ShaderMatrix4X4[3] = pMatrix->_14;

    g_ShaderMatrix4X4[4] = pMatrix->_21;
    g_ShaderMatrix4X4[5] = pMatrix->_22;
    g_ShaderMatrix4X4[6] = pMatrix->_23;
    g_ShaderMatrix4X4[7] = pMatrix->_24;

    g_ShaderMatrix4X4[8]  = pMatrix->_31;
    g_ShaderMatrix4X4[9]  = pMatrix->_32;
    g_ShaderMatrix4X4[10] = pMatrix->_33;
    g_ShaderMatrix4X4[11] = pMatrix->_34;

    g_ShaderMatrix4X4[12] = pMatrix->_41;
    g_ShaderMatrix4X4[13] = pMatrix->_42;
    g_ShaderMatrix4X4[14] = pMatrix->_43;
    g_ShaderMatrix4X4[15] = pMatrix->_44;

    return g_ShaderMatrix4X4;
}


Die nachfolgenden Funktionen ermöglichen es, Arrays mit bis zu 256 Vektoren oder Matrizen an einen Shader zu senden. Arrays von Matrizen und Vektoren werden beispielsweise im Zuge des Vertex Skinnings oder des Geometry Instancings benötigt.

INLINE float* Build_ShaderVector3Array(D3DXVECTOR3* pVectorArray,
                                       long NumArrayElements)
{
for(g_ArrayCounter = 0; g_ArrayCounter < NumArrayElements;
    g_ArrayCounter++)
{
g_ArrayIndex = g_ArrayCounter*3;

g_ShaderVector3Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].x;
g_ArrayIndex++;
g_ShaderVector3Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].y;
g_ArrayIndex++;
g_ShaderVector3Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].z;
}

return g_ShaderVector3Array;
}


INLINE float* Build_ShaderVector4Array(D3DXVECTOR4* pVectorArray,
                                       long NumArrayElements)
{
for(g_ArrayCounter = 0; g_ArrayCounter < NumArrayElements;
    g_ArrayCounter++)
{
g_ArrayIndex = g_ArrayCounter*4;

g_ShaderVector4Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].x;
g_ArrayIndex++;
g_ShaderVector4Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].y;
g_ArrayIndex++;
g_ShaderVector4Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].z;
g_ArrayIndex++;
g_ShaderVector4Array[g_ArrayIndex] = pVectorArray[g_ArrayCounter].w;
}

return g_ShaderVector4Array;
}


INLINE float* Build_ShaderMatrix2X2Array(D3DXMATRIXA16* pMatrixArray,
                                         long NumArrayElements)
{
for(g_ArrayCounter = 0; g_ArrayCounter < NumArrayElements;
    g_ArrayCounter++)
{
g_ArrayIndex = g_ArrayCounter*4;

g_ShaderMatrix2X2Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._11;
g_ArrayIndex++;
g_ShaderMatrix2X2Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._12;
g_ArrayIndex++;

g_ShaderMatrix2X2Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._21;
g_ArrayIndex++;
g_ShaderMatrix2X2Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._22;
}

return g_ShaderMatrix2X2Array;
}


INLINE float* Build_ShaderMatrix3X3Array(D3DXMATRIXA16* pMatrixArray,
                                         long NumArrayElements)
{
for(g_ArrayCounter = 0; g_ArrayCounter < NumArrayElements;
    g_ArrayCounter++)
{
g_ArrayIndex = g_ArrayCounter*9;

g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._11;
g_ArrayIndex++;
g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._12;
g_ArrayIndex++;
g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._13;
g_ArrayIndex++;

g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._21;
g_ArrayIndex++;
g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._22;
g_ArrayIndex++;
g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._23;
g_ArrayIndex++;

g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._31;
g_ArrayIndex++;
g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._32;
g_ArrayIndex++;
g_ShaderMatrix3X3Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._33;
}

return g_ShaderMatrix3X3Array;
}


INLINE float* Build_ShaderMatrix4X4Array(D3DXMATRIXA16* pMatrixArray,
                                         long NumArrayElements)
{
for(g_ArrayCounter = 0; g_ArrayCounter < NumArrayElements;
    g_ArrayCounter++)
{
g_ArrayIndex = g_ArrayCounter*16;

g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._11;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._12;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._13;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._14;
g_ArrayIndex++;

g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._21;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._22;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._23;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._24;
g_ArrayIndex++;

g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._31;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._32;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._33;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._34;
g_ArrayIndex++;

g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._41;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._42;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._43;
g_ArrayIndex++;
g_ShaderMatrix4X4Array[g_ArrayIndex] = pMatrixArray[g_ArrayCounter]._44;
}

return g_ShaderMatrix4X4Array;
}