0% found this document useful (0 votes)
35 views

UnityGraphicsProgramming-vol3

Uploaded by

hidane0v0
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

UnityGraphicsProgramming-vol3

Uploaded by

hidane0v0
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 163

Unity Graphics Programming

vol.3

IndieVisualLab

2018-10-08 IndieVisualLab

1
Unity Unity

tips
github
https://github.com/IndieVisualLab/UnityGraphicsProgramming3

Processing openFrameworks
3DCG Unity
3DCG

IndieVisualLab (& )
Unity
Unity
Unity

Compute Shader Geometry Shader


DirectX11 CPU
(C#)

github issue

2
Web https://docs.google.com/forms/d/e/
1FAIpQLSdxeansJvQGTWfZTBN_2RTuCK_kRqhA6QHTZKVXHCijQnC8zw/viewform
lab.indievisual@gmail.com

3
2

1 Baking Skinned Animation to Texture 7


1.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2 SkinedMeshRenderer 5000 . . 8
1.3 SkinnedMeshRenderer
. . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4 . . . . . . . . . . . . . . . . . . . . 13
1.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.6 5000 . . . . . . . . . . . . . . 27
1.7 . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

2 Gravitational N-Body Simulation 30


2.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.2 N-Body . . . . . . . . . . . . . . . . . . . . 31
2.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.6 . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.8 . . . . . . . . . . . . . . . . . . . . 47
2.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

3 ScreenSpaceFluidRendering 49
3.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.2 Screen Space Fluid Rendering . . . . . . . . . . . . . . . . . 49
3.3 Deferred Rendering , 50

4
3.4 G-Buffer . . . . . . . . . . . . . . . . . . . 52
3.5 CommandBuffer . . . . . . . . . . . . . . . . . . . . . . . 53
3.6 . . . . . . . . . . . . . . . . . . . . . . . 53
3.7 . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
3.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

4 GPU-Based Cellular Growth Simulation 69


4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.2 . . . . . . . . . . . . . . . . . . 70
4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

5 Reaction Diffusion 100


5.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
5.2 Reaction Diffusion . . . . . . . . . . . . . . . . . . . . . . . . 100
5.3 Unity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
5.4 . . . . . . . . . . . . . . . . . . . . . . . . 109
5.5 Surface Shader . . . . . . . . . . . . . . . . . . . . 113
5.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
5.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
5.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

6 Strange Attractor 121


6.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
6.2 Strange Attractor . . . . . . . . . . . . . . . . . . . . . . . . 121
6.3 Lorenz Attractor . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
6.4 Thomas’ Cyclically Symmetric Attractor . . . . . . . . . . . . . . 127
6.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
6.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

7 Portal Unity 132


7.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7.2 . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
7.4 VirtualCamera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
7.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

5
7.6 . . . . . . . . . . . . . . . . . . . . . . . . . 145
7.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
7.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

8 150
8.1 . . . . . . . . . . . . . . . . . . . . . . . 151
8.2 . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.3 . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.4 . . . . . . . . . . . . . . . . . . . . . . . . . . 156
8.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

158

6
1

Baking Skinned Animation to


Texture

1.1

1.1

Unity Animator
SkinnedMeshRenderer

7
1 Baking Skinned Animation to Texture

Animator SkinnedMeshRenderer
GPU

SkinnedMeshRenderer
1

Twitter @sugi_cho
(._.)

1.2 SkinedMeshRenderer
5000
5000
1890
3D

8
1.2 SkinedMeshRenderer 5000

1.2

FPS 8.8
1.3

9
1 Baking Skinned Animation to Texture

1.3 5000

Unity
Window Profiler : Ctr+7
Add Profiler GPU GPU Usage
GPU Usage

GPU Usage

10
1.2 SkinedMeshRenderer 5000

1.4 Profiler Window (GPU Usage)

CPU GPU CPU


GPU 1.4 GPU
7 PostLateUpdate.UpdateAllSkinnedMeshes
Camera.Renderer
GPU
GPU CPU
Usage PostLateUpdate.UpdateAllSkinnedMeshes Camera.Re
nder
PlayerSettings GPU Skinning
GPU CPU CPU
FPS GPU CPU
GPU GPU
CPU CPU
GPU

11
1 Baking Skinned Animation to Texture

1.3 SkinnedMeshRenderer

SkinnedMeshRenderer
SkinnedMeshRenderer.BakeMesh(Mesh)

1.1 SkinnedMeshRenderer.BakeMesh() Example

1: Animator animator;
2: SkinnedMeshRenderer skinedMesh;
3: List<Mesh> meshList;
4:
5: void Start(){
6: animator = GetComponent<Animator>();
7: skinnedMesh = GetComponentInChildren<SkinnedMeshRenderer>();
8: meshList = new List<Mesh>();
9: animator.Play("Run");
10: }
11:
12: void Update(){
13: var mesh = new Mesh();
14: skinnedMesh.BakeMesh(mesh);
15: //mesh
16: meshList.Add(mesh);
17: }

Animator Run SkinnedMeshRenderer


Mesh meshList
1.1
meshList
MeshFilter.sharedMesh SkinnedMeshRenderer

12
1.4

1.4
Mesh
Mesh.indeces, Mesh.uv

1.4.1 Vector3
Vector3

1.2

1.2 Update Mesh

1: Mesh objMesh;
2: List<Vector3>[] vertecesLists;
3: List<Vector3>[] normalsLists;
4: //
5: //Mesh.SetVertices(List<Vector3>)
6:
7: void Start(){
8: objMesh = GetComponent<MeshFilter>().mesh;
9: objMesh.MarkDynamic();
10: }
11:
12: void Update(){
13: var frame = xx;
14: //
15:
16: objMesh.SetVertices(vertecesLists[frame]);
17: objMesh.SetNormals(normalsLists[frame]);
18: }

CPU

VertexTextureFetch

CPU

13
1 Baking Skinned Animation to Texture

1.4.2

Unity Mesh Unity 3D


UV Mesh.vert
ices Vector3
1.1
Unity Texture2D (texture.width)
texture.height (Color)
1.2
1.1 Vector3
x float x
y float y
z float z

1.2 Color
r float
g float
b float
a float

Mesh.vertices 1.1 x,y,z Texture2D


1.2 r,g,b EditorScript TextureAsset

1.3
1.3

1: public void CreateTex(Mesh sourceMesh)


2: {
3: var vertCount = sourceMesh.vertexCount;
4: var width = Mathf.FloorToInt(Mathf.Sqrt(vertCount));
5: var height = Mathf.CeilToInt((float)vertCount / width);
6: // width,height
7:
8: posTex = new Texture2D(width, height, TextureFormat.RGBAFloat, false);
9: normTex = new Texture2D(width, height, TextureFormat.RGBAFloat, false);
10: //Color[] Texture2D

14
1.4

11: //TextureFormat.RGBAFloat Float


12:
13: var vertices = sourceMesh.vertices;
14: var normals = sourceMesh.normals;
15: var posColors = new Color[width * height];
16: var normColors = new Color[width * height];
17: //
18:
19: for (var i = 0; i < vertCount; i++)
20: {
21: posColors[i] = new Color(
22: vertices[i].x,
23: vertices[i].y,
24: vertices[i].z
25: );
26: normColors[i] = new Color(
27: normals[i].x,
28: normals[i].y,
29: normals[i].z
30: );
31: }
32: // Color.rgb = Vector3.xyz
33: // (Color[])
34:
35: posTex.SetPixels(posColors);
36: normTex.SetPixels(normColors);
37: posTex.Apply();
38: normTex.Apply();
39: //
40: }

Mesh

15
1 Baking Skinned Animation to Texture

1.5 Mesh Texture

Index

1.5
Unity Texture2D.SetPixels(Color[]) ColorForm
at.RGBA32,ARGB32,RGB24,Alpha8
Fixed
RGBAHalf, RGBAFloat
1 Clamp
Color RGB
1/256

1.4.3 AnimationClip.SampleAnimation()
AnimationClip.SampleAnimation
(gameObject, time); GameObject

16
1.5

AnimationClip Animation
Animator Animation Animator

AnimationClip

1.5

• AnimationClipTextureBaker.cs
• MeshInfoTextureGen.compute
• TextureAnimPlayer.shader

AnimationClipTextureBaker Animation Animator Anima-


tionClip AnimationClip
ComputeBuffer AnimationClip Mesh
ComputeBuffer MeshInfo-
TextureGen.compute
ComputeShader
TextureAnimPlayer.shader
Shader

17
1 Baking Skinned Animation to Texture

18

1.6 AnimationClipTextureBaker Inspector


1.5

AnimationClipTextureBaker
ComputeShader Sh
ader AnimationClip Clips
1.6

1.7

ContextMenuAttribute Unity

bake
texture Bake 1.6

1.4 AnimationClipTextureBaker.cs

1: using System.Collections.Generic;
2: using System.Linq;
3: using UnityEngine;
4:
5: #if UNITY_EDITOR
6: using UnityEditor;

19
1 Baking Skinned Animation to Texture

7: using System.IO;
8: #endif
9:
10: public class AnimationClipTextureBaker : MonoBehaviour
11: {
12:
13: public ComputeShader infoTexGen;
14: public Shader playShader;
15: public AnimationClip[] clips;
16:
17: //
18: public struct VertInfo
19: {
20: public Vector3 position;
21: public Vector3 normal;
22: }
23:
24: //Reset() GameObject
25: private void Reset()
26: {
27: var animation = GetComponent<Animation>();
28: var animator = GetComponent<Animator>();
29:
30: if (animation != null)
31: {
32: clips = new AnimationClip[animation.GetClipCount()];
33: var i = 0;
34: foreach (AnimationState state in animation)
35: clips[i++] = state.clip;
36: }
37: else if (animator != null)
38: clips = animator.runtimeAnimatorController.animationClips;
39: //Animation Animator AnimationClip

40: }
41:
42: [ContextMenu("bake texture")]
43: void Bake()
44: {
45: var skin = GetComponentInChildren<SkinnedMeshRenderer>();
46: var vCount = skin.sharedMesh.vertexCount;
47: var texWidth = Mathf.NextPowerOfTwo(vCount);
48: var mesh = new Mesh();
49:
50: foreach (var clip in clips)
51: {
52: var frames = Mathf.NextPowerOfTwo((int)(clip.length / 0.05f));
53: var dt = clip.length / frames;
54: var infoList = new List<VertInfo>();
55:
56: var pRt = new RenderTexture(texWidth, frames,
57: 0, RenderTextureFormat.ARGBHalf);
58: pRt.name = string.Format("{0}.{1}.posTex", name, clip.name);
59: var nRt = new RenderTexture(texWidth, frames,
60: 0, RenderTextureFormat.ARGBHalf);
61: nRt.name = string.Format("{0}.{1}.normTex", name, clip.name);
62: foreach (var rt in new[] { pRt, nRt })
63: {

20
1.5

64: rt.enableRandomWrite = true;


65: rt.Create();
66: RenderTexture.active = rt;
67: GL.Clear(true, true, Color.clear);
68: }
69: //
70:
71: for (var i = 0; i < frames; i++)
72: {
73: clip.SampleAnimation(gameObject, dt * i);
74: //AnimationClip GameObject
75: skin.BakeMesh(mesh);
76: //BakeMesh() Skinning
77:
78: infoList.AddRange(Enumerable.Range(0, vCount)
79: .Select(idx => new VertInfo()
80: {
81: position = mesh.vertices[idx],
82: normal = mesh.normals[idx]
83: })
84: );
85: //
86: }
87: var buffer = new ComputeBuffer(
88: infoList.Count,
89: System.Runtime.InteropServices.Marshal.SizeOf(
90: typeof(VertInfo)
91: )
92: );
93: buffer.SetData(infoList.ToArray());
94: // ComputeBuffer
95:
96: var kernel = infoTexGen.FindKernel("CSMain");
97: uint x, y, z;
98: infoTexGen.GetKernelThreadGroupSizes(
99: kernel,
100: out x,
101: out y,
102: out z
103: );
104:
105: infoTexGen.SetInt("VertCount", vCount);
106: infoTexGen.SetBuffer(kernel, "Info", buffer);
107: infoTexGen.SetTexture(kernel, "OutPosition", pRt);
108: infoTexGen.SetTexture(kernel, "OutNormal", nRt);
109: infoTexGen.Dispatch(
110: kernel,
111: vCount / (int)x + 1,
112: frames / (int)y + 1,
113: 1
114: );
115: //ComputeShader
116:
117: buffer.Release();
118:
119: //
120: #if UNITY_EDITOR
121: var folderName = "BakedAnimationTex";

21
1 Baking Skinned Animation to Texture

122: var folderPath = Path.Combine("Assets", folderName);


123: if (!AssetDatabase.IsValidFolder(folderPath))
124: AssetDatabase.CreateFolder("Assets", folderName);
125:
126: var subFolder = name;
127: var subFolderPath = Path.Combine(folderPath, subFolder);
128: if (!AssetDatabase.IsValidFolder(subFolderPath))
129: AssetDatabase.CreateFolder(folderPath, subFolder);
130:
131: var posTex = RenderTextureToTexture2D.Convert(pRt);
132: var normTex = RenderTextureToTexture2D.Convert(nRt);
133: Graphics.CopyTexture(pRt, posTex);
134: Graphics.CopyTexture(nRt, normTex);
135:
136: var mat = new Material(playShader);
137: mat.SetTexture("_MainTex", skin.sharedMaterial.mainTexture);
138: mat.SetTexture("_PosTex", posTex);
139: mat.SetTexture("_NmlTex", normTex);
140: mat.SetFloat("_Length", clip.length);
141: if (clip.wrapMode == WrapMode.Loop)
142: {
143: mat.SetFloat("_Loop", 1f);
144: mat.EnableKeyword("ANIM_LOOP");
145: }
146:
147: var go = new GameObject(name + "." + clip.name);
148: go.AddComponent<MeshRenderer>().sharedMaterial = mat;
149: go.AddComponent<MeshFilter>().sharedMesh = skin.sharedMesh;
150: // Prefab
151:
152: AssetDatabase.CreateAsset(posTex,
153: Path.Combine(subFolderPath, pRt.name + ".asset"));
154: AssetDatabase.CreateAsset(normTex,
155: Path.Combine(subFolderPath, nRt.name + ".asset"));
156: AssetDatabase.CreateAsset(mat,
157: Path.Combine(subFolderPath,
158: string.Format("{0}.{1}.animTex.asset", name, clip.name)));
159: PrefabUtility.CreatePrefab(
160: Path.Combine(folderPath, go.name + ".prefab")
161: .Replace("\\", "/"), go);
162: AssetDatabase.SaveAssets();
163: AssetDatabase.Refresh();
164: #endif
165: }
166: }
167: }

RenderTexture GPU Graphics.CopyTexture(r


t,tex2d); Texture2D UnityAsset

1.4 119,120
ComputeShader
GPU

22
1.5

1.5

1.5 MeshInfoTextureGen.compute

1: #pragma kernel CSMain


2:
3: struct MeshInfo{
4: float3 position;
5: float3 normal;
6: };
7:
8: RWTexture2D<float4> OutPosition;
9: RWTexture2D<float4> OutNormal;
10: StructuredBuffer<MeshInfo> Info;
11: int VertCount;
12:
13: [numthreads(8,8,1)]
14: void CSMain (uint3 id : SV_DispatchThreadID)
15: {
16: int index = id.y * VertCount + id.x;
17: MeshInfo info = Info[index];
18:
19: OutPosition[id.xy] = float4(info.position, 1.0);
20: OutNormal[id.xy] = float4(info.normal, 1.0);
21: // x ID y
22: }

1.8

1.8

x
y
uv.y

Texture.FilterMode = Bilinear

23
1 Baking Skinned Animation to Texture

Shader

Run W
rapMode = Repeat

WrapMode = Clamp
Shader
1.6
1.6 TextureAnimPlayer.shaer

1: Shader "Unlit/TextureAnimPlayer"
2: {
3: Properties
4: {
5: _MainTex ("Texture", 2D) = "white" {}
6: _PosTex("position texture", 2D) = "black"{}
7: _NmlTex("normal texture", 2D) = "white"{}
8: _DT ("delta time", float) = 0e
9:
10: _Length ("animation length", Float) = 1
11: [Toggle(ANIM_LOOP)] _Loop("loop", Float) = 0
12: }
13: SubShader
14: {
15: Tags { "RenderType"="Opaque" }
16: LOD 100 Cull Off
17:
18: Pass
19: {
20: CGPROGRAM
21: #pragma vertex vert
22: #pragma fragment frag
23: #pragma multi_compile ___ ANIM_LOOP
24: //
25:
26: #include "UnityCG.cginc"
27:
28: #define ts _PosTex_TexelSize
29:
30: struct appdata
31: {
32: float2 uv : TEXCOORD0;
33: };
34:
35: struct v2f
36: {
37: float2 uv : TEXCOORD0;
38: float3 normal : TEXCOORD1;

24
1.5

39: float4 vertex : SV_POSITION;


40: };
41:
42: sampler2D _MainTex, _PosTex, _NmlTex;
43: float4 _PosTex_TexelSize;
44: float _Length, _DT;
45:
46: v2f vert (appdata v, uint vid : SV_VertexID)
47: //SV_VertexID ID
48: {
49: float t = (_Time.y - _DT) / _Length;
50: #if ANIM_LOOP
51: t = fmod(t, 1.0);
52: #else
53: t = saturate(t);
54: #endif
55:
56: float x = (vid + 0.5) * ts.x;
57: float y = t;
58: //uv.x ID
59: //uv.y (t)
60:
61: float4 pos = tex2Dlod(
62: _PosTex,
63: float4(x, y, 0, 0)
64: );
65: float3 normal = tex2Dlod(
66: _NmlTex,
67: float4(x, y, 0, 0)
68: );
69: //
70:
71: v2f o;
72: o.vertex = UnityObjectToClipPos(pos);
73: o.normal = UnityObjectToWorldNormal(normal);
74: o.uv = v.uv;
75: return o;
76: }
77:
78: half4 frag (v2f i) : SV_Target
79: {
80: half diff = dot(
81: i.normal,
82: float3(0, 1, 0)
83: ) * 0.5 + 0.5;
84: half4 col = tex2D(_MainTex, i.uv);
85: return diff * col;
86: }
87: ENDCG
88: }
89: }
90: }

Shader VertexTextureFetch VTF

25
1 Baking Skinned Animation to Texture

ID SV_Ve
rtexID
appdata uv appdata P
OSITION,NORMAL
UV uv.y
0 1.0
uv.x vid uv.x = (vid + 0.5) * _TexelSize.
x 0.5
Bilinear (n + 0.5) /
ID
0.5

{TextureName}_TexelSize float4
1.7 (Unity )

x 1.0/width
y 1.0/height
z width
w height

26
1.6 5000

1.6 5000

1.9 5000

SkinnedMeshRenderer Renderer
FPS
8 56.4 1.9
PC GPU GeForce MX150 NVIDIA Pascal GPU

27
1 Baking Skinned Animation to Texture

SkinnedMeshRenderer GPU
Shader

Grap
hics.DrawMeshInstancedIndirect() 4000
Shader GitHub

1.7

Shader AnimatorController

4K 8K 16K
1

VertexShader
PostLateUpdate.UpdateAllSkinnedMeshes C
amera.Render

AnimatorController Unity

28
1.8

1.8


• GPU GPU

29
2

Gravitational N-Body
Simulation

2.1

Gravitational N-Body Simulation GPU

2.1 Result

https://github.com/IndieVisualLab/UnityGraphicsProgramming3

30
2.2 N-Body

Assets/NBodySimulation

2.2 N-Body
N N-Body
N-Body

N-Body

N-Body



2.3

Mm
f =G (2.1)
r2
f G M, m 2
r 2 ( )

Unity 3
2 ( i, j )

m i mj r ij
f ij = G · (2.2)
∥r ij ∥2 ∥r ij ∥

f ij i j mi , m j 2
rij j i

31
2 Gravitational N-Body Simulation

2 1 (i)
Fi
! ! mj r ij
Fi = f ij = Gmi · (2.3)
∥r ij ∥3
j∈N j∈N

Softening ε

! mj r ij
F i ≃ Gmi · 3 (2.4)
j∈N (∥r ij ∥2 + ε) 2

( 0 )
ma = f

Fi
mi ai = F i =⇒ ai = (2.5)
mi

! mj r ij
ai ≃ G · 3 (2.6)
j∈N (∥r ij ∥2 + ε) 2

32
2.4

2.4
(2.6)

2.2

2.4.1
f (t)

df f (t + ∆t) − f (t)
= lim (2.7)
dt ∆t→0 ∆t

33
2 Gravitational N-Body Simulation

f(t)

傾き = 微分値

fn

fn

∆t
t
tn tn

2.3

tn
∆t
(2.7)

2.4.2

∆t

df f (t + ∆t) − f (t)
≃ (2.8)
dt ∆t
∆t
∆t

2.2

34
2.5

dx dx
x(t) dt
v(t) dt
a(t)
2.4 ( )

(2.8) 2.4

dx x(t + ∆t) − x(t)


≃ = v(t) (2.9)
dt ∆t
dv v(t + ∆t) − v(t)
≃ = a(t) (2.10)
dt ∆t
(2.9), (2.10)

x(t + ∆t) = x(t) + v(t + ∆t)∆t = x(t) + (v(t) + a(t)∆t)∆t (2.11)

t ∆t

( )
∆t (
) 1 (60fps 1/60 )

2.5
SimpleNBodySimula-
tion.unity

2.5.1 CPU

(2.7) 1

Body.cs

35
2 Gravitational N-Body Simulation

public struct Body


{
public Vector3 position;
public Vector3 velocity;
public float mass;
}

Byte System.Runtime.InteropServices
Marshal.SizeOf(Type t)
SimpleNBodySimulation.cs

void InitBuffer()
{
// (Read/Write )
bodyBuffers = new ComputeBuffer[2];

// Body
bodyBuffers[READ] = new ComputeBuffer(numBodies,
Marshal.SizeOf(typeof(Body)));

bodyBuffers[WRITE] = new ComputeBuffer(numBodies,


Marshal.SizeOf(typeof(Body)));
}

0
*1

SimpleNBodySimulation.cs

void DistributeBodies()
{
Random.InitState(seed);

//

*1

36
2.5

float scale = positionScale


* Mathf.Max(1, numBodies / DEFAULT_PARTICLE_NUM);

//
Body[] bodies = new Body[numBodies];

int i = 0;
while (i < numBodies)
{
//
Vector3 pos = Random.insideUnitSphere;

//
bodies[i].position = pos * scale;
bodies[i].velocity = Vector3.zero;
bodies[i].mass = Random.Range(0.1f, 1.0f);

i++;
}

//
bodyBuffers[READ].SetData(bodies);
bodyBuffers[WRITE].SetData(bodies);

ComputeShader ∆t
Unity Time.deltaTime GPU

SimpleNBodySimulation.cs

void Update()
{
//
// t
NBodyCS.SetFloat("_DeltaTime", Time.deltaTime);
//
NBodyCS.SetFloat("_Damping", damping);
// Softening
NBodyCS.SetFloat("_SofteningSquared", softeningSquared);
//
NBodyCS.SetInt("_NumBodies", numBodies);

37
2 Gravitational N-Body Simulation

//
NBodyCS.SetVector("_ThreadDim",
new Vector4(SIMULATION_BLOCK_SIZE, 1, 1, 0));

//
NBodyCS.SetVector("_GroupDim",
new Vector4(Mathf.CeilToInt(numBodies / SIMULATION_BLOCK_SIZE), 1, 1, 0));

//
NBodyCS.SetBuffer(0, "_BodiesBufferRead", bodyBuffers[READ]);
NBodyCS.SetBuffer(0, "_BodiesBufferWrite", bodyBuffers[WRITE]);

//
NBodyCS.Dispatch(0,
Mathf.CeilToInt(numBodies / SIMULATION_BLOCK_SIZE), 1, 1);

// Read/Write ( )
Swap(bodyBuffers);
}

ParticleRenderer.cs

void OnRenderObject()
{
particleRenderMat.SetPass(0);
particleRenderMat.SetBuffer("_Particles"", bodyBuffers[READ]);

Graphics.DrawProcedural(MeshTopology.Points, numBodies);
}

2.5.2 GPU
N-Body
O(n2 )
UnityGraphicsProgramming Vol1 3
SharedMemory( )

38
2.5

I/O

2.5

(DispatchThreadID)

1
( /
) 256 (SIMULA-

39
2 Gravitational N-Body Simulation

TION_BLOCK_SIZE) 5x5
256x256

Sync (Sync
) Sync

CPU ComputeShader

Body
Body.cginc cginc

SimpleNBodySimulation.compute

#include "Body.cginc"

//
cbuffer cb {
float _SofteningSquared, _DeltaTime, _Damping;
uint _NumBodies;
float4 _GroupDim, _ThreadDim;
};

//
StructuredBuffer<Body> _BodiesBufferRead;
RWStructuredBuffer<Body> _BodiesBufferWrite;

// ( )
groupshared Body sharedBody[SIMULATION_BLOCK_SIZE];

SimpleNBodySimulation.compute

float3 computeBodyForce(Body body, uint3 groupID, uint3 threadID)


{

uint start = 0; //
uint finish = _NumBodies;

float3 acc = (float3)0;

40
2.5

int currentTile = 0;

// ( )
for (uint i = start; i < finish; i += SIMULATION_BLOCK_SIZE)
{
//
// sharedBody[ ID]
// = _BodiesBufferRead[ ID * + ID]
sharedBody[threadID.x]
= _BodiesBufferRead[wrap(groupID.x + currentTile, _GroupDim.x)
* SIMULATION_BLOCK_SIZE + threadID.x];

//
GroupMemoryBarrierWithGroupSync();

//
acc = gravitation(body, acc, threadID);

GroupMemoryBarrierWithGroupSync();

currentTile++; //
}

return acc;

for

2.6 ID for

41
2 Gravitational N-Body Simulation

for
SimpleNBodySimulation.compute

float3 gravitation(Body body, float3 accel, uint3 threadID)


{

//
//
for (uint i = 0; i < SIMULATION_BLOCK_SIZE;)
{
accel = bodyBodyInteraction(accel, sharedBody[i], body);
i++;
}

return accel;
}

return

(2.2)
SimpleNBodySimulation.compute

//
float3 bodyBodyInteraction(float3 acc, Body b_i, Body b_j)
{
float3 r = b_i.position - b_j.position;

// distSqr = dot(r_ij, r_ij) + EPS^2


float distSqr = r.x * r.x + r.y * r.y + r.z * r.z;
distSqr += _SofteningSquared;

// invDistCube = 1/distSqr^(3/2)
float distSixth = distSqr * distSqr * distSqr;
float invDistCube = 1.0f / sqrt(distSixth);

// s = m_j * invDistCube
float s = b_j.mass * invDistCube;

// a_i = a_i + s * r_ij


acc += r * s;

return acc;
}

42
2.6

SimpleNBodySimulation.compute

[numthreads(SIMULATION_BLOCK_SIZE,1,1)]
void CSMain (
uint3 groupID : SV_GroupID, // ID
uint3 threadID : SV_GroupThreadID, // ID
uint3 DTid : SV_DispatchThreadID // ID
) {

//
uint index = DTid.x;

//
Body body = _BodiesBufferRead[index];

float3 force = computeBodyForce(body, groupID, threadID);

body.velocity += force * _DeltaTime;


body.velocity *= _Damping;

//
body.position += body.velocity * _DeltaTime;

_BodiesBufferWrite[index] = body;

2.6
*2 GPU

2.6.1

*2 Unity Graphics Programming Vol.1 - 5 SPH

43
2 Gravitational N-Body Simulation

( 2.8 )

Quad
1 xy Quad
*3

2.7 GeometryShader Quad

Quad *4

Quad

*3 UnityGraphicsProgramming Vol.1

*4

44
2.6

x
x y y
x
x
y

2.8

ParticleRenderer.shader

45
2 Gravitational N-Body Simulation

[maxvertexcount(4)]
void geom(point v2g input[1], inout TriangleStream<g2f> outStream) {
g2f o;

float4 pos = input[0].pos;

float4x4 billboardMatrix = UNITY_MATRIX_V;

//
billboardMatrix._m03 = billboardMatrix._m13 =
billboardMatrix._m23 = billboardMatrix._m33 = 0;

for (int x = 0; x < 2; x++) {


for (int y = 0; y < 2; y++) {
float2 uv = float2(x, y);
o.uv = uv;

o.pos = pos
+ mul(transpose(billboardMatrix), float4((uv * 2 - float2(1, 1))
* _Scale, 0, 1));

o.pos = mul(UNITY_MATRIX_VP, o.pos);

o.id = input[0].id;

outStream.Append(o);
}
}

outStream.RestartStrip();
}

2.7

46
2.8

2.9 ( ...)

2.8
NBodySimulation.unity 1

NBodySimulation.compute

float3 computeBodyForce(Body body, uint3 groupID, uint3 threadID)


{

uint finish = _NumBodies / div; //

2.1

47
2 Gravitational N-Body Simulation

2.9
Gravitational N-Body Simulation GPU
N-Body
Unity

2.10
• GPU Gems 3 - Chapter 31. Fast N-Body Simulation with CUDA
• N -
http://www.astro-wakate.org/ss2011/web/ss11_proceedings/
proceeding/galaxy_fujii.pdf
• - wgld.org https://wgld.org/d/webgl/w035.
html

48
3

ScreenSpaceFluidRendering

3.1
Deferred Shading
Screen Space Fluid Rendering

3.2 Screen Space Fluid Rendering

Screen Space Fluid Rendering

3.1 Screen Space Fluid Rendering

49
3 ScreenSpaceFluidRendering

3.1

Deferred Rendering

3.3 Deferred Rendering ,

2
Forward Rendering
3.2 Forward Rendering Deferred Rendering

3.2 Foward Rendering Deferred Rendering

50
3.3 Deferred Rendering ,

Forward Rendering 1
Deferred Rendering
2 G-Buffer
2

2 "Deferred " Rendering


Deferred Rendering



• G Buffer PostEffect SSAO


• MSAA


• Orthographic

Deferred Rendering Unity


Deferred Rendering

• Unity Pro
• MRT
• 3.0
• two-sided

Deferred Rendering Orthographic


Orthographic
Forward Rendering

51
3 ScreenSpaceFluidRendering

3.4 G-Buffer

2 G-Buffer Unity
G-Buffer
G-Buffer

render target format datat type


RT0 ARGB32 Diffuse color (RGB), Occulusion (A)
RT1 ARGB32 Specular color (RGB), Roughness (A)
RT2 ARGB2101010 World space normal (RGB)
RT3 ARGB2101010 Emission + (Ambient + Reflections + Lightmaps)
Z-buffer Depth + Stencil

G-Buffer

shader property name data type


_CameraGBufferTexture0 Diffuse color (RGB), occulusion (A)
_CameraGBufferTexture1 Specular color (RGB)
_CameraGBufferTexture2 World space normal (RGB)
_CameraGBufferTexture3 Emission + (Ambient + Reflections + Lightmaps)
_CameraDepthTexture Depth + Stencil

Assets/ScreenSpaceFluidRendering/Scenes/ShowG-
BufferTest G-Buffer

52
3.5 CommandBuffer

3.3 G-Buffer

3.5 CommandBuffer
CommandBuffer Unity API

CPU
GPU
GPU

Unity Graphics.DrawMesh(),
Graphics.DrawProcedural()
Unity API CommandBuffer Unity

Unity
CommandBuffer

https://docs.unity3d.com/ja/current/Manual/GraphicsCommandBuffers.html

3.6
3DCG

53
3 ScreenSpaceFluidRendering

Homogeneous Coordinates
3 (x,y,z) (x,y,z,w) 4
Homogeneous Coordinates
4 4x4 Matrix
4x4 Matrix
4
(x/w, y/w, z/w, 1) =
(x, y, z, w)

Object Space ( , , )

World Space ,
World Space
World Space
Modeling Transform Object Space

Eye(View) Space ,
Eye Space

View Matrix View Transform World


Space

Clip Space ,
Clip Space View Matrix
field of view(FOV) near clip far clip Projection
Matrix View Space
Projection Transform

Normalized Device Coordinates


Clip Space xyz w
-1<=x<=1 -1<=y<=1 0<=z<=1
Normalized Device Coordinates
NDC Persepective Devide

54
3.7

Screen(Window) Space
Normalized Device Coordinates
Direct3D
Deferred Rendering

3.3 3DCG

3.4

3.7

Assets/ScreenSpaceFluidRendering/Scenes/ScreenSpaceFluidRendering

55
3 ScreenSpaceFluidRendering

3.7.1
Screen Space Fluid Rendering

1.
2.
3.
4.

3.7.2

ScreenSpaceFluidRenderer.cs
RenderParticleDepth.shader
BilateralFilterBlur.shader
CalcNormal.shader
RenderGBuffer.shader G-Buffer

3.7.3 CommandBuffer
ScreenSpaceFluidRendering.cs OnWillRenderObject
CommandBuffer Com-
mandBuffer

ScreenSpaceFluidRendering.cs

// (MeshRenderer)
void OnWillRenderObject()
{
//
var act = gameObject.activeInHierarchy && enabled;
if (!act)
{
CleanUp();
return;
}
//
var cam = Camera.current;
if (!cam)
{
return;

56
3.7

//
// CommandBuffer
if (!_cameras.ContainsKey(cam))
{
// CommandBuffer
var buf = new CmdBufferInfo();
buf.pass = CameraEvent.BeforeGBuffer;
buf.buffer = new CommandBuffer();
buf.name = "Screen Space Fluid Renderer";
// G-Buffer
// CommandBuffer
cam.AddCommandBuffer(buf.pass, buf.buffer);

// CommandBuffer
_cameras.Add(cam, buf);
}

Camera.AddCommandBuffer(CameraEvent evt, Render-


ing.CommandBuffer buffer)
CameraEvent.BeforeGBuffer
G-Buffer

Disable RemoveCommandBuffer
Cleanup

CommandBuffer
CommandBuffer.Clear

3.7.4

ScreenSpaceFluidRendering.cs

// --------------------------------------------------------------------
// 1.
// --------------------------------------------------------------------
// ID
int depthBufferId = Shader.PropertyToID("_DepthBuffer");
// RenderTexture

57
3 ScreenSpaceFluidRendering

buf.GetTemporaryRT(depthBufferId, -1, -1, 24,


FilterMode.Point, RenderTextureFormat.RFloat);

//
buf.SetRenderTarget
(
new RenderTargetIdentifier(depthBufferId), //
new RenderTargetIdentifier(depthBufferId) //
);
//
buf.ClearRenderTarget(true, true, Color.clear);

//
_renderParticleDepthMaterial.SetFloat ("_ParticleSize", _particleSize);
// ComputeBuffer
_renderParticleDepthMaterial.SetBuffer("_ParticleDataBuffer",
_particleControllerScript.GetParticleDataBuffer());

//
buf.DrawProcedural
(
Matrix4x4.identity,
_renderParticleDepthMaterial,
0,
MeshTopology.Points,
_particleControllerScript.GetMaxParticleNum()
);

RenderParticleDepth.shader

// --------------------------------------------------------------------
// Vertex Shader
// --------------------------------------------------------------------
v2g vert(uint id : SV_VertexID)
{
v2g o = (v2g)0;
FluidParticle fp = _ParticleDataBuffer[id];
o.position = float4(fp.position, 1.0);
return o;
}

// --------------------------------------------------------------------
// Geometry Shader
// --------------------------------------------------------------------
//
static const float3 g_positions[4] =
{
float3(-1, 1, 0),
float3( 1, 1, 0),
float3(-1,-1, 0),
float3( 1,-1, 0),
};
// UV
static const float2 g_texcoords[4] =
{
float2(0, 1),

58
3.7

float2(1, 1),
float2(0, 0),
float2(1, 0),
};

[maxvertexcount(4)]
void geom(point v2g In[1], inout TriangleStream<g2f> SpriteStream)
{
g2f o = (g2f)0;
//
float3 vertpos = In[0].position.xyz;
// 4
[unroll]
for (int i = 0; i < 4; i++)
{
//
float3 pos = g_positions[i] * _ParticleSize;
pos = mul(unity_CameraToWorld, pos) + vertpos;
o.position = UnityObjectToClipPos(float4(pos, 1.0));
// UV
o.uv = g_texcoords[i];
//
o.vpos = UnityObjectToViewPos(float4(pos, 1.0)).xyz * float3(1, 1, 1);
//
o.size = _ParticleSize;

SpriteStream.Append(o);
}
SpriteStream.RestartStrip();
}

// --------------------------------------------------------------------
// Fragment Shader
// --------------------------------------------------------------------
struct fragmentOut
{
float depthBuffer : SV_Target0;
float depthStencil : SV_Depth;
};

fragmentOut frag(g2f i)
{
//
float3 N = (float3)0;
N.xy = i.uv.xy * 2.0 - 1.0;
float radius_sq = dot(N.xy, N.xy);
if (radius_sq > 1.0) discard;
N.z = sqrt(1.0 - radius_sq);

//
float4 pixelPos = float4(i.vpos.xyz + N * i.size, 1.0);
float4 clipSpacePos = mul(UNITY_MATRIX_P, pixelPos);
//
float depth = clipSpacePos.z / clipSpacePos.w; //

fragmentOut o = (fragmentOut)0;
o.depthBuffer = depth;
o.depthStencil = depth;

59
3 ScreenSpaceFluidRendering

return o;
}

C#
RenderTexture Command-
Buffer.GetTemporaryRT RenderTexture
GetTemporaryRT
ID
ID Unity
int ID Shader.PropertyToID
ID

GetTemporaryRT 2,3 -1
Camera pixel
width, height
4 _DepthBuffer
+ 0
RenderTexture CommandBuffer.SetRenderTarget
ClearRenderTarget

CommandBuffer.DrawProcedural

3.5

60
3.7

Vertex Geometry
Fragment
UV

3.6

3.7.5

61
3 ScreenSpaceFluidRendering

3.7

3.7.6
X Y

3.8

CalcNormal.shader

62
3.7

// --------------------------------------------------------------------
// Fragment Shader
// --------------------------------------------------------------------
// UV
float3 uvToEye(float2 uv, float z)
{
float2 xyPos = uv * 2.0 - 1.0;
//
float4 clipPos = float4(xyPos.xy, z, 1.0);
//
float4 viewPos = mul(unity_CameraInvProjection, clipPos);
//
viewPos.xyz = viewPos.xyz / viewPos.w;

return viewPos.xyz;
}

//
float sampleDepth(float2 uv)
{
#if UNITY_REVERSED_Z
return 1.0 - tex2D(_DepthBuffer, uv).r;
#else
return tex2D(_DepthBuffer, uv).r;
#endif
}

//
float3 getEyePos(float2 uv)
{
return uvToEye(uv, sampleDepth(uv));
}

float4 frag(v2f_img i) : SV_Target


{
// UV
float2 uv = i.uv.xy;
//
float depth = tex2D(_DepthBuffer, uv);

//
#if UNITY_REVERSED_Z
if (Linear01Depth(depth) > 1.0 - 1e-3)
discard;
#else
if (Linear01Depth(depth) < 1e-3)
discard;
#endif
//
float2 ts = _DepthBuffer_TexelSize.xy;

// uv
float3 posEye = getEyePos(uv);

// x
float3 ddx = getEyePos(uv + float2(ts.x, 0.0)) - posEye;
float3 ddx2 = posEye - getEyePos(uv - float2(ts.x, 0.0));
ddx = abs(ddx.z) > abs(ddx2.z) ? ddx2 : ddx;

63
3 ScreenSpaceFluidRendering

// y
float3 ddy = getEyePos(uv + float2(0.0, ts.y)) - posEye;
float3 ddy2 = posEye - getEyePos(uv - float2(0.0, ts.y));
ddy = abs(ddy.z) > abs(ddy2.z) ? ddy2 : ddy;

//
float3 N = normalize(cross(ddx, ddy));

//
float4x4 vm = _ViewMatrix;
N = normalize(mul(vm, float4(N, 0.0)));

// (-1.0 1.0) (0.0 1.0)


float4 col = float4(N * 0.5 + 0.5, 1.0);

return col;
}

3.9

3.7.7
G-Buffer
G-Buffer

64
3.7

ScreenSpaceFluidRendering.cs

// --------------------------------------------------------------------
// 4. G-Buffer
// --------------------------------------------------------------------
buf.SetGlobalTexture("_NormalBuffer", normalBufferId); //
buf.SetGlobalTexture("_DepthBuffer", depthBufferId); //

//
_renderGBufferMaterial.SetColor("_Diffuse", _diffuse );
_renderGBufferMaterial.SetColor("_Specular",
new Vector4(_specular.r, _specular.g, _specular.b, 1.0f - _roughness));
_renderGBufferMaterial.SetColor("_Emission", _emission);

// G-Buffer
buf.SetRenderTarget
(
new RenderTargetIdentifier[4]
{
BuiltinRenderTextureType.GBuffer0, // Diffuse
BuiltinRenderTextureType.GBuffer1, // Specular + Roughness
BuiltinRenderTextureType.GBuffer2, // World Normal
BuiltinRenderTextureType.GBuffer3 // Emission
},
BuiltinRenderTextureType.CameraTarget // Depth
);
// G-Buffer
buf.DrawMesh(quad, Matrix4x4.identity, _renderGBufferMaterial);

RenderGBuffer.shader

// GBuffer
struct gbufferOut
{
half4 diffuse : SV_Target0; //
half4 specular : SV_Target1; //
half4 normal : SV_Target2; //
half4 emission : SV_Target3; //
float depth : SV_Depth; //
};

sampler2D _DepthBuffer; //
sampler2D _NormalBuffer;//

fixed4 _Diffuse; //
fixed4 _Specular; //
float4 _Emission; //

void frag(v2f i, out gbufferOut o)


{
float2 uv = i.screenPos.xy * 0.5 + 0.5;

65
3 ScreenSpaceFluidRendering

float d = tex2D(_DepthBuffer, uv).r;


float3 n = tex2D(_NormalBuffer, uv).xyz;

#if UNITY_REVERSED_Z
if (Linear01Depth(d) > 1.0 - 1e-3)
discard;
#else
if (Linear01Depth(d) < 1e-3)
discard;
#endif

o.diffuse = _Diffuse;
o.specular = _Specular;
o.normal = float4(n.xyz , 1.0);

o.emission = _Emission;
#ifndef UNITY_HDR_ON
o.emission = exp2(-o.emission);
#endif

o.depth = d;
}

SetRenderTarget G-Buffer
1 BuiltinRender-
TextureType GBuffer0 GBuffer1 GBuffer2 GBuffer3
RenderTargetIdentifier 2
CameraTarget G-Buffer

DrawMesh

3.7.8 RenderTexture
GetTemporaryRT RenderTexture Re-
leaseTemporaryRT

66
3.8

3.7.9

3.10

3.8
Deferred Shading

Deferred Rendering Unity

3DCG
Unity Deferred Rendering
CG
Forward
Rendering CG

67
3 ScreenSpaceFluidRendering

3.9
• GDC Screen Space Fluid Rendering for Games, Simon Green, NVIDIA

http://developer.download.nvidia.com/presentations/2010/gdc/Direct3D_Effects.pdf

• , Satoshi Kodaira

https://www.slideshare.net/SatoshiKodaira/ss-69311865

68
4

GPU-Based Cellular Growth


Simulation

4.1
Processing*1
iGeo*2 Cell Division and Growth Algorithm
1 *3 GPU

https://github.com/IndieVisualLab/UnityGraphicsProgramming3
CellularGrowth
GPU

• Append/ConsumeStructuredBuffer GPU

• GPU
• Atomic

*1 https://processing.org/
*2 http://igeo.jp
*3 http://igeo.jp/tutorial/55.html

69
4 GPU-Based Cellular Growth Simulation

4.1 CellularGrowthSphere.scene

Particle Edge

4.2
Particle Edge
2
1 Particle 1

• Growth( ):
• Repulsion( ): Particle
• Division( ): 2 Particle

70
4.3

4.2

Edge Particle Edge


Particle

4.3 Edge Particle

4.3

71
4 GPU-Based Cellular Growth Simulation

4.3.1 Particle (CellularGrowthParticleOnly.cs)


Particle CellularGrowthParticleOnly.cs
Particle
Particle
Particle.cs

[StructLayout(LayoutKind.Sequential)]
public struct Particle_t {
public Vector2 position; //
public Vector2 velocity; //
float radius; //
float threshold; //
int links; // Edge ( scene )
uint alive; //
}

Particle
Append/ConsumeStructuredBuffer GPU

Append/ConsumeStructuredBuffer
Append/ConsumeStructuredBuffer*4 *5 Direct3D11
GPU LIFO(Last In First Out : )
AppendStructuredBuffer ConsumeStructuredBuffer

GPU

Particle
CellularGrowthParticleOnly.cs

protected void Start () {


// Particle
particleBuffer = new PingPongBuffer(count, typeof(Particle_t));

*4 https://docs.microsoft.com/ja-jp/windows/desktop/direct3dhlsl/sm5-object-
appendstructuredbuffer
*5 https://docs.microsoft.com/ja-jp/windows/desktop/direct3dhlsl/sm5-object-
consumestructuredbuffer

72
4.3

//
poolBuffer = new ComputeBuffer(
count,
Marshal.SizeOf(typeof(int)),
ComputeBufferType.Append
);
poolBuffer.SetCounterValue(0);
countBuffer = new ComputeBuffer(
4,
Marshal.SizeOf(typeof(int)),
ComputeBufferType.IndirectArguments
);
countBuffer.SetData(countArgs);

//
dividablePoolBuffer = new ComputeBuffer(
count,
Marshal.SizeOf(typeof(int)),
ComputeBufferType.Append
);
dividablePoolBuffer.SetCounterValue(0);

// Particle ( )
InitParticlesKernel();

...
}

particleBuffer PingPongBuffer
2 Particle

poolBuffer dividablePoolBuffer Append/ConsumeStructuredBuffer


ComputeBufferType ComputeBufferType.Append
Append/ConsumeStructuredBuffer

int Append/ConsumeStructuredBuffer poolBuffer

1. Particle index poolBuffer (Stack


Push)
2. Particle poolBuffer index (Stack
Pop) index particleBuffer Particle alive on

poolBuffer
int Particle index

( 4.4)

73
4 GPU-Based Cellular Growth Simulation

4.4 particleBuffer poolBuffer


particleBuffer Particle Particle
poolBuffer Particle index index
Particle

countBuffer int

Start InitParticlesKernel Particle


GPU
CellularGrowthParticleOnly.cs

protected void InitParticlesKernel()


{
var kernel = compute.FindKernel("InitParticles");
compute.SetBuffer(kernel, "_Particles", particleBuffer.Read);

// AppendStructuredBuffer
compute.SetBuffer(kernel, "_ParticlePoolAppend", poolBuffer);

Dispatch1D(kernel, count);
}

CellularGrowth.compute

74
4.3

THREAD
void InitParticles(uint3 id : SV_DispatchThreadID)
{
uint idx = id.x;

uint count, strides;


_Particles.GetDimensions(count, strides);
if (idx >= count)
return;

// Particle
Particle p = create();
p.alive = false; // Particle
_Particles[idx] = p;

// Particle index
_ParticlePoolAppend.Append(idx);
}

particleBuffer Particle
poolBuffer Particle index

Particle
Particle Particle CellularGrowthPar-
ticleOnly.cs Particle
CellularGrowthParticleOnly.cs

protected void Update() {


...
if(Input.GetMouseButton(0))
{
EmitParticlesKernel(GetMousePoint());
}
...
}

EmitParticlesKernel Particle

CellularGrowthParticleOnly.cs

protected void EmitParticlesKernel(Vector2 point, int emitCount = 32)


{
// emitCount
// _ParticlePoolConsume.Consume()

emitCount = Mathf.Max(
0,
Mathf.Min(emitCount, CopyPoolSize(poolBuffer))

75
4 GPU-Based Cellular Growth Simulation

);
if (emitCount <= 0) return;

var kernel = compute.FindKernel("EmitParticles");


compute.SetBuffer(kernel, "_Particles", particleBuffer.Read);

// ConsumeStructuredBuffer
compute.SetBuffer(kernel, "_ParticlePoolConsume", poolBuffer);

compute.SetVector("_Point", point);
compute.SetInt("_EmitCount", emitCount);

Dispatch1D(kernel, emitCount);
}

InitParticlesKernel _ParticlePoolAppend pool-


Buffer EmitParticlesKernel _ParticlePoolConsume
Append/ConsumeStructuredBuffer

GPU (AppendStructured-
Buffer) (ConsumeStructuredBuffer)
CPU GPU
EmitParticlesKernel emitCount GetPoolSize

index
index (GPU
_ParticlePoolConsume.Consume )

CellularGrowth.compute

THREAD
void EmitParticles(uint3 id : SV_DispatchThreadID)
{
// _EmitCount Particle
if (id.x >= (uint) _EmitCount)
return;

// Particle index
uint idx = _ParticlePoolConsume.Consume();

Particle c = create();

// Particle
float2 offset = random_point_on_circle(id.xx + float2(0, _Time));
c.position = _Point.xy + offset;
c.radius = nrand(id.xx + float2(_Time, 0));

// Particle index
_Particles[idx] = c;

76
4.3

EmitParticles Particle index


Particle particleBuffer index
Particle

Particle
Particle Particle

4.2

• Growth : Particle
• Repulsion : Particle
• Division : Particle

Growth Repulsion
Growth Repulsion Update
CellularGrowthParticleOnly.cs

protected void Update() {


...
UpdateParticlesKernel();
...
}
...
protected void UpdateParticlesKernel()
{
var kernel = compute.FindKernel("UpdateParticles");

//
compute.SetBuffer(kernel, "_ParticlesRead", particleBuffer.Read);

//
compute.SetBuffer(kernel, "_Particles", particleBuffer.Write);

compute.SetFloat("_Drag", drag); //
compute.SetFloat("_Limit", limit); //
compute.SetFloat("_Repulsion", repulsion); //
compute.SetFloat("_Grow", grow); //

Dispatch1D(kernel, count);

// (Ping Pong)
particleBuffer.Swap();

77
4 GPU-Based Cellular Growth Simulation

UpdateParticles
CelluarGrowth.compute

THREAD
void UpdateParticles(uint3 id : SV_DispatchThreadID)
{
uint idx = id.x;

uint count, strides;


_ParticlesRead.GetDimensions(count, strides);
if (idx >= count)
return;

Particle p = _ParticlesRead[idx];

// Particle
if (p.alive)
{
// Grow : Particle
p.radius = min(p.threshold, p.radius + _DT * _Grow);

// Repulsion : Particle
for (uint i = 0; i < count; i++)
{
Particle other = _ParticlesRead[i];
if(i == idx || !other.alive) continue;

// Particle
float2 dir = p.position - other.position;
float l = length(dir);

// Particle *_Repulsion
//
float r = (p.radius + other.radius) * _Repulsion;
if (l < r)
{
p.velocity += normalize(dir) * (r - l);
}
}

float2 vel = p.velocity * _DT;


float vl = length(vel);
// check if velocity length over than zero to avoid NaN position
if (vl > 0)
{
p.position += normalize(vel) * min(vl, _Limit);

// _Drag velocity
p.velocity =
normalize(p.velocity) *
min(

78
4.3

length(p.velocity) * _Drag,
_Limit
);
}
else
{
p.velocity = float2(0, 0);
}
}

_Particles[idx] = p;
}

UpdateParticles Particle
(_ParticlesRead) (_Particles)

GPU
Particle
Particle
( )

UpdateParticles

(
Ping Pong )

Division
Particle
Particle

1. Particle index dividablePoolBuffer


2. dividablePoolBuffer Particle

CellularGrowthParticleOnly.cs

protected void Start() {


...
StartCoroutine(IDivider());
}

...

79
4 GPU-Based Cellular Growth Simulation

protected IEnumerator IDivider()


{
yield return 0;
while(true)
{
yield return new WaitForSeconds(divideInterval);
Divide();
}
}

protected void Divide() {


GetDividableParticlesKernel();
DivideParticlesKernel(maxDivideCount);
}

...

// Particle dividablePoolBuffer
protected void GetDividableParticlesKernel()
{
// dividablePoolBuffer
dividablePoolBuffer.SetCounterValue(0);

var kernel = compute.FindKernel("GetDividableParticles");


compute.SetBuffer(kernel, "_Particles", particleBuffer.Read);
compute.SetBuffer(kernel, "_DividablePoolAppend", dividablePoolBuffer);

Dispatch1D(kernel, count);
}

protected void DivideParticlesKernel(int maxDivideCount = 16)


{
// (maxDivideCount)
// Particle (dividablePoolBuffer )
maxDivideCount = Mathf.Min(
CopyPoolSize(dividablePoolBuffer),
maxDivideCount
);

// (maxDivideCount)
// Particle (poolBuffer )
maxDivideCount = Mathf.Min(CopyPoolSize(poolBuffer), maxDivideCount);

if (maxDivideCount <= 0) return;

var kernel = compute.FindKernel("DivideParticles");


compute.SetBuffer(kernel, "_Particles", particleBuffer.Read);
compute.SetBuffer(kernel, "_ParticlePoolConsume", poolBuffer);
compute.SetBuffer(kernel, "_DividablePoolConsume", dividablePoolBuffer);
compute.SetInt("_DivideCount", maxDivideCount);

Dispatch1D(kernel, count);
}

GetDividableParticles dividablePoolBuffer Par-


ticle(active Particle)
DivideParticles

80
4.3

DivideParticlesKernel

• maxDivideCount
• dividablePoolBuffer Particle
• poolBuffer Particle

CellularGrowth.compute

// Particle
//
bool dividable_particle(Particle p, uint idx)
{
//
float rate = (p.radius / p.threshold);
return rate >= 0.95;

//
// return nrand(float2(idx, _Time)) < 0.1;
}

// Particle
uint divide_particle(uint idx, float2 offset)
{
Particle parent = _Particles[idx];
Particle child = create();

//
float rh = parent.radius * 0.5;
rh = max(rh, 0.1);
parent.radius = child.radius = rh;

//
float2 center = parent.position;
parent.position = center - offset;
child.position = center + offset;

//
float x = nrand(float2(_Time, idx));
child.threshold = rh * lerp(1.25, 2.0, x);

// index Particle
uint cidx = _ParticlePoolConsume.Consume();
_Particles[cidx] = child;

// Particle
_Particles[idx] = parent;

return cidx;
}

uint divide_particle(uint idx)

81
4 GPU-Based Cellular Growth Simulation

{
Particle parent = _Particles[idx];

//
float2 offset =
random_point_on_circle(float2(idx, _Time)) *
parent.radius * 0.25;

return divide_particle(idx, offset);


}

...

THREAD
void GetDividableParticles(uint3 id : SV_DispatchThreadID)
{
uint idx = id.x;
uint count, strides;
_Particles.GetDimensions(count, strides);
if (idx >= count)
return;

Particle p = _Particles[idx];
if (p.alive && dividable_particle(p, idx))
{
_DividablePoolAppend.Append(idx);
}
}

THREAD
void DivideParticles(uint3 id : SV_DispatchThreadID)
{
if (id.x >= _DivideCount)
return;

uint idx = _DividablePoolConsume.Consume();


divide_particle(idx);
}

82
4.3

4.5 CellularGrowthParticleOnly.scene

4.3.2 (CellularGrowth.cs)
Particle Edge

CellularGrowth.cs
Edge Particle Particle

Edge
Edge.cs

[StructLayout(LayoutKind.Sequential)]
public struct Edge_t
{
public int a, b; // Edge 2 Particle index
public Vector2 force; // 2 Particle
uint alive; //
}

Edge Particle Append/ConsumeStructuredBuffer

83
4 GPU-Based Cellular Growth Simulation

Division

1. Edge dividablePoolBuffer
2. Edge Edges 0 Particle(links 0
Particle) 2 Particle Edge
3. Edge dividablePoolBuffer Edge

Particle " Edge"


Particle
Edge Edge

Particle

Edge CellularGrowthParticleOnly.cs

CellularGrowth.cs

protected IEnumerator IDivider()


{
yield return 0;
while(true)
{
yield return new WaitForSeconds(divideInterval);
Divide();
}
}

protected void Divide()


{
// 1. Edge dividablePoolBuffer
GetDividableEdgesKernel();

int dividableEdgesCount = CopyPoolSize(dividablePoolBuffer);


if(dividableEdgesCount == 0)
{
// 2. Edge
// Edges 0 Particle(links 0 Particle)
// 2 Particle Edge
DivideUnconnectedParticles();
} else
{
// 3. Edge dividablePoolBuffer Edge

// ( ) Edge
switch(pattern)

84
4.3

{
case DividePattern.Closed:
//
DivideEdgesClosedKernel(
dividableEdgesCount,
maxDivideCount
);
break;
case DividePattern.Branch:
//
DivideEdgesBranchKernel(
dividableEdgesCount,
maxDivideCount
);
break;
}
}
}

...

protected void GetDividableEdgesKernel()


{
// Edge
dividablePoolBuffer.SetCounterValue(0);

var kernel = compute.FindKernel("GetDividableEdges");


compute.SetBuffer(
kernel, "_Particles",
particlePool.ObjectPingPong.Read
);
compute.SetBuffer(kernel, "_Edges", edgePool.ObjectBuffer);
compute.SetBuffer(kernel, "_DividablePoolAppend", dividablePoolBuffer);

// Particle
compute.SetInt("_MaxLink", maxLink);

Dispatch1D(kernel, count);
}

...

protected void DivideUnconnectedParticles()


{
var kernel = compute.FindKernel("DivideUnconnectedParticles");
compute.SetBuffer(
kernel, "_Particles",
particlePool.ObjectPingPong.Read
);
compute.SetBuffer(
kernel, "_ParticlePoolConsume",
particlePool.PoolBuffer
);
compute.SetBuffer(kernel, "_Edges", edgePool.ObjectBuffer);
compute.SetBuffer(kernel, "_EdgePoolConsume", edgePool.PoolBuffer);

Dispatch1D(kernel, count);
}

85
4 GPU-Based Cellular Growth Simulation

Edge (GetDividableEdges)
CellularGrowth.compute

//
bool dividable_edge(Edge e, uint idx)
{
Particle pa = _Particles[e.a];
Particle pb = _Particles[e.b];

// Particle (_MaxLink)
// dividable_particle
return
!(pa.links >= _MaxLink && pb.links >= _MaxLink) &&
(dividable_particle(pa, e.a) && dividable_particle(pb, e.b));
}

...

// Edge
THREAD
void GetDividableEdges(uint3 id : SV_DispatchThreadID)
{
uint idx = id.x;
uint count, strides;
_Edges.GetDimensions(count, strides);
if (idx >= count)
return;

Edge e = _Edges[idx];
if (e.alive && dividable_edge(e, idx))
{
_DividablePoolAppend.Append(idx);
}
}

Edge Edge Particle


(DivideUnconnectedParticles)
CellularGrowth.compute

// index a Particle b Particle Edge


void connect(int a, int b)
{
// Edge Edge index
uint eidx = _EdgePoolConsume.Consume();

// Atomic ( )
// Particle
InterlockedAdd(_Particles[a].links, 1);
InterlockedAdd(_Particles[b].links, 1);

Edge e;
e.a = a;
e.b = b;
e.force = float2(0, 0);

86
4.3

e.alive = true;
_Edges[eidx] = e;
}

...

// Edge Particle
THREAD
void DivideUnconnectedParticles(uint3 id : SV_DispatchThreadID)
{
uint count, stride;
_Particles.GetDimensions(count, stride);
if (id.x >= count)
return;

uint idx = id.x;


Particle parent = _Particles[idx];
if (!parent.alive || parent.links > 0)
return;

// Particle Particle
uint cidx = divide_particle(idx);

// Particle Particle Edge


connect(idx, cidx);
}

Particle Edge connect Atomic


Particle

87
4 GPU-Based Cellular Growth Simulation

Atomic (InterlockedAdd )

(
( )
)
Atomic ( )
GPU

a
HLSL Interlocked prefix
InterlockedAdd
InterlockedAdd
_Particles[index].links 1

a https://docs.microsoft.com/ja-jp/windows/desktop/direct3d11/direct3d-11-
advanced-stages-cs-atomic-functions

Edge dividablePoolBuffer Edge


DividePattern enum

(DividePattern.Closed)

(DividePattern.Closed)

88
4.3

4.6 (DividePattern.Closed)

CellularGrowth.cs

protected void DivideEdgesClosedKernel(


int dividableEdgesCount,
int maxDivideCount = 16
)
{
//
var kernel = compute.FindKernel("DivideEdgesClosed");
DivideEdgesKernel(kernel, dividableEdgesCount, maxDivideCount);
}

//
protected void DivideEdgesKernel(
int kernel,
int dividableEdgesCount,
int maxDivideCount
)
{
// Consume
// maxDivideCount
maxDivideCount = Mathf.Min(dividableEdgesCount, maxDivideCount);
maxDivideCount = Mathf.Min(particlePool.CopyPoolSize(), maxDivideCount);
maxDivideCount = Mathf.Min(edgePool.CopyPoolSize(), maxDivideCount);
if (maxDivideCount <= 0) return;

89
4 GPU-Based Cellular Growth Simulation

compute.SetBuffer(
kernel, "_Particles",
particlePool.ObjectPingPong.Read
);
compute.SetBuffer(
kernel, "_ParticlePoolConsume",
particlePool.PoolBuffer
);
compute.SetBuffer(kernel, "_Edges", edgePool.ObjectBuffer);
compute.SetBuffer(kernel, "_EdgePoolConsume", edgePool.PoolBuffer);

compute.SetBuffer(kernel, "_DividablePoolConsume", dividablePoolBuffer);


compute.SetInt("_DivideCount", maxDivideCount);

Dispatch1D(kernel, maxDivideCount);
}

GPU (DivideEdgesClosed)
divide_edge_closed Particle Edge

Particle 1 Particle 3
Particle 3 Edge ( 4.7)

4.7 2 Particle Particle 3

Particle 2 Particle
Edge Particle Edge
( 4.8)

90
4.3

4.8 Particle 2 Particle


Edge

CellularGrowth.compute

//
void divide_edge_closed(uint idx)
{
Edge e = _Edges[idx];

Particle pa = _Particles[e.a];
Particle pb = _Particles[e.b];

if ((pa.links == 1) || (pb.links == 1))


{
// 3 Particle Edge
uint cidx = divide_particle(e.a);
connect(e.a, cidx);
connect(cidx, e.b);
}
else
{
// 2 Particle Particle
// Edge
float2 dir = pb.position - pa.position;
float2 offset = normalize(dir) * pa.radius * 0.25;
uint cidx = divide_particle(e.a, offset);

// Particle Particle
connect(e.a, cidx);

// 2 Particle Edge
// Particle Edge
InterlockedAdd(_Particles[e.a].links, -1);
InterlockedAdd(_Particles[cidx].links, 1);
e.a = cidx;
}

_Edges[idx] = e;
}

...

91
4 GPU-Based Cellular Growth Simulation

//
THREAD
void DivideEdgesClosed(uint3 id : SV_DispatchThreadID)
{
if (id.x >= _DivideCount)
return;

// Edge index
uint idx = _DividablePoolConsume.Consume();
divide_edge_closed(idx);
}

Edge

Edge 2 Particle

1. Edge 2 Particle
2. Particle Edge

CellularGrowth.cs

protected void Update() {


...
UpdateEdgesKernel();
SpringEdgesKernel();
...
}

...

protected void UpdateEdgesKernel()


{
// Edge
var kernel = compute.FindKernel("UpdateEdges");
compute.SetBuffer(
kernel, "_Particles",
particlePool.ObjectPingPong.Read
);
compute.SetBuffer(kernel, "_Edges", edgePool.ObjectBuffer);
compute.SetFloat("_Spring", spring);

Dispatch1D(kernel, count);
}

protected void SpringEdgesKernel()


{
// Particle Edge
var kernel = compute.FindKernel("SpringEdges");
compute.SetBuffer(
kernel, "_Particles",

92
4.3

particlePool.ObjectPingPong.Read
);
compute.SetBuffer(kernel, "_Edges", edgePool.ObjectBuffer);

Dispatch1D(kernel, count);
}

CellularGrowth.compute

THREAD
void UpdateEdges(uint3 id : SV_DispatchThreadID)
{
uint idx = id.x;
uint count, strides;
_Edges.GetDimensions(count, strides);
if (idx >= count)
return;

Edge e = _Edges[idx];

//
e.force = float2(0, 0);

if (!e.alive)
{
_Edges[idx] = e;
return;
}

Particle pa = _Particles[e.a];
Particle pb = _Particles[e.b];
if (!pa.alive || !pb.alive)
{
_Edges[idx] = e;
return;
}

// 2 Particle
//
float2 dir = pa.position - pb.position;
float r = pa.radius + pb.radius;
float len = length(dir);
if (abs(len - r) > 0)
{
// ( )
float l = ((len - r) / r);
float2 f = normalize(dir) * l * _Spring;
e.force = f;
}

_Edges[idx] = e;
}

THREAD
void SpringEdges(uint3 id : SV_DispatchThreadID)

93
4 GPU-Based Cellular Growth Simulation

{
uint idx = id.x;
uint count, strides;
_Particles.GetDimensions(count, strides);
if (idx >= count)
return;

Particle p = _Particles[idx];
if (!p.alive || p.links <= 0)
return;

//
float dif = 1.0 / p.links;

int iidx = (int)idx;

_Edges.GetDimensions(count, strides);

// Edge Particle
for (uint i = 0; i < count; i++)
{
Edge e = _Edges[i];
if (!e.alive)
continue;

// Edge
if (e.a == iidx)
{
p.velocity -= e.force * dif;
}
else if (e.b == iidx)
{
p.velocity += e.force * dif;
}
}

_Particles[idx] = p;
}

4.3.3
Edge (dividable_edge )

CellularGrowth.cs enum

(DividePattern.Branch)
4.9

94
4.3

Particle Particle

4.9

CellularGrowth.cs

protected void DivideEdgesBranchKernel(


int dividableEdgesCount,
int maxDivideCount = 16
)
{
//
var kernel = compute.FindKernel("DivideEdgesBranch");
DivideEdgesKernel(kernel, dividableEdgesCount, maxDivideCount);
}

CellularGrowth.compute

//
void divide_edge_branch(uint idx)
{
Edge e = _Edges[idx];
Particle pa = _Particles[e.a];
Particle pb = _Particles[e.b];

// Particleindex
uint i = lerp(e.b, e.a, step(pa.links, pb.links));

uint cidx = divide_particle(i);


connect(i, cidx);
}

...

//
THREAD

95
4 GPU-Based Cellular Growth Simulation

void DivideEdgesBranch(uint3 id : SV_DispatchThreadID)


{
if (id.x >= _DivideCount)
return;

// Edge index
uint idx = _DividablePoolConsume.Consume();
divide_edge_branch(idx);
}

Edge
dividable_edge Particle
(_MaxLink)

4.10 _MaxLink 2 (DividePattern.Branch)

96
4.3

4.11 _MaxLink 3 (DividePattern.Branch)

97
4 GPU-Based Cellular Growth Simulation

4.12 _MaxLink 3 2
(DividePattern.Branch)

4.4
GPU

CG Andy Lomas*6
Morphogenetic Creations J.A.Kaandorp*7
Computational Biology

Maxime Causeret*8 Max Cooper *9

(
Houdini )

*6 http://www.andylomas.com/
*7 https://staff.fnwi.uva.nl/j.a.kaandorp/research.html
*8 http://teresuac.fr/
*9 https://vimeo.com/196269431

98
4.5

2 iGeo
*10 3
3 3
3
https://github.com/mattatz/CellularGrowth

4.5
• http://igeo.jp/tutorial/55.html
• https://msdn.microsoft.com/ja-jp/library/ee422322(v=vs.85).aspx

*10 http://igeo.jp/tutorial/56.html

99
5

Reaction Diffusion

5.1

Unity ComputeShader

ComputeShader UnityGraphicsProgramming vol.1


2 ComputeShader

https://github.com/IndieVisualLab/UnityGraphicsProgramming3
ReactionDiffusion

5.2 Reaction Diffusion


Reaction Diffusion

Reaction Diffusion
Gray-Scott
Gray-Scott 1983 P.Gray S.K.Scott
U V 2

5.1 Gray-Scott Reaction

100
5.2 Reaction Diffusion

5.1 Gray-Scott Reaction

1. U Feed
2. V U Reaction V
3. V V Kill

5.2 U V

5.2 Gray-Scott Diffusion

U V
U V

∂u
= Du∆u − uv 2 + f(1−u)
∂t

∂v
= Dv∆v + uv 2 − (f + k)
∂t

101
5 Reaction Diffusion

U u V v
Du∆u Dv∆v Du Dv u v
∆u ∆v U V
Diffusion
uv 2 U V Reaction U
V
+f(1−u) U Feed
−(f + k)
V Kill
U V U V
U V U +f(1−u)
V −(f + k)
U V Du∆u Dv∆v

5.3 Unity
Unity
ReactionDiffusion2D_1

5.3.1
U V
ComputeShader ComputeBuffer

ReactionDiffusion2D.cs

public struct RDData


{
public float u; // U
public float v; // V
}

5.3.2
ReactionDiffusion2D.cs

/// <summary>
///
/// </summary>
void Initialize()

102
5.3 Unity

{
...

int wh = texWidth * texHeight; //


buffers = new ComputeBuffer[2]; // ComputeBuffer

for (int i = 0; i < buffers.Length; i++)


{
//
buffers[i] = new ComputeBuffer(wh, Marshal.SizeOf(typeof(RDData)));
}

//
bufData = new RDData[wh];
bufData2 = new RDData[wh];

//
ResetBuffer();

// Seed
inputData = new Vector2[inputMax];
inputIndex = 0;
inputBuffer = new ComputeBuffer(
inputMax, Marshal.SizeOf(typeof(Vector2))
);
}

ComputeBuffer buffers
ComputeShader

5.3.3

ReactionDiffusion2D.cs

//
void UpdateBuffer()
{
cs.SetInt("_TexWidth", texWidth);
cs.SetInt("_TexHeight", texHeight);
cs.SetFloat("_DU", du);
cs.SetFloat("_DV", dv);

cs.SetFloat("_Feed", feed);
cs.SetFloat("_K", kill);

cs.SetBuffer(kernelUpdate, "_BufferRead", buffers[0]);

103
5 Reaction Diffusion

cs.SetBuffer(kernelUpdate, "_BufferWrite", buffers[1]);


cs.Dispatch(kernelUpdate,
Mathf.CeilToInt((float)texWidth / THREAD_NUM_X),
Mathf.CeilToInt((float)texHeight / THREAD_NUM_X),
1);

SwapBuffer();
}

C# ComputeShader
ComputeShader

ReactionDiffusion2D.compute

//
[numthreads(THREAD_NUM_X, THREAD_NUM_X, 1)]
void Update(uint3 id : SV_DispatchThreadID)
{

int idx = GetIndex(id.x, id.y);


float u = _BufferRead[idx].u;
float v = _BufferRead[idx].v;
float uvv = u * v * v;
float f, k;

f = _Feed;
k = _K;

_BufferWrite[idx].u = saturate(
u + (_DU * LaplaceU(id.x, id.y) - uvv + f * (1.0 - u))
);
_BufferWrite[idx].v = saturate(
v + (_DV * LaplaceV(id.x, id.y) + uvv - (k + f) * v)
);
}

GetIndex()
ComputeBuffer
ReactionDiffusion2D.compute

//
int GetIndex(int x, int y) {
x = (x < 0) ? x + _TexWidth : x;
x = (x >= _TexWidth) ? x - _TexWidth : x;

y = (y < 0) ? y + _TexHeight : y;
y = (y >= _TexHeight) ? y - _TexHeight : y;

return y * _TexWidth + x;
}

104
5.3 Unity

_BufferRead u v
LaplaceU LaplaceV U V

ReactionDiffusion2D.compute

// U
float LaplaceU(int x, int y) {
float sumU = 0;

for (int i = 0; i < 9; i++) {


int2 pos = laplaceIndex[i];
int idx = GetIndex(x + pos.x, y + pos.y);
sumU += _BufferRead[idx].u * laplacePower[i];
}

return sumU;
}

// V
float LaplaceV(int x, int y) {
float sumV = 0;

for (int i = 0; i < 9; i++) {


int2 pos = laplaceIndex[i];
int idx = GetIndex(x + pos.x, y + pos.y);
sumV += _BufferRead[idx].v * laplacePower[i];
}

return sumV;
}

u v _BufferWrite saturate 0 1

5.3.4
A C U V
A SeedNum
Seed C
ReactionDiffusion2D.cs

/// <summary>
/// Seed
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
void AddSeed(int x, int y)
{

105
5 Reaction Diffusion

if (inputIndex < inputMax)


{
inputData[inputIndex].x = x;
inputData[inputIndex].y = y;
inputIndex++;
}
}

inputData
ReactionDiffusion2D.cs

/// <summary>
/// Seed ComputeShader
/// </summary>
void AddSeedBuffer()
{
if (inputIndex > 0)
{
inputBuffer.SetData(inputData);
cs.SetInt("_InputNum", inputIndex);
cs.SetInt("_TexWidth", texWidth);
cs.SetInt("_TexHeight", texHeight);
cs.SetInt("_SeedSize", seedSize);
cs.SetBuffer(kernelAddSeed, "_InputBufferRead", inputBuffer);
cs.SetBuffer(kernelAddSeed, "_BufferWrite", buffers[0]); // update
0
cs.Dispatch(kernelAddSeed,
Mathf.CeilToInt((float)inputIndex / (float)THREAD_NUM_X),
1,
1);
inputIndex = 0;
}
}

inputBuffer inputeData
ComputeShader
ReactionDiffusion2D.compute

//
[numthreads(THREAD_NUM_X, 1, 1)]
void AddSeed(uint id : SV_DispatchThreadID)
{
if (_InputNum <= id) return;

int w = _SeedSize;
int h = _SeedSize;
float radius = _SeedSize * 0.5;

int centerX = _InputBufferRead[id].x;


int centerY = _InputBufferRead[id].y;
int startX = _InputBufferRead[id].x - w / 2;
int startY = _InputBufferRead[id].y - h / 2;
for (int x = 0; x < w; x++)

106
5.3 Unity

{
for (int y = 0; y < h; y++)
{
float dis = distance(
float2(centerX, centerY),
float2(startX + x, startY + y)
);
if (dis <= radius) {
_BufferWrite[GetIndex((centerX + x), (centerY + y))].v = 1;
}
}
}
}

C# inputBuffer v 1

5.3.5 RenderTexture
RenderTexture
RenderTexture u v
RenderTexture 1
RenderTextureFormat RFloat RenderTextureFor-
mat.RFloat 1 float RenderTexture

ReactionDiffusion2D.cs

/// <summary>
/// RenderTexture
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
RenderTexture CreateRenderTexture(int width, int height)
{
RenderTexture tex = new RenderTexture(width, height, 0,
RenderTextureFormat.RFloat,
RenderTextureReadWrite.Linear);
tex.enableRandomWrite = true;
tex.filterMode = FilterMode.Bilinear;
tex.wrapMode = TextureWrapMode.Repeat;
tex.Create();

return tex;
}

ComputeShader RenderTexture C#
ReactionDiffusion2D.cs

107
5 Reaction Diffusion

/// <summary>
/// ReactionDiffusion
/// </summary>
void DrawTexture()
{
cs.SetInt("_TexWidth", texWidth);
cs.SetInt("_TexHeight", texHeight);
cs.SetBuffer(kernelDraw, "_BufferRead", buffers[0]);
cs.SetTexture(kernelDraw, "_HeightMap", resultTexture);
cs.Dispatch(kernelDraw,
Mathf.CeilToInt((float)texWidth / THREAD_NUM_X),
Mathf.CeilToInt((float)texHeight / THREAD_NUM_X),
1);
}

ComputeShader u v

ReactionDiffusion2D.compute

//
float GetValue(int x, int y) {
int idx = GetIndex(x, y);
float u = _BufferRead[idx].u;
float v = _BufferRead[idx].v;
return 1 - clamp(u - v, 0, 1);
}

...

//
[numthreads(THREAD_NUM_X, THREAD_NUM_X, 1)]
void Draw(uint3 id : SV_DispatchThreadID)
{
float c = GetValue(id.x, id.y);

// height map
_HeightMap[id.xy] = c;

5.3.6
Unlit Shader

ReactionDiffusion2D.cs

/// <summary>
///
/// </summary>
void UpdateMaterial()

108
5.4

{
material.SetTexture("_MainTex", resultTexture);

material.SetColor("_Color0", bottomColor);
material.SetColor("_Color1", topColor);
}

ReactionDiffusion2D.shader

fixed4 frag (v2f i) : SV_Target


{
// sample the texture
fixed4 col = lerp(_Color0, _Color1, tex2D(_MainTex, i.uv).r);
return col;
}

5.3

5.4
Feed Kill

109
5 Reaction Diffusion

5.4.1
Feed:0.037 / Kill:0.06

5.4

5.4.2
Feed:0.03 / Kill:0.062

5.5

110
5.4

5.4.3
Feed:0.0263 / Kill:0.06

5.6

5.4.4
Feed:0.077 / Kill:0.0615

5.7

111
5 Reaction Diffusion

5.4.5
Feed:0.039 / Kill:0.058

5.8

5.4.6
Feed:0.026 / Kill:0.051

5.9

112
5.5 Surface Shader

5.4.7
Feed:0.014 / Kill:0.0477

5.10

5.5 Surface Shader


Surface Shader Unity
ReactionDiffusion2D_2

5.5.1
ReactionDiffusion

RenderTextureFormat.RFloat XYZ
RenderTextureFormat.ARGBFloat

ReactionDiffusion2DForStandard.cs

void Initialize()
{
...
heightMapTexture = CreateRenderTexture(texWidth, texHeight,
RenderTextureFormat.RFloat); //
normalMapTexture = CreateRenderTexture(texWidth, texHeight,

113
5 Reaction Diffusion

RenderTextureFormat.ARGBFloat); //
...
}

/// <summary>
/// RenderTexture
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="texFormat"></param>
/// <returns></returns>
RenderTexture CreateRenderTexture(
int width,
int height,
RenderTextureFormat texFormat)
{
RenderTexture tex = new RenderTexture(width, height, 0,
texFormat, RenderTextureReadWrite.Linear);
tex.enableRandomWrite = true;
tex.filterMode = FilterMode.Bilinear;
tex.wrapMode = TextureWrapMode.Repeat;
tex.Create();

return tex;
}

...

void DrawTexture()
{
...
cs.SetTexture(kernelDraw, "_HeightMap", heightMapTexture);
cs.SetTexture(kernelDraw, "_NormalMap", normalMapTexture); //

cs.Dispatch(kernelDraw,
Mathf.CeilToInt((float)texWidth / THREAD_NUM_X),
Mathf.CeilToInt((float)texHeight / THREAD_NUM_X),
1);
}

ComputeShader

ReactionDiffusion2DStandard.compute

float3 GetNormal(int x, int y) {


float3 normal = float3(0, 0, 0);
float c = GetValue(x, y);
normal.x = ((GetValue(x - 1, y) - c) - (GetValue(x + 1, y) - c));
normal.y = ((GetValue(x, y - 1) - c) - (GetValue(x, y + 1) - c));
normal.z = 1;
normal = normalize(normal) * 0.5 + 0.5;
return normal;
}

...

114
5.5 Surface Shader

//
[numthreads(THREAD_NUM_X, THREAD_NUM_X, 1)]
void Draw(uint3 id : SV_DispatchThreadID)
{
float c = GetValue(id.x, id.y);

// height map
_HeightMap[id.xy] = c;

// normal map
_NormalMap[id.xy] = float4(GetNormal(id.x, id.y), 1);
}

Surface Shader Surface


Shader Unity
surf SurfaceOutputStandard

SurfaceOutputStandard

struct SurfaceOutputStandard
{
fixed3 Albedo; // ( )
fixed3 Normal; //
half3 Emission; //
half Metallic; // 0= , 1=
half Smoothness; // 0= , 1=
half Occlusion; // ( 1)
fixed Alpha; //
};

ReactionDiffusion2DStandard.shader

void surf(Input IN, inout SurfaceOutputStandard o) {

float2 uv = IN.uv_MainTex;

//
half v0 = tex2D(_MainTex, uv).x;

//
float3 norm = UnpackNormal(tex2D(_NormalTex, uv));

// A B
half p = smoothstep(_Threshold, _Threshold + _Fading, v0);

o.Albedo = lerp(_Color0.rgb, _Color1.rgb, p); //


o.Alpha = lerp(_Color0.a, _Color1.a, p); //
o.Smoothness = lerp(_Smoothness0, _Smoothness1, p); //
o.Metallic = lerp(_Metallic0, _Metallic1, p); //
o.Normal = normalize(float3(norm.x, norm.y, 1 - _NormalStrength)); //

115
5 Reaction Diffusion

o.Emission = lerp(_Emit0 * _EmitInt0, _Emit1 * _EmitInt1, p).rgb; //

Unity unpackNormal
SurfaceOutputStandard

5.11 SurfaceShader

RGB

116
5.6

5.6
Reaction Diffusion

RenderTexture ComputeBuffer
ReactionDiffusion3D

5.6.1
RenderTexture

ReactionDiffusion3D.cs

RenderTexture CreateTexture(int width, int height, int depth)


{
RenderTexture tex = new RenderTexture(width, height, 0,
RenderTextureFormat.RFloat, RenderTextureReadWrite.Linear);
tex.volumeDepth = depth;
tex.enableRandomWrite = true;
tex.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
tex.filterMode = FilterMode.Bilinear;
tex.wrapMode = TextureWrapMode.Repeat;
tex.Create();

return tex;
}

tex.volumeDepth tex.dimension
UnityEngine.Rendering.TextureDimension.Tex3D
RenderTexture
RenderTexture
Reaction Diffusion ComputeBuffer

ReactionDiffusion3D.cs

void Initialize()
{
...
int whd = texWidth * texHeight * texDepth;
buffers = new ComputeBuffer[2];
...
for (int i = 0; i < buffers.Length; i++)
{

117
5 Reaction Diffusion

buffers[i] = new ComputeBuffer(whd, Marshal.SizeOf(typeof(RDData)));


}
...
}

5.6.2
ComputeShader RenderTex-
ture ComputeShader RWTex-
ture2D<float> RWTexture3D<float>
ReactionDiffusion3D.compute

RWTexture3D<float> _HeightMap; //

laplacePower

ReactionDiffusion3D.compute

//
static const int3 laplaceIndex[27] = {
int3(-1,-1,-1), int3(0,-1,-1), int3( 1,-1,-1),
int3(-1, 0,-1), int3(0, 0,-1), int3(1, 0,-1),
int3(-1, 1,-1), int3(0, 1,-1), int3(1, 1,-1),

int3(-1,-1, 0), int3(0,-1, 0), int3(1,-1, 0),


int3(-1, 0, 0), int3(0, 0, 0), int3(1, 0, 0),
int3(-1, 1, 0), int3(0, 1, 0), int3(1, 1, 0),

int3(-1,-1, 1), int3(0,-1, 1), int3(1,-1, 1),


int3(-1, 0, 1), int3(0, 0, 1), int3(1, 0, 1),
int3(-1, 1, 1), int3(0, 1, 1), int3(1, 1, 1),
};

//
static const float laplacePower[27] = {
0.02, 0.02, 0.02,
0.02, 0.1, 0.02,
0.02, 0.02, 0.02,

0.02, 0.1, 0.02,


0.1, -1.0, 0.1,
0.02, 0.1, 0.02,

0.02, 0.02, 0.02,


0.02, 0.1, 0.02,
0.02, 0.02, 0.02
};

118
5.6

//
int GetIndex(int x, int y, int z) {
x = (x < 0) ? x + _TexWidth : x;
x = (x >= _TexWidth) ? x - _TexWidth : x;

y = (y < 0) ? y + _TexHeight : y;
y = (y >= _TexHeight) ? y - _TexHeight : y;

z = (z < 0) ? z + _TexDepth : z;
z = (z >= _TexDepth) ? z - _TexDepth : z;

return z * _TexWidth * _TexHeight + y * _TexWidth + x;


}

// U
float LaplaceU(int x, int y, int z) {
float sumU = 0;

for (int i = 0; i < 27; i++) {


int3 pos = laplaceIndex[i];

int idx = GetIndex(x + pos.x, y + pos.y, z + pos.z);


sumU += _BufferRead[idx].u * laplacePower[i];
}
return sumU;
}

// V
float LaplaceV(int x, int y, int z) {
float sumV = 0;

for (int i = 0; i < 27; i++) {


int3 pos = laplaceIndex[i];
int idx = GetIndex(x + pos.x, y + pos.y, z + pos.z);
sumV += _BufferRead[idx].v * laplacePower[i];
}
return sumV;
}

5.6.3
RenderTexture
Unlit Shader Surface Shader

Unity Graphics Programming Vol.1

119
5 Reaction Diffusion

*1

5.12 Reaction Diffusion

5.7
Gray-Scott Feed
Kill

Reaction Diffusion Nakama Kouhei DIFFU-


SION *2 Kitahara Nobutaka Reaction-Diffusion *3
Reaction Diffusion

5.8
• Reaction-Diffusion Tutorial http://www.karlsims.com/rd.html
• Reaction diffusion system Gray-Scott model
https://pmneila.github.io/jsexp/grayscott/

*1 Tips http://tips.hecomi.com/entry/2018/01/05/192332
*2 DIFFUSION https://vimeo.com/145251635
*3 Reaction-Diffusion https://vimeo.com/176261480

120
6

Strange Attractor

6.1
Strange Attractor
Unity GPU

https://github.com/IndieVisualLab/UnityGraphicsProgramming3
StrangeAttractors

6.1.1
• ComputeShader 5.0
• Unity2018.2.9f1

6.2 Strange Attractor

Attractor

Strange Attractor
*1 Lorenz Attractor *2 Thomas’ Cyclically
Symmetric Attractor

*1 Lorenz, E. N. Deterministic Nonperiodic Flow, Journal of Atmospheric Sciences, Vol.20,


pp.130-141, 1963.
*2 Thomas, Ren 1999 . "Deterministic chaos seen in terms of feedback circuits: Analysis,
synthesis, ’labyrinth chaos’". Int. J. Bifurcation and Chaos. 9 10 : 1889 1905.

121
6 Strange Attractor

6.3 Lorenz Attractor


Edward N
Lorenz 1972 *3 1

Lorenz 1963 Lorenz Attractor

6.1 Lorenz attractor

*3 http://eaps4.mit.edu/research/Lorenz/Butterfly_1972.pdf

122
6.3 Lorenz Attractor

6.2 Lorenz attractor

6.3.1 Lorenz
Lorenz

dx
= −px + py
dt
dy
= −xz + rx − y
dt
dz
= xy − bz
dt

p r b p=10 r=28 b=8/3


Strange Attractor

123
6 Strange Attractor

6.3.2 Lorenz Attractor


Lorenz

StrangeAttractor.cs

protected struct Params


{
Vector3 emitPos;
Vector3 position;
Vector3 velocity; // xyz = velocity, w = velocity coef;
float life;
Vector2 size; // x = current size, y = target size.
Vector4 color;

public Params(Vector3 emitPos, float size, Color color)


{
this.emitPos = emitPos;
this.position = Vector3.zero;
this.velocity = Vector3.zero;
this.life = 0;
this.size = new Vector2(0, size);
this.color = color;
}
}

Strange Attractor
StrangeAttractor.cs

ComputeBuffer

LorenzAttrator.cs

protected sealed override void InitializeComputeBuffer()


{
if (cBuffer != null) cBuffer.Release();

cBuffer = new ComputeBuffer(instanceCount, Marshal.SizeOf(typeof(Params)));


Params[] parameters = new Params[cBuffer.count];
for (int i = 0; i < instanceCount; i++)
{
var normalize = (float)i / instanceCount;
var color = gradient.Evaluate(normalize);
parameters[i] = new Params(Random.insideUnitSphere *
emitterSize * normalize, particleSize, color);
}
cBuffer.SetData(parameters);
}

124
6.3 Lorenz Attractor

StrangeAttractor.cs InitializeCompute-
Buffer LorenzAttrator.cs
Unity
gradient emitterSize particleSize
Params ComputeBuffer cBuffer SetData
id
id
Strange Attractor

LorenzAttrator p r b

LorenzAttrator.cs

[SerializeField, Tooltip("Default is 10")]


float p = 10f;
[SerializeField, Tooltip("Default is 28")]
float r = 28f;
[SerializeField, Tooltip("Default is 8/3")]
float b = 2.666667f;

private int pId, rId, bId;


private string pProp = "p", rProp = "r", bProp = "b";

protected override void InitializeShaderUniforms()


{
pId = Shader.PropertyToID(pProp);
rId = Shader.PropertyToID(rProp);
bId = Shader.PropertyToID(bProp);
}

protected override void UpdateShaderUniforms()


{
computeShaderInstance.SetFloat(pId, p);
computeShaderInstance.SetFloat(rId, r);
computeShaderInstance.SetFloat(bId, b);
}

LorenzAttractor.compute

#pragma kernel Emit


#pragma kernel Iterator

125
6 Strange Attractor

#define THREAD_X 128


#define THREAD_Y 1
#define THREAD_Z 1
#define DT 0.022

struct Params
{
float3 emitPos;
float3 position;
float3 velocity; //xyz = velocity
float life;
float2 size; // x = current size, y = target size.
float4 color;
};

RWStructuredBuffer<Params> buf;

[numthreads(THREAD_X, THREAD_Y, THREAD_Z)]


void Emit(uint id : SV_DispatchThreadID)
{
Params p = buf[id];
p.life = (float)id * -1e-05;
p.position = p.emitPos;
p.size.x = 0.0;
buf[id] = p;
}

Emit p.life
id
id

p.size 0

LorenzAttractor.compute

#define DT 0.022

// Lorenz Attractor parameters


float p;
float r;
float b;

//Lorenz
float3 LorenzAttractor(float3 pos)
{

126
6.4 Thomas’ Cyclically Symmetric Attractor

float dxdt = (p * (pos.y - pos.x));


float dydt = (pos.x * (r - pos.z) - pos.y);
float dzdt = (pos.x * pos.y - b * pos.z);
return float3(dxdt, dydt, dzdt) * DT;
}

[numthreads(THREAD_X, THREAD_Y, THREAD_Z)]


void Iterator(uint id : SV_DispatchThreadID)
{
Params p = buf[id];
p.life.x += DT;
// 0 1

p.size.x = p.size.y * saturate(length(p.velocity));


if (p.life.x > 0)
{
p.velocity = LorenzAttractor(p.position);
p.position += p.velocity;
}
buf[id] = p;
}

LorenzAttractor Lorenz
x,y,z

Unity

Unity Time.deltaTime

Strange Attractor

2 DT

6.4 Thomas’ Cyclically Symmetric Attractor


Ren Thomas Thomas’ Cyclically Sym-
metric Attractor

127
6 Strange Attractor

6.3 Thomas’ Cyclically Symmetric Attractor

6.4.1 Thomas’ Cyclically Symmetric

dx
= sin y − bx
dt
dy
= sin z − by
dt
dz
= sin x − bz
dt

b b ≃ 0.208186
Strange Attractor b≃0

128
6.4 Thomas’ Cyclically Symmetric Attractor

6.4.2 Thomas’ Cyclically Symmetric Attractor


Thomas’ Cyclically Symmetric

Lorenz Attractor

CPU

ThomasAttractor.cs

protected sealed override void InitializeComputeBuffer()


{
if (cBuffer != null) cBuffer.Release();

cBuffer = new ComputeBuffer(instanceCount, Marshal.SizeOf(typeof(Params)));


Params[] parameters = new Params[cBuffer.count];
for (int i = 0; i < instanceCount; i++)
{
var normalize = (float)i / instanceCount;
var color = gradient.Evaluate(normalize);
parameters[i] = new Params(Random.insideUnitSphere *
emitterSize * normalize, particleSize, color);
}
cBuffer.SetData(parameters);
}

ThomasAttractor.compute

//Thomas Attractor parameters


float b;

float3 ThomasAttractor(float3 pos)


{
float dxdt = -b * pos.x + sin(pos.y);
float dydt = -b * pos.y + sin(pos.z);
float dzdt = -b * pos.z + sin(pos.x);
return float3(dxdt, dydt, dzdt) * DT;
}

[numthreads(THREAD_X, THREAD_Y, THREAD_Z)]

129
6 Strange Attractor

void Emit(uint id : SV_DispatchThreadID)


{
Params p = buf[id];
p.life = (float)id * -1e-05;
p.position = p.emitPos;
p.size.x = p.size.y;
buf[id] = p;
}

[numthreads(THREAD_X, THREAD_Y, THREAD_Z)]


void Iterator(uint id : SV_DispatchThreadID)
{
Params p = buf[id];
p.life.x += DT;
if (p.life.x > 0)
{
p.velocity = ThomasAttractor(p.position);
p.position += p.velocity;
}
buf[id] = p;
}

ThomasAttractor Thomas’ Cyclically Symmetric

Emit LorenzAttrator

6.5
Strange Attractor GPU

Strange Attractor

*4 UedaAttractor 2 *5 AizawaAttractor

6.6
• http://paulbourke.net/fractals/lorenz/

*4 http://www-lab23.kuee.kyoto-u.ac.jp/ueda/Kambe-Bishop_ver3-1.pdf
*5 http://www.algosome.com/articles/aizawa-attractor-chaos.html

130
6.6

• https://en.wikipedia.org/wiki/Thomas%27_cyclically_symmetric_attractor
• Lorenz, E. N. Deterministic Nonperiodic Flow, Journal of Atmospheric
Sciences, Vol.20, pp.130-141, 1963.
• Thomas, Ren 1999 . "Deterministic chaos seen in terms of feedback
circuits: Analysis, synthesis, ’labyrinth chaos’". Int. J. Bifurcation and
Chaos. 9 10 : 1889 1905.

131
7

Portal Unity

7.1
Portal*1 2007 Valve

Unity

https://github.com/IndieVisualLab/UnityGraphicsProgramming3
PortalGateSystem

7.2



Unity
Adam*2
E

*1 https://ja.wikipedia.org/wiki/Portal_(%E3%82%B2%E3%83%BC%E3%83%A0)
*2 https://assetstore.unity.com/packages/essentials/tutorial-projects/adam-character-pack-
adam-guard-lu-74842

132
7.3

• WASD


• E

Portal-
Gate

7.2.1
Unity Standard Assets *3 FirstPerson-
Character ThirdPersonChracter

Player Player
CullingMask

7.2.2
unity3d-jp playGROWnd*4
Portal

StageColl

7.3
XY
Z+

*3 https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-32351
*4 https://github.com/unity3d-jp/playgrownd

133
7 Portal Unity

7.1

Portal

PortalGun.cs

void Shot(int idx)


{
RaycastHit hit;
if (Physics.Raycast(transform.position,
transform.forward,
out hit,
float.MaxValue,
LayerMask.GetMask(new[] { "StageColl" })))
{
var gate = gatePair[idx];
if (gate == null)
{
var go = Instantiate(gatePrefab);
gate = gatePair[idx] = go.GetComponent<PortalGate>();

var pair = gatePair[(idx + 1) % 2];


if (pair != null)
{
gate.SetPair(pair);
pair.SetPair(gate);
}
}

gate.hitColl = hit.collider;

var trans = gate.transform;


var normal = hit.normal;

134
7.3

var up = normal.y >= 0f ? transform.up : transform.forward;

trans.position = hit.point + normal * gatePosOffset;


trans.rotation = Quaternion.LookRotation(-normal, up);

gate.Open();
}
}

StageColl transform.forward

PortalGate.hitColl
Z

Quaternion.LookRotation() normal.y
transform.up
PortalGate Y
Portal

7.2

135
7 Portal Unity

7.3

7.4 VirtualCamera
7.4.1

(VirtualCamera) RenderTexture
PortalGate
VirtualCamera

136
7.4 VirtualCamera

7.4 VirtualCamera

PortalGate.OnWillRenderObject()
VirtualCamera
PortalGate.cs

private void OnWillRenderObject()


{

VirtualCamera pairVC;
if (!pairVCTable.TryGetValue(cam, out pairVC))
{
if ((vc == null) || vc.generation < maxGeneration)
{
pairVC = pairVCTable[cam] = CreateVirtualCamera(cam, vc);
return;
}
}

137
7 Portal Unity

7.5

1. VirtualCamera
2. VirtualCamera VirtualCamera
3. VirtualCamera VirtualCamera
4.

VirtualCamera
PortalGate.maxGeneration

PortalGate.cs

VirtualCamera CreateVirtualCamera(Camera parentCam, VirtualCamera parentVC)


{
var rootCam = parentVC?.rootCamera ?? parentCam;
var generation = parentVC?.generation + 1 ?? 1;

var go = Instantiate(virtualCameraPrefab);
go.name = rootCam.name + "_virtual" + generation;
go.transform.SetParent(transform);

var vc = go.GetComponent<VirtualCamera>();
vc.rootCamera = rootCam;
vc.parentCamera = parentCam;

138
7.4 VirtualCamera

vc.parentGate = this;
vc.generation = generation;

vc.Init();

return vc;
}

VirtualCamera.rootCamera VirtualCamera

VirtualCamera.cs

public void Init()


{
camera_.aspect = rootCamera.aspect;
camera_.fieldOfView = rootCamera.fieldOfView;
camera_.nearClipPlane = rootCamera.nearClipPlane;
camera_.farClipPlane = rootCamera.farClipPlane;
camera_.cullingMask |= LayerMask.GetMask(new[] { PlayerLayerName });
camera_.depth = parentCamera.depth - 1;

camera_.targetTexture = tex0;
currentTex0 = true;
}

VirtualCamera.Init() Vir-
tualCamera CullingMask Player
parentCamera.dept
h - 1
Camera.CopyFrom() CommandBuffer
PostProcessingStack*5

7.4.2
VirtualCamera PortalGate.maxGeneration

VirtualCamera.cs

private void LateUpdate()


{
// PreviewCamera null
if (parentCamera == null)
{

*5 https://github.com/Unity-Technologies/PostProcessing

139
7 Portal Unity

Destroy(gameObject);
return;
}

camera_.enabled = parentGate.IsVisible(parentCamera);
if (camera_.enabled)
{
var parentCamTrans = parentCamera.transform;
var parentGateTrans = parentGate.transform;

parentGate.UpdateTransformOnPair(
transform,
parentCamTrans.position,
parentCamTrans.rotation
);

UpdateCamera();
}
}

VirtualCamera
PortalGate.cs

public bool IsVisible(Camera camera)


{
var ret = false;

var pos = transform.position;


var camPos = camera.transform.position;

var camToGateDir = (pos - camPos).normalized;


var dot = Vector3.Dot(camToGateDir, transform.forward);
if (dot > 0f)
{
var planes = GeometryUtility.CalculateFrustumPlanes(camera);
ret = GeometryUtility.TestPlanesAABB(planes, coll.bounds);
}

return ret;
}

1.
Z+
2. Unity

140
7.4 VirtualCamera

parentGate.UpdateTransformOnPair()
transform

PortalGate.cs

public void UpdateTransformOnPair(


Transform trans,
Vector3 worldPos,
Quaternion worldRot
)
{
var localPos = transform.InverseTransformPoint(worldPos);
var localRot = Quaternion.Inverse(transform.rotation) * worldRot;

var pairGateTrans = pair.transform;


var gateRot = pair.gateRot;
var pos = pairGateTrans.TransformPoint(gateRot * localPos);
var rot = pairGateTrans.rotation * gateRot * localRot;

trans.SetPositionAndRotation(pos, rot);
}

1.
2. gateRot
3.
4.

gateRot

public Quaternion gateRot { get; } = Quaternion.Euler(0f, 180f, 0f);

Y 180 Z

public Quaternion gateRot { get; } = Quaterion.Euler(180f, 0f, 0f);

141
7 Portal Unity

VirtualCamera.cs

void UpdateCamera()
{
var pair = parentGate.pair;
var pairTrans = pair.transform;
var mesh = pair.GetComponent<MeshFilter>().sharedMesh;
var vtxList = mesh.vertices
.Select(vtx => pairTrans.TransformPoint(vtx)).ToList();

TargetCameraUtility.Update(camera_, vtxList);

// Oblique
// pairGate = nearClipPlane pairGate
var pairGateTrans = parentGate.pair.transform;
var clipPlane = CalcPlane(camera_,
pairGateTrans.position,
-pairGateTrans.forward);

camera_.projectionMatrix = camera_.CalculateObliqueMatrix(clipPlane);
}

Vector4 CalcPlane(Camera cam, Vector3 pos, Vector3 normal)


{
var viewMat = cam.worldToCameraMatrix;

var normalOnView = viewMat.MultiplyVector(normal).normalized;


var posOnView = viewMat.MultiplyPoint(pos);

return new Vector4(


normalOnView.x,
normalOnView.y,
normalOnView.z,
-Vector3.Dot(normalOnView, posOnView)
);
}

VirtualCamera
VirtualCamera
TargetCameraUtility.Update()
Camera.rect
VirtualCamera
Camera.CalculateO
bliqueMatrix()
xyz w
Vector4

142
7.5

7.5



• 7.6
• VirtualCamera
• PortalGate.maxGeneration VirtualCamera
PortalGate

7.6

PortalGate.shader

GrabPass
{
"_BackgroundTexture"
}

GrabPass*6

7.5.1

PortalGate.shader

*6 https://docs.unity3d.com/ja/current/Manual/SL-GrabPass.html

143
7 Portal Unity

v2f vert(appdata_img In)


{
v2f o;

float3 posWorld = mul(unity_ObjectToWorld, float4(In.vertex.xyz, 1)).xyz;


float4 clipPos = mul(UNITY_MATRIX_VP, float4(posWorld, 1));
float4 clipPosOnMain = mul(_MainCameraViewProj, float4(posWorld, 1));

o.pos = clipPos;
o.uv = In.texcoord;
o.sposOnMain = ComputeScreenPos(clipPosOnMain);
o.grabPos = ComputeGrabScreenPos(o.pos);
return o;
}

2
clipPos clipPosOnMain
VirtualCamera
RenderTexture GrabPass

7.5.2

PortalGate.shader

float2 uv = In.uv.xy;
uv = (uv - 0.5) * 2; // map 0~1 to -1~1
float insideRate = (1 - length(uv)) * _OpenRate;

insideRate 1 0
_OpenRate Portal-
Gate.Open()
PortalGate.shader

// background
float4 grabUV = In.grabPos;
float2 grabOffset = float2(
snoise(float3(uv, _Time.y )),
snoise(float3(uv, _Time.y + 10))
);
grabUV.xy += grabOffset * 0.3 * insideRate;
float4 bgColor = tex2Dproj(_BackgroundTexture, grabUV);

snoise include Noise.cginc


SimplexNoise uv grabUV
insideRate

144
7.6

PortalGate.shader

// portal other side


float2 sUV = In.sposOnMain.xy / In.sposOnMain.w;
float4 sideColor = tex2D(_MainTex, sUV);

_MainTex VirutualCamera
UV
PortalGate.shader

// color
float4 col = lerp(bgColor, sideColor, _ConnectRate);

bgColor sideColor _
ConnectRate 0 1 1

PortalGate.shader

// frame
float frame = smoothstep(0, 0.1, insideRate);
float frameColorRate = 1 - abs(frame - 0.5) * 2;
float mixRate = saturate(grabOffset.x + grabOffset.y);
float3 frameColor = lerp(_FrameColor0, _FrameColor1, mixRate);
col.xyz = lerp(col.xyz, frameColor, frameColorRate);

col.a = frame;

insideRate _FrameColor0,_FrameCol
or1

7.6
PortalObj
GameObject

7.6.1

PortalObj

145
7 Portal Unity

7.7

PortalObj.cs

private void OnTriggerStay(Collider other)


{
var gate = other.GetComponent<PortalGate>();
if ((gate != null) && !touchingGates.Contains(gate) && (gate.pair != null))
{
touchingGates.Add(gate);
Physics.IgnoreCollision(gate.hitColl, collider_, true);
}
}

private void OnTriggerExit(Collider other)


{
var gate = other.GetComponent<PortalGate>();
if (gate != null)
{
touchingGates.Remove(gate);
Physics.IgnoreCollision(gate.hitColl, collider_, false);
}
}

OnTriggerEnder() OnTriggerStay()
Enter
tougingGates PortalGa
te.hitColl Physics.IgnoreCo
llision()
OnTriggerExit()
PortalGate.hitColl PortalGate
OnTriggerStay()

146
7.6

7.6.2
PortalObj.cs

private void Update()


{
var passedGate = touchingGates.FirstOrDefault(gate =>
{
var posOnGate = gate.transform.InverseTransformPoint(center.position);
return posOnGate.z > 0f;
});

if (passedGate != null)
{
PassGate(passedGate);
}

if ((rigidbody_ != null) && !rigidbody_.useGravity)


{
if ((Time.time - ignoreGravityStartTime) > ignoreGravityTime)
{
rigidbody_.useGravity = true;
}
}
}

center Transform
PortalObj GameObject

center.position z > 0f
touchingGates
PassGate()
PortalObj

PortalObj.cs

void PassGate(PortalGate gate)


{
gate.UpdateTransformOnPair(transform);

if (rigidbody_ != null)
{
rigidbody_.velocity = gate.UpdateDirOnPair(rigidbody_.velocity);
rigidbody_.useGravity = false;
ignoreGravityStartTime = Time.time;

147
7 Portal Unity

if (fpController != null)
{
fpController.m_MoveDir = gate.UpdateDirOnPair(fpController.m_MoveDir);
fpController.InitMouseLook();
}
}

VirtualCamera
PortalGate.UpdateTransformOnPair() Transform
RigidBody fpController

7.6.3

PortalObj

OnTriggerEn
ter(),OnCollisionEnter() Physics.IgnoreCollision()
On~Enter()
Physics.IgnoreCollision()
Enter

PortalObj

Unity

148
7.7

7.7
Portal Unity

CG

7.8
• Portal http://www.thinkwithportals.com/
• Adam Character Pack https://assetstore.unity.
com/packages/essentials/tutorial-projects/
adam-character-pack-adam-guard-lu-74842
• playGROWnd https://github.com/unity3d-jp/playgrownd
• PostProcessingStack https://github.com/Unity-Technologies/
PostProcessing

149
8

8.1

https://github.com/IndieVisualLab/UnityGraphicsProgramming3
OverReaction

150
8.1

8.1
OverReaction
Inspector

PhysicsScene

8.2
3

1.
2.
3.

moveEnergy

kinetic energy
move energy

Updat
e FixedUpdate
OverReaction.cs

protected void FixedUpdate()


{
this.crntMove = this.transform.position - this.prevPosition;

UpdateMoveEnergy();
UpdateDeformEnergy();
DeformMesh();

this.prevPosition = this.transform.position;
this.prevMove = this.crntMove;
}

FixedUpdate 2
3 ~ Update

151
8

Unity PhysX( ) Fix


edUpdate Update

UpdateMoveEnergy

OverReaction.cs

protected void UpdateMoveEnergy()


{
this.moveEnergy = new Vector3()
{
x = UpdateMoveEnergy
(this.crntMove.x, this.prevMove.x, this.moveEnergy.x),

y = UpdateMoveEnergy
(this.crntMove.y, this.prevMove.y, this.moveEnergy.y),

z = UpdateMoveEnergy
(this.crntMove.z, this.prevMove.z, this.moveEnergy.z),
};
}

X, Y, Z U
pdateMoveEnergy

OverReaction.cs

protected float UpdateMoveEnergy


(float crntMove, float prevMove, float moveEnergy)
{
int crntMoveSign = Sign(crntMove);
int prevMoveSign = Sign(prevMove);
int moveEnergySign = Sign(moveEnergy);

if (crntMoveSign == 0)
{
return moveEnergy * this.undeformPower;
}

public static int Sign(float value)


{
return value == 0 ? 0 : (value > 0 ? 1 : -1);
}

152
8.3

OverReaction.cs

if (crntMoveSign != prevMoveSign)
{
return moveEnergy - crntMove;
}

OverReaction.cs

if (crntMoveSign != moveEnergySign)
{
return moveEnergy + crntMove;
}

OverReaction.cs

if (crntMoveSign < 0)
{
return Mathf.Min(crntMove * this.deformPower,
moveEnergy * this.undeformPower);
}
else
{
return Mathf.Max(crntMove * this.deformPower,
moveEnergy * this.undeformPower);
}

8.3

deformEnergy
UpdateDeformEnergy

153
8

1
0

OverReaction.cs

protected void UpdateDeformEnergy()


{
float deformEnergyVertical
= this.moveEnergy.magnitude
* Vector3.Dot(this.moveEnergy.normalized,
this.crntMove.normalized);

+0.8 -0.8
1 - 0.8 = 0.2
* 0.8 1 * 1.8
OverReaction.cs

protected void UpdateDeformEnergy()


{

float deformEnergyHorizontalRatio
= deformEnergyVertical / this.maxDeformScale;

float deformEnergyHorizontal
= 1 - deformEnergyHorizontalRatio;

deformEnergyVertical = 1 + deformEnergyVertical;
}

154
8.3

deformEnergyHorizontal 1
deformEnergyVertical deformEnergyHorizontal
1
OverReaction.cs

protected void UpdateDeformEnergy()


{
float deformEnergyVertical
= this.moveEnergy.magnitude
* Vector3.Dot(this.moveEnergy.normalized,
this.crntMove.normalized);

float deformEnergyHorizontalRatio
= deformEnergyVertical / this.maxDeformScale;

float deformEnergyHorizontal
= 1 - deformEnergyHorizontalRatio;

if (deformEnergyVertical < 0)
{
deformEnergyVertical = deformEnergyHorizontalRatio;
}

deformEnergyVertical = 1 + deformEnergyVertical;

OverReaction.cs

deformEnergyVertical = Mathf.Clamp(deformEnergyVertical,
this.minDeformScale,
this.maxDeformScale);

deformEnergyHorizontal = Mathf.Clamp(deformEnergyHorizontal,
this.minDeformScale,
this.maxDeformScale);

this.deformEnergy = new Vector3(deformEnergyHorizontal,


deformEnergyVertical,
deformEnergyHorizontal);

155
8

8.4

DeformMesh
GPU

deformEnergy moveEnergy

moveEnergy

OverReaction.cs

protected void DeformMesh()


{
Vector3[] deformedVertices = new Vector3[this.baseVertices.Length];

Quaternion crntRotation = this.transform.localRotation;


Quaternion crntRotationI = Quaternion.Inverse(crntRotation);

Quaternion moveEnergyRotation
= Quaternion.FromToRotation(Vector3.up, this.moveEnergy.normalized);
Quaternion moveEnergyRotationI = Quaternion.Inverse(moveEnergyRotation);

1.
2.
3. deformEnergy
4.
5.

deformEnergy

OverReaction.cs

for (int i = 0; i < this.baseVertices.Length; i++)


{
deformedVertices[i] = this.baseVertices[i];
deformedVertices[i] = crntRotation * deformedVertices[i];
deformedVertices[i] = moveEnergyRotationI * deformedVertices[i];
deformedVertices[i] = new Vector3(
deformedVertices[i].x * this.deformEnergy.x,
deformedVertices[i].y * this.deformEnergy.y,

156
8.5

deformedVertices[i].z * this.deformEnergy.z);
deformedVertices[i] = moveEnergyRotation * deformedVertices[i];
deformedVertices[i] = crntRotationI * deformedVertices[i];
}

this.baseMesh.vertices = deformedVertices;

8.5

157
1 Baking Skinned Animation to Texture - / @sugi_cho
Unity
=> hi@sugi.cc

• https://twitter.com/sugi_cho
• https://github.com/sugi-cho
• http://sugi.cc

2 Gravitational N-Body Simulation / @kodai100


VFX TA
VR
Twitter DM

• https://twitter.com/kodai100_tw
• https://github.com/kodai100
• http://creativeuniverse.tokyo/portfolio/

3 Screen Space Fluid Rendering - / @irishoak


MV
VJ
sugi-cho mattatz Aqueduct

• https://twitter.com/_irishoak
• https://github.com/hiroakioishi
• http://irishoak.tumblr.com/
• https://a9ueduct.github.io/

158
4 GPU-Based Cellular Growth Simulation - / @mattatz
Web

• https://twitter.com/mattatz
• https://github.com/mattatz
• http://mattatz.org/

5 Reaction Diffusion - @kaiware007


Twitter
VJ

• https://twitter.com/kaiware007
• https://github.com/kaiware007
• https://www.instagram.com/kaiware007/
• https://kaiware007.github.io/

6 Strange Attractor - / @sakope

Twitter

• https://twitter.com/sakope
• https://github.com/sakope

7 Portal Unity - / @fuqunaga

• https://twitter.com/fuqunaga
• https://github.com/fuqunaga
• https://fuquna.ga

159
8 - @XJINE

UnityShaderProgramming

• https://twitter.com/XJINE
• https://github.com/XJINE
• http://neareal.com/

160
Unity Graphics Programming vol.3

2018 10 8 5 v1.0.0
IndieVisualLab
IndieVisualLab
IndieVisualLab

(C) 2018 IndieVisualLab

161

You might also like