webGL rendering flow

-- settled by horizon

从最高层开始,处理的过程是这样的:每次你调用类似于drawArrays的函数时,WebGL会处理你之前传递给它的数据,这些数据都是以属性(Attribute)(比如用到的顶点位置数组)和Uniform变量(用来储存模型视图矩阵和投影矩阵)的形式存在的,然后WebGL会把这些数据传递给顶点着色器。

每次当相应顶点的属性建立完成后,都会调用一次顶点着色器;而Uniform变量,就像它的名字一样,在调用过程中并不发生任何改变,顶点着色器只是需要这些数据——这些数据代表着模型视图矩阵和投影矩阵,使得顶点可以被放置到透视中并且调整到当前的模型视图状态下——然后顶点着色器把处理的结果储存在称为“Varying变量”(Varying Variable)的变量中。顶点着色器通常会输出一系列的Varying变量,其中有个特别的也是必须的变量,那就是gl_Position,它储存着经过顶点着色器处理过的顶点坐标。

在顶点着色器处理完成之后,WebGL将会神奇般的将这些Varying变量中描述的3D图形转换为2D图片,然后为图片中的每个像素调用片元着色器(这就是为什么在有些3D图形系统中你会听到他们把片元着色器称为“像素着色器”的原因了)。当然,确切的说是为那些非顶点位置的像素调用片元着色器,而在那些顶点位置上的像素则已经建立好了顶点。这个过程“填充”了各顶点间限定的空间,从而显示出一个可见的形状。片元着色器的作用是返回每个内插点的颜色,并储存在称为gl_FragColor的Varying变量中。

当片元处理器工作完毕后,WebGL会再处理一下它输出的结果,然后放到Frame Buffer(帧缓冲)中,也就是最终显示在屏幕上的东西。

流程关键词

  • VertexArray :绘图场景中所有的物体空间位置由点坐标数组构成。
  • VertexShader :对每个点的位置进行变换,计算光照方程式生成每点的颜色,生成或者变换纹理坐标。对每个点的位置进行变换,计算光照方程式生成每点的颜色,生成或者变换纹理坐标。
    • Attributes ---- 采用点向量数组形式传入的每个点的数据
    • Uniforms ---- vertex shader 使用常量数量
    • Sampler --- 表示纹理的特殊常量。可选。
    • Varying Variable--- Vertex Shader的输出变量,作为Fragment Shader的输入。必须与Fragment Shader中的输入Varying一一对应。
    • gl_Position--- 是每个点固有的Varying .表示点的空间位置。
  • Fragment Shader :通过shader程序对Fragment进行处理。接收varying 的参数,对片元设置颜色,或其他属性
  • Primitive Assembly :管线中这个流程是对所有的点数据进行点线面等基础图元的组装。这个过程会对所有的图元进行剪切和筛选。对于不在视区空间中的部分进行剪切,对于不可见的面进行筛选。
  • Rasterization :光栅化的过程就是对所有的经过Primitive Assembly图元转换成屏幕上可以显示的二维Fragment。
  • Per-Fragment Operations:每个Fragment的操作。每个Fragment在屏幕上都有一个显示坐标(wx,wy)。

Opengl ES 2.0 Shader编程 OverView

  1. 编写vertex Shader和fragment shader源码。
  2. 创建两个shader 实例:GLuint glCreateShader(GLenum type); [gl.createShader]
  3. 给Shader实例指定源码。 glShaderSource [gl.shaderSource]
  4. 编译shaer源码 void glCompileShader(GLuint shader) [gl.compileShader]
  5. 创建shader program -- GLuint glCreateProgram(void) [gl.createProgram]
  6. 绑定shader到program 。 void glAttachShader(GLuint program, GLuint shader)。每个program必须绑定一个vertex shader 和一个fragment shader。 [gl.attachShader]
  7. 链接program 。 void glLinkProgram(GLuint program) [gl.linkProgram]
  8. 使用porgram 。 void glUseProgram(GLuint program) [gl.useProgram]
  9. * 对于使用独立shader编译器编译的二进制shader代码,可使用glShaderBinary来加载到一个shader实例中。

Shader语言中的数据类型与变量

  • Uniforms and Attributes
    • Uniforms 是一个program 中统一分配的,vertext 和fragment中同名的Uniform必须同类型。对应于不经常变化的变量。
    • Attributes 变化率高的变量。主要用来定义输入的每次点属性。
    • Uniforms and Attributes 在shader中通过location 和 name 来对应的。
    • 通过GLint glGetUniformLocation(GLuint program,const char* name).根据一个Uniform的名称获取其location. [gl.getUniformLocation]
    • 通过 glUniform***系列函数可以给一个location 设置一个Uniform的值。 [gl.uniform**]
  • 数据类型
    • 三类基本类型:float , int , boolean
    • 复合类型:
      • 向量(浮点,整型,布尔) vec2 , vec3,vec4
      • 矩阵 mat2, mat3,mat4
      • 结构体 用基本类型和复合类型构建结构体。

      变量初始化。可以由简单类型初始化复杂类型。
      可以通过常量下标访问矩阵,变量数组,向量中的分量。
      操作与C语言类似。
      函数。传参指定in out 或者inout。
      if else 流控制。
      for循环。只支持常数循环次数。
      无论下标,还是循环变量,都只能使用编译时可确定的常数。

    • Uniform 前辍修饰的变量初始值由外部程序赋值。在program中具有统一访问空间。存储空间有限
    • Attribute 前辍修饰的变量定义的是每个vertex的属性变量,包括位置,颜色,法线和纹理坐标
    • Uniform 和 Attribute 类型的变量在shader中是只读的,只能由外部主机程序传入值。

Vertex Attribute

  1. Vertex Attributes 是每点的属性数据。与一个index序号绑定。
    外部程序可通过 glBindAttribLocation将一个attribute 名与一个index绑定起来。[gl.bindAttribLocation]
    当然,OPENGL ES 内部会自动绑定所有attributes.外部程序只需要通过 glGetAttribLocation获取指定attribute名的index. [gl.getAttribLocation]
  2. 给Attribute传值有两种方式:
    可以通过 glVertexAttribPointer会话vertex Array数组。 [gl.vertexAttribPointer]
    可以通过 glVertexAttrib** 给指定location(index)的attribute赋值。 [gl.vertexAttrib**]

Primitive Assembly and Rasterization

  • Primitive : 三角形 ,线 , 点
    • 三角形绘制时可使用:GL_TRIANGLES[gl.TRIANGLES] ,GL_TRIANGLE_STRIP [gl.TRIANGLE_STRIP] ,GL_TRIANGLE_FAN [gl.TRIANGLE_FAN]三种模式。
    • 绘线可用:GL_LINES[gl.LINES], GL_LINE_STRIP[gl.LINE_STRIP], GL_LINE_LOOP[gl.LINE_LOOP]三种模式
    • 线宽可用:void glLineWidth(GLfloat width)进行设置。 [gl.lineWidth]
    • 绘点可用: GL_POINTS [gl.POINTS]. 绘点vertex Shader中内置gl_PointSize输出变量。定义点的半径
    webGL 并未提供图形库
  • Drawing Primitives 绘制primitves时可用的两个函数:
    void glDrawArrays(GLenum mode, GLint first, GLsizei count) [gl.drawArrays]
    void glDrawElements(GLenum mode, GLsizei count,GLenum type, const GLvoid *indices) [gl.drawElements]
    这两个函数调用前都需要绑定点属性数组,主要是坐标数组。后都给定下标数组来绘制primitive,绘制大量的mesh时,有利于提高性能。
  • Primitive Assembly 视区空间,六个面组成。对于每个primitive,凡在空间外的都clip掉。
    • Perspective Division
      标准化坐标,即齐次坐标(x,y,z,w)转化为(x/w,y/w,z/w,1) 其中x,y,z绝对值均小于w
    • Viewport Transformation
      将标准化后的空间坐标转换到平面可视窗口范围内的坐标上。窗口范围定义void glViewport(GLint x, GLint y, GLsizei w, GLsizei h) 。[gl.viewport]
      其中Xw,Yw,Zw是窗口坐标,Z是深度坐标。
    • Rasterization :
      为每个primitive生成一个fragment。一个fragment由屏幕像素坐标(Xw,Yw)和其他的fragment数据组成,这些数据将在fragment shader中被使用。
    • Culling :
      对triangle进行Rasterization之前必须确定它是正面还是反面朝向观察者,Culling操作将会忽略背向观察者的面。首要的问题是如果区分三角形的正面和反面,这跟绘制三角形时三个点的顺序有关。
      通过 void glFrontFace(GLenum dir) [gl.frontFace] 来设定三角形的正面。Dir可为GL_CW [gl.CW] or GL_CCW [gl.CCW](默认). 意思就是如果三角形三个点的绘制顺序为v0,v1,v2,站在观察者的位置,如果v0,v1,v2路径与 glFrontFace [gl.frontFace]设定的时针方向相同,那么面向观察者的这一面就是正面,否则就是反面。
      Culling其他相关接口:
      void glCullFace(GLenum mode) [gl.cullFace] 选择要Cull的面,参数可为 GL_FRONT [gl.FRONT], GL_BACK[gl.BACK], and GL_FRONT_AND_BACK [gl.FRONT_BACK]。默认是GL_BACK[gl.BACK]。
      void glEnable(GLenum cap) [gl.enable]
      void glDisable(GLenum cap) [gl.disable]
      其中 cap参数为 GL_CULL_FACE 。 使能、禁止GL_CULL_FACE功能。

Vertex Shader

  • Vertex shader 能做的事
    • 点操作的矩阵变换
    • 根据光照公式计算每点的color
    • 生成或转换纹理坐标
    • ...
  • vertex shader overview
    1. Built-In Special Variables 输出给管线的后端
      • gl_Position: 点坐标
      • gl_PointSize: point size(大小)绘制单点时有用
      • gl_FrontFacing: 正反面(默认)
    2. Built-In Uniform State
      • uniform gl_DepthRangeParameters gl_DepthRange; 窗口坐标深度远近值。 用gl_DepthRange可设置
    3. Precision Qualifiers 精度限定
      • 对于基于int float的类型变量可以在定义是指定精度,三种:highp,mediump lowp 。 如 highp vec4 position; 默认精度设定,如precision highp float; 对所有未加精度限定的float,使用默认设定highp。
    4. ES 2.0 Vertex Shader Limitations
      • Length of Vertex Shader
      • Temporary Variables
      • Flow Control : for 循环
      • Conditional Statements:if(bool){} 或者if(bool) {}else{}
      • Array Indexing: 只能使用常量。
      • Counting Number of Uniforms Used in a Vertex Shader:OpenGL ES 2.0 实现标准是 128 vec4 entries的uniform常量,被用来存储这几种类型的数值:
        a) Variables declared with the uniform qualifier。如uniform vec4 v;
        b) Const variables. 如: const int i=1;
        c) Literal values。 如 vec(0.0,0.0,0.0,0.0),对于这种情况,应该尽量使用const变量来定义重复的文字常量。如const float z=0.0; 然后使用vec(z,z,z,z)来表示这个0向量。Uniform 存储空间占用会由4个0.0缩小到一个z.

Texturing

  • 纹理坐标
    • ...
  • Texture Objects and Loading Textures
    • Texture Object采用int句柄来指向。
    • void glGenTextures(GLsizei n, GLuint *textures) [gl.genTextures] 创建指定数目的Texture对象。返回句柄到textures
    • void glDeleteTextures(GLsizei n, GLuint *textures) 删除对象 [gl.deleteTextures]
    • 绑定纹理void glBindTexture(GLenum target, GLuint texture),对于2D纹理target使用GL_TEXTURE_2D [gl.bindTexture]
    • 加载纹理: void glTexImage2D(....) [gl.texImage2D]
  • Texture Coordinate Wrapping
    • 纹理坐标超出[0,1]范围时映射纹理使用的回绕方式:
      Texture Wrap Modes : GL_REPEAT [gl.REPEAT],GL_CLAMP_TO_EDGE [gl.CLAMP_TO_EDGE], GL_MIRRORED_REPEAT[gl.MIRRORED_REPEAT]
  • Using Textures in the Fragment Shader
    • 在fragment shader 中定义uniform sampler2D s_texture;常量。
      获取s_texture的location。
      samplerLoc = glGetUniformLocation(
      userData->programObject,
      "s_texture");
      使用void glActiveTexture(GLenum texture) 将GL_TEXTURE0 ..GL_TEXTURE31指定为当前active texture。
      使用glBindTexture 绑定一个texture句柄到GL_TEXTURE*
      glUniform1i(userData->samplerLoc, GL_TEXTURE* ); 当前激活的GL_TEXTURE*传给s_texture
      在shader中使用 texture2D内建函数取纹理坐标对应的color值。如:gl_FragColor = texture2D(s_texture, v_texCoord);
  • Texture Subimage Specification
    • 对纹理的子块进行更新。
      void glTexSubImage2D(GLenum target, GLint level,
      GLint xoffset, GLint yoffset,
      GLsizei width, GLsizei height,
      GLenum format, GLenum type,
      const void* pixels)
  • Blending
    • 生成的fragment是具有(x,y,z)坐标的,其中x,y是在窗口中显示的位置。Z是深度坐标。Blending是指当Fragment要显示在窗口中时,如果使能了blend,那么当前fragment的颜色要经过指定的blend方法去与屏幕中已经存在于(x,y)坐标上的颜色进行混合。

      基本的混合模式是Cfinal = (fsource *Csource )op (fdestingation*Cdestination) Csource 和Cdestination是源和目的颜色,fsource和fdestination是源和目的的系数。也就是最终的颜色将是源和目的的颜色乘以各自的系数然后OP操作进行混合。

      void glBlendFunc(GLenum sfactor, GLenum dfactor);指定对于源和目的都采用什么系数模式。
      void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);

      对于源和目的采用分开的系数模式。即alpha部分与RGB部分独立指定。
      void glBlendEquation(GLenum mode); 指定OP操作。
      void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);对于RGB部分和Alpha部分分开指定OP操作。

  • Reading and Writing Pixels to the Framebuffer
    • 从color buffer中读取图像像素
      void glReadPixels(GLint x, GLint y, GLsizei width,
      GLsizei height, GLenum format,
      GLenum type, void *pixels);