imuncle / imuncle.github.io

大叔的个人小站
https://imuncle.github.io/
78 stars 17 forks source link

三维立体图的原理和制作方法 #86

Open imuncle opened 4 years ago

imuncle commented 4 years ago

记得高一的时候第一次接触三维立体图,非常震惊,热情地推荐给身边的同学,结果到目前为止,没有一个同学和我一样能看出其中的三维信息,十分沮丧。

最开始接触的三维立体图是刘红石大大制作的,他的官网点这里,里面有他的三维立体图,有立体摄影,有文本立体图,还有图片程序下载以及实现的代码,我把他的代码用JavaScript重写了一遍,将文本立体图和三维立体图整合起来,可以使用浏览器直接生成。

代码放在最后说。以下是转载刘红石老师的三维立体画原理和观看方法


三维立体画是利用人眼立体视觉现象制作的绘画作品。普通绘画和摄影作品,包括电脑制作的三维动画,只是运用了人眼对光影、明暗、虚实的感觉得到立体的感觉,而没有利用双眼的立体视觉,一只眼看和两只眼看都是一样的。充分利用双眼立体视觉的立体画,将使你看到一个精彩的世界。

一、立体视觉和立体画原理

人有两只眼,两只眼有一定距离,这就造成物体的影象在两眼中有一些差异,见右图,由图可见,由于物体与眼的距离不同,两眼的视角会有所不同,由于视角的不同所看到是影象也会有一些差异,大脑会根据这种差异感觉到立体的景象。 image

三维立体画就是利用这个原理,在水平方向生成一系列重复的图案,当这些图案在两只眼中重合时,就看到了立体的影象。参见下图,这是一幅不能再简单的立体画了。图中最上一行圆最远,最下一行圆最近,请注意:最上一行圆之间距离最大,最下一行圆之间距离最小。  image

这是怎么发生是呢?让我们再看下图,从图中我们可以看到,重复图案的距离决定了立体影象的远近,生成三维立体画的程序就是根据这个原理,依据三维影象的远近,生成不同距离的重复图案。 image

二、立体画的观看

如果你现在还不会看立体画,是不是已经很着急了,下面我将介绍怎样看立体画。

立体画有两种形式:第一种是由相同的图案在水平方向以不同间隔排列而成,看起来是远近不同的物体,请看下图。这样的立体画可用任意一种图象处理软件制作,如Photoshop、Windows画笔等,你也可以一试。 image

另一种立体画较复杂,在这种立体画上你不能直接看到物体的形象,画面上只有杂乱的图案,制作这样的立体画只有使用程序了,我为此编写了一些程序,有C和QBASIC的源程序,请看自制立体画和程序下载。两种作品看法是一样的,原理都是使左眼看到左眼的影象,让右眼看到右眼的影象,(有人说了:你这不是废话吗?)听我说具体的方法:当你看立体画时,你要想象你在欣赏玻璃橱窗中的艺术品,也就是说你不要看屏幕上的立体画,而要把屏幕看成是玻璃橱窗的玻璃,你要看的是玻璃之内的影象。 image

三、两点练习法

请把下图上方的两点作为目标,先使眼睛休息片刻,然后象眺望远方那样,用稍模糊的视线瞄准两点,就会看从两点各自分离出另外两个点,然后调整视线,试图将里面两个点合成一点,当四点变为三点时,你便会看到立体图象。 image

四、另一种观看方法

从电脑上看费劲的话,可以这样,如果画面上标有两点(如没有,可以通过仔细观看,在横向上,相隔约3-5厘米,就有相同的图案,如两个相同颜色和大小的点等),那么可以用两个颜色深点的线垂直粘在显示器屏幕的上面(可以进入屏幕少许),使两条线垂直并分别与两点相连。然后,在显示器后面上方放个小东西做参照物,沿显示器上边沿来看参照物,前后移动眼睛的焦点,使左眼、左线、参照物成一直线,右眼、右线、参照物成一直线,可以挡上一只眼调整,然后两眼看参照物,此时两条线就变成了三条,让视线沿中间的线爬进立体画面就看到了立体效果。


刘老师的主页里有示例代码,然而最关键的部分却没有注释:

/************************************************/
/*                                              */ 
/*   三维画生成程序,我的个人主页               */
/*   http://www.liuhs.com                       */
/*   中的三维画不是用此程序生成,但原理一样     */
/*   此程序用 Turbo C 2.00 调试通过             */
/*                                              */
/*                             刘红石           */
/************************************************/

# include <stdlib.h>
# include <graphics.h>
# include <conio.h>
# include <alloc.h>

# define NO 999     /* 一个标志,指示有无对应的点*/
# define W 64       /* 重复的图案宽度 */
# define MAXX 639   /* 三维画最大宽度 */
# define MAXY 479   /* 三维画最大高度 */
# define MAXZ 7     /* 三维形象最大层数 */

int dot[MAXX+1][3];    

/* 下面数据决定看到的三维形象 */
char *hf[]={
     "11111111111111111111111111111111",
     "11111111111111111111111111111111",
     "11111111111111111111111111111111",
     "11117777777711111177777777711111",
     "11117777777771111177111111771111",
     "11111111117771111177111111771111",
     "11111111117771111177111111771111",
     "11117777777771111177111111771111",
     "11111111117771111177111111771111",
     "11111111117771111177111111771111",
     "11117777777771111177111111771111",
     "11117777777711111177777777711111",
     "11111111111111111111111111111111",
     "11111111111111111111111111111111",
     "11112233445566776655443322111111",
     "11112233445566776655443322111111",
     "11111111111111111111111111111111",
     "11111111111111111111111111111111",
     "11112223344456665443322211111111",
     "11112223344456665443322211111111",
     "10101010101010101010101010101010",
     "01010101010101010101010101010101",
     "10101010101010101010101010101010",
     "01010101010101010101010101010101"};

void main()
{
  int lx,rx,tx;
  int gd=0,gm;
  int i;
  int x,y,h;
  int *p;

  initgraph(&gd,&gm,"");         /* 初始化图形设备 */
  for(i=1;i<=8000;i++){          /* 在屏幕上画出随机的线,作为三维画的底纹 */
    setcolor(i % 8+7);
    x=random(MAXX); y=random(MAXY);
    line(x,y,x+random(7),y+random(7));
  }
  p=(int*)malloc(imagesize(0,0,19,1));  /* 分配内存,准备存放图块数据 */

 for(y=0;y<= MAXY;y+=20){               /* 从0到MAXY一行一行进行 */
    for(x=0;x<=MAXX;x++){               /* 生成平面数据 */
      dot[x][1]=NO;
      dot[x][2]=NO;
      if (x+W <= MAXX) dot[x][1]= x + W;
      if (x-W >= 0)    dot[x][2]= x - W;
    }
  for(h=1;h<=MAXZ;h++){                 /* 从1层到MAXZ层,逐层处理 */
    for(x=0;x<=MAXX;x++){
      lx = x - W /2 + h;                /* 越靠前的图层,两点的距离越近 */
      rx = x + W / 2 - h;
      if (hf[y/20][x/20]-48==h && lx >= 0 && rx <= MAXX){          //这个是在干什么啊_(:з」∠)_
    if (dot[lx][1] !=NO) dot[dot[lx][1]][2]= NO;
    dot[lx][1] = rx;
    if(dot[rx][2] != NO) dot[dot[rx][2]][1] = NO;
    dot[rx][2] = lx;
      }
    }
  }
  /* 依据前面计算得到的数据画出一行三维画 */
  for(x=0;x<=MAXX;x++){          //这个for循环又是在干什么啊_(:з」∠)_
    if(dot[x][2]==NO){
      dot[x][0] = x;
      tx = x;
      while(dot[tx][1]!=NO){
    tx = dot[tx][1];
    dot[tx][0] = x;
      }
    }
    if(dot[x][0] != x){
      getimage(dot[x][0],y,dot[x][0],y+19,p);
      putimage(x,y,p,0);
    }
  }
 }
 getch();             /* 等待按键 */
 closegraph();        /* 关闭图形设备 */
}

奈何我太菜研究了几个小时也没搞到这关键的四五行代码是啥。

最后附上我用JavaScript复现的三维立体画制作小程序: 三维立体图生成

源代码见:https://github.com/imuncle/Funny/blob/master/3D/index.htm