[Python] Numpy で3D座標変換 その3

[Python] Numpy で3D座標変換 その3

Depth から World Position を復元

3D座標変換その2 で (320. , 33.06740882, 0.9986713, 1.0)という値から、World Position を復元する。
このベクトルの値は SV_POSITION の値として、頂点シェーダーから出力され、ピクセルシェーダーに入ってきた値を float4 の値を想定している。

ここに乗せている Python コードは「3D座標変換その2」に書いているコードの後ろに足せば、動くようにしている。

Depth から World Position を求めるのは以下の通り

  • ピクセルシェーダーに来た座標を NDC 空間に戻す
  • View と Proj の逆行列を求める
  • NDC 空間の座標に、ViewProj の逆行列を掛ける
    • ViewProj 行列の逆行列を求める
    • もしくは View と Proj の逆行列をそれぞれ求めて、InvProj から順番にかけていく。
  • 求めたベクトルの X,Y,Z を W で割る

NDC 空間に戻す

uv = np.array([v1[0,0]/screenWidth, v1[0,1]/screenHeight])
uv = (uv * 2.0) - 1.0
ndc = np.array([uv[0], uv[1], v1[0,2], 1.0])
ndc[1] = ndc[1] * -1.0

SV_Position の XY にはピクセルのスクリーン位置が返ってくる。その値を描画したスクリーンサイズで割ることで、[0,1] 範囲の UV を求めている。そして、NDC の XY 座標は [-1,1] の範囲を取るため、2倍して1引くことで NDC での座標に変換している。

ndc 変数に値をセットしているが、XY は先ほど UV の値から求めた座標を入れて、Z は SV_Position の値をそのまま代入している。(これは、DirectX の NDC の Z が [0,1] のためそのまま使用。[-1,1] の環境では、別途変換が必要)

View Proj の逆行列を掛ける

viewProjInv = np.linalg.inv(view*proj)
wp = np.matmul(ndc, viewProjInv)

#projInv = np.linalg.inv(proj)
#viewInv = np.linalg.inv(view)
#wp = np.matmul(ndc, projInv)
#wp = np.matmul(wp, viewInv)

wp = wp/wp[0,3]

ViewProj の逆行列をかけるか、もしくは、View, Proj の逆行列をそれぞれ求めて、projInv から順番にかけていく。最後に W で割れば、World Position が復元できた。

[[0.  2.5 2.  1. ]]
[Python] Numpy Matrix

[Python] Numpy Matrix

Import

import numpy as np

行列の定義

viewport = np.mat(
    [
        [1, 0, 0, 0]
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
    ]
)

行列の掛け算

v1 = np.matmul(v1, viewport)

掛ける方向に注意。np.array と np.matrix や np.matrix と np.matrix でも可能。

逆行列を求める

viewProjInv = np.linalg.inv(proj)

要素にアクセス

mat = np.matrix([[0,1]])
print(mat[0,1]) #1

[0,1] という形でカンマで区切るので注意。
他の言語の感覚で mat[0][1] という形でアクセスしようとしてはまった。

[Python] Numpy で3D座標変換 その2

[Python] Numpy で3D座標変換 その2

import numpy as np

# ローカルの頂点座標
v1 = np.array([-1.0,1.0,-1.0,1.0])

# Model のワールド座標
wPost = np.array([1.0,1.5,3.0])

# カメラポジション
eye = np.array([0.0, 0.0,-5.0])
at = np.array([0.0,0.0,0.0])
up = np.array([0.0,1.0,0.0])

# スクリーンサイズ
width = 640
height = 480

# Near/Far Z
zn = 0.01
zf = 100.0

# 移動行列
trans = np.mat(
    [
        [1.0,0.0,0.0,0.0],
        [0.0,1.0,0.0,0.0],
        [0.0,0.0,1.0,0.0],
        [wPost[0],wPost[1],wPost[2],1.0],
    ]
)

v1 = np.matmul(v1,trans)

# zaxis
a = at - eye
b = np.linalg.norm(at-eye)
zaxis = a / b

# xaxis
d = np.cross(up, zaxis)
e = np.linalg.norm(d)
xaxis = d / e

# yaxis
yaxis = np.cross(zaxis, xaxis)

view = np.mat(
        [
            [xaxis[0], yaxis[0], zaxis[0], 0.0],
            [xaxis[1], yaxis[1], zaxis[1], 0.0],
            [xaxis[2], yaxis[2], zaxis[2], 0.0],
            [-np.dot(xaxis, eye), -np.dot(yaxis, eye), -np.dot(zaxis,eye),1.0]
        ]
    )

povy = 0.785398163
yscale = 1.0 / np.tan(povy/2.0)

aspect = width / height
xscale = yscale / aspect

proj = np.mat(
    [
        [xscale, 0, 0, 0],
        [0, yscale, 0, 0],
        [0, 0,  zf / (zf-zn), 1 ],
        [0, 0, -zn * zf / (zf-zn), 0]
    ]
)

v1 = np.matmul(v1,view)
v1 = np.matmul(v1,proj)

w = width/2
h = height/2
viewport = np.mat(
    [
        [w, 0, 0, 0],
        [0, -h, 0, 0],
        [0, 0, 1, 0],
        [w, h, 0, 1]
    ]
)

v1 = v1 / v1[0,3]
v1 = np.matmul(v1, viewport)
v1

Numpy で実行した時の出力結果は下記になる。

matrix([[320.        ,  33.06740882,   0.9986713 ,   1.        ]])

X, Y が座標変換後のスクリーン座標。(320, 33) 位置になる。
Z が Depth Buffer に書かれる値。Z 0.99867

World 座標の Transform で (1.0, 1.5, 3.0) の位置に変換している。

[Python] Numpy で3D座標変換 その1

[Python] Numpy で3D座標変換 その1

World 行列

Scale

Transform

trans = np.mat(
    [
        [1.0,0.0,0.0,0.0],
        [0.0,1.0,0.0,0.0],
        [0.0,0.0,1.0,0.0],
        [1.0,2.0,3.0,1.0],
    ]
)

v1 = np.array([-1.0,1.0,-1.0,1.0])
v1 = np.matmul(v1,trans)
v1

Rotation

View 行列

eye = np.array([0.0, 0.0,-5.0])
at = np.array([0.0,0.0,0.0])
up = np.array([0.0,1.0,0.0])

# zaxis
a = at - eye
b = np.linalg.norm(at-eye)
zaxis = a / b

# xaxis
d = np.cross(up, zaxis)
e = np.linalg.norm(d)
xaxis = d / e

# yaxis
yaxis = np.cross(zaxis, xaxis)

view = np.mat(
        [
            [xaxis[0], yaxis[0], zaxis[0], 0.0],
            [xaxis[1], yaxis[1], zaxis[1], 0.0],
            [xaxis[2], yaxis[2], zaxis[2], 0.0],
            [-np.dot(xaxis, eye), -np.dot(yaxis, eye), -np.dot(zaxis,eye),1.0]
        ]
    )

view

Projection 行列

povy = 0.785398163
yscale = 1.0 / np.tan(povy/2.0)

aspect = 1.333333333
xscale = yscale / aspect

zn = 0.01
zf = 100.0

proj = np.mat(
    [
        [xscale, 0, 0, 0],
        [0, yscale, 0, 0],
        [0, 0,  zf / (zf-zn), 1 ],
        [0, 0, -zn * zf / (zf-zn), 0]
    ]
)

proj

頂点を計算

座標 (-1.0, 1.0, -1.0) の頂点を View, Proj 変換を行う。

v1 = np.array([-1.0,1.0,-1.0,1.0])
v1 = np.matmul(v1,view)
v1 = np.matmul(v1,proj)

v1 頂点を変換して、頂点シェーダーからの出力として、SV_POSITION に渡される値は下記になる。

[-1.81066017,  2.41421356,  3.99039904,  4.        ]

なお、この時点での座標は w が 4.0 の同次座標と呼ばれるものになっている。w 4.0 を使って xyz を割る(正規化する)ことにより、座標は NDC(Normalized Device Coordinate)と呼ばれる空間に移る。

この空間は xy が [-1, 1] で、z が [0, 1] の範囲の座標になる。(OpenGL の場合は z は [-1, 1] まで)w で割った後の座標は下記になる。

[-0.45266504,  0.60355339,  0.99759976]

xy が [-1,1] で、z が [0,1] の範囲に収まっている。この値を、次に説明する Viewport のスケール行列を掛けることにより、スクリーン上の座標へとスケールする。

ラスタライザで Pixel 位置を計算

頂点は v1 / v1[0,3] で、w で割っている。その後に Viewport のスケール行列を掛ける。

w = 640.0/2
h = 480.0/2
viewport = np.mat(
    [
        [w, 0, 0, 0],
        [0, -h, 0, 0],
        [0, 0, 1, 0],
        [w, h, 0, 1]
    ]
)

v1 = v1 / v1[0,3]
v1 = np.matmul(v1, viewport)
v1

v1 の結果として、下記になる。

[175.14718614,  95.14718618,   0.99759976,   1.        ]

この結果により座標変換を行った頂点は Viewport 640×480 の 175, 96 位置にラスタライザされる。 Depth Buffer に書かれるのは、この時の3つ目の要素 0.9976 あたりがこの時の深度値。

参考

https://shikihuiku.github.io/post/projection_matrix/

[JIRA]プロジェクトを作成

[JIRA]プロジェクトを作成

プロジェクトテンプレートは「スクラム」
プロジェクトのタイプは「チーム管理対象」が使いやすい。
スクラムの場合は、バックログの項目が存在する。

テンプレート「カンバン」
プロジェクトのタイプは「チーム管理対象」でも、シンプルで良い。
ボードの管理のみ。

[Windows] ジャンクション設定

[Windows] ジャンクション設定

実データが D ドライブのフォルダにあり、そこから C ドライブへジャンクションを貼る場合は下記コマンドを使用する。

mklink /j C:\temp D:\temp

バージョン管理ソフトに Perforce などを使っている時はジャンクションの方が不具合が無かった。

[UE4] Visual Studio からプロジェクトを起動

[UE4] Visual Studio からプロジェクトを起動

エンジンの挙動を確認するため、Visual Studio からデバッグ実行したい。

Github からコードをダウンロード

Github アカウントと、Unreal Engine アカウントが必要。

下記2つのページを参考にしてアカウントの連携を行えば、EpicGames のリポジトリにアクセス出来る。

https://docs.unrealengine.com/4.26/ja/ProgrammingAndScripting/ProgrammingWithCPP/DownloadingSourceCode/

https://www.unrealengine.com/ja/ue-on-github

Editor をビルド

Github から落としてきた UE4.sln を開いて、Development Editor/Win64/UE4 構成でビルドする。成功すれば下記パス以下に UE4Editor の実行ファイルが生成される。

D:\Github\UnrealEngine\Engine\Binaries\Win64\UE4Editor.exe

プロジェクトの作成

UE4Editor を開いて、新規 C++ プロジェクト(MyProject)を作成する。Visual Studio から単体のゲームとして起動するには、一度 Cook して pak ファイルを作っておく必要がある?ので Package Project から Windwos を選択してデータを吐き出しておく。

MyProject.sln ビルド

MyProject を開き、Debug/Win64/MyProject 構成で F5 でビルドする。成功すれば Engine コードがデバッグ可能な状態でゲームが起動できる。

その他

UnrealVS を使うと Editor 起動時にプロジェクトファイルを指定できる?

https://docs.unrealengine.com/5.2/en-US/using-the-unrealvs-extension-for-unreal-engine-cplusplus-projects/

[Window] キーボード配列を変更する

[Window] キーボード配列を変更する

使用しているノート PC のスペースキーが壊れてしまった。

Change key というソフトウェアを使用することで、スペースキーを隣の「無変換」「変換」キーに割り当てて解決出来た。このソフトウェアはレジスタを書き換えているみたいなので、一度設定するだけで良かった。

後は指がなれるまで我慢することかな。

Reference

Change Key
https://forest.watch.impress.co.jp/library/software/changekey/

[Chromebook] C/C++ 開発環境

[Chromebook] C/C++ 開発環境

Chromebook で C/C++ 開発環境を構築する。Linux 環境を構築後に下記のコマンドで進めていく。

sudo apt update
sudo apt install build-essential
sudo apt install gdb

Visual Studio Code の拡張

C/C++ for Visual Studio Code をインストールする。

新規ファイルで、c/cpp ファイルを作成して、メニューの Run から実行することが出来た。

pthread を使用する

gcc -pthread main.c -o main
./main

-pthread を指定してリンクする必要があった。

Reference

Using C++ on Linux in VS Code
https://code.visualstudio.com/docs/cpp/config-linux

[Python] Numpy で行列演算

[Python] Numpy で行列演算

行列の掛け算

numpy.ndarray を 2つ定義して np.matmul() で計算する。

import numpy as np
data1 = np.array(
    [
        [0, 1, 2], 
        [3, 4, 5],
        [6, 7, 8],
    ]
)

data2 = np.array(
    [
        [0, 1, 2], 
        [3, 4, 5],
        [6, 7, 8],
    ]
)

result = np.matmul(data1, data2)
print(result)

実行結果

[[ 15  18  21]
 [ 42  54  66]
 [ 69  90 111]]

逆行列を求める

逆行列は np.linalg.inv() を使用する

import numpy as np

data1 = np.array([
    [0,1,3],
    [3,4,5],
    [6,7,8]]
)
                
data1_inv = np.linalg.inv(data1)

print(data1_inv)
print(np.matmul(data1,data1_inv))

実行結果

[[ 1.         -4.33333333  2.33333333]
 [-2.          6.         -3.        ]
 [ 1.         -2.          1.        ]]
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]