3d显示基础

三维坐标系转换

常用的坐标转换模型有哪些? 常用的坐标转换模型如下: (1)三维七参数转换模型:用于不同地球椭球基准下的大地坐标系统间点位坐标转换,涉及三个平移参数,三个旋转参数和一个尺度因子,同时需顾及两种大地坐标系所对应的两个地球椭球长半轴和扁率差。 (2)二维七参数转换模型:用于不同地球椭球基准下的椭球面上的点位坐标转换,涉及三个平移参数,三个旋转参数和一个尺度因子。 (3)三维四参数转换模型:用于局部坐标系间的坐标转换,涉及三个平移参数和一个旋转参数。 (4)二维四参数转换模型:用于范围较小的不同高斯投影平面坐标转换,涉及两个平移参数,一个旋转参数和一个尺度因子。对于三维坐标,需将坐标通过高斯投影变换得到平面坐标,再计算转换参数。 (5)多项式拟合模型:用于全国/全省或相对独立的平面坐标系统转换。

三维坐标系之间的转换关系:R(旋转矩阵) 、T(平移矩阵)
  在大地测量、工程测量、摄影测量等领域中,坐标系之间的转换是必不可少的。空间坐标转换的实质是用公共点的2套坐标和非公共点的1套坐标推估非公共点的另1套坐标。

  坐标转换过程通常分2步,先由公共点坐标解算转换参数,再由转换参数转换非公共点。转换参数通常分为旋转、平移和尺度参数,其中旋转参数的确定是坐标转换的核心。

  传统的三维坐标转换模型是用3个旋转角作为旋转参数,建立的模型是非线性的,常需要用泰勒级数展开的方法将模型线性化,计算比较繁杂。

  在小角度旋转情况下,可对旋转矩阵作近似处理,得到线性模型,如常用的布尔莎模型。

  针对大旋角的坐标转换问题,多采用罗德里格矩阵表示旋转矩阵的坐标转换方法,仅有3个旋转参数,计算过程无需线性化,且能适用大旋角转换。
https://zhuanlan.zhihu.com/p/458827599

通过3个以上的已知点求坐标转换系数

function [R,lambda,dxyz]=  getCoordTransformPara_3dTo3d(xyzs,XYZs)
%该函数用来进行坐标转换
%输入xyzs:xyz坐标系的点,每一行代表一个点,3列,分别表示x,y,z
%输入XYZs:同上
%输出R:旋转矩阵
%输出lambda:缩放因子,暂定为1

%这两行是测试代码
%xyzs = [0,2,0;2,0,0;1,1,2];
%XYZs = [-1,1,-1;1,-1,-1;0,0,1];


xs = xyzs(:,1)';ys = xyzs(:,2)';zs = xyzs(:,3)';
Xs = XYZs(:,1)';Ys = XYZs(:,2)';Zs = XYZs(:,3)';
xn1s = xs(2:end)-xs(1);  yn1s = ys(2:end)-ys(1);zn1s = zs(2:end)-zs(1);
Xn1s = Xs(2:end)-Xs(1);Yn1s = Ys(2:end)-Ys(1);Zn1s = Zs(2:end)-Zs(1);

lambda = 1;
n_point = length(xs);
A = [];
for i  = 1:n_point-1
    A = [A; 0 ,-lambda* zn1s(i)-Zn1s(i) , -lambda*yn1s(i)-Yn1s(i);
        -lambda* zn1s(i)-Zn1s(i), 0 ,lambda* xn1s(i)+Xn1s(i);
        lambda*yn1s(i)+Yn1s(i), lambda* xn1s(i)+Xn1s(i) ,0 ]; 
end
L = [];
for i  = 1:n_point-1
    L = [L ; Xn1s(i) - lambda * xn1s(i); Yn1s(i) - lambda * yn1s(i); Zn1s(i) - lambda * zn1s(i) ];
end
X = inv(A'*A)*A'*L;
a = X(1); b=X(2);c = X(3);  %[a,b,c] = X';
R = 1/(1+a^2+b^2+c^2)*[1+a^2-b^2-c^2 , -2*c-2*a*b , -2*b+2*a*c ; 2*c-2*a*b , 1-a^2+b^2-c^2 , -2*a-2*b*c ; 2*b+2*a*c , 2*a-2*b*c , 1-a^2-b^2+c^2 ];
dxyz = [Xs(1);Ys(1);Zs(1)] - lambda*R*[xs(1);ys(1);zs(1)];
 

那么相应的坐标转换的函数 就写为

function [XYZs]=  coordTransform_3dTo3d(xyzs,R,lambda,dxyz)
[m,n] = size(xyzs);
XYZs = [];
for i = 1: m
 u = lambda * R * xyzs(i,:)'+dxyz;
 XYZs = [XYZs;u'];
end

写一个代码对上述过程进行测试

xyzs_base = [-2,-2,0;-(2-sqrt(2)/2),-(2+sqrt(2)/2),0;-2,-2,2];%世界坐标系
XYZs_base = [0,0,0;0,1,0;1,0,0];%摄像头坐标系
[R,lambda,dxyz]=  getCoordTransformPara_3dTo3d(xyzs_base,XYZs_base);


%坐标转换
xyzs = [0,0,0;1,0,0;1,1,0;0,1,0;0,0,1;1,0,1;1,1,1;0,1,1];
faces = [1,2,3,4;5,8,7,6;8,4,3,7;5,6,2,1;5,1,4,8;6,7,3,2];
[XYZs]=  coordTransform_3dTo3d(xyzs,R,lambda,dxyz);

for i_face = 1:length(faces(:,1))
   h = fill(XYZs(faces(i_face,:),1),XYZs(faces(i_face,:),2),[0.8,0.8,0.8]);
   set(h,'facealpha',0.3);
   hold on;
end

通常我们在软件中进行3维预览时,往往通过拖动鼠标来移动视角(实际就是变化摄像头坐标),这时就需要把三个标准点也进行绕轴旋转,基本原理在https://blog.csdn.net/maple_2014/article/details/104443928

x = nx * t + x0;
y = ny * t + y0;
z = nz * t + z0;
t0 = solve(nx * (x - px) + ny * (y - py) + nz * (z - pz) == 0, t)
%}

t0 = nx * (px - x0) + ny * (py - y0) + nz * (pz - z0);
xc = x0 + nx * t0;
yc = y0 + ny * t0;
zc = z0 + nz * t0;
r = sqrt((px - xc)^2 + (py - yc)^2 + (pz - zc)^2);

OP = [px - xc; py - yc; pz - zc] / r;
yVector = cross([nx; ny; nz], OP);
R = [OP, yVector, [nx; ny; nz]];

xtemp = r * cos(alpha);
ytemp = r * sin(alpha);
p = [R(1,1) * xtemp + R(1,2) * ytemp + xc
    R(2,1) * xtemp + R(2,2) * ytemp + yc
    R(3,1) * xtemp + R(3,2) * ytemp + zc];
p = [simplify(p(1)); simplify(p(2)); simplify(p(3))]

%% 写成矩阵形式,并验证结果正确性
K = 1 - cos(alpha);
M = nx * x0 + ny * y0 + nz * z0;
T = [nx^2 * K + cos(alpha),  nx * ny * K - nz * sin(alpha),  nx * nz * K + ny * sin(alpha),  (x0 - nx * M) * K + (nz * y0 - ny * z0) * sin(alpha)
    nx * ny * K + nz * sin(alpha),  ny^2 * K + cos(alpha),  ny * nz * K - nx * sin(alpha),  (y0 - ny * M) * K + (nx * z0 - nz * x0) * sin(alpha)
    nx * nz * K - ny * sin(alpha),  ny * nz * K + nx * sin(alpha),  nz^2 * K + cos(alpha),  (z0 - nz * M) * K + (ny * x0 - nx * y0) * sin(alpha)
    0, 0, 0, 1]

res = simplify([nx^2 * K + cos(alpha),  nx * ny * K - nz * sin(alpha),  nx * nz * K + ny * sin(alpha),  (x0 - nx * M) * K + (nz * y0 - ny * z0) * sin(alpha)
    nx * ny * K + nz * sin(alpha),  ny^2 * K + cos(alpha),  ny * nz * K - nx * sin(alpha),  (y0 - ny * M) * K + (nx * z0 - nz * x0) * sin(alpha)
    nx * nz * K - ny * sin(alpha),  ny * nz * K + nx * sin(alpha),  nz^2 * K + cos(alpha),  (z0 - nz * M) * K + (ny * x0 - nx * y0) * sin(alpha)
    0, 0, 0, 1] * [px; py; pz; 1] - [p(1); p(2); p(3); 1])

另外一种更直观的方法是让视野中一点A跟随鼠标位置,固定平面上一点O,类似于摇杆操作,那么求B点的程序如下

function  ABO = getABO(A1,O1,l,d);
%设A点坐标为(xA,yA,zA),xA、yA为已知
%O点坐标为(xO,yO,0),是已知的
%下面开始推导:
xO = O1(1); 
yO =O1(2);
xA = A1(1);
yA = A1(2);
OA1 = sqrt((xA-xO)^2+(yA-yO)^2);
zA =  sqrt(l^2-OA1^2);
theta1 = acos(OA1/l);
theta2 = atan((xO-xA) /(yO-yA));
%以下代码用来求斜角
dd = 1/cos(theta1);
ee = 1/cos(theta2);
ff2= tan(theta1)^2+tan(theta2)^2;
theta3 = acos((dd^2+ee^2-ff2)/(2*dd*ee)); 

%求B的坐标
zB = d/tan(theta3)*sin(theta1);
OE = d/sin(theta3);
xE = xO;
yE = yO+OE;
xB = xE - zB/tan(theta1)*sin(theta2);
yB = yE - zB/tan(theta1)*cos(theta2);
A =[xA,yA,zA];
B = [xB,yB,zB];
O = [xO,yO,0];
ABO = [A;B;O];

获得B后我们就可以根据三点求旋转参数,进而求出其他点的坐标,用鼠标控制摇杆位置的程序如下所示

function main()
clc;clear all;
figure();
text(0,0.5,'图中点击,将会看到跟随移动的立方体');
set(gcf,'WindowButtonDownFcn',@ButttonDownFcn);
end
function ButttonDownFcn(src,event)
pt = get(gca,'CurrentPoint');
x = pt(1,1);
y = pt(1,2);
l = 10; %手柄长度
d = 2;%手柄半宽度
A = [0,0,l];%物体坐标系中的点
B = [0,d,0];
O=[0,0,0];
A1 = [x,y];
O1 = [0,0];
ABO = getABO(A1,O1,l,d);%世界坐标系中的点
[R,lambda,dxyz]=  getCoordTransformPara_3dTo3d([A;B;O],ABO);
%坐标转换
xyzs = [-d,-d,0;-d,d,0;d,d,0;d,-d,0;-d,-d,l;-d,d,l;d,d,l;d,-d,l];
faces = [1,2,3,4;5,6,7,8;1,2,6,5;3,4,8,7;1,5,8,4;2,6,7,3];
[XYZs]=  coordTransform_3dTo3d(xyzs,R,lambda,dxyz);
la = 10;
x_axis = [0,-la/720,0;la,-la/720,0;la,-la/180,0;la+la/20,0,0;la,la/180,0;la,la/720,0;0,la/720,0];
%绘制坐标轴
 fill(x_axis(:,1),x_axis(:,2),[0.8,0.8,0.8]);hold on;
 text(la+la/20,la/20,'x');
fill(x_axis(:,2),x_axis(:,1),[0.8,0.8,0.8]);hold on
text(la/20,la+la/20,0,'y');
%绘制两个面
for i_face = 1:length(faces(:,1))
   h = fill(XYZs(faces(i_face,:),1),XYZs(faces(i_face,:),2),[0.8,0.8,0.8]);
   set(h,'facealpha',0.3);
   hold on;
end
axis([-l*1.2, l *1.2,-l*1.2, l*1.2 ]); 
hold off
end
============================找指导老师布置任务吧==============================