当前位置: 首页 > news >正文

Unity技术学习:渲染大量物体的解决方案,外加RenderMesh、RenderMeshInstanced、RenderMeshIndirect的简单使用

叠甲:本人比较菜,如果哪里不对或者有认知不到的地方,欢迎锐评(不玻璃心)!

导师留了个任务,渲染大量的、移动的物体。

寻找解决方案:

当时找了几个解决方案:

静态批处理:

要求:字如其意,必须是静态的,不能移动旋转缩放等等。

原理:Unity 会自动将相同材质球的物体合并到一个大的mesh里,提取顶点和索引数据放到共享的顶点缓冲和索引缓冲

并不减少drawcall,减少了渲染状态的设置。

缺点;在处理的时候会把相同模型的顶点信息都保存,并变换到世界空间。会导致包体和运行时体积变大。

哪怕勾上,上万个物体就寄了(虽然灵活度很高),只需要勾上就可以了。

动态批处理:

要求:不超过300个顶点(不超过总计900个属性),不包含镜像的缩放,材质一样,物体lightmap指向的位置一样

原理:运行时将所有共享同一材质的模型顶点信息变换到世界空间,一次drawcall 绘制多个模型。

内置渲染管线:

URP:(HDRP没有)

真正意义上的减少drawcall。

缺点:会有额外的cpu性能消耗,而且新一代的图形api在批次间消耗降低,反而使用动态批处理可能更差。

对于打断动态批处理的情况:

  • 若优先级低,会自动禁用:SRPBatch>静态批处理>GPUinstance>动态批处理
  • 多passShader自动禁用动态批处理
  • shadow casters可以使用不用材质,但shadow casters pass使用的参数是相同的就不会禁用动态批处理。
  • 向前渲染路径,如果一个物体接受了多个光照,会为每个per pixel light 产生提交和绘制,从而附加多个Pass导致无法合批。

SRPBatch:

原理:简化了批处理的渲染循环,可以加速相同着色器变体的多种材质在场景中的CPU渲染速度。

要求:Shader中的变体一致,对象不可以是粒子或蒙皮网格(好像最新版已经支持蒙皮了),位置不相邻且中间夹杂着不同shader或不同变体的其他物体,不会进行同批处理。

这个详细阅读一下官方文档,这个本文不做深入研究,但注意:srpBatch与GPUinstance不兼容!(参考上方的优先级)

Unity - 手册:可编写脚本的渲染管线批处理程序

URP:(HDRP默认开启)

缺点:

降低setpass call的消耗

GPUInstance:

要求:同材质、同mesh,默认材质仅支持transform的改变。支持meshRenderer和Graphics.RenderMesh,不支持SkinMeshRenderer。缩放为负的情况下会失效,代码动态改变材质变量就不算同一材质,就会失效,但是可以通过将颜色变化等变量加入常量缓冲区实现。仅支持一个实时光,需要多个光源只能切换到延迟渲染路径。

原理:仅绘制一个,剩下的“复制”。

只需要勾上材质里的GPUInstancing、当使用该材质时,就会合批。但经实测,仅支持完全相同的物体,方块和球使用相同材质并不会合批。而且:不要对顶点少于 256 个的网格使用 GPU 实例化(官方文档提醒)。

减少了draw call 

灵活度也挺高的,但是对于超过上限(貌似是1023?)的物体数量,依旧拉胯,batch数虽然不是猛增但是帧率感人(后续可能会做性能分析)

RenderMeshIndirect:

原理:手动GPUInstance多个对象。

跑了一下示例,woc,吊的一批,就是每个块可控性比较差(也可能是我比较菜,目前已知的就是:使用动画贴图、时间来控制每个单体的行为;而且没有碰撞、单独操作难度高:需要在shader里写改变顶点位置),但是,一次性绘制10w,600帧没有一点儿压力。1000w个能跑十几帧(视觉效果拉满)。而且支持光照和阴影。

所以接下来我从 RenderMesh到RenderMeshInstanced再到RenderMeshIndirect的API给说一下,并且跑一下示例:

小总结: 

(抄过来的,侵删)

 RenderMesh:

使用给定的渲染参数渲染网格(就单纯绘制网格,没有任何合批)。

public static void RenderMesh(ref RenderParams rparams, Mesh mesh, int submeshIndex, Matrix4x4 objectToWorld, Nullable<Matrix4x4> prevObjectToWorld = null);

rparams:

camera用于渲染的相机。如果设置为 null(默认),则为所有摄像机渲染。
layer用于渲染的图层。要使用的图层。
lightProbeProxyVolume用于渲染的光探针代理体积 (LPPV)。
lightProbeUsage光探头使用的类型。
material用于渲染的材质。
matProps用于渲染的材质属性。
motionVectorMode用于渲染的运动矢量模式。
receiveShadows描述渲染的几何体是否应接收阴影。
reflectionProbeUsage用于渲染的反射探针的类型。
rendererPriority渲染器优先级。
renderingLayerMask用于渲染的渲染器图层蒙版。
shadowCastingMode描述几何体是否应投射阴影。
worldBounds定义几何体的世界空间边界。用于对渲染的几何体进行剔除和排序。

MaterialPropertyBlock-CSDN博客

submeshIndex:

当网格体包含多个材质(子网格)时,子网格体 Unity 的索引会呈现。对于具有单个材质的网格,请使用值 0(这个是啥子,冲浪了好久,但是找不到)

objectToWorld:

Unity 用于将网格从对象转换为世界空间的转换矩阵。

prevObjectToWorld:

Unity 使用前面的帧变换矩阵来计算网格的运动矢量。

using UnityEngine;public class ExampleClass : MonoBehaviour
{public Material material;public Mesh mesh;void Update(){RenderParams rp = new RenderParams(material);rp.camera = Camera.main;rp.layer = 6;rp.matProps = new MaterialPropertyBlock();rp.worldBounds = new Bounds(new Vector3(0,0,0),new Vector3(1,1,1));for (int i = 0; i < 10; ++i)Graphics.RenderMesh(rp, mesh, 0, Matrix4x4.Translate(new Vector3(-6.5f + i, 0.0f, 5.0f)));}
}

RenderMeshInstanced:

使用 GPU 实例渲染网格的多个实例(但是不会传入命令)。

public static void RenderMeshInstanced(RenderParams rparams, Mesh mesh, int submeshIndex, NativeArray<T> instanceData, int instanceCount = -1, int startInstance = 0);

instanceData:

用于呈现实例的实例数据数组。

instanceCount:

要呈现的实例数。当此参数为 -1(默认值)时,Unity 会呈现从数组到末尾的所有实例。

startInstance:

要呈现的第一个实例。

using UnityEngine;public class ExampleClass : MonoBehaviour
{public Material material;public Mesh mesh;const int numInstances = 1000;struct MyInstanceData{public Matrix4x4 objectToWorld;public float myOtherData;public uint renderingLayerMask;};void Update(){RenderParams rp = new RenderParams(material);MyInstanceData[] instData = new MyInstanceData[numInstances];for (int i = 0; i < numInstances; ++i){instData[i].objectToWorld = Matrix4x4.Translate(new Vector3(-4.5f + i*3, 0.0f, 5.0f));instData[i].renderingLayerMask = (i & 1) == 0 ? 1u : 2u;}Graphics.RenderMeshInstanced(rp, mesh, 0, instData);}
}

RenderMeshIndirect:

与RenderMeshInstanced类似,但是可以从command buffer中获取命令参数,并且只需要调用一次即可执行。

public static void RenderMeshIndirect(ref RenderParams rparams, Mesh mesh, GraphicsBuffer commandBuffer, int commandCount = 1, int startCommand = 0); 

commandBuffer:

 把命令打包的buffer

using UnityEngine;public class ExampleClass : MonoBehaviour
{public Material material;public Mesh mesh;GraphicsBuffer commandBuf;GraphicsBuffer.IndirectDrawIndexedArgs[] commandData;const int commandCount = 1;public uint   num=100;void Start(){commandBuf = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, commandCount, GraphicsBuffer.IndirectDrawIndexedArgs.size);commandData = new GraphicsBuffer.IndirectDrawIndexedArgs[commandCount];}void OnDestroy(){commandBuf?.Release();commandBuf = null;}void Update(){RenderParams rp = new RenderParams(material);rp.worldBounds = new Bounds(Vector3.zero, 10000 * Vector3.one); // use tighter bounds for better FOV cullingrp.matProps = new MaterialPropertyBlock();rp.matProps.SetMatrix("_ObjectToWorld", Matrix4x4.Translate(new Vector3(0f, 0, 0)));commandData[0].indexCountPerInstance = mesh.GetIndexCount(0);commandData[0].instanceCount = num;commandBuf.SetData(commandData);Graphics.RenderMeshIndirect(rp, mesh, commandBuf, commandCount);}
}

示例shader: 

Shader "ExampleShader"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#define UNITY_INDIRECT_DRAW_ARGS IndirectDrawIndexedArgs#include "UnityIndirect.cginc"struct v2f{float4 pos : SV_POSITION;float4 color : COLOR0;};uniform float4x4 _ObjectToWorld;v2f vert(appdata_base v, uint svInstanceID : SV_InstanceID){InitIndirectDrawArgs(0);v2f o;uint cmdID = GetCommandID(0);uint instanceID = GetIndirectInstanceID(svInstanceID);float timeOffset = _Time.y * 0.5; // 调整运动速度float xOffset = sin(timeOffset + instanceID *5) * 10; // x方向偏移float yOffset = cos(timeOffset + instanceID * 7) * 10; // y方向偏移float zOffset = sin(timeOffset + instanceID * 9) *10; // z方向偏移float4 wpos = mul(_ObjectToWorld, v.vertex + float4(instanceID%1000+ xOffset, cmdID+ yOffset, instanceID / 1000 *3+zOffset, 0));o.pos = mul(UNITY_MATRIX_VP, wpos);o.color = float4(cmdID & 1 ? 0.0f : 1.0f, cmdID & 1 ? 1.0f : 0.0f, instanceID / float(GetIndirectInstanceCount()), 0.0f);return o;}float4 frag(v2f i) : SV_Target{return i.color;}ENDCG}}
}

100w个方块能跑150帧,cool!

相关文章:

Unity技术学习:渲染大量物体的解决方案,外加RenderMesh、RenderMeshInstanced、RenderMeshIndirect的简单使用

叠甲&#xff1a;本人比较菜&#xff0c;如果哪里不对或者有认知不到的地方&#xff0c;欢迎锐评&#xff08;不玻璃心&#xff09;&#xff01; 导师留了个任务&#xff0c;渲染大量的、移动的物体。 寻找解决方案&#xff1a; 当时找了几个解决方案&#xff1a; 静态批处…...

[数据概念|方案实操][最新]数据资产入表4月速递

“ 在各地数据资产变现“热辣滚烫”” 国家数据局全国数据工作会议前后&#xff0c;数据资源“入表”的尝试在各地持续热火朝天地展开&#xff0c;多地实现数据资产入表和利用数据资产进行融资实现“零的突破”。 我们今天就把4月前后的案例做一个小结&#xff0c;之前的案例大…...

C++中使用Multimap和Vector管理和展示数据

一&#xff1a; 在本文中&#xff0c;我们将探讨如何在C中使用vector和multimap容器来管理一个简单的员工数据系统。我们将创建一个员工类&#xff0c;随机生成员工数据&#xff0c;将员工分组&#xff0c;并展示各组员工的详细信息。此示例展示了C标准模板库&#xff08;STL&…...

Java---类和方法的再学习

上一篇主要介绍了面向对象的思想以及内存实现&#xff0c;关于类与对象感觉写的不够好&#xff0c;因此才会有这一篇作为补充&#xff1b; 一&#xff1a;类与对象 &#xff08;1&#xff09;类 一些相同属性和行为的事物的统称&#xff0c;比较广泛、抽象&#xff0c;比如…...

C语言每日一练(12、水仙花数)

在编程的领域中&#xff0c;我们常常会遇到一些有趣而富有挑战性的问题。今天&#xff0c;让我们一起来探讨一个经典的编程题目——打印出所有的“水仙花数”。 所谓“水仙花数”&#xff0c;是指一个三位数&#xff0c;其各位数字的立方和等于该数本身。例如&#xff0c;153 …...

HTML5实现酷炫个人产品推广、工具推广、信息推广、个人主页、个人介绍、酷炫官网、门户网站模板源码

文章目录 1.设计来源1.1 主界面1.2 我的产品界面1.3 关于我们界面1.4 照片墙界面1.5 发展历程界面1.6 优秀人才界面1.7 热门产品界面1.8 联系我们界面 2.灵活调整模块3.效果和源码3.1 动态效果3.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.c…...

系统如何做好安全加固?

一、Windows系统 Windows系统出厂时&#xff0c;微软为了兼容性&#xff0c;默认并未对系统安全做严格的限制&#xff0c;因此还需要做一些基本的安全加固&#xff0c;方可防止黑客入侵。 1、系统补丁更新 为什么要更新系统补丁&#xff1f;很多人感觉漏洞更新没必要&#x…...

对NI系统和PLC系统的应用比较

以下是对这两种系统的基本比较&#xff1a; 1. 设计和功能性 NI系统&#xff1a; 通常基于LabVIEW等软件平台&#xff0c;提供强大的数据采集、信号处理和图形界面开发能力。高度模块化和可扩展&#xff0c;支持各种传感器和信号类型。适合进行复杂的数据分析和高级控制算法的…...

微服务架构中的挑战及应对方式:Outbox 模式

使用 Outbox 模式保持微服务数据一致性 在一个由许多小型服务组成的系统中保持数据一致性是困难的&#xff0c;因为它们分散在各处。以下是一些常见问题以及如何处理它们的方法&#xff1a;当服务发送消息时&#xff0c;同时更新数据库和发送消息是棘手的问题。 在微服务中发出…...

使用Docker安装MySQL5.7.36

拉取镜像并查看 docker pull mysql:5.7.36拉取成功后查看&#xff08;非必须&#xff09; docker images创建并设置宿主机 mysql 配置文件目录和数据文件目录 创建相关文件夹将容器中的mysql数据保存到本地&#xff0c;这样即使容器被删除&#xff0c;数据也不会丢失。 mkd…...

【PyTorch】6-可视化(网络结构可视化、CNN可视化、TensorBoard、wandb)

PyTorch&#xff1a;6-可视化 注&#xff1a;所有资料来源且归属于thorough-pytorch(https://datawhalechina.github.io/thorough-pytorch/)&#xff0c;下文仅为学习记录 6.1&#xff1a;可视化网络结构 Keras中可以调用model.summary()的API进行模型参数可视化 torchinfo…...

C++容器——map和pair对组

pair&#xff08;对组&#xff09; 是一种模板类&#xff0c;允许将两个不同类型的值组合在一起。它由两个数据成员first和second组成&#xff0c;分别用来保存这两个值。 头文件 加头文件 #include<utility> 对于 C11 及以上标准&#xff0c;pair 类型可以在不包含头…...

MVC和DDD的贫血和充血模型对比

文章目录 架构区别MVC三层架构DDD四层架构 贫血模型代码示例 充血模型代码示例 架构区别 MVC三层架构 MVC三层架构是软件工程中的一种设计模式&#xff0c;它将软件系统分为 模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Contro…...

如何利用AI提高内容生产效率?

如何利用AI提高内容生产效率? 简介&#xff1a;探讨如何通过AI技术提升内容生产的效率和质量。 方向一&#xff1a;自动化内容生成 自动化内容生成是一种利用人工智能技术来自动创建文本、图像、音频等内容的方法。 以下是一些常见的自动化内容生成方式&#xff1a; 基于…...

C++ stack、queue以及deque

1、stack和queue常用接口 严格来说栈和队列的实现是容器适配器 1、常用接口&#xff1a; 栈&#xff1a;top、push、pop、size、emptystack - C Reference (cplusplus.com) 队列&#xff1a;top、push、pop、swap、size、emptyqueue - C Reference (cplusplus.com) 2、deque&a…...

科沃斯,「扫地茅」荣光恐难再现

作者 | 辰纹 来源 | 洞见新研社 科沃斯恐怕已经很难再回到被市场誉为“扫地茅”时的荣光了。 不久前&#xff0c;科沃斯发布2023年财报&#xff0c;报告期内营业收入155亿&#xff0c;同比仅增长1.16%&#xff0c;归母净利润6.12亿元&#xff0c;同比下降63.96%&#xff0c;直…...

双向BFS算法学习

双向BFS算法学习 推荐练习题 力扣“127”题&#xff1a;单词接龙 “752”题&#xff1a;打开轮盘锁 这里推荐一篇力扣题解 双向BFS 这里使用打开轮盘锁的题干进行举例&#xff1a; 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字&#xff1a; ‘0’, ‘1’, ‘2’,…...

C++从入门到精通---模版

文章目录 泛型编程函数模版模版参数的匹配原则类模版类模版的定义格式类模版的实例化 总结 泛型编程 泛型编程是一种编程范式&#xff0c;旨在实现通用性和灵活性。它允许在编写代码时使用参数化类型&#xff0c;而不是具体的类型&#xff0c;从而使代码更加灵活和可重用。 在…...

Unity数据持久化之Json

Json概述 Json是什么? 全称:JavaScript对象简谱(JavaScript Object Notation) Json是国际通用的一种轻量级的数据交换格式 主要在网络通讯中用于传输数据,或本地数据存储和读取 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率 我们一般使用Json文件来…...

LeetCode 35.搜索插入位置

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2 示例 2: 输入…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...