GLSL语言基础
GLSL(wiki chs)是OpenGL(OpenGL ES、WebGL)的着色器语言,拥有如下特点:
1. 基于C语言的语法(如:大小写敏感,每条语句必须以分号结尾),是一门面向过程的强类型语言(type sensitive language)
2. 预处理中没有#include包含文件指令
3. 除了bool、int、uint、float基础类型外,还支持数组类型,另外GLSL还内置了适合3D图形操作的向量与矩阵类型,以及采样器(纹理)类型
4. double、dvec*、dmat*类型需要#version 400及以上才支持
5. 更严格类型隐式转换规则
6. 变量没有赋初值时,都会被填充为false、0或0.0
7. if条件语句和switch条件语句与C语言一致
8. for循环语句和while循环语句与C语言一致
9. return、continue和break与C语言一致。另外引入了discard,该关键字只能在fs中使用,表示放弃当前片元,直接处理下一个片元。
10. 无指针、无字符和字符串类型
11. 无union、无enum
12. 向量、矩阵、自定义结构体变量可通过构造函数进行初始化
13. 增添了一些C++语言的特性:如函数重载
shader运行时,死循环或者崩溃,会导致进程无响应(弹出如下对话框),驱动崩溃重启
与C语言一样,包括:预处理 -- 编译 -- 链接 -- 执行 四大步骤
示意代码如下:
const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; // build and compile our shader program // ------------------------------------ // vertex shader unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); // check for shader compile errors int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); // 检查是否vs编译成功 if (!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); // 获取编译错误消息 std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } // fragment shader unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); // check for shader compile errors glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); // 检查是否fs编译成功 if (!success) { glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); // 获取编译错误消息 std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } // link shaders unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); // check for linking errors glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); // 检测链接着色器程序是否失败 if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); // 获取链接错误消息 std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } glDeleteShader(vertexShader); // 链接完成后,不在需要vs着色器对象 glDeleteShader(fragmentShader); // 链接完成后,不在需要fs着色器对象 // render loop // ----------- while (1) { // Sleep for a while glUseProgram(shaderProgram); // 设置要使用的着色器程序 // Draw Something }
// 释放着色器程序
glDeleteProgram(shaderProgram);
注释
单行注释
// Hello GLSL.
多行注释
/********************************* This is my first GLSL. Let's take a look. *********************************/
预处理
#version XXX
// 用于指定当前glsl文件的版本
①不同的版本glsl,所能使用语言特性是不一样的。如:layout (location = 0) in vec3 Position; // layout是330才支持的特性
②如果要加该#version指令,必须是当前glsl文件的第一句有效代码行
③如果不加该#version指令,默认为GLSL 1.1,即:#version 110 注:WebGL1.0中vs和fs开头不用写#version版本,其语法对应GLSL ES 1.0(或GLSL 1.1)
#version 110 // GLSL 1.1 OpenGL 2.0 【默认】 #version 120 // GLSL 1.2 OpenGL 2.1 #version 130 // GLSL 1.3 OpenGL 3.0 #version 140 // GLSL 1.4 OpenGL 3.1 #version 150 // GLSL 1.5 OpenGL 3.2 #version 330 // GLSL 3.3 OpenGL 3.3 #version 330 core // GLSL 3.3核心模式 OpenGL 3.3 #version 400 // GLSL 4.0 OpenGL 4.0 #version 410 // GLSL 4.1 OpenGL 4.1 #version 420 // GLSL 4.2 OpenGL 4.2 #version 430 // GLSL 4.3 OpenGL 4.3 #version 440 // GLSL 4.4 OpenGL 4.4 #version 450 // GLSL 4.5 OpenGL 4.5 #version 460 // GLSL 4.6 OpenGL 4.6 #version 150 compatibility // GLSL 1.5 并向前兼容GLSL 1.2、1.3、1.4的语法 #version 300 es // GLSL ES 3.0 或称 ESSL 3.0 注:声明为这个后,宏GL_ES会被定义为1
GLSL不同版本的差异点,可参考:这里
#extension extname : behaivor
extname为编译器支持的扩展名称
behaivor可以是require、enable、warn、disable
#extension all : disable // 禁用所有扩展 #extension GL_NV_shadow_samplers_cube : enable // 启用名为GL_NV_shadow_samplers_cube的扩展
扩展行为说明
扩展行为 | 说明 |
require | 扩展是必需的。若当前扩展不受支持,将抛出错误 |
enable | 启用扩展。若当前扩展不支持,将抛出警告 |
warn | 对于扩展的任何使用均抛出警告 |
disable | 禁用扩展。若当前扩展被使用,将抛出错误 |
#if #elif [defined(), !defined()] #else #ifdef #ifndef #endif
// 条件编译
#define TEST1 // 定义为空的宏 //#define TEST1 1 // 宏已被定义 编译失败 error C7101: Macro TEST1 redefined #ifdef TEST1 // 条件成立 #endif //#if TEST1 // 空值不能进行条件判断 编译失败 error C0105: Syntax error in #if //#endif #undef TEST1 // 取消TEST1宏 #if TEST1 // 条件不成立 注:OpenGL有效; 但openGL es下 编译失败 error C0105: Syntax error in #if #endif #ifndef TEST1 // 条件成立 #endif #define TEST1 -1 // 定义为int的宏 #define TEST2 // 定义为空的宏 #if TEST1 && defined(TEST2) // 条件成立 #endif #define TEST3 true // 定义为bool的宏 #if TEST3 // 条件不成立 注:OpenGL有效;但openGL es下,bool不能进行条件判断 编译失败 error C0105: Syntax error in #if #endif #if !TEST3 // 条件成立 注:OpenGL有效;openGL es下,bool不能进行条件判断 编译失败 error C0105: Syntax error in #if #endif #if TEST3==true // 条件成立 注:OpenGL有效;openGL es下,bool不能进行条件判断 编译失败 error C0105: Syntax error in #if #endif #if !defined(TEST0) && TEST1 && defined(TEST2) // 条件成立 #endif #define TEST4 100 // 定义为int的宏 #if TEST4 // 条件成立 #endif #if TEST4 > 150 #elif (TEST4 > 120) || TEST1 // 进入elif分支 #endif #if !TEST4 #else // 进入else分支 #endif #define TEST5 2.0 // 定义为float的宏 //#if TEST5 //float不能进行条件判断 编译失败 error C0105: Syntax error in #if //#endif //#if TEST5>0.0 //float不能进行条件判断 编译失败 error C0105: Syntax error in #if //#endif
更多 #if 示例:
// Shader inputs and outputs keywords // // - ATTRIBUTE: used to mark a vertex shader inputs // - SHADER_IN: used to mark a non-vertex shader inputs // - SHADER_OUT: used to mark a non-fragment shader output // - OUT: used to mark a fragment shader output #if __VERSION__ >= 130 #define ATTRIBUTE in #define SHADER_IN in #define SHADER_OUT out #define OUT out #else #define ATTRIBUTE attribute #define SHADER_IN varying #define SHADER_OUT varying #define OUT #endif // Support array attributes #if __VERSION__ >= 130 #define ARRAY_ATTRIBUTE(name, size) name[size] #else #define ARRAY_ATTRIBUTE(name, size) name[size] #endif // Uniform blocks #if __VERSION__ >= 130 #define BEGIN_UNIFORM_BLOCK(name) uniform name { #define END_UNIFORM_BLOCK() }; #else #define BEGIN_UNIFORM_BLOCK(name) #define END_UNIFORM_BLOCK() #endif // Input and output blocks #if __VERSION__ >= 150 #define BEGIN_INPUT_BLOCK(name) in name { #define END_INPUT_BLOCK() }; #define BEGIN_OUTPUT_BLOCK(name) out name { #define END_OUTPUT_BLOCK() }; #else #define BEGIN_INPUT_BLOCK(name) #define END_INPUT_BLOCK() #define BEGIN_OUTPUT_BLOCK(name) #define END_OUTPUT_BLOCK() #endif // Texturing functions #if __VERSION__ >= 130 #define TEXTURE_2D texture #define TEXTURE_3D texture #define TEXTURE_RECT texture #define TEXTURE_CUBE texture #if __VERSION__ >= 150 #define TEXTURE_SIZE(sampler) textureSize(sampler) #else #define TEXTURE_SIZE(sampler) sampler ## _Size #endif #else #define TEXTURE_2D texture2D #define TEXTURE_3D texture3D #define TEXTURE_RECT texture2DRect #define TEXTURE_CUBE textureCube #endif // Invariance #if __VERSION__ >= 120 #define INVARIANT invariant #else #define INVARIANT #endif // Attribute location #if defined(GL_ARB_explicit_attrib_location) #define LOCATION(loc) layout(location = loc) #else #define LOCATION(loc) #endif // Geometry shader layout #if __VERSION__ >= 150 #define GEOMETRY_LAYOUT_IN(from) layout (from) in #define GEOMETRY_LAYOUT(to, max) layout (to, max_vertices = max) out #else #define GEOMETRY_LAYOUT_IN(from) #define GEOMETRY_LAYOUT(to, max) #endif // 为GLSL ES着色器脚本时,需要设置float和int类型的精度 #ifdef GL_ES precision mediump float;// 设置float类型的精度为中精度 注:高精度为highp、低精度为lowp precision mediump int; // 设置int类型的精度为中精度 注:高精度为highp、低精度为lowp #endif
#define #undef
// 宏定义、宏取消
#define TEST1 100 // 定义TEST1宏为100 #ifdef TEST1 // 条件成立 #undef TEST1 // 取消TEST1宏的定义 #endif #if !defined(TEST1) // 条件成立 #endif #define SQUARE(a) ((a)*(a)) #define MAX(a,b) \ // 宏必须在一行写完,多行写时必须带上 \行连接符 (((a) > (b)) ? (a) : (b)) #define MERGE(a, b) a##b // ## 字符拼接符
#line
// 指示下一行的行号,及当前所在的文件;该命令会修改__FILE__、__LINE__的值
该命令是提供给编译器使用的,程序员最好不要使用该命令
#if __LINE__==10 // 判断当前行号是否为10 #endif #if __FILE__==0 // 条件成立 __FILE__好像缺省为0 #endif #line 116 // 指定下一行的行号为116 #if __LINE__==116 // 条件成立 #endif #line 200 5 // 指定下一行的行号为200,当前文件的ID标识为5 #if __FILE__==5 // 条件成立 #endif
#pragma
// 用来控制编译器的一些行为
#pragma optimize(on) // 开启编译器优化 【默认】 #pragma optimize(off) // 关闭编译器优化 #pragma debug(on) // 开启debug选项,以获得更多的调试信息 #pragma debug(off) // 关闭debug选项 【默认】 #pragma STDGL invariant(all) // 让所有的变量全都不变 注:因为编译器需要保证不变性,所以会限制对着色器进行优化
#error
// error命令被执行,会导致当前文件编译失败
#if __VERSION__ >= 150 // __VERSION__为内置宏,十进制整数,为当前GLSL文件的版本号 #error This is an error! // error C0000: This is an error! #endif
__VERSION__、__FILE__、__LINE__、GL_ES、GL_FRAGMENT_PRECISION_HIGH
#if __VERSION__ >= 330 // __VERSION__为内置宏,int类型,为当前GLSL文件的版本号 #endif #line 100 3 // 指定下一行的行号为100,当前文件的ID标识为3 #if __FILE__==3 // 条件成立 宏__FILE__为int类型,当前Source String的唯一ID标识 #endif #if __LINE__==103 // 条件成立 宏__LINE__为int类型,当前的行号 #endif #if GL_ES // OpenGL下,内置宏GL_ES未定义,条件不成立;openGL es下,GL_ES为1,条件成立 #if GL_FRAGMENT_PRECISION_HIGH // 若当前片元着色器支持高浮点精度,则宏GL_FRAGMENT_PRECISION_HIGH为1 precision highp float; // 设置float类型的精度为高精度 precision highp int; // 设置int类型的精度为高精度 #else precision mediump float;// 设置float类型的精度为中精度 precision lowp int; // 设置int类型的精度为低精度 #endif #endif
运算符
除了没有指针相关的运算符外,其他的与c语言完全一致
注:对于向量、矩阵类型,运算符会在各个分量上进行
变量
变量名需要符合以下规则:
① 只能包括大小写字母、数字和下划线
② 变量名不能以数字开头
③ 不能是关键字或预留的关键字
④ 不能以gl_、webgl_或_webgl_开头
全局变量
定义在函数体外的变量。作用域规则与c语言全局变量一致。
局部变量
定义在函数内的变量。作用域规则与c语言局部变量一致。
内置常量
以gl_开头
所在shader | 常量 |
vs |
const mediump int gl_MaxVertexAttribs = 16; // 可用的顶点输入变量(GLSL 1.3之前为attribute变量)的最大数量 const mediump int gl_MaxVertexUniformVectors = 256; // 可用的vec4统一变量(uniform变量)的最大数量 需要#version 410及以上 const mediump int gl_MaxVertexOutputVectors = 16; // 可用的顶点输出变量(GLSL 1.3之前为varying变量)的最大数量 需要#version 410及以上 const mediump int gl_MaxVertexTextureImageUnits = 16; // 可用的纹理的最大数量 const mediump int gl_MaxCombinedTextureImageUnits = 32; // vs和fs中可用的纹理的最大数量的总和 |
fs |
const mediump int gl_MaxFragmentInputVectors = 15; // 片元输入变量(GLSL 1.3之前为varying变量)的最大数量 需要#version 410及以上 const mediump int gl_MaxTextureImageUnits = 16; // 可用的纹理的最大数量 const mediump int gl_MaxFragmentUniformVectors = 224; // 可用的vec4统一变量(uniform变量)的最大数量 需要#version 410及以上 const mediump int gl_MaxDrawBuffers = 4; // 多重渲染目标(MRT)的最大支持数量。 const mediump int gl_MinProgramTexelOffset = -8; // 内建ESSL函数texture*Offset偏移参数迟滞的最小偏移值 需要#version 410及以上 const mediump int gl_MaxProgramTexelOffset = 7; // 内建ESSL函数texture*Offset偏移参数迟滞的最大偏移值 需要#version 410及以上 |
内置变量
以gl_开头
所在shader | 变量 |
vs |
vec4 gl_Position; //必须将变换后的位置写入到这个变量中,用于输出顶点位置的裁剪坐标。该变量用highp精度限定符声明。 float gl_PointSize; // 用于写入以像素表示的点精灵尺寸。在渲染点时使用。该变量用highp精度限定符声明。 int gl_VertexID; // 输入变量,用于保存当前顶点的索引。该变量用highp精度限定符声明。 int gl_InstanceID; // 输入变量,用户保存当前图元的编号。对于常规的绘图调用,该值为0。该变量用highp精度限定符声明。 bool gl_FrontFacing; // 一个特殊变量,但不是vs直接写入,而是根据vs生成的位置和渲染的图元类型生成的。
struct gl_DepthRangeParameters |
fs |
vec4 gl_FragCoord; // 只读变量,保存窗口相对坐标(x,y,z,1/w)。 bool gl_FrontFacing; // 只读变量,片段是正面时为ture,否则为false。 vec2 gl_PointCoord; // 只读变量,保存点精灵的纹理坐标,处于[0.0, 1.0]区间 float gl_FragDepth; // 一个只写输出变量。在fs中写入时,覆盖片段固定功能深度值。应谨慎使用该功能,可能禁用GPU的深度优化(如:Early-Z) vec4 gl_FragColor; // fs输出的片元颜色 |
const变量
表示该变量的值不能被修改。声明的同时必须对它进行初始化,声明之后就不能再去改变它的值了,否则会导致编译错误。
const int lightspeed = 399792458; // 光速 m/s const vec4 red = vec4(1.0, 0.0, 0.0); // 红色 const mat4 identity = mat4(1.0); // 4x4单位矩阵
顶点输入变量(GLSL 1.3之前为attribute变量)
与顶点相关的输入型变量,被用来表示逐顶点的信息,也叫顶点属性(Vertex Attribute),必须声明为全局变量,仅在vs中使用。如:顶点的position、normal、uv等。这些变量为cpu传来的数据。
其只能是float或由float组合成的复合类型(vec2、vec3、vec4、mat2、mat3、mat4)
在#version 130及以上,顶点输入变量使用关键字in,而不再使用attribute
#if __VERSION__ >= 130 in vec3 position; in vec3 normal; in vec2 uv; in vec2 uv2; #else attribute vec3 position; attribute vec3 normal; attribute vec2 uv; attribute vec2 uv2; #endif
顶点输入变量可以使用布局限定符来修饰
layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal;
location的值由void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)函数的第一个参数index来指定
统一变量(uniform变量)
是只读的(只是在shader中不能修改,但可在cpu上修改,修改后再传入到shader中),与顶点和像素无关的输入型变量,被用来表示非逐顶点、非逐像素、各顶点、各像素共用的信息,必须声明为全局变量,可在vs和fs中使用。如:mvp矩阵
可以是除了数组或结构体之外的任意类型。如果vs和fs中声明了同名的统一变量(uniform变量),那么它就会被两种着色器共享。
vs代码:
#version 330 layout (location = 0) in vec3 Position; uniform mat4 gWVP; out vec4 Color; void main() { gl_Position = gWVP * vec4(Position, 1.0); Color = vec4(clamp(Position, 0.0, 1.0), 1.0); }
openGL代码:
GLuint ShaderProgram = glCreateProgram(); // ... ... GLuint Location = glGetUniformLocation(ShaderProgram, "gWVP"); // 获取shader中uniform mat4 gWVP变量的location if (Location != INVALID_UNIFORM_LOCATION) { float m[4][4] = { {0.0f, 1.0f, 0.5f, 0.2f}, {0.1f, 0.0f, 1.5f, 0.0f}, {0.0f, 1.2f, -0.5f, 0.1f}, {0.2f, 1.0f, 1.5f, 0.7f} };
glUseProgram(ShaderProgram); // 要先指定Program glUniformMatrix4fv(Location, 1, GL_TRUE, (const GLfloat*)m); // 设置uniform mat4 gWVP变量的值 }
统一变量块(uniform变量块)
OpenGL中使用统一变量缓冲区对象来支持统一变量块(uniform变量块) 数据的存储。
相比单一的统一变量(uniform变量),统一变量块(uniform变量块) 具有以下优势:
① 更高效 ② 不受默认大小限制,增加了统一变量的可用存储
vs代码:
// ... ... layout (std140) uniform LightBlock { vec3 lightDirection; vec4 lightPosition; }; // ... ...
openGL代码:
GLuint ShaderProgram = glCreateProgram(); // ... ... GLuint blockId, bufferId; GLint blockSize; GLuint bindingPoint = 1; GLfloat lightData[] = { // lightDirection (padding to vec4 based on std140 rule) 1.0f, 0.0f, 0.0f, 0.0f, // lightPosition 0.0f, 0.0f, 0.0f, 1.0f }; // Retrieve the uniform block index blockId = glGetUniformBlockIndex(ShaderProgram, "LightBlock"); // Associate the uniform block index with a binding point glUniformBlockBinding(ShaderProgram, blockId, bindingPoint); // Get the size of lightData // alternatively, we can calculate it using sizeof(lightData) in this example glGetActiveUniformBlockiv(ShaderProgram, blockId, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); // Create and fill a buffer object glGenBuffers(1, &bufferId); glBindBuffer(GL_UNIFORM_BUFFER, bufferId); glBufferData(GL_UNIFORM_BUFFER, blockSize, lightData, GL_DYNAMIC_DRAW); // 将lightData数组的数据绑定到名为统一变量块LightBlock上 // Bind the buffer object to the uniform block binding point glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, bufferId);
布局限定符
限定符 | 说明 |
shared(默认) | 多个着色器程序间共享。可省略不写 |
packed | 最小内存占用,无法在多个着色器程序间共享 |
std140 | 标准内存布局 |
column_major(默认) | 矩阵变量以列优先顺序。可省略不写 |
row_major | 矩阵变量以行优先顺序 |
① shared、packed和std140有着不同地内存对齐方式
② 布局限定符可用于统一的变量块,也可以用在统一变量块中某个单独的变量上 注:用在单独变量上时,对于该变量会覆盖掉全局对应的布局限定符
layout (shared, column_major) uniform TransformBlock { layout (std140, row_major) mat4 matViewProj; // matViewProj布局为std140行优先 layout (row_major) mat3 matNormal; // matNormal布局为shared行优先 mat3 matTexGen; // matViewProj布局为shared列优先 };
带资源句柄的Uniform
虽然Uniform不能在shader中修改(即资源句柄不能改),但资源句柄指向的Buffer和Texture是可以修改的
Buffer
基于协定的格式读的Buffer(Uniform Buffer)
可以随机读写的Buffer(Storage Buffer / UAV)
以某种pixel格式读的Buffer(Texture Buffer)
以某些pixel格式随机读写的Buffer(Storage Texture Buffer)
Texture
以某种pixel格式采样的Texture(Texture Sample)
以某些pixel格式随机读写的Texture(Storage Image)
顶点输出与片元输入变量(GLSL 1.3之前为varying变量)
必须声明为全局变量,其任务是从vs向fs传输数据。该数据对cpu是不可见的。
在#version 410和#version 300 es之前,在vs和fs中必须声明同名、同类型的该变量。注:当变量的类型和名字一样时,OpenGL就会把两个变量在链接到一起(在链接程序对象时完成的),这样它们之间就能发送数据了。
需要注意的是,vs中输出变量的值并不是直接传给fs的输入变量。
在进入fs之前,会进行光栅化,根据绘制的图形,对vs的输出变量进行插值(Interpolation),然后再传递给fs的v输入变量。 注:varying变量会被光栅化插值输出到fs中
该类型的变量只能是float或由float组合成的复合类型(vec2、vec3、vec4、mat2、mat3、mat4)
在#version 130及以上,顶点输出与片元输入变量使用关键字in,而不再使用varying
vs中顶点输出变量
#if __VERSION__ >= 130 out vec3 vPosition; out vec3 vNormal; out vec2 vUv; out vec2 vUv2; #else varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUv; varying vec2 vUv2; #endif
为了保证不变性,GLSL引入了invariant关键字来修饰顶点输出变量。
主要的原因是,为了优化,编译器可能对shader指令重新排序,可能导致2个着色器之间的等价计算不能保证产生完全相同的结果。特别是在进行多遍渲染时,尤其可能成为问题。
例如:fs中第一遍中计算镜面反射光,第二遍计算环境光和漫反射光,顶点输出变量如果不使用invariant关键字,精度带来的小误差会导致深度冲突(Z fighting)。
invariant关键字既可用于声明变量,也可以用于已经声明过的变量。
invariant vec2 texCoord; // 用于声明变量 invariant gl_Position; // 用于已经声明过的变量 #pragma STDGL invariant(all) // 让所有的变量全都不变 注:因为编译器需要保证不变性,所以会限制对着色器进行优化
fs中片元输入变量
#if __VERSION__ >= 130 in vec3 vPosition; in vec3 vNormal; in vec2 vUv; in vec2 vUv2; #else varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUv; varying vec2 vUv2; #endif
fs中片元输出变量
layout(location = 0) out vec4 FragColor; void main() { FragColor = vec4(1.0, 0.0, 0.0, 0.0); }
注1:在典型情况下,只会渲染一个颜色缓冲区,这个时候,layout(location = 0)布局限定符可忽略不写(默认输出变量会进入位置0)
注2:当渲染到多个渲染目标(MRT)时,可以使用布局限定符指定每个输出前往的渲染目标
在#version 410和#version 300 es之前,与顶点输入变量相比,顶点输出与片元输入变量不能使用layout布局限定符。
在#version 410和#version 300 es及之后,顶点输出与片元输入变量可使用layout布局限定符的location索引来建立关联,不需要两边的变量同名。更多请查看:Program separation linkage
vs中顶点输出变量
layout(location = 0) out vec4 color; layout(location = 1) out vec2 texCoord; layout(location = 2) out vec3 normal;
fs中片元输入变量
layout(location = 0) in vec4 diffuseAlbedo; layout(location = 1) in vec2 texCoord; layout(location = 2) in vec3 cameraSpaceNormal;
插值限定符来修饰顶点输出与片元输入变量(GLSL 1.3之前为varying变量)
插值限定符 | 说明 | 示例 |
smooth(缺省) |
平滑着色 |
/*** vs ***/ smooth out vec3 v_color; // smooth可省略不写 /*** fs ***/ smooth in vec3 v_color; // smooth可省略不写 |
flat | 平面着色 |
/*** vs ***/ flat out vec3 v_color; /*** fs ***/ flat in vec3 v_color; |
centroid |
质心采样(centroid sampling)
使用多重采样渲染时,centroid可用于强制插值发生在被渲染图元内部 这可防止图元的边缘出现伪像 |
/*** vs ***/ centroid out vec3 v_color; /*** fs ***/ centroid in vec3 v_color; |
变量总结
vs/fs中能容纳的顶点输入变量(attribute变量)、uniform变量、顶点输出与片元输入变量(varying变量)是储存在硬件中的,其最大数目与设备有关,具体详见下表:
变量类别 | 保存在硬件的哪个位置 | 是否会打包 | 内置全局变量(表示最大数量) | glGetintegerv查询 | OpenGL ES 3.0最小值 |
顶点输入变量(attribute变量) | No | const mediump int gl_MaxVertexAttribs | GL_MAX_VERTEX_ATTRIBS | 16 | |
vs:统一变量(uniform变量) | 常量存储:对应一个向量的物理数组 | Yes | const mediump int gl_MaxVertexUniformVectors | GL_MAX_VERTEX_UNIFORM_VECTORS | 256 |
fs:统一变量(uniform变量) | 常量存储:对应一个向量的物理数组 | Yes | const mediump int gl_MaxFragmentUniformVectors | GL_MAX_FRAGMENT_UNIFORM_VECTORS | 224 |
顶点输出变量 片元输入变量 (varying变量) |
插值器:对应一个向量的物理数组 |
Yes |
const mediump int gl_MaxVertexOutputVectors const mediump int gl_MaxFragmentInputVectors const mediump int gl_MaxVaryingVectors |
16 15 8(Open GL ES 2.0) |
注1:为了有效利用宝贵的硬件存储资源,OpenGL会对变量打包再进行存放。
注2:用uniform修饰的变量、const变量、字面值(如:在代码中写的0,1.0等)都会占用常量储存空间。
注:GLSL 1.3及以后(也就在OpenGL 3.0及以后,OpenGL ES 3.0及以后,WebGL 2.0及以后),开始使用in修饰顶点输入变量和片元输入变量,使用out修饰顶点输出变量
精度限定符
用来表示每种数据具有的精度(占用的字节数)。精度越高,计算结果误差就越小、效果也更好,但也意味着更大的内存、更多能耗和更久的计算时间。
使用精度限定符,可以精细地控制shader程序在效果和性能间的平衡。
精度限定符 | 描述 | float数值范围和精度 | int数值范围 |
highp(高精度) |
vs的最低精度
注:在某些webgl环境中,fs可能不支持highp精度 如果支持的话,会定义内置宏GL_FRAGMENT_PRECISION_HIGH |
范围:(-2^62~2^62) 精度:2^-16 |
(-2^16~2^16) |
mediump(中精度) |
fs的最低精度 |
范围:(-2^14~2^14) 精度:2^-10 |
(-2^10~2^10) |
lowp(低精度) | 可以表示所有的颜色 |
范围:(-2~2) 精度:2^-8 |
(-2^8~2^8) |
各精度限定符的数值范围和精度实际上与系统环境有关,可动态地使用gl.GetShaderPrecisionFormat()来获取
对单个变量
mediump float size; // 中精度的浮点型变量 highp vec4 position; // 具有高精度浮点型元素的vec4对象 lowp vec4 color; // 具有低精度浮点型元素的vec4对象
对整个文件
precision mediump float; // 所有float和由float组合成的复合类型(vec2、vec3、vec4、mat2、mat3、mat4)的数默认为中精度 precision highp int; // 所有int和由int组合成的复合类型的数默认为高精度
注:需要放在shader文件紧跟#version xxx的后面,其他语句之前
各数据类型的默认精度
shader类型 | 数据类型 | 默认精度 |
vs | int | highp |
float | highp | |
sampler2D | lowp | |
samplerCube | lowp | |
fs | int | mediump |
float |
无(需手动指定,否则会编译报错) |
|
sampler2D | lowp | |
samplerCube | lowp |
基础类型
bool b1 = true; int n1 = 10; int n2 = -25; int n3 = 0; //int n4 = 3.5; // 编译失败! 不能将float隐式地转换成int //uint u1 = 0; // 编译失败! 不能将int隐式地转换成uint uint u2 = 0u; uint u3 = 100u; float f1 = 2.0; float f2 = 3; float f3 = 0.2f; float f4 = n1; float f5 = float(n1); float f6 = u2; float f7 = float(u3); //float f8 = b1; // 编译失败! 不能将bool隐式地转换成float //float f9 = (float)b1;// 编译失败! 不能使用c类型的转换方式 float f9 = float(b1); int n4 = int(f3); //int n5 = f3; // 编译失败! 不能将float隐式地转换成int //int n6 = u3; // 编译失败! 不能将uint隐式地转换成int int n7 = int(u3); //uint u4 = n1; // 编译失败! 不能将int隐式地转换成uint uint u5 = uint(n2); //bool b2 = n1; // 编译失败! 不能将int隐式地转换成bool bool b3 = bool(n1); //bool b4 = u3; // 编译失败! 不能将uint隐式地转换成bool bool b5 = bool(u3); //bool b6 = f3; // 编译失败! 不能将float隐式地转换成bool bool b7 = bool(f3);
数组
只支持一维数组。
const float a1[3] = float[](0.25, 0.5, 1.0); float a2[2] = float[2](0.3, 0.8); float a4[3]; a4[0] = 0.1; a4[1] = 0.2; a4[2] = 0.3; int len = a4.length(); // len为3 获取数组a4的元素个数 const int N = 2; vec3 va1[N]; va1[0] = vec3(1.0, 0.0, 0.0); va1[1] = vec3(0.0, 0.0, 1.0); vec3 va2[2] = vec3[](vec3(1.0), vec3(0.2, 0.3, 0.5));
结构体(struct)
① 与C语言结构体一样,不可从其他结构体上派生
② 与C语言结构体一样,不允许有构造函数和析构函数
③ 可直接对2个结构体变量进行比较
#version 330 out vec4 FragColor; struct light // 与C语言一样,不允许从其他结构体上派生 { vec4 color; vec3 position; //light(){} // 与C语言一样,不允许有构造函数 //~light(){} // 与C语言一样,不允许有析构函数 void test() { color.x = 0.2; } } GL1; // 定义light结构体,并定义全局变量GL1 light GL2; // 定义light结构体的全局变量GL2 void main() { // light结构体的构造函数参数的顺序必须与结构体定义中的成员顺序一致 // 如下:参数一 vec4(1.0, 1.0, 0.0, 1.0)会赋值给vec4 color 参数二 vec3(10.0, 10.0, 0.0)会赋值给vec3 position GL1 = light(vec4(1.0, 1.0, 0.0, 1.0), vec3(10.0, 10.0, 0.0)); GL1.test(); GL2.color.r = 1.0; GL2.position = vec3(3.0, 4.0, 5.0); light L1, L2; // 定义light结构体的局部变量L1,L2 L1.color = vec4(0.0, 1.0, 0.0, 1.0); L2 = L1; if (L1 == L2) // true 结构体支持==运算符比较 { } L2.position.x = 100; if (L1 != L2) // true 结构体支持!=运算符比较 { } FragColor = GL1.color; }
向量
向量的分量可以为:
① {x,y,z,w} : 用来获取顶点坐标分量
② {r,g,b,a} : 用来获取颜色分量
③ {s,t,p,q} :用来获取纹理坐标分量
从向量中可以同时抽取多个分量,这个过程称作混合或重组(swizzling)。但要注意地是,以上三种不能相互混着使用,如:xyr、rgst、xrsw等
分量类型 |
2维 |
3维 |
4维 |
bool | bvec2 | bvec3 | bvec4 |
int | ivec2 | ivec3 | ivec4 |
uint | uvec2 | uvec3 | uvec4 |
float | vec2 | vec3 | vec4 |
vec2 v2a; // v2a为(0.0, 0.0) vec2 v2b = vec2(1.0f); // v2b为(1.0f, 1.0f) vec2 v2c = vec2(1.0, 0.0); // v2c为(1.0, 0.0) //vec3 v2d = v2c.xx; // 编译失败 incompatible types in initialization vec2 v2e; // v2e为(0.0, 0.0) v2e = v2c.yx; // v2e为(0.0, 1.0) vec2 v2f; // v2f为(0.0, 0.0) v2f = v2c.rr; // v2f为(1.0, 1.0) float f1 = v2c[0]; // f1为1.0 v2c[0]=v2c.x=v2c.r=v2c.s float f2 = v2c.y; // f1为0.0 v2c.y=v2c.g=v2c.t=v2c[1] // 向量运算 vec2 v2g = vec2(0.4, 0.6); vec2 v2h = vec2(0.1, 0.2); vec2 v2i = v2g + v2h; // v2i为(0.5, 0.8) vec2 v2j = v2g + 0.2; // v2j为(0.6, 0.8) vec2 v2k = v2h * 2; // v2k为(0.2, 0.4) vec2 v2m = v2g * v2h; // v2m为(0.04, 0.12) vec3 v3a = vec3(0.5); // v3a为(0.5, 0.5, 0.5) //vec3 v3b = vec3(1.0, 1.0); // 编译失败 too little data in type constructor vec3 v3c = vec3(1.0, 0.5, 0.25); // v3c为(1.0, 0.5, 0.25) //vec3 v3d = vec3(v2a); // 编译失败 cast not allowed vec3 v3e = vec3(v2a, 1.0); // v3e为(0.0, 0.0, 1.0) vec2 v2n = vec2(v3c); // 使用v3c的前2个元素 v2n为(1.0, 0.5) vec4 v4a = vec4(0.0, 0.3, 1.0, 0.5); // v4a为(0.0, 0.3, 1.0, 0.5) v4a.xw = vec2(0.2, 0.6); // v4a为(0.2, 0.3, 1.0, 0.6) v4a.gbr = vec3(0.0, 0.0, 1.0); // v4a为(1.0, 0.0, 0.0, 0.6) v4a.pq = vec2(1.0, 0.0); // v4a为(1.0, 0.0, 1.0, 0.0) vec4 v4b = vec4(v2c, v3c); // 先使用v2c来填充,如果没填满继续使用v3c来填充 v4b为(1.0, 0.0, 1.0, 0.5) vec4 v4c; v4c = vec4(0.0, 0.3, 1.0, 0.5).wwwx; // v4c为(0.5, 0.5, 0.5, 0.0)
矩阵
分量类型均为float,按照列优先顺序来存储(列主序)
矩阵类型 | 解释 |
mat2(mat2x2) | 由2个列向量vec2组成(可通过m[0]、m[1]来访问) |
mat2x3 | 由2个列向量vec3组成(可通过m[0]、m[1]来访问) |
mat2x4 | 由2个列向量vec4组成(可通过m[0]、m[1]来访问) |
mat3x2 | 由3个列向量vec2组成(可通过m[0]、m[1]、m[2]来访问) |
mat3(mat3x3) | 由3个列向量vec3组成(可通过m[0]、m[1]、m[2]来访问) |
mat3x4 | 由3个列向量vec4组成(可通过m[0]、m[1]、m[2]来访问) |
mat4x2 | 由4个列向量vec2组成(可通过m[0]、m[1]、m[2]、m[3]来访问) |
mat4x3 | 由4个列向量vec3组成(可通过m[0]、m[1]、m[2]、m[3]来访问) |
mat4(mat4x4) | 由4个列向量vec4组成(可通过m[0]、m[1]、m[2]、m[3]来访问) |
vec2 v2a = vec2(1.0, 0.5); // v2a为(1.0, 0.5) vec3 v3a = vec3(0.3, 0.2, 0.9); // v3a为(0.3, 0.2, 0.9) vec4 v4a = vec4(0.2, 0.3, 0.8, 0.0); // v4a为(0.2, 0.3, 0.8, 0.0) mat2 m2a; // m2a为(0.0, 0.0, 0.0, 0.0) mat2 m2b = mat2(1.0); // m2b为(1.0, 0.0, 0.0, 1.0) // | 1.0 0.0 | // | 0.0 1.0 | mat2 m2c = mat2(0.2, 0.5, 0.0, 0.8); // m2c为(0.2, 0.5, 0.0, 0.8) // | 0.2 0.0 | // | 0.5 0.8 | mat2 m2d = mat2(v2a, 0.1, 0.3); // m2d为(1.0, 0.5, 0.1, 0.3) mat2 m2e = mat2(0.6, v3a); // m2e为(0.6, 0.3, 0.2, 0.9) mat2 m2f = mat2(v2a, v3a); // 先使用v2a来填充,如果没填满继续使用v3a来填充 m2f为(1.0, 0.5, 0.3, 0.2) mat2 m2g = mat2(v4a); // m2g为(0.2, 0.3, 0.8, 0.0) // | 0.2 0.8 | // | 0.3 0.0 | mat2 m2h = mat2(v2a.yyxx); // m2g为(0.5, 0.5, 1.0, 1.0) // | 0.5 1.0 | // | 0.5 1.0 | mat2x3 m2x3a = mat2x3(v2a.xyx, v3a); // m2x3a为(1.0, 0.5, 1.0, 0.3, 0.2, 0.9) // | 1.0 0.3 | // | 0.5 0.2 | // | 1.0 0.9 | vec2 v2b = m2b[1]; // v2b为(0.0, 1.0) vec3 v3b = m2x3a[0]; // v3b为(1.0, 0.5, 1.0) int n = 1; vec3 v3c = m2x3a[n]; // v3c为(0.3, 0.2, 0.9) float f1 = m2c[1][0]; // f1为0.0 float f2 = m2c[0].y; // f2为0.5 float f3 = m2x3a[0].b; // f3为1.0 mat4 m4a = mat4(0.5); // m4a为(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5) // | 0.5 0.0 0.0 0.0 | // | 0.0 0.5 0.0 0.0 | // | 0.0 0.0 0.5 0.0 | // | 0.0 0.0 0.0 0.5 | // 矩阵与浮点数相加 mat4 m4b = m4a + 0.5; // m4b为(1.0, 0.5, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 0.5, 1.0) // | 1.0 0.5 0.5 0.5 | // | 0.5 1.0 0.5 0.5 | // | 0.5 0.5 1.0 0.5 | // | 0.5 0.5 0.5 1.0 | // 矩阵与浮点数相乘 mat4 m4c = m4a * 2; // m4c为(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0) // | 1.0 0.0 0.0 0.0 | // | 0.0 1.0 0.0 0.0 | // | 0.0 0.0 1.0 0.0 | // | 0.0 0.0 0.0 1.0 | mat4 m4d = mat4(1.0, 0.5, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 0.25, 0.25, 0.5, 0.5, 0.5, 0.5, 1.0); // | 1.0 0.5 0.5 0.5 | // | 0.5 1.0 0.25 0.5 | // | 0.5 0.5 0.25 0.5 | // | 0.5 0.5 0.5 1.0 | // 矩阵右乘向量 vec4 v4b = m4d * v4a; // v4b为(0.75, 0.6, 0.45, 0.65) 注:v4a被当成列向量 // v4b.x = m4d[0].x * v4a.x + m4d[1].x * v4a.y + m4d[2].x * v4a.z + m4d[3].x * v4a.w = 0.75 // v4b.y = m4d[0].y * v4a.x + m4d[1].y * v4a.y + m4d[2].y * v4a.z + m4d[3].y * v4a.w = 0.6 // v4b.z = m4d[0].z * v4a.x + m4d[1].z * v4a.y + m4d[2].z * v4a.z + m4d[3].z * v4a.w = 0.45 // v4b.w = m4d[0].w * v4a.x + m4d[1].w * v4a.y + m4d[2].w * v4a.z + m4d[3].w * v4a.w = 0.65 // 矩阵左乘向量 vec4 v4c = v4a * m4d; // v4c为(0.75, 0.8, 0.375, 0.65) 注:v4a被当成行向量 // v4c.x = v4a.x * m4d[0].x + v4a.y * m4d[0].y + v4a.z * m4d[0].z + v4a.w * m4d[0].w = 0.75 // v4c.y = v4a.x * m4d[1].x + v4a.y * m4d[1].y + v4a.z * m4d[1].z + v4a.w * m4d[1].w = 0.8 // v4c.z = v4a.x * m4d[2].x + v4a.y * m4d[2].y + v4a.z * m4d[2].z + v4a.w * m4d[2].w = 0.375 // v4c.w = v4a.x * m4d[3].x + v4a.y * m4d[3].y + v4a.z * m4d[3].z + v4a.w * m4d[3].w = 0.65 // 矩阵矩阵相乘 mat2 m2i = mat2(0.1, 0.0, 0.6, 0.2); // m2i为(0.1, 0.0, 0.6, 0.2) // | 0.1 0.6 | // | 0.0 0.2 | mat2 m2j = m2c * m2i; // m2j为(0.02, 0.05, 0.12, 0.46) // | m2c[0].x*m2i[0].x+m2c[1].x*m2i[0].y m2c[0].x*m2i[1].x+m2c[1].x*m2i[1].y | | 0.02 0.12 | // | m2c[0].y*m2i[0].x+m2c[1].y*m2i[0].y m2c[0].y*m2i[1].x+m2c[1].y*m2i[1].y | | 0.05 0.46 |
采样器(纹理)
只能是uniform变量,该变量从OpenGL中接受纹理单元编号,使得shader能通过该编号来访问纹理数据。
采样器(sampler)类型:
大类型 | 小类型 | 说明 | 示例 |
浮点采样器(不透明) | sampler1D | 1D纹理 | uniform sampler1D s; |
sampler2D | 2D纹理 | uniform sampler2D s; | |
sampler3D | 3D纹理 | uniform sampler3D s; | |
samplerCube | 立方图纹理 | uniform samplerCube s; | |
samplerCubeShadow | 带有对比的立方图深度纹理 | uniform samplerCubeShadow s; | |
sampler1DShadow | 带有对比的1D深度纹理 | uniform sampler1DShadow s; | |
sampler1DArray | 1D数组纹理 | uniform sampler1DArray s; | |
sampler1DArrayShadow | 带有对比的1D数组深度纹理 | uniform sampler1DArrayShadow s; | |
sampler2DShadow | 带有对比的2D深度纹理 | uniform sampler2DShadow s; | |
sampler2DArray | 2D数组纹理 | uniform sampler2DArray s; | |
sampler2DArrayShadow | 带有对比的2D数组深度纹理 | uniform sampler2DArrayShadow s; | |
有符号整数采样器(不透明) | isampler1D | 有符号整数1D纹理 | uniform isampler1D s; |
isampler2D | 有符号整数2D纹理 | uniform isampler2D s; | |
isampler3D | 有符号整数3D纹理 | uniform isampler3D s; | |
isamplerCube | 有符号整数立方图纹理 | uniform isamplerCube s; | |
isampler1DArray | 有符号整数1D数组纹理 | uniform isampler1DArray s; | |
isampler2DArray | 有符号整数2D数组纹理 | uniform isampler2DArray s; | |
无符号整数采样器(不透明) | usampler1D | 无符号整数1D纹理 | uniform usampler1D s; |
usampler2D | 无符号整数2D纹理 | uniform usampler2D s; | |
usampler3D | 无符号整数3D纹理 | uniform usampler3D s; | |
usamplerCube | 无符号整数立方图纹理 | uniform usamplerCube s; | |
usampler1DArray | 无符号整数1D数组纹理 | uniform usampler1DArray s; | |
usampler2DArray | 无符号整数2D数组纹理 | uniform usampler2DArray s; |
除了=、==和!=外,采样器变量不可以作为操作数参与运算
vs/fs中能使用采样器的个数,详见下表:
着色器类型 | 最大数量 | 最小数量 |
vs | gl_MaxVertexTextureImageUnits | 0 |
fs | gl_MaxTextureImageUnits | 8 |
示例代码如下:
1. 创建并填充数据到纹理对象
unsigned int TexID; glGenTextures(1, &TexID); //分配1个独一无二的id,并赋值给TexID glBindTexture(GL_TEXTURE_2D, TexID); // 把id为TexID绑定到GL_TEXTURE_2D类型的Buffer上 // 为当前绑定的纹理对象设置环绕、过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加载并生成纹理 int width, height, nrChannels; unsigned char *data = stbi_load("container.tga", &width, &height, &nrChannels, 0); if (data) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); // 拷贝data数据块(内存)到GL_TEXTURE_2D类型的Buffer(显存) glGenerateMipmap(GL_TEXTURE_2D); // 生成mipmap } stbi_image_free(data);
2. 创建并填充VBO
/****************** 创建并填充VBO ******************/ float vertices[] = { // ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 - 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下 -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下 -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上 }; unsigned int VBO; glGenBuffers(1, &VBO); //分配1个独一无二的id,并赋值给VBO glBindBuffer(GL_ARRAY_BUFFER, VBO); // 把id为VBO绑定到GL_ARRAY_BUFFER类型的Buffer上 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 拷贝vertices数组(内存)到GL_ARRAY_BUFFER类型的Buffer(显存)
3. 绑定VBO并传递顶点属性(Vertex Attribute)到vs着色器
/************ 使用VAO来记录顶点属性(Vertex Attribute)的上下文信息 ************/ unsigned int VAO; glGenVertexArrays(1, &VAO); //分配1个独一无二的id,并赋值给VAO glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); // 把id为VBO绑定到GL_ARRAY_BUFFER类型的Buffer上 // position顶点属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 启用location为0的顶点属性 注:顶点属性默认是禁用的 // color顶点属性 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3* sizeof(float))); glEnableVertexAttribArray(1); // 启用location为1的顶点属性 注:顶点属性默认是禁用的 // uv顶点属性 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2);
4. 编译并链接ShaderProgram,并设置名为ourTexture的Uniform变量
// 下面为Shader编译和Program链接的部分代码
unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glUseProgram(shaderProgram); glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0); // 将shader中名为ourTexture的Uniform变量,绑定到GL_TEXTRUE0纹理上
5. 顶点着色器代码:
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aTexCoord; out vec3 ourColor; out vec2 TexCoord; void main() { gl_Position = vec4(aPos, 1.0); ourColor = aColor; // 将颜色值传给片段着色器 TexCoord = aTexCoord; // 将纹理坐标传给片段着色器 }
6. 片段着色器代码:
#version 330 core out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; uniform sampler2D ourTexture; void main() { FragColor = texture(ourTexture, TexCoord); // 用TexCoord的uv坐标从贴图ourTexture上采样颜色值 }
7. 用纹理采样的方式来渲染对象
while (1) { // Sleep for a while glActiveTexture(GL_TEXTURE0); // 设置当前活动纹理为GL_TEXTURE0 注:OpenGL一般至少支持16张贴图:GL_TEXTURE0、GL_TEXTURE1、... 、GL_TEXTURE15 glBindTexture(GL_TEXTURE_2D, TexID); // 将TexID的纹理绑定到GL_TEXTURE0上 glUseProgram(shaderProgram); // 使用ID为shaderProgram的Shader glBindVertexArray(VAO); // Draw Something }
8. 游戏结束前释放相关资源
glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glDeleteTextures(1, &TexID);
函数
函数用法和C语言一样
vs和fs执行的入口函数均为void main() { }
函数参数限定词说明:
限定词 | 说明 |
in | 缺省限定词,可以省略不写 |
const | 当前参数为常量,不可在函数内被修改 |
out |
① 进入函数时,会被初始化为0, 0.0或false ② 在函数内修改后,对外可见 |
inout |
① 接受函数外传入的初始值 ② 在函数内修改后,对外可见 |
① 参数限定词只需要用在函数上,函数调用时不用带。
② 不允许函数递归 这一限制的原因是编译器会把函数都内联展开,以支持没有堆栈的GPU
float square(float value) // 求平方 { return value * value; } void fuc1(const float value) // const参数不能在函数内部修改 { //value = 0.2; // assignment to const variable value 编译错误 } void fuc2(out float value) // value进入函数时,会被重新初始化为0;在函数内修改,对外可见 { value += 0.5; } void fuc3(inout float value) // value的初始值从函数外传入;在函数内修改,对外可见 { value += 0.5; } int fun4(int n) // 如果在main函数中直接或间接调用fun4函数,会编译报错:recursive call to function { if (n == 0 || n == 1) { return 1; } return fun4(n-1) + fun4(n-2); }
内置函数
类别 | 内置函数 |
角度函数 |
radians //角度转弧度 degrees // 弧度转角度 |
三角函数 |
sin // 正弦 cos // 余弦 tan // 正切 asin // 反正弦 acos // 反余弦 atan // 反正切 |
指数函数 |
pow // x^y exp // 自然指数 log // 自然对数 exp2 // 2^x log2 // 以2为底对数 sqrt // 开平方 inversesqrt // 开平方倒数 |
通用函数 |
abs // 绝对值 min // 最小值 max // 最大值 mod // 取余数 sign // 取正负号 floor // 向下取整 ceil // 向上取整 clamp // 限定范围 mix // 线性内插 step // 步进函数 smoothstep // 艾米内插步进 fract // 获取小数部分 |
几何函数 |
length // 矢量长度 distance // 两点间距离 dot // 内积 cross // 外积 normalize // 归一化 reflect // 矢量反射 faceforward // 使矢量“朝前” |
矩阵函数 | matrixCmpMult // 逐元素乘法 |
矢量函数 |
lessThan // 逐元素小于 lessThanEqual // 逐元素小于等于 greaterThan // 逐元素大于 greaterThanEqual // 逐元素大于等于 equal // 逐元素相等 notEqual // 逐元素不等 any // 任一元素为true则为true all // 所有元素为true则为true not // 逐元素取补 |
纹理函数 |
textureSize // 获取某级lod纹理的宽高 如:vec2 textureSize(sampler2D sampler, int lod)
texelFetch // 取得单一texel的颜色值 如:vec4 texelFetch(sampler2D sampler, ivec2 P, int lod)
texelFetchOffset // 带Offset取得单一texel的颜色值 如:vec4 texelFetchOffset(sampler2D sampler, ivec2 P, int lod, ivec2 offset)
// 设置的过滤采样算法,会影响从texture*函数中得到的颜色值
texture // 从纹理上读取颜色值(P为指定的uv坐标,bias为mip的offset)() 如:vec4 texture(sampler2D sampler, vec2 P [, float bias]) textureProj // 用投影方式从纹理上读取颜色值 如:vec4 textureProj(sampler2D sampler, vec3 P [, float bias]) textureLod // 指定lod从纹理上读取颜色值 如:vec4 textureLod(sampler2D sampler, vec2 P, float lod) textureOffset // 带Offset从纹理上读取颜色值 如:vec4 textureOffset(sampler2D sampler, vec2 P, ivec2 offset [, float bias]) textureProjOffset // 用投影方式带Offset从纹理上读取颜色值 如:vec4 textureProjOffset(sampler2D sampler, vec3 P, ivec2 offset [, float bias]) textureLodOffset // 指定lod带Offset从纹理上读取颜色值 如:vec4 textureLodOffset(sampler2D sampler, vec2 P, float lod, ivec2 offset) textureProjLod // 指定lod用投影方式从纹理上读取颜色值 如:vec4 textureProjLod(sampler2D sampler, vec3 P, float lod) textureProjLodOffset // 指定lod用投影方式带Offset从纹理上读取颜色值 如:vec4 textureProjLodOffset(sampler2D sampler, vec3 P, float lod, ivec2 offset) textureGrad // 指定梯度从纹理上读取颜色值 如:vec4 textureGrad(sampler2D sampler, vec2 P, vec2 dPdx, vec2 dPdy) textureGradOffset // 指定梯度带Offset从纹理上读取颜色值 如:vec4 textureGradOffset(sampler2D sampler, vec2 P, vec2 dPdx, vec2 dPdy, ivec2 offset) textureProjGrad // 指定梯度用投影方式从纹理上读取颜色值 如:vec4 textureProjGrad(sampler2D sampler, vec3 P, vec2 dPdx, vec2 dPdy) textureProjGradOffset // 指定梯度带Offset用投影方式从纹理上读取颜色值 如:vec4 textureProjGradOffset(sampler2D sampler, vec3 P, vec2 dPdx, vec2 dPdy, vec2 offset) |
限定符顺序规则
一般变量中:不变性(invariant ) > 插值(smooth、flat) > 存储(const、in、out、uniform、centroid in、centroid out) > 精度(highp、mediump、lowp)
参数变量中:存储(const、in、out、uniform、centroid in、centroid out) > 参数(in、out、inout) > 精度(highp、mediump、lowp)
帮助手册
OpenGL ES 1.1 API(含GLSL内置函数)帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/
OpenGL ES 2.0 API(含GLSL内置函数)帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/es2.0/
OpenGL ES 3.0 API(含GLSL内置函数)帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/es3.0/
OpenGL ES 3.1 API(含GLSL内置函数)帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/es3.1/
OpenGL ES 3.2 API(含GLSL内置函数)帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/es3/
OpenGL 2.1, GLX, and GLU帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/
OpenGL 4.5 API(含GLSL内置函数)帮助手册:https://www.khronos.org/registry/OpenGL-Refpages/gl4/
Khronos OpenGL and OpenGL ES 参考页:https://www.khronos.org/registry/OpenGL-Refpages/
WebGL 1.0 API帮助手册:https://www.khronos.org/registry/webgl/specs/latest/1.0/
WebGL 2.0 API帮助手册:https://www.khronos.org/registry/webgl/specs/latest/2.0/
参考卡片
OpenGL ES 2.0(含API和GLSL)参考卡片:https://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf
OpenGL ES 3.0(含API和GLSL)参考卡片:https://www.khronos.org/files/opengles3-quick-reference-card.pdf
OpenGL ES 3.1(含API和GLSL)参考卡片:https://www.khronos.org/files/opengles31-quick-reference-card.pdf
OpenGL ES 3.2(含API和GLSL)参考卡片:https://www.khronos.org/files/opengles32-quick-reference-card.pdf
OpenGL 3.2(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl-quick-reference-card.pdf
OpenGL 4.1(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl41-quick-reference-card.pdf
OpenGL 4.2(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl42-quick-reference-card.pdf
OpenGL 4.3(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl43-quick-reference-card.pdf
OpenGL 4.4(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl44-quick-reference-card.pdf
OpenGL 4.5(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl45-quick-reference-card.pdf
OpenGL 4.6(含API和GLSL)参考卡片:https://www.khronos.org/files/opengl46-quick-reference-card.pdf
WebGL 1.0(含API和GLSL)参考卡片:https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf
WebGL 2.0(含API和GLSL)参考卡片:https://www.khronos.org/files/webgl20-reference-guide.pdf
Vulkan 1.0(含API)参考卡片:https://www.khronos.org/files/vulkan10-reference-guide.pdf
Vulkan 1.1(含API)参考卡片:https://www.khronos.org/files/vulkan11-reference-guide.pdf
Vulkan各版本specs:https://registry.khronos.org/vulkan/specs/
Vulkan 1.0 Core API + all published Extensions:https://registry.khronos.org/vulkan/specs/1.0-extensions/html/
Vulkan 1.1 Core API + all published Extensions:https://registry.khronos.org/vulkan/specs/1.1-extensions/html/
Vulkan 1.2 Core API + all published Extensions:https://registry.khronos.org/vulkan/specs/1.2-extensions/html/index.html
Vulkan 1.3 Core API + all published Extensions:https://registry.khronos.org/vulkan/specs/1.3-extensions/html/index.html