zoukankan      html  css  js  c++  java
  • 《OpenGLES 2.0 Programming Guide》学习笔记

    介绍OpenGLES

    glesKhronosGroup创立,目前有3个版本1.0,1.1(统称1.x)2.0ES1.0,1.1OpenGL1.3,1.5继承而来,ES2.0OpenGL2.0继承而来。

    OpenGLES 2.0specifications有两份:theOpenGL ES 2.0 API specificationtheOpenGL ES Shading Language Specification(OpenGL ES SL)

    下图是ES2.0的图形流水线,灰色的方框是可编程阶段。

    VertexShader

    vs实现了一种作用于顶点的通用可编程方法。Vs的输入由以下几种组成:

    1Attributes– 由顶点数组提供的Per-vertexdata

    2Uniforms– vs使用的常量数据

    3Samplers– 一种特殊的uniform,用于表示vs使用的纹理。Samplers对于vs是可选的。

    4Shaderprogram – vs程序,源代码或者可执行的,描述了对于顶点进行的操作。


    Vs的输出叫做varying变量。在primitiverasterization阶段,varying的值对于每个生成的fragment进行计算,并将结果作为输入传递给fragmentshader。从赋予图元每个顶点的varying值为每个fragment生成一个varying值的机制叫做插值(interpolation)

    下图是vs的输入输出图:

    Vs可用来进行传统的基于顶点的操作,例如使用矩阵变换位置、进行光照计算产生一个per-vertex的颜色、生成或变换纹理坐标。然而,因为VS是一个程序,VS可以用来进行任意的自定义顶点变换。

    一个简单的VS:

    uniformmat4 u_mvpMatrix;

    attributevec4 a_position;

    attributevec4 a_color;

    varyingvec4 v_color;

    voidmain()

    {

    v_color= a_color;

    gl_Position= u_mvpMatrix*a_position;

    }

    其中gl_Position是一个内置的varying,不用声明,vs必须向它写入。Main函数是shader唯一的入口点。

    PrimitiveAssembly 图元装配阶段

    vs之后的流水线阶段就是图元装配,所谓图元是指可以用OpenGLEs绘制命令绘制的基本几何体。图元由顶点组成,顶点有各种属性,如位置和颜色。vs使用这些属性进行计算。在图元集成阶段,经过shade的顶点被集成进一个个独立的几何图元中,例如三角形,线或点精灵。对于每个图元,必须对其进行viewfrustum裁剪。如果图元部分在viewfrustum中,会被clip,如果图元完全在viewfrustum外则被丢弃。clipping之后,顶点的位置被转换到屏幕坐标系。另外,背面剔除也会在这阶段进行。这之后,图元就准备被传送到下一个阶段-光栅化。

    Rasterization光栅化阶段

    光栅化将图元转化成二维片段(fragments)的集合,这些片段将被fragmentshader处理。Fragment表示了可以被绘制到屏幕上的像素。(注:图元的属性是之前VS输出的varying,在光栅化阶段,基于图元的varying值被插值计算为基于片段的varying)

    FragmentShader

    Fs实现了一种作用于片段的通用可编程方法。FS对光栅化阶段产生的每个片段进行操作。

    Fs的输入:

    1Varyingvariables Vs输出的varying经过光栅化插值后对每个片段产生的值。

    2UniformsFS使用的常量数据

    3Samplers:一种特殊类型的uniform,表示FS使用的纹理

    4ShaderprogramFS程序,代码或可执行的,描述了对于片段的操作。

    Fs可以丢弃片段或者为片段生成一个颜色,输出保存到gl_FragColor中。

    光栅化阶段生成的color,depth,stencil和窗口坐标(Xw,Yw)将会成为流水线per-fragment操作阶段的输入(FS之后的阶段)。

    一个简单的FS程序:

    precisionmediump float;

    varyingvec4 v_color; // input vertex color from vertex shaderVS必须写这个v_color

    voidmain(void)

    {

    gl_FragColor= v_color; //gl_FragColor是唯一的输出

    }

    Per-FragmentOperations阶段

    Fs之后就是逐片段操作阶段。光栅化产生的一个片段,具有窗口坐标(Xw,Yw),只能修改framebuffer中位置在(Xw,Yw)的像素。

    下图是Per-FragmentOperations阶段的操作过程:

    其中PixelOwnershipTest是用来决定framebuffer中某个位置的像素是否属于OpenGLEScontext,比如如果OpenGLES的显示窗口被其他窗口遮住了,则一些像素就通不过这个测试,也就不会被OpenGLES显示。(似乎程序不需要控制这个)

    Scissortest:测试(Xw,Yw)的片段是否在scissor矩形内,如果不在,片段被丢弃。

    Stenciland depth test:测试incomingfragmentstencildepth值,决定片段是否被丢弃。

    Blending:将新产生的片段的颜色和framebuffer中相应位置像素的颜色进行混合。

    Dithering:抖动

    per-fragment阶段之后,(Xw,Yw)处的片段要么被拒绝要么会产生一个fragmentcolor,depth,stencil值写入到framebuffer(Xw,Yw)位置。Fragmentcolor, depth, stencil值是否真的写入framebuffer还取决于相应的writemasks是否enable

    另外,OpenGLES2.0也提供了从framebuffer回读像素的接口,但只有colorbuffer可以回读,depthstencil值是读不到的。

    alphatest不再在pre-fragmentstage中支持,需要在fragmentshader中实现

    logicOp被去掉了,因为很少使用。(alphatestlogicopOpenGL2.0OpenGLES1.x中是存在的)

    ES2.0ES1.1的兼容性

    ES2.0不向后兼容ES1.1,2.0不支持1.1中的固定功能流水线。2.0vertexshader代替了1.1中的固定功能顶点处理单元,即:顶点T&L,纹理坐标生成和变换,顶点颜色计算。fragmentshader代替了1.1的固定功能texturecombine单元,即为每个textureunit实现一个texturecombine stage

    (不同于OpenGL2.0,OpenGL2.0是完全向后兼容的,同时支持可编程流水线和固定流水线。)

    EGL简介

    OpenGLEs命令需要一个renderingcontext和一个drawingsurfaceRenderingcontext存储OpenGLEs状态。Drawingsurface是图元绘制上去的surfacedrawingsurface指定了渲染所需的buffer的类型,例如colorbuffer, depth bufferstencilbuffer,以及每种buffer的位数。

    OpenGLES API不提供如何创建renderingcontext并且attachnatviewindow systemEGL是一个在OpenGLEsnative windowsystem之间的接口。然而厂商在实现OpenGLEs时并不一定提供EGL,不同平台可能有各自的接口。


    2  Shaders and Programs


    -----------创建编译shader---------------------------

    Gluintshader = glCreateShader(type);

    glShaderSource(shader, 1,&shaderSrc, NULL);

    glCompileShader(shader);

    -----------program------------------------------------

    创建:

    Gluintprogram = glCreateProgram();

    Attach:

    glAttachShader(program,shader);

    1)一个programobject必须且仅能attach一个vs和一个 fs

    1. attach可以在任意时刻进行,哪怕shader没有compile,甚至没有source

    2. 可用glDetachShaderprogram上移除shader

    3. 如果一个shader已经被attach到一个programobject上,调用glDeleteShader不会立即删除这个shader,这个shader会被标记为待删除,一旦这个shader不再attach到任何programobject上,这个shader的内存将被释放。

    Link:

    当两个shaderattachprogram上,并且shader已经成功compile,使用glLinkProgram连接program

    1)linker会检查,确保fs使用的所有varyingvariables会被vs写入,并且被声明成同样的类型

    2)linker会检查,确保vsfs中声明的uniforms具有匹配的类型

    3)linker会确保最终的program适合实现的限制(即,attribute,uniform, varying的数量,使用的指令)

    link阶段会产生最终的硬件指令(link是在GPU上进行的)

    有些问题如纹理没有被绑定到samper,在Link时是不能查到的,可使用glValidateProgram(program);验证program是否可以在当前状态下执行。这个操作很慢,一般只在debug版本中使用。

    使用:

    glUseProgram(program);

    -----------uniformsand attributes------------------------------------

    uniform: 应用程序通过通过es2.0API传送给shader的只读变量;uniformprogramobject中是共享的,即一个programobject只有一组uniforms。如果uniform同时在vsfs中声明,必须具有相同的类型,并且在这两个shader中该uniform的值是一样的。在link时,linker会为program中的每个activeuniform分配一个uniformlocation。应用程序使用这个uniformlocation作为标识来载入uniform的值。(location需要手动查询得到)

    所谓“active”uniform是值在program中实际使用的uniform,如果仅仅是声明不算。

    载入uniform值,首先使用

    glGetActiveUniform(program,index, bufSize, &length, &size, &type, uniform_name);

    获得uniform的名字,以及数据类型type,数组元素数size(如果不是数组size就是1

    然后使用

    location= glGetUniformLocation(program, uniform_name);

    获得uniformlocation。这样就可以使用一系列的glUniform*函数载入uniform的值。

    使用glUniform*的时候,不用programhandle作为参数,因为这是针对当前useprogram的。但是一旦为一个program中的uniform设置了值后,这个值将会保存在这个program中。

    Gettingand Setting Attributes:

    @seeChapter6 “Vertex Attributes, Vertex Arrays and Buffer Objects”


    3 OpenGL ES Shading language (GLESSL)


    数据类型

    float,int, bool

    float,vec2, vec3, vec4

    int,ivec2, ivec3, ivec4

    bool,bvec2, bvec3, bvec4

    mat2,mat3, mat4

    变量声明

    floatspecularAtten;

    vec4vPosition;

    mat4mViewProjection;

    ivec2vOffset;

    变量可以在声明时初始化也可以以后初始化。初始化通过使用构造器完成,构造器也被用作类型转换。

    变量构造器

    GLESSL的类型转换是很严格的,变量只能被赋值给相同类型的变量或者和相同类型的变量一起计算。为了进行类型转换,语言中有很多构造器。

    FloatmyFloat = 1.0;

    boolmyBool = true;

    intmyInt = 0;

    myFloat= float(myBool); //convert from bool-> float

    myFloat= float(myInt);

    myBool= bool(myInt);

    向量的构造

    单标量参数-向量所有分量值设置为该标量

    多标量或向量参数构造– 向量分量从左到右赋值,参数必须够用

    vec4myVec4 = vec4(1.0); // myVec4={1.0,1.0,1.0,1.0}

    vec3myVec3 = vec3(1.0, 0.0, 0.5);

    vec3temp = vec3(myVec3);

    vec2myVec2 = vec2(myVec3); //myVec2 = {myVec3.x, myVec3.y}

    myVec4= vec4(myVec2, temp, 0.0); //myVec4={myVec2.x, myVec2.y, temp.x, 0.0}

    矩阵的构造

    如果只提供一个标量参数,这个值被设置在矩阵对角线上,例如mat4(1.0)会构造一个4X4单位阵。

    矩阵可用多个向量构造;可用多个标量构造;或者向量和标量混搭构造。

    访问向量和矩阵的成员

    向量可用点访问(”.”) 或数组下标访问。可以用{x,y,z,w},{r,g,b,a},{s,t,r,q}这几种分量名字,但不可混用。

    向量可通过点访问重新排序,如:

    vec3myVec3 = vec3(0.0, 1.0, 2.0);

    vec3temp;

    temp= myVec3.xyz; // temp = {0.0, 1.0, 2.0}

    temp= myVec3.xxx; // temp = {0.0, 0.0, 0.0}

    temp= myVec3.zyx; // temp = {2.0, 1.0, 0.0}

    下标访问,从0开始,[0]=x,[1]=y,[2]=z;下标如果不是常量,gles2.0可能不支持。

    矩阵被看出是由很多向量组成。例如mat3是由3vec3组成。矩阵的列用数组下标选择。

    然后每个列向量使用向量访问规则。例如:

    mat4myMat4 = mat4(1.0); //单位阵

    vec4col0 = myMat4[0];

    floatm1_1 = myMat4[1][1];

    floatm2_2 = myMat4[2].z;

    常量

    可定任意基本类型的常量。常量变量在shader中值不改变。常量必须在声明时初始化。

    const float zero = 0.0;

    constfloat pi = 3.14159;

    constvec4 red = vec4(1.0, 0.0, 0.0, 1.0);

    constmat4 identity = mat4(1.0);

    结构体

    structfogStruct

    {

    vec4color;

    floatstart;

    floatend;

    }fogVar;

    fogVar= fogStruct(vec4(0.0,1.0,0.0,0.0), 0.5, 2.0);

    数组

    floatfloatArrary[4];

    vec4vecArray[2];

    关于数组的重要事项:

    1)很多OpenGLEs实现不允许使用无法在编译时确定值的变量去索引数组。

    2)不能在创建时初始化数组-没有这样的语法。因此,数组不能是常量。数组元素需要逐个初始化。

    操作符

    操作必须使用于具有相同基本类型的变量之间

    *, / , + , - (基本类型必须是float或者int,乘法可在floats,vectors,matrices之间组合)

    ++, --

    =

    +=,-=, *=, /=

    ==,!=, <, >, <=, >= (只能作用于标量,要比较向量使用内置函数)

    &&

    ^^(逻辑异或)

    ||

    函数

    c类似,最大的区别在于参数的传入方式。OpenGLES提供了特定的修饰符来定义参数是否可以被函数修改:

    in (默认,如果不指定)passedby value,不会被函数修改

    inout passed by reference, 如果被修改,函数返回后改变值。

    out 变量的值不传入函数,函数返回时变量修改。

    例子:

    vec4myFunc(inout float myFloat, out vec4 myVec4, mat4 myMat4);

    例子,计算diffuse光照的函数:

    vec4diffuse(vec3 normal, vec3 light, vec4 baseColor)

    {

    returnbaseColor * dot(normal, light);

    }

    另外,函数不可以递归,因为一些实现会以inline的方式实现函数调用。

    内置函数

    使用例子,计算高光:

    floatnDotL = dot(normal, light);

    floatrDotV = dot(viewDir, (2.0*normal)*nDotL - light);

    floatspecular = specularColor * pow(rDotV, specularPower);

    控制流

    c类似,但有很多限制。

    1)判断表达式必须是bool类型(bool变量或者比较表达式),因为glessl不支持隐式转换。

    2)使用循环有各种限制,归结为:OpenGLES必须在编译时知道迭代数。

    必须只有一个循环迭代变量,并且只能使用简单的表达式增加或减少(i++,i--,i+=constant,i-=constant)

    循环的结束条件判断必须是循环索引和一个常量表达式之间的比较;

    不能在循环中改变迭代变量的值;

    基本上,gles2不需要实现真正支持loop,它这样限制loop是为了让编译器可以将loop展开。

    Uniforms

    uniform变量保存应用程序通过OpenGLES2.0API传给shader的只读值。uniform可以保存shader使用的各种数据,如变换矩阵,光的参数或者颜色。基本上,shader需要使用的任意参数如果对于所有的顶点和片段都是常量(但在编译时不知道)应该传入给uniform

    Uniform变量在全局范围内定义:

    uniformmat4 viewProjMatrix;

    uniformmat4 viewMatrix;

    uniformvec3 lightPosition;

    uniform存储于硬件上的“constantstore”, 可使用的数量有限制。OpenGLES2.0至少支持128vertexuniform vectors16fragmentuniform vectors

    Attributes

    只在vs中使用,用于指定per-vertex的输入。通常保存位置,法线,纹理坐标,颜色等数据。

    attributevec4 a_position;

    attributevec4 a_texCoord0;

    attribute数量的限制:至少是8

    Varyings

    vs的输出,fs的输入(在光栅化时进行了图元内线性插值)。在vs,fs中的声明必须一致。

    varyingvec2 texCoord;

    varyingvec4 color;

    varying在硬件上就是interpolator(插值器),数量至少为8.

    Preprocessorand Directives 预处理器和指令

    宏定义和测试指令:

    #define

    #undef

    #if

    #ifdef

    #ifndef

    #else

    #elif

    #endif

    宏不能用参数定义;#if,#else,#elif可以使用defined测试某个宏是否定义。

    预定义的宏:

    __LINE__

    __FILE__ //ES2.0中总是0

    __VERSION__//OpenGL ES Sl的版本(e.g,100)

    GL_ES //1

    #error指令会在编译shader时引发一个编译错误并将message写入infolog

    #pragma指定用于编译器实现相关的指令。

    #version指令用于表示glesslshader语言的版本,必须写在最上面,目前版本是#version 100

    #extension用于厂商对语言的扩展。

    Uniformand Varying Packing

    存储布局为4Xn网格,packing自动进行。

    PrecisionQualifiers

    用于指定任意基于float或者int类型的变量。

    highpvec4 position;

    varyinglowp vec4 color;

    mediumpfloat specularExp;

    如果变量没有指定精度,则使用默认精度。默认精度在shader代码头部指定:

    precisionhighp float; //用于所有基于浮点数值的变量

    precisionmediump int; //用于所有基于整型值的变量

    vertexshader中,如果没有指定默认精度,则intfloat的默认精度都是highp。对于fragmentshaderfloat类型的默认精度没有默认值,必须显示的声明。并且在fs中,不一定支持highp,可查询得知是否支持(GL_FRAGMENT_PRECISION_HIGH定义,或者查询OES_fragment_precision_high扩展)。

    #ifdefGL_FRAGMENT_PRECISION_HIGH

    precisionhighp float;

    #else

    precisionmediump float;

    #endif

    Invariance

    invariant关键字可以作用于vs输出的任意varying变量上。

    shader在编译时,编译器可能进行优化,导致指令被重排。这意味着两个shader间相同的计算,不一定产生精确相等的结果。这对于multipass渲染来说是个问题,一个物体被渲染多次,如果计算出来的位置有差别,就会有瑕疵。比如产生z-fighting

    使用invariant可以在写shader时指定如果使用了相同的计算,输出的结果必须精确一致。

    invariant关键字可以使用在varying声明上或者已经声明的varying上。

    invariantgl_Position; //内置的已经声明的varying,使用invariant

    invariantvarying texCoord; //声明时使用invariant

    可以使用#pragma指令器然所有的变量invariant:

    #pragmaSTDGL invariant(all)

    注意,为了实现invariant,编译器限制了优化,所有仅当需要时才使用invariant


    4 Vertex Attributes, Vertex Arrays, VBO


    什么是attributes

    如何指定attributes数据和他们支持的数据类型?

    如何将attribute的索引和vertexshader中相应的顶点attribute名字绑定?

    什么是attributes

    顶点数据或称为顶点属性,是给每个顶点指定的数据,可以每个顶点逐一指定也可所有顶点使用一个常量值。

    opengles 1.1中,顶点属性有预定义的名字,例如position,normal,color,texturecoordinates。因为固定流水线只需要这些顶点属性,所以预定义这些属性是合理的。在可编程流水线中,开发者需要在vertexshader中使用他们自己的顶点属性名字,因此对于gles2.0,用户自定义顶点属性名是必须的,既然如此,预定义名字也就不需要了。


    如何指定顶点属性数据?

    逐个顶点指定属性数据使用vertexarray;图元的所有顶点使用相同数据则使用常量值。

    查询支持的最大顶点属性数,用glGetIntegerv(GL_MAX_VERTEX_ATTRIBS,&maxVertexAttribs);

    这个值至少为8。


    图元中所有顶点如果使用相同属性值就只用常量顶点属性

    常量顶点属性只能使用GLfloat,通过glVertexAttrib1/2/3/4f[v]指定。例如:

    void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);

    这个函数将(x,y,z,1.0)设定到索引为index的顶点属性上。

    顶点数组

    vertex arrays为每个顶点指定属性数据,他们是存储在应用程序地址空间的buffer(即client space)。

    vertex array提供了一种高效而灵活的方式来指定顶点属性数据。要指定vertex array,需要使用

    glVertexAttribPointer函数: 

    void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *ptr);

    index: 指定通用顶点属性的索引,取值为0到支持的最大顶点属性数-1

    size: 顶点数组中,index所引用的顶点属性的组件数。有效值为1-4

    type: 数据格式。有效值为:GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FLOAT, GL_FIXED, GL_HALF_FLOAT_OES

    normalized: 用于指出是否非浮点数据格式在被转换为浮点数时是否需要归一化。

    stride: size参数所指定的顶点属性组件们是为逐顶点顺序存储在顶点数组中的。stride指出了顶点i和顶点i+1数据之间的差值。如果stride是0,所有顶点的属性数据是序列存储的。如果stride>0,stride作为一个pitch值来获取下一个顶点的数据。

    顶点数组存储方式:

    1)array of structures: 所有顶点属性存储在同一个单一的buffer中

    2)structure of arrays: 每种顶点属性存储在一个独立的buffer中


    在vertex shader中定义attribute:

    attribute vec4 a_position;

    attribute vec2 a_texcoord;

    attribute vec3 a_normal;

    attribute修饰符只能出现在vertex shader中,在fragment shader中会导致编译错误;attribute只能是float, vec2, vec3, vec4, mat2, mat3, mat4类型的。attribute变量不能是数组或者结构。

    ES2.0实现支持GL_MAX_VERTEX_ATTRIBS个vec4类型的vertex attributes。float, vec2, vec3被计算做一个vec4。mat2,mat3,mat4分别计算为2,3,4个vec4。不像uniform和varying变量可以被自动pack,attribute不会被pack。

    attribute变量在shader中是只读的。

    在vs中定义的attribute如果没有使用是不会被认为激活的,也不会计算为使用的容量。

    如果attribute的总量超过了GL_MAX_VERTEX_ATTRIBS,vertex shader会link失败。

    一旦program成功被link,我们可以查出这个program的vs使用的active的vertex attribute数量:

    glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numActiveAttribs);

  • 相关阅读:
    iframe跨域
    jquery 中的 $(“#”) 与 js中的document.getElementById(“”) 的区别
    jQuery中this与$(this)的区别
    div层调整zindex属性无效原因分析及解决方法
    The local variable......been initialized
    equals方法的重写
    eclipse快捷键
    JAVA中几个常用的方法
    Java基础4(方法基础和一维数组)
    Java基础3笔记
  • 原文地址:https://www.cnblogs.com/Anzhongliu/p/6092088.html
Copyright © 2011-2022 走看看