最近忙着搞毕业设计的问题,好久都没有发博文了。刚好最近导师叫我帮忙弄个小程序,实现从三维模型中映射出一张三维模型任意角度的二维图像。上网搜索了一下这种问题的相关实现办法,选了一种Z-Buffer的消隐算法,因为是实验的性质,所以直接就用matlab来做了,这样比较方便的。
首先,我们要先了解一下什么叫消隐算法?通俗一点来讲,也就是把需要显示的三维模型部分显示在屏幕上,不需要显示的部分隐藏起来,这就是消隐算法。我们的视角决定了三维模型要显示的部分,也就是平时人眼看东西的时候,我们只看到了前面的部分,而后面的我们肉眼看不到,但是也是真实存在的。消隐算法实际上就是处理人眼看物体的这个过程。
然后,我们来看看Z-Buffer算法的实现原理,简单的算法流程如下:
我们假设三维模型有k个三角网格,用img表示输出图像,其像素大小为m*n,用ZB来作为深度缓冲区储存
1. 初始化img和ZB,这里ZB设置为无穷大
2. For f=1:k %遍历每一个三角网格
For i=1:m %对每一个三角网格遍历所有的像素
For j=1:n
if判断这个点是否在三角网格的投影三角形中
如果在其中则比较这点的深度Z(i,j)和ZB(i,j)的大小
if Z(i,j)
上面是一般的Z-Buffer消隐算法的整个流程图,可以看出,我们通过face距离屏幕的距离来逐步剔除不能显示在屏幕上的face,这些都是通过深度缓存技术实现的,所以这个方法也叫作Z-Buffer。
接着我们来看看怎么实现不同的角度,不同的角度其实是很容易实现的,我们在进行三维映射之前把三维模型进行一定角度的旋转就可以实现了。我这里直接用绕Y轴的旋转矩阵进行旋转。代码如下:
theta = 0 * pi / 180;
R = [cos(theta) 0 sin(theta);0 1 0;-sin(theta) 0 cos(theta)]; %绕Y轴旋转theta度的旋转矩阵
vertex = R*vertex;
最后,我分析一下我的Z-Buffer程序,原理和上面的基本一样,有一些细节不同。
%% Matlab Ver.
%s2d是三维点映射到二维平面上的二维坐标
%vertex是三维空间点
%tri是三角网格索引
%texture是三角网格纹理坐标
%img_src是纹理图像
function [img] = ZBuffer(s2d, vertex, tri, texture, img_src)
%计算输出图像大小
height = floor(max(s2d(2,:)) - min(s2d(2,:)));
width = floor(max(s2d(1,:)) - min(s2d(1,:)));
img = zeros(height, width , 3);
imgr = zeros(height, width , 1);
n = size(tri, 2);
%三维点在二维平面的坐标
pointA = s2d(:, tri(1, :)); % A point 3*n
pointB = s2d(:, tri(2, :)); % B point 3*n
pointC = s2d(:, tri(3, :)); % C point 3*n
%二维坐标在图像中的位置信息
locationA = texture(:, tri(1, :)); %2*n
locationB = texture(:, tri(2, :));
locationC = texture(:, tri(3, :));
%计算每个face和屏幕的距离,这里直接用Z坐标代替
cent3d = (vertex(:, tri(1, :)) + vertex(:, tri(2, :)) + vertex(:, tri(3, :))) / 3;
r = cent3d(3,:);
for i = 1:n
i
pt1 = pointA(:,i); %3*1
pt2 = pointB(:,i); %3*1
pt3 = pointC(:,i); %3*1
pt = [pt1, pt2, pt3]; %3*3
loc1 = locationA(:,i); %2*1
loc2 = locationB(:,i);
loc3 = locationC(:,i);
loc = [loc1, loc2, loc3]; %2*3
[umin,x1] = min([pt1(1); pt2(1); pt3(1)]);
umin = ceil(umin);
xmin = loc(1,x1);
[umax,x2] = max([pt1(1), pt2(1), pt3(1)]);
umax = floor(umax);
xmax = loc(1,x2);
[vmin,y1] = min([pt1(2), pt2(2), pt3(2)]);
vmin = ceil(vmin);
ymin = loc(2,y1);
[vmax,y2] = max([pt1(2), pt2(2), pt3(2)]);
vmax = floor(vmax);
ymax = loc(2,y2);
if(umax < umin || vmax < vmin || umax > width || umin < 1 || vmax > height || vmin < 1)
continue;
end
%我们用的是贴图,一般算法中直接整个face就一种颜色,这样获得图像的精度不高
RGB = [];
for u = umin : umax
for v = vmin : vmax
if( imgr(v,u) < r(i) && triCpoint([u;v], pt1, pt2, pt3))%判断Zb条件和该点是否在三角网格中
imgr(v,u) = r(i);
if(umax==umin || vmax==vmin)
x = (xmin+xmax)/2;
y = (ymin+ymax)/2;
else
x = (u-umin)/(umax-umin)*(xmax-xmin)+xmin;
y = (v-vmin)/(vmax-vmin)*(ymax-ymin)+ymin;
end
x = ceil(x);
y = ceil(y);
RGB = img_src(y,x,:);%从图像中获得颜色进行贴图
img(v,u,:) = RGB;
end
end
end
end
下面附带完整的可运行代码https://download.csdn.net/download/fsencen/10423037