GLSL

类型说明

空类型,即不返回任何值

布尔类型 true,false

带符号的整数 signed integer

带符号的浮点数 floating scalar

n维浮点数向量 n-component floating point vector

n维布尔向量 Boolean vector

n维整数向量 signed integer vector

2×2, 3×3, 4×4 浮点数矩阵 float matrix

2D纹理 a 2D texture

盒纹理 cube mapped texture

类型说明 结构 struct type-name{} 类似c语言中的 结构体 数组 float foo[3] glsl只支持1维数组,数组可以是结构体的成员

glsl中的向量(vec2,vec3,vec4)往往有特殊的含义,比如可能代表了一个空间坐标(x,y,z,w),或者代表了一个颜色(r,g,b,a),再或者代表一个纹理坐标(s,t,p,q) 所以glsl提供了一些更人性化的分量访问方式.

vector.xyzw 其中xyzw 可以任意组合

vector.rgba 其中rgba 可以任意组合

vector.stpq 其中rgba 可以任意组合

优先级(越小越高)运算符说明结合性 1

聚组:a*(b+c) N/A 2

数组下标__[]

,属性访问__a.b__,自增/减后缀__a++ a–__ L – R 3

自增/减前缀__++a –a__,正负号(一般正号不写)

,取反__!false__ R – L 4

乘除数学运算 L – R 5

加减数学运算 L – R 7

关系运算符 L – R 8

相等性运算符 L – R 12

逻辑与 L – R 13

逻辑排他或(用处基本等于!=) L – R 14

逻辑或 L – R 15

三目运算符 L – R 16

赋值与复合赋值 L – R 17

顺序分配运算 L – R

ps 左值与右值:

glsl中,没有隐式类型转换,原则上glsl要求任何表达式左右两侧(l-value),(r-value)的类型必须一致 也就是说以下表达式都是错误的:

下面来分别说说可能遇到的情况:

1. floatint:

float与float , int与int之间是可以直接运算的,但float与int不行.它们需要进行一次显示转换.即要么把float转成int: int(1.0) ,要么把int转成float: float(1) ,以下表达式都是正确的:

2. floatvec(向量) mat(矩阵):

vec,mat这些类型其实是由float复合而成的,当它们与float运算时,其实就是在每一个分量上分别与float进行运算,这就是所谓的 逐分量运算.glsl里 大部分涉及vec,mat的运算都是 逐分量运算,但也并不全是. 下文中就会讲到特例.

逐分量运算是线性的,这就是说 vec 与 float 的运算结果是还是 vec.

int 与 vec,mat之间是不可运算的, 因为vec和mat中的每一个分量都是 float 类型的. 无法与int进行逐分量计算.

下面枚举了几种 float 与 vec,mat 运算的情况

3. vec(向量)vec(向量):

两向量间的运算首先要保证操作数的阶数都相同.否则不能计算.例如: vec3*vec2 vec4+vec3 等等都是不行的.

它们的计算方式是两操作数在同位置上的分量分别进行运算,其本质还是逐分量进行的,这和上面所说的float类型的 逐分量运算可能有一点点差异,相同的是 vec 与 vec 运算结果还是 vec, 且阶数不变.

3. vec(向量)mat(矩阵):

要保证操作数的阶数相同,且vec与mat间只存在乘法运算.

它们的计算方式和线性代数中的矩阵乘法相同,不是逐分量运算.

向量与矩阵的乘法规则如下:

4. mat(矩阵)mat(矩阵):

要保证操作数的阶数相同.

在mat与mat的运算中, 除了乘法是线性代数中的矩阵乘法外.其余的运算任为逐分量运算.简单说就是只有乘法是特殊的,其余都和vec与vec运算类似.

矩阵乘法规则如下:

修饰符说明

(默认的可省略)本地变量,可读可写,函数的输入参数既是这种类型

声明变量或函数的参数为只读类型

只能存在于vertex shader中,一般用于保存顶点或法线数据,它可以在数据缓冲区中读取数据

在运行时shader无法改变uniform变量, 一般用来放置程序传递给shader的变换矩阵,材质,光照参数等等.

主要负责在vertex 和 fragment 之间传递变量

const:

和C语言类似,被const限定符修饰的变量初始化后不可变,除了局部变量,函数参数也可以使用const修饰符.但要注意的是结构变量可以用const修饰, 但结构中的字段不行.

const变量必须在声明时就初始化 const vec3 v3 = vec3(0.,0.,0.)

局部变量只能使用const限定符.

函数参数只能使用const限定符.

attribute:

attribute变量是 全局只读的,它只能在vertex shader中使用,只能与浮点数,向量或矩阵变量组合, 一般attribute变量用来放置程序传递来的模型顶点,法线,颜色,纹理等数据它可以访问数据缓冲区 (还记得__gl.vertexAttribPointer__这个函数吧)

uniform:

uniform变量是 全局只读的,在整个shader执行完毕前其值不会改变,他可以和任意基本类型变量组合, 一般我们使用uniform变量来放置外部程序传递来的环境数据(如点光源位置,模型的变换矩阵等等) 这些数据在运行中显然是不需要被改变的.

varying:

varying类型变量是 vertex shader 与 fragment shader 之间的信使,一般我们在 vertex shader 中修改它然后在fragment shader使用它,但不能在 fragment shader中修改它.

要注意全局变量限制符只能为 const、attribute、uniform和varying中的一个.不可复合.

函数的参数默认是以拷贝的形式传递的,也就是值传递,任何传递给函数参数的变量,其值都会被复制一份,然后再交给函数内部进行处理. 我们可以为参数添加限定符来达到传递引用的目的,glsl中提供的参数限定符如下:

限定符说明 < none: default > 默认使用 in 限定符 in 复制到函数中在函数中可读写 out 返回时从函数中复制出来 inout 复制到函数中并在返回时复制出来

in 是函数参数的默认限定符,最终真正传入函数形参的其实是实参的一份拷贝.在函数中,修改in修饰的形参不会影响到实参变量本身.

out 它的作用是向函数外部传递新值,out模式下传递进来的参数是write-only的(可写不可读).就像是一个”坑位”,坑位中的值需要函数给他赋予. 在函数中,修改out修饰的形参会影响到实参本身.

inout inout下,形参可以被理解为是一个带值的”坑位”,及可读也可写,在函数中,修改inout修饰的形参会影响到实参本身.

glsl允许在程序的最外部声明函数.函数不能嵌套,不能递归调用,且必须声明返回值类型(无返回值时声明为void) 在其他方面glsl函数与c函数非常类似.

glsl中变量可以在声明的时候初始化, float pSize = 10.0 也可以先声明然后等需要的时候在进行赋值.

聚合类型对象如(向量,矩阵,数组,结构) 需要使用其构造函数来进行初始化. vec4 color = vec4(0.0, 1.0, 0.0, 1.0);

glsl可以使用构造函数进行显式类型转换,各值如下:

glsl在进行光栅化着色的时候,会产生大量的浮点数运算,这些运算可能是当前设备所不能承受的,所以glsl提供了3种浮点数精度,我们可以根据不同的设备来使用合适的精度.

在变量前面加上 highp mediump lowp 即可完成对该变量的精度声明.

我们一般在片元着色器(fragment shader)最开始的地方加上 precision mediump float; 便设定了默认的精度.这样所有没有显式表明精度的变量 都会按照设定好的默认精度来处理.

如何确定精度:

变量的精度首先是由精度限定符决定的,如果没有精度限定符,则要寻找其右侧表达式中,已经确定精度的变量,一旦找到,那么整个表达式都将在该精度下运行.如果找到多个, 则选择精度较高的那种,如果一个都找不到,则使用默认或更大的精度类型.

invariant关键字:

由于shader在编译时会进行一些内部优化,可能会导致同样的运算在不同shader里结果不一定精确相等.这会引起一些问题,尤其是vertx shader向fragmeng shader传值的时候. 所以我们需要使用 invariant 关键字来显式要求计算结果必须精确一致. 当然我们也可使用 #pragma STDGL invariant(all)来命令所有输出变量必须精确一致, 但这样会限制编译器优化程度,降低性能.

限定符的顺序:

当需要用到多个限定符的时候要遵循以下顺序:

1.在一般变量中: invariant > storage > precision

2.在参数中: storage > parameter > precision

我们来举例说明:

以 # 开头的是预编译指令,常用的有:

比如 #version 100 他的意思是规定当前shader使用 GLSL ES 1.00标准进行编译,如果使用这条预编译指令,则他必须出现在程序的最开始位置.

内置的宏:

__LINE__ : 当前源码中的行号.

__VERSION__ : 一个整数,指示当前的glsl版本 比如 100 ps: 100 = v1.00

GL_ES : 如果当前是在 OPGL ES 环境中运行则 GL_ES 被设置成1,一般用来检查当前环境是不是 OPENGL ES.

GL_FRAGMENT_PRECISION_HIGH : 如果当前系统glsl的片元着色器支持高浮点精度,则设置为1.一般用于检查着色器精度.

实例:

1.如何通过判断系统环境,来选择合适的精度:

2.自定义宏:

glsl程序使用一些特殊的内置变量与硬件进行沟通.他们大致分成两种 一种是 input类型,他负责向硬件(渲染管线)发送数据. 另一种是 output类型,负责向程序回传数据,以便编程时需要.

在 vertex Shader 中:

output 类型的内置变量:

变量说明单位 highp vec4

; gl_Position 放置顶点坐标信息 vec4 mediump float

; gl_PointSize 需要绘制点的大小,(只在gl.POINTS模式下有效) float

在 fragment Shader 中:

input 类型的内置变量:

变量说明单位 mediump vec4

; 片元在framebuffer画面的相对位置 vec4 bool

; 标志当前图元是不是正面图元的一部分 bool mediump vec2

; 经过插值计算后的纹理坐标,点的范围是0.0到1.0 vec2

output 类型的内置变量:

变量说明单位 mediump vec4

; 设置当前片点的颜色 vec4 RGBA color mediump vec4

设置当前片点的颜色,使用glDrawBuffers数据数组 vec4 RGBA color

glsl提供了一些内置的常量,用来说明当前系统的一些特性. 有时我们需要针对这些特性,对shader程序进行优化,让程序兼容度更好.

在 vertex Shader 中:

1.const mediump int gl_MaxVertexAttribs>=8

gl_MaxVertexAttribs 表示在vertex shader(顶点着色器)中可用的最大attributes数.这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 不过最低不能小于 8 个.

2.const mediump int gl_MaxVertexUniformVectors >= 128

gl_MaxVertexUniformVectors 表示在vertex shader(顶点着色器)中可用的最大uniform vectors数. 这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 不过最低不能小于 128 个.

3.const mediump int gl_MaxVaryingVectors >= 8

gl_MaxVaryingVectors 表示在vertex shader(顶点着色器)中可用的最大varying vectors数. 这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 不过最低不能小于 8 个.

4.const mediump int gl_MaxVertexTextureImageUnits >= 0

gl_MaxVaryingVectors 表示在vertex shader(顶点着色器)中可用的最大纹理单元数(贴图). 这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 甚至可以一个都没有(无法获取顶点纹理)

5.const mediump int gl_MaxCombinedTextureImageUnits >= 8

gl_MaxVaryingVectors 表示在 vertex Shader和fragment Shader总共最多支持多少个纹理单元. 这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 不过最低不能小于 8 个.

在 fragment Shader 中:

1.const mediump int gl_MaxTextureImageUnits >= 8

gl_MaxVaryingVectors 表示在 fragment Shader(片元着色器)中能访问的最大纹理单元数,这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 不过最低不能小于 8 个.

2.const mediump int gl_MaxFragmentUniformVectors >= 16

gl_MaxFragmentUniformVectors 表示在 fragment Shader(片元着色器)中可用的最大uniform vectors数,这个值的大小取决于 OpenGL ES 在某设备上的具体实现, 不过最低不能小于 16 个.

3.const mediump int gl_MaxDrawBuffers = 1

gl_MaxDrawBuffers 表示可用的drawBuffers数,在OpenGL ES 2.0中这个值为1, 在将来的版本可能会有所变化.

glsl中还有一种内置的uniform状态变量, gl_DepthRange 它用来表明全局深度范围.

结构如下:

除了 gl_DepthRange 外的所有uniform状态常量都已在glsl 1.30 中 &#x5E9F;&#x5F03;.

glsl的流控制和c语言非常相似,这里不必再做过多说明,唯一不同的是片段着色器中有一种特殊的控制流 discard. 使用discard会退出片段着色器,不执行后面的片段着色操作。片段也不会写入帧缓冲区。

glsl提供了非常丰富的函数库,供我们使用,这些功能都是非常有用且会经常用到的. 这些函数按功能区分大改可以分成7类:

通用函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法说明 T abs(T x) 返回x的绝对值 T sign(T x) 比较x与0的值,大于,等于,小于 分别返回 1.0 ,0.0,-1.0 T floor(T x) 返回

T mod(T x, float y) 取x,y的余数 T min(T x, T y)

T min(T x, float y) 取x,y的最小值 T max(T x, T y)

T max(T x, float y) 取x,y的最大值 T clamp(T x, T minVal, T maxVal)

T clamp(T x, float minVal,float maxVal) min(max(x, minVal), maxVal),返回值被限定在 minVal,maxVal之间 T mix(T x, T y, T a)

T mix(T x, T y, float a) 取x,y的线性混合,x(1-a)+ya T step(T edge, T x)

T step(float edge, T x) 如果 x

T smoothstep(float edge0,float edge1, T x) 如果x

角度&三角函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法说明 T radians(T degrees) 角度转弧度 T degrees(T radians) 弧度转角度 T sin(T angle) 正弦函数,角度是弧度 T cos(T angle) 余弦函数,角度是弧度 T tan(T angle) 正切函数,角度是弧度 T asin(T x) 反正弦函数,返回值是弧度 T acos(T x) 反余弦函数,返回值是弧度 T atan(T y, T x)

T atan(T y_over_x) 反正切函数,返回值是弧度

指数函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法说明 T pow(T x, T y) 返回x的y次幂 xy T exp(T x) 返回x的自然指数幂 ex T log(T x) 返回x的自然对数 ln T exp2(T x) 返回2的x次幂 2x T log2(T x) 返回2为底的对数 log2 T sqrt(T x) 开根号 √x T inversesqrt(T x) 先开根号,在取倒数,就是 1/√x

几何函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法说明 float length(T x) 返回矢量x的长度 float distance(T p0, T p1) 返回p0 p1两点的距离 float dot(T x, T y) 返回x y的点积 vec3 cross(vec3 x, vec3 y) 返回x y的叉积 T normalize(T x) 对x进行归一化,保持向量方向不变但长度变为1 T faceforward(T N, T I, T Nref) 根据 矢量 N 与Nref 调整法向量 T reflect(T I, T N) 返回 I – 2 * dot(N,I) * N, 结果是入射矢量 I 关于法向量N的 镜面反射矢量 T refract(T I, T N, float eta) 返回入射矢量I关于法向量N的折射矢量,折射率为eta

矩阵函数:

mat可以为任意类型矩阵.

方法说明 mat matrixCompMult(mat x, mat y) 将矩阵 x 和 y的元素逐分量相乘

向量函数:

下文中的 类型 T可以是 vec2, vec3, vec4, 且可以逐分量操作.

bvec指的是由bool类型组成的一个向量:

方法说明 bvec lessThan(T x, T y) 逐分量比较x < y,将结果写入bvec对应位置 bvec lessThanEqual(T x, T y) 逐分量比较 x

bvec equal(bvec x, bvec y) 逐分量比较 x == y,将结果写入bvec对应位置 bvec notEqual(T x, T y)

bvec notEqual(bvec x, bvec y) 逐分量比较 x!= y,将结果写入bvec对应位置 bool any(bvec x) 如果x的任意一个分量是true,则结果为true bool all(bvec x) 如果x的所有分量是true,则结果为true bvec not(bvec x) bool矢量的逐分量取反

纹理查询函数:

图像纹理有两种 一种是平面2d纹理,另一种是盒纹理,针对不同的纹理类型有不同访问方法.

纹理查询的最终目的是从sampler中提取指定坐标的颜色信息. 函数中带有Cube字样的是指 需要传入盒状纹理. 带有Proj字样的是指带投影的版本.

以下函数只在vertex shader中可用:

以下函数只在fragment shader中可用:

在 vertex shader 与 fragment shader 中都可用:

下面的shader如果你可以一眼看懂,说明你已经对glsl语言基本掌握了.

Vertex Shader:

Fragment Shader:

Original: https://www.cnblogs.com/zhoug2020/p/16397382.html
Author: 莫水千流
Title: GLSL

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/554564/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java Script 循环,数组,对象,判断,阶乘,查找-综合运用合集

    输出100个hello world. for (var i = 1; i console.log("hello world");} 创建一个包含1~100的数组…

    技术杂谈 2023年6月21日
    093
  • RedisSCAN命令

    获取指定前缀的key 需求描述: Redis中有大量以xxx开头的key,在不使用keys命令的情况下,如何快速获取这些前缀的key 解决方案: redis自带的scan命令可以解…

    技术杂谈 2023年7月24日
    064
  • switchyomega的使用

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/welhzh/p/16387637.htmlAuthor…

    技术杂谈 2023年6月1日
    0102
  • SQL自定义函数

    存储函数和存储过程统称为存储例程(store routine),存储函数的限制比较多,例如不能用临时表,只能用表变量,而存储过程的限制较少,存储过程的实现功能要复杂些,而函数的实现…

    技术杂谈 2023年7月25日
    055
  • Python列表和元组知识点

    list.pop()方法:默认删除列表中最后一个元素,也可按照索引位置删除指定元素,并将删除的元素返回。 li = [23, 4, ‘ab’, True] print(li.pop…

    技术杂谈 2023年6月21日
    084
  • 七夕专属限定:1 分钟 Serverless 极速抽盲盒

    体验简介 在阿里云云起实验中,通过Serverless架构部署一个抽奖系统,当前,Serverless在移动应用、游戏等场景已经实现规模化应用,Serverless 技术可以更好的…

    技术杂谈 2023年7月11日
    072
  • 监控平台SkyWalking9入门实践

    简便快速的完成对分布式系统的监控; 一、业务背景 微服务作为当前系统架构的主流选型,虽然可以应对复杂的业务场景,但是随着业务扩展,微服务架构本身的复杂度也会膨胀,对于一些核心的业务…

    技术杂谈 2023年7月23日
    086
  • JCL 日志门面

    JCL( Jakarta Commons Logging ),是 Apache 提供的一个 通用日志 API 。用户可以自由选择第三方的日志组件作为具体实现,像 Log4j 或 J…

    技术杂谈 2023年7月11日
    077
  • 树状数组

    简述 什么是树状数组呢,顾名思义就是树一样的数组,本质就是用数组模拟树形结构。 树状数组有什么用呢,树状数组可以实现单点更新,单点查询,区间查询和区间更新,维护的东西和线段树可以类…

    技术杂谈 2023年7月11日
    084
  • Linux学习笔记(一)初识Linux

    初始Linux Linux可划分为以下四部分: Linux内核 GNU工具 图形化桌面环境 应用软件 每一部分在Linux系统中各司其职,下图是各部分对应关系: 1、Linux内核…

    技术杂谈 2023年7月11日
    073
  • Mac系统下Datagrip打不开、点击没反应?

    有没有可能是因为你从网上下载了一些破解软件导致的? Mac系统下JB公司家的IDEA、 Datagrip、PyCharm 或 Goland 打不开点击没反应…&#823…

    技术杂谈 2023年7月24日
    081
  • Java实现飞机大战游戏

    飞机大战详细文档 文末有源代码,以及本游戏使用的所有素材,将plane2文件复制在src文件下可以直接运行。 实现效果: 结构设计 角色设计 飞行对象类 FlyObject 战机类…

    技术杂谈 2023年7月24日
    077
  • 在vue中使用websocket回调函数中调用其他methods函数报错Uncaught TypeError: this.getHistory is not a function at WebSocket.ws.onmess解决方案

    1、报错详情 在vue中,使用了websocket类,在websocket的onmessage回调函数中调用methods的 getHistory 函数,发现一调用就报错 Unca…

    技术杂谈 2023年7月11日
    071
  • 目标检测常用库MMCV安装中遇到的问题总结

    我看很多教程说mmcv是只支持linux,支持windows版本较少,所以很难和torch、cuda匹配上,所以报错较多难安装。今天试了一下,遇到的和想到的报错问题基本如下: 一、…

    技术杂谈 2023年7月25日
    079
  • 论游戏的可玩性

    论游戏的可玩性 为啥在技术博客写关于游戏的? 肯定读到这篇随笔的时候大家会问这个问题 主要是主观觉得IT人对游戏的理解会更加深刻,玩的也多,认识的很多IT朋友玩游戏也会比较深,所以…

    技术杂谈 2023年6月21日
    086
  • 古传拳经拳法秘要

    《古传拳经拳法秘要》(手抄本) 国术馆 2022-05-05 18:33 更多珍贵拳谱资料 关注公众号”老拳谱” 勿使前辈之遗珍失于我手 点击图片了解与报名…

    技术杂谈 2023年5月31日
    075
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球