GLSL学习笔记
设计理念
WebGL只关心两件事:裁剪空间中的坐标值和颜色值。使用WebGL只需要给它提供这两个东西。 你需要提供两个着色器来做这两件事:
- 顶点着色器提供裁剪空间坐标值
- 片断着色器提供颜色值。
无论你的画布有多大,裁剪空间的坐标范围永远是 -1 到 1 。
三种变量
属性:attribute
缓冲是发送到GPU的一些二进制数据序列,通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等。 你可以存储任何数据。
属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器。
// attribute声明顶点位置变量
attribute vec4 position;
// attribute声明顶点颜色变量
attribute vec4 a_color;
// attribute声明顶点法向量
attribute vec4 normal;
// 与顶点相关的浮点数
attribute float scale;
在Three.js中,attribute已经被预定义了,因此在着色器代码中,直接使用即可,无需再次定义:
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main() {
vec3 vNormal = normalize( normalMatrix * normal);
vec3 vNormel = normalize( normalMatrix * viewVector);
intensity = pow( c - dot(vNormal, vNormel), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0);
}
全局变量:uniform
全局变量在着色程序运行前赋值,在运行过程中全局有效。
uniform vec3 viewVector;
uniform float c;
uniform float p;
可变量:varying
可变量是一种顶点着色器给片断着色器传值的方式。
比如我们会在顶点着色器中计算光强度,然后通过varying类型的变量,传递给片元着色器:
varying float intensity;
坐标转换
https://blog.csdn.net/aoxuestudy/article/details/109923749

光照计算
摘自羡澈的文章:
将光源位置(这是通过参数传入的)乘以模型视图矩阵(这是着色器提供给我们的),就能得到光源相对于摄像机的位置;
将这一位置归一化(就是计算单位向量),得到光源相对于摄像机的角度;
将其点乘单位法向量,得到亮度值(回想一下,两个向量点乘的意思是,获得一个向量在另一个向量上的投影,所以,这里得到的是光源在法向量方向的投影长度,它决定了一个点有多亮)。
基本语法
常用内置变量
instanceStart
instanceEnd
position
理解glsl语言的gl_FragCoord与u_resolution_杨大大28的博客-CSDN博客
常用内置函数
-
normalize: 向量归一化
-
mix:插值
-
dot:计算两个向量的点积(点积是啥?有何用处?用得还挺多的)
点积的主要作用是可以用来计算角度;点积的结果是一个数,不是向量
- mod:计算一个参数对另一个参数取模的值
函数
in、out等限定词:
https://blog.51cto.com/u_14459992/3312958
命名规范
类型作为前缀,比如
varying float vLineDistance;
调试方法
将调试信息以颜色形式输出在屏幕上,自行判断对错:
https://stackoverflow.com/questions/15764785/debug-glsl-code-in-webgl
绘制流程
Fragment Shader的输入是在什么空间? Vertex Shader的输出在Clip Space,那Fragment Shader的输入在什么空间?不是NDC,而是屏幕空间Screen Space。 我们前面说到Vertex Shader的输出在Clip Space,接着GPU会做透视除法变到NDC。这之后GPU还有一步,应用视口变换,转换到Screen Space,输入给Fragment Shader:
(Vertex Shader) => Clip Space => (透视除法) => NDC => (视口变换) => Screen Space => (Fragment Shader)
绘制线条
Three中绘制线,是采用【instance】方式来绘制的,每个线段作为一个instance进行绘制,每个instance通过着色器来生成虚线效果。
基础概念
剪裁空间(Clip Space)和标准化设备坐标(NDC):
https://zhuanlan.zhihu.com/p/65969162
(TODO)纹理采样
opengl 使用 texture2d 关键步骤_aiyaya730的博客-CSDN博客_opengl texture2d函数
图片纹理的使用流程是这样的:
- 准备 纹理的像素数据 imageData
- glGenTextures(); 存放在一个 句柄 GLuint texture
- glBindTexture() 刚才生成的 Texture, 第一个参数是 GL_TEXTURE_2D
- glTexImage2D() ,用 imageData 和 刚才绑定的 texture 结合起来,这样texture 生成完毕
- 清理 imageData 内存
- glBindTexture() 第一个参数 是 GL_TEXTURE_2D, 第二个传 0. 和 刚才的 texture 句柄 解除绑定
纹理采样器-sampler2D
纹理数据是在显存中的,可以通过映射关系去获取其数据。
纹理采样器就是用来根据坐标获取数据的工具。
纹理采样器不止sampler2D这一种,只是我们经常用到这个而已。
为什么texture2D()的纹理采样器参数可以为空:
在前边我们了解了
uniform这个变量,这里在片段着色器中声明了一个采样器,但是在代码中却没有给它赋值,目前看工作正常,能够绘制出图片!这是因为OpenGL中有默认的纹理单元0,且这个纹理单元默认激活!
Three.js中关于Shader的加载机制
https://juejin.cn/post/6964935853671972878
Threejs将shader代码分为ShaderLib和ShaderChunk两部分,
ShaderLib通过组合ShaderChunk的代码来构建vertexShader和fragmentShader。
src/renderers/shaders/ShaderChunk目录下,是各种通用的着色器代码片段
然后通过src/renderers/shaders/ShaderChunk.js进行了整合,统一导出。
WebGLProgram.js
https://threejs.org/docs/#api/en/renderers/webgl/WebGLProgram
这个类定义了很多常量
resolveIncludes()方法会将include的着色器源码拼接到一起
从LineMaterial看Shader编程的基本流程
调试技巧
通过着色器绘制数字
OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式 - 自由布鲁斯 - 博客园
用计算机最原始的0和1来向Shader提问!
opengl - How to debug a GLSL shader? - Stack Overflow
简单点,可以通过一个N维数组,存储不同数字的坐标信息,然后当需要输出该数字时,判断当前顶点的x、y坐标是否在这个数组内,在的话,就设置为其他颜色。
通过纹理定位坐标
可以在预期的地方,通过偏移量来绘制纹理:
void main(){
float bug=0.0;
vec3 tile=texture2D(colMap, coords.st).xyz;
vec4 col=vec4(tile, 1.0);
if(something) bug=1.0;
col.x+=bug;
gl_FragColor=col;
}
(精)通过纹理输出字母
不过这个很耗费GPU,我的2050被占满了,帧数还只有10几帧。
如果电脑好,这个调试方式很不错。
资料
GLSL中文手册:
GitHub - wshxbqq/GLSL-Card: 着色器语言 GLSL (opengl-shader-language)入门大全
视频教程:
对应的代码:
https://github.com/NikLever/GLSLfromScratch.git
Shadertoy新手教程:
https://shadertoyunofficial.wordpress.com/
Shadertoy的特有概念(对于我转写为GLSL有帮助;而且里面还有教程,太棒了):
https://shadertoyunofficial.wordpress.com/2016/07/20/special-shadertoy-features/
https://www.youtube.com/watch?v=0ifChJ0nJfM
glslViewer-没有UI的控制台也能玩shader:
GitHub - patriciogonzalezvivo/glslViewer: Console-based GLSL Sandbox for 2D/3D shaders