openGL之几何变换(绘制球体)---openGL学习笔记(六)

openGL中的变换包括:
  ①视图(modeling)---指定观察者或者相机位置  GLU.glLookAt() 默认情况下,在透视投影中观察者是从原点向Z轴负方向看去,也可以自行设置。
  ②模型(viewing)---在场景中移动物体 包含移动、旋转、缩放
  ③模型视图(modelview)---描述制图和模型变换的对偶性 例如:观察者靠近物体,与物体放置靠近观察者,所呈现的效果是一样的。
  ④投影---(projection)改变可视区域的大小以及重新设置形状  glFrustum() 包含正交投影(也叫平行投影,没有远近概念)和透视投影(有深度概念)等。
  ⑤视口---(viewport)伪变换,对窗口上的最终输出进行缩放

 

顶点变换的管线:

 
            乘以模型视图矩阵                    乘以投影矩阵           应用透视除法                        视口变换

  源顶点数据----------------->经过变换的视图坐标------------->剪裁坐标--------------->经过规范化的设备坐标------------->窗口坐标


我们要绘制一个球体,那么就要先把球体切开,先水平切成几部分,把其中的一部分拿出来是一条带状,将这部分切开将会是一个平行四边形带,如下图。

需要注意的是:因为openGL中剔除表面时通常剔除背面,而绘制三角形过程中,采用画三角形带的方法,如果按照0-->1-->2-->0   1-->2-->3-->1   的方式绘制的话就会导致所绘制的三角形带一个逆时针,一个顺时针,而剔除时,就会把其中一个剔除掉,所以openGL在绘制三角形带时,实际的做法是:0-->1-->2-->0   2-->1-->3-->2,这样就可以保证所有的三角形全部为逆时针方向绘制。

那么这个平行四边形可以用画三角形带的方法画出来,那么如果分的份数够多(微积分的思想),且把每一个部分都以平行四边形的方式画出来,那么球体就可以实现,如下图:


首先是坐标的计算:

假如以球心圆点为坐标原点,那么水平切成的几个部分的Y坐标可以确定,且不同。而在内循环绘制每个圆面的时候的X、Z坐标是可以确定且不同的,所以,给定圆的半径R,就能用双循环的方式绘制出球体的各个点坐标。

float R = 0.7f;//球的半径
int statck = 20;//statck:切片----把球体横向切成几部分
float statckStep = (float) (Math.PI / statck);//单位角度值
int slice = 50;//纵向切几部分
float sliceStep = (float) (Math.PI / slice);//水平圆递增的角度

float r0,r1,x0,x1,y0,y1,z0,z1; //r0r1为圆心引向两个临近切片部分表面的两条线 (x0,y0,z0)(x1,y1,z1)为临近两个切面的点。
float alpha0 = 0,alpha1 = 0; //前后两个角度
float beta = 0; //切片平面上的角度
List<Float> coordsList = new ArrayList<Float>();
//外层循环
for( int i = 0;i < statck;i++ ){
    alpha0 = (float) (- Math.PI / 2 + (i*statckStep));
    alpha1 = (float) (- Math.PI / 2 + ((i+1)*statckStep));
    y0 = (float) (R * Math.sin(alpha0));
    r0 = (float) (R * Math.cos(alpha0));
    y1 = (float) (R * Math.sin(alpha1));
    r1 = (float) (R * Math.cos(alpha1));

    //循环每一层圆
    for( int j = 0;j <= (slice * 2);j ++ ){
        beta = j * sliceStep;
        x0 = (float) (r0 * Math.cos(beta));
        z0 = -(float) (r0 * Math.sin(beta));
        x1 = (float) (r1 * Math.cos(beta));
        z1 = -(float) (r1 * Math.sin(beta));
        coordsList.add(x0);
        coordsList.add(y0);
        coordsList.add(z0);
        coordsList.add(x1);
        coordsList.add(y1);
        coordsList.add(z1);

    }
坐标确定了之后剩下就简单了,一样的,先要设置清屏色,设置绘图颜色,然后指定模型视图矩阵,加载单位矩阵,放置眼球位置,设置旋转角度。最后再指定顶点指针,绘制三角形带。

运行效果图:

注:为方便观察,这个图是用画线带的方式绘制的,想要画球体该用画三角形带的方法。

最后附代码:

public class MySphereRenderer extends AbstractRenderer{
    @Override
    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, 0, 0, 5, 0, 0, 0, 0, 1, 0);//放置眼球位置
        gl.glRotatef(xRotate, 1, 0, 0);//x轴旋转角度
        gl.glRotatef(yRotate,0,1,0);//y轴旋转角度

        /***********************计算球体坐标****************************/

        float R = 0.7f;//球的半径
        int statck = 20;//statck:切片----把球体横向切成几部分
        float statckStep = (float) (Math.PI / statck);//单位角度值
        int slice = 50;//纵向切几部分
        float sliceStep = (float) (Math.PI / slice);//水平圆递增的角度

        float r0,r1,x0,x1,y0,y1,z0,z1; //r0r1为圆心引向两个临近切片部分表面的两条线 (x0,y0,z0)(x1,y1,z1)为临近两个切面的点。
        float alpha0 = 0,alpha1 = 0; //前后两个角度
        float beta = 0; //切片平面上的角度
        List<Float> coordsList = new ArrayList<Float>();
        //外层循环
        for( int i = 0;i < statck;i++ ){
            alpha0 = (float) (- Math.PI / 2 + (i*statckStep));
            alpha1 = (float) (- Math.PI / 2 + ((i+1)*statckStep));
            y0 = (float) (R * Math.sin(alpha0));
            r0 = (float) (R * Math.cos(alpha0));
            y1 = (float) (R * Math.sin(alpha1));
            r1 = (float) (R * Math.cos(alpha1));

            //循环每一层圆
            for( int j = 0;j <= (slice * 2);j ++ ){
                beta = j * sliceStep;
                x0 = (float) (r0 * Math.cos(beta));
                z0 = -(float) (r0 * Math.sin(beta));
                x1 = (float) (r1 * Math.cos(beta));
                z1 = -(float) (r1 * Math.sin(beta));
                coordsList.add(x0);
                coordsList.add(y0);
                coordsList.add(z0);
                coordsList.add(x1);
                coordsList.add(y1);
                coordsList.add(z1);

            }
            //指定顶点指针
            gl.glVertexPointer(3,GL10.GL_FLOAT,0, BufferUtils.list2FloatBuffer(coordsList));
            //若要画球体,应以画三角形带的方式绘制,为方便观察,此处用画线带的方式
            gl.glDrawArrays(GL10.GL_LINE_STRIP,0,coordsList.size() / 3);
        }

    }
}

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值