实例化渲染

 

  实例化渲染适用于使用同一个模型渲染多次的情景,比如草地,一堆岩石等。

 

  1、先看一个渲染方形100次的例子:

  

  其顶点着色器会有少许适应实例化渲染的变化

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

out vec3 fColor;

uniform vec2 offsets[100];

void main()
{
    vec2 offset = offsets[gl_InstanceID];
    gl_Position = vec4(aPos + offset, 0.0, 1.0);
    fColor = aColor;
}

  可以看到着色器中传入了100个偏移向量。顶点着色器中内置了一个变量gl_InstanceID,此变量表示当前绘制的实例编号(从0开始)。那么我们可以用此内置变量索引对应的偏移向量,用来在屏幕不同的地方绘制方形。

  向顶点着色器中传入100个偏移向量的代码如下:

shader.use();
for(unsigned int i = 0; i < 100; i++)
{
    stringstream ss;
    string index;
    ss << i; 
    index = ss.str(); 
    shader.setVec2(("offsets[" + index + "]").c_str(), translations[i]);
}

  渲染代码如下:

void Quad::Draw_Instancing() {
    glBindVertexArray(VAO);
    glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);
    glBindVertexArray(0);
}

 

  2、使用实例化数组

  向着色器中传入uniform数据是有最大数量限制,我们可以使用实例化数组传入数据,可以解决数量限制问题,当然还是会限制于内存大小的,只要内存允许就可以极大数量的传入数据。

  顶点着色器如下:

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

out vec3 fColor;

void main()
{
    vec2 pos=aPos*(gl_InstanceID/100.0);
    gl_Position = vec4(pos+aOffset, 0.0, 1.0);
    fColor=aColor;
}

  现在去掉了uniform 偏移向量,改为由顶点属性传入了

layout (location = 2) in vec2 aOffset; 

  那么同其它顶点属性一样得将偏移向量存入缓存

glGenBuffers(1, &this->instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, this->instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 100, &this->translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

  设置顶点属性指针

glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->instanceVBO);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(2, 1);
glBindVertexArray(0);

  可以发现现在比以往多加了一行代码

glVertexAttribDivisor(2, 1);

  其中第一个参数指顶点属性2,第二个参数可以取0,1,2……。当取值0表示逐顶点更新,也就是默认的方式;取值1时表示逐实例更新;其它值x,表示每x个实例更新一次。

  

 

  3、看一个复杂点的例子,绘制行星带

  

  其中包含一个大的行星,然后一个环绕它的岩石带。这些岩石是一样的,我们当然可以将一个岩石绘制多次,每个岩石只有model矩阵不同,这样的话在我的机器上可以正常绘制10000个岩石,帧率还能在36帧。

  使用实例化渲染后可以正常渲染1000000个岩石,帧率在20帧,可以看出实例化渲染可以比正常渲染快100倍,当然这是就这个例子而言。

  

  实例化渲染需要在顶点着色器中的顶点属性中传入model矩阵

layout (location = 3) in mat4 model;

  传入着色器的代码如下:

void PlanetaryBelt::setupBuffer(Model* obj) {
    
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, this->amount * sizeof(glm::mat4), &this->modelMatrices[0], GL_STATIC_DRAW);

    std::vector<AssimpMesh>* meshes = obj->assimpModel->GetAssimpMesh();
    for (unsigned int i = 0; i < meshes->size(); i++) {
        glBindVertexArray(meshes->at(i).GetVAOBuffer());
        GLsizei vec4Size = sizeof(glm::vec4);
        glEnableVertexAttribArray(3);
        glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)0);
        glEnableVertexAttribArray(4);
        glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(vec4Size));
        glEnableVertexAttribArray(5);
        glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(2 * vec4Size));
        glEnableVertexAttribArray(6);
        glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(3 * vec4Size));
        glVertexAttribDivisor(3, 1);
        glVertexAttribDivisor(4, 1);
        glVertexAttribDivisor(5, 1);
        glVertexAttribDivisor(6, 1);
        glBindVertexArray(0);
    }
}

  可以看到代码较之前略有变化,因为顶点属性允许的最大数据大小为vec4,所以我们要在顶点属性位置3,4,5,6分别传入矩阵的一列值,然后在着色器中获取一个mat4。

  

posted @ 2019-09-06 15:09 茶飘香~ 阅读(...) 评论(...) 编辑 收藏