SlideShare a Scribd company logo
1 of 53
Download to read offline
@XR Kaigi 2019
ARグラスで
魅⼒的な絵作り
比留間 和也
2
1. ⾃⼰紹介
2. 会社紹介
3. プロダクト紹介
3-1. PORTAL紹介
4. PORTALをNreal Lightに移植中の話
CONTENTS
3
⾃⼰紹介
Self introduction
4
@edo_m18 @edom18
http://edom18.hateblo.jp/e.blog:
⽐留間 和也
5
MESONの紹介
MESON introduction
6
1. 制作体制
2. テキストのみ
2-1. テキストA
2-2. テキストB
3. テキストと写真
4. 図系
7
Nreal Lightエヴァンジェリスト
Nreal Lightのエヴァンジェリストに
8
プロダクト紹介
9
プロダクト紹介
⾼精細にスキャンしたモデルたちがARランウェイの上を歩き、
それに実際に近づいて⾒てもらうコンテンツ。
PORTAL
CONFIDENCIAL PAGE
PORTAL
X10
11
AUGGIE AWARDSファイナリスト
AUGGIE AWARDSという海外のアワードで
ソフトウェアとしては⽇本初のファイナリストに選出されました。
AR/VRで最⼤のAUGGIE AWARDSにてファイナリストに選出
12
PORTALをNreal Lightに移植中の話
Import the PORTAL to the Nreal Light
13
現在、CES向けにPORTALをNreal Lightに移植中
14
PORTALの移植で⼤事にしたこと
15
Interaction
インタラクション
Graphic
グラフィック
Effect
演出
PORTALの移植で⼤事にした点
iPad版にも劣らないようなグラフィック作り
ARグラスならではのインタラクション設計
PORTALのイメージであるパーティクル演出
16
移植にあたっての課題
17
iPad版に⽐べて
⾊味が明るすぎる
視野⾓の端にモデルを
配置すると枠が気になる
パーティクルを
⼤量に出すための⼯夫
移植にあたっての課題
1 2 3
18
1. iPad版に⽐べて⾊味が明るすぎる
19
iPad版に⽐べて⾊味が明るすぎる
PCで⾒た絵 ARグラス越しの絵
PCディスプレイで⾒た場合の⾊味 Nreal Lightで⾒た場合の⾊味
無加⼯だと⾊味が全然違う
20
課題はコントラスト
全体的に明るすぎてコントラストが低い
⾊に応じてコントラストを調整したい
※単純に黒を⾜して明るさを調整しても輝度が下がるだけ
21
iPad版に⽐べて⾊味が明るすぎる
PCで⾒た絵 ARグラス越しの絵
中⼼が無加⼯。右が⾊を2乗したもの
左は単純に黒を⾜したもの
加⼯したものが⼀番くっきりと⾒える
改善した状態
22
⾊を2乗する(コントラストを上げる)
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = pow(col.rgb, 2.0);
return col;
}
コード的にはシンプルだが効果は絶⼤
23
2. 視野⾓の端にモデルを配置すると枠が気になる
24
視界付近にモデルを配置すると枠が気になる
枠の端でぱっきりと切れると「枠」が意識される
視界の端でぱっきりと切れる
25
視界付近にモデルを配置すると枠が気になる
フェードアウトさせると区切りが意識されなくなる
視界の端をフェードアウトさせる
26
ポストプロセスで上下左右の際をフェードアウトさせる
画⾯端に近づくにつれてフェードアウトさせるポストエフェクトを書く
27
ポストプロセスで上下左右の際をフェードアウトさせる
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 uv = abs(i.uv * 2.0 - 1.0);
float2 u = _Width / _ScreenParams.xy * 0.5;
u = smoothstep(0, u, 1.0 - uv);
col = col * u.x * u.y;
return col;
}
UV値を利⽤して画⾯端に近づくにつれてフェードさせる
(ARグラスは黒=発光しない=透明になっていく)
28
3. パーティクルを⼤量に出すための⼯夫
29
30
31
32
シーン内の⼤量のパーティクルが
様々なオブジェクトの形に姿を変える
33
移植する上での課題
1. 10万弱のパーティクルを描画する
2. 複数モデルグループをパーティクル処理
3. モデル以外の形状にも変化する
4. 常に⽣成済パーティクルを利⽤する
(新規⽣成はしない)
34
処理フロー
35
処理フロー
1. ターゲットのメッシュ情報を集める
2. (1)のデータをグルーピング
3. Compute Shaderで位置計算
4. GPU Instancingで描画
36
Compute Shaderのセットアップ
- その場で、ランウェイに登
場する服の素材感を知るこ
とができる
37
利⽤するカーネルは3つ
Update ExplosionCalculate
01 02 03
パーティクルのターゲットを
更新する
ターゲットがない形状に
変形させる
パーティクルの位置を
計算・更新する
38
パーティクルの位置更新
[numthreads(THREAD_NUM,1,1)]
void Calculate(uint id : SV_DispatchThreadID)
{
Particle p = _Particles[id];
float3 pos = (p.targetPosition - p.position) * _DeltaTime * p.speed;
p.position += pos;
p.useTexture = 1;
_Particles[id] = p;
}
パーティクルの位置を毎フレーム更新
39
パーティクルのターゲット変更
[numthreads(THREAD_NUM,1,1)]
void Update(uint id : SV_DispatchThreadID)
{
Particle p = _Particles[id];
float4x4 mat = _MatrixData[_InitDataList[id].targetId];
p.isActive = _InitDataList[id].isActive;
p.targetPosition = mul(mat, float4(_InitDataList[id].targetPosition,
1.0)).xyz;
p.uv = _InitDataList[id].uv;
p.targetId = _InitDataList[id].targetId;
_Particles[id] = p;
}
パーティクルのターゲット先を変更(更新)する
40
パーティクルのターゲット変更
[numthreads(THREAD_NUM,1,1)]
void Explosion(uint id : SV_DispatchThreadID)
{
Particle p = _Particles[id];
if (_OnCircle == 1)
{
float h = rand(p.id);
float s = sin(p.id + _Time) * 1.5;
float c = cos(p.id + _Time) * 1.5;
float3 pp = float3(s, h * 1.5, c) + noise(p.position);
float3 pos = (pp - p.position) * _DeltaTime * p.speed;
p.position += pos;
p.color = float4(1, 1, 1, 1);
p.useTexture = 0;
}
else
{
float h = rand(p.id);
float3 pos = (p.targetPosition + float3(0, cos(h + _Time) * 0.01, 0) -
p.position) * _DeltaTime * p.speed;
p.position += pos;
}
p.color = float4(1, 1, 1, 1);
p.useTexture = 0;
_Particles[id] = p;
}
別形状のためのカーネル
41
C#側のセットアップ
42
メッシュの情報を集める
public class ParticleTarget : MonoBehaviour
{
private Mesh _mesh = null;
public Mesh Mesh => _mesh ?? (_mesh = GetComponent<MeshFilter>().mesh);
private Renderer _renderer = null;
private Renderer Renderer => _renderer ?? (_renderer =
GetComponent<Renderer>());
public int VertexCount => Mesh.vertexCount;
public Vector3[] Vertices => Mesh.vertices;
public Vector2[] UV => Mesh.uv;
public Matrix4x4 WorldMatrix => transform.localToWorldMatrix;
public Texture2D Texture => Renderer.material.mainTexture as Texture2D;
}
シンタックスシュガー的にデータを渡すだけのコード
43
頂点、UV情報をひとつの配列にまとめる
private void CollectAllData()
{
int count = GetCount();
_allVertices = new Vector3[count];
_allUV = new Vector2[count];
int idx = 0;
foreach (var t in _targets)
{
System.Array.Copy(t.Vertices, 0, _allVertices, idx,
t.Vertices.Length);
System.Array.Copy(t.UV, 0, _allUV, idx, t.UV.Length);
idx += t.Vertices.Length;
}
}
グループの頂点などをメモリアクセスしやすいように配列にまとめる
44
ターゲットモデルのマトリクスをまとめる
private void UpdateMatrices(ParticleTargetGroup group)
{
for (int i = 0; i < group.ParticleTargets.Length; i++)
{
_matrixData[i] = group.ParticleTargets[i].WorldMatrix;
}
_matrixBuffer.SetData(_matrixData);
_computeShader.SetBuffer(_kernelSetup, _propertyDef.MatrixDataID,
_matrixBuffer);
}
各頂点の⾏列計算はシェーダで⾏うためMatrixデータをまとめてGPUに送る
45
テクスチャをTextureArrayにまとめる
private void CreateTextureArray()
{
int count = _targets.Length;
int width = _targets[0].Texture.width;
int height = _targets[0].Texture.height;
_textureArray = new Texture2DArray(width, height, count,
TextureFormat.RGBA32, false, true);
_textureArray.filterMode = FilterMode.Bilinear;
_textureArray.wrapMode = TextureWrapMode.Repeat;
for (int i = 0; i < _targets.Length; i++)
{
_textureArray.SetPixels(_targets[i].Texture.GetPixels(0), i, 0);
}
_textureArray.Apply();
}
Matrix同様、テクスチャもシェーダ側でアクセスするためTextureArrayにまとめる
46
描画
47
GPUインスタンシングで描画
v2f vert (appdata v, uint id : SV_InstanceID)
{
Particle p = _Particles[id];
v2f o;
v.vertex.xyz = (v.vertex.xyz * _BaseScale) +
p.position;
o.vertex = mul(UNITY_MATRIX_VP,
float4(v.vertex.xyz, 1.0));
o.uv.xy = p.uv.xy;
o.uv.z = p.targetId;
o.color = p.color;
o.texid = p.targetId;
o.useTex = p.useTexture;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col;
if (i.useTex == 1)
{
col = UNITY_SAMPLE_TEX2DARRAY(_Textures,
i.uv);
col = pow(col, 1.5);
}
else
{
col = i.color;
}
return col;
}
Compute Shaderの計算に基づいてパーティクルをレンダリングする
頂点シェーダ フラグメントシェーダ
48
GPUインスタンシングで描画
DrawMeshInstancedIndirectメソッドで⼀度に描画
private void DrawParticles()
{
Graphics.DrawMeshInstancedIndirect(
_particleMesh,
0, // submesh index
_particleMat,
new Bounds(Vector3.zero, Vector3.one * 32f),
_argsBuffer,
0,
null,
UnityEngine.Rendering.ShadowCastingMode.Off,
false,
gameObject.layer
);
}
49
50
Nreal Light(Android)でも現実的なレベルで動く!
51
iPad版に⽐べて
⾊味が明るすぎる
視野⾓の端にモデルを
配置すると枠が気になる
パーティクルを
⼤量に出すための⼯夫
まとめ
⾊を2乗する
視界の端を
フェードさせる
Compute Shaderで
がんばる
52
ご静聴ありがとうございました
53
@edo_m18 @edom18
⽐留間 和也
http://edom18.hateblo.jp/e.blog:
MESONではARグラス時代のユースケースを開発する
エンジニアを積極募集中です。
興味がある⽅はぜひご連絡ください!
MESON

More Related Content

Similar to ARグラスで 魅力的な絵作り

Inside of excel 方眼紙撲滅委員会 #pyfes
Inside of excel 方眼紙撲滅委員会 #pyfesInside of excel 方眼紙撲滅委員会 #pyfes
Inside of excel 方眼紙撲滅委員会 #pyfes
Takeshi Komiya
 
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築するピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
Takahito Tejima
 
Cocos2d x-sprite3d
Cocos2d x-sprite3dCocos2d x-sprite3d
Cocos2d x-sprite3d
aktsk
 

Similar to ARグラスで 魅力的な絵作り (10)

Flashup13 Basic Training of Flare3D
Flashup13 Basic Training of Flare3DFlashup13 Basic Training of Flare3D
Flashup13 Basic Training of Flare3D
 
【Unity道場 2月】シェーダを書けるプログラマになろう
【Unity道場 2月】シェーダを書けるプログラマになろう【Unity道場 2月】シェーダを書けるプログラマになろう
【Unity道場 2月】シェーダを書けるプログラマになろう
 
Flashup 12 Basic Training of Away3D
Flashup 12 Basic Training of Away3DFlashup 12 Basic Training of Away3D
Flashup 12 Basic Training of Away3D
 
OpenCVとRGB-Dセンサで作ろう壁面タッチパネル
OpenCVとRGB-Dセンサで作ろう壁面タッチパネルOpenCVとRGB-Dセンサで作ろう壁面タッチパネル
OpenCVとRGB-Dセンサで作ろう壁面タッチパネル
 
Inside of excel 方眼紙撲滅委員会 #pyfes
Inside of excel 方眼紙撲滅委員会 #pyfesInside of excel 方眼紙撲滅委員会 #pyfes
Inside of excel 方眼紙撲滅委員会 #pyfes
 
Cocos2d xをさらにさわってみよう!
Cocos2d xをさらにさわってみよう!Cocos2d xをさらにさわってみよう!
Cocos2d xをさらにさわってみよう!
 
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築するピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
 
A-Framで始めるWebAR (Blenderハンズオンの続きver.)
A-Framで始めるWebAR (Blenderハンズオンの続きver.)A-Framで始めるWebAR (Blenderハンズオンの続きver.)
A-Framで始めるWebAR (Blenderハンズオンの続きver.)
 
Cocos2d x-sprite3d
Cocos2d x-sprite3dCocos2d x-sprite3d
Cocos2d x-sprite3d
 
Cocos2d-x v3.2を利用してシューティングゲームを作ろう!
Cocos2d-x v3.2を利用してシューティングゲームを作ろう!Cocos2d-x v3.2を利用してシューティングゲームを作ろう!
Cocos2d-x v3.2を利用してシューティングゲームを作ろう!
 

More from Kazuya Hiruma

More from Kazuya Hiruma (20)

MESONプロジェクトから学ぶこれからのAR開発に必要なこと
MESONプロジェクトから学ぶこれからのAR開発に必要なことMESONプロジェクトから学ぶこれからのAR開発に必要なこと
MESONプロジェクトから学ぶこれからのAR開発に必要なこと
 
PORTAL with Nreal in CES 2020 開発の学び @XR Hub
PORTAL with Nreal in CES 2020 開発の学び @XR HubPORTAL with Nreal in CES 2020 開発の学び @XR Hub
PORTAL with Nreal in CES 2020 開発の学び @XR Hub
 
AWE Nite ARKit3 Hackathon
AWE Nite ARKit3 HackathonAWE Nite ARKit3 Hackathon
AWE Nite ARKit3 Hackathon
 
MESONで手がけたARアプリ AR Developer Meetup #2
MESONで手がけたARアプリ AR Developer Meetup #2MESONで手がけたARアプリ AR Developer Meetup #2
MESONで手がけたARアプリ AR Developer Meetup #2
 
みんなレイ飛ばしてる?
みんなレイ飛ばしてる?みんなレイ飛ばしてる?
みんなレイ飛ばしてる?
 
VRゲーム制作楽しいよ! @UnityおとなのLT大会
VRゲーム制作楽しいよ! @UnityおとなのLT大会VRゲーム制作楽しいよ! @UnityおとなのLT大会
VRゲーム制作楽しいよ! @UnityおとなのLT大会
 
ElminaAR - Unity x ARKit 入門Meetup
ElminaAR - Unity x ARKit 入門MeetupElminaAR - Unity x ARKit 入門Meetup
ElminaAR - Unity x ARKit 入門Meetup
 
今すぐ始められるモバイルVR〜あなたも今日からVRエンジニア〜
今すぐ始められるモバイルVR〜あなたも今日からVRエンジニア〜今すぐ始められるモバイルVR〜あなたも今日からVRエンジニア〜
今すぐ始められるモバイルVR〜あなたも今日からVRエンジニア〜
 
UnityでARKitハンズオン
UnityでARKitハンズオンUnityでARKitハンズオン
UnityでARKitハンズオン
 
すぐそこにある未来〜AR〜
すぐそこにある未来〜AR〜すぐそこにある未来〜AR〜
すぐそこにある未来〜AR〜
 
VRで酔わないコンテンツ作り
VRで酔わないコンテンツ作りVRで酔わないコンテンツ作り
VRで酔わないコンテンツ作り
 
WebVRコンテンツ制作入門
WebVRコンテンツ制作入門WebVRコンテンツ制作入門
WebVRコンテンツ制作入門
 
WebVRってこんなことできるよ!
WebVRってこんなことできるよ!WebVRってこんなことできるよ!
WebVRってこんなことできるよ!
 
そしてWebVR
そしてWebVRそしてWebVR
そしてWebVR
 
Unity入門ハンズオン
Unity入門ハンズオンUnity入門ハンズオン
Unity入門ハンズオン
 
WebVR 酔いづらいコンテンツの作り方
WebVR 酔いづらいコンテンツの作り方WebVR 酔いづらいコンテンツの作り方
WebVR 酔いづらいコンテンツの作り方
 
WebVRことはじめ
WebVRことはじめWebVRことはじめ
WebVRことはじめ
 
集まっTail #5 LT
集まっTail #5 LT集まっTail #5 LT
集まっTail #5 LT
 
WebGL入門LT大会資料
WebGL入門LT大会資料WebGL入門LT大会資料
WebGL入門LT大会資料
 
WebGL入門ハンズオン資料
WebGL入門ハンズオン資料WebGL入門ハンズオン資料
WebGL入門ハンズオン資料
 

ARグラスで 魅力的な絵作り