自學(xué)計算機(jī)圖形學(xué)6: 用北太天元輔助理解從世界坐標(biāo)系到相機(jī)坐標(biāo)系下的坐標(biāo)變換

%北太天元 幫助理解 計算機(jī)圖形學(xué)的坐標(biāo)變換
%立方體
%
points = [
???% 下底面的四個點
???-1 -1 -1??; %第一個點
???-1 1 -1??; %第二個點
???1 1 -1??;
???1 -1 -1??;
??????%上底面的四個點
???-1 -1 1??;
???-1 1 1??;
???1 1 1??;
???1 -1 1??;
??????%原點
??????0 0?0
???];
points = points' ; %每個點的坐標(biāo)改成列向量
%開始的up方向 (0, 0, 1);
% pitch = 45, yaw =?0?方向是 (1,1,0)
up = [0; 1; 0];
pitch = 30; yaw = -45;
ca_axes = camera_axes(up, pitch_yaw(pitch,yaw));
ca_points = ca_axes' *points;
max_p = max(ca_points,[], 2); %獲得camera view 坐標(biāo)系下的所有點坐標(biāo)的最大值
min_p = min(ca_points,[], 2);%獲得camera view 坐標(biāo)系下的所有點坐標(biāo)的最小值
%現(xiàn)在camera的方向是direction = -ca_axes(3,:)的轉(zhuǎn)置
% 目前假設(shè)的camera的位置在(0,0,0)
% 需要把camera 沿著camera的方向反向移動一段距離 distance,
% 使得整個立方體都在camera的前方,
% 現(xiàn)在 ca_points 在 ca_axes(3,:) 的坐標(biāo)的范圍是
min_d = min(ca_points(3,:));
max_d = max(ca_points(3,:));
%相機(jī)需要移動的距離是
distance = max_d + 0.1*(max_d - min_d);
% camera 的位置(在移動前的camera 坐標(biāo)系下)
camera_p = [ 0; 0; distance ];
%相機(jī)的位置在世界坐標(biāo)系下是
camera_position =?ca_axes * camera_p ;
% 經(jīng)過了先旋轉(zhuǎn),再平移
% 最終得到的 look_at( up, pitch_yaw(pitch,yaw), position) 下的
% 點的坐標(biāo)(在平移后camera坐標(biāo)系下)是
final_points = ca_points - camera_p;
%還有一種方式來計算 camera_view 下的坐標(biāo), 使用齊次坐標(biāo)
qi_points = [points ;?ones(size(points(1,:))) ]; %最后一行加上了1
%齊次坐標(biāo)的從world coordinates變換到
% 相機(jī)坐標(biāo)系下的 transform 矩陣
trans_mat = [??ca_axes' , -camera_p; [ 0, 0, 0, 1] ];
?平移_mat = [ eye(3), -camera_position; [0 0 0 1] ]
?旋轉(zhuǎn)_mat = [ ca_axes', zeros(3,1); [0 0 0 1] ]
% 你可以驗證 trans_mat = 平移_mat * 旋轉(zhuǎn)_mat
disp("trans_mat - 平移_mat * 旋轉(zhuǎn)_mat")
?trans_mat - 平移_mat * 旋轉(zhuǎn)_mat?%
disp("trans_mat - 旋轉(zhuǎn)_mat * 平移")
?trans_mat -?旋轉(zhuǎn)_mat * 平移_mat
% 這和 https://learnopengl.com/Getting-started/Camera 的 LookAt
% 矩陣是相同的
qi_final_points = trans_mat * qi_points;
%比較兩種方式得到的camera_view 下的坐標(biāo)
final_points - qi_final_points(1:3, :)
%根據(jù) pitch 和 yaw 確定 camer 的方向
% 開始的camera的方向是(1,0,0), 首先繞z軸正向逆時針方向旋轉(zhuǎn)pitch角度
% 然后再繞y軸正向順時針方向旋轉(zhuǎn) ywa 角度
% 最后得到的camera的方向
% 例如 pitch_yaw(0,-90) 得到的方向是 (0,0,-1);
% 不過,最后的向量都寫成了列向量
function direction = pitch_yaw( pitch, yaw)
???direction = [
?????????cos( deg2rad(pitch) )*cos( deg2rad(yaw) );
?????????sin( deg2rad(pitch) );
?????????cos( deg2rad(pitch) )*sin( deg2rad(yaw) );
??????];
end
%對于開始給定的up方向和 direction方向
% 計算出right 方向 : camera_right = direction x up 再normalize
% 然后根據(jù)camera_right方向和direction方向計算出camera_up:
% camera_up = right x direction?再normalize
% 最后得到的camera坐標(biāo)系的三個軸是
%?(right,
% https://learnopengl.com/Getting-started/Camera 這篇文章中的
% direction 不是camera 看向的方向,而是相反的。
% 我們這里和上面的帖子不同,direction 還保持camera 看向的方向
% 也就是 (right, camera_up, -direction) 是一個右手系
% https://learnopengl.com/Getting-started/Camera 這篇文章中的
% 的lookat 的矩陣也是怪怪的,可能也會導(dǎo)致模仿者出錯。
function ca_axes = camera_axes(up, direction)
???up = up(:);?% 確保得到的up是一個列向量
???direction = direction(:);?%確保得到的direction 是一個列向量
???%確保輸入的direction是一個單位向量
???norm_d = norm(direction, 2);
???if(norm_d < eps)
??????error('輸入的direction向量接近于0');
???end
???direction = direction /norm_d;
???camera_right =?cross( direction, up ); %?drection 叉乘 up
???% 這個地方要檢查一下 是不是direction 和 up 貢獻(xiàn)
???norm_r = norm(camera_right,2) ; %計算camera_right的2范數(shù)
???if(norm_r < eps)
??????error("up 和 directin 共線")
???end
???camera_right = camera_right / norm_r; %把camera_right單位化
???camera_up = cross(camera_right, direction); %?right 叉乘 direction
???camera_up = camera_up / norm(camera_up, 2);
???ca_axes = [ camera_right, camera_up, -direction ];?
end