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-将不必要的逻辑放到前端(发送调查问卷)
目录一:背景介绍背景二:思路&方案重大问题:解决办法优点:三:总结一:背景介绍 本篇博客书写的意义是警示大家不必把不必要的逻辑放到前端。 明确前后端分离的意义。 背景 下面的主要逻辑是࿱…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 :爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
