【three.js / glsl】 用 shader 写出夜空中的萤火虫

极客匠人说
• 阅读 4964
参考课程是这位老哥的 three.js 教程 非常厉害 感兴趣的可以买来看看
如果本身对 shader 还不够了解的话,the book of shaders 是非常好的入门读物

说到做“萤火虫”,肯定会想到用“粒子”(particles)。

用 buffer geometry 创建粒子

首先定义一个 buffer geometry, 然后创建一个 BufferAttribute 用于存储每一个萤火虫的位置信息;

const firefliesGeometry = new THREE.BufferGeometry()
const firefliesCount = 30 //创建萤火虫的个数
const positionArray = new Float32Array(firefliesCount * 3)

for(let i = 0; i < firefliesCount; i++)
{
    positionArray[i * 3 + 0] = Math.random() * 4
    positionArray[i * 3 + 1] = Math.random() * 4
    positionArray[i * 3 + 2] = Math.random() * 4
}

firefliesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))

我们先给它一个 pointsMaterial 看看这些点都在哪里;

const firefliesMaterial = new THREE.PointsMaterial({ size: 0.1, sizeAttenuation: true })

const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial)
scene.add(fireflies)

看到了空中的小点点,发现完全的随机位置有点奇怪,所以改改生成位置的代码:

for(let i = 0; i < firefliesCount; i++)
{
    positionArray[i * 3 + 0] = (Math.random() - 0.5) * 4
    positionArray[i * 3 + 1] = Math.random() * 1.5
    positionArray[i * 3 + 2] = (Math.random() - 0.5) * 4
}

基础的小点点有了,接下来就是 shader 的魔法时刻。

自定义 shader material: vertex shader

首先是 vertex shader,用于决定物体的节点的位置属性。

The vertex shader's purpose is to position the vertices of the geometry. The idea is to send the vertices positions, the mesh transformations (like its position, rotation, and scale), the camera information (like its position, rotation, and field of view). Then, the GPU will follow the instructions in the vertex shader to process all of this information in order to project the vertices on a 2D space that will become our render —in other words, our canvas.

这段代码几乎没有什么含义,重头戏在 fragment shader 上。
但是有一个地方需要注意一下,即 gl_PointSize 需要根据屏幕分辨率而变化,所以需要从 js 文件中传进去一个 uniform: window.devicePixelRatio

const firefliesMaterial = new THREE.ShaderMaterial({
    uniforms:
    {
        uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
        uSize: { value: 100 },
        uTime: { value: 0 },
    },
    blending: THREE.AdditiveBlending, //叠加模式也很重要
    vertexShader: firefliesVertexShader,
    fragmentShader: firefliesFragmentShader
})

同时,要监听 resize 事件,实时更新分辨率:

window.addEventListener('resize', () =>
{
    // ...

    // Update fireflies
    firefliesMaterial.uniforms.uPixelRatio.value = Math.min(window.devicePixelRatio, 2)
})

vertex.glsl

uniform float uPixelRatio;
uniform float uSize; // 粒子大小
uniform float uTime;

void main()
{
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 projectionPosition = projectionMatrix * viewPosition;

    gl_Position = projectionPosition;
    gl_PointSize = uSize * uPixelRatio; //每一个点的渲染尺寸
    gl_PointSize *= (1.0 / - viewPosition.z); //近大远小
    
    // 萤火虫的浮动
    vec4 modelPosition = modelMatrix * vec4(position, 1.0); 
    modelPosition.y += sin(uTime);
}

想让萤火虫浮动起来,还需要在外面更新 uTime

const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update materials
    firefliesMaterial.uniforms.uTime.value = elapsedTime

    // ...
}

自定义 shader material: fragment shader

我们的目标是创造一个中心渐变的图案,听起来很简单(确实

void main()
{
    float distanceToCenter = distance(gl_PointCoord, vec2(0.5)); // 计算每一个像素点到中心的距离
    float strength = 0.05 / distanceToCenter - 0.1; // 中心大,周围小

    gl_FragColor = vec4(1.0, 1.0, 1.0, strength); // 将 strength 作为 alpha
}

voila.

推荐阅读

jam3-webgl-shader-threejs
book of shaders

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
4年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
4年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
4年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
极客匠人说
极客匠人说
Lv1
谁说我不会乐器,我退堂鼓打的可好了。
文章
4
粉丝
0
获赞
0