はじめに
謝辞
今回の記事を書くにあたって、以下のスライドを参考にさせていただきました。
PLAYDEADさんに感謝!
https://docs.google.com/presentation/d/1_JHp-k4bDc9WhcMlC9hAPSGj_0UaJh5DBpSlLkwI74M/mobilepresent?slide=id.g5dea027fa_0375
0. 雨を描画するまでの簡単な流れ
三角形を大量に出す -> 乱数を使って三角形を高速に揺らす -> 雨っぽく見える
1. 三角形を大量に生成するスクリプト
以下のスクリプトを適当なGameObjectにアタッチしてください。
ProcedualRandomMesh.cs
using UnityEngine;
using System.Collections.Generic;
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class ProcedualRandomMesh : MonoBehaviour
{
// 三角形の個数
private int triangleCount = 1500;
// 三角形の大きさ
private float triangleScale = 0.002f;
// メッシュの大きさ
private Vector3 meshScale = new Vector3(4f, 4f, 4f);
private void Start()
{
this.Initialize();
}
/// <summary>
/// 初期化
/// </summary>
public void Initialize()
{
var vertices = new List<Vector3>();
var triangles = new List<int>();
int pos = 0;
for (int i = 0; i < this.triangleCount; i++)
{
var v1 = Vector3.Scale(new Vector3(Random.value, Random.value, Random.value) - Vector3.one * 0.5f, this.meshScale);
var v2 = v1 + new Vector3(Random.value - 0.5f, 0f, Random.value - 0.5f) * triangleScale;
var v3 = v1 + new Vector3(Random.value - 0.5f, 0f, Random.value - 0.5f) * triangleScale;
vertices.Add(v1);
vertices.Add(v2);
vertices.Add(v3);
triangles.Add(pos + 0);
triangles.Add(pos + 1);
triangles.Add(pos + 2);
pos += 3;
}
//メッシュ生成
var mesh = new Mesh();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
var meshFilter = this.GetComponent<MeshFilter>();
meshFilter.mesh = mesh;
}
}
決められた領域からランダムな座標をとって三角形を大量に生成。
最後にそれら全ての三角形を1つのメッシュにまとめています。
2. 雨っぽいアニメーションをするシェーダー
以下のシェーダーをProcedualRandomMesh.cs
がアタッチされているGameObjectにアタッチしてください。
rain.shader
// 参考URL
// https://docs.google.com/presentation/d/1_JHp-k4bDc9WhcMlC9hAPSGj_0UaJh5DBpSlLkwI74M/mobilepresent?slide=id.g5dea027fa_0375
Shader "Custom/Rain" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Color ("Color", Color) = (0,0,0,1.0) // 雨の色
_Speed ("Speed", Float) = 6.0
_Scale ("Scale", Float) = 4.0
}
SubShader {
Tags {
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
CGPROGRAM
#pragma surface surf Lambert alpha
#pragma vertex vert
sampler2D _MainTex;
// [0;1]のランダムな値を返す関数
float nhash11(float n){
return frac(sin(n) * 43758.5453);
}
// 値域[a;b] を 値域[0;1]へ変換する関数
float remap(float t, float a, float b){
return clamp((t-a)/(b-a), 0, 1);
}
fixed4 _Color;
half _Speed;
half _Scale;
// 頂点シェーダー関数
void vert(inout appdata_full v) {
fixed rnd = nhash11(fmod(v.vertex.z, 512.0));
float timer = _Time.w * _Speed * remap(0.7, 1.0, rnd);
v.vertex.y -= fmod(-v.vertex.y + timer, _Scale) + v.vertex.y - _Scale * 0.5;
}
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb * _Color.rgb;
o.Alpha = _Color.a;
}
ENDCG
}
}
雨のアニメーションは頂点シェーダー関数の中で行っています。
fixed rnd = nhash11(fmod(v.vertex.z, 512.0));
float timer = _Time.w * _Speed * remap(0.7, 1.0, rnd);
v.vertex.y -= fmod(-v.vertex.y + timer, _Scale) + v.vertex.y - _Scale * 0.5;
3. 動かす
上記を実行すると以下のように描画されます。
4. 雨を乱数で揺らす
雨を乱数で揺らしてみます。
ProcedualRandomMesh.cs
へ以下のコードを追加
private float randomness = 0.1f;
void Update()
{
this.transform.position = new Vector3(Random.value, 0f, Random.value) * randomness;
}