質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
86.02%
Unity

Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

Q&A

解決済

UNITYで座標を変えて変形を行うシェーダーを作りたいです。

O_HIROKO
O_HIROKO

総合スコア1

Unity

Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

1回答

0グッド

0クリップ

221閲覧

投稿2022/11/23 23:33

イメージ説明

前提

UNITYで、オブジェクトの中心からカメラに向かって伸びるように頂点が移動するシェーダーを作りたいです。
以下のコードを自分で作ってみたのですが、実際にはカメラ方向以外の向きへ変形してしまい、上手くいかない為、アドバイスを頂けると嬉しいです。
何卒宜しくお願い致します。

実現したいこと

コードを書いてやりたかった事
①オブジェクトの原点をワールド座標に合わせる
②オブジェクトの向きをワールド座標に合わせる
③オブジェクトの原点からカメラ方向へオブジェクトを引き延ばす
④オブジェクトの向きを元に戻す
⑤オブジェクトの原点を元に戻す

該当のソースコード

Shader "Unlit/AAA" { Properties{ } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f// { float4 vertex : SV_POSITION; }; v2f vert (appdata v) {      //カメラとオブジェクトのワールド座標を設定 float3 C = UNITY_MATRIX_I_V._m03_m13_m23; float3 B = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz ; v2f o;   o.vertex = mul( unity_ObjectToWorld, v.vertex );      //オブジェクトの原点をワールド座標に合わせる half4x4 VA = half4x4 (1, 0, 0, -B.x, 0, 1, 0, -B.y, 0, 0, 1, -B.z, 0, 0, 0, 1); o.vertex = mul(VA, o.vertex);      //座標の回転1 half4x4 VB= half4x4 ((C.y-B.y)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), -(C.x-B.x)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), 0, 0, (C.x-B.x)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), (C.y-B.y)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); o.vertex = mul(VB, o.vertex);      //座標の回転2 half4x4 VC = half4x4 (1, 0, 0, 0, 0, -(C.z-B.z)/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y))/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), 0, 0, -sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y))/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), -(C.z-B.z)/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), 0, 0, 0, 0, 1); o.vertex = mul(VC, o.vertex);      //オブジェクトをカメラのほうへ引き延ばす o.vertex.x =o.vertex.x*0.001;      //座標の回転2の反対 half4x4 WC = half4x4 (1, 0, 0, 0, 0, -(C.z-B.z)/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), -sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y))/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), 0, 0, sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y))/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), -(C.z-B.z)/sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)+(C.z-B.z)*(C.z-B.z)), 0, 0, 0, 0, 1); o.vertex = mul(WC, o.vertex);      //座標の回転1の反対 half4x4 WB= half4x4 ((C.y-B.y)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), (C.x-B.x)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), 0, 0, -(C.x-B.x)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), (C.y-B.y)/ sqrt((C.x-B.x)*(C.x-B.x)+(C.y-B.y)*(C.y-B.y)), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); o.vertex = mul(WB, o.vertex);      //オブジェクトの原点を元に戻す half4x4 WA = half4x4 (1, 0, 0, B.x, 0, 1, 0, B.y, 0, 0, 1, B.z, 0, 0, 0, 1); o.vertex = mul(WA, o.vertex); o.vertex = mul( UNITY_MATRIX_VP,o.vertex ); //頂点座標をVP変換//ワールド座標→clip space return o; } fixed4 frag (v2f i) : SV_Target { return float4(1, 0, 0, 0); } ENDCG } } }

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

UNITYのバージョン:2021.3.5f1

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答1

1

ベストアンサー

X軸がオブジェクトとカメラを結ぶ直線に沿うように座標変換し、X方向に拡大縮小するという方針でいいでしょうかね?
下記のように変換してみましたが、いかがでしょうか。

ShaderLab

1Shader "Unlit/AAA" 2{ 3 Properties 4 { 5 _Scale ("Scale", Range(0.0, 4.0)) = 1.0 6 } 7 SubShader 8 { 9 Tags { "RenderType"="Opaque" } 10 LOD 100 11 12 Pass 13 { 14 CGPROGRAM 15 #pragma vertex vert 16 #pragma fragment frag 17 18 #include "UnityCG.cginc" 19 20 struct appdata 21 { 22 float4 vertex : POSITION; 23 float3 normal : NORMAL; 24 }; 25 26 struct v2f 27 { 28 float4 vertex : SV_POSITION; 29 float3 normal : NORMAL; 30 }; 31 32 float _Scale; 33 34 v2f vert (appdata v) 35 { 36 // カメラとオブジェクトのワールド座標を設定 37 float3 C = UNITY_MATRIX_I_V._m03_m13_m23; 38 float3 B = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz; 39 40 v2f o; 41 o.vertex = mul(unity_ObjectToWorld, v.vertex); 42 43 // オブジェクトの原点をワールド座標に合わせる 44 half4x4 VA = half4x4(1, 0, 0, -B.x, 45 0, 1, 0, -B.y, 46 0, 0, 1, -B.z, 47 0, 0, 0, 1); 48 o.vertex = mul(VA, o.vertex); 49 50 // あらかじめC-Bを求めて、さらに正規化しておくことで以降で頻出する 51 // 「sqrt~」を消すことができ、見やすくなるんじゃないかと思います 52 float3 dir = normalize(C - B); 53 float2 dirXY = dir.xy; 54 float lDirXY = length(dirXY); 55 dirXY /= lDirXY; 56 57 // 座標の回転1 58 // まずXZ平面がdirと平行になるようZ軸周りに回すことにしました 59 half4x4 VB = half4x4( dirXY.x, dirXY.y, 0, 0, 60 -dirXY.y, dirXY.x, 0, 0, 61 0, 0, 1, 0, 62 0, 0, 0, 1); 63 o.vertex = mul(VB, o.vertex); 64 65 // 座標の回転2 66 // 次にY軸周りに回すことで、X軸をdirの向きに合わせました 67 half4x4 VC = half4x4(lDirXY, 0, dir.z, 0, 68 0, 1, 0, 0, 69 -dir.z, 0, lDirXY, 0, 70 0, 0, 0, 1); 71 o.vertex = mul(VC, o.vertex); 72 73 // オブジェクトをカメラのほうへ引き延ばす 74 o.vertex.x = o.vertex.x * _Scale; 75 76 // 座標の回転2の反対 77 // VCは直交行列なので、mulの引数の左右を入れ替えると 78 // わざわざ逆行列を作らなくても逆変換できるかと思います 79 o.vertex = mul(o.vertex, VC); 80 81 // 引数の順序を入れ替えたくない場合は、変換行列を転置するのでもいいはずです 82 // half4x4 WC = transpose(VC); 83 // o.vertex = mul(WC, o.vertex); 84 85 // 座標の回転1の反対 86 // 同じく、VBも引数を入れ替えて逆変換することにしました 87 o.vertex = mul(o.vertex, VB); 88 89 // こちらも、引数の順序はそのままで行列の方を転置してもいいでしょう 90 // half4x4 WB = transpose(VB); 91 // o.vertex = mul(WB, o.vertex); 92 93 // オブジェクトの原点を元に戻す 94 half4x4 WA = half4x4(1, 0, 0, B.x, 95 0, 1, 0, B.y, 96 0, 0, 1, B.z, 97 0, 0, 0, 1); 98 o.vertex = mul(WA, o.vertex); 99 100 o.vertex = mul(UNITY_MATRIX_VP, o.vertex); // 頂点座標をVP変換//ワールド座標→clip space 101 o.normal = UnityObjectToWorldNormal(v.normal); 102 return o; 103 } 104 105 fixed4 frag (v2f i) : SV_Target 106 { 107 // 実行結果を示す上で、物体の形をわかりやすくするため 108 // Y方向に陰影を付けることにしました 109 return fixed4(normalize(i.normal).y * 0.5 + 0.5, 0, 0, 0); 110 } 111 ENDCG 112 } 113 } 114}

図

あるいは、カメラは常にオブジェクトの原点を注視している...といった条件があれば、ビュー座標上でZ軸に沿って伸縮させるのでも同様の効果が得られるんじゃないかと思います。

ShaderLab

1 v2f vert (appdata v) 2 { 3 // MV変換行列の平行移動成分は、オブジェクト座標(0, 0, 0)をビュー空間に変換した結果に 4 // 相当するため、つまりビュー空間でのオブジェクトの原点を指すことになります 5 float centerZ = UNITY_MATRIX_MV._m23; 6 7 v2f o; 8 o.vertex = mul(UNITY_MATRIX_MV, v.vertex); // 頂点をビュー座標に変換し... 9 o.vertex.z -= centerZ; // オブジェクトの原点Zを0に合わせて... 10 o.vertex.z *= _Scale; // Z方向に伸縮させ... 11 o.vertex.z += centerZ; // オブジェクトの原点Zを元に戻し... 12 o.vertex = mul(UNITY_MATRIX_P, o.vertex); // 最後にビュー座標をクリップ座標に変換 13 o.normal = UnityObjectToWorldNormal(v.normal); 14 return o; 15 }

ちなみに、変形の様子をわかりやすくするため陰影付けを行いましたが、陰影はスケールを変えても変化しておりません。
こういった非等方スケーリングが絡む場合に変形後の形に基づいて陰影付けするには、たとえばhttps://paroj.github.io/gltut/Illumination/Tut09%20Normal%20Transformation.htmlで解説されているように法線の向きを適切に変換してやる必要があるかと思います。

投稿2022/11/26 19:44

編集2022/11/26 22:02
Bongo

総合スコア10549

Y0241-N👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

O_HIROKO

2022/11/28 00:53

解答頂きありがとうございます! 思っていた通りの動きをしています。こんなに見やすいコードで書けるのかと感動しています。 自分では思い至れなかったコードの書き方を教えて頂き、とても勉強になりました。 分かりやすく教えて頂き、誠にありがとうございました!

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

ただいまの回答率
86.02%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Unity

Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。