中心点 unity_Unity制作动森自定义烟花模拟器(一)

本文介绍了如何在Unity中模拟动森自定义烟花,通过C#动态生成Mesh,创建随机分布的彩色圆点。讨论了如何计算顶点、UV坐标,以及使用两套UV来采样自定义图案颜色。文中还提出了在Shader中修改UV的局限性,并探讨了使用DrawMeshInstanced的潜在优化方案。
摘要由CSDN通过智能技术生成

005e52bfa887189ec155e547394c99be.png

8-16更新了第二篇。

潇潇奈何:Unity制作动森自定义烟花模拟器(二)​zhuanlan.zhihu.com
db6fba8837fc8ec7d0d7ba2608539a2c.png

最近动森进行了夏季第二次免费更新,目前岛上也已经举办了2次烟花大会了!比较好玩的是可以自己绘制自定义图案然后放成烟花。

ca5a1260267829c99a6a471d9849280d.png

5cf117be8b3bcc9cdfc2732af56d973a.gif

大概思考了一下实现方式不难,然后在Unity试着还原了一下,大概是这样:

945e959d8e7e329e3b85f93958685b51.gif
好像爆炸的时间和速度没调好

虽然不是很长但因为时间问题分两部分写,第一部分说C#,第二部分再放上Shader部分

好下面我们开始动手实现吧!先讲拍脑瓜就着手写的方式。


  • 先观测自定义烟花的特点
  1. 有许多位置随机分布的圆点组成图案
  2. 每个烟花点一个颜色
  3. 烟花点大小不一,会不停的闪烁
  • 大致思路

动态生成Mesh,先整齐的生成NxN的网格,并且每个格子拥有完整的四个顶点。

计算每个格子随机数,然后利用随机数移动格子的位置

第一套uv每个格子顶点为0-1,用来计算烟花小圆点。

第二套uv每个格子都取中心点,在大格子上对应0-1的位置,用来采样自定义图案的颜色

顶点色RG通道用来记录每个格子随机数。(做完后回想可以把uv2放在顶点色的BA通道)

ccfc60df5784a79c01f701c45046b69a.png
  • 开始实现
  1. 定义所需变量
    private Mesh fireWorks;
    private MeshRenderer mR;
    private MeshFilter mF;

    private List<Vector3> vertices;
    private List<int> triangles;
    private List<Vector2> uv1;
    private List<Vector2> uv2;
    private List<Color> colors;//顶点色 储存 - 每个片的random信息-XY

    private Vector3 startPos;
    public int Column = 2; //每行多少个格子 - 格子总数为Column * Column
    public float Length = 4;//总边长
    private float halflength;   //小正方形的一半边长

2. 初始化,循环生成每个格子的顶点、uv0、uv1和顶点色并设置。

        mR = this.GetComponent<MeshRenderer>();
        mF = this.GetComponent<MeshFilter>();
        fireWorks = new Mesh() { name = "fireWorks" };

        int totalQuadNum = Column * Column;

        vertices = new List<Vector3>();
        triangles = new List<int>();
        uv1 = new List<Vector2>();
        uv2 = new List<Vector2>();
        colors = new List<Color>();

        startPos = Vector3.zero;
        halflength = Length / (Column * 2);//方便后面计算每个小正方形的中心点

//这里循环生成每个格子
        for (int j = 0; j < totalQuadNum; j++)
        {
            //leftDownCorner
            //center在(0,0) 所以从-L/2左下角作为起点计算
            startPos.x = -Length/2 + (Length / Column) * (j % Column) ;
            startPos.y = -Length/2 + (Length / Column) * Mathf.Floor(j / Column);

            //*0.1*halflength是个经验数值,让偏移范围小一点
            Vector2 Random = RandomPerGridVertices(startPos) * halflength*1.4f;

            Vector3[] temp = CalculateVertex(startPos, Length / Column,Random);
            Vector2[] tempuv0 = CalculateUV0();
            Vector2[] tempuv1 = CalculateUV1(startPos, halflength);
            Color tempCol = SetPreVertexColor(Random);
            int[] tempTri = CalculateTriangle(j);

            for (int i = 0; i < 4; i++)
            {              
                vertices.Add(temp[i]);
                uv1.Add(tempuv0[i]);
                uv2.Add(tempuv1[i]);
                colors.Add(tempCol);//每个小方块共用一个顶点色,uv2同理的
            }

            for (int i = 0; i < 6; i++)
            {
                triangles.Add(tempTri[i]);
            }
        }

        fireWorks.SetVertices(vertices);
        fireWorks.SetTriangles(triangles,0);
        fireWorks.SetUVs(0, uv1);
        fireWorks.SetUVs(1, uv2);
        fireWorks.SetColors(colors);
        mF.sharedMesh = fireWorks;
    

3. 计算顶点,觉得平面表现的差别比较小的话也可以加入Z轴的变换,这里只计算XY平面

    Vector3[] CalculateVertex(Vector3 pos,float length,Vector2 random)
    {
        Vector3[] vertex = new Vector3[4];
        float StartposX = pos.x +random.x ;
        float StartposY = pos.y + random.y ;

        vertex[0 ] = new Vector3(StartposX,StartposY,0);
        vertex[1 ] = new Vector3(StartposX + length, StartposY, 0);
        vertex[2 ] = new Vector3(StartposX, StartposY + length, 0);
        vertex[3 ] = new Vector3(StartposX + length, StartposY+length, 0);

        return vertex;
    }    

7466d81b9edde8d95f297d51d4dad9aa.png
5x5格还没做随机的格子

4. 计算随机数。这里用每个格子的左下角(每个格子的第0点)作为随机种子,然后随意找了个随机算法,格子多了之后好像有一定的规律性,改进的时候可以换一个计算方式。

    Vector2 RandomPerGridVertices(Vector3 pos)
    {
        Vector2 random = new Vector2();

        float dotX = Vector3.Dot(new Vector3(37.0f, 17.0f, 0), pos);
        float dotY = Vector3.Dot(new Vector3(11.0f, 47.0f, 0), pos);
        random.x = Mathf.Sin(1.0f + dotX);
        random.x = random.x - Mathf.Floor(random.x);

        random.y = Mathf.Sin(2.0f + dotY);
        random.y = random.y - Mathf.Floor(random.y);

        return random;
    }

51113e800d521314dc1f2e71965fc5a2.png
加入随机计算后的格子

提问!为什么不再Shader里面修改uv来做格子的随机呢?

放一下我一开始的写法思路:

float2 floorUV = floor(i.uv * 5);
float2 fracUV = frac(i.uv + hash22(floorUV) * 5 );
//或这么写
//float2 fracUV = frac(i.uv  * 5 + hash22(floorUV));
float dropRound = smoothstep(0.8,- 0.3, length(fracUV-0.5) );

9bb1fd16a7dc06116bd64163e15da7fd.png

因为计算出来改变的是uv,而且限制都再0-1或-x ~x之间,圆点没有办法跨过别的uv区域显示自己。所以还是改用Mesh + 两套UV

如果大佬们有处理的方法请告诉我QwQ

5. 计算UV。uv0用Smoothstep来画圆点,如果不需要模糊过渡的话其实step也可以的。

uv1 记录格子中心点在 0-1上的uv坐标,方便整个格子统一采样到的颜色

7ef7717981b1668ef763d17aaa77f519.png
短线段/长线段就是这个格子的U
 ///uv0用来计算烟花的圆点
    Vector2[] CalculateUV0()
    {
        Vector2[] uv0 = new Vector2[4];
        uv0[0 ] = new Vector2(0, 0);
        uv0[1 ] = new Vector2(1, 0);
        uv0[2 ] = new Vector2(0, 1);
        uv0[3 ] = new Vector2(1, 1);
        return uv0;
    }
//uv1用来计算采样自定义贴图的,可以放在顶点色的BA通道中
    Vector2[] CalculateUV1(Vector3 uvPos,float length)
    {
        //因为每个球一个颜色,所以取方块中心点的uv采样的颜色 作为球的颜色
        float centerX = Mathf.Abs(uvPos.x + length - (-1) * Length / 2) / Length;
        float centerY = Mathf.Abs(uvPos.y + length - (-1) * Length / 2) / Length;
        Vector2[] uv1 = new Vector2[4];
        uv1[0] = new Vector2(centerX, centerY);
        uv1[1] = new Vector2(centerX, centerY);
        uv1[2] = new Vector2(centerX, centerY);
        uv1[3] = new Vector2(centerX, centerY);

        return uv1;
    }

6. 把之前算好的随机数传入顶点色供Shader使用

    Color SetPreVertexColor(Vector2 random )
    {
        Color col = Color.black;
        col.r = random.x;
        col.g = random.y;
        return col;
    }

到这里C# 部分就结束啦!

让我们看一个采样贴图 + 计算小圆形的烟花胚子

1b8067004c4e1f57f4607f4e19bf37d2.png
如果你最近看了《三十而已》,那你一定明白我在玩梗了

采样的图是:

838a7f0e9c39a715247b488464571641.png

写完之后我感觉用DrawMeshInstanced来处理多个四边形Mesh也是可以的,甚至更好。其中的参数都可以通过MaterialPropertyBlock传递,而且面片位置也可以移动,会比每帧修改一整个大mesh要好得多。

ec56367eeb550cdf8dd80e4849542fea.png

作为小玩具就没有多探究了,有兴趣的同学可以试一试QwQ。

下一篇分享一下Shader部分的处理,链接在下面。

潇潇奈何:Unity制作动森自定义烟花模拟器(二)​zhuanlan.zhihu.com
db6fba8837fc8ec7d0d7ba2608539a2c.png
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值