Unity ProjectTiny在ECS运行时改变Mesh的UV来显示纹理

上篇创建的是LitMesh,这种Mesh要计算Normal和Tangent才能正确显示颜色,不然是黑色的。Tangent是根据Normal和UV计算出来的,所以改变一次UV就得计算一遍Tangent。目前还用不到光照,这篇就创建简单的UnlitMesh来做例子了。

还有Tiny运行时无法动态加载Material,也无法像GameObject那样将Material转成预制体引用起来(至少目前没看到类似的用法),所以只能在场景中建一个球体,挂上要用到的材质,然后在运行过程中创建好DynamicMesh后,把球体的材质赋值给DynamicMesh。

这篇的主要效果就是动态创建一个UnlitMesh,然后给它赋值一个带纹理的材质,再动态改变Mesh的UV,从而显示纹理上的不同部分。代码有点多,还有用Unity的ECS写代码容易出错,可能稍微改点代码就运行不起来了,这个要注意。

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny.Rendering;
using Unity.Tiny;

namespace TinyMesh {
	public struct MaterialHolder : IComponentData
    {
    }
    public struct ChangeMeshUV : IComponentData
    {
    }
    // [DisableAutoCreation]
    [UpdateBefore(typeof(ChangeMeshUVSystem))]
    public class CreateUnlitMeshSystem : SystemBase {
        EntityCommandBuffer ecb;

        protected override void OnCreate() {
            base.OnCreate();
            ecb = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>().CreateCommandBuffer();
        }

        protected override void OnUpdate() {
            Entities.WithAll<MaterialHolder>().ForEach((Entity e) => {
                var mat = EntityManager.GetComponentData<MeshRenderer>(e).material;
                var unlitMesh = ecb.CreateEntity(); // ForEach里边只能用EntityCommandBuffer来创建Mesh,用EntityManager会报错
                // 添加储存顶点数据的Buffer
                var vertex = ecb.AddBuffer<DynamicSimpleVertex>(unlitMesh); // 不需要光照的SimpleVertex
                // 添加储存顶点下标的Buffer
                var index = ecb.AddBuffer<DynamicIndex>(unlitMesh);
                vertex.Capacity = 4; // buffer的总大小,可以不用跟ResizeUninitialized的设置一样,但必须大于等于
                vertex.ResizeUninitialized(4); // 把buffer长度设置为4,且不初始化每个数据的值(里边的值是乱的)
                index.Capacity = 6;
                index.ResizeUninitialized(6);
                DynamicMeshData dmd = new DynamicMeshData{
                    Dirty = true,
                    IndexCapacity = index.Capacity,
                    VertexCapacity = vertex.Capacity,
                    NumIndices = index.Length,
                    NumVertices = vertex.Length,
                    UseDynamicGPUBuffer = true
                };

                MeshBounds mb;
                var org = new float3(-0.5f, -0.5f, 0);
                var du = new float3(1, 0, 0);
                var dv = new float3(0, 1, 0);
                var destVertices = vertex.AsNativeArray().Reinterpret<DynamicSimpleVertex, SimpleVertex>();
                var destIndices = index.AsNativeArray().Reinterpret<DynamicIndex, ushort>();
                MeshHelper.FillPlaneMesh(destVertices, destIndices, org, du, dv, out mb.Bounds);
                unsafe {
                    int n = vertex.Length;
                    SimpleVertex *vp = (SimpleVertex *)vertex.GetUnsafePtr();
                    for (int i = 0; i < n; i++) {
                        vp[i].Color = new float4(1, 0, 0, 1);
                    }
                }

                // 添加各种渲染UnlitMesh必须用到的组件
                ecb.AddComponent<DynamicMeshData>(unlitMesh, dmd); // 动态网格数据,这种可以在运行时改顶点数据的。还有一种是固定了顶点数据的,无法在运行时改数据!
                ecb.AddComponent<MeshRenderer>(unlitMesh, new MeshRenderer{ // 网格渲染组件
                    mesh = unlitMesh,
                    material = mat,
                    startIndex = 0,
                    indexCount = dmd.NumIndices
                });
                ecb.AddComponent(unlitMesh, new SimpleMeshRenderer()); // 这个组件用来表明该Mesh是不支持光照的网格
                ecb.AddComponent(unlitMesh, mb); // Mesh的包围盒
                ecb.AddComponent<Translation>(unlitMesh, new Translation { Value = float3.zero });
                ecb.AddComponent<Rotation>(unlitMesh, new Rotation { Value = quaternion.RotateX(179) });
                ecb.AddComponent<LocalToWorld>(unlitMesh, new LocalToWorld { Value = float4x4.Translate(float3.zero) });
                ecb.AddComponent(unlitMesh, new ChangeMeshUV());
                ecb.SetComponent(unlitMesh, dmd);
                
            }).WithoutBurst().Run();

            // 在Entities.ForEach外面可以用EntityManager创建Entity
            var unlitMaterial = CreateUnlitMaterial();
            var _unlitMesh = CreateUnlitMesh(unlitMaterial);
            EntityManager.SetComponentData<Translation>(_unlitMesh, new Translation{Value = new float3(-2, 1, 0)});
            EntityManager.AddComponentData(_unlitMesh, new DemoSpinner { spin =  math.normalize(new quaternion(new float4(1, 0, 0, 1))) });

            // 销毁该System,避免重复创建。在OnCreate中无法调用EntityManager创建Entity的
            World.GetExistingSystem<SimulationSystemGroup>().RemoveSystemFromUpdateList(this);
            World.DestroySystem(this);
        }

        // 注意:该接口不能在Entities.ForEach中调用
        Entity CreateUnlitMesh(Entity unlitMaterial) {
            var unlitMesh = EntityManager.CreateEntity();
            // 添加储存顶点数据的Buffer
            EntityManager.AddBuffer<DynamicSimpleVertex>(unlitMesh); // 不需要光照的SimpleVertex
            // 添加储存顶点下标的Buffer
            EntityManager.AddBuffer<DynamicIndex>(unlitMesh);
            var vertex = EntityManager.GetBuffer<DynamicSimpleVertex>(unlitMesh);
            var index = EntityManager.GetBuffer<DynamicIndex>(unlitMesh);
            vertex.Capacity = 4; // buffer的总大小,可以不用跟ResizeUninitialized的设置一样,但必须大于等于
            vertex.ResizeUninitialized(4); // 把buffer长度设置为4,且不初始化每个数据的值(里边的值是乱的)
            index.Capacity = 6;
            index.ResizeUninitialized(6);
            DynamicMeshData dmd = new DynamicMeshData{
                Dirty = true,
                IndexCapacity = index.Capacity,
                VertexCapacity = vertex.Capacity,
                NumIndices = index.Length,
                NumVertices = vertex.Length,
                UseDynamicGPUBuffer = true
            };

            MeshBounds mb;
            var org = new float3(-0.5f, -0.5f, 0);
            var du = new float3(1, 0, 0);
            var dv = new float3(0, 1, 0);
            var destVertices = vertex.AsNativeArray().Reinterpret<DynamicSimpleVertex, SimpleVertex>();
            var destIndices = index.AsNativeArray().Reinterpret<DynamicIndex, ushort>();
            // 手动设置顶点数据
            destVertices[0] = new SimpleVertex { Position = org, Color = new float4(1, 0, 0, 1), TexCoord0 = new float2(0, 0) };
            destVertices[1] = new SimpleVertex { Position = org + du, Color = new float4(0, 1, 0, 1), TexCoord0 = new float2(1, 0) };
            destVertices[2] = new SimpleVertex { Position = org + du + dv, Color = new float4(0, 0, 1, 1), TexCoord0 = new float2(1, 1) };
            destVertices[3] = new SimpleVertex { Position = org + dv, Color = new float4(0.5f, 0.5f, 0.5f, 1), TexCoord0 = new float2(0, 1) };
            destIndices[0] = 0; destIndices[1] = 1; destIndices[2] = 2;
            destIndices[3] = 2; destIndices[4] = 3; destIndices[5] = 0;
            mb.Bounds = MeshHelper.ComputeBounds(destVertices);

            // 调用接口设置顶点数据,颜色默认是白色
            // MeshHelper.FillPlaneMesh(destVertices, destIndices, org, du, dv, out mb.Bounds);
            // unsafe { // 可以这样重新设置顶点颜色
            //     int n = vertex.Length;
            //     SimpleVertex *vp = (SimpleVertex *)vertex.GetUnsafePtr();
            //     for (int i = 0; i < n; i++)
            //         vp[i].Color = new float4(1, 0, 0, 1);
            // }

            // 添加各种渲染UnlitMesh必须用到的组件
            EntityManager.AddComponentData<DynamicMeshData>(unlitMesh, dmd); // 动态网格数据,这种可以在运行时改顶点数据的。还有一种是固定了顶点数据的,无法在运行时改数据!
            EntityManager.AddComponentData<MeshRenderer>(unlitMesh, new MeshRenderer{ // 网格渲染组件
                mesh = unlitMesh,
                material = unlitMaterial,
                startIndex = 0,
                indexCount = dmd.NumIndices
            });
            EntityManager.AddComponentData(unlitMesh, new SimpleMeshRenderer()); // 这个组件用来表明该Mesh是不支持光照的网格
            EntityManager.AddComponentData(unlitMesh, mb); // Mesh的包围盒
            EntityManager.AddComponentData<Translation>(unlitMesh, new Translation { Value = float3.zero });
            EntityManager.AddComponentData<Rotation>(unlitMesh, new Rotation { Value = quaternion.identity });
            EntityManager.AddComponentData<LocalToWorld>(unlitMesh, new LocalToWorld { Value = float4x4.Translate(float3.zero) });

            return unlitMesh;
        }

        // 创建一个不带光照的材质。该接口也不能在Entities.ForEach中调用
        Entity CreateUnlitMaterial() {
            var unlitMaterial = EntityManager.CreateEntity();
            EntityManager.AddComponentData(unlitMaterial, new SimpleMaterial
            {
                texAlbedoOpacity = Entity.Null,
                constAlbedo = new float3(1, 1, 1),
                constOpacity = 1.0f,
                twoSided = true,
                blend = BlendOp.Disabled,
                transparent = false,
                billboarded = false,
                scale = new float2(1, 1),
                offset = new float2(0, 0)
            });
            return unlitMaterial;
        }
    }
}

动态改变UV的System

using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny.Rendering;
using Unity.Tiny;

namespace TinyMesh {
    public class ChangeMeshUVSystem : SystemBase
    {
        float time = 0;

        const int count = 16;

        const float size = 1.0f / count;

        protected override void OnUpdate()
        {
            Entities.WithAll<ChangeMeshUV>().ForEach((Entity entity, ref DynamicMeshData dmd, ref DynamicBuffer<DynamicSimpleVertex> dsv) => {
                time += World.Time.DeltaTime;
                var u = (int)(time % count);
                var v = (int)((time / count) % count);
                unsafe {
                    int n = dsv.Length;
                    SimpleVertex *vp = (SimpleVertex *)dsv.GetUnsafePtr();
                    vp[0].TexCoord0 = new float2(size * u, size * v);
                    vp[1].TexCoord0 = new float2(size * u + size, size * v);
                    vp[2].TexCoord0 = new float2(size * u + size, size * v + size);
                    vp[3].TexCoord0 = new float2(size * u, size * v + size);
                }
                dmd.Dirty = true;
            }).WithoutBurst().Run();
        }
    }
}

打包安卓后运行效果
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值