珠沙落吧 关注:86贴子:8,480
  • 3回复贴,共1
Computer Graphics: Z Buffer法


 最大最小法 仅适用于曲面函式可以用显函式y = f(x, z)来表示时,如果曲面函式非显函式形式,则无法使用最大最小法来处理深度问题。

Z Buffer有些类似画家演算法,都是以近景遮盖远景的方法来处理深度问题,所不同的是Z Buffer使用的是裁剪(culling)的方法,并以像素为处理的对象,Z Buffer将绘图画布内所有的座标当作一个深度缓冲区阵列zbuf[]的索引,每一个zbuf[]的元素记录一个像素绘制时的Z深度资讯,可以使用它来处理隐函式的图形绘制。

假设画布大小为600X400(X, Y),则zbuf[]的大小必须设定为600*400=240000(X*Y),一开始时所有zbuf[]元素的值设定为一个极小值,也就是所有的像素都表示空间中一个极深的位置,开始绘制之后,必须在zbuf[]中记录每一个像素的z值。

zbuf[]是个一维阵列,所以我们必须计算座标的索引值,如果以列为主的话,则(x, y)对应的zbuf[]元素值为:zbuf[x + y * 画布高度];当然您也可以使用二维阵列zbuf[][]来直接对应。

如果后来要绘制的点之z值大于zbuf[]中记录的值,表示此点在之前所绘点的前面,于是绘制此点来覆盖之前所绘的点,并更新zbuf[] 中的z值为目前点的z值;如果后来要绘制的点之z值小于zbuf[]中记录的值,表示此点在之前所绘点的后面,于是不用绘制此点,当然也不用更新zbuf []中的z值。

Z Buffer的深度处理方式无论从哪一个点开始绘制,都不会影响处理的结果;Z buffer的缺点就是使用大量的记忆体作为缓冲区,而由于它是以像素为处理的单位,所以需耗用相当大量的运算资源。

下面这个程式并不是一个很好的示范,因为我们并不是每个像素都考虑到,但可以让您了解Z Buffer的演算法,如果要考虑所有的像素,这个程式要画的好,最好加上阴影的效果,绘制的图形之参数式如下,其中 a 表示圆的粗细: 

x = (1+a*cosθ) * sinφ 
y = a * sinθ 
z = (1+a*cosθ)*cosφ 
0 < a < 1, 0 <= θ <= 2π, 0 <= φ <= 2π 



ZBufferMethod.java 
package onlyfun.caterpillar; import java.awt.Color;import java.awt.Graphics;import javax.swing.JApplet; public class ZBufferMethodDemo extends JApplet {    private int orgX;    private int orgY;    private double[] zbuf;      public void init() {        super.init();        setBackground(Color.black);        orgX = (int)getSize().width /2;        orgY = (int) (getSize().height / 2);        zbuf =             new double[getSize().width * getSize().height];    }        public void paint(Graphics g) {        g.setColor(Color.yellow);                // 从斜角绘制        // 绕 x 轴转 30 度        double angleX = Math.toRadians(30);                double a = 0.3;        double k = 200.0;                for(int l = 0; l < zbuf.length; l++)             zbuf[l] = -10000.0;                 // 由于是单色,调整一下 j 与 i 可以看的明显一些        for(double j = 0; j < 360; j+=0.2) {              for(double i = 0; i < 360; i+=0.1) {                 double x = (1 + a*Math.cos(Math.toRadians(i))) *                             Math.sin(Math.toRadians(j));                 double y = a * Math.sin(Math.toRadians(i));                 double z = (1 + a*Math.cos(Math.toRadians(i))) *                             Math.cos(Math.toRadians(j));                 // 立体旋转,从斜角绘制,调整绘图中心至视窗中心                double pointX = orgX + k * x;                double pointY = orgY - k *                    (y*Math.cos(angleX) - z*Math.sin(angleX));                                // Z buffer处理                int index = (int) (pointX +                                pointY * getSize().width);                if(z > zbuf[index]) {                    g.drawLine((int)pointX, (int)pointY,                                (int)pointX, (int)pointY);                    zbuf[index] = z;                }            }         }    }}
 



1楼2008-05-27 09:44回复
    2楼2008-05-27 09:44
    回复
      任何一个物体,从是自己的局部坐标空间到屏幕显示,要经过一系列的变换矩阵的处理 
       空间物体的坐标是三维的有xyz三个分量,同样的在屏幕空间上也是一样的 
       可以简单的认为xy分量就是屏幕坐标,在分量就是距离屏幕的垂直屏幕方向的距离了 
       
       在现实生活当中,你也可以体会到,睁一眼闭一眼的时候,你是分不出来物体距离你的远近的,因为从眼睛出发,一条射线上的所有点,投影到眼睛上的时候,位置是一样的 
       但是实际上他们到达眼睛的距离是不同的,这个距离就是z,屏幕坐标上的在 
       找z最小的,显示出来,并且记录这个z数值,以后向这个点写数据的时候,和记录的这个z数值比 
       比这个数值小就写入,否则就不写。这样就可以始终让距离眼睛最近的点显示,远处的不显示。


      3楼2008-05-27 09:46
      回复
        • 211.69.206.*
        回复:3楼
        一只眼睛就没有深度感觉了?
        请你闭一只眼拿东西再说
        双眼可以辅助定位深度,但绝对不是只靠时差来确定深度的


        5楼2010-07-26 09:55
        回复