官术网_书友最值得收藏!

Dynamically subdividing a plane using the geometry shader with instanced rendering

In order to avoid pushing the same data multiple times, we can exploit the instanced rendering functions. We will now see how we can omit the multiple glDrawElements calls in the previous recipe with a single glDrawElementsInstanced call.

Getting ready

Before doing this, we assume that the reader knows how to use the geometry shader in the OpenGL 3.3 core profile. The code for this recipe is in the Chapter1\SubdivisionGeometryShader_Instanced directory.

How to do it…

Converting the previous recipe to use instanced rendering requires the following steps:

  1. Change the vertex shader to handle the instance modeling matrix and output world space positions (shaders/shader.vert).
    #version 330 core
    layout(location=0) in vec3 vVertex;  
    uniform mat4 M[4];
    void main()
    {
     gl_Position = M[gl_InstanceID]*vec4(vVertex, 1);
    }
  2. Change the geometry shader to replace the MVP matrix with the PV matrix (shaders/shader.geom).
    #version 330 core
    layout (triangles) in;
    layout (triangle_strip, max_vertices=256) out;
    uniform int sub_divisions;
    uniform mat4 PV;
    
    void main()
    {
      vec4 v0 = gl_in[0].gl_Position;
      vec4 v1 = gl_in[1].gl_Position;
      vec4 v2 = gl_in[2].gl_Position;
      float dx = abs(v0.x-v2.x)/sub_divisions;
      float dz = abs(v0.z-v1.z)/sub_divisions;
      float x=v0.x;
      float z=v0.z;
      for(int j=0;j<sub_divisions*sub_divisions;j++) {
     gl_Position = PV * vec4(x,0,z,1); EmitVertex();
     gl_Position = PV * vec4(x,0,z+dz,1); EmitVertex();
     gl_Position = PV * vec4(x+dx,0,z,1); EmitVertex();
     gl_Position = PV * vec4(x+dx,0,z+dz,1); EmitVertex();
        EndPrimitive();
        x+=dx;
        if((j+1) %sub_divisions == 0) {
          x=v0.x;
          z+=dz;
        }
      }
    }
  3. Initialize the per-instance model matrices (M).
    void OnInit() {
      //set the instance modeling matrix
     M[0] = glm::translate(glm::mat4(1), glm::vec3(-5,0,-5));
     M[1] = glm::translate(M[0], glm::vec3(10,0,0));
     M[2] = glm::translate(M[1], glm::vec3(0,0,10));
     M[3] = glm::translate(M[2], glm::vec3(-10,0,0));
      ..
      shader.Use();
        shader.AddAttribute("vVertex");
     shader.AddUniform("PV");
     shader.AddUniform("M");
         shader.AddUniform("sub_divisions");
         glUniform1i(shader("sub_divisions"), sub_divisions);
     glUniformMatrix4fv(shader("M"), 4, GL_FALSE, glm::value_ptr(M[0])); 
      shader.UnUse();
  4. Render instances using the glDrawElementInstanced call.
    void OnRender() {
      glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
      glm::mat4 T =glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, dist));
      glm::mat4 Rx=glm::rotate(T,rX,glm::vec3(1.0f, 0.0f, 0.0f));
      glm::mat4 V =glm::rotate(Rx,rY,glm::vec3(0.0f, 1.0f,0.0f));
     glm::mat4 PV = P*V;
      
      shader.Use();
     glUniformMatrix4fv(shader("PV"),1,GL_FALSE,glm::value_ptr(PV));
        glUniform1i(shader("sub_divisions"), sub_divisions);
     glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0, 4);
      shader.UnUse();
      glutSwapBuffers();
    }

How it works…

First, we need to store the model matrix for each instance separately. Since we have four instances, we store a uniform array of four elements (M[4]). Second, we multiply the per-vertex position (vVertex) with the model matrix for the current instance (M[gl_InstanceID]).

Tip

Note that the gl_InstanceID built-in attribute will be filled with the index of each instance automatically at the time of the glDrawElementsInstanced call. Also note that this built-in attribute is only accessible in the vertex shader.

The MVP matrix is omitted from the geometry shader since now the input vertex positions are in world space. So we only need to multiply them with the combined view projection (PV) matrix. On the application side, the MV matrix is removed. Instead, we store the model matrix array for all four instances (glm::mat4 M[4]). The values of these matrices are initialized in the OnInit() function as follows:

M[0] = glm::translate(glm::mat4(1), glm::vec3(-5,0,-5));
M[1] = glm::translate(M[0], glm::vec3(10,0,0));
M[2] = glm::translate(M[1], glm::vec3(0,0,10));
M[3] = glm::translate(M[2], glm::vec3(-10,0,0));

The rendering function, OnRender(), creates the combined view projection matrix (PV) and then calls glDrawElementsInsntanced. The first four parameters are similar to the glDrawElements function. The final parameter is the total number of instances desired. Instanced rendering is an efficient mechanism for rendering identical geometry whereby the GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER bindings are shared between instances allowing the GPU to do efficient resource access and sharing.

void OnRender() {
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glm::mat4 T = glm::translate(glm::mat4(1.0f),glm::vec3(0.0f, 0.0f, dist));
  glm::mat4 Rx = glm::rotate(T,  rX, glm::vec3(1.0f, 0.0f, 0.0f));
  glm::mat4 V = glm::rotate(Rx, rY, glm::vec3(0.0f, 1.0f, 0.0f));
  glm::mat4 PV = P*V;
  shader.Use();
 glUniformMatrix4fv(shader("PV"),1,GL_FALSE,glm::value_ptr(PV));
 glUniform1i(shader("sub_divisions"), sub_divisions);
 glDrawElementsInstanced(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,0, 4);
  shader.UnUse();
  glutSwapBuffers();
}

There is always a limit on the maximum number of matrices one can output from the vertex shader and this has some performance implications as well. Some performance improvements can be obtained by replacing the matrix storage with translation and scaling vectors, and an orientation quaternion which can then be converted on the fly into a matrix in the shader.

See also

The official OpenGL wiki can be found at http://www.opengl.org/wiki/Built-in_Variable_%28GLSL%29.

An instance rendering tutorial from OGLDev can be found at http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html.

主站蜘蛛池模板: 阳谷县| 锦屏县| 青岛市| 九江市| 荆门市| 桃江县| 泸州市| 尉氏县| 阳西县| 剑川县| 乐都县| 聂荣县| 忻城县| 资阳市| 商洛市| 清镇市| 酒泉市| 邛崃市| 德兴市| 古交市| 镶黄旗| 临沧市| 合山市| 永济市| 许昌县| 安西县| 呼玛县| 中方县| 东城区| 博兴县| 钟山县| 景东| 应城市| 信阳市| 嵩明县| 南雄市| 新邵县| 沙坪坝区| 河北区| 榆社县| 苏尼特左旗|