webgl_gpgpu_birds 样例分析
webgl_gpgpu_birds 是一个 three.js 的官方样例,这个例子模拟了鸟群的运动,是一个群组动画,并且动画的帧率也很高;鸟群的运动很自然,非常值得研究。类似的群组动画还有鱼群,boid是‘类鸟群’的英文
大概两年前,第一次看这个例子时,很枯燥,看不懂,有很多地方不知道是什么意思。第一次看这个例子时,才知道原来 纹理 texture 可以这样来使用,这个例子可以作为一个通用的并行计算框架的例子来看待。
这个例子的鸟群中一共有 32 x 32 共 1024 只鸟。鸟群中的每只鸟的位置是一个三维坐标,保存在一张 32 x 32 像素的纹理图片缓存中,初始化时,fillPositionTexture函数负责为 每只鸟 赋予一个 [-400, 400] 闭区间内的一个随机值。鸟的位置的x y z 分量都在 -400 到 400内取随机值。
鸟群中的每只鸟的速度是一个三维向量,保存在另外一张 32 x 32 像素的纹理图片缓存中,初始化时,fillVelocityTexture函数负责为 每只鸟 的速度的x y z分向量都赋予一个 [-5, 5] 闭区间内的一个随机值。每只鸟的速率和方向都是不同的。
例子中的 GPUComputationRenderer 负责在每一帧渲染前,都去以一定的规律 或 计算模式去更新鸟群的位置纹理 和 速度纹理。这两个纹理的分辨率都是 32 x 32 像素,对于渲染器来说,分辨率很小。渲染器更新的纹理的分辨率一般都是屏幕分辨率,1024 * 768 等;所以,更新这两张贴图对于渲染器来说很轻量,写这两张纹理对应的片元着色器代码时,不用过于考虑效率问题。
第一次看这个例子时,差不多就知道这些。片元着色器和顶点着色器的代码完全看不懂。
这个例子一共有四个着色器。
a. 片元着色器 birdFS,负责更新鸟群中每只鸟的颜色,最简单
b. 顶点着色器birdVS,负责更新鸟群中每只鸟的姿态和位置坐标,第二难理解
c. 片元着色器fragmentShaderVelocity,负责更新鸟群中每只鸟的速度,相对来说最难理解,
d. 片元着色器fragmentShaderPosition,负责更新鸟群中每只鸟的三维坐标,第二简单
这四个着色器,是透彻理解这个例子绕不过去的。
着色器先放一放,先来讲场景构建,BirdGeometry其实名称不确切,应该叫BirdFlockGeometry.因为这个几何体实际上是描述鸟群的。vertices 属性保存每只鸟的几何顶点,birdColors属性保存每只鸟的顶点颜色,references 属性保存每只鸟在鸟群中的编号,可以通过这个编号找到每只鸟的在纹理图片中的三维坐标和三维速度向量。birdVertex属性保存一只鸟的顶点编号,每只鸟由三个三角面组成,每个三角面又由三个顶点组成。一只鸟就有九个顶点。这个编号就是从 0 到 8,每只鸟都是 0 到 8,这个birdVertex属性 只用于 birdVS 顶点着色器,用于找到鸟翅膀的两个顶点,修改两个顶点 y 坐标,这样每只鸟的一双翅膀就上下扇动起来了。
BirdGeometry的构造函数中,定义了每只鸟的形状;每只鸟顶点颜色,是从深灰到浅灰的不同数值
场景构建时,这行代码要留意一下 camera.position.z = 350; 摆在了正对着世界坐标的 xy 平面,并且世界坐标的原点位于屏幕的正中心。函数 fillPositionTexture 和 fillVelocityTexture 分别用于初始化每只鸟的位置和速度。
在绘制每一帧前都要调用gpuCompute.compute(),去更新两张 32 x 32像素的纹理图片,每只鸟的位置和速度就变化起来了。这两张纹理然后再传递给 鸟群的顶点着色器birdVS ,更新每只鸟的位置和姿态。
birdFS 中根据每只鸟的位置 z 坐标,来更新鸟的灰度,
varying vec4 vColor;
varying float z;uniform vec3 color;void main() {// Fake colors for nowfloat z2 = 0.2 + ( 1000. - z ) / 1000. * vColor.x;gl_FragColor = vec4( z2, z2, z2, 1. );}
z 越接近相机,越接近350,颜色边深,变暗,超过350,飞到相机后面,看不见了。
birdVS中,
if ( birdVertex == 4.0 || birdVertex == 7.0 ) {// flap wingsnewPosition.y = sin( tmpPos.w ) * 5.;
}
使每只鸟的翅膀上下扇动起来
velocity.z *= -1.;
float xz = length( velocity.xz );
float xyz = 1.;
float x = sqrt( 1. - velocity.y * velocity.y );float cosry = velocity.x / xz;
float sinry = velocity.z / xz;float cosrz = x / xyz;
float sinrz = velocity.y / xyz;
根据速度向量,求方位角 cosry sinry 和俯仰角 cosrz sinrz
假设 velocity 等于 (0, 0, 1.0), 那么 sinry == 1.0;表示需要绕 y轴 旋转90°,进行偏航;
在 BirdGeometry 中对单只鸟的形状构建,可以看到单只鸟的原始朝向就是 (0, 0, 1.0),也就是,velocity 等于 (0, 0, 1.0)时,其实不应该有 偏航;代码中的 576行,birdMesh.rotation.y = Math.PI / 2;
又把这种不一致纠正回来。
newPosition = maty * matz * newPosition;
newPosition += pos;
每只鸟的每个顶点,先绕z轴 (俯仰角)旋转,再绕y轴(方位角)旋转
fragmentShaderPosition片元着色器负责更新每只鸟的三维坐标,其中的 phase 保存在 w 分量中,用于在之后的 birdVS 顶点着色器中使用,来更新翅膀的摆动幅度,期望速率越大时,摆动的幅度也越大,频率也越快, 其中的 62.83 约等于 PI 的20倍。
uniform float time;
uniform float delta;void main() {vec2 uv = gl_FragCoord.xy / resolution.xy;vec4 tmpPos = texture2D( texturePosition, uv );vec3 position = tmpPos.xyz;vec3 velocity = texture2D( textureVelocity, uv ).xyz;float phase = tmpPos.w;phase = mod( ( phase + delta +length( velocity.xz ) * delta * 3. +max( velocity.y, 0.0 ) * delta * 6. ), 62.83 );gl_FragColor = vec4( position + velocity * delta * 15. , phase );}
最后一个最复杂,代码最多的fragmentShaderVelocity片元着色器,更新每只鸟的速度向量。
可以看到优先级最高的是规避 捕食者,让鸟群远离捕食者一定的距离;
可以看作是来自捕食者的排斥力,这是有条件的,只有鸟靠近捕食者一定距离,才会收到这种斥力,
第二优先级是,使鸟始终向着屏幕的中心移动,这些鸟始终都受到来自屏幕中心的引力;如果没有这个力,鸟群就散开了,很快飞到相机看不见的位置了。
紧接着是一个32 * 32的二重循环,来对鸟群中每只鸟应用 来自其他鸟的排斥力,吸引力,偏向力
if ( dist < 0.0001 ) continue;
表示如果当前像素就是自己,直接跑完这次循环
if ( distSquared > zoneRadiusSquared ) continue;
表示这只鸟 离当前自己太远,不会对我产生排斥力,偏向力,吸引力,直接跑完这次循环,忽略掉。
接下来,就是 if … else if … else … 三个分支,其实可以想象一个三个大小不同的圆组成一个同心圆环。最内层的圆表示,如果我自己和其他鸟的距离小于圆半径,则我受到来自这只鸟的排斥力;
如果我自己和其他鸟的距离在最小圆半径 和 次小圆半径之间,则我受到来自这只鸟的偏向力;我的飞行姿态要向这只鸟看齐,如果我自己和其他鸟的距离在次小圆半径 和最大圆半径之间,则受到来自这只鸟的吸引力。
排斥力,偏向力,吸引力三个力是鸟群之间的相互作用力。三个力是互斥的,鸟A 只能受到 鸟B三个力中的一种,也可能 鸟A 和 鸟B之间完全没有相互作用力。三个力的优先级是 排斥力 > 偏向力 > 吸引力。
separationDistance 定义排斥力半径, separationDistance + alignmentDistance的次圆面积 减去 半径为 separationDistance的最小圆面积,得到一个圆环区域;以我自己为圆心,如果其他鸟在这个圆环区域内,则我向这只鸟看齐,受到来自只鸟的偏向力;最大圆的半径是 separationDistance + alignmentDistance + cohesionDistance;最大圆 减去 次小圆又是另一个圆环;这个圆环内小鸟对我产生吸引力
// Attraction / Cohesion - move closer
float threshDelta = 1.0 - alignmentThresh;
float adjustedPercent;
if( threshDelta == 0. ) adjustedPercent = 1.;
else adjustedPercent = ( percent - alignmentThresh ) / threshDelta;f = ( 0.5 - ( cos( adjustedPercent * PI_2 ) * -0.5 + 0.5 ) ) * delta;velocity += normalize( dir ) * f;
上面代码里还考虑了除零异常。cohesionDistance是允许为零的,为零时,f = 1.5 * delta;
delta表示前一帧和当前帧之间流逝了多少时间,以毫秒为单位;
代码中当 cohesionDistance == 0, 并且 alignmentDistance == 0,当percent == 1时,直接进入else分支,这时鸟群之间没有偏向力,只有吸引力和排斥力两种。
相关文章:

webgl_gpgpu_birds 样例分析
webgl_gpgpu_birds 是一个 three.js 的官方样例,这个例子模拟了鸟群的运动,是一个群组动画,并且动画的帧率也很高;鸟群的运动很自然,非常值得研究。类似的群组动画还有鱼群,boid是‘类鸟群’的英文 大概两…...

以业务行为驱动的反入侵安全能力建设
0x0 背景 最近听到一些甲方安全领域的专家分享了部分安全建设的经验,对安全运营下的反入侵技术能力建设有了些新的看法,依靠单个/多个异构的安全产品的关联能力形成的安全中台并不能在实际的攻防对抗当中占据主动地位,且很容易达到一个天花板…...

Unity3d C#使用DOTween插件的Sequence实现系列动画OnComplete无效和颜色设置无效的问题记录
前言 最近在弄一个文字动画效果的动画,使用了DOTween插件的Sequence来实现,主要就是对一个Text进行的文字打字、缩放和颜色设置等动画,功能是先对Text实现打字的动画,打字完成后,延时几秒对文字进行缩小、颜色变淡&am…...

【蓝桥杯-筑基篇】排序算法
🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 前言: 一、冒泡排序 二、选择排序 三、插入排序 四、图书推荐 前言: 算法工具推荐: 还在为数据结构发愁吗?这款可视化工具,帮助你更好的了解…...

编辑器进化 VSCode + Vim
本文作者为 360 奇舞团前端工程师VSCode 是一款非常流行的代码编辑器。它支持多种编程语言,拥有丰富的插件和调试功能,不论是处理前端工程还是后端工程,VSCode 都能提供给开发者优秀的用户体验。鉴于 VSCode 超高的流行度,我会默认…...

LearnOpenGL-高级OpenGL-6.天空盒
本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject 文章目录天空盒介绍如何采样OpenGL纹理目标例子0:天空盒效果环境映射反射例子1:Cube…...

Printk打印内核日志
一、背景 Linux 内核中提供了内核日志打印的工具printk。它的使用方式C语言中的printf是类似的。接下来我们介绍一下printk的使用方式。本文以打印Binder中的日志为例,进行演示。 printk的方法声明和日志级别binder驱动中增加 打印代码android系统中查看日志信息 …...

界面控件DevExpress WPF 202计划发布的新功能合集
DevExpress WPF拥有120个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。本文将介绍今年DevExpr…...

Spring Cloud Alibaba 微服务2,注册中心演变 + Nacos注册中心与配置中心
目录专栏导读一、什么是Nacos?二、注册中心演变及其设计思想1、RestTemplate调用远程服务2、通过Nginx维护服务列表(upStream)3、通过Nacos实现注册中心4、心跳版Nacos三、Nacos Discovery四、Nacos核心功能1、服务注册2、服务心跳3、服务同步…...

Navicat 图形化界面工具
Navicat 介绍 Navicat是一套可创建多个连接的数据库管理工具,用以方便管理 MySQL、Oracle、SQL Server等不同类型的数据库 目录 Navicat 介绍 Navicat 下载 Navicat 安装 Navicat 使用 Navicat连接MySQL数据库 Navicat创建数据库和表 Navicat 下载 1、点击这…...

2023年网络安全比赛--attack(新)数据包分析中职组(超详细)
一、竞赛时间 180分钟 共计3小时 任务环境说明: 1 分析attack.pcapng数据包文件,通过分析数据包attack.pcapng找出恶意用户第一次访问HTTP服务的数据包是第几号,将该号数作为Flag值提交; 2.继续查看数据包文件attack.pcapng,分析出恶意用户扫描了哪些端口,将全部的端口号…...

C语言之extern(七十)
extern同一个文件:修饰变量声明#include <stdio.h>int add(){extern int x,y;return x y; }int main(){printf("%d\n", add()); }int x 10; int y 20;extern文件之间:修饰函数声明<1>.add.cint sum(){extern int x ;extern in…...

树的前中后序的Morris遍历
目录 一.Morris遍历 1.什么是Morris遍历 2.基本思想 3.Morris遍历的优点和缺点 4.知识回顾----二叉树的线索化 二.中序Morris遍历 1.中序Morris遍历的分析 2.中序Morris遍历的思路 3.具体的代码实现 三.前序Morris遍历 1.前序Morris遍历的思路 2.具体的代码实现 四…...

到底什么是线程?线程与进程有哪些区别?
上一篇文章我们讲述了什么是进程,进程的基本调度 http://t.csdn.cn/ybiwThttp://t.csdn.cn/ybiwT 那么本篇文章我们将了解一下什么是线程?线程与进程有哪些区别?线程应该怎么去编程? 目录 http://t.csdn.cn/ybiwThttp://t.csdn…...

你真的知道如何系统高效地学习数据结构与算法吗?
文章目录前言:什么是数据结构?什么是算法?学习这个算法需要什么基础?学习的重点在什么地方?一些可以让你事半功倍的学习技巧1.边学边练,适度刷题2.多问、多思考、多互动3.打怪升级学习法4.知识需要沉淀&…...

Linux操作系统基础的常用命令
1,Linux简介Linux是一种自由和开放源码的操作系统,存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、台式计算机。1.1Linux介绍Linux出现于1991年,…...

Jasypt加密库基本使用方法
目录 1 Jasypt简介... 2 基础知识回顾... 3 Jasypt基本加密器... 4 JasyptPBE加密器... 5 Jasypt池化加密器... 6 Jasypt客户端工具... 7 JasyptSpringboot基本用法... 8 JasyptSpringboot自定义加密器... 9 JasyptSprin…...

C++并发编程之五 高级线程管理
文章目录5.1.1 线程池5.1.1 线程池 在前面我们引入了线程的通信和同步手段,那么为什么还要引入线程池呢? 线程池是一种管理多个线程的技术,它可以减少线程的创建和销毁的开销,提高并发性能。线程池中有一定数量的空闲线程&#x…...

单片机——IIC协议与24C02
1、基础知识 1.1、IIC串行总线的组成及工作原理 I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。 1.2、I2C总线的数据传输 I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟…...

案例05-将不必要的逻辑放到前端(发送调查问卷)
目录一:背景介绍背景二:思路&方案重大问题:解决办法优点:三:总结一:背景介绍 本篇博客书写的意义是警示大家不必把不必要的逻辑放到前端。 明确前后端分离的意义。 背景 下面的主要逻辑是࿱…...

【每日一题】——矩阵相等判定
🌏博客主页:PH_modest的博客主页 🚩当前专栏:每日一题 💌其他专栏: 🔴 每日反刍 🟢 读书笔记 🟡 C语言跬步积累 🌈座右铭:广积粮,缓称…...

Linux防火墙的关闭
查看防火墙的状态打开终端输入如下命令systemctl status firewalld如图所示:running表示防火墙目前处于打开状态输入命令进行关闭防火墙:systemctl stop firewalld如图所示正常的用户是没有权限的,需要输入管理员的密码才能够进行关闭防火墙。…...

Request和Response的概述
⭐作者介绍:大二本科网络工程专业在读,持续学习Java,输出优质文章⭐作者主页:︶ㄣ释然⭐如果觉得文章写的不错,欢迎点个关注😉有写的不好的地方也欢迎指正,一同进步😁Request和Respo…...

常见的Web安全漏洞:SYN攻击/CSRF/XSS
一、SYN攻击(属于DOS攻击) 什么情况下被动方出现SYN_RCVD状态?(flood攻击服务) 客户伪造 ip 端口, 向服务端发送SYN请求。完成2次握手,第三次服务端 等待客户端ACK确认,但由于客户不存在服务端一直未收到确认&#…...

【STC15单片机】 超声波模块的使用
目录 1 基于STC15F2K60S2的超声波测距代码 1.1 基本注意事项 1.1.1 跳线帽接法 1.1.2 晶振设置 1.2 板载超声波工作原理 1.2.1 原理总结 1.2.2 超声波代码思路 1.3 STC15单片机代码部分 1.3.1 定时器0&定时器1初始化 1.3.2 超声波ultrasonic.c ultrasonic.h文件配…...

SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版
前段时间编写了一篇博客SpringBoot 动态操作定时任务(启动、停止、修改执行周期,该篇博客还是帮助了很多同学。 但是该篇博客中的方法有些不足的地方: 只能通过前端控制器controller手动注册任务。【具体的应该是我们提前配置好我们的任务&am…...

快排函数 -- qsort函数(Quick Sort)
文章目录🔎1.qsort函数简介💡1.1.函数原型💡1.2.参数含义🔎2.比较函数介绍🔎3.比较函数使用案例💡3.1.整型数组💡3.2.浮点型数组💡3.3.结构体类型 - 字符串🔎4.利用冒泡排…...

条形码和二维码
前言:需要的包的相关文档 1. Barcode:https://pypi.org/project/python-barcode/0.8.1/ 2. Qrcode:https://pypi.org/project/qrcode/ 3. Zbar: https://pypi.org/project/pyzbar/ 4. Opencv: https://docs.opencv.org/3.4.11/ 5. OpenC…...

大数据-学习实践-5企业级解决方案
大数据-学习实践-5企业级解决方案 (大数据系列) 文章目录大数据-学习实践-5企业级解决方案1知识点2具体内容2.1小文件问题2.1.1 SequenceFile2.1.2 MapFile2.1.3 小文件存储计算2.2数据倾斜2.3 YARN2.3.1 YARN架构2.3.2 YARN调度器2.3.2 YARN多资源队列配置和使用2.4Hadoop官方…...

破解吲哚花菁素IR-808 N3,IR-808 azide,IR-808叠氮,酯溶性染料修饰叠氮基团,相关知识
基础产品数据(Basic Product Data):CAS号:N/A中文名:IR-808叠氮英文名:IR-808 N3,IR-808 azideIR-808结构式(Structural):详细产品数据(Detailed …...