OpenGL-Texture-Buffer-Objekte

Texture-Buffer-Objekte wurden mit der OpenGL-Spezifikation 3.0 eingeführt und dienen im Unterschied zu den Uniform-Buffer-Objekten dem dynamischen Austausch sehr großer Datenmengen zwischen dem Haupt- und dem Grafikspeicher. Zu den Einsatzmöglichkeiten zählt die Speicherung der Geometriedaten eines 3D-Modells oder die der Höhenwerte und Textur-Gewichtungsfaktoren eines Terrains sowie die Darstellung einer sehr großen Anzahl von 3D-Objekten im Rahmen des Geometry-Instancings mit nur einem einzigen Draw Call.
Wie im nachfolgenden Beispiel gezeigt, erfolgt der Zugriff auf einen Texture Buffer in einem Shader-Programm mithilfe der texelFetch()-GLSL-Shaderfunktion:

uniform samplerBuffer SunInstancesTextureBuffer;

[...]


void main()
{
int Index = 4*gl_InstanceID;

mat4 matWorldViewProjection = mat4(texelFetch(SunInstancesTextureBuffer,
                                   Index++),
                                   texelFetch(SunInstancesTextureBuffer,
                                   Index++),
                                   texelFetch(SunInstancesTextureBuffer,
                                   Index++),
                                   texelFetch(SunInstancesTextureBuffer,
                                   Index));

[...]

}




Die Übergabe eines Texture-Buffer-Objekts an einen Shader erfolgt in unseren Programmbeispielen mithilfe der CGLSLShader-Methode Set_TextureBuffer():

void CGLSLShader::Set_TextureBuffer(long Stage, CTexture* pTexture,
                                    char* pParameter)
{
    // Textur bereits gesetzt:
    if(g_StageTexture[Stage] == pTexture)
        return;

    if(pTexture)
    {
        if(Stage > TextureStagesUsed)
            TextureStagesUsed = Stage;

        glActiveTexture(GL_TEXTURE0+Stage);

        glBindTexture(GL_TEXTURE_BUFFER, pTexture->TextureID);

        glUniform1i(glGetUniformLocation(ShaderProgram, pParameter), Stage);
    }

    g_StageTexture[Stage] = pTexture;
}


Für die praktische Handhabung dieser Buffer-Objekte nutzen wir ferner die CTextureBufferObject-Klasse unseres OpenGL/OpenCL-basierten Grafik- und Physik-Frameworks:

class CTextureBufferObject
{
public:

    CTexture* Texture;

    GLuint TextureBufferID;

    long NumBufferElements;
    long NumBufferElementsTU;
    long NumBufferElementsTV;

    CTextureBufferObject();
    ~CTextureBufferObject();

    void Init_TextureBuffer(long numBufferElements, long numColorChannels,
                            float* pData = NULL);
    void Init_TextureBuffer(long numBufferElementsTU,
                            long numBufferElementsTV,
                            long numColorChannels, float* pData = NULL);
    void Update_Buffer(float* pData, long Size);
    void Update_Buffer(float* pData, long Offset, long Size);
};

CTextureBufferObject::CTextureBufferObject()
{
    Texture = NULL;
    TextureBufferID = 0;

    NumBufferElements = 0;
    NumBufferElementsTU = 0;
    NumBufferElementsTV = 0;

    Texture = new CTexture;
}

CTextureBufferObject::~CTextureBufferObject()
{
    if(TextureBufferID > 0)
        glDeleteBuffers(1, &TextureBufferID);

    SAFE_DELETE(Texture)
}


Im Rahmen der täglichen Arbeit stellt man sich einen Texture Buffer sinnvollerweise als eine Kombination aus einem eindimensionalen Textur- und einem separaten Buffer-Objekt vor (das Textur-Objekt verweist auf den Speicherbereich des Buffer-Objekts). Während der Zugriff auf die im Buffer gespeicherten Daten – wie im einleitenden Source-Code-Beispiel gezeigt – innerhalb eines Shader-Programms über ein der Textur zugeordnetes samplerBuffer-Objekt erfolgt, so ist der Zugriff auf die im Buffer gespeicherten Daten aus dem Hauptprogramm heraus auf direkte Weise möglich. Für die Aktualisierung dieser Daten können die beiden Update_Buffer()-Methoden der CTextureBufferObject-Klasse verwendet werden:

void CTextureBufferObject::Update_Buffer(float* pData, long Size)
{
    glBindBuffer(GL_TEXTURE_BUFFER, TextureBufferID);
    glBufferData(GL_TEXTURE_BUFFER, Size, pData, GL_STATIC_DRAW);
    glBindBuffer(GL_TEXTURE_BUFFER, 0);
}

void CTextureBufferObject::Update_Buffer(float* pData, long Offset,
                                         long Size)
{
    glBindBuffer(GL_TEXTURE_BUFFER, TextureBufferID);
    glBufferSubData(GL_TEXTURE_BUFFER, Offset, Size, pData);
    glBindBuffer(GL_TEXTURE_BUFFER, 0);
}


Bevor nun ein Texture-Buffer-Objekt genutzt werden kann, sind die folgenden Initialisierungsschritte erforderlich:

  • Initialisierung des Buffer-Objekts (TextureBufferID).
  • Initialisierung des Textur-Objekts (TextureID).
  • Anbinden des Buffer-Objekts an das Textur-Objekt.

void CTextureBufferObject::Init_TextureBuffer(long numBufferElementsTU,
     long numBufferElementsTV, long numColorChannels, float* pData)
{
if(TextureBufferID > 0)
    glDeleteBuffers(1, &TextureBufferID);

if(Texture->TextureID > 0)
    glDeleteBuffers(1, &Texture->TextureID);

NumBufferElementsTU = numBufferElementsTU;
NumBufferElementsTV = numBufferElementsTV;

NumBufferElements = NumBufferElementsTU*NumBufferElementsTV;

// Buffer-Objekt initialisieren:

glGenBuffers(1, &TextureBufferID);
glBindBuffer(GL_TEXTURE_BUFFER, TextureBufferID);

unsigned long BufferSize;
     
if(numColorChannels == 4)
    BufferSize = NumBufferElements * 4 * sizeof(float);
else if(numColorChannels == 3)
    BufferSize = NumBufferElements * 3 * sizeof(float);
else if(numColorChannels == 2)
    BufferSize = NumBufferElements * 2 * sizeof(float);
else
    BufferSize = NumBufferElements *  sizeof(float);

if(pData == NULL)
    glBufferData(GL_TEXTURE_BUFFER, BufferSize, 0, GL_STATIC_DRAW);
else
    glBufferData(GL_TEXTURE_BUFFER, BufferSize, pData, GL_STATIC_DRAW);

// Textur-Objekt initialisieren:

glGenTextures(1, &Texture->TextureID);
glBindTexture(GL_TEXTURE_BUFFER, Texture->TextureID);

// Buffer-Objekt an das Textur-Objekt anbinden:

if(numColorChannels == 4)
    glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, TextureBufferID);
else if(numColorChannels == 3)
    glTexBuffer(GL_TEXTURE_BUFFER, GL_RGB32F, TextureBufferID);
else if(numColorChannels == 2)
    glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, TextureBufferID);
else
    glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, TextureBufferID);

glBindTexture(GL_TEXTURE_BUFFER, 0);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
}