めーぷるのおもちゃばこ

- アイドルになりたいエンジニア女子の制作日記 -

【Unity】Firebase Unity SDKを使用してビルドで'FirebaseCore/FIRLogger.h' file not foundエラーにつまづいた話

はじめに

FirebaseのUnity SDKを使用してXcodeでのビルドで以下のエラーでつまづきました!

'FirebaseCore/FIRLogger.h' file not found


.xcodeprojではエラーがわからなかったので、.xcworkspaceのほうで開いてビルドしたところこのエラーが出たといった感じです。
いろいろ試した方法と最終的に自分が解決した方法を描こうと思います!
  
  

環境

・Unity2020.1.3f1
・Xcode Version 11.6
・Firebase Unity SDK 6.24.0

  

自分が解決できたやつ

以下にいろいろ方法を書いているのですが、
最終的にここに書いてあったこれを試してうまくいきました。
github.com
  
UnityでAssets/Firebase/EditorAnalyticsDependencies.xml

    <iosPod name="GoogleUtilities/MethodSwizzler" version="6.7.2">
    </iosPod>

  
の2行つけたしてビルド、からの、ビルドファイルの階層で

$ pod install

を実行することでビルドが通りました。

の間に挟むので以下のような感じ。

<dependencies>
  <iosPods>
    <iosPod name="Firebase/Analytics" version="6.24.0" minTargetSdk="8.0">
    </iosPod>
    <iosPod name="GoogleUtilities/MethodSwizzler" version="6.7.2">
    </iosPod>
  </iosPods>
  <androidPackages>
    //(以下略)

  

試したものたち

最終的には上記の方法でうまくいったのですが、人によって解決方法いろいろ違うと思うので、試したものを全部書いておきます!
これらの方法でも解決できるかもなので試してみてください!

Pod周りが結局うまくいってなかったのだけど、それの解決策もいろいろあった。
  

🐼方法1

ビルドファイルの中にあるPodfileに以下を書き足す。

pod 'GoogleUtilities', '[最新のバージョン番号]'

 
追記後、その階層で

$ pod install

 
を実行、生成されたUnity-iPhone.xcworkspaceを開いてビルド。

参考先↓↓
Error satisfying GoogleUtilities/MethodSwizzler dependency. · Issue #1845 · firebase/firebase-ios-sdk · GitHub
UnityのiOSアプリにFirebaseを導入したら「iOS framework addition failed」でビルドできない問題 - Qiita


🐼方法2

CocoaPodsを再設定する。
ビルドフォルダのなかにある.xcworkspaceファイルとPodfile.lockを削除、

$ pod install

を実行
  
参考↓↓
CocoaPodsを使ったプロジェクトのビルドでコンパイルエラーが発生。 | by Swiftでアプリな日々🐟 | Medium

  

🐼方法3

XcodeでのBuildOptionのみなおし。
Xcodeで上のメニューからProduct>Scheme>Edit Scheme ででてくるウィンドウのBuildタブにあるFind Implicit Dependenciesにチェックが入っていないとPods周りのビルドがされないらしいです。

f:id:maplesyrup-cs6:20200827153245p:plain
Find Implicit Dependenciesにチェック

  
  
参考↓
CocoaPodsを使ったプロジェクトのビルドでコンパイルエラーが発生。 | by Swiftでアプリな日々🐟 | Medium
  
 

🐼方法4

Firebase Unity SDKのアップデートをする。
どこ参考にしたかわかんなくなったけど、それで解決したっていうのも見かけたので!

🐼方法5

DLLファイルの削除と有効化
以下を参考に↓↓
Unity プロジェクトに Firebase を追加する
  

🐼方法6

PodのUpdate。どこを参考にしたかわかんなくなったけど以下のように書いてある部分があったので

Apparently that was the issue. I ran pod update and pod repo update and now it builds correctly. Thanks!

  
そのまま

$ pod update
$ pod repo update

をためしてみた。

🐼方法7

$ pod cache clean --all

からの

$ pod install

  
参考↓↓
iOS - CocoaPodsでのエラー Unable to read the license file `LICENSE` for the spec `Firebase (6.13.0)`|teratail

   

🐼その他

・他に別の.hファイルが見つからないエラーがでてる人もだいたいPodfileにつけたすことで解決してるっぽい。
Firebase Unity SDKを5.7.0にアップデートしたところエラー - しばゆに

  
・DLLファイルの削除と有効化
以下を参考に↓↓
Unity プロジェクトに Firebase を追加する


  

感想

Podfileに書き足したのはだめだったのにAnalyticsDependencies.xmlに書き足したらいけたというのはUnityでビルドする際に必要な情報なのかしらん。

 
以上でち🐼

f:id:maplesyrup-cs6:20200827160904p:plain

【Unity】Shaderでオブジェクトの交差位置に色をつける

はじめに

 
オブジェクトとオブジェクトがぶつかる境目に色をつけるシェーダーの作り方を紹介します!🐼

f:id:maplesyrup-cs6:20200809142016g:plain
今回のイメージ図

  
今回のサンプルプロジェクトはこちら↓
github.com

 

作り方

デプスバッファ、つまりカメラからの深度値を使用します。
交差するオブジェクト(今回のイメージ図でいうウサギ)のカメラからの深度値とプレーンの深度値とを比較して、その距離に応じて色をつけます。

 

準備

まずシーンにプレーンとオブジェクトを用意し、交差するように配置します。

f:id:maplesyrup-cs6:20200809150451p:plain
Planeとオブジェクトの準備

  
ヒエラルキーからCreate> C# Scriptでスクリプトを作成し、DepthTexture以下を記述します。

using UnityEngine;

[ExecuteInEditMode]
public class DepthTexture : MonoBehaviour 
{
  private Camera cam;

  void Start () 
  {
    cam = GetComponent<Camera>();
    cam.depthTextureMode = DepthTextureMode.Depth;
  }
}

 
このスクリプトをカメラにセットします。
すると、Cameraコンポーネントの一番下のところに以下のような表示が現れます。

f:id:maplesyrup-cs6:20200809152009p:plain
深度テクスチャ生成モード

 
これによりカメラが深度テクスチャを生成します。
 
このとき交差対象のオブジェクトのマテリアルに適応するシェーダーは影を扱えるものでないといけません(つまりUnlitはだめ)。Unityのドキュメントによると、

デプステクスチャは、シャドウキャスターのレンダリングに使用するのと同じシェーダー内パスを使用してレンダリングされます(“ShadowCaster” pass type)。したがって拡張機能により、シェーダーがシャドウキャスティング(影付け)をサポートしていない場合(例えば、シェーダーかフォールバックのいずれにおいてもシャドウキャスターのパスがない)、そのシェーダーを使用するオブジェクトにデプステクスチャは表示されません。
参照元:
カメラの深度テクスチャ - Unity マニュアル

という理由があるからです。

  

シェーダーを書く

つづいてシェーダーを書いていきます。
プロジェクトウィンドウから Create>Shader>Unlit Shader でShaderをつくります。

Shader "Custom/DepthFade"
{
    Properties
    {
        _Color("Color", Color) = (1, 1, 1, 1)
        _EdgeColor("EdgeColor", Color) = (1, 1, 1, 1)
        _DepthFactor("Depth Factor", float) = 1.0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            uniform sampler2D _CameraDepthTexture;
            fixed4 _Color; 
            fixed4 _EdgeColor; 
            float _DepthFactor;

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

            fixed4 frag (v2f i) : SV_Target
            {
                float4 depthSample = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));
                half depth = LinearEyeDepth(depthSample);
                half screenDepth = depth - i.screenPos.w;
                float foamLine = 1 - saturate(_DepthFactor * screenDepth);
                fixed4 col = lerp(_Color, _EdgeColor, foamLine);
                // fixed4 col = _Color + foamLine * _EdgeColor;   //EdgeをEmissionにしたいばあいはこっち
                return col;
            }
            ENDCG
        }
    }
}

 
シェーダーが書けたら、シェーダーの上で右クリックからCreate>Materialとするとそのシェーダーが適用されたマテリアル が作成されます。
それをプレーンに貼り付けて、インスペクタから設定します。

f:id:maplesyrup-cs6:20200809162837p:plain
インスペクタから設定

  
Color:プレーンのベースの色です。
_EdgeColor:エッジの色です。
Depth Facor :エッジの色の範囲の調整
 
です。

  

シェーダーの解説

uniform sampler2D _CameraDepthTexture;

ここでデプスバッファのテクスチャを宣言しています。
  

o.screenPos = ComputeScreenPos(o.vertex);

ComputeScreenPos()はUnity.cgincに定義されている関数で、xyが0〜wに変換されるらしいです。

  

 SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));

Z方向の膨らみを正規化することで2D平面(ディスプレイ)のどの位置に該当オブジェクトのピクセルがくるのかを計算しています。深度テクスチャをレンダリングしていると考えると良さそうです。プレーンはTransparentなので、ウサギより後にレンダリングされます。つまりこの時点ではウサギのみがレンダリングされている状態です。そのウサギのカメラ深度値をスクリーンポジションに変換しています。

   

half depth = LinearEyeDepth(depthSample);

LinearEyeDepth()はZ バッファ値をリニア化するために使用します。
先ほどのレンダリングされた深度テクスチャを渡すと各ピクセルのカメラからの深度を計算します。これで、ウサギのディスプレイ深度が算出されました。
  

half screenDepth = depth - i.screenPos.w;

depthがウサギの深度値なので、そこからi.screenPos.w(平面の深度値)を引くことで、プレーンとウサギとの距離がでます。距離がわかれば、交差している部分(つまり値が0の部分)もわかりますよね。


float foamLine = 1 - saturate(_DepthFactor * screenDepth);

上記の流れを経て算出されたscreenDepth_DepthFactorを掛けて調整しつつ、それを0から1になるようにクランプします。そしてこのままだと値がオブジェクトの交差部分に近くにつれて0になっていくのですが、扱いづらいのでオブジェクトと交差している部分を1、離れるほど値が0に近づくようにしたいので、1から引いています。

 
そして最後に出した値を色に反映させています。  

fixed4 col = lerp(_Color, _EdgeColor, foamLine);
// fixed4 col = _Color + foamLine * _EdgeColor;   //EdgeをEmissionにしたいばあいはこっち

lerpで線形補完させれば以下のように滑らかにエッジの色をつけることができます。

f:id:maplesyrup-cs6:20200809222619p:plain
線形補間させた見た目

    
ベースの色である_Colorに加算した場合はEmissionとしてエッジの色をくっきりつけることができます。

f:id:maplesyrup-cs6:20200809222645p:plain
_Colorに加算した見た目

  
どっちもかっこいいですね!
以上です!

    

zshでno match found とでたときの解決方法

zshで以下のようなエラーがでたのでその原因と解決策メモです。

zsh: no matches found:

 

原因

原因はコマンドに* ? {} [] ~ などのグロブ表現が含まれていると、zhsがファイル名だと解釈し「そんなファイルはないよ」とエラーを返すからみたいです。

グロブってなに↓
用語集:ファイルグロブ: UNIX/Linuxの部屋

   

解決策

~/.zshrcに以下を記述、

setopt +o nomatch


sourceコマンドで読み込み直しで解決できました。

$ source ~/.zshrc


  

【Unity】Unlit Shaderでリムライティング

はじめに

こんな感じで透過なし(テクスチャありなし)、透過あり(テクスチャありなし)の2種類のリムライトシェーダーをつくりたいとおもいます。

f:id:maplesyrup-cs6:20200806135113p:plain
こんな感じのをつくる

  
リムライトシェーダーとは、後ろから光を当てた時に光が前側に回り込んで、前からみると輪郭部分が光っているように見えるやつのことです。
以下のサイトにある古畑任三郎さんの写真がまさにそれです。
☆PROJECT ASURA☆ [OpenGL] 『リムライティング』

  
今回はこれを実現していきましょう!🐼 
サンプルプロジェクトはこちら↓↓
github.com

   

普通のリムライトシェーダー

まずは透過なしのリムライティングをつくりましょう。
こんなかんじのやつです。

f:id:maplesyrup-cs6:20200806135328p:plain
普通のリムライト

 

【シェーダープログラム】

まずはさっそくシェーダーから書いてみましょう。  
プロジェクトウィンドウからCreate>Shader>Unlit Shaderでシェーダーを作ります。
RimLightUnlitという名前で保存します。

Shader "Custom/RimLightUnlit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
        _RimColor ("RimColor", Color) = (1,1,1,1)
        _RimPower("RimPower", float) = 0.0
    }
    SubShader
    {
        Tags {"RenderType" = "Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 viewDir : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
            };

            sampler2D _MainTex;
            fixed4 _MainTex_ST;
            fixed4 _Color;
            fixed4 _RimColor;
            half _RimPower;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float4x4 modelMatrix = unity_ObjectToWorld;
                o.normalDir = normalize(UnityObjectToWorldNormal(v.normal));
                o.viewDir = normalize(_WorldSpaceCameraPos - mul(modelMatrix, v.vertex).xyz);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * _Color;

                half rim = 1.0 - abs(dot(i.viewDir, i.normalDir));
                fixed3 emission = _RimColor.rgb * pow(rim, _RimPower) * _RimPower;
                col.rgb += emission;
                col = fixed4(col.rgb, 1.0);
                return col;
            }
            ENDCG
        }
    }
}


   
シェーダーファイルを右クリックし、Create>Materialとするとそのシェーダーが適用されたマテリアル が作成されます。
そのマテリアルをモデルにアタッチし、マテリアルの設定します。
Color:オブジェクト自体の色
RimColor:リムライトの色
RimPower:リムライトの強さ

です。
  
テクスチャアリでセットするとこんな感じになります↓↓

f:id:maplesyrup-cs6:20200806132141p:plain
テクスチャあり

    
テクスチャなしで設定するとこんなかんじ↓↓ 

f:id:maplesyrup-cs6:20200806125653p:plain
テクスチャなし

  
  
_Colorを黒くしたりとかしてもすごいかっこよくていい感じになります。

f:id:maplesyrup-cs6:20200806125916p:plain
かっこいい


  

【コードの解説】

 
まず、リムライトを実装する考え方の説明です。
オブジェクトの輪郭部分に当たる面の法線は視線ベクトルと垂直に交わります。輪郭から離れる、つまり中心にいくにつれて法線ベクトルと視線ベクトルは水平になっていきます。
それを調べるには内積を使用します。法線ベクトルと視線ベクトルの内積を取ると、垂直部分が0になり、並行部分が1になります。
このことを利用して、二つのベクトルが垂直に近い部分をエミッションを強くすることでリムライトを実現できます。

f:id:maplesyrup-cs6:20200806165912p:plain
視線ベクトルと法線ベクトルの角度

  
そしてそれを利用しているのがfrag関数の中のこの部分です。

half rim = 1.0 - abs(dot(i.viewDir, i.normalDir));

  
内積は並行ならば1もしくは-1、垂直ならば0になりますが垂直のときにrimの値が1になるようにしたいので、1.0から内積の値を引いています。マイナス値もあるので、absで絶対値にして整数値で求めています。
  
そしてここで求まったrimの係数を_RimColor.rgbに掛けることで輪郭に近くなるごとに_RimColorの色になっていくわけです。

fixed3 emission = _RimColor.rgb * pow(rim, _RimPower) * _RimPower;
col.rgb += emission;

  

順番が前後しますが、ここで使用している肝心のviewDirnormalDirを計算しているのはvert関数内のここです。

o.normalDir = normalize(UnityObjectToWorldNormal(v.normal));
o.viewDir = normalize(_WorldSpaceCameraPos - mul(modelMatrix, v.vertex).xyz);

 
法線ベクトルは法線をワールド座標空間に変換したものを正規化することで出しています。
視線ベクトルはカメラのポジションから、モデルの座標と頂点を行列変換(mul関数)したものを引いて正規化することで出しています。
  
   

透明なリムライティング

  
ホログラムっぽい感じをだしたいときは透明なリムライトシェーダーがGoodです。
これをつくっていきます!

f:id:maplesyrup-cs6:20200806135402p:plain
ホログラムっぽいリムライト

  

【シェーダープログラム】

プロジェクトウィンドウからCreate>Shader>Unlit Shaderでシェーダーを作ります。
RimLightUnlitTransparentという名前で保存します。

Shader "Custom/RimLightUnlitTransparent"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
        _RimColor ("RimColor", Color) = (1,1,1,1)
        _Alpha("Alpha", float) = 0.0
        _RimPower("RimPower", float) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
        LOD 100
        Pass
        {
            ZWrite ON
            ColorMask 0
        }

        Tags { "RenderType"="Transparent" "Queue" = "Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha 
        LOD 100
        ZWrite OFF
        // Ztest LEqual
        LOD 200

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
				//float3 normal : NORMAL;
                float4 vertex : SV_POSITION;
                float3 viewDir : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
            };

            sampler2D _MainTex;
            fixed4 _MainTex_ST;
            half _Glossiness;
            half _Metallic;
            fixed4 _Color;
            half _Alpha;
            fixed4 _RimColor;
            half _RimPower;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float4x4 modelMatrix = unity_ObjectToWorld;
                o.normalDir = normalize(mul(v.normal, unity_ObjectToWorld)).xyz;
                o.viewDir = normalize(_WorldSpaceCameraPos - mul(modelMatrix, v.vertex).xyz);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv) * _Color;

                half rim = 1.0 - abs(dot(i.viewDir, i.normalDir));
                fixed3 emission = _RimColor.rgb * pow(rim, _RimPower) * _RimPower;
                col.rgb += emission;

                half alpha = 1 - (abs(dot(i.viewDir, i.normalDir)));
                alpha = clamp(alpha * _Alpha, 0.1, 1.0);

                col = fixed4(col.rgb, alpha);
                return col;
            }
            ENDCG
        }
    }
}


先ほどと同じく、シェーダーファイルを右クリックし、Create>Materialとするとそのシェーダーが適用されたマテリアル が作成されます。
そのマテリアルをモデルにアタッチし、マテリアルの設定します。

f:id:maplesyrup-cs6:20200806131841p:plain
テクスチャあり

  
  

f:id:maplesyrup-cs6:20200806131912p:plain
テクスチャなし


 

【コードの解説】

リムライト部分は先ほどのと同じです。そこから透明にするためにアルファ値の設定をしています。

half alpha = 1 - (abs(dot(i.viewDir, i.normalDir)));
alpha = clamp(alpha * _Alpha, 0.1, 1.0);


行っていることは先ほどのrimの計算と同じで、法線ベクトルと視線ベクトルの内積を求めています。
それをアルファに入れて係数を掛けているわけですが、
clamp()としているのはalphaの値が0になったときに全てが透明になってしまって何も見えなくなってしまうので、0.1から1.0の値に納めています。
clamp関数は値を指定した範囲の中に納まるように変換してくれる関数です。

 
その他基本的なシェーダーの透過の説明はこちら↓
www.wwwmaplesyrup-cs6.work


  

わーい!

以上です!!わーい!

f:id:maplesyrup-cs6:20200806135139p:plain
わーい!

   

【Unity】AR Foundationの平面認識のPlaneをカスタマイズする

🐼はじめに

ARの平面認識のプレーンがデフォルトだとこんな感じなのですが、

f:id:maplesyrup-cs6:20200805133451p:plain
デフォルトのやつ

   
アプリのテイストなどに合わせて柄や色を変えたかったりしますよね!
今回は以下のように、好きな色でドットの柄にする方法、テクスチャの柄にする方法、テクスチャを貼りつつ周りは透明にする方法の3つを紹介したいとおもいます!

f:id:maplesyrup-cs6:20200805133627p:plain
今回3種類やるよ

  

サンプルプロジェクトはこちら↓↓↓
github.com

  
そもそものAR Foundationでの平面認識の方法はこちら↓↓↓
www.wwwmaplesyrup-cs6.work


  

🐼カスタマイズの方法

プレーンをカスタマイズするにはAR Default Planeプレファブにアタッチされているマテリアルのシェーダーを自分でかいたものに変更することでできます🐼

f:id:maplesyrup-cs6:20200805141649p:plain
AR Plane Manager

  
わざわざシェーダーをかかなくても、この最初からついてるマテリアルにドット絵模様や柄の画像を貼ったらいいんじゃないの?とおもうかもしれませんが、それをすると平面が大きくなっていくとテクスチャが引き延ばされてしまい、思ったような描画になりません。
シェーダーを使用して描画することで平面の大きさに関係なく一定の間隔で柄を出すことができます。
  

AR Default PlaneはAssets下のどこかに自分でPrefab化しているのですが、見つからない場合はヒエラルキーにあるAR Session Originのインスペクタから、AR Plane ManagerにアタッチしてあるAR Default Planeをクリックすることでプロジェクトウィンドウがその場所を開いてくれます。

f:id:maplesyrup-cs6:20200805141953p:plain
ここ


  
AR Default PlaneのプレファブのインスペクターのMesh Rendererのところに現在のマテリアルがセットされています。ここのマテリアル を、自分で書いたシェーダーを適応したマテリアル に変えていきます。

f:id:maplesyrup-cs6:20200805142305p:plain
AR Default Planeのインスペクター


  

🐼ドット柄のシェーダー

【シェーダーを書く】

  
まずはドット柄からやっていきましょう🐼

f:id:maplesyrup-cs6:20200805162619p:plain
ドット柄こんな感じ

   
プロジェクトウィンドウからCreate>Shader>Unlit ShaderでShaderをつくります。
ここではAR Planeという名前で保存しました。
シェーダーファイルを開き、以下のプログラムを入力します。

Shader "Custom/ARPlane"
{
    Properties
    {
        _Color("_Color", Color) = (1, 1, 1, 1)
        _DotColor("_DotColor", Color) = (1, 1, 1, 1)
        _Ratio("Ratio", float) = 1
        _Size("Size", float) = 0.3
    }
    SubShader
    {
        Blend SrcAlpha OneMinusSrcAlpha
        Pass{
        Tags {"Queue" = "Transparent" "RenderType"="Transparent" }
        LOD 200

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

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

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

        v2f vert(appdata input)
        {
            v2f output;
            output.position = UnityObjectToClipPos(input.vertex);
            output.uv = input.uv;
            return output;
        }

        float _Ratio;
        float _Size;
        fixed4 _Color;
        fixed4 _DotColor;

        fixed4 frag (v2f input) :  SV_Target
        {
            float2 uv = input.uv * _Ratio;
            float2 st = frac(uv);
            fixed d = distance(st, fixed2(0.5, 0.5));
            fixed4 col = d < _Size ? _DotColor : _Color;
            
            return col;
        }
        ENDCG
        }
    }
}

  
シェーダーが書けたら、シェーダーファイルの上で右クリック> Create > Material をすると、そのシェーダーがすでにセットされたマテリアル が作成されます。

f:id:maplesyrup-cs6:20200805144348p:plain
シェーダーが割り当てられたマテリアル

  
いまのままだとまだマテリアル は真っ白です。
インスペクタから値を設定していきます。

_Colorドットじゃないところの色
_DotColorドットの色
Ratioタイリングの値
_Sizeドットのサイズ
です。

f:id:maplesyrup-cs6:20200805144920p:plain
インスペクタの設定


Ratioの値は20くらいからそれ以上がちょうど良さそうです、
_Color_DotColorもアルファ値が使用できるので、少し透過して床をみせるのも良さそうです。
_ColorのAlpha値を0にしてこういうのもよさそう。

こんな感じで色やサイズをカスタマイズできます。

f:id:maplesyrup-cs6:20200805150052p:plain
ドット以外の部分が透明

 
マテリアルができたら先ほど説明したようにAR Default PlaneのMaterialを入れ替えればいいです。ドットのサイズ感などは実際にビルドしながら確かめながら変更していくとよいです。

  

【シェーダーの解説】

fixed4 frag (v2f input) :  SV_Target
{
    float2 uv = input.uv * _Ratio;
    float2 st = frac(uv);
    fixed d = distance(st, fixed2(0.5, 0.5));
    fixed4 col = d < _Size ? _DotColor : _Color;
            
    return col;
}

このfrag関数の中をみていきます。
まずこの部分↓

float2 uv = input.uv * _Ratio;
float2 st = frac(uv);

まずUVを_Ratioに与えた値で掛けることでUVの値が0〜1から0〜_Ratioになります。
それをfrac(uv)とすることで、frac関数は小数点以下の部分の値を返してくれるので、0から1を繰り返してくれるため、_Ratioの数だけタイリングをしてくれます。

f:id:maplesyrup-cs6:20200805231309g:plain
Frac関数でタイリングをする

  
  
そして次に、ドットを描いています。

    fixed d = distance(st, fixed2(0.5, 0.5));
    d = step(_Size, d);

distance関数はdistance(x, y)でxとyの距離をfloatで返してくれる関数です。
シェーダーは(0, 0)が左下なので、(0.5, 0.5)がエリアの真ん中になります。

f:id:maplesyrup-cs6:20200805235209p:plain
(0, 0)は左下

  
なのでdistance(st, fixed2(0.5, 0.5)); とすることでそれぞれのエリアの中心からの距離が描画されます。

f:id:maplesyrup-cs6:20200805235140p:plain
distance(st, fixed2(0.5, 0.5));



それを最後に_Sizeに渡した値で区切って内側と外側の色をつけることで、円と円の外にそれぞれ色をつけています。

fixed4 col = d < _Size ? _DotColor : _Color;

  
そしてこの円の描画はfrac関数によってタイリングされているため、全てのエリアに同じものが描画されるので、ドット柄になってくれるということです。

   
   

🐼テクスチャをタイリングするシェーダー

【シェーダーをかく】

つづいてテクスチャをタイリングするシェーダーです。

f:id:maplesyrup-cs6:20200805162730p:plain
こんな感じのやつ

  
プロジェクトウィンドウからCreate>Shader>Unlit Shaderからシェーダーをつくり、ARPlaneTextureという名前にし、以下のシェーダーを書きます。 
  

Shader "Custom/ARPlaneTexture"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
        _Color("_Color", Color) = (1, 1, 1, 1)
        _Ratio("Ratio", float) = 1
    }
    SubShader
    {
        Blend SrcAlpha OneMinusSrcAlpha
        Pass{
        Tags {"Queue" = "Transparent" "RenderType"="Transparent" }
        LOD 200

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

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

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

        v2f vert(appdata input)
        {
            v2f output;
            output.position = UnityObjectToClipPos(input.vertex);
            output.uv = input.uv;
            return output;
        }

        sampler2D _MainTex;
        float4 _MainTex_ST;
        fixed4 _Color;
        float _Ratio;

        fixed4 frag (v2f input) :  SV_Target
        {
            float2 uv = input.uv * _Ratio;
            float2 st = frac(uv);
            fixed4 col = tex2D(_MainTex, st);
            col *= _Color;
            
            return col;
        }
        ENDCG
        }
    }
}

  
シェーダーが書けたら先ほどと同じようにシェーダーファイルの上で右クリック> Create > Materialをしてマテリアル を作成します。
インスペクタからテクスチャをセットし、Ratioの値を変更します。
するとテクスチャがタイリングされてテクスチャ柄のマテリアル になります。

f:id:maplesyrup-cs6:20200805161548p:plain
インスペクタからテクスチャとRatioを設定

  
先ほどと同じ原理でテクスチャを反映させただけなので説明は割愛します。

 

🐼透過付きの画像をタイリングする

続いてこちらの透過付き画像のタイリングです。

f:id:maplesyrup-cs6:20200805162941p:plain
透過付き画像のタイリング

  
こちらは先ほどのテクスチャをタイリングするシェーダーを当てはめたマテリアル に透過付き画像をセットするだけでいけます。
一応説明しておくと、

Blend SrcAlpha OneMinusSrcAlpha

が、透過情報を反映してくれるので、Alpha付きの画像をセットするだけで大丈夫です。
  
   

🐼アウトラインの色を変更する

以上ですが、もう一つだけおまけ、AR Default PlaneのLine Rendererのから外側の線の太さや色もカスタマイズできます。プレーンの色や柄に合わせて変えると良いでしょう!

f:id:maplesyrup-cs6:20200806003701p:plain
rainnno


  

【Unity】AR Foundationで平面の検出をする

🐼はじめに

UnityのAR Foundationを使用して平面を検出し、平面にオブジェクトを出すまでの一連の流れについてです!

まずAR Foundationの導入方法については以下の記事をご覧ください!
UnityにAR Foundationを入れて基本のセットアップとビルドのセッティングをするまで書いてあります!今回の記事はその続きから書きます。
www.wwwmaplesyrup-cs6.work


  
今回のサンプルプロジェクトはこちら↓↓
github.com


 

🐼開発環境

・Unity 2019.3.15f1
・AR Foundation 2.1.8
・ARKit XR Plugin 2.1.9

  

🐼平面を検出する

まずは平面の検出をできるようにします。
ヒエラルキーのAR Session OriginにインスペクタからAR Plane Managerコンポーネントを追加します。

f:id:maplesyrup-cs6:20200804093522p:plain
AR Plane Managerの追加

    
Ditection Modeは平面検知の種類を水平方向、垂直方向、両方、から選択します。
使用用途で選びましょう。今回は水平方向だけにします。

f:id:maplesyrup-cs6:20200804100140p:plain
Ditection Mode


ちなみに、AR Foundationのドキュメントには以下のように書かれています。

Some platforms require extra work to perform vertical plane detection, so if you only need horizontal planes, you should disable vertical plane detection.
参照元:AR plane manager | AR Foundation | 4.0.2

いくつかのプラットフォームでは垂直平面の検知をするために特殊な追加作業が必要になるため、垂直平面の検知が必要出ない場合は水平方向の検知のみにしておくと良いとのことです。

  

🐼平面検出のプレーンを出す

せっかくなので平面検出のプレーンをだしましょう!(出したくない場合はこの工程はとばしてください!)

f:id:maplesyrup-cs6:20200804094053p:plain
AR Default Planeの追加

  
 
ARのプレーンはGameObject>XR>AR Default Planeを選択します。
ヒエラルキーに追加されたら次にこれを、プロジェクトウィンドウにドラッグ&ドロップでプレファブ化し、ヒエラルキーにある元のやつを削除します。

f:id:maplesyrup-cs6:20200804100451p:plain
プロジェクトウィンドウにドラッグアンドドロップ

  
次にこれを先ほどのAR Session Originに追加したAR Plane ManagerのPlane Prefabのところにセットします。

f:id:maplesyrup-cs6:20200804101303p:plain
Plane Prefabにセット

  
ちなみにこれはプレファブ化しなくても使えるのですが、AR Foundationのドキュメントではプレファブ化することを推奨しています。シーンにプレーンを残すとゼロスケールのプレーンアーティファクトが残ってしまうとのこと。アーティファクトってなんだろう🥺 ぴえん(古物って意味らしい)

It is recommended to save the AR Default Plane as a prefab first, delete the AR Default Plane GameObject, and then use that in the prefab field as leaving the plane in your scene will leave a zero scale plane artifact in the scene.
参照元:AR plane manager | AR Foundation | 4.0.2


これで平面検知の設定完了!
これで一旦ビルドしてみると、こんな感じで床を認識しました。

f:id:maplesyrup-cs6:20200804103526g:plain
床認識してる!


   

🐼タップした場所にオブジェクトを出す

床認識したら次はオブジェクトを出したいですね!
タップしたらその平面にオブジェクトを出しましょう。
まず、

f:id:maplesyrup-cs6:20200804105643p:plain
AR Raycast Managerの追加

  

つづいてプロジェクトウィンドウで右クリックで Create>C# Script からスクリプトを作成します。
ここでは ObjectDeployer という名前にします。
以下のスクリプトを入力します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;   //忘れないように
using UnityEngine.XR.ARSubsystems;   //忘れないように

[RequireComponent(typeof(ARRaycastManager))]
public class ObjectDeployer : MonoBehaviour
{
    [SerializeField] GameObject _objPrefab;
    
    private ARRaycastManager _raycastManager;
    private List<ARRaycastHit> hits = new List<ARRaycastHit>();


    void Start()
    {
        _raycastManager  = GetComponent<ARRaycastManager>();
    }

    void Update()
    {
       if(Input.GetMouseButtonDown(0))
       {
           if(_raycastManager.Raycast(Input.GetTouch(0).position, hits, TrackableType.All))
           {
                Vector3 pos = new Vector3(hits[hits.Count - 1].pose.position.x, 
                hits[hits.Count - 1].pose.position.y + _objPrefab.transform.position.y, 
                hits[hits.Count - 1].pose.position.z);

                Instantiate(_objPrefab, pos, Quaternion.identity);
           }
       } 
    }
}

  
スクリプトがかけたらこれをAR Session Originにセットします。
スクリプトの解説は後に書きます。

f:id:maplesyrup-cs6:20200804113623p:plain
AR Session Originにセットする

 

先ほどセットしたスクリプトに、出したいオブジェクトのプレファブをインスペクタからセットします。
キューブとかをプレファブ化してそれをセットするとかでも大丈夫です。

f:id:maplesyrup-cs6:20200804114126p:plain
プレファブのセット

  
これでビルドしてみます!
わーい!タップしたとこにオブジェクトでましたん!

f:id:maplesyrup-cs6:20200804123120g:plain
わーい

  
※でないよ!って人はもしかしたらオブジェクトがでかすぎて自分がうもれている可能性があるので、オブジェクトは小さめにしておくとよかです。
  
   

🐼コードの解説

コードの解説をしていきます!

[RequireComponent(typeof(ARRaycastManager))]

RequierComponentARRaycastManagerを指定しています。
今回の記事では手動でARRaycastManagerコンポーネントをつけましたが、ここでこう書いておくとこのスクリプトをセットしたときに一緒にARRaycastManagerもセットされるので付け忘れ防止になります。

   
つづいて、

if(_raycastManager.Raycast(Input.GetTouch(0).position, hits, TrackableType.All))
{
    Vector3 pos = new Vector3(hits[hits.Count - 1].pose.position.x, 
    hits[hits.Count - 1].pose.position.y + _objPrefab.transform.position.y, 
    hits[hits.Count - 1].pose.position.z);

    Instantiate(_objPrefab, pos, Quaternion.identity);
}

タップした位置にオブジェクトを出すため、AR Raycast ManagerクラスのRaycastメソッドを使用します。引数は以下の通りです。

public bool Raycast(Vector2 screenPoint, List<ARRaycastHit> hitResults, TrackableType trackableTypeMask = TrackableType.All);

public bool Raycast(Ray ray, List<ARRaycastHit> hitResults, TrackableType trackableTypeMask = TrackableType.All, float pointCloudRaycastAngleInDegrees = 5f);

画面上のタップした座標からRayを飛ばし平面とぶつかった座標をARRaycastHitのリストに格納して返してくれます。
このARRaycastHitのリストに格納されている衝突情報は距離によってソートされます。
0番目が最も近い場所でヒットした情報になります。

今回は、

hits[hits.Count - 1].pose.position

とすることで一番遠い位置(高さが一番下の位置)を平面としています。つまり、机の上と床をよみこむと机の上にだそうとタップしても床の位置にオブジェクトがでます。
逆にhits[0]とすると一番近い位置を平面とするので、その場合机と床を読み取ったら机の高さにオブジェクトがでることになります。
  
また、オブジェクトの出すポジションについて、hits[hits.Count - 1].pose.position.y_objPrefab.transform.position.yを加算しているのは、オブジェクトの原点が中心にあるとプレーンの位置に出した時に半分下が埋もれてしまうので、プレファブ側で少し高さを上げておいてそれをプレーンのy軸に加算することでプレーンの上にうまくでるようにしています。

f:id:maplesyrup-cs6:20200804130820j:plain
原点がまんなかなのでプレーンにうもれちゃう

  
ちなみに、プログラムがわかる方はこの部分をもうすこし簡潔に書きたい場合Linqを使って以下のようにかくこともできます。hits.Last()と書けるので読みやすいです。
こちらの書き方をする場合 using System.Linqを忘れずに。

//Linqを使用してわかりやすく書く場合はこっち
Vector3 pos = new Vector3(hits.Last().pose.position.x, 
hits.Last().pose.position.y+_objPrefab.transform.position.y, 
hits.Last().pose.position.z);

 
第三引数で指定しているTrackableTypeはRayが当たる対象の指定です。
他にも以下のものから選択できます。

[Flags]
public enum TrackableType
{
    None = 0x0,     //検出しない
    PlaneWithinPolygon = 0x1,    //形状に沿った平面
    PlaneWithinBounds = 0x2,    //矩形平面
    PlaneWithinInfinity = 0x4,     //無限遠の平面 
    PlaneEstimated = 0x8,         
    Planes = 0xF,                         //全ての種類の平面
    FeaturePoint = 0x10,             //得頂点
    Image = 0x20,                      
    Face = 0x40,            
    All = 0x7F                             //すべてのタイプを検出
}


以上です!🐼

   

【めも】

平面検出のプレーンを消したい時はSetActivefalseにするのが良いみたいです。
ARPlainManagerが有効になっている間はDestroyするべきでないとAR Foundationのドキュメントに書かれていました。

foreach (var plane in planeManager.trackables)
{
    plane.gameObject.SetActive(false);
}

  

【Unity】テクスチャ情報から頂点を変形するShaderを書く

   

はじめに

テクスチャを読み込んでその情報から頂点座標をいじるシェーダーについて書きます。
頂点テクスチャフェッチという手法を使用します。


以下サンプルプロジェクトです。
サンプルプロジェクトにはフラグメントシェーダーのコードとサーフェスシェーダーでのコード両方入れてます。
今回はフラグメントシェーダーで説明しますが、サーフェスシェーダーも同じです!
github.com

  
  

頂点テクスチャフェッチとは

頂点テクスチャフェッチとは、頂点シェーダー内でテクスチャを参照する手法のことをいいます。
普通はテクスチャは画像として使用するのでフラグメントシェーダーで参照されます。
しかし、テクスチャは色以外の用途としても情報を格納することができます。
どういうことかというと、テクスチャと聞くと画像のイメージが強いですが、RGBAはただのfloat4つぶんの要素であり、テクスチャはそうしたfloat4つぶんのデータをただ格納しているだけです。
なので、そのデータを色以外の用途に使用することもできるわけです。
そのデータを頂点シェーダーで参照し、使用することを頂点テクスチャフェッチといいます。


頂点テクスチャフェッチの細かい内容については以下のサイトにとても詳しく書いてあるので、詳しくは以下をご覧ください!
wgld.org

  

今回つくるもの

今回はパーリンノイズのテクスチャをセットしてそのテクスチャ情報から頂点座標をイジります。

f:id:maplesyrup-cs6:20200802220027p:plain
このようなものをつくるよ

 
パーリンノイズのテクスチャは、フォトショップで フィルター>描画>雲模様2を白黒にして明るさとコントラストを変更することでつくれます。
こちらどうぞ使ってください↓↓

f:id:maplesyrup-cs6:20200803013502p:plain
パーリンノイズ

   

コードを書く

Unityのプロジェクトウィンドウから、Create > Shader > Unlit Shader で Shaderを作成します。以下のコードを記述します。
  

Shader "Custom/vtfSample_Fragment"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Tex2Dlod("Tex2DlodSample", 2D) = "white"{}
        _Scale("Scale", range(0, 10)) = 5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Cull off

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

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float2 texcoord : TEXCOORD1;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _Tex2Dlod;
            float4 _Tex2Dlod_ST;

            half _Scale;

            v2f vert (appdata v)
            {
                v2f o;

                float d = tex2Dlod(_Tex2Dlod, float4(v.texcoord.xy, 0, 0)).r;
                v.vertex.y += d * _Scale;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

  
コードを記述したら、マテリアル にこちらのシェーダーを適用し、
プレーンにマテリアルをセットします。

f:id:maplesyrup-cs6:20200802215954p:plain
テクスチャをセット

  

すると、以下のような感じで頂点たちがでこぼこします。

f:id:maplesyrup-cs6:20200802220027p:plain
テクスチャの情報に合わせて頂点たちがでこぼこ

   
   

コードの解説

重要なのはvert関数の中のここです。

float d = tex2Dlod(_Tex2Dlod, float4(v.texcoord.xy, 0, 0)).r;
v.vertex.y += d * _Scale;

  
バーテックスシェーダー内でテクスチャを参照する場合はtex2D代わりにtex2Dlodを使用します。

tex2Dlod(テクスチャ、float4(U値, V値, 0, w値 = (値の指定0〜7))

  
です。なぜtex2Dlodを使用するかというと、以下の通りです。

これは テクスチャlodの値がdepth値(カメラからの距離)によって決定されるため depth値がvertexシェーダでメッシュのソートが解決されてPixelシェーダにデータが渡るときに計算が行われるので vertexシェーダ内ではlodの自動割り当てが出来なくなり 直接lod値を指定してあげる必要があるからです。
引用:UnityのシェーダーでVTF(vertex texture fetch) | ヤマヤタケシのブログ

  
tex2Dlodで取得した値を、v.vertex.yに入れることで頂点のy軸の位置を変更しています。_Scaleはインスペクタから変更できるようにし、この値を乗算して頂点の移動具合を調整しています。

v.vertex.y += d * _Scale;

値の調整をする

これで頂点座標をテクスチャ情報から制御できました。
ただ、いまのままだと、_Scaleの値を上げると全体的な頂点座標がどんどん上に上がってしまいます。

f:id:maplesyrup-cs6:20200803013025p:plain
元の位置から座標がだいぶ上がってしまう

  
何故こうなってしまうかというと、テクスチャの値はそもそも色として使用するために0から1の間の値しか格納されていないのです。マイナス値がありません。
つまりdが0から1の値になっているところにScaleを乗算しているのでさらに値はプラス側に大きくなり、全体的に上にずれていくというわけです。
なので、0から1になっているdの値を-1から1に変更します。

float d = tex2Dlod(_Tex2Dlod, float4(v.texcoord.xy, 0, 0)).r;
d = d * 2 -1;     //0から1になっているdの値を-1から1に変換
v.vertex.y += d * _Scale;

    
0から1になっているdの値に2を掛けて1を引くと-1から1になります。
なぜこれでそうなるかというと以下の通りです。

f:id:maplesyrup-cs6:20200803085356p:plain
0から1の値を−1から1に変換

  
  
   
これで、元の位置のまま頂点が動くようになりました。

f:id:maplesyrup-cs6:20200803021657g:plain
元の位置のまま頂点が動く


さいごに

以上でち🐼
これを使用することで、動かせば海の波のような凸凹感をつくれたり、地面の凸凹感を表現することができるかなとおもいます。