【OpenGL ES】图元装配与光栅化

OpenGL ES 3.0支持三种基本图元,三角形、直线和点,它们由一组表示顶点位置的顶点描述,其它如颜色、纹理坐标和几何法线等也作为通用属性与每个顶点关联。

1、三角形

三角形有三种类型,GL_TRIANGLES用于绘制一系列单独的三角形,GL_TRIANGLE_STRIPE用于绘制一系列相互连接的三角形,GL_TRIANGLE_FAN用于绘制一系列扇形连接的三角形,如下图所示,对于一组顶点数据(v0,v1,v2,v3,v4,v5),GL_TRIANGLES绘制两个三角形(v0,v1,v2)和(v3,v4,v5),GL_TRIANGLE_STRIPE绘制三个三角形(v0,v1,v2)、(v2,v1,v3)和(v2,v3,v4),GL_TRIANGLE_FAN绘制三个三角形(v0,v1,v2)、(v0,v2,v3)和(v0,v3,v4)。

这里写图片描述

2、直线

直线也有三种类型,分别是绘制不相连线段的GL_LINES、绘制相连线段的GL_LINE_STRIPE和绘制首尾相连线段的GL_LINE_LOOP,如下图所示,顶点数据间的连接顺序比较简单。

这里写图片描述

设置线宽使用glLineWidth,默认为1,实际值则由与之最接近的整数而定,线宽有个允许的范围,通过glGetXxx查询GL_ALIASED_LINE_WIDTH_RANGE可得,但这个范围是否支持由实现而定,只有1是保证支持的,查询线宽的值使用GL_LINE_WIDTH。

3、点

点对应的图元类型为GL_POINTS,是指定位置和半径的屏幕对齐的正方形,位置描述正方形的中心,半径用于计算正方形的四个坐标。gl_PointSize是可用于在顶点着色器中输出点半径或者点尺寸的内建变量,受到特定实现所支持的非平滑点尺寸范围的限制,通过glGet查询GL_ALIASED_POINT_SIZE_RANGE可得。默认情况下,OpenGL ES 3.0将窗口原点描述为左下角,但是,对于点,点坐标的原点是左上角。gl_PointCoord是只能在渲染图元为点时用于片段着色器内部的内建变量,它用mediump精度限定符声明为一个vec2变量,随着我们从左侧移到右侧,从顶部移到底部,赋予gl_PointCoord的值从0到1变化,如下图所示。

这里写图片描述

4、绘制图元

OpenGL ES 3.0绘制图元包括如下五个API:

void glDrawArrays(GLenum mode,
    GLint first,
    GLsizei count);
void glDrawElements(GLenum mode,
    GLsizei count,
    GLenum type,
    const GLvoid * indices);
void glDrawRangeElements(GLenum mode,
    GLuint start,
    GLuint end,
    GLsizei count,
    GLenum type,
    const GLvoid * indices);
void glDrawArraysInstanced(GLenum mode,
    GLint first,
    GLsizei count,
    GLsizei primcount);
void glDrawElementsInstanced(GLenum mode,
    GLsizei count,
    GLenum type,
    const void * indices,
    GLsizei primcount);

glDrawArrays用元素索引为first到first+count-1的元素指定的顶点绘制mode指定的图元,适用于由一系列顺序元素索引描述的图元其几何形状的顶点不共享。但是,游戏或者其它3D应用程序使用的典型对象由多个三角形网格组成,其中的元素索引可能不一定按照顺序,顶点通常在网格的三角形之间共享,这种情况下使用glDrawElements或glDrawRangeElements更好,其中indices存储了元素索引,type为元素索引的类型,start、end为indices中的最小、最大元素索引。例如,对于一个立方体来说,如果使用glDrawArrays进行绘制,立方体的每一面都要调用glDrawArrays一次,因为立方体的顶点是多个面共享的,所以这些共享顶点必须重复绘制,绘制GL_TRIANGLE_FAN时需要分配24个顶点,绘制GL_TRIANGLES时则需要分配多达36个顶点,而不是实际的8个顶点,这不是一个高效的方法,如果使用glDrawElements进行绘制,程序在GPU上运行的将更快。

OpenGL ES 3.0引入了glDrawArraysInstanced和glDrawElementsInstanced用于几何形状实例化,这种方式很高效,可以用一次API调用多次渲染具有不同属性的同一个对象,在渲染大量类似对象时很有用,降低了向OpenGL ES引擎发送许多API调用的CPU处理开销,其中primcount指定绘制的图元实例数量。可以使用两种方法访问每个实例的数据,一种是使用glVertexAttribDivisor指示OpenGL ES对每个实例读取一次或多次顶点属性,另一种是使用内建输入变量gl_InstanceID作为顶点着色器中的缓冲区索引以访问每个实例的数据。另外,最新的OpenGL ES 3.1及3.2还引入了其它绘图API,这里不作介绍。

绘制图元时,如果没有限定符,那么顶点着色器的输出值在图元中使用线性插值,但是使用平面着色时没有发生插值,所以片段着色器中只有一个顶点值可用,对于给定的图元示例,这个驱动顶点确定使用顶点着色器的哪一个顶点输出,因为只能使用一个顶点,所以驱动顶点的选择有既定的规则,为当前图元对应的最大索引。

绘制图元的另一个概念是图元重启,可以在一次绘图调用中渲染多个不相连的图元,这对于降低绘图API的调用开销是有利的。使用图元重启,可以通过在索引列表中插入一个特殊索引来重启一个用于索引绘图调用的图元,如glDrawElements、glDrawRangeElements或glDrawElementsInstanced,这个特殊索引是该索引类型的最大可能索引,如索引类型为GL_UNSIGNED_BTYE时其值为255。例如,假定两个三角形条带分别有元素索引(0,1,2,3)和(8,9,10,11),如果我们想利用图元重启在一次调用glDrawElements中绘制两个条带,索引类型为GL_UNSIGNED_BTYE,则组合的元素索引列表为(0,1,2,3,255,8,9,10,11),可以用如下代码启用和禁用图元重启。

glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
// draw primitives
glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);

5、图元绘制性能

应用程序应该确保用尽可能大的图元尺寸调用glDrawElements或glDrawElementsInstanced,如果绘制GL_TRIANGLES,这很容易做到,但是对于GL_TRIANGLE_STRIPE或GL_TRIANGLE_FAN,则可以用上面提到的图元重启将这些网格连接在一起,而不用对每个三角形网格单独调用glDrawElements。不使用图元重启的另一种方法是添加造成退化三角形的元素索引,代价是使用更多的索引,新添加的索引数量取决于三角形是GL_TRIANGLE_STRIPE还是GL_TRIANGLE_FAN,如果是前者,特别要注意其索引数量及两个图元连接时的索引顺序。需要的附加元素索引数量和生成的退化三角形数量取决于第一个三角形的顶点数量,必须保留下一个连接三角形的弯曲顺序。退化三角形是两个或者更多顶点相同的三角形,GPU可以非常简单地检测和拒绝退化三角形,所以这是很好的性能改进,我们可以将一个很大的图元放入由GPU渲染的队列。

例如,对于两组三角形索引(0,1,2,3)和(8,9,10,11),用一次glDrawElements调用绘制两个GL_TRIANGLE_STRIPE,组合元素索引列表为(0,1,2,3,3,8,8,9,10,11),添加了3和8,绘制的三角形为(0,1,2)、(2,1,3)、(2,3,3)、(3,3,8)、(3,8,8)、(8,8,9)、(8,9,10)和(9,10,11),其中有重复索引的三角形为退化三角形共四个;对于两组三角形索引(0,1,2,3,4)和(8,9,10,11),用一次glDrawElements调用绘制两个GL_TRIANGLE_STRIPE,组合元素索引列表则为(0,1,2,3,4,4,4,8,8,9,10,11),添加了4、4和8,绘制的三角形为(0,1,2)、(2,1,3)、(2,3,4)、(4,3,4)、(4,4,4)、(4,4,8)、(4,8,8)、(8,8,9)、(8,9,10)和(9,10,11),其中有重复索引的三角形为退化三角形共五个。

在确定如何安排图元元素索引时考虑变换后顶点缓存的大小也有助于总体性能的提升,大部分GPU采用一个变换后顶点缓存,在顶点着色器执行由元素索引给出的顶点之前,进行一次检查,以确定顶点是否已经存在于变换后缓存,没有缓存才执行顶点操作,这种技术减少了顶点着色器执行重用顶点的次数。

6、图元装配

图元装配的输入来自顶点着色器的输出,最后输出到光栅化阶段,其中包括三个阶段,裁剪、透视分割和视口变换。在图元装配过程,顶点经历不同的坐标系统,首先以本地坐标空间输入到OpenGL ES,在顶点着色器执行之后,顶点进入裁剪坐标空间,这一坐标变换通过顶点着色器中定义的uniform变量完成,随后经过透视分割进入规范化设备坐标空间,最后经过视口变换进入窗口坐标空间。

裁剪有一个裁剪体或者叫视景体,通过坐标(Xc,Yc,Zc,Wc)指定,由上、下、左、右、远、近共六个平面组成,位于裁剪体之外的图元将被裁剪抛弃,避免处理不必要的图元。需要注意的是,三角形、直线的裁剪在硬件中的执行代价可能很高,部分在x和y平面之外的图元不一定需要裁剪,通过渲染到一个大于glViewport指定的视口尺寸的视口,x和y平面中的裁剪变成剪裁操作Scissor,GPU可以高效地实施剪裁,这个更大的视口区域被称作保护带区域。

透视分割取得裁剪坐标(Xc,Yc,Zc,Wc)指定的点,并将其投影到视口上,每个坐标除以Wc也就是(Xc/Wc,Yc/Wc,Zc/Wc,Wc/Wc),得到规范化的设备坐标(Xd,Yd,Zd),其值位于-1.0到1.0之间,包括边界值。

视口是一个二维矩形窗口区域,是所有OpenGL ES渲染操作最终显示的地方,在视口变换阶段,坐标(Xd,Yd)根据视口的大小将被转换为真正的窗口坐标,Zd根据glDepthRangef指定的near和far深度值将被转换为窗口的Z值。视口变换调用如下的glViewport完成,x和y指定左下角坐标,单位是像素,初始值为(0,0),width和height指定宽和高,初始值为eglCreateWindowSurface中指定的EGLNativeWindowType的值,最后窗口坐标换算公式如下:

这里写图片描述

使用glDepthRangef指定从规范化设备坐标到窗口坐标变换的深度范围,n表示近平面,初始值为0,f表示远平面,初始值为1,变化范围从0到1,窗口坐标Zw等于Zd * (f - n) / 2 + (f + n) / 2。

void glViewport(GLint x,
    GLint y,
    GLsizei width,
    GLsizei height);
void glDepthRangef(GLfloat n,
    GLfloat f);

7、光栅化

在顶点变换和图元裁剪之后,光栅化管线取得单独图元,并为该图元生成对应的片段,每个片段由屏幕空间中的位置(x,y)标识,片段代表了屏幕空间中(x,y)指定的像素位置和由片段着色器处理而生成片段颜色的附加片段数据。

以三角形为例,在三角形被光栅化之前,需要进行剔除操作Culling,剔除操作抛弃背向观察者的三角形,以避免GPU去光栅化不可见的三角形,提升应用程序的整体性能。剔除涉及两个概念,方向与正反面,从三角形的第一个顶点开始,到第二个、第三个顶点,再返回第一个顶点,其弯曲方向为顺时针或逆时针方向,然后面向观察者的三角形面为正面,另一面为反面。默认情况下,剔除被禁用,启用时用GL_CULL_FACE调用glEnable,禁用则调用glDisable,然后使用glFrontFace指定三角形正面的方向,默认为GL_CCW即逆时针方向,有效值还可以是GL_CW即顺时针方向,最后使用glCullFace指定剔除哪些面,默认为GL_BACK背面,有效值还包括GL_FORNT、GL_FORNT_AND_BACK。

void glFrontFace(GLenum mode);
void glCullFace(GLenum mode);

当绘制两个相互重叠的多边形时,很有可能产生深度缓冲伪像,这是因为三角形光栅化时精度有限,从而影响逐片段处理生成的深度值的精度而产生伪像。为了避免伪像,需要在执行深度测试和深度值写入深度缓冲区之前,把计算出来的深度值加一个偏移量,如果深度测试通过,原始深度值将被保存到深度缓冲区。启用或禁用深度偏移使用GL_POLYGON_OFFSET_FILL调用glEnable或glDisable。深度偏移调用如下的glPolygonOffset,factor和units的初始值都为0,这两个值可以通过glGet函数查询GL_POLYGON_OFFSET_FACTOR、GL_POLYGON_OFFSET_UNITS,深度偏移等于m * factor + r * units,m是三角形的最大深度斜率,在三角形光栅化阶段由OpenGL ES实现计算,r是OpenGL ES实现定义的常量,代表深度值中可以保证产生差异的最小值。

void glPolygonOffset(GLfloat factor,
    GLfloat units);

这里写图片描述

最后,介绍一个深度缓冲相关函数glDepthFunc,参数func可以是GL_NEVER、GL_LESS、GL_EQUAL、GL_LEQUAL、GL_GREATER、GL_NOTEQUAL、GL_GEQUAL、GL_ALWAYS,默认值为GL_LESS,默认情况下,将来的深度值小于存储的深度值时将通过深度测试。

void glDepthFunc(GLenum func);

8、遮挡查询

遮挡查询,就是用查询对象来跟踪通过深度测试的任何片段或样本,这种方法可用于不同的技术,例如镜头眩光特效的可见性测试,以及避免在可视体被遮挡的不可见对象上进行几何形状处理的优化,涉及如下几个函数,为了获得更好的性能,应该等待几帧再执行glGetQueryObjectuiv调用,以等待GPU中的结果可用。

GLboolean glIsQuery(GLuint id);
void glGenQueries(GLsizei n,
    GLuint * ids);
void glDeleteQueries(GLsizei n,
    const GLuint * ids);
void glBeginQuery(GLenum target,
    GLuint id);
void glEndQuery(GLenum target);
void glGetQueryiv(GLenum target,
    GLenum pname,
    GLint * params);
void glGetQueryObjectuiv(GLuint id,
    GLenum pname,
    GLuint * params);

下图是用一次实例化绘图调用绘制100个立方体的结果,其中每个立方体实例的颜色不同。

这里写图片描述

// Instancing.c
#define NUM_INSTANCES   100
#define POSITION_LOC    0
#define COLOR_LOC       1
#define MVP_LOC         2

typedef struct
{
   // Handle to a program object
   GLuint programObject;
   // VBOs
   GLuint positionVBO;
   GLuint colorVBO;
   GLuint mvpVBO;
   GLuint indicesIBO;
   // Number of indices
   int       numIndices;
   // Rotation angle
   GLfloat   angle[NUM_INSTANCES];
} UserData;

// Initialize the shader and program object
int Init ( ESContext *esContext )
{
   GLfloat *positions;
   GLuint *indices;

   UserData *userData = esContext->userData;
   const char vShaderStr[] =
      "#version 300 es                             \n"
      "layout(location = 0) in vec4 a_position;    \n"
      "layout(location = 1) in vec4 a_color;       \n"
      "layout(location = 2) in mat4 a_mvpMatrix;   \n"
      "out vec4 v_color;                           \n"
      "void main()                                 \n"
      "{                                           \n"
      "   v_color = a_color;                       \n"
      "   gl_Position = a_mvpMatrix * a_position;  \n"
      "}                                           \n";

   const char fShaderStr[] =
      "#version 300 es                                \n"
      "precision mediump float;                       \n"
      "in vec4 v_color;                               \n"
      "layout(location = 0) out vec4 outColor;        \n"
      "void main()                                    \n"
      "{                                              \n"
      "  outColor = v_color;                          \n"
      "}                                              \n";

   // Load the shaders and get a linked program object
   userData->programObject = esLoadProgram ( vShaderStr, fShaderStr );

   // Generate the vertex data
   userData->numIndices = esGenCube ( 0.1f, &positions,
                                      NULL, NULL, &indices );

   // Index buffer object
   glGenBuffers ( 1, &userData->indicesIBO );
   glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->indicesIBO );
   glBufferData ( GL_ELEMENT_ARRAY_BUFFER, sizeof ( GLuint ) * userData->numIndices, indices, GL_STATIC_DRAW );
   glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );
   free ( indices );

   // Position VBO for cube model
   glGenBuffers ( 1, &userData->positionVBO );
   glBindBuffer ( GL_ARRAY_BUFFER, userData->positionVBO );
   glBufferData ( GL_ARRAY_BUFFER, 24 * sizeof ( GLfloat ) * 3, positions, GL_STATIC_DRAW );
   free ( positions );

   // Random color for each instance
   {
      GLubyte colors[NUM_INSTANCES][4];
      int instance;

      srandom ( 0 );

      for ( instance = 0; instance < NUM_INSTANCES; instance++ )
      {
         colors[instance][0] = random() % 255;
         colors[instance][1] = random() % 255;
         colors[instance][2] = random() % 255;
         colors[instance][3] = 0;
      }

      glGenBuffers ( 1, &userData->colorVBO );
      glBindBuffer ( GL_ARRAY_BUFFER, userData->colorVBO );
      glBufferData ( GL_ARRAY_BUFFER, NUM_INSTANCES * 4, colors, GL_STATIC_DRAW );
   }

   // Allocate storage to store MVP per instance
   {
      int instance;

      // Random angle for each instance, compute the MVP later
      for ( instance = 0; instance < NUM_INSTANCES; instance++ )
      {
         userData->angle[instance] = ( float ) ( random() % 32768 ) / 32767.0f * 360.0f;
      }

      glGenBuffers ( 1, &userData->mvpVBO );
      glBindBuffer ( GL_ARRAY_BUFFER, userData->mvpVBO );
      glBufferData ( GL_ARRAY_BUFFER, NUM_INSTANCES * sizeof ( ESMatrix ), NULL, GL_DYNAMIC_DRAW );
   }
   glBindBuffer ( GL_ARRAY_BUFFER, 0 );

   glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
   return GL_TRUE;
}

// Update MVP matrix based on time
void Update ( ESContext *esContext, float deltaTime )
{
   UserData *userData = ( UserData * ) esContext->userData;
   ESMatrix *matrixBuf;
   ESMatrix perspective;
   float    aspect;
   int      instance = 0;
   int      numRows;
   int      numColumns;

   // Compute the window aspect ratio
   aspect = ( GLfloat ) esContext->width / ( GLfloat ) esContext->height;

   // Generate a perspective matrix with a 60 degree FOV
   esMatrixLoadIdentity ( &perspective );
   esPerspective ( &perspective, 60.0f, aspect, 1.0f, 20.0f );

   glBindBuffer ( GL_ARRAY_BUFFER, userData->mvpVBO );
   matrixBuf = ( ESMatrix * ) glMapBufferRange ( GL_ARRAY_BUFFER, 0, sizeof ( ESMatrix ) * NUM_INSTANCES, GL_MAP_WRITE_BIT );

   // Compute a per-instance MVP that translates and rotates each instance differnetly
   numRows = ( int ) sqrtf ( NUM_INSTANCES );
   numColumns = numRows;

   for ( instance = 0; instance < NUM_INSTANCES; instance++ )
   {
      ESMatrix modelview;
      float translateX = ( ( float ) ( instance % numRows ) / ( float ) numRows ) * 2.0f - 1.0f;
      float translateY = ( ( float ) ( instance / numColumns ) / ( float ) numColumns ) * 2.0f - 1.0f;

      // Generate a model view matrix to rotate/translate the cube
      esMatrixLoadIdentity ( &modelview );

      // Per-instance translation
      esTranslate ( &modelview, translateX, translateY, -2.0f );

      // Compute a rotation angle based on time to rotate the cube
      userData->angle[instance] += ( deltaTime * 40.0f );

      if ( userData->angle[instance] >= 360.0f )
      {
         userData->angle[instance] -= 360.0f;
      }

      // Rotate the cube
      esRotate ( &modelview, userData->angle[instance], 1.0, 0.0, 1.0 );

      // Compute the final MVP by multiplying the
      // modevleiw and perspective matrices together
      esMatrixMultiply ( &matrixBuf[instance], &modelview, &perspective );
   }

   glUnmapBuffer ( GL_ARRAY_BUFFER );
}

// Draw a triangle using the shader pair created in Init()
void Draw ( ESContext *esContext )
{
   UserData *userData = esContext->userData;

   // Set the viewport
   glViewport ( 0, 0, esContext->width, esContext->height );

   // Clear the color buffer
   glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

   // Use the program object
   glUseProgram ( userData->programObject );

   // Load the vertex position
   glBindBuffer ( GL_ARRAY_BUFFER, userData->positionVBO );
   glVertexAttribPointer ( POSITION_LOC, 3, GL_FLOAT,
                           GL_FALSE, 3 * sizeof ( GLfloat ), ( const void * ) NULL );
   glEnableVertexAttribArray ( POSITION_LOC );

   // Load the instance color buffer
   glBindBuffer ( GL_ARRAY_BUFFER, userData->colorVBO );
   glVertexAttribPointer ( COLOR_LOC, 4, GL_UNSIGNED_BYTE,
                           GL_TRUE, 4 * sizeof ( GLubyte ), ( const void * ) NULL );
   glEnableVertexAttribArray ( COLOR_LOC );
   glVertexAttribDivisor ( COLOR_LOC, 1 ); // One color per instance

   // Load the instance MVP buffer
   glBindBuffer ( GL_ARRAY_BUFFER, userData->mvpVBO );

   // Load each matrix row of the MVP.  Each row gets an increasing attribute location.
   glVertexAttribPointer ( MVP_LOC + 0, 4, GL_FLOAT, GL_FALSE, sizeof ( ESMatrix ), ( const void * ) NULL );
   glVertexAttribPointer ( MVP_LOC + 1, 4, GL_FLOAT, GL_FALSE, sizeof ( ESMatrix ), ( const void * ) ( sizeof ( GLfloat ) * 4 ) );
   glVertexAttribPointer ( MVP_LOC + 2, 4, GL_FLOAT, GL_FALSE, sizeof ( ESMatrix ), ( const void * ) ( sizeof ( GLfloat ) * 8 ) );
   glVertexAttribPointer ( MVP_LOC + 3, 4, GL_FLOAT, GL_FALSE, sizeof ( ESMatrix ), ( const void * ) ( sizeof ( GLfloat ) * 12 ) );
   glEnableVertexAttribArray ( MVP_LOC + 0 );
   glEnableVertexAttribArray ( MVP_LOC + 1 );
   glEnableVertexAttribArray ( MVP_LOC + 2 );
   glEnableVertexAttribArray ( MVP_LOC + 3 );

   // One MVP per instance
   glVertexAttribDivisor ( MVP_LOC + 0, 1 );
   glVertexAttribDivisor ( MVP_LOC + 1, 1 );
   glVertexAttribDivisor ( MVP_LOC + 2, 1 );
   glVertexAttribDivisor ( MVP_LOC + 3, 1 );

   // Bind the index buffer
   glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->indicesIBO );

   // Draw the cubes
   glDrawElementsInstanced ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, ( const void * ) NULL, NUM_INSTANCES );
}

// Cleanup
void Shutdown ( ESContext *esContext )
{
   UserData *userData = esContext->userData;

   glDeleteBuffers ( 1, &userData->positionVBO );
   glDeleteBuffers ( 1, &userData->colorVBO );
   glDeleteBuffers ( 1, &userData->mvpVBO );
   glDeleteBuffers ( 1, &userData->indicesIBO );

   // Delete program object
   glDeleteProgram ( userData->programObject );
}

int esMain ( ESContext *esContext )
{
   esContext->userData = malloc ( sizeof ( UserData ) );

   esCreateWindow ( esContext, "Instancing", 640, 480, ES_WINDOW_RGB | ES_WINDOW_DEPTH );

   if ( !Init ( esContext ) )
   {
      return GL_FALSE;
   }

   esRegisterShutdownFunc ( esContext, Shutdown );
   esRegisterUpdateFunc ( esContext, Update );
   esRegisterDrawFunc ( esContext, Draw );

   return GL_TRUE;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基本信息 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田浩一,罗杰.李) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121229428 上架时间:2014-6-11 出版日期:2014 年6月 开本:16开 页码:470 版次:1-1 --------------------- 目录 《WebGL编程指南》 第1 章 WebGL 概述 1 WebGL 的优势 3 使用文本编辑器开发三维应用 3 轻松发布三维图形程序 4 充分利用浏览器的功能 5 学习和使用WebGL 很简单 5 WebGL 的起源 5 WebGL 程序的结构 6 总结 7 第2 章 WebGL 入门 9 Canvas 是什么? 10 使用[canvas] 标签 11 DrawRectangle.js 13 最短的WebGL 程序:清空绘图区 16 HTML 文件(Hel loCanvas.html) 16 JavaScript 程序(HelloCanvas.js) 17 用示例程序做实验 22 绘制一个点(版本1) 22 HelloPoint1.html 24 HelloPoint1.js 24 着色器是什么? 25 使用着色器的WebGL 程序的结构 27 初始化着色器 29 顶点着色器 31 片元着色器 33 绘制操作 34 WebGL 坐标系统 35 用示例程序做实验 37 绘制一个点(版本2) 38 使用attribute 变量 38 示例程序(HelloPoint2.js) 39 获取attribute 变量的存储位置 41 向attribute 变量赋值 42 gl.vertexAttrib3f() 的同族函数 44 用示例程序做实验 45 通过鼠标点击绘点 46 示例程序(ClickedPoints.js) 47 注册事件响应函数 48 响应鼠标点击事件 50 用示例程序做实验 53 改变点的颜色 55 示例程序(ColoredPoints.js) 56 uniform 变量 58 获取uniform 变量的存储地址 59 向uniform 变量赋值 60 gl.uniform4f() 的同族函数 61 总结 62 第3 章 绘制和变换三角形 63 绘制多个点 64 示例程序(MultiPoint.js) 66 使用缓冲区对象 69 创建缓冲区对象(gl.createBuffer()) 70 绑定缓冲区(gl.bindBuffer()) 71 向缓冲区对象中写入数据(gl.bufferData()) 72 类型化数组 74 将缓冲区对象分配给attribute 变量(gl.vertexAttribPointer()) 75 开启attribute 变量(gl.enableVertexAttribArray()) 77 gl.drawArrays() 的第2 个和第3 个参数 78 用示例程序做实验 79 Hello Triangle 80 示例程序(HelloTriangle.js) 80 基本图形 82 用示例程序做实验 83 Hello Rectangle(HelloQuad) 84 用示例程序做实验 85 移动、旋转和缩放 86 平移 87 示例程序(TranslatedTriangle.js) 88 旋转 91 示例程序(RotatedTriangle.js) 93 变换矩阵:旋转 97 变换矩阵:平移 100 4&times;4 的旋转矩阵 101 示例程序(RotatedTriangle_Matrix.js) 102 平移:相同的策略 105 变换矩阵:缩放 106 总结 108 第4 章 高级变换与动画基础 109 平移,然后旋转 109 矩阵变换库:cuon-matrix.js 110 示例程序(RotatedTriangle_Matrix4.js) 111 复合变换 113 示例程序(RotatedTranslatedTriangle.js) 115 用示例程序做实验 117 动画 118 动画基础 119 示例程序(RotatingTriangle.js) 119 反复调用绘制函数(tick()) 123 按照指定的旋转角度绘制三角形(draw()) 123 请求再次被调用(requestAnimationFrame()) 125 更新旋转角(animate()) .126 用示例程序做实验 128 总结 130 第5 章 颜色与纹理 131 将非坐标数据传入顶点着色器 131 示例程序(MultiAttributeSize.js) 133 创建多个缓冲区对象 134 gl.vertexAttribPointer() 的步进和偏移参数 135 示例程序(MultiAttributeSize_Interleaved.js) 136 修改颜色(varying 变量) 140 示例程序(MultiAttributeColor.js) 141 用示例程序做实验 144 彩色三角形(ColoredTriangle.js) 145 几何形状的装配光栅化 145 调用片元着色器 149 用示例程序做实验 149 varying 变量的作用和内插过程 151 在矩形表面贴上图像 153 纹理坐标 156 将纹理图像粘贴到几何图形上 156 示例程序(TexturedQuad.js) 157 设置纹理坐标(initVertexBuffers()) 160 配置和加载纹理(initTextures()) 160 为WebGL 配置纹理(loadTexture()) 164 图像Y 轴反转 164 激活纹理单元(gl.activeTexture()) 165 绑定纹理对象(gl.bindTexture()) 166 配置纹理对象的参数(gl.texParameteri()) 168 将纹理图像分配给纹理对象(gl.texImage2D()) 171 将纹理单元传递给片元着色器(gl.uniform1i()) 173 从顶点着色器向片元着色器传输纹理坐标 174 在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 186 GLSL ES 概述 186 你好,着色器! 187 基础 187 执行次序 187 注释 187 数据值类型(数值和布尔值) 188 变量 188 GLSL ES 是强类型语言 189 基本类型 189 赋值和类型转换 190 运算符 191 矢量和矩阵 192 赋值和构造 193 访问元素 195 运算符 197 结构体 200 赋值和构造 200 访问成员 200 运算符 201 数组 201 取样器(纹理) 202 运算符优先级 203 程序流程控制:分支和循环 203 if 语句和if-else 语句 203 for 语句 204 continue、break 和discard 语句 205 函数 205 规范声明 207 参数限定词 207 内置函数 208 全局变量和局部变量 209 存储限定字 209 const 变量 209 Attribute 变量 210 uniform 变量 211 varying 变量 211 精度限定字 211 预处理指令 213 总结 215 第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察旋转后的三角形 225 示例程序(LookAtRotatedTriangles.js) 227 用示例程序做实验 228 利用键盘改变视点 230 示例程序(LookAtTrianglesWithKeys.js) 230 独缺一角 232 可视范围(正射类型) 233 可视空间 234 定义盒状可视空间 235 示例程序(OrthoView.html) 236 示例程序(OrthoView.js) 237 JavaScript 修改HTML 元素 239 顶点着色器的执行流程 239 修改near 和far 值 241 补上缺掉的角(LookAtTrianglesWithKeys_ViewVolume.js) 243 用示例程序做实验 245 可视空间(透视投影) 246 定义透视投影可视空间 247 示例程序(perspectiveview.js) 249 投影矩阵的作用 251 共冶一炉(模型矩阵、视图矩阵和投影矩阵) 252 示例程序(PerspectiveView_mvp.js) 254 用示例程序做实验 257 正确处理对象的前后关系 258 隐藏面消除 260 示例程序(DepthBuffer.js) 262 深度冲突 263 立方体 266 通过顶点索引绘制物体 268 示例程序(HelloCube.js) 268 向缓冲区中写入顶点的坐标、颜色与索引 271 为立方体的每个表面指定颜色 274 示例程序(ColoredCube.js) 275 用示例程序做实验 277 总结 279 第8 章 光照 281 光照原理 281 光源类型 283 反射类型 284 平行光下的漫反射 286 根据光线和表面的方向计算入射角 287 法线:表面的朝向 288 示例程序(LightedCube.js) 291 环境光下的漫反射 296 示例程序(LightedCube_ambient.js) 298 运动物体的光照效果 299 魔法矩阵:逆转置矩阵 301 示例程序(LightedTranslatedRotatedCube.js) 302 点光源光 304 示例程序(PointLightedCube.js) 305 更逼真:逐片元光照 308 示例程序(PointLightedCube_perFragment.js) 309 总结 310 第9 章 层次模型 311 多个简单模型组成的复杂模型 311 层次结构模型 313 单关节模型 314 示例程序(JointMode.js) 315 绘制层次模型(draw()) 319 多节点模型 321 示例程序(MultiJointModel.js) 323 绘制部件(drawBox()) 326 绘制部件(drawSegments()) 327 着色器和着色器程序对象:initShaders() 函数的作用 332 创建着色器对象(gl.createShader()) 333 指定着色器对象的代码(gl.shaderSource()) 334 编译着色器(gl.compileShader()) 334 创建程序对象(gl.createProgram()) 336 为程序对象分配着色器对象(gl.attachShader()) 337 连接程序对象(gl.linkProgram()) 337 告知WebGL 系统所使用的程序对象(gl.useProgram()) 339 initShaders() 函数的内部流程 339 总结 342 第10 章 高级技术 343 用鼠标控制物体旋转 343 如何实现物体旋转 344 示例程序(RotateObject.js) 344 选中物体 347 如何实现选中物体 347 示例程序(PickObject.js) 348 选中一个表面 351 示例程序(PickFace.js) 352 HUD(平视显示器) 355 如何实现HUD 355 示例程序(HUD.html) 356 示例程序(HUD.js) 357 在网页上方显示三维物体 359 雾化(大气效果) 359 如何实现雾化 360 示例程序(Fog.js) 361 使用w 分量(Fog_w.js) 363 绘制圆形的点 364 如何实现圆形的点 364 示例程序(RoundedPoint.js) 366 α 混合 367 如何实现α 混合 367 示例程序(LookAtBlendedTriangles.js) 369 混合函数 369 半透明的三维物体(BlendedCube.js) 371 透明与不透明物体共存 372 切换着色器 373 如何实现切换着色器 374 示例程序(ProgramObject.js) 375 渲染到纹理 379 帧缓冲区对象和渲染缓冲区对象 380 如何实现渲染到纹理 381 示例程序(FramebufferObject.js) 382 创建帧缓冲区对象(gl.createFramebuffer()) 385 创建纹理对象并设置其尺寸和参数 385 创建渲染缓冲区对象(gl.createRenderbuffer()) 386 绑定渲染缓冲区并设置其尺寸(gl.bindRenderbuffer(), gl.renderbufferStorage()) 386 将纹理对象关联到帧缓冲区对象(gl.bindFramebuffer(), gl.framebufferTexture2D()) 388 将渲染缓冲区对象关联到帧缓冲区对象(gl.framebufferRenderbuffer()) 389 检查帧缓冲区的配置(gl.checkFramebufferStatus()) 390 在帧缓冲区进行绘图 390 绘制阴影 392 如何实现阴影 392 示例程序(Shadow.js) 393 提高精度 399 示例程序(Shadow_highp.js) 400 加载三维模型 401 OBJ 文件格式 404 MTL 文件格式 405 示例程序(OBJViewer.js) 406 自定义类型对象 409 示例程序(OBJViewer.js 解析数据部分) 411 响应上下文丢失 418 如何响应上下文丢失 419 示例程序(RotatingTriangle_contextLost.js) 420 总结 422 附录A WebGL 中无须交换缓冲区 423 附录B GLSL ES 1.0 内置函数 427 角度和三角函数 428 指数函数 429 通用函数 430 几何函数 433 矩阵函数 434 矢量函数 435 纹理查询函数 436 附录C 投影矩阵 437 正射投影矩阵 437 透视投影矩阵 437 附录D WebGL/OpenGL :左手还是右手坐标系? 439 示例程序(CoordinateSystem.js) 440 隐藏面消除和裁剪坐标系统 443 裁剪坐标系和可视空间 444 什么是对的? 446 总结 448 附录E 逆转置矩阵 449 附录F 从文件中加载着色器 453 附录G 世界坐标系和本地坐标系 . 457 本地坐标系 458 世界坐标系 459 变换与坐标系 461 附录H WebGL 的浏览器设置 . 463
MAPGIS 实验报告 一.实验目的 MAPGIS 是通用的工具型地理信息系统软件,具有强大的空间数据的图形显示、各类地图制图的 制作功能,作为各类数字信息的可视化工具,可将数字形式的地理信息以直观的图形形 式在屏幕上显示,能自动进行线段跟踪、结点平差、线段接点裁剪与延伸,多边形拓扑 结构的自动生成,还可以消除图幅之间图元的接边误差,为地学信息的综合分析提供了 一个理想的桌面式地理信息系统。所以,在制作土地利用现状图时应用MAPGIS软件是十 分便捷的,同时综合了此软件的基础操作和方法,能培养我们的自主探究能力和综合分 析能力。 二、实验内容 通过对MAPGIS软件的学习,将长沙市雨花区的地形图(JPG)生成该区域的土地利用图( MPJ),并导出各用地类型的面积,分析此地区的用地现状是否合理,并将所有结果文件 打包上交。 三、实验步骤 在制作土地利用现状图时,利用MAPGIS 的图形文件对于图形的输入和编辑在技术上分为三个步骤:数据准备、计算机制图、成 果输出。其中包括四个文件夹,(MSI文件,控制点文件,矢量化文件,系列标准图框)。 下面按照制作图像的步骤来编写实验报告。 1. :MSI文件的转化制作,用来的雨花亭的JPG格式的图像转化为MSI格式。在MAPGIS目 录上面的打开:图像处理 图像分析 文件 数据输入 添加文件,得到下面的图片 点击图上面的转换,就得到了MSI文件。 (二): 雨花亭文件精矫正的制作,先制作系列标准图框:实用服务 投影变换 系列标 准图框 生成1:5000图框 点击确定,得到了系列标准图框,修改一部分的数据,得到 接着:图像处理 图像分析 文件 打开影像;镶嵌融合 打开参照文件 参照点文件 参照 线文件 参照区文件。 下面到了添加控制点的步骤:镶嵌融合 添加控制点,依次的添加30个控制点,现在左 边的MSI图片上面找一个控制点,再在右边的图框上面对应的位置上面添加,得到了30控 制点。同时得到控制点的文件GCP。接着:镶嵌融合 影像融合;镶嵌融合 影像精矫正, 得到下图: 影像的精矫正到这里就完成了。 (三):矢量化 注记的重新标记,加深原有的标记,使得原有的文字变的更加的清晰。图像处理 输入 编辑 文件 打开精矫正图片。在左边工作栏中右击 新建点 输入文件名,得到新建的点 文件。放大图片,找到需要重新标注的地方:N点编辑 输入点图元 修改注释的高度,注 释宽度分别为2.5 确定 点击需要修改的地方 弹出编辑框 输入你修改的文字 确定 点击 移动点 移动修改的文字到需要覆盖的地方,下面的是修改后面的图片的效果: 修改完之后保存就可了。 线编辑,区域的分化。 打开精矫正文件,左边工作区域右击新建线,输入文件名地物分类现状图。在精矫正的 图片上面进行分区,每个区域用线分别的勾勒出来,首先先把把图像画一个边框,在这 个基础之上,在把道路勾勒出来,之后在一次的进行分区,分区好了得到下图: 这个图的大体的分区如上图所示,接下来的工作就是要进行线段的检查了,检查 线拓扑 错误检查 出现对话框,显示的是一些没有闭合的线段,或者是重合的线段。一个一个的 进行修改,在进行拓扑检查,直到没有错误了。 下图是编辑好的现状图: 区域的涂色 打开前面的分好区域的线文件,分区已经分好了,接下来就是进行涂色了,在上面的工 具栏中点击 输入区 编辑输入区的颜色,一个形同的区用一样子的颜色,不同的区用不 一样子的颜色,依次的涂好色。 得到下图: 接下来就是:工程输出 文件 页面设置 点击确定,R光栅输出 生成JPEG图像。把生成的图像保存在文件夹里面。 :图像投影转换 使用服务 投影变换 文件 打开影像 P投影转换 P进行投影变换 选择文件 当前投影 点击确定 点击目的投影 点击确定,回到初始的对话框,继续点击开始转换,得到了投影之后的图像。 :属性库管理和数据的输出 库管理 属性库管理 文件 装入区文件;结构 编辑属性结构 编辑区属性结构 添加相应 的名称和数据 输入好数据之后,属性 编辑属性 编辑区属性,依次的在地类名称和地类号中输入数据 ,得到: 上面只是截图的一部分。输入好之后保存文件。 :数据的输出 库管理 属性库管理 文件 导出 分别在用户DSN和系统DSN中保存文件,其中需要注意的事情就是文件名必须的要一致, 否则文件输入不出来。 下面就是输出的数据截图: 以上就是输出的数据表格。 四:实验收获 通过这次作业的完成,让我更加的明白清楚的知道了MAPGIS的一些基本的运用和一些情 况的处理,让我更加的熟悉软件的过程,能够让我更加熟练的运用软件,在学习的过程 之中遇到了许多的问题,通过想老师和同学的请教,让问题得到了很好的解决,同时也 成功的完成了这次的作业和实验报告的总结,谢谢老师对我们的耐心的教导。 -------

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值