内容简介:【博物纳新】是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 自动构建模型
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Haskell School of Music
Paul Hudak、Donya Quick / Cambridge University Press / 2018-10-4 / GBP 42.99
This book teaches functional programming through creative applications in music and sound synthesis. Readers will learn the Haskell programming language and explore numerous ways to create music and d......一起来看看 《The Haskell School of Music》 这本书的介绍吧!
JSON 在线解析
在线 JSON 格式化工具
Markdown 在线编辑器
Markdown 在线编辑器