【UE5】GeometryScript を利用した宝石のカッティング

# 【UE5】GeometryScript を利用した宝石のカッティング
## 概略
　この「端折った」部分をほどほどに展開して記事にしたものです。  
プロジェクト付き（末尾のリンクからどうぞ）。

https://x.com/NKDTR/status/1862141739534053746

## 前置き
### カッティングとは
　宝石の原石を切断したり研磨したりしてきれいに見える形に加工する作業をカッティングといいます。

https://gstvfan.jp/media/knowledge/gems-cutting

### UE5上でのカッティング
　要するにモデリングということになりますので Modeling Mode で何とかすることも勿論可能です。ただ、今回の趣旨は宝石の形状によく見られる特徴を利用して、条件に当てはまる場合について楽をしようというところにあります。  

　多分に趣味的な事例であって何か新しかったり珍しい機能を紹介しようというものではありません（というか、言ってしまえば所謂 Boolean です）が、立体図形の好きな方には面白い、かも。

### 宝石の典型的なカッティング形状について

https://gstvfan.jp/media/knowledge/types-of-gem-cuts

　例外はありますが、多くが以下のような特徴を持ちます。
* 平滑な面の集合である
* 凹みを持たない
* 対称性が高い


　美しく見えやすいという理由の他に、加工機がこのような形状を作るのに適しているという理由もありそうです（卵が先か鶏が先かみたいな話ではありますが）。
加工機は「宝石研磨機」とか「faceting machine」とか呼ばれるものです。検索するとどのような装置なのかが概ね分かるかと思います。

　本記事ではこのような典型的な形状を効率よく作成する方法の例を示します。  

## 実装
### GeometryScript について
　今回は Modeling Mode ではなく、プロシージャルかつ非破壊的な処理に適した GeometryScript を用います。  
　機能自体はかなり前から存在していた GeometryScript ですが UE5.5 になってヌルっと Beta 表記が無くなりましたので遠慮なく使っていきます。  
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/db8e1769-66ac-870a-6df1-ab0fb69121e9.png)
　でも、本記事用のプロジェクトは UE5.3 で作られています。

### カッティング用マップ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/de027e9c-390c-b636-8914-6ca30b39a183.png)
　このようなマップで作業します。
　
　以下各要素について説明していきますが、詳細は省いて考え方のみに留めます。  
プロジェクトへのリンクが末尾にありますので必要に応じてダウンロードしてみてください。  

　各要素といっても主要なクラスは二つのみで、それぞれに対して形状のバリエーションが多少存在するくらいです。  
　いずれも GeneratedDynamicMeshActor のサブクラスとして定義されています。

#### ストーン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/7bf322de-b50e-c1db-fd54-0598d9d5a772.png)
　カットの対象となる元の石にあたります。  
CGでは手軽にカットを行えるので初期形状の重要性は低いと言えますが、無駄にボリュームがあり過ぎると編集中の画面が見づらくなることもあるので程々の円柱が扱いやすいです。  
　子としてアタッチされた（__断じて ChildActor ではない__）カッターを適用し形状を生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/0c5887e9-7073-fccd-2b3b-9147a67df626.png)

　単に平面での切り離し（PlaneCut）を繰り返すことによって大抵のカットは実現できるわけですが、これに用いる全ての平面を逐一定義するのは大変です。  
　例として、恐らく最も有名なカットであるブリリアント・カット（ダイヤモンドの典型的なカット）には57のファセット（面）が含まれ、57の平面による PlaneCut が必要になります。

　まとめて多数の面を生成したいので多面体との Boolean(Intersection) によって形を作ることを考えます。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/c36c4a3e-4768-91ea-3a11-54882c4c5542.png)
↑こんな感じのことをカッターの数だけ繰り返します。立体同士の交差部分を結果として取り出しています。  
 
#### カッター
　ストーンをカットするための形状定義です。  
これは面ではなく立体的な形状（多面体）になります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/ee1e0223-4db1-dd27-c2b1-951cc6dd2f54.png)

　半透明の面とワイヤーフレームで可視化しています（これらは GeneratedDynamicMeshActor のプロパティで設定できます）。  
カットを行うと必然的にストーンとの間で Z-fight が発生しますので、マテリアルで僅かに画面奥へオフセットします。これには切断した部分をいい感じに可視化する効果もあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/d12c5005-620f-54f9-2ed2-15b2ec76f1cc.png)
　↑0.1mm 相当のオフセットをしています。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/b88771c8-0d3a-6789-bf57-890c6a8b03c2.png)
　↑緑色でない部分が表示中のカッターで生成された面（ファセット）です。


　おそらく、カッターの中で最も有用な形状は多角錐です。
　宝石のカットの多くは回転対称な形をしていますが、それを実現するために必要な PlaneCut の配置もまた回転対称となります。  
　回転対称に配置した PlaneCut の結果は（底面を無視すれば）多角錐との And 演算の結果と同様ですので、多角錐を用いることでカットの手間を大きく省くことができます。

### 作成例
　実際のカット過程の例を二つ紹介していきます。
 
#### ブリリアントカットを作る
　名前では何のことか分からないかもしれませんので、とりあえず完成形を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/dc5f1213-befc-3805-4967-60950cec1095.png)
　宝石のカットとしては最もよく知られた形状かと思います。  
各面の角度については細かい決まりがあるのですが、別に出荷しようというわけでもないので下のページを参考にしつつある程度適当にやります。
https://www.thesparklr.com/learn/57-facets-brilliant-story
https://4cs.gia.edu/en-us/blog/diamond-anatomy-explained/

##### 手順
###### 円柱に近い形のストーンを置く
　とりあえず32角柱にしました。  
このあと大部分は切り捨てられます。本物の石であればかなり勿体ない使い方ですね。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/21963d4a-4c53-f495-329e-6bc300e30828.png)

###### パビリオン（下部）のカット
　8角錐のカッターを逆さまに置きます。  
以下、カッターはストーンの子としてアタッチします（子といっても、__決して ChildActor ではありません__ ので注意して下さい）。  
対称性のため、相対座標のX,Y成分は0になります。
側面の角度は41度らしいのでそのように設定します。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/092955ae-b97e-3e87-8a17-2aa567ee0d54.png)

　ストーンの Apply Cutters をオンにするとカットが適用された状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/1673bd4a-676b-f846-782a-007069007a95.png)

　次に16角錐のカッターでガードル（側面）接触部（Lower Girdle）を作ります。  
先ほど配置したものを複製すると早いでしょう。  
Outliner 上で作業中のカッターのみを可視状態にする（目のアイコンが開いた状態）と、そのカッターの効果をわりといい感じに表示できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/ee5b7169-3309-2851-a356-ea1a383be0c6.png)
↑表示中のカッターで切った部分がストーンの元の色で描画されます

　ここまででパビリオン側の作業は終了です。  
以降もカッターを配置していくだけなのですが、なにげに繊細な作業ではあります。
直径 50cm という巨大なストーンを用いているにもかかわらず、ミリ単位の配置調整が必要になったりします。
 
###### クラウン（上部）のカット
　やることは下部とあまり変わらないので以下は巻いていきます。  

　こうして、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/6de74fb8-c339-e75f-135b-e92f53bb0569.png)
↑どういう順序でカットするかには検討の余地がありそうです。今回は大きめの面積を持つ凧型の部分を最初に作っています。

　こうして、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/2140e14f-6f23-1e48-ef33-f531da499c45.png)
↑天辺の平面を作りました。スクリーンショットに次で使うカッター（UpperGirdle）がチラ見えしています。なお、ストーンにアタッチされていないカッターは形状に影響を与えません

　こう！（2個いっぺんに置きました）
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/470352fc-8f25-1864-b435-a09ac4e2526d.png)

　ストーンに戻って Output Mesh に適当な StaticMesh （__変更して良いものを割り当てる。Engine に入っているやつとかはダメ、絶対__）を割り当て、Copy To Static Mesh ボタンを押すと・・・
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/07647644-ae17-0516-cff3-9aa214256043.png)

　とりあえずスタティックメッシュが出来上がります。  
これを複製するなどしていい感じに使えば良いということになります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/dc5f1213-befc-3805-4967-60950cec1095.png)

　微妙にずれた頂点が沢山作られることを心配されるかもしれませんが、ある程度であれば GeometryScript の機能で最適化できます（Simplify 系）。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/eb8d7af8-f34b-872f-4213-013beefe78ae.png)
　元の精度が高いに越したことはありませんが。



#### 陰陽カットを作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/0d1170c2-23cf-1602-3ed5-9461bb767469.png)
　実は今回のカッティングの仕組みはコレを作るために用意したものです。
　太極図をモチーフとしたカットです。  
上面が太極図（陰陽と書いた方が通じるでしょうか？）模様になっています。  
　日本語では見つけづらいかもしれませんが、"Yin Yang cut gem" などと検索すると実物が見つかります。
 
##### 手順
　どのような図形を重ねればこの形状になるのかを想像できるでしょうか。  
　下部は単純に多角錐ですが、上部の太極図部分に特徴があり一筋縄でいかないようにも見えます。  
　イメージしづらいと思いますし説明も難しいのですが、多角錐を真ん中から伸ばしたような形状を互い違いに配置することで形成できます。  
　このためだけに専用のカッターを実装しました（どうしてもやりたかったので）。  
これ自体は直方体から PlaneCut で削り出しています。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/f86bcde6-d5df-239f-84fd-bf6fdf6ef16d.png)上から
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/250ca5a8-c12e-3c33-a7a2-9716f9b7c33c.png)下から

　これをかなり浅い角度に広げて使用します。  
1つだけ適用すると下のようになります。
左手前が多角錐部分で、右奥側が伸ばした平面部分でカットされています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/1016f52c-a424-f47d-172b-f02a5b91fb61.png)

　左奥にずらして180度回転させた二つ目のカッターを適用すると、こうなります。  
直観的にこれを納得できる方は、もしかしてカットの設計に向いているのかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/cda702fb-38f6-40a5-967a-e7a3dd9ac9da.png)

　あとは底の方を多角錐でカットすればこの形状が出来上がります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61553/0d1170c2-23cf-1602-3ed5-9461bb767469.png)

## まとめ
　GeometryScript を用いて宝石のカットを行う例を示しました。
以下のリンク先にプロジェクトを公開してありますので具体的な実装内容が知りたい方はダウンロードしてみて下さい。

https://github.com/nkdtr/GemCutting

　マテリアルについては、別途いい感じにして頂ければと思います。  
例として [CrystalRenderer](https://fab.com/s/458469529971) を使うとこうなります。  
そちらについても [技術的な解説](https://nkdtr.hatenablog.com/entry/2024/01/30/230537) がありますのでよければご覧ください。
<iframe width="560" height="315" src="https://www.youtube.com/embed/taKkOU8bogA?si=ACOuKoj-HAhTXEEd" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>