回答編集履歴

2

視錐台切断の実演映像を追加

2023/11/19 12:06

投稿

Bongo
Bongo

スコア10811

test CHANGED
@@ -72,3 +72,97 @@
72
72
  ![式9](https://ddjkaamml8q8x.cloudfront.net/questions/2023-11-16/39d5a5ad-6059-49c3-b4b5-e54cd9b16002.png)
73
73
 
74
74
  となります。これで①と②が同じ形になりましたので、Ilettさんのやり方でもビュー空間上のポータル平面を求めることができそうです。
75
+
76
+ ## 視錐台を斜めに切断した時のカメラの映像
77
+
78
+ 斜めのプロジェクション行列を使うとカメラの映像がどう見えるか試してみましたので追記いたします。
79
+ カメラには下記のようなスクリプトをアタッチしました。
80
+
81
+ ```C#
82
+ using UnityEngine;
83
+
84
+ [RequireComponent(typeof(Camera))]
85
+ public class NearPlaneExperiment : MonoBehaviour
86
+ {
87
+ // 視錐台の切断面となるオブジェクト
88
+ [SerializeField] private Transform planeTransform;
89
+
90
+ private readonly Vector3[] clipCorners = new Vector3[8];
91
+ private new Camera camera;
92
+
93
+ private void Start()
94
+ {
95
+ this.camera = this.GetComponent<Camera>();
96
+ }
97
+
98
+ private void LateUpdate()
99
+ {
100
+ if (this.planeTransform == null)
101
+ {
102
+ return;
103
+ }
104
+
105
+ // ワールド空間でのニアクリップ面ベクトルを作成する
106
+ var plane = new Plane(this.planeTransform.forward, this.planeTransform.position);
107
+ var planeWorldVector = (Vector4)plane.normal;
108
+ planeWorldVector.w = plane.distance;
109
+
110
+ // ビュー変換行列を取得する
111
+ var worldToView = this.camera.worldToCameraMatrix;
112
+
113
+ // ニアクリップ面ベクトルをビュー座標に直す
114
+ var planeViewVector = worldToView.inverse.transpose * planeWorldVector;
115
+
116
+ // プロジェクション行列を正しく加工するには、切断面の法線が画面奥を
117
+ // 向いている必要があるため、法線のZ成分の符号に応じて面を反転する
118
+ planeViewVector *= Mathf.Sign(-planeViewVector.z);
119
+
120
+ // 斜めに加工したプロジェクション行列を作る
121
+ var obliqueMatrix = this.camera.CalculateObliqueMatrix(planeViewVector);
122
+
123
+ // カメラにプロジェクション行列をセットする
124
+ this.camera.projectionMatrix = obliqueMatrix;
125
+
126
+ // クリップ空間を視覚的に確認するため、角のワールド座標を求める
127
+ var clipToView = obliqueMatrix.inverse;
128
+ var viewToWorld = worldToView.inverse;
129
+ for (var z = 0; z < 2; z++)
130
+ {
131
+ for (var y = 0; y < 2; y++)
132
+ {
133
+ for (var x = 0; x < 2; x++)
134
+ {
135
+ var p = clipToView.MultiplyPoint((new Vector3(x, y, z) * 2.0f) - Vector3.one);
136
+ p *= Mathf.Sign(-p.z);
137
+ this.clipCorners[(z << 2) | (y << 1) | x] = viewToWorld.MultiplyPoint3x4(p);
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ private void OnDrawGizmos()
144
+ {
145
+ // シーンビュー上に枠線を描く
146
+ Gizmos.color = Color.red;
147
+ Gizmos.DrawLine(this.clipCorners[0], this.clipCorners[1]);
148
+ Gizmos.DrawLine(this.clipCorners[1], this.clipCorners[3]);
149
+ Gizmos.DrawLine(this.clipCorners[3], this.clipCorners[2]);
150
+ Gizmos.DrawLine(this.clipCorners[2], this.clipCorners[0]);
151
+ Gizmos.color = new Color(1.0f, 0.5f, 0.0f);
152
+ Gizmos.DrawLine(this.clipCorners[0], this.clipCorners[4]);
153
+ Gizmos.DrawLine(this.clipCorners[1], this.clipCorners[5]);
154
+ Gizmos.DrawLine(this.clipCorners[2], this.clipCorners[6]);
155
+ Gizmos.DrawLine(this.clipCorners[3], this.clipCorners[7]);
156
+ Gizmos.color = Color.yellow;
157
+ Gizmos.DrawLine(this.clipCorners[4], this.clipCorners[5]);
158
+ Gizmos.DrawLine(this.clipCorners[5], this.clipCorners[7]);
159
+ Gizmos.DrawLine(this.clipCorners[7], this.clipCorners[6]);
160
+ Gizmos.DrawLine(this.clipCorners[6], this.clipCorners[4]);
161
+ }
162
+ }
163
+ ```
164
+
165
+ シーン上にはQuadを置き、別のスクリプトを使ってゆらゆらと回転させました。
166
+ このQuadの位置と法線を使ってカメラの視錐台を切断したところ、下図のような映像になりました。シーンビュー上の赤~黄色で描いた枠線が変形された視錐台で、この範囲内がカメラに映ることになります。
167
+
168
+ ![図](https://ddjkaamml8q8x.cloudfront.net/questions/2023-11-19/bd82620d-02fc-4536-9f8d-2f189e4a7e5a.gif)

1

ビュー空間ポータル平面を求めるパートにfuqunagaさんのコードへのリンクを追加

2023/11/16 20:14

投稿

Bongo
Bongo

スコア10811

test CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  という形で表現できるかと思います。なお、 **_b_** が平行移動成分で **_A_** が回転・拡縮・スキュー成分ですが、ビュー変換は視点を変えているだけですので拡縮やスキューは起こらないはずです。[リファレンス](https://docs.unity3d.com/ja/current/ScriptReference/Camera-worldToCameraMatrix.html)によるとZ軸が反転するようですが、反転なら基底の長さは変わりません。つまり **_A_** の3つの基底は長さが1で互いに直交しているはずですから、 **_A_** は[直交行列](https://ja.wikipedia.org/wiki/%E7%9B%B4%E4%BA%A4%E8%A1%8C%E5%88%97)となります。
22
22
 
23
- カメラから見たポータル面を求めるとなると、素直にやるならポータルのワールド座標とワールド法線をそれぞれビュー座標に直して`Plane`を作り、その法線と距離を取り出して組み合わせ`Vector4`にまとめることになるかと思います。fuqunagaさんはこの方式(`Plane`は作らずダイレクトに`Vector4`を構築しておりますが)を採用なさったご様子で、まずはその過程を式で表してみることにします。
23
+ カメラから見たポータル面を求めるとなると、素直にやるならポータルのワールド座標とワールド法線をそれぞれビュー座標に直して`Plane`を作り、その法線と距離を取り出して組み合わせ`Vector4`にまとめることになるかと思います。fuqunagaさんはこの方式([VirtualCamera.CalcPlane](https://github.com/IndieVisualLab/UnityGraphicsProgramming3/blob/137165956adcda6512d32187a79da51ec73013ea/Assets/PortalGateSystem/Camera/VirtualCamera.cs#L123)によると`Plane`は作らずダイレクトに`Vector4`を構築しておりますが)を採用なさったご様子で、まずはその過程を式で表してみることにします。
24
24
 
25
25
  ポータルのワールド座標を **_p_**_w_、ポータル面のワールド法線を **_n_**_w_ とすると、ポータルのビュー座標 **_p_**_v_ とビュー法線 **_n_**_v_ は
26
26