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

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

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

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

Q&A

解決済

1回答

1421閲覧

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

O_HIROKO

総合スコア1

Unity

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

0グッド

0クリップ

投稿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

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

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

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

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

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

guest

回答1

0

ベストアンサー

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

総合スコア10807

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

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

O_HIROKO

2022/11/28 00:53

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問