1-刷底色的基本步骤
1.在html中建立canvas 画布
<canvas id="canvas"></canvas>
2.在js中获取canvas画布
const canvas=document.getElementById('canvas’);
3.使用canvas 获取webgl 绘图上下文
const gl=canvas.getContext('webgl’);
4.指定将要用来清空绘图区的颜色
gl.clearColor(0,0,0,1);
5.使用之前指定的颜色,清空绘图区
gl.clear(gl.COLOR_BUFFER_BIT);
整体代码
<canvas id="canvas"></canvas><script> const canvas=document.getElementById('canvas'); canvas.width=window.innerWidth; canvas.height=window.innerHeight; const gl=canvas.getContext('webgl'); gl.clearColor(0,0,0,1); gl.clear(gl.COLOR_BUFFER_BIT);</script>
clearColor(r,g,b,a) 中的参数是红、绿、蓝、透明度,其定义域是[0,1]
2-灵活操作webgl中的颜色
css 中有一个“rgba(255,255,255,1)” 颜色,其中r、g、b的定义域是[0,255],这里要和webgl里的颜色区分一下。
我们可以简单了解一下将css颜色解析为webgl 颜色的原理:
const rgbaCSS = "rgba(255,0,0,1)";const reg = RegExp(/\((.*)\)/);const rgbaStr = reg.exec(rgbaCSS)[1];const rgb = rgbaStr.split(",").map((ele) => parseInt(ele));const r = rgb[0] / 255;const g = rgb[1] / 255;const b = rgb[2] / 255;const a = rgb[3]; gl.clearColor(r, g, b, a); gl.clear(gl.COLOR_BUFFER_BIT);
在three.js 里有一个非常完美的颜色对象-Color,我们通过这个对象可以轻松的控制颜色。
案例-多姿多彩的画布
1.引入Color 对象
import { Color } from "https://unpkg.com/three/build/three.module.js";
我这是通过CDN 引入的,这种方法不适用于nodejs,因为nodejs 无法直接通过网络路径请求资源。
2.实例化Color 对象
const color = new Color(1, 0, 0);
3.建立色相偏移动画
!(function ani() { color.offsetHSL(0.005, 0, 0); gl.clearColor(color.r, color.g, color.b, 1); gl.clear(gl.COLOR_BUFFER_BIT); requestAnimationFrame(ani); })();
关于颜色的操作我们就说到这,Color 对象还有很多其它方法,可以在threejs官网查看。
3-webgl 的绘图步骤
1.在html中建立canvas 画布
<canvas id="canvas"></canvas>
2.在js中获取canvas画布
const canvas=document.getElementById('canvas');
3.使用canvas 获取webgl 绘图上下文
const gl=canvas.getContext('webgl');
4.在script中建立顶点着色器和片元着色器,glsl es
//顶点着色器<script id="vertexShader" type="x-shader/x-vertex"> void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 100.0; }</script>//片元着色器<script id="fragmentShader" type="x-shader/x-fragment"> void main() { gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); }</script>
5.在js中获取顶点着色器和片元着色器的文本
const vsSource = document.getElementById('vertexShader').innerText;const fsSource = document.getElementById('fragmentShader').innerText;
6.初始化着色器
initShaders(gl, vsSource, fsSource);
7.指定将要用来清空绘图区的颜色
gl.clearColor(0,0,0,1);
8.使用之前指定的颜色,清空绘图区
gl.clear(gl.COLOR_BUFFER_BIT);
9.绘制顶点
gl.drawArrays(gl.POINTS, 0, 1);
整体代码
<canvas id="canvas"></canvas><!-- 顶点着色器 --><script id="vertexShader" type="x-shader/x-vertex"> void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 100.0; }</script><!-- 片元着色器 --><script id="fragmentShader" type="x-shader/x-fragment"> void main() { gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); }</script><script> // canvas 画布 const canvas = document.getElementById('canvas'); canvas.width=window.innerWidth; canvas.height=window.innerHeight; // webgl画笔 const gl = canvas.getContext('webgl'); // 顶点着色器 const vsSource = document.getElementById('vertexShader').innerText; // 片元着色器 const fsSource = document.getElementById('fragmentShader').innerText; // 初始化着色器 initShaders(gl, vsSource, fsSource); // 指定将要用来清理绘图区的颜色 gl.clearColor(0., 0.0, 0.0, 1.0); // 清理绘图区 gl.clear(gl.COLOR_BUFFER_BIT); // 绘制顶点 gl.drawArrays(gl.POINTS, 0, 1); function initShaders(gl,vsSource,fsSource){ //创建程序对象 const program = gl.createProgram(); //建立着色对象 const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); //把顶点着色对象装进程序对象中 gl.attachShader(program, vertexShader); //把片元着色对象装进程序对象中 gl.attachShader(program, fragmentShader); //连接webgl上下文对象和程序对象 gl.linkProgram(program); //启动程序对象 gl.useProgram(program); //将程序对象挂到上下文对象上 gl.program = program; return true; } function loadShader(gl, type, source) { //根据着色类型,建立着色器对象 const shader = gl.createShader(type); //将着色器源文件传入着色器对象中 gl.shaderSource(shader, source); //编译着色器对象 gl.compileShader(shader); //返回着色器对象 return shader; }</script>
对于上面的步骤1、2、3,大家应该都比较好理解,接下来咱们详细说一下第4 步,在script 里用GLSL ES语言写着色器。
5-着色器
5-1-着色器的概念
webgl 绘图需要两种着色器:
- 顶点着色器(Vertex shader):描述顶点的特征,如位置、颜色等。
- 片元着色器(Fragment shader):进行逐片元处理,如光照。
看了这两个名词的解释,我想很多初学者会是懵的。
我给大家翻译翻译:
补间动画大家知道不?顶点着色器里的顶点就是补间动画里的关键帧,片元着色器里的片元就是关键帧之间以某种算法算出的插值。当然,咱们webgl里的片元是像素的意思。
再给大家举一个更简单、更贴切的例子:
两点决定一条直线大家知道不?顶点着色器里的顶点就是决定这一条直线的两个点,片元着色器里的片元就是把直线画到画布上后,这两个点之间构成直线的每个像素。
关于概念咱们就说到这,接下来咱们说着色器语言。
5-2-着色器语言
webgl 的着色器语言是GLSL ES语言
- 顶点着色程序,要写在type=“x-shader/x-vertex” 的script中。
<script id="vertexShader" type="x-shader/x-vertex"> void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 100.0; } </script>
- 片元着色程序,要写在type=“x-shader/x-fragment” 的script中。
<script id="fragmentShader" type="x-shader/x-fragment"> void main() { gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); } </script>
void main() {…… } 是主体函数。
在顶点着色器中,gl_Position 是顶点的位置,gl_PointSize 是顶点的尺寸,这种名称都是固定的,不能写成别的。
在片元着色器中,gl_FragColor 是片元的颜色。
vec4() 是一个4维矢量对象。
将vec4() 赋值给顶点点位gl_Position 的时候,其中的前三个参数是x、y、z,第4个参数默认1.0,其含义我们后面会详解;
将vec4() 赋值给片元颜色gl_FragColor 的时候,其中的参数是r,g,b,a。
至于GLSL ES语言的其它知识,咱们会在后面另开一篇详解,这里先以入门为主。
在第6步中,我们使用了一个自定义的方法initShaders() ,这是用于初始化着色器的,接下来咱们详细说一下。
6-着色器初始化
初始化着色器的步骤:
-
建立程序对象,目前这只是一个手绘板的外壳。
const shaderProgram = gl.createProgram();
-
建立顶点着色器对象和片元着色器对象,这是手绘板里用于接收触控笔信号的零部件,二者可以分工合作,把触控笔的压感(js信号)解析为计算机语言(GLSL ES),然后让计算机(浏览器的webgl 渲染引擎)识别显示。
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
-
将顶点着色器对象和片元着色器对象装进程序对象中,这就完成的手绘板的拼装。
gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader);
-
连接webgl 上下文对象和程序对象,就像连接触控笔和手绘板一样(触控笔里有传感器,可以向手绘板发送信号)。
gl.linkProgram(shaderProgram);
-
启动程序对象,就像按下了手绘板的启动按钮,使其开始工作。
gl.useProgram(program);
上面第二步中的建立着色对象方法loadShader(),是一个自定义的方法,其参数是(webgl上下文对象,着色器类型,着色器源文件),gl.VERTEX_SHADER 是顶点着色器类型,gl.FRAGMENT_SHADER是片元着色器类型。
function loadShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); return shader; }
gl.createShader(type) :根据着色器类型建立着色器对象的方法。
gl.shaderSource(shader, source):将着色器源文件传入着色器对象中,这里的着色器源文件就是我们之前在script 里用GLSL ES写的着色程序。
- gl.compileShader(shader):编译着色器对象。
在以后的学习里,initShaders 会经常用到,所以我们可以将其模块化。
function initShaders(gl,vsSource,fsSource){ //创建程序对象 const program = gl.createProgram(); //建立着色对象 const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); //把顶点着色对象装进程序对象中 gl.attachShader(program, vertexShader); //把片元着色对象装进程序对象中 gl.attachShader(program, fragmentShader); //连接webgl上下文对象和程序对象 gl.linkProgram(program); //启动程序对象 gl.useProgram(program); //将程序对象挂到上下文对象上 gl.program = program; return true; }function loadShader(gl, type, source) { //根据着色类型,建立着色器对象 const shader = gl.createShader(type); //将着色器源文件传入着色器对象中 gl.shaderSource(shader, source); //编译着色器对象 gl.compileShader(shader); //返回着色器对象 return shader; }export {initShaders}
后面在需要的时候,import 引入即可。
import {initShaders} from '../jsm/Utils.js';