GPU Dissolve(GPU 消散)学习GPU Instancing

一:摘要

 

        通过制作一个模型GPU消散效果来学习GPU Instancing 也就是实例化。

目标效果是杨超大佬文章《GPU shatter》里面的消散效果如图:

 Tags:模型顶点分裂(Mesh Vertex Splitting), 实例化绘制(GPU Instancing Drawing),顶点运动(Vertex Anim)。

二:实现原理简述

1:构建获取数据:(instancing数据及模型信息)

        instancing数据需要的M矩阵,及自己想要传递的信息。

        鹿模型mesh的顶点信息(mesh.vertices)和索引信息(mesh.triangles)以及面数(N)等信息,通过computerBuffer传递给材质。

2:构建instancing用的Triangle mesh(uv and vertices)

3:Render

        正常render鹿模型。

        通过instancing绘制三角面,数量位置等信息已通过鹿模型获取并传递,M矩阵也构建,隐藏可以得到另一个鹿模型。

4:构建动画(compute shader anim  or vertex anim)

        最简单的就是使用vertex anim顶点动画。方便易懂。

        compute shader动画复杂一点但是性能应该会更好。

5:调参

        把效果跳的稍微能看一点

三:实现

1:获取模型数据

  第一步:构建instaning数据(M矩阵构建)
    //创建对应结构体private struct MeshProperties{public Matrix4x4 drawMeshInsM;}//在初始化时构建M矩阵void OnEnable(){//num为面数for (int i = 0; i < num; i++){Vector3 pos = commonDrawGO.transform.position;pos.x = -pos.x;Quaternion rotation = commonDrawGO.transform.rotation;Vector3 scale = commonDrawGO.transform.localScale;//通过Transform信息构建对应模型tmpProperties.drawMeshInsM = Matrix4x4.TRS(pos, rotation, scale);properties[i] = tmpProperties;}// 通过computeBuffer传参给Material//(使用computeBuffer是因为之前写的用到了CS)meshPropertiesBuffer = new ComputeBuffer(num, meshPropertiesSize);meshPropertiesBuffer.SetData (properties);GPUDrawMat.SetBuffer("_Properties", meshPropertiesBuffer);}
第二步:构建mesh数据(顶点等)
        //mesh起始索引等信息uint[] args = new uint[5] { 0, 0, 0, 0, 0 };args[0] = (uint) mesh.GetIndexCount(0);args[1] = (uint) num;args[2] = (uint) mesh.GetIndexStart(0);args[3] = (uint) mesh.GetBaseVertex(0);//verticesGPUDrawMat.SetBuffer("_Properties", meshPropertiesBuffer);meshVerticesBuffer =new ComputeBuffer(TargetMesh.vertexCount, sizeof(float) * 3);meshVerticesBuffer.SetData(TargetMesh.vertices);GPUDrawMat.SetBuffer("_Vertices", meshVerticesBuffer);//triangles meshindicesBuffer =new ComputeBuffer(TargetMesh.triangles.Length, sizeof(int));meshindicesBuffer.SetData(TargetMesh.triangles);GPUDrawMat.SetBuffer("_Indices", meshindicesBuffer);

       2:构建Triangle

构建triangle时为了实现边线亮中间暗淡效果,同时为了解决边界锯齿以及边界线不等宽问题对uv进行了设计。看采样贴图及很好理解。

构建等边三角形以及渐变贴图解决(图片是求美术大佬用sp生成的)

uv信息其实和顶点位置是一样的,但是顶点位置原点在三角形中心,顶点uv在左下角。

private Mesh CreateTriMesh(){Mesh ans = new Mesh();//等边三角形三点位置Vector3[] vertices = new Vector3[3];vertices[0] = new Vector3(0, 0.134f, 0) - Vector3.one * 0.5f;vertices[1] = new Vector3(1, 0.134f, 0) - Vector3.one * 0.5f;vertices[2] = new Vector3(0.5f, 1, 0) - Vector3.one * 0.5f;//等边三角形三点UVVector2[] uvs = new Vector2[3];uvs[0] = new Vector2(0, 0.134f);uvs[1] = new Vector2(1, 0.134f);uvs[2] = new Vector2(0.5f, 1);int[] indices = new int[3];indices[0] = 0;indices[1] = 1;indices[2] = 2;ans.vertices = vertices;ans.uv = uvs;ans.triangles = indices;return ans;}

3:Render

第一步:C++++端
//instancing绘制
Graphics.DrawMeshInstancedIndirect(mesh, 0, GPUDrawMat, bounds, argsBuffer);
//另外一个走默认渲染就行
第二步:shader端(Vert And Frag)
struct MeshProperties{float4x4 drawMeshInsM;};
StructuredBuffer<MeshProperties> _Properties;StructuredBuffer<float3> _Vertices;StructuredBuffer<int> _Indices;v2f vert(appdata_t i, uint instanceID: SV_InstanceID,uint vertexID : SV_VertexID) {//通过vertexID(0,1,2)和instanceID去_Vertices获取真实的顶点信息//然后再乘上对应的M矩阵。float4 pos = mul(_Properties[instanceID].drawMeshInsM,float4(_Vertices[_Indices[vertexID + instanceID * 3.0]]  - center,1));//}

4:构建动画及着色

这里直接以顶点动画为例,其实也写了computershader的但是写的有瑕疵

第一步:构建旋转函数(Rotate)

前面有提到原点再三角中心,所以先构建一个旋转函数

经典的构建旋转矩阵,先把点移动到原点,然后再乘以旋转函数,再移动回自己的位置

            void Rotate(inout float4 vertex, float3 center, float3 around, float angle){float4x4 translation = float4x4(1, 0, 0, -center.x,0, 1, 0, -center.y,0, 0, 1, -center.z,0, 0, 0, 1);float4x4 translationT = float4x4(1, 0, 0, center.x,0, 1, 0, center.y,0, 0, 1, center.z,0, 0, 0, 1);around.x = -around.x;around = normalize(around);float s = sin(angle);float c = cos(angle);float ic = 1.0 - c;float4x4 rotation = float4x4(ic * around.x * around.x + c           , ic * around.x * around.y - s * around.z, ic * around.z * around.x + s * around.y, 0.0,ic * around.x * around.y + s * around.z, ic * around.y * around.y + c           , ic * around.y * around.z - s * around.x, 0.0,ic * around.z * around.x - s * around.y, ic * around.y * around.z + s * around.x, ic * around.z * around.z + c           , 0.0,0.0                                    , 0.0                                    , 0.0                                    , 1.0);vertex = mul(translationT, mul(rotation, mul(translation, vertex)));if((instanceID  + 1.0) % _BatchCount < _BatchCount * _Range){o.insID = 1;}else{o.insID = 0;}}
第二步:构建位移动画(Pos And Scale)
                //构建中心点float3 center = _Vertices[_Indices[ instanceID * 3.0]] +_Vertices[_Indices[ instanceID * 3.0 + 1]] + _Vertices[_Indices[ instanceID * 3.0 + 2]];center /=3;//构建位置float4 pos = mul(_Properties[instanceID].drawMeshInsM,float4(_Vertices[_Indices[vertexID + instanceID * 3.0]]  - center,1));float4  pos1 = float4(_Vertices[_Indices[vertexID + instanceID * 3.0]],1);float3 around = normalize(GetRandomF3(pos.xyz));//float3(0.0,1.0,0.0);//动画时间数据float statyTime = 0.4;float offsetIntensity = saturate((_BatchCount * _Range - (instanceID + 1.0)%_BatchCount)/10 -statyTime);float offsetIntensity1 = max(-statyTime,((_BatchCount * _Range - (instanceID + 1.0)%_BatchCount)/10 - statyTime)) + statyTime;offsetIntensity1 = min(offsetIntensity1 * 3 ,1.0);o.alphaLerp = offsetIntensity1;pos1.xyz = (1 - offsetIntensity) * pos1.xyz + offsetIntensity * center;float angle = _Speed * offsetIntensity;float3 positionWS = pos1;float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - positionWS);//around = viewDir;Rotate(pos1,center,around,angle );pos1 = mul(_Properties[instanceID].drawMeshInsM,pos1);pos1.y += offsetIntensity * _FlowSpeed * 0.1;

第四步:着色

没有技巧全是smoothstep出来(按理不该这么做,性能很差)

                //frag  //使用insID来表示当前Tri是否还需要显示是否消失half4 frag(v2f i, uint instanceID: SV_InstanceID) : SV_Target {float insID = i.insID;if(insID > 0.9){fixed4 col = tex2D(_MainTex, i.uv);float uuu1 = smoothstep(_Pos - _Width * 0.5 - _SmoothRange,_Pos - _Width * 0.5,col.r);float uuu2 = 1 - smoothstep(_Pos + _Width * 0.5 ,_Pos + _Width * 0.5+ _SmoothRange,col.r);float lines = uuu1 * uuu2;float tris = saturate((uuu2 - uuu1) * uuu2);return (lines * _LineColor + tris * _TriColor) * i.alphaLerp;}return 0;}

5:调参

        略

四:总结

        通过对模型进行拆分使用instancing进行重绘制,对模型数据结构以及instancing做了简单了解,还有用到的顶点动画较为简单,以及有很多可以优化的地方,比如M矩阵其实都是一样的,有些位置数据是没用的可以省略等等等。

后续会补上源代码链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/39516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源可商业运营的ChatGpt网页源码v1.2.2

&#x1f916; 主要功能 后台管理系统,可对用户,Token,商品,卡密等进行管理 精心设计的 UI&#xff0c;响应式设计 极快的首屏加载速度&#xff08;~100kb&#xff09; 支持Midjourney绘画和DALLE模型绘画,GPT4等应用 海量的内置 prompt 列表&#xff0c;来自中文和英文 一键导…

【猿灰灰赠书活动 - 02期】- 【Java从入门到精通2023年7月最新(第7版)】

说明&#xff1a;博文为大家争取福利&#xff0c;与清华大学出版社合作进行送书活动 图书&#xff1a;《Java从入门到精通》 一、好书推荐 图书介绍 Java入门经典&#xff0c;95万Java程序员的入行选择。配备升级版Java开发资源库&#xff0c;在线大咖课在线答疑&#xff0c;学…

C语言 棱形图案

目录 一、问题分析 上部分&#xff1a; 下部分&#xff1a; 二、代码演示 一、问题分析 如上图所示&#xff0c;我们可以将棱形进行拆解&#xff0c;分为上下两个部分。 上部分&#xff1a; 通过观察&#xff0c;我们得到 单边空格数 上半部分总行数 - 行数 - 1 …

Nginx详解

1、高并发时代 单台tomcat在理想情况下可支持的最大并发数量在200~500之间&#xff0c;如果大于这个数量可能会造成响应缓慢甚至宕机。 解决方案是通过多台服务器分摊并发压力&#xff0c;这不仅需要有多台tomcat服务器&#xff0c;还需要一台服务器专门用来分配请求。这既是…

Mysql - 配置Mysql主从复制-keepalived高可用-读写分离集群

目录 高可用&#xff1a; 为什么需要高可用呢&#xff1f; 高可用的主要作用&#xff1a; keepalived是什么&#xff1f;它用在哪里&#xff1f; 什么是VRRP协议&#xff0c;它的作用是什么&#xff1f; 搭建一个基于keepalived的高可用Mysql主从复制读写分离集群 一、项…

Vue Baidu Map--自定义点图标bm-marker

自定义点图标 将准备好的图标放到项目中 使用import引入&#xff0c; 并在data中进行声明 <script> import mapIconRed from ./vue-baidu-map/img/marker_red_sprite.png export default {data() {return {mapIconRed,}}, } </script>在<bm-marker>中加入参…

【Linux】进程的基本属性|父子进程关系

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 我的gitee&#xff1a;Linux仓库 个人专栏&#xff1a;Linux专栏 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处 文章目录 前言进程属性1.进程PID和PPID2.fork函数创建子进程1&#xff09;为什…

C语言学习笔记---数据的存储详解

C语言程序设计笔记---015 C语言数据的存储1、数据类型的意义1.1、unsigned与signed数据类型例程11.2、补码与原码相互转换例程2 2、大小端的介绍2.1、大小端的例程12.2、大小端的例程2 --- 判断当前编译器环境属于大端或小端 3、综合练习题探究数据的存储3.1、练习题13.2、练习…

自动化安装系统(一)

系统安装过程 加载boot loader加载启动安装菜单加载内核和initrd文件加载根系统运行anaconda的安装向导 安装光盘中与安装相关的文件 安装autofs启动后会自动出现/misc目录。 在虚拟机设置中添加CD/DVD&#xff0c;使用系统ISO文件&#xff0c;登录系统后mount /dev/cdrom …

青翼科技自研2路250MSPS DA回放FMC子卡模块

FMC150_V30是一款基于VITA57.1规范的2路125MSPS采样率16位分辨率AD采集、2路250MSPS采样率16位分辨率DA回放FMC子卡模块。该模块遵循VITA57.1规范&#xff0c;可直接与符合VITA57.1规范的FPGA载卡配合使用&#xff0c;板卡ADC器件采用ADI公司的AD9268芯片&#xff0c;板卡DAC器…

机器学习理论笔记(一):初识机器学习

文章目录 1 前言&#xff1a;蓝色是天的机器学习笔记专栏1.1 专栏初衷与定位1.2 本文主要内容 2 机器学习的定义2.1 机器学习的本质2.2 机器学习的分类 3 机器学习的基本术语4 探索"没有免费的午餐"定理&#xff08;NFL&#xff09;5 结语 1 前言&#xff1a;蓝色是天…

mac安装vscode 配置git

1、安装vscode 官网地址 下载mac稳定版安装很慢的解决办法 (转自) mac电脑如何解决下载vscode慢的问题 选择谷歌浏览器右上角的3个点&#xff0c;选择下载内容&#xff0c;右键选择复制链接地址&#xff0c;在新窗口粘贴地址&#xff0c; 把地址中的一段替换成下面的vscode.cd…

[C++] 模板template

目录 1、函数模板 1.1 函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 1.4.1 隐式实例化 1.4.2 显式实例化 1.5 模板参数的匹配原则 2、类模板 2.1 类模板的定义格式 2.2 类模板的实例化 讲模板之前呢&#xff0c;我们先来谈谈泛型编程&am…

Qt读写Excel--QXlsx编译为静态库2

1、概述&#x1f954; 在使用QXlsx时由于源码文件比较多&#xff0c;如果直接加载进项目里面&#xff0c;会增加每次编译的时间&#xff1b; 直接将源码加载进项目工程中&#xff0c;会导致项目文件非常多&#xff0c;结构变得更加臃肿&#xff1b; 所以在本文中将会将QXlsx编译…

骨传导耳机头晕是怎么回事?骨传导耳机好不好

骨传导耳机在音频传输上采用了不同于传统耳机的方式。它们通过将声音振动传递到颞骨&#xff0c;然后通过骨骼传导到内耳&#xff0c;从而使用户能够听到音乐或声音。 然而&#xff0c;有些人在使用骨传导耳机时可能会感到头晕。这可能与以下几个原因有关&#xff1a; 1、刚开…

99%的Python用户都不知道的f-string隐秘技巧

f-string想必很多Python用户都基础性的使用过&#xff0c;作为Python3.6版本开始引入的特性&#xff0c;通过它我们可以更加方便地向字符串中嵌入自定义内容&#xff0c;但f-string真正蕴含的功能远比大多数用户知道的要丰富&#xff0c;今天我们就来一起get它们~ 「最基础用法…

【CI/CD】基于 Jenkins+Docker+Git 的简单 CI 流程实践(上)

基于 JenkinsDockerGit 的简单 CI 流程实践&#xff08;上&#xff09; 在如今的互联网时代&#xff0c;随着软件开发复杂度的不断提高&#xff0c;软件开发和发布管理也越来越重要。目前已经形成一套标准的流程&#xff0c;最重要的组成部分就是 持续集成 及 持续交付、部署。…

Effective C++学习笔记(7)

目录 条款41&#xff1a;了解隐式接口和编译多态条款42&#xff1a;了解typename的双重意义条款43&#xff1a;学习处理模板化基类内的名称条款44&#xff1a;将与参数无关的代码抽离templates条款45&#xff1a;运用成员函数模板接受所有兼容类型条款46&#xff1a;需要类型转…

avue多选列表根据后端返回的某个值去判断是否选中;avue-curd多选回显

效果如上&#xff1a; getSiteList().then(res > {//列表数据this.siteData res.data.datathis.$nextTick(()>{this.siteData.forEach(item>{//业务条件if(item.configid&&item.configid!0&&item.configid>0){//符合条件时调用选中的方法this.$…

JAVASE---数组的定义与使用

数组的基本概念 什么是数组 数组是具有相同类型元素的集合&#xff0c;在内存中连续存储。 1. 数组中存放的元素其类型相同 2. 数组的空间是连在一起的 3. 每个空间有自己的编号&#xff0c;起始位置的编号为0&#xff0c;即数组的下标 数组的创建及初始化 数组的创建 T[…