La renderització instanciada és una tècnica avançada en OpenGL que permet dibuixar múltiples còpies d'un mateix objecte amb una sola crida de dibuix. Això és especialment útil per a escenes amb molts objectes idèntics, com ara arbres en un bosc o partícules en un sistema de partícules. Aquesta tècnica millora significativament el rendiment en comparació amb la renderització de cada objecte individualment.

Conceptes Clau

  1. Instàncies: Còpies d'un mateix objecte que es dibuixen amb una sola crida de dibuix.
  2. Atributs d'Instància: Atributs que varien entre instàncies, com ara la posició, la rotació o l'escala.
  3. glDrawArraysInstanced: Funció d'OpenGL per dibuixar instàncies d'un objecte.
  4. glVertexAttribDivisor: Funció per especificar la freqüència d'actualització dels atributs d'instància.

Exemples Pràctics

Configuració de les Dades

Primer, necessitem configurar les dades dels nostres objectes i els atributs d'instància. Suposem que volem dibuixar múltiples quadrats en diferents posicions.

// Coordenades dels vèrtexs d'un quadrat
float vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.5f,  0.5f, 0.0f,
    -0.5f,  0.5f, 0.0f
};

// Índexs dels vèrtexs per formar un quadrat
unsigned int indices[] = {
    0, 1, 2,
    2, 3, 0
};

// Posicions de les instàncies
glm::vec2 translations[100];
int index = 0;
float offset = 0.1f;
for (int y = -10; y < 10; y += 2) {
    for (int x = -10; x < 10; x += 2) {
        glm::vec2 translation;
        translation.x = (float)x / 10.0f + offset;
        translation.y = (float)y / 10.0f + offset;
        translations[index++] = translation;
    }
}

Configuració dels Buffers

Després, configurem els buffers per als vèrtexs i els atributs d'instància.

unsigned int VBO, VAO, EBO, instanceVBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glGenBuffers(1, &instanceVBO);

glBindVertexArray(VAO);

// Buffer de vèrtexs
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// Buffer d'índexs
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// Atributs de vèrtexs
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// Buffer d'instàncies
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 100, &translations[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribDivisor(1, 1); // Actualitza l'atribut d'instància per cada instància

glBindVertexArray(0);

Shader

El shader ha de ser capaç de gestionar els atributs d'instància.

Vertex Shader:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aOffset;

void main() {
    gl_Position = vec4(aPos.x + aOffset.x, aPos.y + aOffset.y, aPos.z, 1.0);
}

Fragment Shader:

#version 330 core
out vec4 FragColor;

void main() {
    FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}

Dibuixar les Instàncies

Finalment, utilitzem glDrawElementsInstanced per dibuixar les instàncies.

// Dibuixar les instàncies
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, 100);
glBindVertexArray(0);

Exercici Pràctic

Exercici

  1. Modifica el codi anterior per dibuixar cercles en lloc de quadrats.
  2. Afegeix un atribut d'instància per variar el color de cada instància.

Solució

Vertex Shader:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aOffset;
layout (location = 2) in vec3 aColor;

out vec3 ourColor;

void main() {
    gl_Position = vec4(aPos.x + aOffset.x, aPos.y + aOffset.y, aPos.z, 1.0);
    ourColor = aColor;
}

Fragment Shader:

#version 330 core
out vec4 FragColor;
in vec3 ourColor;

void main() {
    FragColor = vec4(ourColor, 1.0);
}

Configuració dels Buffers:

// Colors de les instàncies
glm::vec3 colors[100];
for (int i = 0; i < 100; i++) {
    colors[i] = glm::vec3((float)i / 100.0f, 0.5f, 0.2f);
}

unsigned int colorVBO;
glGenBuffers(1, &colorVBO);
glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * 100, &colors[0], GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(2);
glVertexAttribDivisor(2, 1); // Actualitza l'atribut de color per cada instància

Conclusió

La renderització instanciada és una tècnica poderosa per optimitzar la renderització d'objectes repetitius en una escena. Utilitzant atributs d'instància i funcions com glDrawElementsInstanced, podem millorar significativament el rendiment de les nostres aplicacions gràfiques. Practicar amb diferents tipus d'objectes i atributs d'instància és una bona manera de dominar aquesta tècnica.

© Copyright 2024. Tots els drets reservats