Android OpenGL ES 3.0 开发 :3D实例化(Instancing 3D)

1.OpenGL ES 实例化(Instancing)

  • OpenGL ES 实例化(Instancing)是一种只调用一次渲染函数就能绘制出很多物体的技术,可以实现将数据一次性发送给 GPU ,告诉 OpenGL ES 使用一个绘制函数,将这些数据绘制成多个物体。

  • 实例化(Instancing)避免了 CPU 多次向 GPU 下达渲染命令(避免多次调用 glDrawArrays 或 glDrawElements 等绘制函数),节省了绘制多个物体时 CPU 与 GPU 之间的通信时间,提升了渲染性能。

2.使用实例化渲染需要使用的绘制接口

//普通渲染
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);

相对于普通绘制,实例化绘制多了一个参数 instancecount,表示需要渲染的实例数量,调用完实例化绘制函数后,我们便将绘制数据一次性发送给GPU,然后告诉它该如何使用一个函数来绘制这些实例。

实例化(Instancing)的目标并不是实现将同一物体绘制多次,而是能基于某一物体绘制出位置、大小、形状或者颜色不同的多个物体。OpenGL ES 着色器中有一个与实例化绘制相关的内建变量 gl_InstanceID

gl_InstanceID 表示当前正在绘制实例的 ID ,每个实例对应一个唯一的 ID ,通过这个 ID 可以轻易实现基于一个物体而绘制出位置、大小、形状或者颜色不同的多个物体(实例)。

3.3D实例化 实战

3.1 顶点着色器
#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;

}
  • 相对于其他的,多了一个offset ,这个表示多个实例的偏移量
  • 注意MVP矩阵是:观察矩阵x模型矩阵x投影矩阵 然后再跟顶点相乘就是顶点的位置了
  • transpose:这个函数是求转置矩阵
  • inverse:这个是求逆矩阵的
3.2片元着色器
#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);
}

根据传进来的参数,计算光照以及衰减,最后将光照跟颜色相乘得到最终的颜色

3.2 c++代码实现
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);

}
  • 将MVP矩阵和模型矩阵传给shader
  • 将光线的数据传给shader
  • 将衰减参数传给shader
  • glDrawArraysInstanced 进行绘制

猜你喜欢

转载自blog.csdn.net/u014078003/article/details/127975093