openGL之深度测试、表面剔除、剪裁---openGL学习笔记(五)

来源:互联网 发布:服装厂出货软件 编辑:程序博客网 时间:2024/05/18 02:23

深度测试:

上次用画三角形扇面的方法绘制的棱锥,发现一个问题,就是无论将棱锥如何旋转,底面总是可见的。

这与实际不符,实际中,当我们从棱锥的顶点向下看的时候是看不见底面的,出现这种情况的原因是因为,我们先画的棱锥锥面,后画的棱锥底面,在openGL中后画出来的部分,会一直可见。

那么为了解决这个问题,我们就需要用到openGL中的深度测试。

深度测试简单来说就是,让应该被挡住的物体不显示。

深度测试的原理是:启用一个z值(深度Z轴),在绘制的时候会拿这个z值对比,从而确定是否显示。

一般的,我们在onDrawFram()方法中启用深度测试:

//启用深度测试gl.glEnable(GL10.GL_DEPTH_TEST); 
而要启用深度测试就要先清除深度缓存:

//清除深度缓冲区gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
这样,就可以让应该被挡住的物体不显示了。

表面剔除:

一张纸有两面,即正面和反面,那么同样的,我们绘制的棱锥的锥面和锥底都有两面,外面和里面,外面就是我们可见的部分,里面就是在棱锥闭合空间内的部分,这部分我们是看不见的,那么既然看不见,我们就可以告诉openGL,里面的部分不要绘制出来,这样既不影响图像,又能提升性能,这个让不可见的部分不绘制的过程,叫做表面剔除。

表面剔除和深度测试一样,是一个状态机,与所有状态机的开启方式一样的:

//启用表面剔除gl.glEnable(GL10.GL_CULL_FACE);
启用了表面剔除之后,我们还需指定,顺时针方向为正面还是逆时针方向为正面,需要剔除正面还是背面。

根据我们的需求,我们的圆锥是逆时针绘制的,那么圆锥内部就是顺时针绘制的,我们要剔除的是圆锥内部,那么就设置逆时针方向为正面,剔除背面:

gl.glFrontFace(GL10.GL_CW);//设定前面 ccwcounter clock wise--->逆时针(cw:顺时针)gl.glCullFace(GL10.GL_BACK);//剔除背面
这样绘制完了会发现,棱锥的底面不见了,这是因为,底面也是逆时针绘制的,因为指定了逆时针方向为正面,而我们可见底面的部分刚好为背面,所以就将底面我们可见的部分剔除了,而我们不可见的部分还在。

所以,这就需要在我们绘制底面的时候再次手动指定:剔除正面。

gl.glCullFace( GL10.GL_FRONT ); //剔除正面
这样就将棱锥内部不可见的部分剔除了。

裁剪:

scissor:剪刀

有的时候,我们只想让画面局部更新,其他地方保持原样,那么这就要用到裁剪。

在屏幕上,我们实际可见的部分是视口决定的,而设置视口的方法在onSurfaceChanged()方法中:

//设置视口gl.glViewport(0,0,width,height);
那么我们就要在这个方法中将视口的width,和height赋给定义的成员变量。

这样就可以设置我们想要的剪裁后的区域了。

首先裁剪也是状态机,先要启用剪裁:

//启用剪裁gl.glEnable( GL10.GL_SCISSOR_TEST );
启用了剪裁之后,还需要设置剪裁区:

gl.glScissor();

如上图所示,假如每次缩进20px,那么蓝色区域的宽和高就都会比白色区域小40px,因为上下左右都需要缩进。同样的绿色区域也比蓝色区域小40px。

gl.glScissor();方法一共四个参数,前两个参数x,y即是缩进后的矩形左下角的坐标。那么如定义缩进量为20的话,(x,y)就是(20,20)。

第三个第四个参数为缩进后的矩形的宽高,那么此时应该是(width-20*2,heihgt-20*2)。

为不同颜色显示,我们设置一个二维数组:

float [] [] colors = {        {1f,0f,0f,1f},        {0f,1f,0f,1f},        {0f,0f,1f,1f},        {1f,1f,0f,1f},        {0f,1f,1f,1f},        {1f,0f,1f,1f},};
顶点坐标为:

float [] coords = {    -ratio,1f,2f,    -ratio,-1f,2f,    ratio,-1f,2f,    ratio,1f,2f,};
假定缩进量为100px,那么该如下设置:

int step = 100;for( int i = 0;i < 6;i++ ){    //设置剪裁区    gl.glScissor(i*step,i*step,with - (i*step*2),height - (i*step*2));    gl.glColor4f( colors[i][0],colors[i][1],colors[i][2],colors[i][3] );    gl.glVertexPointer( 3,GL10.GL_FLOAT,0,BufferUtils.array2ByteBuffer(coords) );    gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0,4);}
这样就完成了剪裁。

附剪裁源码:

public class MyScissorRenderer extends AbstractRenderer {    private int with;    private int height;    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {        this.with = width;        this.height = height;        //设置视口        gl.glViewport(0,0,width,height);        //计算视口比例        ratio = (float)width / (float)height;        //设置矩阵模式        gl.glMatrixMode(GL10.GL_PROJECTION);        //加载单位矩阵        gl.glLoadIdentity();        //设置平截头体        gl.glFrustumf(ratio,-ratio,-1f,1f,3f,7f);//ratiowith/height,所以高度为,宽度为ratio    }    public void onDrawFrame(GL10 gl) {        //清除颜色缓冲区        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);        //设置绘图颜色        gl.glColor4f(1f, 1f, 1f, 1f);        //设置模型视图矩阵        gl.glMatrixMode(GL10.GL_MODELVIEW);        //载入单位矩阵        gl.glLoadIdentity();        //放置眼球位置        GLU.gluLookAt(gl, 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f);        //启用剪裁        gl.glEnable( GL10.GL_SCISSOR_TEST );        //旋转角度,以便更直观查看        gl.glRotatef(xRotate,1,0,0);// x轴旋转 (openGL规定,顺时针旋转为负值)        gl.glRotatef(yRotate,1,0,0);// y轴旋转        float [] coords = {            -ratio,1f,2f,            -ratio,-1f,2f,            ratio,-1f,2f,            ratio,1f,2f,        };        float [] [] colors = {                {1f,0f,0f,1f},                {0f,1f,0f,1f},                {0f,0f,1f,1f},                {1f,1f,0f,1f},                {0f,1f,1f,1f},                {1f,0f,1f,1f},        };        int step = 100;        for( int i = 0;i < 6;i++ ){            //设置剪裁区            gl.glScissor(i*step,i*step,with - (i*step*2),height - (i*step*2));            gl.glColor4f( colors[i][0],colors[i][1],colors[i][2],colors[i][3] );            gl.glVertexPointer( 3,GL10.GL_FLOAT,0,BufferUtils.array2ByteBuffer(coords) );            gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0,4);        }    }}
附深度测试、表面剔除源码:(画棱锥为例)

/** * 概念: * cone:圆锥 * 顶点的着色模式: * 1.平滑 smooth(渐变模式):默认 * 2.单调 flat(单调模式)也是就和最后一个点的颜色相同 * * 深度测试:被挡住的部分不显示 * 原理为启用一个z值(深度轴z轴),被遮挡的物体看不见,但是还是会绘制。 * * 表面剔除:如果看不见的部分,就告诉openGL不要绘制该部分,这样可以提高性能。 * 例如在画一个椎面,该锥面为逆时针绘制,相应的锥面内部就是顺时针绘制, * 那么指定逆时针方向为正面后,可以剔除背面,那么背面部分就会不绘制。 */public class MyTriangleConeRenderer extends AbstractRenderer{    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {        super.onSurfaceChanged(gl, width, height);        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);    }    public void onDrawFrame(GL10 gl) {        //清除颜色缓冲区        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);        //清除深度缓冲区        gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);        //设置绘图颜色        gl.glColor4f(1f, 1f, 1f, 1f);        //启用深度测试        gl.glEnable(GL10.GL_DEPTH_TEST); //启用深度测试前要清除深度缓冲区        //启用表面剔除        gl.glEnable(GL10.GL_CULL_FACE);        gl.glFrontFace(GL10.GL_CW);//设定前面 ccwcounter clock wise--->逆时针(cw:顺时针)        gl.glCullFace(GL10.GL_BACK);//剔除背面        //设置着色模式        gl.glShadeModel(GL10.GL_FLAT); //单调模式        //设置模型视图矩阵        gl.glMatrixMode(GL10.GL_MODELVIEW);        //载入单位矩阵        gl.glLoadIdentity();        //放置眼球位置        GLU.gluLookAt(gl, 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f);        //旋转角度,以便更直观查看        gl.glRotatef(xRotate, 1, 0, 0);// x轴旋转 (openGL规定,顺时针旋转为负值)        gl.glRotatef(yRotate,1,0,0);// y轴旋转        /**         * 计算点的坐标         * @param r 半径         * @param coordsList 坐标集合         * @param x,y,z 每个点的坐标         * @param alpha 角度         *         */        float r = 0.5f;//半径        float x = 0f,y = 0f,z = -0.5f;//点的坐标        /******************** 锥面 **********************/        List<Float> coordsList = new ArrayList<Float>();        //添加锥顶点        coordsList.add(0f);        coordsList.add(0f);        coordsList.add(0.5f);        //顶点颜色集合        List<Float> colorList = new ArrayList<Float>();        colorList.add(1f);//r        colorList.add(0f);//g        colorList.add(0f);//b        colorList.add(1f);//a        /******************** 锥底 **********************/        List<Float> coordsConeBottomList = new ArrayList<Float>();        coordsConeBottomList.add(0f);        coordsConeBottomList.add(0f);        coordsConeBottomList.add(-0.5f);        boolean flag = false;        //底面        for( float alpha = 0f; alpha < Math.PI * 6;alpha = (float) (alpha+Math.PI / 8 )){            x = (float) (Math.cos(alpha) * r);            y = (float) (Math.sin(alpha) * r);            //锥面坐标            coordsList.add(x);            coordsList.add(y);            coordsList.add(z);            //锥底坐标            coordsConeBottomList.add(x);            coordsConeBottomList.add(y);            coordsConeBottomList.add(z);            //点颜色值            if( flag = !flag ){                //黄色                colorList.add(1f);                colorList.add(1f);                colorList.add(0f);                colorList.add(1f);            }else {                //红色                colorList.add(1f);                colorList.add(0f);                colorList.add(0f);                colorList.add(1f);            }            /*****使锥底颜色与锥面颜色错位*****/            if( flag = !flag ){                //黄色                colorList.add(1f);                colorList.add(1f);                colorList.add(0f);                colorList.add(1f);            }else {                //红色                colorList.add(1f);                colorList.add(0f);                colorList.add(0f);                colorList.add(1f);            }        }        /****************绘制锥面*********************/        ByteBuffer colorBuffer = BufferUtils.list2ByteBuffer(colorList);        gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer );//顶点颜色指针        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtils.list2ByteBuffer(coordsList));//指定顶点坐标指针        gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, coordsList.size() / 3);//绘制锥面        /****************绘制锥底*********************/        colorBuffer.position(4); //四个数为一个颜色,从五个颜色开始就会从第二个颜色开始绘制        gl.glCullFace( GL10.GL_FRONT ); //剔除正面        gl.glColorPointer(4, GL10.GL_FLOAT, 0,colorBuffer );//顶点颜色指针        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtils.list2ByteBuffer(coordsConeBottomList));//指定顶点坐标指针        gl.glDrawArrays( GL10.GL_TRIANGLE_FAN,0,coordsConeBottomList.size() / 3 );//绘制锥底    }}

0 0
原创粉丝点击