一.前言
除了矩形,圆形在实际的图形运用中也比较广泛。例如水波或光圈效果;若是用最基础的webgl理论来讲,我们都知道圆属于多边形的绘制,可以通过绘制圆上的各点,再将其连接起来通过gl.drawArrays方法,传入gl.TRIANGLE_FAN来进行绘制。今天我们介绍Three.js的绘制方法。
二.绘制原理
其实在three.js中,是有CircleGeometry圆几何体类的,我们可以很轻松的new一个来创建一个圆形平面,然后填充材质即可得到圆形的图案。那我们为什么要自己来绘制呢,原因很简单,自定义的shader易于扩展和变换,可以做一切你想做的事情。
言归正传,Three.js用shader画圆原理也非常简单
1.先创建一个矩形平面和自定义Shader材质
const geo = new THREE.PlaneBufferGeometry(this.width, this.height, 32, 32); //创建plane类型的图形
const mat = new THREE.ShaderMaterial({ //shader材质
vertexShader: vs,
fragmentShader: fs,
uniforms: this.uniforms,
transparent: true,
side: THREE.DoubleSide, //双面材质
wireframe: false,
});
this.shape = new THREE.Mesh(geo, mat);
2.片元着色器来确定点的绘制
这里我们只处理颜色,顶点着色器用默认的就好
void main() {
vec2 st = 2.0 * v_uv - 1.0; //坐标范围由[0,1]转化为[-1,1]
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
float dist = distance(st, vec2(0.0)); //与vec2(0,0)之间得距离
if (dist > u_radius) { //距离大于u_radius则用插值来确定该点的透明度
color.a = 0.0;
}
gl_FragColor = color;
}
上面代码就是根据距离取确定点的透明度,大于半径color.a=0.0,小于等于半径color.a=1.0
得到如下图:
虽是成功绘制了圆,但我们仔细观察,圆周上可以看到很明显的锯齿。前面我们也分析过,圆就是"多边形",所以在shader绘制中不能得到理想的曲线。请继续往下看,后面会讲解如何消除锯齿
三.圆环的绘制
上面我们绘制了圆,如法炮制按照上述思想绘制圆环也十分容易,这里不讲解了直接上代码
vec4 getColor(vec2 st) {
float dist = distance(st, vec2(0.0)); //距离[0,0]的距离
vec4 color = vec4(0.0);
if (dist < u_radius && dist > u_radius - u_border) { //u_radius-u_border ~ u_radius之间为非透明,即显示的环
color.a = 1.0;
}
return color;
}
void main() {
vec2 st = 2.0 * v_uv - 1.0;
gl_FragColor = getColor(st);
}
u_radius,u_border值均由js传入,分别为圆的半径,环的宽度
四.消除圆的锯齿
在上一篇Metaballs的解析中,我们运用了smoothstep的平滑埃尔米特插值来处理融合部分。
这里我们也可以用它来处理圆周部分,使它的透明度0-1分界之间没有那么生硬
修改片元着色器绘制:
void main() {
vec2 st = 2.0 * v_uv - 1.0;
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
float dist = distance(st, vec2(0.0)); //与vec2(0,0)之间得距离
if (dist > u_radius) { //距离大于u_radius则用插值来确定该点的透明度
float sub = dist - u_radius;
color.a = sub < 0.01 ? smoothstep(1.0, 0.0, sub/0.01): 0.0;
}
gl_FragColor = color;
}
在半径周围0.01的范围处做透明度的平滑过渡,我们来看下修改之后的效果:
是不是平滑多了,看不到明显的锯齿了
五.圆环的随机变换
关于圆弧应用最广泛的应该使波浪效果了,那么波浪效果如何实现呢?如果你也想到了三角函数sin,cos 那么恭喜你。我们需要做的就是周期性的修改圆弧的顶点,再添加一些随机性就能实现波浪效果
1.通过顶点着色器对顶点进行偏移
void main() {
v_uv = uv;
float amplitude = sin(u_time) * 0.03 + 0.01; //振幅,随时间变化
vec3 pos = position;
pos.x += cos(14. * uv.y + 8. * u_time) * amplitude ; //x偏移值随u_time变化 受amplitude影响 幅度也在一定范围内随机
pos.y += sin(4. * uv.x + 8. * u_time) * amplitude ; //y偏移 同理与x的处理,将cos改成sin
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
我们这里添加了多个半径不一圆圈的示例,得到的效果如下:
如果你熟悉三角函数,你可以做任意你想要的变化。比如:
这样螺旋变化,思考一下该如何实现呢, 自己尝试一下吧