webgl入门

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-着色器初始化

初始化着色器的步骤:

  1. 建立程序对象,目前这只是一个手绘板的外壳。

    const shaderProgram = gl.createProgram();
  2. 建立顶点着色器对象和片元着色器对象,这是手绘板里用于接收触控笔信号的零部件,二者可以分工合作,把触控笔的压感(js信号)解析为计算机语言(GLSL ES),然后让计算机(浏览器的webgl 渲染引擎)识别显示。

    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
  3. 将顶点着色器对象和片元着色器对象装进程序对象中,这就完成的手绘板的拼装。

    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
  4. 连接webgl 上下文对象和程序对象,就像连接触控笔和手绘板一样(触控笔里有传感器,可以向手绘板发送信号)。

    gl.linkProgram(shaderProgram);
  5. 启动程序对象,就像按下了手绘板的启动按钮,使其开始工作。

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


上一篇:WebGL之构建导洞多边形


下一篇:WebGL入门(二十二)-通过顶点索引绘制渐变色的立方体