WebGL入门教程四:借助

作者:心叶
时间:2019-09-12 14:51

通过前面三篇文档的说明,大家应该基本了解了webgl的绘制方法,为了下一步更深入的学习,我们先来学习一下一个辅助库image3D,这个库主要是提供一些辅助方法。

引入image3D

通过npm方式管理,首先你需要通过命令行安装image3D,就像这样:

npm install --save image3d

安装好了以后,在需要的地方引入即可:

import $$ from 'image3d';

const $$ = require("image3d");

着色器

现在,我们再次绘制一个点,完整的代码点击这里

顶点着色器

<script type='x-shader/x-vertex' id='vs'>
    void main(){
        gl_Position=vec4(0.0,0.0,0.0,1.0);
        gl_PointSize=100.0;
    }
</script>

gl_Position和gl_PointSize都是着色器固定的内置变量,分别表示点的位置和大小。

片段着色器

<script type='x-shader/x-fragment' id='fs'>
    void main(){
        gl_FragColor=vec4(1.0,0.0,0.0,1.0);
    }
</script>

gl_FragColor是片段着色器的内置变量,表示点的颜色。

绘图代码

我们都知道,着色器就是二段字符串(如果一些细节忘了,可以回去看看前三篇基本概念),下面的代码才是JS,上面只是写好的字符串,提供给我们用。

这里,我们不再使用原生的方法编写了(重要部分还是原生的,为了方便大家理解原理),如何引入image3D大家可以查看README.md,我们这里就直接使用了($$表示image3D)。

<canvas width=500 height=500>非常抱歉,您的浏览器不支持canvas!</canvas>

假设页面中有一个canvas,使用image3D的第一步是获取3D启动对象:

var render3D = $$(document.getElementsByTagName('canvas')[0]).render3D();

好了,余下的就很容易了。回忆一下,绘图的第一步是什么?是的,让着色器生效:

// 启用着色器
render3D.shader(
    document.getElementById('vs').innerHTML,
    document.getElementById('fs').innerHTML
);

render3D提供了一个方法shader,你只要把二段着色器字符串传递给它,它就会帮你让着色器生效。

着色器生效以后,GPU其实就有了这些数据,接下来只需要告诉GPU帮我绘制就好了:

// 获取画笔
var gl = render3D.painter();

// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);

注意,上面的gl提供的是原生的方法,比如这里的drawArrays绘制了一个点。

好了,一个点就绘制完成了。怎么样,是不是更加简单了。接下来我们就希望通过这个库,给大家讲解webgl3D绘图的一些基础知识,可能涉及:3D模型数据的加载和解析、层次模型、光照、透视和纹理等。

缓冲区

主要说明第一个缓冲区的使用,例子完整代码在这里,第二类缓冲区稍微麻烦一点,我们后面再说明。

我们的需求是,绘制三个三角形,三角形每个顶点的颜色都不一样。我们知道,这里一共有9个顶点,9中颜色(和顶点对应),无法直接写死在着色器中,怎么办,很简单,写入缓冲区,然后把缓冲区分配给着色器中的变量。

还是先看看着色器的代码。

顶点着色器

<script type='x-shader/x-vertex' id='vs'>
    attribute vec4 a_position;
    attribute vec4 a_color;
    varying vec4 v_color;
    void main(){
        gl_Position=a_position;
        v_color=a_color;
    }
</script>

我们定义了一个变量a_position和a_color,这就是过会要和缓冲区绑定的点的坐标和颜色的变量,没问题,那v_color是干什么的,接着看片段着色器。

片段着色器

<script type='x-shader/x-fragment' id='fs'>
    precision mediump float;
    varying vec4 v_color;
    void main(){
        gl_FragColor=v_color;
    }
</script>

我们发现,片段着色器中也定义了v_color,是的,片段着色器中没办法一下子传递很多数据,都是绘制点的时候,来自顶点着色器(为了大家理解这样描述,不是很准确,想具体了解的,可以查看这里或点击issue提问)

传递数据

和上面例子差不多的就不解释了,大家可以看完整代码

着色器写好以后,我们要让它生效,生效以后,是不是就调用绘图方法绘图就可以了?

不是的,前一个例子我们把数据写死在着色器中了,这个例子我们是定义一个变量接收数据,因此,我们在绘图前,需要传递数据给着色器。

怎么传递?使用缓冲区呀:

// 初始化缓冲区
var buffer = render3D.buffer()
    // 数据写入缓冲区
    .write(data)
    // 写入缓冲区的数据分配
    .use('a_position', 3, 6, 0)
    .use('a_color', 3, 6, 3);

render3D.buffer方法返回缓冲区对象,使用write写入数据,然后使用use分配数据给前面定义的变量即可(data是数据,完整代码中有,这里不贴出来了)。

绘图

好了数据有了以后,就简单了:

// 绘制三个三角形
gl.drawArrays(gl.TRIANGLES, 0, 9);

看看运行截图:
WebGL入门教程四:借助

后记

你可以试着修改第二个例子中点的坐标试试。这篇文章主要是要入门借助image3D绘图的流程,是不是很容易。例子中涉及的方法和一些细节我们后面再说明。有任何疑惑可以留言提问。