LoginSignup
1
2

More than 1 year has passed since last update.

HoloLens2 × ZIGSIM やーる(ARKitの位置情報を使って水面をアゲサゲ)

Last updated at Posted at 2021-12-11

はじめに

HoloLensアドベントカレンダー2021の10日目の記事です。
今回はZIGSIMを用いて、iPhoneのARKit(位置情報)を使って水面をアゲサゲしてみましょう。

開発環境

  • Windows 10 PC
  • Unity 2020.3.0f1
  • MRTK 2.7.2
  • HoloLens2
  • iPhone 12 Pro
  • ZIG SIM PRO

実装

1.1日目の記事を参考にセットアップお願いします

2.【シェーダーグラフメモ その50】Voronoiを利用した水面エフェクトを参考に水面シェーダーを作ります。ShaderGraphからShaderLabに移植したものが下記になります

WaterSurface.shader
Shader "Unlit/WaterSurface"
{
    Properties
    {
        _TimeScale("TimeScale", Float) = 0.3
        _CellSpeed("CellSpeed", Float) = 3.5
        _CellDensity("CellDensity", Float) = 14
        _Power("Power", Float) = 2.38
        _WhiteSmoothstep("WhiteSmoothstep", Vector) = (-0.47, 1.97, 0.0, 0.0)
        _OverColor("OverColor", Color) = (1, 1, 1, 1)
        _BaseColor("BaseColor", Color) = (0.3622641, 0.627297, 1.0, 0.0)
        _UvDistortionSpeed("UvDistortionSpeed", Float) = 0.1
        _DistortionNoiseScale("DistortionNoiseScale", Float) = 63
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            float _TimeScale;
            float _CellSpeed;
            float _CellDensity;
            float _Power;
            float4 _WhiteSmoothstep;
            float4 _OverColor;
            float4 _BaseColor;
            float _UvDistortionSpeed;
            float _DistortionNoiseScale;

            void Unity_Multiply_float(float A, float B, out float Out)
            {
                Out = A * B;
            }

            void Unity_Multiply_float4(float4 A, float4 B, out float4 Out)
            {
                Out = A * B;
            }


            void Unity_Add_float2(float2 A, float2 B, out float2 Out)
            {
                Out = A + B;
            }

            void Unity_Add_float4(float4 A, float4 B, out float4 Out)
            {
                Out = A + B;
            }


            inline float Unity_SimpleNoise_RandomValue_float(float2 uv)
            {
                return frac(sin(dot(uv, float2(12.9898, 78.233)))*43758.5453);
            }

            inline float Unity_SimpleNnoise_Interpolate_float(float a, float b, float t)
            {
                return (1.0-t)*a + (t*b);
            }

            inline float Unity_SimpleNoise_ValueNoise_float(float2 uv)
            {
                float2 i = floor(uv);
                float2 f = frac(uv);
                f = f * f * (3.0 - 2.0 * f);

                uv = abs(frac(uv) - 0.5);
                float2 c0 = i + float2(0.0, 0.0);
                float2 c1 = i + float2(1.0, 0.0);
                float2 c2 = i + float2(0.0, 1.0);
                float2 c3 = i + float2(1.0, 1.0);
                float r0 = Unity_SimpleNoise_RandomValue_float(c0);
                float r1 = Unity_SimpleNoise_RandomValue_float(c1);
                float r2 = Unity_SimpleNoise_RandomValue_float(c2);
                float r3 = Unity_SimpleNoise_RandomValue_float(c3);

                float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
                float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
                float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
                return t;
            }

            void Unity_SimpleNoise_float(float2 UV, float Scale, out float Out)
            {
                float t = 0.0;

                float freq = pow(2.0, float(0));
                float amp = pow(0.5, float(3-0));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                freq = pow(2.0, float(1));
                amp = pow(0.5, float(3-1));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                freq = pow(2.0, float(2));
                amp = pow(0.5, float(3-2));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                Out = t;
            }

            void Unity_Remap_float(float In, float2 InMinMax, float2 OutMinMax, out float Out)
            {
                Out = OutMinMax.x + (In - InMinMax.x) * (OutMinMax.y - OutMinMax.x) / (InMinMax.y - InMinMax.x);
            }

            inline float2 Unity_Voronoi_RandomVector_float (float2 UV, float offset)
            {
                float2x2 m = float2x2(15.27, 47.63, 99.41, 89.98);
                UV = frac(sin(mul(UV, m)));
                return float2(sin(UV.y*+offset)*0.5+0.5, cos(UV.x*offset)*0.5+0.5);
            }

            void Unity_Voronoi_float(float2 UV, float AngleOffset, float CellDensity, out float Out, out float Cells)
            {
                float2 g = floor(UV * CellDensity);
                float2 f = frac(UV * CellDensity);
                float t = 8.0;
                float3 res = float3(8.0, 0.0, 0.0);

                for(int y=-1; y<=1; y++)
                {
                    for(int x=-1; x<=1; x++)
                    {
                        float2 lattice = float2(x,y);
                        float2 offset = Unity_Voronoi_RandomVector_float(lattice + g, AngleOffset);
                        float d = distance(lattice + offset, f);

                        if(d < res.x)
                        {
                            res = float3(d, offset.x, offset.y);
                            Out = res.x;
                            Cells = res.y;
                        }
                    }
                }
            }

            void Unity_Power_float(float A, float B, out float Out)
            {
                Out = pow(A, B);
            }

            void Unity_Smoothstep_float(float Edge1, float Edge2, float In, out float Out)
            {
                Out = smoothstep(Edge1, Edge2, In);
            }

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                // STEP 1 : Voronoiを使って水面のベースを作る
                float _Multiply_Out;
                Unity_Multiply_float(_Time.x, _TimeScale, _Multiply_Out);

                float _Multiply_Out_2;
                Unity_Multiply_float(_Multiply_Out, _CellSpeed, _Multiply_Out_2);

                // STEP3 : UVにノイズを混ぜる(完成)
                float _Multiply_Out_5;
                Unity_Multiply_float(_Multiply_Out, _UvDistortionSpeed, _Multiply_Out_5);

                float2 _Add_Out_2;
                Unity_Add_float2(i.uv, _Multiply_Out_5.xx, _Add_Out_2);

                float _SimpleNoise_Out;
                Unity_SimpleNoise_float(_Add_Out_2, _DistortionNoiseScale, _SimpleNoise_Out);

                float _Remap_Out;
                Unity_Remap_float(_SimpleNoise_Out, float2(0.0, 1.0), float2(-0.5, 0.5), _Remap_Out);

                float _Multiply_Out_6;
                Unity_Multiply_float(_Remap_Out, 0.1, _Multiply_Out_6);

                float2 _Add_Out_3;
                Unity_Add_float2(i.uv, _Multiply_Out_6.xx, _Add_Out_3);
                // return float4(_Add_Out_3, 0,0);

                // STEP1のVoronoiの入力に
                float _Voronoi_Out;
                float _Voronoi_Cells;
                Unity_Voronoi_float(_Add_Out_3, _Multiply_Out_2, _CellDensity, _Voronoi_Out, _Voronoi_Cells);

                float _Power_Out_2;
                Unity_Power_float(_Voronoi_Out, _Power, _Power_Out_2);

                float _Smoothstep_Out;
                Unity_Smoothstep_float(_WhiteSmoothstep[0], _WhiteSmoothstep[1], _Power_Out_2, _Smoothstep_Out);
                // return _Smoothstep_Out.xxxx;

                // STEP2. 色を付ける
                float _Multiply_Out_3;
                Unity_Multiply_float(_OverColor[3], _Smoothstep_Out, _Multiply_Out_3);

                float4 _Multiply_Out_4;
                Unity_Multiply_float4(_OverColor, _Multiply_Out_3.xxxx, _Multiply_Out_4);

                float4 _Add_Out;
                Unity_Add_float4(_BaseColor, _Multiply_Out_4, _Add_Out);
                return _Add_Out;
            }
            ENDCG
        }
    }
}

3.マテリアルを作成し、Planeにアタッチしましょう
image.png

4.ZigSimWaterSurfaceManager.csを作成し、Planeをアタッチします

image.png

ZigSimWaterSurfaceManager.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

#if UNITY_EDITOR
using System.Net;
using System.Net.Sockets;
#else
using System;
using System.IO;
using Windows.Networking.Sockets;
#endif

public class ZigSimWaterSurfaceManager : MonoBehaviour
{
    int port = 3333;
    Osc.Parser osc = new Osc.Parser();
    public GameObject plane;

    void OnMessage(Osc.Message msg)
    {
        // Debug.LogFormat("{0} => {1}", msg.path, msg.data[0]);
        if (String.Equals(msg.path,"/ZIGSIM/{Device_UUID}/arkitposition")){
            float posy = (float)msg.data[1];
            plane.transform.position = new Vector3(0.0f, posy-1.0f, 0.0f);
        }
    }

#if UNITY_EDITOR
    UdpClient udpClient;
    IPEndPoint endPoint;

    void Start()
    {
        endPoint = new IPEndPoint(IPAddress.Any, port);
        udpClient = new UdpClient(endPoint);
    }

    void Update()
    {
        while (udpClient.Available > 0) {
            var data = udpClient.Receive(ref endPoint);
            osc.FeedData(data);
        }

        while (osc.MessageCount > 0) {
            var msg = osc.PopMessage();
            OnMessage(msg);
        }
    }
#else
    DatagramSocket socket;
    object lockObject = new object();

    async void Start()
    {
        try {
            socket = new DatagramSocket();
            socket.MessageReceived += OnMessage;
            await socket.BindServiceNameAsync(port.ToString());
        } catch (System.Exception e) {
            Debug.LogError(e.ToString());
        }
    }

    void Update()
    {
        lock (lockObject) {
            while (osc.MessageCount > 0) {
                var msg = osc.PopMessage();
                OnMessage(msg);
            }
        }
    }

    async void OnMessage(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
    {
        using (var stream = args.GetDataStream().AsStreamForRead()) {
            byte[] buffer = new byte[1024];
            await stream.ReadAsync(buffer, 0, 1024);
            lock (lockObject) {
                osc.FeedData(buffer);
            }
        }
    }
#endif
}

Debug.LogFormat("{0} => {1}", msg.path, msg.data[0]);のところで、ZIGSIMから下記のようなメッセージが送られてきます。

/{Device_UUID}/deviceinfo => iPhone13,3
/ZIGSIM/{Device_UUID}/arkitposition => -0.01249655
/ZIGSIM/{Device_UUID}/arkitrotation => -0.0370841

msg.pathがarkitpositionのときに、msg.data[0]~[2]がそれぞれx,y,zになります。
Planeのyの値を変化させましょう。

5.デプロイして、ZIGSIMを起動します。下記の設定でStartし、iPhoneを上下させると水面も上下すれば成功です!

Sensor Setting Start
264133344_593879755004127_8579732609732418049_n.jpg 264037270_1085044185666335_8374676234995130039_n.jpg 265388215_2733134830316514_2378475078834609276_n.jpg

デモ

お疲れ様でした

1
2
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
1
2