座標変換
・sin,cosについて
角度θがあるとします。ここでいう角度とは反時計回りならば正の値
時計回りならば負の値とします。X軸に対してθの角度をなしている
直線を考えます。この直線と半径1の円が交差している点のX座標の値が
cosθ、Y座標の値がsinθとなります。
・一次変換
(x,y)の点を反時計回りにθだけ回転させる場合(回転後の点は(x',y'))
x' = x * cosθ - y * sinθ
y' = x * sinθ + y * cosθ
これを行列表現にすると
|x'| = | cosθ -sinθ ||x|
|y'| | sinθ cosθ ||y|
以下概念図
|x0' y0'| = x0 * ix + y0 * iy
|x0' y0'| = |x0 y0||cosθ sinθ|
|-sinθ cosθ|
・3次元の座標変換
x,y,z:変換前の座標;
x',y',z':変換後の座標;
θ:回転する角度;
lx,ly,lz:平行移動量;
X軸に関する回転
|1 0 0 0|
|0 cosθ sinθ 0|
[x' y' z' 1] = [x y z 1]|0 -sinθ cosθ 0|
|0 0 0 1|
Y軸に関する回転
|cosθ 0 -sinθ 0|
|0 1 0 0|
[x' y' z' 1] = [x y z 1]|sinθ 0 cosθ 0|
|0 0 0 1|
Z軸に関する回転
|cosθ sinθ 0 0|
|-sinθ cosθ 0 0|
[x' y' z' 1] = [x y z 1]|0 0 1 0|
|0 0 0 1|
平行移動
|1 0 0 0|
|0 1 0 0|
[x' y' z' 1] = [x y z 1]|0 0 1 0|
|lx ly lz 1|
拡大縮小(スケーリング)
|sx 0 0 0|
|0 sy 0 0|
[x' y' z' 1] = [x y z 1]|0 0 sz 0|
|0 0 0 1|
これで、物体の姿勢を表現するときは
[物体の姿勢の変換行列] = [Z軸の回転行列][X軸の回転行列][Y軸の回転行列][平行移動]
|XX XY XZ 0| XX,XY,XZ・・・X軸の単位ベクトルを変換した場合のベクトル
|YX YY YZ 0| YX,YY,YZ・・・Y軸の単位ベクトルを変換した場合のベクトル
= |ZX ZY ZZ 0| ZX,ZY,ZZ・・・Z軸の単位ベクトルを変換した場合のベクトル
|LX LY LZ 1| LX,LY,LZ・・・平行移動量ベクトル
とするのが一つの方法です。この4×4行列を同次変換と呼ぶようです。
この姿勢から相対的な移動を加える場合は
[相対的な移動] = [Z軸の回転行列][X軸の回転行列][Y軸の回転行列][平行移動]
[移動後の物体の姿勢の変換行列] = [相対的な移動][移動前の物体の姿勢の変換行列]
という具合に行列の左側にどんどん変換をかけて、
頂点を変換したいなと思ったとき
[x' y' z' 1] = [x y z 1][移動後の物体の姿勢の変換行列]
としてやると便利です。
・ビューポートへの変換
ビューポートの姿勢を同次変換で表している場合は
ワールド座標からビューポート座標に変換しなければなりません
その変換は以下のようになります(スケーリングが入ってない場合)
| 1 0 0 0 || XX YX ZX 0 |
| 0 1 0 0 || XY YY ZY 0 |
[ビューポート座標系への変換] = | 0 0 1 0 || XZ YZ ZZ 0 |
| -LX -LY -LZ 1 || 0 0 0 1 |
左上の3×3行列の転置行列をとり
平行移動成分の正負を反転させたものとなります。
・関節について
関節を表現するときの基本的な考え方は相対移動とほぼいっしょです。
[関節の変換行列] = [どれかの軸の回転行列(通常xかz?)][アームの長さ分の平行移動(通常z?)]
としておいて
[x' y' z' 1] = [x y z 1][関節の変換行列(n)][関節の変換行列(n-1)]・・・
[関節の変換行列(1)][関節の変換行列(0)]
このとき一番根元の方が[関節の変換行列(0)]で、手先の方が[関節の変換行列(n)]と
なります。また、x,y,zはn番めの関節に属する頂点です。
・任意軸回転
(rx, ry, rz)のベクトルを軸とした場合のθ度回転は以下の行列になります.
|
|
|
rx*rx*(1 - cos(θ))
|
rx*ry*(1 - cos(θ))
|
rx*rz*(1 - cos(θ)) - ry*sin(θ)
|
|
|
|
|
|
rx*ry*(1 - cos(θ)) - rz*sin(θ)
|
ry*ry*(1 - cos(θ)) + cos(θ)
|
ry*rz*(1 - cos(θ)) + rx*sin(θ)
|
|
|
|
|
|
rx*rz*(1 - cos(θ)) + ry*sin(θ)
|
ry*rz*(1 - cos(θ)) - rx*sin(θ)
|
rz*rz*(1 - cos(θ)) + cos(θ)
|
|
|
・透視投影
三次元の物体を二次元のスクリーンに投影することで3DCGは描かれます。
三次元のポリゴンの頂点を二次元のスクリーンに投影して
その投影された頂点を結んで2次元の多角形を描く方法をとるならば
変換をする対象は頂点ということになります。
実際に頂点の変換について考えてみます。
(X0, Y0, Z0)の点を視点からの距離dのところにあるスクリーンに投影します。
投影された後の点は(X0', Y0')とします。
これは三角形の相似から
X0' = X0 * d / Z0
Y0' = Y0 * d / Z0
となります。
これは,いわゆる遠くのものは小さく近くのものは大きくという具合の変換となっています.
・反射光の計算
物体に光が当てられたとき、その物体自身の生じるくらい部分を陰(shade)と呼びます。
(光が物体にさえぎられてできる部分を影(shadow)と呼びます)
ここではその陰の計算について話を進めていきます。
物体が見えるということは、光が目に入ってくるということです。
しかし、たいがいの物体は自分では光らずに周りからくる光を反射していて、
その反射した光が目に入るということになります。
まずはその光の反射の一つの拡散反射について話を進めていこうと思います。
光があたったときに、すべての方向に同じような強さで光を射出する反射を
拡散反射と呼びます。一般的には余り表面が滑らかでない(ざらざらでこぼこ)した物体は
拡散反射の割合が多いです。
拡散反射が多い物体を考えたときの陰のつけるには、物体の表面にどれだけの光があたっているか
ということから計算を行います。面にあたる光の強さが一定の場合
単位面積あたりにあたる光の強さはその面に対する入射角によって影響を受けます。
そのことから以下のLanbertの余弦則が導かれます。
Lanbertの余弦則
表面の輝度 … D
入射光の強さ … I
物体の拡散反射係数 … k
入射光と面の法線ベクトルのなす角 … θ
光源への方向のベクトル(単位ベクトル) … L
面の法線ベクトル(単位ベクトル) … N
D = k * I * cosθ = k * I * (L・N) (L・N >= 0)
D = 0 (L・N < 0)
・鏡面反射
まずは光の反射を求めます。
2 * (-L・N) * N + Lが反射した後の光線のベクトルとなります
そのあと、反射した後の光線ベクトルと面から視線への方向ベクトル(単位ベクトル)
との内積をとってやります。このとき内積の値が大きければ大きいほど
2つのベクトルは同じ方向を向いているわけで目に入ってくる光の量も大きいということになります。
ただ、鏡面反射は反射した後の光源ベクトルの方向に強く光を飛ばすため
明るい部分の範囲は拡散反射のときよりも狭くなります。
よって、求めた内積の値に対して30乗とか100乗とかの計算をします。
鏡面反射と拡散反射両方
鏡面反射のみ
拡散反射のみ
・平行光源
平行光源とはどこにいても同じ方向から光が来るような光源です.
地球上では太陽光はほぼ平行光源とみなすことができます.
・シェーディングの種類
フラットシェーディング
面の法線によって面全体の明るさを決定.
Direct3Dは面を構成する頂点のうちの
一つの法線を使用することが多いらしい.
グローシェーディング
頂点ごとに明るさを計算しその明るさを補間する
一番明るい部分は頂点の上に来る.
フォンシェーディング
頂点に割り当てられたほう線を描画する面の位置に応じて補間し
そこからピクセルの明るさを計算する.
グローシェーディングでは取り落とされる可能性がある
一番明るい部分も明るくなる.
・テクスチャマップの張り方の種類
1.平行投影マッピング、
計算式の例
1)
u = x
v = y
2)
|u v 0 0| = |x y z 1|R・|1 1 0 0|
Rは同次変換
円柱マッピング
計算式の例
1)
PIは平方根とする
if x > 0 then
u = arctan(y / x) / (2 * PI)
else
u = arctan(y / x) / (2 * PI) + 0.5
v = z
2)
|xa ya za 1| = |x y z 1|R
Rは同次変換
if xa > 0 then
u = arctan(ya / xa) / (2 * PI)
else
u = arctan(ya / xa) / (2 * PI) + 0.5
v = za
Rは同次変換
球状マッピング
if x > 0 then
u = arctan(y / x) / (2 * PI)
else
u = arctan(y / x) / (2 * PI) + 0.5
tmp = sqrt(vec[i].x * vec[i].x + vec[i].y * vec[i].y);
if tmp > 0 then
v = arctan(z / tmp) / PI + 0.5;
|