共计 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 绘图的流程,是不是很容易。例子中涉及的方法和一些细节我们后面再说明。有任何疑惑可以留言提问。