地形渲染: TerrainRendering - MartinRGB/GLES30_ProgrammingGuide_NDK GitHub Wiki

Wiki参考 - OpenGL terrain renderer: rendering the terrain mesh

地形渲染的步骤:

1.建立正方形构成的顶点地形网格,参考 esShapes.c 中的 esGenSquareGrid

int ESUTIL_API esGenSquareGrid ( int size, GLfloat **vertices, GLuint **indices )
{
   int i, j;
   int numIndices = ( size - 1 ) * ( size - 1 ) * 2 * 3;

   // Allocate memory for buffers
   if ( vertices != NULL )
   {
      int numVertices = size * size;
      float stepSize = ( float ) size - 1;
      *vertices = malloc ( sizeof ( GLfloat ) * 3 * numVertices );

      for ( i = 0; i < size; ++i ) // row
      {
         for ( j = 0; j < size; ++j ) // column
         {
            ( *vertices ) [ 3 * ( j + i * size )     ] = i / stepSize;
            ( *vertices ) [ 3 * ( j + i * size ) + 1 ] = j / stepSize;
            ( *vertices ) [ 3 * ( j + i * size ) + 2 ] = 0.0f;
         }
      }
   }

   // Generate the indices
   if ( indices != NULL )
   {
      *indices = malloc ( sizeof ( GLuint ) * numIndices );

      for ( i = 0; i < size - 1; ++i )
      {
         for ( j = 0; j < size - 1; ++j )
         {
            // two triangles per quad
            ( *indices ) [ 6 * ( j + i * ( size - 1 ) )     ] = j + ( i )   * ( size )    ;
            ( *indices ) [ 6 * ( j + i * ( size - 1 ) ) + 1 ] = j + ( i )   * ( size ) + 1;
            ( *indices ) [ 6 * ( j + i * ( size - 1 ) ) + 2 ] = j + ( i + 1 ) * ( size ) + 1;

            ( *indices ) [ 6 * ( j + i * ( size - 1 ) ) + 3 ] = j + ( i )   * ( size )    ;
            ( *indices ) [ 6 * ( j + i * ( size - 1 ) ) + 4 ] = j + ( i + 1 ) * ( size ) + 1;
            ( *indices ) [ 6 * ( j + i * ( size - 1 ) ) + 5 ] = j + ( i + 1 ) * ( size )    ;
         }
      }
   }

   return numIndices;
}

绘制顶点网格较好的办法是 GL_TRIANGLE_STRIP 可以改善 顶点缓存局部性来提高渲染性能。

2.利用灰度图拉扯顶点

解析灰度图的颜色,通过颜色拉扯顶点,在灰度图中,颜色越接近白色,则被拉扯的越高(详细解析方法见下文顶点着色器)

顶点网格的顶点数量越多,采样越精确,高度图越平滑

3.在顶点着色器中,从高度图中读取高度值

在高度图中,计算法线的方法一般如下:

方法一:

vec3 ComputeNormals(vec3 position,sampler2D map)
{
   float left=texture(heightMap,position.xy - vec2(1.0,0.0)).w;
   float right=texture(heightMap,position.xy + vec2(1.0,0.0)).w;
   float bottom=texture(heightMap,position.xy - vec2(0.0,1.0)).w;
   float top=texture(heightMap,position.xy + vec2(0.0,1.0)).w;
   return vec3(left-right,bottom-top,2.0);
}

方法二:finite difference method

OpenGL - How to calculate normals in a terrain height grid?

  // # P.xy store the position for which we want to calculate the normals
  // # height() here is a function that return the height at a point in the terrain

  // read neightbor heights using an arbitrary small offset
  vec3 off = vec3(1.0, 1.0, 0.0);
  float hL = height(P.xy - off.xz);
  float hR = height(P.xy + off.xz);
  float hD = height(P.xy - off.zy);
  float hU = height(P.xy + off.zy);

  // deduce terrain normal
  N.x = hL - hR;
  N.y = hD - hU;
  N.z = 2.0;
  N = normalize(N);

vertex中使用方法一:

#version 300 es                                      
uniform mat4 u_mvpMatrix;                            
uniform vec3 u_lightDirection;                       
layout(location = 0) in vec4 a_position;             
uniform sampler2D s_texture;                         
out vec4 v_color;                                    
void main()                                          
{                                                    
   // compute vertex normal from height map          
   float hxl = textureOffset( s_texture,             
                  a_position.xy, ivec2(-1,  0) ).w;  
   float hxr = textureOffset( s_texture,             
                  a_position.xy, ivec2( 1,  0) ).w;  
   float hyl = textureOffset( s_texture,             
                  a_position.xy, ivec2( 0, -1) ).w;  
   float hyr = textureOffset( s_texture,             
                  a_position.xy, ivec2( 0,  1) ).w;  
   vec3 u = normalize( vec3(0.05, 0.0, hxr-hxl) );   
   vec3 v = normalize( vec3(0.0, 0.05, hyr-hyl) );   
   vec3 normal = cross( u, v );                      
                                                     
   // compute diffuse lighting                       
   float diffuse = dot( normal, u_lightDirection );  
   v_color = vec4( vec3(diffuse), 1.0 );             
                                                     
   // get vertex position from height map            
   float h = texture ( s_texture, a_position.xy ).w; 
   vec4 v_position = vec4 ( a_position.xy,           
                            h/2.5,                   
                            a_position.w );          
   gl_Position = u_mvpMatrix * v_position;           
}