![005e52bfa887189ec155e547394c99be.png](https://i-blog.csdnimg.cn/blog_migrate/1e6c5df01e07ce6341291d5c4a62ce2f.jpeg)
8-16更新了第二篇。
潇潇奈何:Unity制作动森自定义烟花模拟器(二)zhuanlan.zhihu.com![db6fba8837fc8ec7d0d7ba2608539a2c.png](https://i-blog.csdnimg.cn/blog_migrate/173f80ba02543af50f20b4b9464da853.jpeg)
最近动森进行了夏季第二次免费更新,目前岛上也已经举办了2次烟花大会了!比较好玩的是可以自己绘制自定义图案然后放成烟花。
![ca5a1260267829c99a6a471d9849280d.png](https://i-blog.csdnimg.cn/blog_migrate/6cc138ed0eca0e0fb3aba52c4262f08d.jpeg)
![5cf117be8b3bcc9cdfc2732af56d973a.gif](https://i-blog.csdnimg.cn/blog_migrate/51661520bea933ac75728b6cda73d5ad.gif)
大概思考了一下实现方式不难,然后在Unity试着还原了一下,大概是这样:
![945e959d8e7e329e3b85f93958685b51.gif](https://i-blog.csdnimg.cn/blog_migrate/eb8ee71bc1ee70b668ccb2d6b29fa052.gif)
虽然不是很长但因为时间问题分两部分写,第一部分说C#,第二部分再放上Shader部分
好下面我们开始动手实现吧!先讲拍脑瓜就着手写的方式。
- 先观测自定义烟花的特点
- 有许多位置随机分布的圆点组成图案
- 每个烟花点一个颜色
- 烟花点大小不一,会不停的闪烁
- 大致思路
动态生成Mesh,先整齐的生成NxN的网格,并且每个格子拥有完整的四个顶点。
计算每个格子随机数,然后利用随机数移动格子的位置
第一套uv每个格子顶点为0-1,用来计算烟花小圆点。
第二套uv每个格子都取中心点,在大格子上对应0-1的位置,用来采样自定义图案的颜色
顶点色RG通道用来记录每个格子随机数。(做完后回想可以把uv2放在顶点色的BA通道)
![ccfc60df5784a79c01f701c45046b69a.png](https://i-blog.csdnimg.cn/blog_migrate/c86c8df4fc35ed15754538c00523345f.png)
- 开始实现
- 定义所需变量
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](https://i-blog.csdnimg.cn/blog_migrate/2188714670a96ae720fcddc85e7a14f5.jpeg)
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](https://i-blog.csdnimg.cn/blog_migrate/de69faf84615f9224a016c36c808a918.jpeg)
提问!为什么不再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](https://i-blog.csdnimg.cn/blog_migrate/e3b8a7008e3cfea9dbed7eb8d6b211ed.png)
因为计算出来改变的是uv,而且限制都再0-1或-x ~x之间,圆点没有办法跨过别的uv区域显示自己。所以还是改用Mesh + 两套UV
如果大佬们有处理的方法请告诉我QwQ
5. 计算UV。uv0用Smoothstep来画圆点,如果不需要模糊过渡的话其实step也可以的。
uv1 记录格子中心点在 0-1上的uv坐标,方便整个格子统一采样到的颜色
![7ef7717981b1668ef763d17aaa77f519.png](https://i-blog.csdnimg.cn/blog_migrate/acef32354ffc132391189834b6dfe231.jpeg)
///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](https://i-blog.csdnimg.cn/blog_migrate/e53019d00d6522afc7ea6010f3569e62.png)
采样的图是:
![838a7f0e9c39a715247b488464571641.png](https://i-blog.csdnimg.cn/blog_migrate/7e09c34f09910593c991b7a1ab0e55fd.png)
写完之后我感觉用DrawMeshInstanced来处理多个四边形Mesh也是可以的,甚至更好。其中的参数都可以通过MaterialPropertyBlock传递,而且面片位置也可以移动,会比每帧修改一整个大mesh要好得多。
![ec56367eeb550cdf8dd80e4849542fea.png](https://i-blog.csdnimg.cn/blog_migrate/fad96c35c2454dc7658b8be2d4a12d9a.png)
作为小玩具就没有多探究了,有兴趣的同学可以试一试QwQ。
下一篇分享一下Shader部分的处理,链接在下面。
潇潇奈何:Unity制作动森自定义烟花模拟器(二)zhuanlan.zhihu.com![db6fba8837fc8ec7d0d7ba2608539a2c.png](https://i-blog.csdnimg.cn/blog_migrate/173f80ba02543af50f20b4b9464da853.jpeg)