当前位置:首页 > 智能硬件 > 智能硬件
[导读]摘 要 该文介绍了三维立体画的原理和制作 方法 ,并给出了用C语言编写的源程序。借助于Pbrush.exe,读者可以自己设计和欣赏各式各样的三维立体画。目前 ,市面上正在流行各式各样的立体画,其特点是从外表来看与一般的图

摘 要 该文介绍了三维立体画的原理和制作 方法 ,并给出了用C语言编写的源程序。借助于Pbrush.exe,读者可以自己设计和欣赏各式各样的三维立体画。

目前 ,市面上正在流行各式各样的立体画,其特点是从外表来看与一般的图案很相似,但是双眼紧盯着注视片刻后,一恍惚之间眼前便出现了画中画——立体像。笔者第一次看到这种画便被发明者的创意所倾倒。利用众所皆知的双眼视差原理,竟能在一张平面纸上制造出如此奇幻。但是立体画本身除了其发明者的灵感和画面创作者的别出心裁之外,其原理上并无神秘之处。用 计算 机程序来实现它,可说是易如反掌。笔者用一个晚上时间,便在微机上用BASIC语言实现了简单形体——平面圆饼的立体画。当然,要使该程序具有完善的功能,提高其制作速度,还是应该用编译语言(如C语言)来编写。本文中给出的源程序借助于Windows中的.BMP图形文件,可使大家自己制作任意形态的立体画。

一、立体画的原理

看过立体电影的人都知道,当人的双眼分别接收不同视角拍摄的图像时便会产生立体感。这是由于人眼长期观察的习惯造成的。和立体电影原理相同的立体摄影风景照片也很早就已出现。图1中给出了这种立体照片的示意图。左、右照片分别是人的双眼角度上观察一棱锥体时左右眼看到的图像(图2)。左眼看到的是棱锥的顶端向右错动了一些的图像,右眼的看到则是棱锥的顶端向左错动了一些的图像。如果用一张硬卡片隔开两张照片(如图3),@@09A04000.GIF;图1@@

@@09A04001.GIF;图2@@

@@09A04002.GIF;图3双眼分别看两张画,会看到一个立体的棱锥体。这种立体照片的观察方法在测绘学中也早已采用。

但是,目前的三维立体画在形式上与这些很不相同。它是怎样在同一张画面上呈现立体的呢?首先, 分析 一下人们是怎样从这些立体画中看出“立体形体”的。从前面所说的可以知道,人眼要得到立体感,双眼必须有视差,即双眼看到的图像应该有差异。人们在看立体画时,都有“恍惚”一下的过程。在这过程中,双眼的视中心发生了错动(如图4)。这样@@09A04003.GIF;图4左眼看到的是画面的“偏左像”,右眼看到的是画面的“偏右像”。@@只要“偏左像”和“偏右像”的 内容 相当于图1的左、右照片,双眼就会感到立体形体。那么,能否把图1的左、右照片分别当做“偏左像”和“偏右像”,简单重叠来得到立体画呢?

显然不行。能够合成立体画的“偏左像”和“偏右像”是要满足一定条件的。

如果图5中表现的棱锥体的表面上有图案的话,

@@09A04004.GIF;图5像素a和像素a#39;应该具有相同的颜色,因为它们是从不同视角观察的@@同一个实体点。像素b和像素b#39;、像素c和像素c#39;的情况与此相同。把两幅画分别当作“偏左图”和“偏右图”,部分重叠成为同一画面时,在新的画面上这种关系仍应该表现为a=a#39;,b=b#39;,c=c#39;(如图6)。但这时应该注意到,在这张合成

@@09A04005.GIF;图6画面上,点a#39;既是“偏右图”上的点a#39;,又是“偏左图”上的点b。而@@一张画面上相同坐标点的像素只可能是一种颜色,因此,产生了新的像素关系a#39;=b。另外,点a既是“偏左图”上的点a,又是“偏右图”上的点c#39;,所以,a=c#39;。以此类推,点b#39;和点c也有类似的情况。因此出现了新的关系表示式,...#39;=c=c#39;=a=a#39;=b=b#39;=...。这就构成了立体画面上像素必须要满足的条件:“等颜色像素链”。立体画上的所有点都从属于某一条“等颜色像素链”。这就是所有立体画图案都呈现出某种程度上的水平周期性的原因。

因此,对于任意立体形状,只要构造出相应的这种“等像素链”,并按其 规律 充填图案即可得到立体画。但是正如前面所述,由于这种“等像素链”条件的约束,人们虽然可以随意构造出各种形体的立体画,但其立体形体的表面图案是不能完全随人意愿的。

二、制作立体画的计算机程序

由于人的双眼的水平性,以上的“等像素链”只按水平方向分布,与垂直方向无关。因此,在程序中,各个像素行的处理过程是相互独立的。制作立体画的程序主结构图如图7。

@@09A04006.GIF;图7 制作立体画的程序主结构图在以上结构图中,关键是如何建立“等@@像素链”。具体的处理如下。对于立体形体上的每一个点,首先求出该点在“偏左图”和“偏右图”上的坐标。以图1中的棱锥顶点为例,实际上其X坐标是在中心点,但由于双眼的位置并不在其正上方,顶点在“偏左图”上向右位移,在“偏右图”上向左位移,而且其位移值的大小显然与其高度有关,即该点坐标越高位移值就越大。a,b,c等点也都有这些位移。在求出一个点在“偏左图”和“偏右图”上的坐标后,再算出在合成图(如图6)上的对应坐标

,以建立“等像素”关系,如a=a#39;。当立体形体的一个水平剖面上的全部点经过以上处理后,合成图的各条“等像素链”关系也就 自然 形成了。

另外,由于有可能出现高点遮盖低点的情况,“等像素链”的构造应该从低点到高点逐层进行,高点的“等像素”关系将替代低点的“等像素”关系。这也是程序主结构图中“首先,对于没有任何形体存在的背景平面构造‘等像素链’”的原因。

下面给出了根据以上结构图用C语言编写的源程序。程序中,每一个坐标点对应一个结构型数据,它包含“前像素”、“后像素”两个指针。“前像素”指针指向该坐标点作为“偏右图”上的一点,在“偏左图”所对应的点的坐标。“后像素”指针指向该坐标点作为“偏左图”上的一点,在“偏右图”所对应的点的坐标。程序中,“立体形体水平剖面的高低坐标数据”、“原始图案素材”和输出的“立体画”的文件格式都是采用了Windows3.1 的Pbrush产生的BMP图形文件格式。图幅大小要求都是640×400,用16种颜色方式。其中,立体形体上各点的高低坐标用图形文件中的颜色值表示,因此该图形文件的图形与带颜色的等高线图安全相同。通常情况下,在16色的BMP文件中颜色值从小到大的顺序为:黑色、暗红色、暗绿色、暗黄色、暗蓝色、暗紫色、暗青色、暗灰色、灰色、明红色、明绿色、明黄色、明蓝色、明紫色、明青色、白色。本程序采用最简单的“图案充填”方案,即各条“链”上的像素点皆采用该“链”上的第一个像素的颜色。程序中的常数EYE-SPACE表示“偏左图”和“偏右图”之间的偏差,BO-DOT是表明“链”的首或尾的指针标志。

该程序寄生在Windows 3.1中的Pbrush软件上。借助于它来构筑立体形体(即立体形体水平剖面高低坐标数据文件图8),设计原始图案(图9)。程序运行后,逐行输入并处理以上两个文件中的图形,然后输出立体画结果文件(图10)。最后,用Pbrush来观赏立体画result.bmp。当然,要设计出令人赏心悦目的立体画,必须在立体形体和图案素材的选择和搭配上做到天衣无缝,独具匠心。

@@09A04007.GIF;图8@@

@@09A04008.GIF;图9@@

@@09A04009.GIF;图10程序清单@@

/*--from 1995.4.19--to 1995.5.18----*/include<stdio.h>

#define COMPRESSION 0

#define SIZE-OF-BITMAPFILEHEADER 14

#define SIZE-OF-BITMAPINFOHEADER 40

#define SIZE-OF-RGBQUAD 4

#define PIXEL-DATE-OFFSET 14+40+4*16

/*SIZE-OF-BITMAPFILEHEADER+SIZE-OF-BITMAPINFOHEADER+BITS-PER-PIXEL

*NUM-COLOR*/

#define NUM-COLOR 16

#define NUM-LINE 400

#define WIDTH 640

#define BITS-PER-PIXEL 4

#define PIXEL-PER-BYTE 2 /*8/BITS-PER-PIXEL*/

#define BYTE-PER-LINE 320 /*((WIDTH*BITS-PER-PIXEL-1)/32+1)*4 */

#define NO-DOT WIDTH+1

#define EYE-SPACE 128

struct{

unsignde char color ;

unsigned int pri-x ;

unsigned int nxt-x ;

}dot[WIDTH];

main()

{

struct tagBITMAPFILEHEADER{

unsigned char bfType1,bfType2; /* always equal to#39;BM#39;*/

unsigned long int bfSize; /*size of file */

unsigned int bfReserved1,bfReserved2; /* set to zero */

unsiged long int bfoffits; /*byte offset from BITMAPFILEHEADER to bitmap p

ixel

data in the file */

}BITMAPFILEHEADER;

struct tagBITMAPINFOHEADER{

unsigned long int biSize,/* size of BITMAPINFOHEADER */

biWidth;/* width in pixels biHeight;/* height in pixels */

unsigned int biPlanes, /* always 1 */

biBitCount; /* color bits per pixel must be 1,4,8 or 24 */

unsigned long int biCompression, /*BI-RGB,BI-RLE 8 or 4*/

biSizeImage, /*total bytes in image */

biXPelsPerMeter,/* 0,or opt,h res. */

biYPelsPerMeter,/* 0,or opt,h res. */

biClrUsed, /* normally 0,can set a lower no. colors than biBitCount */

biClrImportant; /* normally 0 */

}BITMAPINFOHEADER;

struct tagRGBQUAD{

unsigned char rgbBlue, /* blue intensity,0-255 */

rgbGreen, /* green intensity,0-255 */

rgbRed, /* red intensity,0-255 */

rgbReserved; /* reserved,set to Zero */

}RGBQUAD[NUM-COLOR];

char *fn-layer="layer.bmp";

char *fn-org="origin.bmp";

char *fn-result="result.bmp";

FILE *flayer,*fOrigin, *fResult;

unsigned char tmp-byte1,tmp-byte2;

unsigned int line,i-byte,i-pixel,x;

unsigned int layer;

int left-x,right-x;tmp-x;

unsigned long int cur-offset;

unsigned char h[WIDTH],org-color[WIDTH];

puts("---WINTRICK---");

puts("---by Li Jisong ---");

if( (fLayer=fopen(fn-layer,"rb") )!=NULL) {

fread(&BITMAPFILEHEADER,SIZE-OF-BITMAPFILEHADER,1,fLayer);

fread(&BITMAPINFOHEADER,SIZE-OF-BITMAPINFOHEADER,1,fLayer);

if( BITMAPFILEHEADER.bfType1==#39;B#39; && BITMAPFILEHEADER.bfType2==#39;M#39;

&& BITMAPINFOHEADER.biWidth==WIDTH && BITMAPINFOHEADER.biHeight==NUM-

LINE

&& BITMAPINFOHEADER.biBitCount==BITS-PER-PIXEL

&& BITMAPINFOHEADER.biCompression==COMPRESSION)

fread(RGBQUAD,SIZE-OF-RGBQUAD,NUM-COLOR,fLayer);

else{

fclose(fLayer);

printf("File %s is not fit for this program! ",fn-layer);

getch();

exit(1);

}

}

else{

printf("File %s does not exist! ",fn-layer);

getch();

exit(2);

}

if( (fOrigin=fopen(fn-org,"rb"))!=NULL) {

fread(& BITMAPFILEHEADER,SIZE-OF-BITMAPFILEHEADER,1,fOrigin);

fread(& BITMAPINFOHEADER,SIZE-OF-BITMAPINFOHEADER,1,fOrigin);

if( BITMAPFILEHEADER.bfType1==#39;B#39; && BITMAPFILEHEADER.bfType2==#39;M#39;

&& BITMAPINFOHEADER.biWidth==WIDTH && BITMAPINFOHDADER.biHeight==NUM-LINE

&& BITMAPINFOHEADER.biBitCount==BITS-PER-PIXEL

&& BITMAPINFOHEADER.biCompression==COMPRESSION)

fread(RGBQUAD,SIZE-OF-RGBQUAD,NUM-COLOR,fOrigin);

else {

fclose(fOrigin);

printf("File %s is not fit for this program! ",fn-org);

getch();

exit(3);

}

}

else {

printf("File %s does not exist! ",fn-org);

getch();

exit(4);

}

if( (fResult=fopen(fn-result,"wb"))!=NULL){

fwrite(&BITMAPFILEHEADER,SIZE-OF-BITMAPFILEHEADER,1,fResult);

fwrite(& BITMAPINFOHIADER,SIZE-OF-BITMAPINFOHEADER,1,fResult);

fwrite(RGBQUAD,SIZE-OF-RGBQUAD,NUM-COLOR,fResult);

}

else {

printf("File %s open error! ",fn-result);

getch();

exit(5);

}

for(line=0;line<NUM-LINE;line++){

printf("line=%d ",line);

cur-offset=(unsigned long int)PIXEL-DATA-OFFSET+(unsigned long int)BYTE-

PER-LINE*line;

fseek (fLayer,cur-offset,SEEK-SET);

fseek (fOrigin,cur-offset,SEEK-SET);

for(i-byte=0;i-byte<BYTE-PER-LINE;i-byte++) {

fread(&tmp-byte1,1,1,fLayer);

fread(&tmp-byte2,1,1,fOrigin);

for(i-pixel=0;i-pixel<PIXEL-PER-BYTE;i-pixel++) {

x=i-byte*PIXEL-PER-BYTE+i-pixel;

if(x<WIDTH) {

h[x]=(unsigned char) (tmp-byte1<<(BITS-PER-PIXEL*i-pixel)

) /((unsigned char)0x80>>(BITS-PER-PIXEL-1));

org-color[x]=(unsigned char) (tmp-byte2<<(BITS-PER-PIXEL*i-pixel))

/((unsigned char)0x80>>(BITS-PER-PIXEL-1));

}

}}

for(x=0;x<WIDTH;x++) {

dot[x].color=0;

dot[x].nxt-x=NO-DOT;

dot[x].pri-x=NO-DOT;

if((x+EYE-SPACE)<WIDTH) dot[x].nxt-x=x+EYE-SPACE;

if((signed)x-EYE-SPACE)>=0)dot[x].pri-x=x-EYE-SPACE;

}

for(layer=1;layer<NUM-COLOR;layer++)

for(x=0;x<WIDTH;x++) {

left-x=x-EYE-SPACE/2+(layer/2);

right-x=x+EYE-SPACE/2-((layer+1)/2);

if((h[x]==layer)&&(left-x>=0)&&(rignt-x<WIDTH)) {

if (dot[left-x].nxt-x!=NO-DOT) dot[dot[left-x].nxt-x].pri-x=NO

-DOT;

dot[left-x].nxt-x=right-x;

if (dot[right-x].pri-x!=NO-DOT) dot[dot[right-x].nxt-x].nxt-x=

NO-DOT;

dot[right-x].pri-x=left-x;

}

}

for(x=0;x<WIDTH;x++) {

if(dot[x].pri-x==NO-DOT) {

dot[x].color=org-color[x];

tmp-x=x;

while( dot[tmp-x].nxt-x!=NO-DOT) {

tmp-x=dot[tmp-x].nxt-x;

dot[tmp-x].color=org-color[x];

}

}

}

fseek(fResult,cur-offset,SEEK-SET);

for(i-byte=0;i-byte<BYTE-PER-LINE;i-byte++) {

tmp-bytel=0;

for(i-pixel=0;i-pixel<PIXEL-PER-BYTE;i-pixel++) {

x=i-byte*PIXEL-PER-BYTE+i-pixel;

if(x<WIDTH)

tmp-byte1=(unsigned char) (tmp-byte1<<(BITS-PER-PIXEL*i

-pixel)) +dot[x].color;

}

fwirte(&tmp-byte1,1,1,fResult);

}

}

fclose(fLayer);fclose(fOrigin);fclose(fResult);

}

参考 文献

孙志辉、王萃寒、王茜.实用Windows 3.1详解.北京: 电子 工业 出版社,1994.

更多计算机与外设信息请关注21ic计算机与外设频道

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

今天,我们来聊一道常见的考题,也出现在腾讯面试的三面环节,非常有意思。具体的题目如下:文件中有40亿个QQ号码,请设计算法对QQ号码去重,相同的QQ号码仅保留一个,内存限制1G.这个题目的意思应该很清楚了,比较直白。为了...

关键字: 腾讯 BITMAP UNSIGNED 内存

今天,我们来聊一道常见的考题,也出现在腾讯面试的三面环节,非常有意思。具体的题目如下:文件中有40亿个QQ号码,请设计算法对QQ号码去重,相同的QQ号码仅保留一个,内存限制1G. 这个题目的意思应该很清楚了,比较直白。为...

关键字: BITMAP UNSIGNED 内存 腾讯

近日,据外媒报道,谷歌新一代原生安卓旗舰Pixel 4可能将搭载90Hz屏幕! 外媒据内部可靠消息称,Pixel 4系列将分为Pixel 4与Pixel 4 XL两款机型,背部左上角均搭

关键字: PIXEL 90HZ GB 安卓

继先前Google自行公布下一款手机产品名称就是Pixel 4,同时也确认将搭载更精准的脸部识别,以及以Soli技术打造的隔空手势操作功能后,再有消息指称此次预计推出新机将会采用画面更新率达90

关键字: PIXEL GOOGLE DISPLAY SNAPDRAGON

谷歌新机Pixel 4被曝光。其可以说是谷歌潜心研发数年的手势操控技术 Soli的应用实机了,将带来手机界的新革命。在Google官方刚刚公布了Pixel 4系列手机的宣传视频中,向我们展示了P

关键字: 手机 PIXEL 摄像头 传感技术

提到真无线蓝牙耳机,苹果AirPods无疑是其中的佼佼者,不过近两年,不少品牌也开始推出具有竞争力的产品来挑战它。谷歌最近发布了第二代Pixel Buds耳机,新的谷歌Pixel Buds能否挑战苹果

关键字: 无线耳机 PIXEL BUDS AIRPODS

据西班牙《世界报》网站报道,在有关摄像头、屏幕、尺寸、连接性和设计的诸多手机新元素中,人们也许会错过新款三星盖乐世Note 10手机的一个细节:可以用手写笔S-Pen体验多种功能。报道称,S-P

关键字: 智能手机 三星 PEN PIXEL

谷歌在推特上发布了一段简短的视频,展示了用户如何通过手势控制手机,而无需触屏。该公司还在博客文章中解释了该功能是如何工作的。 据外媒报道,谷歌预计将在今年秋天的某个时候发布Pixel 4

关键字: 手机 PIXEL MOTION SENSE

在有关摄像头、屏幕、尺寸、连接性和设计的诸多手机新元素中,人们也许会错过新款三星盖乐世Note 10手机的一个细节:可以用手写笔S-Pen体验多种功能。报道称,S-Pen就像是一根魔杖。为了让它

关键字: 智能手机 三星 PEN PIXEL

继去年10月15日谷歌推出了包括Pixel 4和Pixel 4 XL两个版本在内的新一代Pixel 4系列旗舰后,这段时间以来陆续有关于一款被称为Pixel 4a的新机的爆料传出。现在有最新消息,近日

关键字: PIXEL GEEKBENCH GB 6G
关闭
关闭