GLSL语法知识汇总

    xiaoxiao2021-03-26  43

    一、基础类型和限定符

    1.GLSL基础变量类型:

    (1)Float;// IEEE浮点值 (2)int ; (3)uint; (4)bool; (5)sampler; 采样器,作为访问纹理图像的不透明句柄。 OGL实现可能并不严格地实现这些类型,例如整数也可以存储在浮点寄存器中,最大整数也不一定是2^15。

    2.变量的作用域

    变量可以在使用时候声明,类似C++,不像C(c语言要求在函数开头声明所有使用到的类型)。 在函数外部声明的变量具有全局作用域,他们在着色器程序的所有函数中均可见(program中,也就是shader对象源代码在链接后可以共享该全局变量)。 函数内部的变量作用域和C++相同。

    3.变量的初始化和转换

    变量声明时候就可以初始化。可以指定为八进制,十进制,十六进制常量。u或U无符号。3E-7等价于3*10^-7, -3E-7等价于-3*10^-7的科学计数法。 例如: int i, numParticles = 100; float force, g = -9.8; bool falling = true; 变量的转换,没有隐式类型转换,要求比C++还要严格,不同类型的转换到要使用类似该类型的构造函数一样的语法。 例如: float scale = 10.0; int ten = int(scale);

    4.聚合类型(向量和矩阵类型)

    GLSL支持每种基本类型的二维,三维,四维向量和矩阵(默认是列主序矩阵)。 float 的 vec2 vec3 vec4; mat2, mat3, mat4, mat2x2, mat2x3, mat3x4, mat4x4,mat4x2等各维。 int类型对应增加前缀i, 如ivec3。 uint类型对应增加前缀u, 如uvec3. bool类型对应增加前缀b, 如bvec3. mat3x4含义是3列4行,也就是行主序的mat4x3的写法一样。其实转置矩阵就是本来行主序的矩阵,用列主序类写,当然向量也要用列序来写,这样:v' = v*M; v'^T = (v*M)^T= M^T * v^T; 列式矩阵用v'^T描述向量的结果,其实坐标系意义上都是x,y,z,w所以都是一样的。 在行式矩阵中4D空间引入主要是包含平移的变换也放到矩阵中和透视除法,为了平移时候矩阵最后一列总是( 0,0,0,1)向量; 透视除法时候,主要是因为透视时候存在对结果每个元素统一的除法,可以放到向量第四维去,然后约定向量结果中如果最后一位不是1那么需要除以w,所以一般传入向量w=1。 列式矩阵中4D空间存在也是一样的,只是矩阵最后一列改为最后一行,最后一行平移变为了最后一列。向量最后一列改为了最后一行。全部矩阵向量都用列式表示,右乘矩阵(所以D3D中基于父坐标系表示子坐标系位置的M缩放*M旋转*M平移代数变换顺序,到OGL中就变成了基于local coordinate的M平移^T*M旋转^T*M缩放^T, ^T刚好是列式矩阵;模型到视口的变换, OGL中也变成了M视口^T * M模型^T),右乘向量实现变换,结果为列式向量即可。 在CG语法中v*M时候,v会转换为行向量,乘积结果等于列式的M^T*v。 例如,世界坐标系中计算法向量: // the same as mul(transpose(_World2Object), float4(input.normal, 0.0)) but fast. float3 normalDir = normalize(mul(normalObjectDir, _World2Object).xyz);

    向量的使用

    向量的构造 vec3 velocity = vec3(0.0, 2.0, 3.0); 向量类型转换: ivec3 = ivec3(velocity); 向量的截短和拉长: vec4 color; vec3 rgb = vec3(color); vec4 white = vec4(rgb, 1.0); 访问和赋值向量中的元素: 1)通过名称 float r = color.r; color.r = 1.0f; 2)通过下标 float r = color[0]; color[0] = 1.0f; 3)搅拌式 vec3 luminance = color.rrr; vec4 color2 = color.abgr;

    矩阵的使用

    矩阵的初始化: 可以用向量初始化,或单个值指定,但是OGL是列主序矩阵,所以先填充的是第一列。 mat3 m =mat3 (1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 1.0); vec3 colum1 = (1.0, 0.0, 0.0); vec3 colum2 = (1.0, 0.0, 0.0); vec3 colum3 = (1.0, 0.0, 0.0); mat3 m = mat3(colum1, colum2, colum3); mat4 m = mat4(colum1, 1.0, colum2, 2.0, colum3, 1.0); 矩阵的访问和赋值: 通过下标形式访问和赋值矩阵的一个向量或者一个元素: mat4 m = mat4(1.0); vec4 = m[0]; //矩阵的第一列 float yScale = m[1][1];// 矩阵的第二列,第二行

    6.结构

    结构定义一组类型不一样的数据作为一个组,方便数据的记录和传递;定义一个结构后会定义一个新的类型,并隐式定义一个构造函数,可以通过这个构造函数来给结构赋值,通过.成员名来访问结构中的数据。 struct Particle( float lifetime, vec3 position, vec3 velocity); Particle p = Particle(3.0, pos, vel);

    7.数组

    GLSL中可以定义任何类型的数组,包括结构体,从0到n-1, 但是只能是一维的,不能是二维及以上的。数组的声明: 可以指明大小也可以不指明大小,使用的时候指明大小,数组可以作为函数参数和返回值使用。 float coeff[3];// 等价于float[3] coeff; int coeff[]; 定义时候的初始化: float coeff[3] = float[3](2.3, 1.0, 3.0); 可以用数组的下标来访问数组元素。 数组长度可以用默认内建.length()得到n大小; for(int i = 0; i < coeff.length(); i++) { coeff[i] *= 1.0f; }

    8.布局限定符layout

    顶点着色器输出和片段着色器输入不能使用布局限定符(顶点输入和片段输出中才有,输出的index是用于MRT指定的,默认是0)。 layout(location = 0)表示该变量在顶点属性中的位置为0. 对应顶点属性数组中0: // 设置(设置或解析)数据到顶点属性的指定位置内 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vVertices); // 激活GPU内部顶点属性的指定位置 glEnableVertexAttribArray(0); "#version 300 es                         " "layout(location = 0) in vec4 vPosition;  " "void main()                              " "{                                        " "   gl_Position = vPosition;              " "}                                        ";

    9.类型限定符

    (1)const 全局常量类型,编译期常量值,可以在函数内部也可以作为函数参数类型。 (2)in 全局变量,着色器阶段输入。(GSLS 1.3以前attribute限定顶点着色器输入,varying限定片段着色器的输入,GSLS 1.3后就消除了)。片段着色器额外的in关键字限定符(和顶点着色器的输出的限定符一样): centroid 在打开多点采样的时候,强迫一个片段输入变量的采样位于图元像素覆盖的区域内。 smooth 以透视校正的方式插值片段输入变量。 flat 不对片段输入插值(对于所有的片段输入都是和归属的顶点一样的,就像在单调着色中一样)。 noperspective 线性插值片段变量。 例如:flat centroid in fragment; 例如: // Vertex Shader smooth centroid out vec3 v_color; // Fragment shader smooth centroid in vec3 v_color; (3)out 全局变量,着色器阶段输出, 顶点着色器的输出变量可以和片段着色器输入变量,使用相同的in关键字限定符,含义和片段中的in关键字相同。 (4)invariant限定符,强制多道渲染算法中,每道着色器渲染时计算位置完全相同(否则因为指令顺序的累积误差,多pass渲染时候,可能前面的计算和后面的计算会存在误差,大多数情况下是可以接受的) 在顶点和片段着色器中都声明为invariant,它可能对着色器性能产生影响,会禁用GLSL编译器所执行的优化。 在调试时候可以用: #pragma STDGL invariant(all) 强制要求所有的变量都使用不变性。 (5)uniform 指定这个值从应用程序输入给着色器(在着色器真正执行前),在着色器内部图元中保持为常量值。 glUseProgram(programId); glUniform2f(offsetLocationId, fXOffset, fYOffset); //绘制三角形,真正根据设置执行着色器程序 glDrawArrays(GL_TRIANGLES, 0, 3); 若uniform变量是由顶点和片段着色器共享的,那么他们必须声明为相同的全局变量,任何类型都可以声明为uniform类型。 用glGetUniformLocation获取变量句柄,例如:GLuint offsetLocationId = glGetUniformLocation(programId, "offset");获取uniform变量句柄。 用glUniform*或glUniformMatrix*设置变量的值,例如:glUniform2f(offsetLocationId, fXOffset, fYOffset);设置uniform变量的值,设置变量可以是一个基础类型,一个向量,一个数组或包含向量的数组。 Uniform修饰结构体,然后用结构体传参: 例如: struct gl_DepthRangeParameters{ float near; float far; float diff; }; uniform gl_DepthRangeParameters gl_DepthRange;//通过app访问 gl_DepthRange实现通信。

    uniform块:

    Unform变量可以在指定的uniform块中声明,uniform块可以支持着色器的共享及其他功能。 uniform块是Opengl 3.1引入的,因为uniform的数目越来越增加,经常出现多个着色器程序共享uniform的情况,因为uniform变量会在glLinkProgram的时候产生uniform索引位置,所以不能共享,引入了uniform块,通过统一缓冲区对象(uniform buffer object),实现优化uniform的访问,和使得多着色器程序共享uniform变量。 uniform块的声明: uniform Matrices { mat4 ModelView; mat4 Projection; mat4 Color; } 所有类型,除了采样器,都允许放到一个uniform块中。此外,uniform块必须声明为全局作用域。 uniform块的布局限定符 shared 指定uniform块在多个程序对象之间共享(默认的共享设置)。 packed 布局uniform块使得使用的内存最小化,通常不允许跨程序共享。 std140 使用opengl规范中默认的布局。 row_major 使得uniform块中的矩阵按照行主序的方式存储。 column_major 指定矩阵应该按照列主序的方式存储(这是默认的排序)。 uniform块的布局应用: layout(shared, row_major) uniform uniformBlockName{...}; // 只是声明该uniform块的布局 影响所有后续的uniform布局可以使用: layout(shared, row_major) uniform;该行之后声明的所有uniform都会使用该布局,直到修改或覆盖uniform声明位置。 访问uniform块中的变量,uniform块的uniform符号并不作为变量作用域,所以两个相同的Uniform块中声明相同的uniform变量将会导致错误,所以访问uniform变量的时候没有必要使用uniform块的名字。 访问uniform块的步骤: 1)glGetUniformBlockIndex获取uniform块索引号 GLuint glGetUniformBlockIndex(GLuint  program, const GLchar * uniformBlockName ); program Specifies the name of a program containing the uniform block. uniformBlockName Specifies the address an array of characters to containing the name of the uniform block whose index to retrieve. 2)用glGetActiveUniformBlockiv()使用GL_UNIFORM_BLOCK_DATA_SIZE返回编译生成的uniform block 大小, glGetActiveUniformBlockiv()还可以获取和一个指定的uniform块相关的其它参数。 例如:glGetActiveUniformBlockiv(program, uboIdex, GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize); 3)用malloc申请内存块,获取uniform块中的各个成员类型和大小,程序中的值填写malloc内存块 获取该uniform block的所有成员的索引号, glGetUniformIndices  retrieves the indices of a number of uniforms within  program .使用: void glGetUniformIndices(GLuint program, GLsizei uniformCount, // 输入的个数 const GLchar **uniformNames, // 输入的名字 GLuint *uniformIndices); // 输出的索引号 获取这个特定的索引的offset,size,type(或者每个成员的该值调用一次): glGetActiveUniformsiv — Returns information about several active uniform variables for the specified program object void glGetActiveUniformsiv( GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); 4)使用glGenBuffers/glBindBuffer/glBufferData填充到缓冲区 申请缓冲区,把缓冲区对象绑定到GL_UNIFORM_BUFFER目标,且把上述应用程序填写的malloc buffer值拷贝到缓冲区中。 5)调用glBindBufferRange/glBindBufferBase将unform块和帧缓冲区对象联系起来 void glBindBufferRange( GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); void glBindBufferBase (GLenum target​ , GLuint index​ , GLuint buffer ​ ); 将缓冲区对象buffer和uniform块index联系起来。target可以是GL_UNIFORM_BUFFER或者GL_TRANSFORM_FEEDBACK_BUFFER。glBindBufferBase在offset等于0,size等于缓冲区对象的大小。 5)用初始化和修改缓冲区对象的命令来初始化和修改uniform block块的值。 6)如果有多个着色器程序共享一个uniform块,为了避免它为每个程序分配一个不同的块索引,在调用glLinkProgram之前要调用glUniformBlockBinding()来绑定块。 void glUniformBlockBinding (GLuint program​ , GLuint uniformBlockIndex​ , GLuint uniformBlockBinding​ ); program The name of a program object containing the active uniform block whose binding to assign. uniformBlockIndex The index of the active uniform block within  program​  whose binding to assign. uniformBlockBinding Specifies the binding point to which to bind the uniform block with index  uniformBlockIndex​  within  program​ .(应该是之前和缓冲区对象绑定好的uniform block index).

    uniform块使用实例:

    "uniform Uniforms = { vec3 translation; // 平移 float scale; // 缩放 vec4 rotation; //四元数 bool enabled; }" uboIndex = glGetUniformBlockIndex(program, "Uniforms"); glGetActiveUniformBlockiv(program, uboIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize); GLvoid *buffer = malloc(uboSize); const char *names[NumUniformMembers] = { "translation", "scale", "rotation", "enabled", }; enum { Translation, Scale, Rotation, Enabled, NumUniformMembers } // 得到每个成员的indices,size,offset, type;用于填充CPU中的memory buffer数据,然后用 glBufferData传输到GPU VBO中。 GLuint indices[NumUniformMembers]; GLuint size[NumUniformMembers]; GLuint offset[NumUniformMembers]; GLuint type[NumUniformMembers]; glGetUniformIndices(program, NumUniformMembers, names, indices); glGetActiveUniformsiv(program, NumUniformMembers, indices, GL_UNIFORM_OFFSET, offset); glGetActiveUniformsiv(program, NumUniformMembers, indices, GL_UNIFORM_SIZE, offset); glGetActiveUniformsiv(program, NumUniformMembers, indices, GL_UNIFORM_TYPE, offset); 使用memcpy将CPU应用程序中的具体变量值通过size,type字节数赋值给buffer中。 void *memcpy(void *dest, const void *src, size_t n); memcpy(buffer + offset[Scale], &scale, size[Scale] * TypeSize(type[Scale])); memcpy(buffer + offset[Translation], &scale, size[Translation] * TypeSize(type[Translation])); memcpy(buffer + offset[Rotation], &scale, size[Rotation] * TypeSize(type[Rotation])); memcpy(buffer + offset[Enabled], &scale, size[Enabled] * TypeSize(type[Enabled])); 填充满buffer后。 glGenBuffers(1,&ubo); glBindBuffer(GL_UNIFORM_BUFFER, ubo); glBufferData(GL_UNIFORM_BUFFER, uboSize, buffer, GL_STATIC_RAW); glBindBufferBase(GL_UNIFORM_BUFFER, uboIndex, ubo);

    二、运算符和语句

    1.算术操作符,和C++基本一样,包括优先级,就是没有求模运算符。

    2.运算符重载

    GLSL中绝大多数运算符都进行了重载,尤其是向量和矩阵的运算操作。 因为运算符重载的存在,OGL中的矩阵右乘向量,矩阵左乘矩阵;如果是用矩阵左乘向量,那么向量会变为行式向量,矩阵不变,所以进行运算需要谨慎,最好查询CG或HLSL的说明文档。

    3.语句

    if else语句。 switch case default语句。 for,while, do..while语句和C++中一样,OES2.0对循环的限制比较多,3.0基本没有限制,但是为了性能和发热尽量减少循环的使用。 流程控制关键字: break; continue; return; discard; // 丢弃当前的片断值,且终止片段着色器的执行。

    三、函数

    内建函数

    GLSL CG内建函数见: http://http.developer.nvidia.com/Cg/index_stdlib.html HLSL内建函数: https://msdn.microsoft.com/en-us/library/bb509638(v=vs.85).aspx

    自定义函数

    用户自定义的函数可以在单个着色器对象内部定义,并在多个着色器程序中使用。 函数的参数有限定符: in(默认), const in, out, inout(输入输出). 函数参数可以是任何类型,如果是数组类型要指明长度。 函数返回值可以是内置的GLSL类型或自定义的结构体,但不能是数组, 如果函数没有返回值便是void。 形式: returnType functionName([accesssModifier] type1 variable1, [accesssModifier] type2 variable2, ...) { return returnValue; // 除非没有返回值 } 函数不能递归,原因是一些实现真正将编译链接后的着色器程序内嵌到GPU中运行,以支持没有堆栈支持的GPU进行渲染。

    四、在GLSL程序中使用OpenGL状态值

    Opengl api可以设置的所有值几乎都可以在顶点和片段着色器中访问和设置。

    五、在着色器中访问纹理图像

    顶点着色器和片段着色器使用纹理图像,OGL实现不一定支持顶点着色器使用纹理图像,但是片段着色器一定能使用纹理图像。 OGL纹理图像在片段着色器中需要用uniform修饰的采样器标识纹理图像,app cpu程序中需要访问和关联纹理对象到着色器采样器中。 例如: 着色器中: uniform sampler2D tex; void main() { // 需要纹理坐标在图像指定位置提取纹理单元值 gl_FragColor = gl_Color * texture2D(tex, gl_Texcoord[0].st); } CPU中: GLint texSampler = glGetUniformLocation(program, "tex"); // 给采样器分配一个纹理单位,用glUniform1i或glUniform1iv glUniform1i(texSampler, 2); // set tex to use GL_TEXTURE2 对纹理采样之后进行的计算由着色器代码控制(可以控制mipmap选择的偏移值,并使用投影纹理技巧),但是纹理图像的采样方法仍然由应用程序控制。例如CPU控制一幅纹理图像是否包含mipmap层,这些mipmap层是如何进行采样的,以及用于解析返回纹理单元值(基本是glTexParameter*()函数所设置的参数)的过滤器。 采样器除了可以作为全局变量,还可以作为函数参数,但是必须是类型匹配的纹理采样器。

    采样器类型

    https://msdn.microsoft.com/en-us/library/windows/desktop/bb509644(v=vs.85).aspx https://www.khronos.org/opengl/wiki/Sampler_(GLSL)

    依赖性纹理读取

    读取纹理贴图中的值需要纹理坐标来指定读取,而纹理坐标可以来自顶点着色器自然来自应用程序的指定,也可以来自一个纹理图像。纹理图像还可以存放其它的图形数据,例如高度图,uv纹理坐标,法线贴图,光照材质信息等。 一个纹理的读取依赖另一个纹理的读取,前面一个纹理读取就叫依赖性纹理读取。 例如: uniform sampler1D coords; uniform sampler3D volume; void main() { vec3 texCoords = texture1D(coords, gl_TexCoord[0].s); vec3 volumeColor = texture3D(volume, texCoords); ... }

    纹理缓冲区

    GLSL着色器中数组可以用作静态初始化的值,也可以作为值的集合呈现在uniform变量中的一个数组。如果需要考虑超出可用大小限制的数组,那么需要纹理缓冲区(ogl 3.1以前是直接用纹理图像来存储,但不直接),纹理缓冲区类似一维纹理,可以在着色器中使用一个整型值来索引,它提供了较为昂贵的纹理内存,且也支持较大的数据集合。可以像创建任何缓冲区对象一样来创建缓冲区,然后将缓冲区对象关联到纹理缓冲区。 关联缓冲区对象和纹理缓冲区对象的函数是glTexBuffer() void glTexBuffer( GLenum target, // target是GL_TEXTURE_BUFFER GLenum internalFormat, // 纹理格式,解释buffer中的数据 GLuint buffer); // 缓冲区对象buffer句柄 类似其它纹理图像,通过调用glActiveTexture()来指定那个纹理单元和纹理缓冲区相关联。

    六、着色器预处理器

    GLSL预处理指令由GLSL编译器在编译和链接之前解析。用于控制编译,创建条件编译代码以及定义一些值。但是没有文件包含指令。 #define // 宏定义,定义值和访问语句,但是不能提供字符串替换和链接工具

    预定义的宏

    __LINE__ __FILE__ __VERSION__ #undef // 取消宏的定义 #if //条件编译指令,对#define的处理 #ifdef #ifndef #else #elif #endif #error text Cause the compiler to insert text (up to the first newline character) into the shader information log #pragma options Control compiler specific options 例如: #pragma optimize(on) 优化开启(默认是开启的) #pragma optimize(off) #pragma debug(on) 开启或禁止调试(默认是禁止调试) #pragma debug(off) #pragma STDGL invariant(all) 开启多通道渲染不变性 #extension options Specify compiler operation with respect to specified GLSL extensions #version number Mandate a specific version of GLSL version support #line options Control diagnostic line numbering

    七、顶点着色器的细节

    1.顶点着色器的输入

    (1).glVertex*, glNormal*, glColor*等输入为: gl_Vertex, gl_Normal, gl_TexCoord[n], gl_Color, gl_SecondaryColor, gl_FogCoord, gl_MultiTexCoord[n], gl_VertexID, gl_InstanceID. (2).用户定义的uniform变量,直接用app设置uniform变量的值。 glGetUniformLocation(programId, "offset"); glUniform2f(offsetLocationId, fXOffset, fYOffset); (3).用户定义的顶点属性,顶点信息的输入。 1)在OGL 1.3之前使用 GLint  glGetAttribLocation (GLuint  program, const GLchar * name );获取顶点属性的值,或者void glBindAttribLocation(GLuint program,GLuint index,const GLchar *name);来绑定那个index(来自于之前用 glGetAttribLocation得到的index)和name关联。 设置着色器顶点属性或者变量的值为: glVertexAtrrib*来设置或返回着色器变量中的值。 如果是矩阵变量,需要多次调用glVertexAtrrib4f来设置矩阵每列的值。 2)顶点着色器还可以利用顶点数组这个工具,和其它顶点数据一样,顶点属性变量的值也 可以存储在顶点数组中。通过glDrawArrays(), glDrawElement()等函数进行更新,可以用 glVertexAttribPointer()函数指定更新变量的数组(对VBO数据块进行解析)。 指定解析顶点数组是其中一部分,客户端还需要 启用顶点属性数组是通过glEnableVertexAttribArray(index)函数实现, index必须是0到GL_MAX_VERTEX_ATTRIBS-1之间的值。 //顶点位置和颜色数据 const GLfloat vertexData[] = { -0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; glGenVertexArrays(1, &vaoId); glBindVertexArray(vaoId); //创建vertex buffer object对象 glGenBuffers(1, &vboId); glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); // 指定VAO如何去解析VBO数据块中的数据。 // 在Shader创建之前或创建之后指定都是可以的,因为glDrawXXX时候才真正去处理Opengl状态机中的设置。 //启用顶点位置属性索引,要在display中指定的(vao包装了切换vao即可),因为需要开启顶点属性索引才能绘制,特别是绘制物体多的时候,需要切换才能正确绘制。 // 也可以封装在VAO中,只负责启用glEnableVertexAttribArray不关闭即可。 glEnableVertexAttribArray(0); // 激活顶点属性数组 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); //指定Position顶点属性数据格式,大小会根据glDrawArrays截断。 //启用顶点颜色属性索引 glEnableVertexAttribArray(1); //48是4*3*4 = 48, 指定Color顶点属性数据格式,大小会根据glDrawArrays截断。 // 输入到Shader中的时候会并行的从顶点取得一个数据,从颜色取得一个数据作为整个顶点的输入.再进行下一个顶点的输入。 glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)48);

    2.顶点着色器的输出

    1)顶点着色器输出,片段着色器不显式使用的变量 // vec4,经过变换后的顶点坐标(在裁剪坐标系中) gl_Position 在着色器中通常是= gl_ModelViewProjectionMatrix * gl_Vertex实现。 多道渲染算法中要求gl_Position一致,那么用gl_Position = ftransform(); // float 顶点的点大小 gl_PointSize // 用于控制点的输出大小,与glPointSize()类似,只是这里的操作是基于顶点进行的。 // 着色器中设置gl_PointSize,那么应用程序需要glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // 用户定义的裁剪平面坐标系最终的顶点位置(和裁剪平面一样的坐标系,视觉或物体坐标系) gl_ClipVertex 用户定义的裁剪平面使用glClipPlane函数指定,并且写入到gl_ClipVertex的坐标值中(该坐标值位于裁剪平面中)普通的裁剪空间是在视觉坐标系中,可以把当前的顶点变换到视觉坐标中: gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex; 2)可以写入到片段着色器,并且它的值可以在片段着色器中读取的变量: gl_FrontColor(图元正面颜色), gl_BackColor(图元背面颜色), gl_FrontSecondaryColor, gl_BackSecondaryColor,默认情况下选择的都是正面颜色,要选择背面的颜色,用glEnable(GL_VERTEX_PROGRAM_TWO_SIDE),OGL底层将根据底层图元的方向来选择正面或者背面的颜色。 gl_TexCoord[n], // 第n个纹理坐标值 gl_FogFragCoord,// 片段雾坐标值 顶点着色器中使用纹理贴图,和片段着色器中一样,但是不能自动选择mipmap需要用GLSL的texture*Lod函数手工选择mipmap层。确保OGL是否支持顶点着色器纹理,用 GLint nVertexShaderTex; glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &nVertexShaderTex);返回非0则支持。 顶点着色器输出的信息,可以在反馈模式下输出,在链接shader program之前调用glTransformFeedbackVarings glGenQueries后,绑定反馈输出到缓冲区对象,然后glBeginTransformFeedback(),开始glDrawCall后查看反馈信息即可。

    八、片断着色器的细节

    1.片断着色器的输入

    OpenGL程序也可以向片断着色器发生数据,片断着色器的数据来源有: 1) gl_FragCoord// vec4类型,着色器中好像没有直接利用 gl_Color// vec4类型 gl_SecondaryColor// vec4类型 gl_TexCoord[n] // vec4类型 gl_FogFragColor ;//float 类型,要是是视觉坐标系中的z值,要么是插值雾坐标 gl_FrontFacing // bool 类型,指定片断是否是正面图元 gl_PointCoord // vec2类型,点块纹理的位置在[0,1]之间,没有启用点块纹理则不起作用 上述的这些值都来自于顶点着色器或固定管线变换后,经过光栅化插值,到达基于单个像素的片断中。 2) 用户定义的uniform变量值,来自应用程序或者顶点着色器。 3)用户定义的顶点属性

    2.片断着色器的输出

    这些变量经过片断着色器都会产生输出: 1)gl_FragColor 是片断输出的最终颜色值,如果有辅助颜色,那么混合的辅助颜色。 2)gl_FragDepth 片断的最终深度,用于后面的深度测试,片断中无法修改x,y值,但是可以修改z值。 3)gl_FragData[n];gl_FragData数组允许写入到额外的缓冲区中 片断着色器可以将值写入到gl_FragColor或gl_FragData中,但是不能同时写入到两者。 写入到gl_FragColor中是写入了颜色缓冲区中,写入gl_FragData[n]中是写入了glDrawBuffers函数的数组的第n个缓冲区中,可以同时写入到多个非颜色缓冲区的glDrawBuffers指定的缓冲区中。 GLSL 1.3后允许给片断着色器的输出变量重命名,且可以控制这些输出到各种绑定的绘制缓冲区中。可以在链接之前指定片断输出名到一个缓冲区的映射,也可以在链接之后询问程序来确定变量的布局。 链接之前指定片断输出名到一个缓冲区的映射,用: void glBindFragDataLocation (GLuint program​ , GLuint colorNumber​ , const char * name​ ); program The name of the program containing varying out variable whose binding to modify colorNumber 是0到GL_MAX_DRAW_BUFFERS的绘制缓冲区索引。 The color number to bind the user-defined varying out variable to name 是非gl_开头的用户自定义的变量,如果是gl_开头则会报错 The name of the user-defined varying out variable whose binding to modify 链接之后可以通过: GLint glGetFragDataLocation( GLuint program, const char * name); query the bindings of color numbers to user-defined varying out variables. program The name of the program containing varying out variable whose binding to query. name The name of the user-defined varying out variable whose binding to query. 返回和来自GLSL program的name输出相关的颜色缓冲区索引,利用该索引应该可以访问到该值,或者操作写入到 colorNumber缓冲区中的着色器输出结果,实现自己需要的缓存值和实现图形结果
    转载请注明原文地址: https://ju.6miu.com/read-659109.html

    最新回复(0)