WebGL入门教程四借助image3D使用着色器和缓冲区

33次阅读

共计 2321 个字符,预计需要花费 6 分钟才能阅读完成。

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

通过前面三篇文档的说明,大家应该基本了解了 webgl 的绘制方法,为了下一步更深入的学习,我们先来学习一下一个辅助库 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 大家可以查看 image3D 在线文档,我们这里就直接使用了($$ 表示 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);

看看运行截图:

后记

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

正文完
 0