はじめに
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に移植したものが下記になります
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
}
}
}
4.ZigSimWaterSurfaceManager.csを作成し、Planeをアタッチします
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 |
---|---|---|
#デモ
水面をアゲサゲ#HoloLens #MRTK #Unity #ZIGSIM pic.twitter.com/5H0CggatXt
— がちもとさん@メタバース熊本 (@sotongshi) December 11, 2021
お疲れ様でした