LoginSignup
13
16

More than 5 years have passed since last update.

【Unity】 頂点シェーダーを使った雨の描画

Last updated at Posted at 2017-01-07

はじめに

頂点シェーダーを使って雨を描画させてみました。

サンプル

謝辞

今回の記事を書くにあたって、以下のスライドを参考にさせていただきました。
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. 動かす

上記を実行すると以下のように描画されます。

雨の落ち方に偏りがあってちょっと残念な感じ。
rain_noRandom.gif

4. 雨を乱数で揺らす

雨を乱数で揺らしてみます。
ProcedualRandomMesh.cs へ以下のコードを追加

private float randomness = 0.1f;
void Update()
{
    this.transform.position = new Vector3(Random.value, 0f, Random.value) * randomness;
}

結果

rain_random.gif
いい感じにバラついてくれました。

5. 完成

雨の色のアルファ値を下げて完成!
rain_small_alpha.gif

雨の色を白くしても面白いかもしれません。
rain_small_white.gif

13
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
16