万人同屏插件获取&体验Demo下载
万人同屏插件是一款通用的Unity性能优化插件,是实现千人同屏、万人同屏的必备插件,直击性能消耗痛点功能,将2D Spine和3D动画渲染性能十倍至百倍提升,Jobs多线程移动避让和索敌算法,以及兼容全平台的高性能渲染器。新手友好、简单易用,无ECS技术门槛,传统开发方式,同样适用于旧项目低成本性能优化。
万人同屏插件兼容Unity全平台,包括WebGL(2.0),微信/抖音小游戏等平台同样大幅性能翻倍
Unity万人同屏插件功能性能演示
Unity万人同屏集成方案Pro 支持微信小游戏
https://efunstudio.cn/
性能/功能/红蓝对抗测试Demo Web在线体验
https://assets.efunstudio.cn/
十万单位红蓝对抗 多兵种战斗 弹幕游戏项目模板
网页版在线测试性能:
正文
Unity海量粒子 掉血飘字动画 子弹特效 性能优化 PC 手游 微信小游戏帧数翻倍 终极性能 FPS优化技巧 Shader实现教程
前言
前面的博文中我们分享了如何实现一套通用的Unity项目性能优化插件,并在PC和移动平台使帧数提升数十倍。由于DOTS的Entities Graphics是通过BatchRendererGroup(简称BRG)接口封装实现,而WebGL暂不支持GraphicsBuffer、Compute Shader,因此BRG当前不兼容WebGL平台,也就导致了DOTS目前不支持Web平台,不能用于开发微信小游戏。本文将分享如何支持Web平台高性能合批渲染,以及如何对海量粒子特效、飘字动画、子弹弹幕等游戏特效性能优化。
WebGL高性能渲染
如何解决DOTS不兼容Web平台的痛点呢?虽然Web平台不支持多线程和Burst,但Jobs代码仍然能以单个工作线程正常工作,Entities系统也能正常运作。也就意味着我们只需要解决Entities Graphics不支持Web平台的渲染问题即可。
那我们就有两种解决方案,一是使用WebGL平台支持的图形接口替代,如Graphics.RenderMeshInstanced。二是参考团结引擎实现GPU Resident Drawer兼容Web平台的方法,针对小游戏不支持SSBO和Compute Shader的限制,使用Texture存储Renderer数据。这里我们免费分享方法一的实现,给大家介绍如何使用Graphics.RenderMeshInstanced接口自定义WebGLGraphicsSystem无感知接管Entities Graphics System,从而实现Web平台的GPU Instancing高性能合批渲染。
在Unity万人同屏插件里我们已经使用Entities Graphics API高度封装了ECSGraphics渲染器,为了保持接口无感知兼容Web平台,只需定义一个WebGLGraphicsSystem的Job System代码,仅在WebGL时强制停用Entities Graphics System,启动我们自定义的WebGLGraphicsSystem。
#if UNITY_WEBGL// && !UNITY_EDITOR
var graphicsSystem = ECSWorld.GetExistingSystemManaged<PresentationSystemGroup>();
ECSWorld.GetExistingSystemManaged<SimulationSystemGroup>().RemoveSystemFromUpdateList(graphicsSystem);
var entitiesGraphicsSystem = ECSWorld.GetExistingSystemManaged<EntitiesGraphicsSystem>();
ECSWorld.GetExistingSystemManaged<SimulationSystemGroup>().RemoveSystemFromUpdateList(entitiesGraphicsSystem);
var webglRenderSystem = ECSWorld.GetExistingSystemManaged<WebGLGraphicsSystem>();
if (webglRenderSystem == null)
{
webglRenderSystem = ECSWorld.CreateSystemManaged<WebGLGraphicsSystem>();
ECSWorld.GetExistingSystemManaged<SimulationSystemGroup>().AddSystemToUpdateList(webglRenderSystem);
}
#endif
首先RenderMeshInstanced(RenderParams rparams, Mesh mesh, int submeshIndex, NativeArray<T> instanceData, int instanceCount = -1, int startInstance = 0)我们可以了解到WebGL下合批渲染所需的数据,其中instanceData就是我们需要传递给GPU的渲染数据。
根据不同Mesh和Material拆分渲染批次,通过MaterialPropertyBlock为每个渲染物设置不同的材质参数值。
var matBlock = new MaterialPropertyBlock();
m_BatchesRenderParams.Add(batchKey, new RenderParams(ECSGraphicsComponent.Instance.GetMaterialFromInfo(batchKey.x))
{
shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On,
receiveShadows = false,
lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off,
matProps = matBlock
});
通过遍历Entities组织矩阵数据(RenderMeshInstanced接口所需渲染数据参数)
m_Query = this.GetEntityQuery(ComponentType.ReadOnly<LocalToWorld>(), ComponentType.ReadOnly<MaterialMeshInfo>());
m_BatchesMatrices = new NativeHashMap<int2, NativeList<Matrix4x4>>(16, Allocator.Persistent);
尽管WebGL平台不支持Jobs多线程,使用IJobsEntity遍历仍比直接Query遍历性能高。由于已知是单线程执行,因此这里也就不必考虑Native容器的并行风险。
unsafe partial struct RenderDataJob : IJobEntity
{
[NativeDisableContainerSafetyRestriction][WriteOnly] public NativeHashMap<int2, NativeList<Matrix4x4>> BatchesMatrices;
#if ENABLE_COLOR
[NativeDisableContainerSafetyRestriction][WriteOnly] public NativeHashMap<int2, NativeList<float4>> BatchesColors;
[ReadOnly] public NativeHashMap<int2, float4> DefaultColors;
#endif
void Execute([EntityIndexInChunk] int index, ECSNodeAspect node)
{
float4x4 matrix = node.Transform.ValueRO.Value;
var meshInfo = node.MeshInfo.ValueRO;
int2 batchKey = new int2(meshInfo.Material, meshInfo.Mesh);
BatchesMatrices[batchKey].Add(matrix);
#if ENABLE_COLOR
BatchesColors[batchKey].Add(node.Color.IsValid ? node.Color.ValueRO.Value : DefaultColors[batchKey]);
#endif
}
当然,我们还必须支持视锥裁剪,以节省开销。
检测Entity包围盒是否在相机视口
Unity.Rendering.FrustumPlanes.IntersectResult Intersect(NativeArray<Plane> cullingPlanes, AABB a)
{
float3 m = a.Center;
float3 extent = a.Extents;
var inCount = 0;
int length = cullingPlanes.Length;
for (int i = 0; i < length; i++)
{
float3 normal = cullingPlanes[i].normal;
float dist = math.dot(normal, m) + cullingPlanes[i].distance;
float radius = math.dot(extent, math.abs(normal));
if (dist + radius <= 0)
return Unity.Rendering.FrustumPlanes.IntersectResult.Out;
if (dist > radius)
{
inCount++;
}
}
return (inCount == length) ? Unity.Rendering.FrustumPlanes.IntersectResult.In : Unity.Rendering.FrustumPlanes.IntersectResult.Partial;
}
若在视口外侧跳过将此Entity的矩阵数据添加进渲染列表
Unity.Rendering.FrustumPlanes.IntersectResult intersectResult = Intersect(cullingPlanes, aabb);
if (intersectResult == Unity.Rendering.FrustumPlanes.IntersectResult.Out) return;
海量飘字动画特效优化

使用Shader实现,通过二阶贝塞尔曲线得到飘字的运动轨迹,对顶点偏移Vertex Offset来实现动画效果。
t * (1f - t) * pA + Mathf.Pow(t, 2) * pB;

Shader中绘制动态数字参考之前分享的教程。百万血条图文HUD优化:https://www.bilibili.com/video/BV1oqjPzhEs7/

海量子弹优化
通过对顶点偏移确定子弹移动距离,通过UV偏移对子弹贴图采样绘制得到子弹移动效果,为避免不同视角下子弹面片穿帮,还需要实现绕Z轴旋转的Billboard。

海量粒子特效优化

两种简单的方式,一是shader 帧动画+Billboard,足以应对2D粒子。二是使用Animation记录关键帧替代ParticleSystem,然后直接使用GPU动画插件转换为GPU动画。
以下是ASE中帧动画实现方法:





2万+

被折叠的 条评论
为什么被折叠?



