内容简介:【博物纳新】是UWA旨在为开发者推荐新颖、易用、有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目、前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性。很多时候,我们并不知道自己想要什么,直到某一天我们遇到了它。更多精彩内容请关注:lab.uwa4d.com现代图形API支持“纹理数组”,它是具有相同大小和格式纹理的数组。它们被着色器视为单个资源,对它们进行采样需要一个额外的坐标,指示要从中采样的数组元素。
【博物纳新】是UWA旨在为开发者推荐新颖、易用、有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目、前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性。很多时候,我们并不知道自己想要什么,直到某一天我们遇到了它。
更多精彩内容请关注:lab.uwa4d.com
一、前言
现代图形API支持“纹理数组”,它是具有相同大小和格式纹理的数组。它们被着色器视为单个资源,对它们进行采样需要一个额外的坐标,指示要从中采样的数组元素。
通常,纹理数组可用作纹理图集的替代,或者在对象使用一组相同大小纹理的其他情况,例如:同样几何形状的不同角色,各自有不同的贴图,或者该物体有多个不同属性的纹理贴图例如漫反射颜色、法线贴图、高光密度贴图等。
在上述情况下,我们需要使用多个纹理的时候,需要在绘制命令之前将所有的所需纹理绑定完毕。当我们还需要更新纹理对象本身时,每一次材质切换(类似glBindTexture)都会对性能产生一些影响。纹理数组可以将这些尺寸格式相同的纹理合并到一个集合,直接减少材质切换的次数,甚至可以降低DrawCall的数量,从而提高性能。Unity纹理数组就提供了这样的功能。
而对于具有不同贴图的粒子系统,就可以使用Texture2DArray来实现。今天介绍的这个开源库项目就提供了一个解决方案。
二、效果展示
三、使用方式、代码分析与简单原理
该项目提供了一些Demo以供学习。
1、载入相应的贴图,要求格式尺寸相同。并赋予TextureArray中的Textures数组。
TextureArray类用于新建Texture2DArray并读入像素数据。目前在Unity纹理数组中没有导入管道,必须在运行时或编辑器脚本中从代码创建。使用Graphics.CopyTexture对于将像素数据从常规2D纹理快速复制到纹理数组。将该纹理数组传递给GPUParticleSystem材质(Butterfly着色器)。
void Start () { //构造Texture2DArray array = new Texture2DArray(width, height, count, TextureFormat.RGB24, false); array.Apply(); material.SetTexture("_TextureArray", array); material.SetFloat("_Depth", count); Load(textures); } void Load (List<Texture2D> textures) { var candidates = textures.FindAll(tex => tex.format == TextureFormat.RGB24).ToList(); int cn = candidates.Count; for(int i = 0; i < count; i++) { //读取图片像素信息 Graphics.CopyTexture(textures[i % cn], 0, 0, array, current, 0); current = (current + 1) % count; } array.Apply(); material.SetTexture("_TextureArray", array); }
2、FboPingpong类继承System.IDisposable,便于实现资源释放,该类包含两张RenderTexture,用于实现双缓冲绘制。
GPUParticleProp类继承System.IDisposable,该类包含一个关键词和一个FboPingpong类,根据关键词向对应材质、着色器传递FboPingpong中的RT。
3、GPUParticleUpdaterController脚本用于控制效果切换,当演示效果时长达到规定时长或者按下”N“键,停止当前协程,新建协程调用Step(),激活下一个效果的GPUParticleUpdaterGroup中每一个GPUParticleUpdater。
IEnumerator Repeater () { yield return 0; while(true) { Step (); yield return new WaitForSeconds(current.Duration); } } void Step () { if(current != null) current.Deactivate(); current = groups[index % groups.Count]; current.Activate(); index++; }
4、GPUParticleUpdaterGroup中包含多个GPUParticleUpdater用于实现对应效果,GPUParticleUpdater类及其子类,接收GPUParticleProp类中的FboPingpong类的RT,将实现效果渲染到RT上。
例如:ApplyRotation子类接收RT。
public override void Render (GPUParticleSystem system) { var velocity = system.GetProp("_Velocity"); var rotation = system.GetProp("_Rotation"); if(velocity != null && rotation != null) { material.SetTexture(velocity.Key, velocity.FBO.ReadTex); material.SetTexture(rotation.Key, rotation.FBO.ReadTex); Blit(rotation.FBO, material); } }
渲染RT:
protected void Blit (FboPingpong fbo, Material mat, int pass = -1) { Graphics.Blit(null, fbo.WriteTex, mat, pass); fbo.Swap(); }
5、将上述得到的RT传递给GPUParticleSystem中的材质GPUParticleSystem材质(Butterfly着色器)。
GPUParticleSystem会在项目启动时生成一个全部是点的Mesh。
相关代码:
Mesh Build (int count = 10000) { var mesh = new Mesh(); int dcount = count * count; var vertices = new Vector3[dcount]; var uv = new Vector2[dcount]; var indices = new int[dcount]; for(int i = 0; i < dcount; i++) { int k = i; float tx = (1f * (k % count)) / count; float ty = (1f * (k / count)) / count; vertices[i] = Random.insideUnitSphere; uv[i] = new Vector2(tx, ty); indices[i] = i; } mesh.vertices = vertices; mesh.uv = uv; mesh.SetIndices(indices, MeshTopology.Points, 0); return mesh; }
在Update的时候,通过Butterfly着色器,根据点的位置绘制面片(Geometry Shader),渲染得到一个网格Mesh,获得效果。
GPUParticleSystem中绘制Mesh相关代码:
void Update () { updaters.ForEach(updater => { if(updater != null && updater.isActiveAndEnabled) { updater.Render(this); } }); props.ForEach(prop => { material.SetTexture(prop.Key, prop.FBO.ReadTex); }); Graphics.DrawMesh(mesh, transform.localToWorldMatrix, material, 0); }
Butterfly Shader中相关代码:
#pragma vertex vert #pragma geometry geom #pragma fragment frag [maxvertexcount(8)] void geom (point v2g IN[1], inout TriangleStream<g2f> triStream) { float halfS = 0.5f * _Size; float3 right = rotate_vector(float3(1, 0, 0), IN[0].rot) * halfS; float3 up = rotate_vector(float3(0, 1, 0), IN[0].rot) * halfS; float4 v[6]; //绘制一个面片 v[0] = float4(IN[0].pos + halfS * right - halfS * up, 1.0f); v[1] = float4(IN[0].pos + halfS * right + halfS * up, 1.0f); v[2] = float4(IN[0].pos - halfS * up, 1.0f); v[3] = float4(IN[0].pos + halfS * up, 1.0f); v[4] = float4(IN[0].pos - halfS * right - halfS * up, 1.0f); v[5] = float4(IN[0].pos - halfS * right + halfS * up, 1.0f);
想要拓展更多的效果可以构建GPUParticleUpdater的子类及对应材质和着色器,组成一个新的GPUParticleUpdaterGroup,并添加到GPUParticleUpdaterController类的groups中。
四、性能测试
由于所使用的着色器使用了Geometry Shader,故在安卓端受到很大的使用限制。但可以在Editor里的Frame Debug中查看DrawCall数量:
可以看出,借助Texture2DArray可以将DrawCall 降为1,这样可以大幅提升CPU端的渲染性能。
五、注意事项
当使用高版本的Unity打开项目,进行自动升级操作后:需要将Butterfly Shader第119行中的:
float4x4 vp = UnityObjectToClipPos(unity_WorldToObject);
改为:
float4x4 vp = UNITY_MATRIX_MVP;
今天的推荐就到这儿啦,或者它可直接使用,或者它需要您的润色,或者它启发了您的思路......
请不要吝啬您的 点赞和转发 ,让我们知道我们在做对的事。当然如果您可以留言给出宝贵的意见,我们会越做越好。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 在 Android Studio 里使用构建分析器提升构建性能
- 使用 Docker 构建
- 使用 webpack 构建应用
- 使用Dockerfile构建镜像
- 使用模式构建:总结
- 使用 AutoAI 自动构建模型
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。