From f13d7989cba09737a2011839950cc5baa862d7d0 Mon Sep 17 00:00:00 2001 From: lijie67 Date: Wed, 20 Apr 2022 16:17:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/_posts/2022-04-20-webgl-shader.md | 212 +++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 source/_posts/2022-04-20-webgl-shader.md diff --git a/source/_posts/2022-04-20-webgl-shader.md b/source/_posts/2022-04-20-webgl-shader.md new file mode 100644 index 0000000000..a95a753406 --- /dev/null +++ b/source/_posts/2022-04-20-webgl-shader.md @@ -0,0 +1,212 @@ +title: webGL基础-着色器 +subtitle: +cover: +categories: 3d webgl shader +tags: + - Phaser3 + - 合成大西瓜 + - matter.js +author: + nick: 杰 + github_name: eijil + date: 2021-03-09 17:58:08 + +--- + + 在元宇宙概念的背景下,上级领导也要求做点技术储备,在学习了一些常用的Web3D框架(babylonjs,threejs)后, 好奇于它们的底层原理,所以去了解下webGL的知识,本文主要介绍WebGl中着色器的原理。 + + +## webGL + +webGL其实是一个很底层的图像引擎,主要提供的能力就是接收输入的图形的顶点坐标,程序在GPU中帮你画出图形(光珊化)和上色后输出到浏览器,在GPU运行的程序则是一种OpenGL着色语言(GLGS),一个webGL程序大概是如下步骤、其中最关键的还是着色器部分。 + +![](/img/webgl/webgl.png) + +下面的一些代码基本都会省略api调用部分 + +## 着色器 + +简单点的说着色器就干两件事: + +1. 根据顶点坐标绘制图像 +2. 给图形的每个像素点上色 + +在`webGL`中分别对应两种着色器,**顶点着色器(vertext shader)** 和 **片元着色器(fragment shader)** ,两种是成对出现的组成一段着色程序在GPU上运行。 + + + + +## 缓冲区 + +缓冲是发送到GPU的一些二进制数据序列,通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等。 也可以存储任何数据。 + + +如果我们要画一个三角形,那我们在缓冲区写入三个顶点的数据,数据类型是一个`Float32Aarray` 就是一个一维数组: + +``` js + +//三个2维顶点 +var positions = new Float32Array([ + -0.5, -0.5, //v0 + -0.5, 0.5, //v1 + 0.5, -0.5 //v2 +]) + +//创建buffer等方法省略。。。。 + +``` +在gl中的坐标是一种叫裁切空间的坐标,取值是`(-1,1)`最后渲染后会换算成屏幕空间坐标,假如我们的画布是400x300, 那`(-0.5,0.5)`的坐标就是`(100,225)`。 +``` js +(-1,1) (1,1) + ---------------------------------- + | | + | | + | | + | | + | | + | (0,0) | + | | + | | + | | + | | + ---------------------------------- +(-1,-1) (1,-1) + +``` + +这是配置顶点数据的,下面我们继续看下画一个三角形顶点着色器和片元着色器的代码: + +## 画一个三角形 + +``` js + +//顶点着色器 +const vsSource = ` + //属性值,从缓冲中获取数据 + attribute vec4 position; + void main(void) { + //gl_Position内置的系统变量,记录顶点坐标 + gl_Position = position; + }`; + +//片元着色器 +const fsSource = ` + void main(void) { + //gl_FragColor内置的系统变量,记录像素颜色 + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); //绿色 + }` + +//画图 +const type = gl.TRIANGLES +const offset = 0 //从第一个顶点开始 +const vCount = 3 //总顶点数,执行多少次 +gl.drawArrays(type, offset, vCount) + +``` + +**`attributes`** 属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器,前面我们已经在缓冲区创建了一个顶点数组,两个可以理解为等值,通过webGL api绑定后,在顶点着色器中就可以获取到,再赋值给`gl_Position` + +**片元着色器** 是在顶点着色器运行完后给图形的每个片元(也可以理解为每个像素)上色,上面代码中我们是设置了固定的颜色,gl中颜色值是`vec4(r,g,b,a)`,每个通道的取值范围是0-1,rgb分别代表红绿蓝,如果红色则是`vec4(1, 0, 0 ,1)`。 + +最后调用`gl.drawArrays(type, offset, vCount)` 来执行程序,顶点着色器的运行次数是一个确切的数字,例如这个三角形的例子,我们是设置了三个顶点,所以`vCount`这个值就是`3`,每次从属性值中获取`2`个,这个`2`个也是通过api指定的,为了方便理解,代码没有写出来,假设我们是一个三维的图形(x,y,z),那么就是指定每次获取3个。 + +如果画个矩形,那就是: +``` js +var positions = new Float32Array([ + -0.5, -0.5, //v0 + -0.5, 0.5, //v1 + 0.5, -0.5 //v2 + + -0.5, 0.5, //v3 + 0.5, -0.5, //v4 + 0.5, 0.5 //v5 +]) +const type = gl.TRIANGLES +const offset = 0 //从第一个顶点开始 +const vCount = 6 //总顶点数,执行多少次 +gl.drawArrays(type, offset, vCount) + +``` + +## 给片元着色器传值 + +前面片元着色器颜色是固定,但在实际程序中,我们可能会给每个三角形设置不同颜色,或者贴图的时候需要传纹理坐标,那么如何传值呢?同样我们需要创建一个buffer,使用关键字`varying`将数据通过顶点着色器再传到片元着色器,如下代码: + +``` js +//顶点颜色 +const colors = Float32Array([ + 0.1, 0.0 , 0.0, 0.1, //red + 0.1, 1.0 , 0.1, 0.1, //green + 0.1, 0.0 , 0.1, 0.1, //blue +]) + +//顶点着色器 +const vsSource = ` + //属性值,从缓冲中获取数据 + attribute vec4 position; + //缓冲区取color数据 + attribute vec4 colors; + //定义varying数据 + varying vec4 v_color; + void main(void) { + gl_Position = position; + v_color = colors; + }`; + +//片元着色器 +const fsSource = ` + varying vec4 v_color; + void main(void) { + gl_FragColor = v_color; + }` + +``` +从代码上看我们还是用`attribute`从缓冲区取数据,然后通过`varying`关键字定义了`v_color`,在顶点着色器中赋值,最后片元着色器也是通过`varying` 取到颜色值。我们给三角形三个顶点设置了不同颜色,每个片元的值是由顶点的线性插值得到,所以看到的是下面的渐变的颜色。 +![](/img/webGL/11.png) + + +## 执行顺序 + +根据下图,我们再来梳理下着色器的执行流程,首先在缓冲区中获得数据,顶点着色器根据坐标输出图形(三角形),这一步可以叫做图元装配,确定形状后,我们就知道图形中存在多少片元(像素),每个像素都会调用一次片元着色器进行上色。 +![](/img/webGL/2.png) + +## 顶点索引 + +我们知道再复杂的模型的是由三角形的组成的,前面我们画一个平面是使用了两个三角6个顶点来画的,但其实两个三角中其中其实是有两个顶点是重复的,所以我们可以使用`gl.drawElemnt`来代替`gl.drawArrays`,前者支持通过索引来指定顶点,这样可以减少我们顶点传输量,一个平面可能就只减少了两个点,但如果图形复杂就减少很多内存了。 + +``` js + +//平面顶点 +const position = [ + -1.0, -1.0, 1.0 //顶点0 + 1.0, -1.0, 1.0, //顶点1 + 1.0, 1.0, 1.0, //顶点2 + -1.0, 1.0, 1.0, //顶点3 +] +//索引指定使用哪个顶点 +const index = [ + 0, 1, 2, 0, 2, 3 //下标 + ] +//api部分 +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index); +gl.drawElements(gl.TRIANGLES, 36); + +``` + +## 总结 + +本篇到这里就结束了,其实只是讲了一点点基础概念,实际要发开3d还需要非常多的储备知识、比如还要了解光照原理,摄像机(透视矩阵)原理等等,其实对数学知识的要求还是很高的、当然webGL也就了解下就好,实际开发可以使用three.js,babylonjs等框架。 + +## 参考 + +[webGL 理论基础](https://webGLfundamentals.org/webGL/lessons/zh_cn/) + +[mdn Getting started with webGL](https://developer.mozilla.org/zh-CN/docs/Web/API/webGL_API/Tutorial/Getting_started_with_webGL) + +[An Introduction to webGL](https://dev.opera.com/articles/introduction-to-webGL-part-1/) + +[Raw webGL 101 — Part 1: Getting Started](https://dev.opera.com/articles/raw-webGL-part-1-getting-started/) + +[webGL model view projection](https://developer.mozilla.org/zh-CN/docs/Web/API/webGL_API/webGL_model_view_projection#perspective_matrix) + +《webGL 编程指南》 \ No newline at end of file From 04f391ef005a25e688d99f8ee4c4b4b42c1ffa17 Mon Sep 17 00:00:00 2001 From: lijie67 Date: Wed, 20 Apr 2022 16:20:37 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/_posts/2022-04-20-webgl-shader.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/_posts/2022-04-20-webgl-shader.md b/source/_posts/2022-04-20-webgl-shader.md index a95a753406..473b5659f7 100644 --- a/source/_posts/2022-04-20-webgl-shader.md +++ b/source/_posts/2022-04-20-webgl-shader.md @@ -1,15 +1,15 @@ title: webGL基础-着色器 subtitle: cover: -categories: 3d webgl shader +categories: Web开发 tags: - - Phaser3 - - 合成大西瓜 - - matter.js + - 3D + - WebGL + - Shader author: nick: 杰 github_name: eijil - date: 2021-03-09 17:58:08 + date: 2022-04-20 17:58:08 --- From 8fd66b375f67eb69a5be54261a66920aacc3bee7 Mon Sep 17 00:00:00 2001 From: lijie67 Date: Wed, 20 Apr 2022 16:22:23 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/_posts/2022-04-20-webgl-shader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/_posts/2022-04-20-webgl-shader.md b/source/_posts/2022-04-20-webgl-shader.md index 473b5659f7..7a39fd643d 100644 --- a/source/_posts/2022-04-20-webgl-shader.md +++ b/source/_posts/2022-04-20-webgl-shader.md @@ -195,7 +195,7 @@ gl.drawElements(gl.TRIANGLES, 36); ## 总结 -本篇到这里就结束了,其实只是讲了一点点基础概念,实际要发开3d还需要非常多的储备知识、比如还要了解光照原理,摄像机(透视矩阵)原理等等,其实对数学知识的要求还是很高的、当然webGL也就了解下就好,实际开发可以使用three.js,babylonjs等框架。 +本篇到这里就结束了,其实只是讲了一点点基础概念,实际要开发3d项目还需要非常多的储备知识、比如还要了解光照原理,摄像机(透视矩阵)原理等等,其实对数学知识的要求还是很高的、当然webGL也就了解下就好,实际开发可以使用three.js,babylonjs等框架。 ## 参考 From dd1da33888678ab58a968ce904b62179773d63ce Mon Sep 17 00:00:00 2001 From: lijie67 Date: Wed, 20 Apr 2022 16:27:39 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E5=9B=BE=E5=BA=8A?= =?UTF-8?q?=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/_posts/2022-04-20-webgl-shader.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/_posts/2022-04-20-webgl-shader.md b/source/_posts/2022-04-20-webgl-shader.md index 7a39fd643d..61121481b3 100644 --- a/source/_posts/2022-04-20-webgl-shader.md +++ b/source/_posts/2022-04-20-webgl-shader.md @@ -20,7 +20,7 @@ author: webGL其实是一个很底层的图像引擎,主要提供的能力就是接收输入的图形的顶点坐标,程序在GPU中帮你画出图形(光珊化)和上色后输出到浏览器,在GPU运行的程序则是一种OpenGL着色语言(GLGS),一个webGL程序大概是如下步骤、其中最关键的还是着色器部分。 -![](/img/webgl/webgl.png) +![](https://img10.360buyimg.com/imagetools/jfs/t1/181430/1/23122/47131/625fc3ccE1817f782/68bb2226f2f70419.png) 下面的一些代码基本都会省略api调用部分 @@ -162,13 +162,13 @@ const fsSource = ` ``` 从代码上看我们还是用`attribute`从缓冲区取数据,然后通过`varying`关键字定义了`v_color`,在顶点着色器中赋值,最后片元着色器也是通过`varying` 取到颜色值。我们给三角形三个顶点设置了不同颜色,每个片元的值是由顶点的线性插值得到,所以看到的是下面的渐变的颜色。 -![](/img/webGL/11.png) +![](https://img11.360buyimg.com/imagetools/jfs/t1/200339/34/23479/73833/625fc3ccEcdcb7a01/f91e861b1017ed64.png) ## 执行顺序 根据下图,我们再来梳理下着色器的执行流程,首先在缓冲区中获得数据,顶点着色器根据坐标输出图形(三角形),这一步可以叫做图元装配,确定形状后,我们就知道图形中存在多少片元(像素),每个像素都会调用一次片元着色器进行上色。 -![](/img/webGL/2.png) +![](https://img11.360buyimg.com/imagetools/jfs/t1/123504/28/27636/997473/625fc3cbE8b406d80/1e2186d5500075e6.png) ## 顶点索引