GPU Dissolve(GPU 消散)学习GPU Instancing

news2024/11/18 19:51:26

一:摘要

 

        通过制作一个模型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);

        //vertices
        GPUDrawMat.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;
        //等边三角形三点UV
        Vector2[] 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.coloradmin.cn/o/886126.html

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

相关文章

【100天精通python】Day36:GUI界面编程_高级功能操作和示例

专栏导读 专栏订阅地址&#xff1a;https://blog.csdn.net/qq_35831906/category_12375510.html 一、GUI 高级功能 1 自定义主题和样式 自定义主题和样式可以让你的GUI应用程序在外观方面更加出色。在使用Tkinter时&#xff0c;你可以使用ttkthemes库来应用不同的主题和样式。…

一文搞懂Spring是如何解决Bean循环依赖的?

一.什么是Bean循环依赖 循环依赖是指Bean对象循环引用&#xff0c;是两个或多个Bean之间相互持有对方的引用&#xff0c;循环依赖有2中表现形式 第一种相互依赖&#xff0c;就是A依赖B&#xff0c;B又依赖A 第二种是自我依赖&#xff0c;就是A依赖自己形成自我依赖 对象引用…

上山取石&#xff0c;下江取锦。诗人秋浦啸傲&#xff0c;新津樵唱。江南山水秀美&#xff0c;水乡文化流长。而水&#xff0c;则是这些山水风景的灵魂所在。 水&#xff0c;雨露滋润万物生长的泉源。 它潺潺流淌于山间溪涧&#xff0c;涓涓细流化成了青山的眼泪。水顺势而下&a…

Unity如何制作声音控制条(控制音量大小)

一&#xff1a;UGUI制作 1. 首先在【层级】下面创建UI里面的Slider组件。设置好它对应的宽度和高度。 2.调整Slider滑动条的填充颜色。一般声音颜色我黄色&#xff0c;所以我们也调成黄色。 我们尝试滑动Slider里面的value。 a.滑动前。 b.滑动一半。 c.滑动完。 从以上滑动va…

Openai中的tokens怎么估计

大规模语言模型&#xff08;LLM&#xff09;的出现给自然语言处理领域带来了变革的可能性&#xff0c;Openai开放了chatgpt的API&#xff0c;方便了开发人员使用LLM的推理能力&#xff0c;注册时赠送5美元的使用额度&#xff0c;有效期3个月。 如果想便捷的使用chatgpt的API&a…

判断推理

六哥爱学习呀 产品经理 不是说我努力学习我就一定可以通过考试&#xff0c;所以是推不出&#xff0c;类似数学中充分必要性 8 回复 发布于 2019-08-07 16:28 官方解析&#xff1a; 当丙的范围足够大时&#xff0c;可能与甲相交或完全包含甲&#xff0c;在此情况下&#xff0c;有…

【数据结构】顺序队列模拟实现

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

ansible 修改远程主机nginx配置文件

安装ansible brew install ansible 或者 pip3 install ansible 添加远程主机 设置秘钥 mac登录远程主机 ssh -p 5700 root192.168.123.211 ssh localhost #设置双机信任 ssh-kyegen -t rsa #设置主机两边的ssh配置文件 vi /etc/ssh/sshd_config/ PermitRootL…

C++写文件,直接写入结构体

C写文件&#xff0c;直接写入结构体 以前写文件都是写入字符串或者二进制再或者就是一些配置文件&#xff0c;今天介绍一下直接写入结构体&#xff0c;可以在软件参数较多的时候直接进行读写&#xff0c;直接将整个结构体写入和读取&#xff0c;看代码&#xff1a; #include&…

除了ping你用过traceroute吗

一、检查两个计算机间的网络是否通 在检查两个机器的网络通不通&#xff0c;我们经常使用的命令是ping 但是当ping不通时&#xff0c;我们就不知道网络是哪里不通了&#xff0c;只能找网管排查。 这里介绍一个检查网络工具 traceroute 二 、TraceRoute是什么 TraceRoute的中文…

驱动 - 20230816

练习 1.编写LED灯的驱动&#xff0c;可以控制三个灯&#xff0c;应用程序中编写控制灯的逻辑&#xff0c;要使用自动创建设备节点机制 驱动头文件 ledHead.h #ifndef __HEAD_H__ #define __HEAD_H__#define PHY_GPIOE_MODER 0X50006000 #define PHY_GPIOE_ODR 0X50006014 #d…

toB营销如何从品牌营销转向获客营销?

“解构纷享新营销&#xff0c;赋能用户新增长”&#xff0c;这是2023年下半年&#xff0c;纷享销客践行“以客户成功定义成功”价值观&#xff0c;针对企业用户市场营销领域的全国巡回研讨会&#xff0c;希望把纷享销客在成长路上经历的、收获的经验、踩过的“坑”与用户共享&a…

Postman如何做接口测试:什么?postman 还可以做压力测试?

我们都知道&#xff0c; postman 是一款很好用的接口测试工具。不过 postman 还可以做简单的压力测试&#xff0c;而且步骤只需要 2 步。 首先&#xff0c;打开 postman, 编写接口的请求参数。 然后&#xff0c;点击右下方的 runner 运行器&#xff0c;把需要测试的接口拖动到…

序列模型和循环网络

Sequence Modeling and Recurrent Networks Sequence modeling tasks 在以往的模型中&#xff0c;各个输入之间是独立分布的 x ( i ) x^{(i)} x(i) 之间是相互独立的&#xff0c;同样输出 y ( i ) y^{(i)} y(i)之间也是相互独立的。 但是在序列模型中&#xff0c;输入输出是…

应用开源框架平台,实现流程化办公!

如今&#xff0c;实现流程化办公&#xff0c;管理好数据资源是很多企业的共同想法。如果采用传统的办公方式显然无法实现这一愿望。利用开源框架平台&#xff0c;可以管理好数据资源&#xff0c;为企业提高办公协作效率&#xff0c;进入流程化办公。流辰信息是专业的低代码技术…

如何使用Python编写小游戏?

大家好&#xff0c;我是沐尘而生&#xff0c;如果你是一个热爱编程的小伙伴&#xff0c;又想尝试游戏开发&#xff0c;那么这篇文章一定能满足你的好奇心。不废话&#xff0c;让我们马上进入Python游戏开发的精彩世界吧&#xff01; Python游戏开发的魅力 编写小游戏不仅仅是锻…

Linux系统管理:虚拟机ESXi安装

目录 一、理论 1.VMware Workstation 2.VMware vSphere Client 3.ESXi 二、实验 1.ESXi 7安装 一、理论 1.VMware Workstation 它是一款专业的虚拟机软件&#xff0c;可以在一台物理机上运行多个操作系统&#xff0c;支持Windows、Linux等操作系统&#xff0c;可以模拟…

opencv-进阶05 手写数字识别原理及示例

前面我们仅仅取了两个特征维度进行说明。在实际应用中&#xff0c;可能存在着更多特征维度需要计算。 下面以手写数字识别为例进行简单的介绍。 假设我们要让程序识别图 20-2 中上方的数字&#xff08;当然&#xff0c;你一眼就知道是“8”&#xff0c;但是现在要让计算机识别…

lvs负载均衡集群(NAT模式)

lvs负载均衡集群&#xff1a; 1.什么是集群&#xff08;含义&#xff09;&#xff1a;就是将多台主机作为一个整体&#xff0c;对外提供相同的服务 2.集群使用在哪一个场景&#xff1a;高并发 并发量过大时候加服务器的方式就是向外扩展(横向扩展)&#xff0c;就是集群。 3…

HoudiniVex笔记_P24_ForceBasics力基础

原视频&#xff1a;https://www.youtube.com/playlist?listPLzRzqTjuGIDhiXsP0hN3qBxAZ6lkVfGDI Bili&#xff1a;Houdini最强VEX算法教程 - VEX for Algorithmic Design_哔哩哔哩_bilibili Houdini版本&#xff1a;19.5 1、什么是Force 本章主要讲重力、弹力、速度与质量、…