Android OpenGL ES 3.0 Development: 3D Instancing (Instancing 3D)

1. OpenGL ES instantiation (Instancing)

  • OpenGL ES instantiation (Instancing) is a technology that can draw many objects by calling a rendering function only once. It can send data to the GPU at one time and tell OpenGL ES to use a drawing function to draw these data into multiple objects. .

  • Instancing prevents the CPU from issuing rendering commands to the GPU multiple times (avoiding multiple calls to drawing functions such as glDrawArrays or glDrawElements), saves communication time between the CPU and GPU when drawing multiple objects, and improves rendering performance.

2. Use the drawing interface that needs to be used for instanced rendering

//普通渲染
glDrawArrays (GLenum mode, GLint first, GLsizei count);

glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);

//实例化渲染
glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount);

glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);

Compared with normal drawing, instanced drawing has one more parameter instancecount, indicating the number of instances to be rendered. After calling the instanced drawing function, we send the drawing data to the GPU at one time, and then tell it how to use a function to draw these instance.

The goal of instancing is not to draw the same object multiple times, but to draw multiple objects with different positions, sizes, shapes, or colors based on an object. There is a built-in variable in OpenGL ES shaders related to instanced drawing gl_InstanceID.

gl_InstanceIDIndicates the ID of the instance currently being drawn. Each instance corresponds to a unique ID. Through this ID, multiple objects (instances) with different positions, sizes, shapes or colors can be drawn easily based on one object.

3.3D instantiation combat

3.1 Vertex shader
#version 300 es
precision mediump float;
layout(location = 0) in vec4 a_position;   //顶点
layout(location = 1) in vec2 a_texCoord;   //材质顶点
layout(location = 2) in vec3 a_normal;  
layout(location = 3) in vec3 offset;      //多个实例化的偏移量

out vec3 normal;
out vec3 fragPos;               //输出到片元着色器的 位置信息
out vec2 v_texCoord;            //输出到片元的材质顶点

uniform mat4 u_MVPMatrix;       //MVP矩阵
uniform mat4 u_ModelMatrix;     //模型矩阵


void main() {
    gl_Position = u_MVPMatrix * (a_position + vec4(offset, 1.0));   //投影变换之后,输出的是gl_Position,也就是说你最终画在屏幕里面的哪个位置
    fragPos = vec3(u_ModelMatrix * (a_position + vec4(offset, 1.0))); //当前片元坐标:也就是通过旋转等变换之后的坐标
    //transpose:矩阵求转置矩阵     inverse :求矩阵的逆矩阵
    normal = mat3(transpose(inverse(u_ModelMatrix))) * a_normal;
    v_texCoord = a_texCoord;

}
  • Compared with others, there is one more offset, which represents the offset of multiple instances
  • Note that the MVP matrix is: observation matrix x model matrix x projection matrix and then multiplied by the vertex is the position of the vertex
  • transpose: This function is to find the transpose matrix
  • inverse: This is the inverse matrix
3.2 Fragment shaders
#version 300 es
precision mediump float;

struct Light {
    vec3 position;          //光的位置
    vec3 direction;         //方向
        vec3 color;         //光的颜色
        float cutOff;           //内切光角
        float outerCutOff;      //外切光角
        float constant;     //衰减系数
        float linear;       //一次项
        float quadratic;    //二次项
};

in vec3 normal;
in vec3 fragPos;
in vec2 v_texCoord;

layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;
uniform vec3 viewPos;
uniform Light light;

void main() {
    vec4 objectColor = texture(s_TextureMap, v_texCoord);   //拿到材质指定位置的颜色
    vec3 lightDir = normalize(light.position - fragPos);  //归一化

    float theta = dot(lightDir, normalize(-light.direction));  //dot 点乘计算夹角
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);  //clamp将一个值截取到两个值之间

    // Ambient  环境光
    float ambientStrength = 0.4;
    vec3 ambient = ambientStrength * light.color;

    // Diffuse 散射光
    vec3 norm = normalize(normal);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * light.color;

    // Specular 镜面光
    vec3 viewDir = normalize(viewPos - fragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
    vec3 specular = spec * light.color;

    float distance    = length(light.position - fragPos);  //计算两个点的距离
    //计算衰减
    float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));

    diffuse  *= attenuation;
    specular *= attenuation;

    diffuse *= intensity;
    specular*= intensity;

    vec3 finalColor = (ambient + diffuse + specular) * vec3(objectColor);
    outColor = vec4(finalColor, 1.0f);
}

According to the parameters passed in, calculate the illumination and attenuation, and finally multiply the illumination and color to get the final color

3.2 c++ code implementation
void MSInstancing3DSample::PaintGL() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    value += 1;
    value = value % 360;
    value = value % 360;

    //转化为弧度角
    float radiansX = static_cast<float>(MATH_PI / 180.0f * value);

    glm::mat4 modelMat = glm::mat4(1.0f);  //模型矩阵
    modelMat = glm::scale(modelMat, glm::vec3(1.2f, 1.2f, 1.2f));
    modelMat = glm::rotate(modelMat, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
    modelMat = glm::rotate(modelMat, radiansX, glm::vec3(0.0f, 1.0f, 0.0f));
    modelMat = glm::translate(modelMat, glm::vec3(0.0f, 0.0f, 0.0f));

    glm::mat4 Projection =glm::perspective(glm::radians(60.0f), (float) 9 / (float) 18, 0.1f,
                                           1000.0f);

    // viewMat matrix
    glm::mat4 viewMat = glm::lookAt(
            glm::vec3(-3, 0, 3), // Camera is at (0,0,1), in World Space
            glm::vec3(0, 0, 0), // and looks at the origin
            glm::vec3(0, 1, 0)  // Head is up (set to 0,-1,0 to look upside-down)
    );


    glm::mat4 mvpMatrix = Projection * viewMat * modelMat;


    m_pOpenGLShader->Bind();  //内部调用的就是使用程序
    m_pOpenGLShader->SetUniformValue("u_MVPMatrix", mvpMatrix);
    m_pOpenGLShader->SetUniformValue("u_ModelMatrix", modelMat);


    glBindVertexArray(m_VaoId);

    m_pOpenGLShader->SetUniformValue("viewPos",glm::vec3(0.0f, 0.0f, 3.0f));

    m_pOpenGLShader->SetUniformValue("s_TextureMap",0);
    // Bind the RGBA map
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_texID[3]);

    // 设置光源的位置、颜色和方向
    m_pOpenGLShader->SetUniformValue("light.position",glm::vec3(0.0f, 0.0f, 3.0f));
    m_pOpenGLShader->SetUniformValue("light.color",glm::vec3(1.0f, 1.0f, 1.0f));
    m_pOpenGLShader->SetUniformValue("light.direction",glm::vec3(0.0f, 0.0f, -1.0f));

    // 用于计算边缘的过度,cutOff 表示内切光角,outerCutOff 表示外切光角
    m_pOpenGLShader->SetUniformValue("light.cutOff",glm::cos(glm::radians(10.5f)));
    m_pOpenGLShader->SetUniformValue("light.outerCutOff",glm::cos(glm::radians(11.5f)));


    // 衰减系数,常数项 constant,一次项 linear 和二次项 quadratic。

    m_pOpenGLShader->SetUniformValue("light.constant",1.0f);
    m_pOpenGLShader->SetUniformValue("light.linear",0.09f);
    m_pOpenGLShader->SetUniformValue("light.quadratic",0.032f);


    glDrawArraysInstanced(GL_TRIANGLES, 0, 36, 125);
    glBindVertexArray(0);

}
  • Pass the MVP matrix and model matrix to the shader
  • Pass the light data to the shader
  • Pass the attenuation parameter to the shader
  • glDrawArraysInstanced for drawing

Guess you like

Origin blog.csdn.net/u014078003/article/details/127975093