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.rhkb.cn/news/93456.html

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

相关文章

在思科(Cisco)路由器中使用 SNMP

什么是SNMP SNMP&#xff0c;称为简单网络管理协议&#xff0c;被发现可以解决具有复杂网络设备的复杂网络环境&#xff0c;SNMP 使用标准化协议来查询网络上的设备&#xff0c;为网络管理员提供保持网络环境稳定和远离停机所需的重要信息。 为什么要在思科设备中启用SNMP S…

网络通信原理IP头部格式(第四十二课)

字段作用解析:1)版本: 指的IP地址的版本 (IPv4 或 IPV6)2)首部长度: 次数据包的首部长度一共是多少,没有加可选项3)优先级与服务类型:表示****数据包是否需要优选传递4)总长度: 表示的是整个数据包的大小,也就****是首部+数据5)标识符、标志、段偏移量:的作用将拆开的…

SDK是什么,SDK和API有什么区别

SDK&#xff08;Software Development Kit&#xff09;是一种开发工具包&#xff0c;通常由软件开发公司或平台提供&#xff0c;用于帮助开发人员构建、测试和集成特定平台或软件的应用程序。SDK 包含一系列的库、工具、示例代码和文档&#xff0c;旨在简化开发过程并提供所需的…

RocketMQ 5.1.0 源码详解 | Producer 发送流程

文章目录 初始化DefaultMQProducer实例发送流程DefaultMQProducer#sendDefaultMQProducerImpl#sendMQClientInstance#updateTopicRouteInfoFromNameServer使用特定 topic 获取路由信息使用默认 topic 获取路由信息 DefaultMQProducerImpl#sendDefaultImpl发送流程总结 初始化De…

爬虫逆向实战(十七)--某某丁简历登录

一、数据接口分析 主页地址&#xff1a;某某丁简历 1、抓包 通过抓包可以发现数据接口是submit 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现有一个enPassword加密参数 请求头是否加密&#xff1f; 通过查看请求头可以发现有一个To…

晨光初中生入学必备最全文具清单盘点,这些你入了吗?

又是一年开学季&#xff0c;对于即将迈入初中的学生们来说&#xff0c;购买文具是必不可少的&#xff0c;但是市面上的文具太多了&#xff0c;怎么才能选到合适的也是难题。为了帮助大家在新的学期中更好地学习和成长&#xff0c;这份初中生入学必备最全文具清单送给大家。 晨…

无涯教程-Perl - syswrite函数

描述 此函数尝试将SCALAR中的LENGTH个字节写入与FILEHANDLE相关的文件。如果指定了OFFSET,则从提供的SCALAR中的OFFSET字节中读取信息。该函数使用C /操作系统的write()函数,该函数绕过普通缓冲。 语法 以下是此函数的简单语法- syswrite FILEHANDLE, SCALAR, LENGTH, OFFS…

【数据结构】二叉树篇| 纲领思路02+刷题

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; 是瑶瑶子啦每日一言&#x1f33c;: 所谓自由&#xff0c;不是随心所欲&#xff0c;而是自我主宰。——康德 目录 一、前言二、刷题1、翻转二叉树 2、二叉树的层序遍历✨3、 二…

django实现文件上传

在django中实现文件上传有三种方法可以实现&#xff1a; 自己手动写使用Form组件使用ModelForm组件 其中使用ModelForm组件实现是最简单的。 1、自己手写 先写一个上传的页面 upload_file.html enctype"multipart/form-data 一定要加这个&#xff0c;不然只会上传文件名…

Windows Hyper-V Ubuntu 22.04 LTS安装

文章目录 Ubuntu准备Hyper-V启用虚拟化支持services.msc 打开服务列表&#xff0c;关注Hyper-V服务是否启动打开管理器创建虚拟机 启动备份 Ubuntu 下载Ubuntu-Desktop&#xff0c;这是个iso文件。 准备 20GB以上的磁盘空间&#xff0c;ubuntu安装后的虚拟磁盘文件超过15GB一…

Azure存储账户

存储账户的概念 Azure存储账户是Azure提供的一种云存储解决方案&#xff0c;用于存储和访问各种类型的数据&#xff0c;包括文件、磁盘、队列、表格和Blob&#xff08;二进制大对象&#xff09;数据。存储账户可以基于访问模式和冗余需求来选择不同的类型&#xff0c;以满足应…

ubuntu 编译安装nginx及安装nginx_upstream_check_module模块

一、下载安装包 # 下载nginx_upstream_check_module模块 wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master# 解压 unzip master# 下载nginx 1.21.6 wget https://github.com/nginx/nginx/archive/refs/tags/release-1.21.6.tar.gz # 解压…

【javaSE】初识Java

目录 Java是什么 Java语言发展简史 初识Java的main方法 运行Java程序 JDK、JRE、JVM之间的关系 Java中的标识符 Java是什么 Java是一种优秀的程序设计语言&#xff0c;它具有令人赏心悦目的语法和易于理解的语义. 不仅如此&#xff0c;Java还是一个有一系列计算机软件和规…

使用Druid解析SQL,获取SQL中所有使用的表

一、sqlParse组成 Druid SQL Parser分三个模块&#xff1a; - Parser - AST - Visitor 1.1 Parser parser是将输入文本转换为ast&#xff08;抽象语法树&#xff09;&#xff0c;parser有包括两个部分&#xff0c;Parser和Lexer&#xff0c;其中Lexer实现词法分析&#x…

Actuator微服务信息完善-Eureka—SpringCloud(版)微服务学习教程(11)

一、Actuator是什么&#xff1f; Actuator是Springboot提供的用来对应用系统进行自省和监控的功能模块&#xff0c;借助于Actuator开发者可以很方便地对应用系统某些监控指标进行查看、统计等。 在Springboot中使用Actuator监控非常简单&#xff0c;只需要在工程POM文件中引入…

【word密码】word怎么限制格式,但可以修改文字?

想要限制word文件中文字的格式&#xff0c;但是又希望别人能够删除、输入文字&#xff0c;想要实现这种设置我们可以对word文件设置限制编辑。 点击word文件工具栏中的审阅 – 限制编辑&#xff0c;勾选上【限制对选定的样式设置格式】 然后在弹出的提示框中&#xff0c;输入我…

mqtt学习记录

目录 1 匿名登录2 ⽤户名密码登录&#xff0c;配置接收的主题mosquitto 配置文件修改添加⽤户信息添加topic和⽤户的关系登录演示 3 遗嘱机制 1 匿名登录 ⾸先打开三个终端&#xff0c; 启动代理服务&#xff1a;mosquitto -v -v 详细模式 打印调试信息 默认占⽤&#xff1a;…

酒店管理系统哪家好?的修设备售后服务管理系统有什么用?

随着人们的生活水平不断提升&#xff0c;越来越多的人开始对五星级酒店的品质提出更高的要求。这些规模宏大、设施齐全的酒店&#xff0c;需要日常进行复杂的维护工作才能保持正常运转。然而&#xff0c;传统的人工维保早已无法满足信息化管理的需求。为了更好地管理这些设备设…

C++11并发与多线程笔记(3)线程传参详解,detach()大坑,成员函数做线程函数

C11并发与多线程笔记&#xff08;3&#xff09;线程传参详解&#xff0c;detach 大坑&#xff0c;成员函数做线程函数 1、传递临时对象作为线程参数1.1 要避免的陷阱11.2 要避免的陷阱21.3 总结 2、临时对象作为线程参数2.1 线程id概念2.2 临时对象构造时机抓捕 3、传递类对象…

电脑提示数据错误循环冗余检查怎么办?

有些时候&#xff0c;我们尝试在磁盘上创建分区或清理硬盘时&#xff0c;还可能会遇到这个问题&#xff1a;数据错误循环冗余检查。这是如何导致的呢&#xff1f;我们又该如何解决这个问题呢&#xff1f;下面我们就来了解一下。 导致冗余检查错误的原因有哪些&#xff1f; 数据…