Create rain in Unity and upload to STYLY

I explain how to create rain with Unity and upload it to STYLY.

※You can access the sample project from https://github.com/styly-dev/STYLY-Unity-Examples/tree/master/Assets/STYLY_Examples/Rain

Unityの頂点シェーダーを使った雨のサンプル

A sample of rain by Vertex Shader in Unity

 

How to create rain

Before creating rain, I give an overview of how to create it.

It requires 3 STEPS as shown below.

  1. Create a lot of triangular meshes.
  2. Shake the triangular meshes arranged at high speed.
  3. Adjust the colour to make them look like rain (Completion).

Create a lot of triangular meshes

三角形メッシュをプログラムで作成

Create triangular meshes by a program

I arranged a lot of fine triangular meshes so that they fill a cube.

Shake the triangular meshes at high speed

頂点シェーダーで三角形メッシュを揺らす

Shake the triangular meshes by Vertex Shader

Shake the vertices by Vertex Shader

Adjust the colour to make them look like rain (completion)

色を調整し、雨っぽい見た目にして完成

Adjust the colour to make them look like rain. It completes the modelling.

Colour the meshes in translucent light blue to make them look like rain. It completes modelling rain.

 

Create meshes

From now, I explain the actual steps to model rain.

 

Firstly, we create meshes.

プログラムで大量の三角形メッシュを作成

Create a lot of triangular meshes by a program

Preparations

This time, we use two C# scripts to create triangular meshes.

I explain how to add two C# scripts here.

 

Script 1: Create RandomMesh.cs

Firstly, right-click in the Project window to create a C# script.

C#スクリプト作成

Create a C# script

 

Rename it RandomMesh.

作成したスクリプトの確認

Check the script created.

 

Open the script, RandomMesh, and edit it as shown below.

using UnityEngine;
using System.Collections.Generic;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class RandomMesh : MonoBehaviour
{
    const string k_MeshName = "[Generated]RainMesh"; // Mesh Name
    [SerializeField, Header("The number of raindrops")] int m_TriangleCount = 1500; // The number of triangular meshes
    [SerializeField, Header("The size of raindrop")] float m_TriangleScale = 0.3f; // The size of triangular meshes
    [SerializeField, Header("The randomness of rain")] Vector3 m_TriangleRange = new Vector3(4f, 4f, 4f); // The randomness of rain
    [SerializeField, HideInInspector] string m_OnCreateText = "No mesh information\n Update Mesh";

    public int TriangleCount { get { return m_TriangleCount; } }
    public float TriangleScale { get { return m_TriangleScale; } }
    public Vector3 TriangleRange { get { return m_TriangleRange; } }
    public string OnCreateText { get { return m_OnCreateText; } set { m_OnCreateText = value; } }

    /// <summary>
    /// Craete a new mesh 
    /// </summary>
    public Mesh CreateNewMesh()
    {
        Vector3[] vertices = new Vector3[m_TriangleCount * 3]; // The coordinates of vertex
        int[] triangles = new int[m_TriangleCount * 3]; // The index of vertex
        Vector3[] normals = new Vector3[vertices.Length];
        int pos = 0;
        for (int i = 0; i < m_TriangleCount; i++)
        {
            var v1 = Vector3.Scale(new Vector3(Random.value, Random.value, Random.value) - Vector3.one * 0.5f, m_TriangleRange);
            var v2 = v1 + new Vector3(Random.value - 0.5f, 0f, Random.value - 0.5f) * m_TriangleScale;
            var v3 = v1 + new Vector3(Random.value - 0.5f, 0f, Random.value - 0.5f) * m_TriangleScale;

            vertices[pos + 0] = v1;
            vertices[pos + 1] = v2;
            vertices[pos + 2] = v3;
            pos += 3;
        }

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

        for (int i = 0; i < normals.Length; i++)
        {
            normals[i] = new Vector3(0f, 1f, 0f);
        }
        
        //Create a mesh
        var mesh = new Mesh();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.normals = normals;

        return mesh;
    }

}

It completes the RandomMesh script.

 

Script2: Create RandomMeshInspector.cs

In the same way, create a script, RandomMeshInspector, as well.

スクリプトを開く

Open the script.

 

Edit the content of the RandomMeshInspector script as shown below.

using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.IO;

// Customize Editor
[CustomEditor(typeof(RandomMesh))]
public class RandomMeshInspector : Editor
{
    const string k_ExportMeshName = "[Generated]RainMesh"; // The name of the mesh to save
    static readonly Color k_ButtonColor = Color.yellow;
    static readonly Color k_ButtonTextColor = Color.white;

    /// <summary>
    /// Drawing the Inspector
    /// </summary>
    public override void OnInspectorGUI()
    {
        if (PrefabUtility.GetPrefabType(target) != PrefabType.Prefab)
        {
            EditorGUILayout.HelpBox("You must edit settings via Prefab.", MessageType.Info);
            return;
        }

        var defaultColor = GUI.color;
        var defaultContentColor = GUI.contentColor;

        var randomMesh = target as RandomMesh;

        // Display mesh inforamtion
        EditorGUILayout.BeginVertical("Box");
        EditorGUILayout.LabelField(randomMesh.OnCreateText, GUILayout.Height(54f));
        EditorGUILayout.EndVertical();

        // Change colour
        GUI.color = k_ButtonColor;
        GUI.contentColor = k_ButtonTextColor;

        if (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab)
        {
            if (GUILayout.Button("Update Mesh"))
            {
                EditorCreateMesh();
            }
        }

        // Undo
        GUI.color = defaultColor;
        GUI.contentColor = defaultContentColor;

        base.DrawDefaultInspector();
    }

    /// <summary>
    /// Create and save mesh
    /// </summary>
    [ContextMenu("Create Mesh")]
    void EditorCreateMesh()
    {
        var randomMesh = target as RandomMesh;
        var meshFilter = randomMesh.GetComponent<MeshFilter>();

        // Delete the old mesh installed
        var meshAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(randomMesh));
        foreach (var meshAsset in meshAssets)
        {
            if (meshAsset is Mesh)
            {
                Object.DestroyImmediate(meshAsset, true);
            }
        }

        // Create mesh
        var newMesh = randomMesh.CreateNewMesh();
        newMesh.name = k_ExportMeshName;
        meshFilter.sharedMesh = newMesh;

        // Install new mesh
        AssetDatabase.AddObjectToAsset(newMesh, randomMesh);

        randomMesh.OnCreateText = string.Format(
            "Mesh Information\n・The number of raindrops:{0}\n・The size of raindrops:{1}\n・The size of whole rain: {2}",
            randomMesh.TriangleCount,
            randomMesh.TriangleScale,
            randomMesh.TriangleRange
        );

        // Save
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }


    /// <summary>
    /// Generate the path to save mesh(at the same level as Prefab)
    /// </summary>
    string GenerateMeshPath()
    {
        var path = AssetDatabase.GetAssetPath(target);
        var parentFullPath = Directory.GetParent(path).FullName;
        var parentPath = "Assets" + parentFullPath.Substring(Application.dataPath.Length);
        var assetPath = parentPath + Path.DirectorySeparatorChar + k_ExportMeshName; // The path to save the asset
        return assetPath;
    }
}

The two C# scripts required are ready now.

 

Create Meshes

Next, we actually create triangular meshes.

三角形メッシュ

Triangular meshes

 

Create a Rain object

Create a GameObject on the Hierarchy window.

空オブジェクトを作成

Create an Empty object

 

Rename the object ‘Rain‘.

名前の変更

Rename the object.

 

名前変更後のオブジェクト

The object renamed.

 

Add the RandomMesh component

Add the RandomMesh component to the Rain object.

コンポーネントRandomMeshの追加

Add the RandomMesh component

 

The Inspector looks the shown below after adding the component.

追加前と追加後のInspectorウィンドウの比較

How the inspector looks before and after adding the component.

 

コンポーネントの確認

Check the component added.

Convert the Rain object into Prefab

Next, convert the Rain object into a Prefab.

 

Drag the Rain object in the Hierarchy window onto the Project window.

ドラッグアンドドロップでPrafabを作成

Create a Prefab by dragging the object onto the Project window.

 

A Prefab has been created.

作成されたPrefab

A Prefab created

 

Create meshes

With the Rain prefab selected, click the ‘Update Mesh’ button on the Inspector window.

ボタンを押してメッシュ更新

Click the button to update the mesh

 

A lot of triangular meshes are displayed.

作成されたメッシュ

Thw meshes created

Why are the meshes coloured in pink?

You can see meshes coloured in pink. That is because no material has been assigned to the MeshRenderer of the Rain object.

マテリアルの有無の比較

How they look with and without Material

Once a material is assigned, you can adjust the colour.

 

Assign a material to MeshRender

I will show how to assign a material to the MeshRenderer.

 

Expand the Materials section of the MeshRenderer.

Materialsをクリックして開く

Open ‘Materials’ by clicking the label.

 

マテリアルの登録箇所

Where to assign the material

 

Click the circular button on the Materials section to bring up the material-list window. Select the material you want to assign.

Default-Diffuseマテリアルを登録

Assign the ‘Default-Diffuse’ material

Select ‘Default-Diffuse’ to assign the Default-Diffuse material to the MeshRenderer.

 

マテリアル登録後の三角形メッシュ

How the triangular meshes look after assigning the material

If assigned the Default-Diffuse material, they would be coloured in white.

 

Add motion to triangular meshes

Next, we add motion to the meshes to make them look like rain.

 

I explain how to add motion to the triangular meshes here.

 

Create a motion by Custom Shader

This time, we use ‘Custom Shader’ to move vertices like rain.

三角形メッシュとカスタムシェーダーを組み合わせて雨を作る

Combining the triangular meshes with a custom shader to render rain

Create Custom Shader

I explain how to create a custom shader here.

Rainシェーダー

the Rain shader

 

Firstly, click ‘Create’ on the Project window (Alternatively, right-click on the Project window).

Createをクリック

Click ‘Create’.

 

Select ‘Shader/Standard Surface Shader‘.

Standard Surface Shaderを選択

Select ‘Standard Surface Shader’.

 

A shader is created. Rename it ‘Rain‘.

シェーダーの名前をRainにする

Rename the shader ‘Rain’.

 

Edit the shader file

Open the Rain shader.

シェーダーRainをダブルクリック

Double-click the Rain shader.

 

Edit Rain.shader as shown below.

Shader "Custom/Rain" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _Speed ("[VS]Rain Speed", Float) = 6.0 
        _Scale ("[VS]Rain Height", Float) = 4.0
		_Remap("[VS]Remap", Range(0.0, 1.0)) = 0.7
        [Space]
        _Albedo ("[FS]Rain Colour", Color) = (1.0, 1.0, 1.0, 0.5) 
        _Emission ("[FS]Rain Emission Colour", Color) = (0.0, 0.0, 0.0, 0.0) 
        _Specular ("[FS]Rain Specular Power", Range(0.0, 1.0)) = 0.5 
        _Gloss ("[FS]Rain Specular Intensity", Range(0.0, 1.0)) = 0.5 
	}

    SubShader {
        Tags {
            "Queue" = "Transparent"
            "RenderType" = "Transparent"
        }
        CGPROGRAM
        #pragma surface surf Lambert alpha
        #pragma vertex vert

        sampler2D _MainTex;

        // Function to generate random value from [0;1]
        float nhash11(float n){
            return frac(sin(n) * 43758.5453);
        }

        // Convert the range [a;b] into [0;1]
        float remap(float t, float a, float b){
            return clamp((t-a)/(b-a), 0, 1);
        }
        // Vertex Shader
        half _Speed;
		half _Scale;
		half _Range;
		fixed _Remap;

        // Colour
        half4 _Albedo;
        half4 _Emission;
        fixed _Specular;
        fixed _Gloss;


        // Vertex Shader Function
        void vert(inout appdata_full v) {
            fixed rnd = nhash11(fmod(v.vertex.z, 512.0)); // Random value
			//float timer = _Time.w * _Speed * remap(0.7, 1.0, rnd);
			float timer = _Time.w * _Speed * remap(_Remap, 1.0, rnd); // Convert the range [_Remap, 1.0] -> [0;1]
            v.vertex.y -= fmod(-v.vertex.y + timer, _Scale) + v.vertex.y - _Scale * 0.5;
            // v.vertex.y -= fmod(-v.vertex.y + timer, 4.0 / _Range) + v.vertex.y - _Scale * 0.5;
        }

        struct Input {
            float2 uv_MainTex;
        };

        // Surface Shader Function
        void surf(Input IN, inout SurfaceOutput o) {
            half4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb * _Albedo.rgb;
            o.Emission = _Emission; 
            o.Specular = _Specular; 
            o.Gloss = _Gloss; 
            o.Alpha = _Albedo.a; 
        }
        ENDCG
    }
}

It completes editing the shader.

 

Additional Note: About the filename of Shader

The shader name is ‘Rain‘ on Unity, but the actual filename is ‘Rain.shader‘.

シェーダーファイルの名前の確認

Check the filename of the shader

You can check the filename at the bottom of the Project window.

 

Create Material

Next, I explain how to create a material.

シェーダーからマテリアル作成

Create a material from the shader

 

Right-click the Rain shader and select ‘Create/Material‘.

マテリアル作成

Create a material.

 

A material, ‘Custom_Rain’, has been created.

作成されたマテリアルの確認

Check the material created.

It finishes creating a material to shake the vertices.

 

Test the material

Assign the Custom_Rain material created to a 3D model in the Scene.

マテリアルをオブジェクトにドラッグアンドドロップ

Drag the material onto an object.

 

When you assign the material to a 3D model, the model moves as shown below.

マテリアル追加後の動き

The model motion after adding the material

The Custom_Rain material moves the vertices of the model so that it gives a wavy motion like this.

 

Check the setting of the material

Click the material to show the setting in the Inspector window.

マテリアルの設定項目

The setting items of the material

By adjusting those values, you can change the behaviour of the material.

Add motion to rain

By combining the Custom_Rain material with the Rain object, you can make a rain-like motion.

三角形メッシュとマテリアルを組み合わせて雨を作る

Combining the triangular meshes and the material to render rain

 

I explain how to add motion to triangular meshes by assigning the material to it.

 

Firstly, select the Rain prefab and click the circular button on the right side of the Materials section.

雨のPrefabへのマテリアル登録

Assign the material to the Rain prefab.

 

Select the Custom_Rain material created earlier from the material list.

マテリアルCustom_Rainを選択

Select the Custom_Rain material.

 

Once the Custom_Rain material is set, the motion is added then the objects move like rain.

マテリアル登録後の三角形メッシュ

The triangular mesh after assigning the material.

Adjust the appearance of the rain

Adjust the size and colour of the rain to make it look like realistic rain more.

 

Firstly, select the Custom_Rain material and click the clour-property box.

マテリアルの色の入力部をクリック

Click the colour-property box for the material.

 

It brings up the Color Picker window.

Set it to translucent blue-ish colour as shown below.

水色を設定

Set it to light blue.

 

カラーウィンドウの詳細

The Color Picker window

 

When you set the colour, the rain object looks as shown below.

色を変更した後の雨

How the rain looks after changing the colour.

Modify the shape (The size and number of raindrops)

By adjusting the setting of the Rain prefab, you can change the size and the number of the raindrops.

Prefabでの雨の設定

The setting of the Rain prefab

 

Reduce the size of raindrops

By changing the ‘Raindrop Size’ value, you can change the size of the raindrops.

雨粒の大きさの変更

Modify the size of raindrops.

 

If you set the size to 0.1, the rain will look as shown below.

雨粒の大きさ変更後

After changing the size of raindrops.

Increase the number of raindrops

You can render heavy rain by increasing the number of raindrops.

雨粒の数の変更

Change the number of raindrops.

 

By setting the number of raindrops to 6000, the rain looks as shown below.

雨粒の数の変更後

After changing the number of raindrops

 

Add some randomness to the rain

You can add some randomness to the rain by increasing ‘the randomness of rain’ value.

雨全体のバラつきの変更

雨全体のバラつきの変更

 

The shown below is the rain with the randomness set to (16,4,16).

雨粒のバラつきの変更後

How the rain looks after changing the randomness of raindrops

 

Upload to STYLY

Right-click the Rain prefab and select ‘Upload prefab to STYLY’.

STYLYへPrefabをアップロード

Upload the prefab to STYLY

It starts uploading. Wait patiently.

 

When you see the ‘Upload Succeeded’ window, the upload is completed.

アップロード完了時に出現するウィンドウ

The window pops up when the upload is completed.

See ‘My Models’ in STYLY. You will find the Rain asset uploaded there.

アップロードしたPrefab

The prefab uploaded.

 

Now it’s time to make it rain on STYLY.

STYLYにRainを配置

Place the Rain asset in STYLY.

 

※You can access the sample project from https://github.com/styly-dev/STYLY-Unity-Examples/tree/master/Assets/STYLY_Examples/Rain