🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Blender

Blenderとは、オープンソースの3DCGソフトウェアです。フリーでありながら、3Dモデル作成、レンダリング、アニメーション、コンポジットなどのハイエンドに匹敵する高い機能を持ち、さらにゲームエンジンも搭載しています。

Q&A

解決済

1回答

3559閲覧

平面を球体に綺麗に変形させるアニメーションを作りたい

KTN

総合スコア16

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Blender

Blenderとは、オープンソースの3DCGソフトウェアです。フリーでありながら、3Dモデル作成、レンダリング、アニメーション、コンポジットなどのハイエンドに匹敵する高い機能を持ち、さらにゲームエンジンも搭載しています。

0グッド

1クリップ

投稿2021/01/19 09:29

編集2021/01/20 02:58

前提・実現したいこと

こんにちは。
Unityでこの動画のような技
![イメージ説明
を作ってみたくなり、Blenderを用いて画面と同じサイズの平面を作成、そしてその平面を球体に変形させるアニメーションを作り、Unityに取り込み、その物体にレンダーテクスチャを使って再現を試みたのですが、上手く行かなかったので相談させて頂きたいです。

試したこと

先ずシンプル変形(Simple Deform)を用い、それをアニメーションにして綺麗な変形は作れました。
イメージ説明
しかしこれはあくまで見た目の変形?でありメッシュは変形していないため、このアニメーションをUnityに持っていっても変形しませんでした。

次に平面を湾曲(Warp)させ球体に変形、そしてシェイプキー(Shape Keys)を用いてアニメーションを作成、こちらはしっかりとUnityでも動作しました。
イメージ説明
ただ変形の仕方が上記ゲームの動画及びシンプル変形を用いた場合に比べ綺麗ではなく、求めるものとは違った結果となってしまいました。

綺麗に変形させUnityに取り込む方法、或いはシェーダーなどを用いて上記のような結果を得る方法はありますでしょうか?
少しだけでもご助力頂きたいです!

補足情報(FW/ツールのバージョンなど)

Blender 2.91.0

###追記:半球を用いた場合
イメージ説明
こちらは普通の球体。
変形の動き以外は問題ありません。

イメージ説明
こちらは半球。
変形の動きは綺麗ですがテクスチャが引き伸ばされてしまう…。
また私が想定している使用法上、360°どこからでも見えるようにしたいので半球だと背後をカバー出来なくなってしまう。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

球でないと都合が悪いでしょうかね?
ブレンドシェイプを試してみたところ、球への変形だと確かにご質問者さんのお示しいただいたような変形の仕方でしたが、半球への変形でしたらなかなか悪くない感じでした。もしさしつかえなければ半球にしてしまってはいかがでしょう。

あいにくBlenderを使い慣れていないため、メッシュはスクリプトから作ることにしました。
下記のようなスクリプトをプロジェクト内のEditorフォルダに入れ...

C#

1using System.Linq; 2using UnityEditor; 3using UnityEngine; 4 5public class PlaneToHemisphereWizard : ScriptableWizard 6{ 7 [SerializeField][Min(0.0f)] private float planeWidth = 4.0f; 8 [SerializeField][Min(0.0f)] private float planeHeight = 3.0f; 9 [SerializeField][Min(0.0f)] private float hemisphereRadius = 1.0f; 10 [SerializeField][Min(1)] private int divisions = 8; 11 12 private void OnWizardCreate() 13 { 14 var bottomLeft = new Vector3(-this.planeWidth * 0.5f, -this.planeHeight * 0.5f, 0.0f); 15 var texelWidth = 0.5f / this.divisions; 16 var texelHeight = 0.5f / this.divisions; 17 var deltaX = this.planeWidth * texelWidth; 18 var deltaY = this.planeHeight * texelHeight; 19 var vertexCountX = (this.divisions * 2) + 1; 20 var vertexCountY = (this.divisions * 2) + 1; 21 var planeVertices = Enumerable.Range(0, vertexCountY).SelectMany( 22 j => Enumerable.Range(0, vertexCountX).Select( 23 i => bottomLeft + new Vector3(i * deltaX, j * deltaY, 0.0f))).ToArray(); 24 var planeNormals = planeVertices.Select(_ => Vector3.back).ToArray(); 25 var planeTangents = planeVertices.Select(_ => new Vector4(1.0f, 0.0f, 0.0f, -1.0f)).ToArray(); 26 var planeUvs = Enumerable.Range(0, vertexCountY).SelectMany( 27 j => Enumerable.Range(0, vertexCountX).Select( 28 i => new Vector2(i * texelWidth, j * texelHeight))).ToArray(); 29 var quadTriangles = new[] 30 { 31 0, vertexCountX, 1, vertexCountX + 1, 1, vertexCountX 32 }; 33 var planeTriangles = Enumerable.Range(0, vertexCountY - 1).SelectMany( 34 j => Enumerable.Range(0, vertexCountX - 1).SelectMany( 35 i => quadTriangles.Select(k => i + (j * vertexCountX) + k))).ToArray(); 36 var hemisphereNormals = planeUvs.Select( 37 uv => 38 { 39 var angleH = (uv.x + 0.5f) * Mathf.PI; 40 var angleV = (uv.y - 0.5f) * Mathf.PI; 41 var sinH = Mathf.Sin(angleH); 42 var cosH = Mathf.Cos(angleH); 43 var sinV = Mathf.Sin(angleV); 44 var cosV = Mathf.Cos(angleV); 45 return new Vector3(-sinH * cosV, sinV, cosH * cosV); 46 }).ToArray(); 47 var hemisphereTangents = planeUvs.Select( 48 uv => 49 { 50 var angleH = (uv.x + 0.5f) * Mathf.PI; 51 var sinH = Mathf.Sin(angleH); 52 var cosH = Mathf.Cos(angleH); 53 return new Vector4(-cosH, 0.0f, -sinH, -1.0f); 54 }).ToArray(); 55 var hemisphereVertices = hemisphereNormals.Select(normal => normal * this.hemisphereRadius).ToArray(); 56 var mesh = new Mesh 57 { 58 name = "PlaneToHemisphere", 59 vertices = planeVertices, 60 normals = planeNormals, 61 tangents = planeTangents, 62 uv = planeUvs, 63 triangles = planeTriangles 64 }; 65 mesh.AddBlendShapeFrame( 66 "Deformation", 67 100.0f, 68 planeVertices.Zip( 69 hemisphereVertices, 70 (planeVertex, hemisphereVertex) => hemisphereVertex - planeVertex).ToArray(), 71 planeNormals.Zip( 72 hemisphereNormals, 73 (planeNormal, hemisphereNormal) => hemisphereNormal - planeNormal).ToArray(), 74 planeTangents.Zip( 75 hemisphereTangents, 76 (planeTangent, hemisphereTangent) => (Vector3)hemisphereTangent - (Vector3)planeTangent).ToArray()); 77 mesh.RecalculateNormals(); 78 mesh.RecalculateTangents(); 79 var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); 80 var newObject = new GameObject(mesh.name); 81 var skinnedMeshRenderer = newObject.AddComponent<SkinnedMeshRenderer>(); 82 skinnedMeshRenderer.sharedMesh = mesh; 83 skinnedMeshRenderer.sharedMaterial = cube.GetComponent<Renderer>().sharedMaterial; 84 DestroyImmediate(cube); 85 ProjectWindowUtil.CreateAsset(mesh, $"{mesh.name}.asset"); 86 } 87 88 [MenuItem("Assets/Create/Plane to Hemisphere")] 89 private static void OpenWizard() 90 { 91 DisplayWizard<PlaneToHemisphereWizard>("Plane to Hemisphere"); 92 } 93}

メニューの「Assets」→「Create」→「Plane To Hemisphere」でメッシュアセットを作成、併せてシーン上にそれをセットしたオブジェクトを生成させました。
SkinnedMeshRendererの「BlendShapes」→「Deformation」を操作すると、下図のように平面から半球に変形しました。

図1

##シェーダー上で変形する案

下記のようなマテリアルを使用したところ...

ShaderLab

1Shader "Custom/PlaneToSphere" 2{ 3 Properties 4 { 5 _Color ("Color", Color) = (1, 1, 1, 1) 6 _MainTex ("Albedo (RGB)", 2D) = "white" {} 7 _Glossiness ("Smoothness", Range(0, 1)) = 0.5 8 _Metallic ("Metallic", Range(0, 1)) = 0.0 9 _SphereRadius ("Sphere Radius", Range(0, 10)) = 1.0 10 _Deformation ("Deformation", Range(0, 1)) = 0.0 11 } 12 SubShader 13 { 14 Tags { "RenderType"="Opaque" } 15 16 Cull Off 17 18 CGPROGRAM 19 #pragma surface surf Standard fullforwardshadows vertex:vert 20 #pragma target 3.0 21 22 sampler2D _MainTex; 23 half _Glossiness; 24 half _Metallic; 25 fixed4 _Color; 26 float _SphereRadius; 27 float _Deformation; 28 29 struct Input 30 { 31 float2 uv_MainTex; 32 }; 33 34 #define EPSILON 0.000001 35 36 void vert(inout appdata_full v) 37 { 38 if (_Deformation < EPSILON) 39 { 40 return; 41 } 42 43 // 法線、接線はブレンドシェイプ版と同じ線形モーフィングでも 44 // 粗が目立たなかったためブレンドシェイプ版と同様に補間した 45 float sinH; 46 float cosH; 47 float sinV; 48 float cosV; 49 sincos(v.texcoord.x * 2.0 * UNITY_PI, sinH, cosH); 50 sincos((v.texcoord.y - 0.5) * UNITY_PI, sinV, cosV); 51 float3 sphereNormal = float3(-sinH * cosV, sinV, cosH * cosV); 52 float3 sphereTangent = float3(cosH, 0.0, sinH) * v.tangent.w; 53 v.normal = lerp(v.normal, sphereNormal, _Deformation); 54 v.tangent.xyz = lerp(v.tangent.xyz, sphereTangent, _Deformation); 55 56 // 頂点座標の水平成分を円弧に沿った補間とすることで見苦しさを軽減する 57 // 垂直成分は線形補間でもさほど違和感はなさそうだった 58 float sinTheta; 59 float cosTheta; 60 float geographicCoord = (2.0 * v.texcoord.x - 1.0) * UNITY_PI; 61 sincos(_Deformation * geographicCoord, sinTheta, cosTheta); 62 float3 arcPosition = float3(sinTheta, 0.0, 1.0 - cosTheta) * _SphereRadius / _Deformation; 63 arcPosition.yz += float2(lerp(v.vertex.y, sphereNormal.y * _SphereRadius, _Deformation), lerp(0.0, -_SphereRadius, _Deformation)); 64 arcPosition.x *= lerp(max(abs(v.vertex.x), EPSILON) / max(abs(_SphereRadius * geographicCoord), EPSILON), 1.0, _Deformation); 65 arcPosition.xz *= lerp(1.0, cosV, _Deformation); 66 v.vertex.xyz = arcPosition; 67 } 68 69 void surf(Input IN, inout SurfaceOutputStandard o) 70 { 71 fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; 72 o.Albedo = c.rgb; 73 o.Metallic = _Metallic; 74 o.Smoothness = _Glossiness; 75 o.Alpha = c.a; 76 } 77 ENDCG 78 } 79 FallBack "Diffuse" 80}

下図のような変形の仕方になりました。マテリアルの_Deformationをスクリプトから操作すれば平面から球体へ変形できるかと思います。

図2

##Unlit版

ShaderLab

1Shader "Unlit/PlaneToSphere" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 _SphereRadius ("Sphere Radius", Range(0, 10)) = 1.0 7 _Deformation ("Deformation", Range(0, 1)) = 0.0 8 } 9 10 SubShader 11 { 12 Tags { "RenderType"="Opaque" } 13 14 Cull Off 15 16 Pass 17 { 18 CGPROGRAM 19 #pragma vertex vert 20 #pragma fragment frag 21 #pragma multi_compile_fog 22 23 #include "UnityCG.cginc" 24 25 struct appdata 26 { 27 float4 vertex : POSITION; 28 float2 uv : TEXCOORD0; 29 }; 30 31 struct v2f 32 { 33 float2 uv : TEXCOORD0; 34 UNITY_FOG_COORDS(1) 35 float4 vertex : SV_POSITION; 36 }; 37 38 sampler2D _MainTex; 39 float4 _MainTex_ST; 40 float _SphereRadius; 41 float _Deformation; 42 43 #define EPSILON 0.000001 44 45 void deform(inout appdata v) 46 { 47 if (_Deformation < EPSILON) 48 { 49 return; 50 } 51 52 // Unlitなら法線、接線は不要なはずなので簡略化した 53 float sinV; 54 float cosV; 55 sincos((v.uv.y - 0.5) * UNITY_PI, sinV, cosV); 56 float sinTheta; 57 float cosTheta; 58 float geographicCoord = (2.0 * v.uv.x - 1.0) * UNITY_PI; 59 sincos(_Deformation * geographicCoord, sinTheta, cosTheta); 60 float3 arcPosition = float3(sinTheta, 0.0, 1.0 - cosTheta) * _SphereRadius / _Deformation; 61 arcPosition.yz += float2(lerp(v.vertex.y, sinV * _SphereRadius, _Deformation), lerp(0.0, -_SphereRadius, _Deformation)); 62 arcPosition.x *= lerp(max(abs(v.vertex.x), EPSILON) / max(abs(_SphereRadius * geographicCoord), EPSILON), 1.0, _Deformation); 63 arcPosition.xz *= lerp(1.0, cosV, _Deformation); 64 v.vertex.xyz = arcPosition; 65 } 66 67 v2f vert(appdata v) 68 { 69 v2f o; 70 deform(v); 71 o.vertex = UnityObjectToClipPos(v.vertex); 72 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 73 UNITY_TRANSFER_FOG(o,o.vertex); 74 return o; 75 } 76 77 fixed4 frag(v2f i) : SV_Target 78 { 79 fixed4 col = tex2D(_MainTex, i.uv); 80 UNITY_APPLY_FOG(i.fogCoord, col); 81 return col; 82 } 83 ENDCG 84 } 85 } 86}

投稿2021/01/19 21:56

編集2021/01/20 20:47
Bongo

総合スコア10811

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KTN

2021/01/20 03:01 編集

回答ありがとうございます! 実は私も半球の場合は綺麗なアニメーションが作れることは把握していましたが、いくつかの問題(追記に記載致しましたのでそちらを参照ください)があるのでやはりしっかりとした球体が望ましいです…。 長くお付き合いさせてしまって申し訳ないのですが、その半球の状態から球体に持っていくことは出来ないでしょうか?
Bongo

2021/01/20 14:04

ブレンドレイプには限界を感じ、方針を変更してシェーダー上で変形させるのを試してみました。 ブレンドシェイプだと頂点座標が平面の位置から球面上の位置へ一直線に移動していきますが、それだと特に水平の動きが見苦しさの原因になっているように感じました。そこで水平方向の動きを紙を筒状に丸めるのをイメージした形にしてみましたが、いかがでしょうかね? 追記の実験で使ったメッシュは当初の回答で使用したブレンドシェイプ付きメッシュではありますが(作り直すのが面倒で手抜きしました...)、ブレンドシェイプ機能は使用していませんので、Z=0の平面上に配置されたただの平面メッシュであれば適用可能かと思います。
KTN

2021/01/20 15:44

うわ!ありがとうございます!ご記載して頂いたシェーダーで試したところ理想の動きをしてくれました!! ただすみません…もはや二つ目の質問になってしまい本当に申し訳ないのですが、元々レンダーテクスチャの色をそのまま出すため、Unlitシェーダーを用いていたんですよね。 ただ私まだまだシェーダーについて把握しておらず、どのように追記すればいいか分からず…。 ヒントだけでも構いませんので、ご記載頂いたシェーダーにUnlitの機能をつける方法をお教え願えないでしょうか…?
Bongo

2021/01/20 20:48

Unlit版を作ってみました。法線や接線の補間は不要なため省略しましたので、いくらかシンプルになりましたね。
KTN

2021/01/21 03:12

本当にありがとうございました!ご提示頂いたシェーダーで想像していた通りの動きが出来ました! それとすみません、最初からUnlitでお願いしておけば手間を減らせましたね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問