世界杯举办地_世界杯预选赛巴西 - emsxbc.com

【Threejs进阶教程-着色器篇】10.粒子Shader入门与基础雪花效果

【Threejs进阶教程-着色器篇】10.粒子Shader入门与基础雪花效果

本系列教程第一篇地址,建议按顺序学习模板代码解析gl_PointSize让粒子大小随着深度变化而变化

修改粒子的样式gl_PointCoord简介给粒子贴图处理透明处叠加

使用顶点着色器操作粒子动态粒子大小动态粒子位置

小练习,模拟下雪效果生成随机的粒子让粒子循环下落

雪花效果完整源码如有不明白的,可以在下方留言或者加群

本系列教程第一篇地址,建议按顺序学习

本系列目前已累计第十篇,这里直接省略了2到9篇的地址,可以通过上方专栏来查阅前面的教程 【Threejs进阶教程-着色器篇】1. Shader入门(ShadertoyShader和ThreejsShader入门)

本篇使用到的模板代码,从这里自取粒子模板代码 【模板代码】用于编写Threejs Demo的模板代码

粒子效果入门教程 【Threejs基础教程-点线精灵篇】4.2 基本粒子效果Points

模板代码解析

首先,我们先分析模板代码,片元着色器与之前本教程讲的片元着色器变化不大,在后面编写代码时才会有变化,所以现在仅讲解顶点着色器代码

varying vec2 vUv;

void main(){

vUv = vec2(uv.x,uv.y);

vec3 u_position = position;

vec4 mvPosition = modelViewMatrix * vec4( u_position, 1.0 );

gl_PointSize = 300.0 / -mvPosition.z;

gl_Position = projectionMatrix * mvPosition;

}

第四行,u_position,主要用于保存当前的变量,而不在原有的变量上做计算,包括对uv的保存等

第五行,mvPosition,字面意思**[模型视图位置]**,修改此位置,可以影响最终渲染的位置,可以简单尝试修改一下这个vec4变量看看最终效果,这个计算,在后续WebGL教程中会详细讲解,第五行可以视为现阶段,threejs的粒子shader的固定写法

gl_PointSize

此属性主要用于调整粒子大小,可以先修改为下面的代码,然后随便改变下数字,我们运行下看下效果,加深对此属性的理解

gl_PointSize = 20.0;

让粒子大小随着深度变化而变化

如果让gl_PointSize 设置为一个固定的数字,那么我们无论如何调整视角,我们的粒子实际上都是固定的大小,类似于之前在SpriteMaterial的讲解中,提到的sizeAttenuation属性

所以,我们除以mvPosition的z,来让粒子产生深度效果

gl_PointSize = 300.0/ - mvPosition.z;

大家可以多次修改这里的公式以及常量,来感受一下粒子大小的变化 这里的公式,现阶段可以看作为固定公式,为什么mvPosition.z 要为负值这个问题,在后续的WebGL教程中会做讲解

300,是本人在使用过程中,对模板代码创建的粒子的一个大致的合适的大小的常量,此常量根据个人需求修改即可 做了这样的修改后,我们的粒子大小,就会随着你的视角拉近拉远而放大缩小了

最后面的代码,其实本质上和之前的代码是一致的,不过是把mvPosition单独拆分出来,给粒子效果使用了而已

修改粒子的样式

我们依然可以用片元着色器的gl_FragColor来控制最终颜色,但是,当我们想使用uv去控制粒子的效果的时候,发现uv并不能对粒子产生影响,这里我们就需要一个新的变量gl_PointCoord

gl_PointCoord简介

//片元着色器代码

varying vec2 vUv;

void main(){

vec2 gpc = gl_PointCoord;

float a = 1.0 - distance(gpc,vec2(0.5));

gl_FragColor = vec4(a,0.0,0.0,1.0);

}

gl_PointCoord,你可以理解为是粒子效果专用的uv,基本用法与uv相似

给粒子贴图

当然,我们也完全可以用 texture2D(texture,gpc); 这种方式,给粒子贴一张图,资源我们依然使用threejs开发包中的资源 three\examples\textures\sprites\circle.png csdn直接保存图片会有水印,但是仅在教学过程中使用,是完全没问题的

记得开启透明度

let uniforms = {

iTime:{value:0}

}

function addMesh() {

let textureLoader = new THREE.TextureLoader();

let map = textureLoader.load('./circle.png');

uniforms.pointMap = {value:map};

let geometry = new THREE.BoxGeometry(1,1,1);

let material = new THREE.ShaderMaterial({

uniforms,

vertexShader:document.getElementById('vertexShader').textContent,

fragmentShader:document.getElementById('fragmentShader').textContent,

transparent:true

})

let points = new THREE.Points(geometry,material);

scene.add(points);

}

处理透明处叠加

这里有人发现了,我们的图是贴到粒子上了,但是透明的地方有问题 但是,我们尝试给材质追加 alphaTest 的时候,没有任何作用,在ShaderMaterial中,我们要想处理这种透明度错误,一般用下面这种办法

alphaTest的代码非常简单,就是判断透明度的值,然后使用discard 关键字,丢弃掉这个片元

可以看到,我们把透明度小于等于0的部分直接舍弃掉,这样,我们的透明边缘就得到了一定的改善,我们可以通过不断的调整此数值达到最佳效果,这里本人就不再做调整了,大家自行尝试即可

使用顶点着色器操作粒子

动态粒子大小

首先我们回顾一下之前讲到的动效,我们使用uniforms添加一个iTime来控制时间变化

let uniforms = {

iTime:{value:0}

}

function addMesh() {

let textureLoader = new THREE.TextureLoader();

let map = textureLoader.load('./circle.png');

uniforms.pointMap = {value:map};

let geometry = new THREE.BoxGeometry(1,1,1);

let material = new THREE.ShaderMaterial({

uniforms,

vertexShader:document.getElementById('vertexShader').textContent,

fragmentShader:document.getElementById('fragmentShader').textContent,

transparent:true,

})

let points = new THREE.Points(geometry,material);

scene.add(points);

}

function render() {

uniforms.iTime.value += 0.01;

renderer.render(scene,camera);

orbit.update();

requestAnimationFrame(render);

}

这里我们使用sin函数做周期变化,用绝对值,让计算消除负值,这样我们就可以制作出呼吸效果的粒子效果了

具体要怎么变化,看各位对下面的数学公式的加工了

动态粒子位置

当然我们也可以直接操作粒子的位置

小练习,模拟下雪效果

生成随机的粒子

首先我们先把上面修改完的顶点着色器改回来

我们上面一直在用BoxGeometry来生成粒子,所以生成的粒子,主要以Box的8个顶点为准

这里我们要更换成随机的粒子,来模仿下雪的感觉

function addMesh() {

let textureLoader = new THREE.TextureLoader();

let map = textureLoader.load('./circle.png');

uniforms.pointMap = {value:map};

//随机生成1000个粒子

let geometry = new THREE.BufferGeometry();

let pointsCount = 1000;

let vecArray = [];

for(let i = 0;i< pointsCount;i++){

let point = new THREE.Vector3();

point.x = Math.random() * 100 - 50;

point.y = Math.random() * 100 - 50;

point.z = Math.random() * 100 - 50;

vecArray.push(point);

}

geometry.setFromPoints(vecArray);

let material = new THREE.ShaderMaterial({

uniforms,

vertexShader:document.getElementById('vertexShader').textContent,

fragmentShader:document.getElementById('fragmentShader').textContent,

transparent:true,

})

let points = new THREE.Points(geometry,material);

scene.add(points);

}

因为我们的粒子贴图是很白很白的,所以这里我们也要把renderer的alpha属性去掉,黑色的背景看起来更清晰

renderer = new THREE.WebGLRenderer({

//alpha:true, 开启此属性后,背景变为透明,背景色变成html的背景色

antialias:true

});

让粒子循环下落

虽然我们可以用很简单的方法,直接操作u_position -= iTime来实现下落,但是我们还需要让下落到底的粒子,再回到最顶部

所以,我们要控制粒子的高度,始终在 最高高度到最低高度内,也就是 -50~50这个区间

我们首先第一步,要计算出我们的粒子所在的位置到整个高度轴空间的百分比,然后用这个百分比去加上一个周期变化的iTime,最后在百分比上做变化后,乘以总高度并计算偏移后,计算出粒子的最终位置,即可得到我们的循环下落效果

雪花效果完整源码

Title

如有不明白的,可以在下方留言或者加群

如有其他不懂的问题,可以在下方留言,也可以加入qq群咨询, Web3D+GIS开源社区为新群,群内相对来说学习气氛良好,群号131995948 本人的群,群号867120877 欢迎大家来群里交流技术