搜索文章

输入关键词开始搜索

GLSL学习笔记

Three.js#Three.js#计算机图形学#3D#AI

设计理念

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

coor-transfrom

光照计算

摘自羡澈的文章

将光源位置(这是通过参数传入的)乘以模型视图矩阵(这是着色器提供给我们的),就能得到光源相对于摄像机的位置;

将这一位置归一化(就是计算单位向量),得到光源相对于摄像机的角度;

将其点乘单位法向量,得到亮度值(回想一下,两个向量点乘的意思是,获得一个向量在另一个向量上的投影,所以,这里得到的是光源在法向量方向的投影长度,它决定了一个点有多亮)。

基本语法

常用内置变量

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函数

图片纹理的使用流程是这样的:

  1. 准备 纹理的像素数据 imageData
  2. glGenTextures(); 存放在一个 句柄 GLuint texture
  3. glBindTexture() 刚才生成的 Texture, 第一个参数是 GL_TEXTURE_2D
  4. glTexImage2D() ,用 imageData 和 刚才绑定的 texture 结合起来,这样texture 生成完毕
  5. 清理 imageData 内存
  6. glBindTexture() 第一个参数 是 GL_TEXTURE_2D, 第二个传 0. 和 刚才的 texture 句柄 解除绑定

纹理采样器-sampler2D

纹理数据是在显存中的,可以通过映射关系去获取其数据。

纹理采样器就是用来根据坐标获取数据的工具。

纹理采样器不止sampler2D这一种,只是我们经常用到这个而已。

为什么texture2D()的纹理采样器参数可以为空:

在前边我们了解了uniform这个变量,这里在片段着色器中声明了一个采样器,但是在代码中却没有给它赋值,目前看工作正常,能够绘制出图片!这是因为OpenGL中有默认的纹理单元0,且这个纹理单元默认激活!

https://blog.csdn.net/aiynmimi/article/details/126668472

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;
}

(精)通过纹理输出字母

Shader - Shadertoy BETA

不过这个很耗费GPU,我的2050被占满了,帧数还只有10几帧。

如果电脑好,这个调试方式很不错。

资料

GLSL中文手册:

GitHub - wshxbqq/GLSL-Card: 着色器语言 GLSL (opengl-shader-language)入门大全

视频教程:

https://www.bilibili.com/video/BV1kP4y1Y7MT?p=4&spm_id_from=pageDriver&vd_source=1ddc293a4439c7106ebd7878040f7c81

对应的代码:

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