GLSL语法知识汇总

一、基础类型和限定符

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缓冲区中的着色器输出结果,实现自己需要的缓存值和实现图形结果

你可能感兴趣的:(OpenGL图形学)