android 使用OPENGL ES繪製一個圓環-三維空間

劉國棟發表於2017-10-29

效果圖:



編寫huanCH.java

  *設定圓錐曲面的控制屬性,包括紋理、環半徑、截面半徑、環角度切分單位和截面角度切分單位
  *通過上述屬性可以控制圓環曲面的大小,並獲取網格頂點座標;最後設定頂點、紋理、法向量緩衝,並定義繪製方      法drawSelf()

   
package com.scout.eeeeeee;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

public class huanCH
{
    private FloatBuffer ding;//頂點緩衝
    private FloatBuffer wen;//紋理緩衝
    private FloatBuffer myNormalBuffer;//法向量緩衝

    int vcount;
    int textureid;

    float rSpan;
    float cSpan;

    float ring_Radius;
    float circle_Radius;

    public float mAngleX;
    public float mAngleY;
    public float mAngleZ;

    public huanCH(float rSpan,float cSpan,float ring_Radius,float circle_Radius,int textureid)
    {
        //rSpan表示環每一份多少度;cSpan表示圓截環每一份多少度;ring_Radius表示環半徑;circle_Radius圓截面半徑。
        this.rSpan=rSpan;
        this.cSpan=cSpan;
        this.circle_Radius=circle_Radius;
        this.ring_Radius=ring_Radius;
        this.textureid=textureid;

        ArrayList<Float> val=new ArrayList<Float>();
        ArrayList<Float> ial=new ArrayList<Float>();//法向量存放列表

        for(float circle_Degree=50f;circle_Degree<130f;circle_Degree+=cSpan)
        {
            for(float ring_Degree=-90f;ring_Degree<0f;ring_Degree+=rSpan)
            {
                float x1=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree)))*Math.cos(Math.toRadians(ring_Degree)));
                float y1=(float) (circle_Radius*Math.sin(Math.toRadians(circle_Degree)));
                float z1=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree)))*Math.sin(Math.toRadians(ring_Degree)));

                float x2=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree)))*Math.cos(Math.toRadians(ring_Degree+rSpan)));
                float y2=(float) (circle_Radius*Math.sin(Math.toRadians(circle_Degree)));
                float z2=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree)))*Math.sin(Math.toRadians(ring_Degree+rSpan)));

                float x3=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree+cSpan)))*Math.cos(Math.toRadians(ring_Degree+rSpan)));
                float y3=(float) (circle_Radius*Math.sin(Math.toRadians(circle_Degree+cSpan)));
                float z3=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree+cSpan)))*Math.sin(Math.toRadians(ring_Degree+rSpan)));

                float x4=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree+cSpan)))*Math.cos(Math.toRadians(ring_Degree)));
                float y4=(float) (circle_Radius*Math.sin(Math.toRadians(circle_Degree+cSpan)));
                float z4=(float) ((ring_Radius+circle_Radius*Math.cos(Math.toRadians(circle_Degree+cSpan)))*Math.sin(Math.toRadians(ring_Degree)));

                val.add(x1);val.add(y1);val.add(z1);
                val.add(x4);val.add(y4);val.add(z4);
                val.add(x2);val.add(y2);val.add(z2);

                val.add(x2);val.add(y2);val.add(z2);
                val.add(x4);val.add(y4);val.add(z4);
                val.add(x3);val.add(y3);val.add(z3);

                //各個頂點圓截面中心的組成的圓環上的點的座標

                float a1=(float) (x1-(ring_Radius*Math.cos(Math.toRadians(ring_Degree))));
                float b1=y1-0;
                float c1=(float) (z1-(ring_Radius*Math.sin(Math.toRadians(ring_Degree))));
                float l1=getVectorLength(a1, b1, c1);//模長
                a1=a1/l1;//法向量規格化
                b1=b1/l1;
                c1=c1/l1;

                float a2=(float) (x2-(ring_Radius*Math.cos(Math.toRadians(ring_Degree+rSpan))));
                float b2=y1-0;
                float c2=(float) (z2-(ring_Radius*Math.sin(Math.toRadians(ring_Degree+rSpan))));
                float l2=getVectorLength(a2, b2, c2);//模長
                a2=a2/l2;//法向量規格化
                b2=b2/l2;
                c2=c2/l2;

                float a3=(float) (x3-(ring_Radius*Math.cos(Math.toRadians(ring_Degree+rSpan))));
                float b3=y1-0;
                float c3=(float) (z3-(ring_Radius*Math.sin(Math.toRadians(ring_Degree+rSpan))));
                float l3=getVectorLength(a3, b3, c3);//模長
                a3=a3/l3;//法向量規格化
                b3=b3/l3;
                c3=c3/l3;

                float a4=(float) (x4-(ring_Radius*Math.cos(Math.toRadians(ring_Degree))));
                float b4=y1-0;
                float c4=(float) (z4-(ring_Radius*Math.sin(Math.toRadians(ring_Degree))));
                float l4=getVectorLength(a4, b4, c4);//模長
                a4=a4/l4;//法向量規格化
                b4=b4/l4;
                c4=c4/l4;

                ial.add(a1);ial.add(b1);ial.add(c1);//頂點對應的法向量
                ial.add(a2);ial.add(b2);ial.add(c2);
                ial.add(a4);ial.add(b4);ial.add(c4);

                ial.add(a2);ial.add(b2);ial.add(c2);
                ial.add(a3);ial.add(b3);ial.add(c3);
                ial.add(a4);ial.add(b4);ial.add(c4);
            }
        }
        vcount=val.size()/3;
        float[] vertexs=new float[vcount*3];
        for(int i=0;i<vcount*3;i++)
        {
            vertexs[i]=val.get(i);
        }
        ByteBuffer vbb=ByteBuffer.allocateDirect(vertexs.length*4);
        vbb.order(ByteOrder.nativeOrder());
        ding=vbb.asFloatBuffer();
        ding.put(vertexs);
        ding.position(0);

        //法向量
        float[] normals=new float[vcount*3];
        for(int i=0;i<vcount*3;i++)
        {
            normals[i]=ial.get(i);
        }
        ByteBuffer ibb=ByteBuffer.allocateDirect(normals.length*4);
        ibb.order(ByteOrder.nativeOrder());
        myNormalBuffer=ibb.asFloatBuffer();
        myNormalBuffer.put(normals);
        myNormalBuffer.position(0);

        //紋理

        int row=(int) (360.0f/cSpan);
        int col=(int) (360.0f/rSpan);
        float[] textures=generateTexCoor(row,col);

        ByteBuffer tbb=ByteBuffer.allocateDirect(textures.length*4);
        tbb.order(ByteOrder.nativeOrder());
        wen=tbb.asFloatBuffer();
        wen.put(textures);
        wen.position(0);
    }

    public void drawSelf(GL10 gl)
    {
        gl.glRotatef(mAngleX, 1, 0, 0);//旋轉
        gl.glRotatef(mAngleY, 0, 1, 0);
        gl.glRotatef(mAngleZ, 0, 0, 1);

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, ding);

        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);//開啟法向量緩衝
        gl.glNormalPointer(GL10.GL_FLOAT, 0, myNormalBuffer);//指定法向量緩衝

        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, wen);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureid);

        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vcount);

        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);//關閉緩衝
        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
    }

    //自動切分紋理產生紋理陣列的方法
    public float[] generateTexCoor(int bw,int bh)
    {
        float[] result=new float[bw*bh*6*2];
        float sizew=1.0f/bw;//列數
        float sizeh=1.0f/bh;//行數
        int c=0;
        for(int i=0;i<bh;i++)
        {
            for(int j=0;j<bw;j++)
            {
                //每行列一個矩形,由兩個三角形構成,共六個點,12個紋理座標
                float s=j*sizew;
                float t=i*sizeh;

                result[c++]=s;
                result[c++]=t;

                result[c++]=s;
                result[c++]=t+sizeh;

                result[c++]=s+sizew;
                result[c++]=t;

                result[c++]=s+sizew;
                result[c++]=t;

                result[c++]=s;
                result[c++]=t+sizeh;

                result[c++]=s+sizew;
                result[c++]=t+sizeh;
            }
        }
        return result;
    }

    //法向量規格化,求模長度
    public float getVectorLength(float x,float y,float z)
    {
        float pingfang=x*x+y*y+z*z;
        float length=(float) Math.sqrt(pingfang);
        return length;
    }

}

MyGLSurfaceView.java

package com.scout.eeeeeee;

/**
 * Created by liuguodong on 2017/10/29.
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.view.MotionEvent;

import java.io.IOException;
import java.io.InputStream;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

public class MyGLSurfaceView extends GLSurfaceView {
    private final float TOUCH_SCALE_FACTOR = 180.0f/320;//角度縮放比例
    private SceneRenderer mRenderer;//場景渲染器
    private float mPreviousY;//上次的觸控位置Y座標
    private float mPreviousX;//上次的觸控位置Y座標
    private int lightAngle=90;//燈的當前角度

    public MyGLSurfaceView(Context context) {
        super(context);
        mRenderer = new SceneRenderer();   //建立場景渲染器
        setRenderer(mRenderer);             //設定渲染器
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//設定渲染模式為主動渲染
    }

    //觸控事件回撥方法
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        float y = e.getY();
        float x = e.getX();
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                float dy = y - mPreviousY;//計算觸控筆Y位移
                float dx = x - mPreviousX;//計算觸控筆Y位移
                mRenderer.cylinder.mAngleX += dy * TOUCH_SCALE_FACTOR;//設定沿x軸旋轉角度
                mRenderer.cylinder.mAngleZ += dx * TOUCH_SCALE_FACTOR;//設定沿z軸旋轉角度
                requestRender();//重繪畫面
        }
        mPreviousY = y;//記錄觸控筆位置
        mPreviousX = x;//記錄觸控筆位置
        return true;
    }

    private class SceneRenderer implements GLSurfaceView.Renderer
    {
        int textureId;//紋理名稱ID
        huanCH cylinder;//建立圓環體

        public SceneRenderer()
        {

        }

        public void onDrawFrame(GL10 gl) {
            //清除顏色快取
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            //設定當前矩陣為模式矩陣
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            //設定當前矩陣為單位矩陣
            gl.glLoadIdentity();

            gl.glPushMatrix();//保護變換矩陣現場

            float lx=0; //設定光源的位置
            float ly=(float)(7*Math.cos(Math.toRadians(lightAngle)));
            float lz=(float)(7*Math.sin(Math.toRadians(lightAngle)));
            float[] positionParamsRed={lx,ly,lz,0};
            gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, positionParamsRed,0);

            initMaterial(gl);//初始化紋理
            gl.glTranslatef(0, 0, -25f);//平移
            initLight(gl);//開燈
            cylinder.drawSelf(gl);//繪製
            closeLight(gl);//關燈

            gl.glPopMatrix();//恢復變換矩陣現場
        }

        public void onSurfaceChanged(GL10 gl, int width, int height) {
            //設定視窗大小及位置
            gl.glViewport(0, 0, width, height);
            //設定當前矩陣為投影矩陣
            gl.glMatrixMode(GL10.GL_PROJECTION);
            //設定當前矩陣為單位矩陣
            gl.glLoadIdentity();
            //計算透視投影的比例
            float ratio = (float) width / height;
            //呼叫此方法計算產生透視投影矩陣
            gl.glFrustumf(-ratio, ratio, -1, 1, 1, 100);
        }

        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            //關閉抗抖動
            gl.glDisable(GL10.GL_DITHER);
            //設定特定Hint專案的模式,這裡為設定為使用快速模式
            gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_FASTEST);
            //設定螢幕背景色黑色RGBA
            gl.glClearColor(0,0,0,0);
            //設定著色模型為平滑著色
            gl.glShadeModel(GL10.GL_SMOOTH);
            //啟用深度測試
            gl.glEnable(GL10.GL_DEPTH_TEST);

            textureId=initTexture(gl,R.drawable.stone2);//紋理ID
            cylinder=new huanCH(12f,12f,10f,2f,textureId);//建立圓柱體

//            //開啟一個執行緒自動旋轉光源
//            new Thread()
//            {
//               public void run()
//               {
//                  while(true)
//                  {
//                   lightAngle+=5;//轉動燈
//                   mRenderer.cylinder.mAngleY+=2*TOUCH_SCALE_FACTOR;//球沿Y軸轉動
//                    requestRender();//重繪畫面
//                    try
//                    {
//                       Thread.sleep(50);//休息10ms再重繪
//                    }
//                    catch(Exception e)
//                    {
//                       e.printStackTrace();
//                    }
//                  }
//               }
//            }.start();
        }
    }

    //初始化白色燈
    private void initLight(GL10 gl)
    {
        gl.glEnable(GL10.GL_LIGHTING);//允許光照
        gl.glEnable(GL10.GL_LIGHT1);//開啟1號燈

        //環境光設定
        float[] ambientParams={0.2f,0.2f,0.2f,1.0f};//光引數 RGBA
        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_AMBIENT, ambientParams,0);

        //散射光設定
        float[] diffuseParams={1f,1f,1f,1.0f};//光引數 RGBA
        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE, diffuseParams,0);

        //反射光設定
        float[] specularParams={1f,1f,1f,1.0f};//光引數 RGBA
        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_SPECULAR, specularParams,0);
    }

    //關閉燈
    private void closeLight(GL10 gl)
    {
        gl.glDisable(GL10.GL_LIGHT1);
        gl.glDisable(GL10.GL_LIGHTING);
    }

    //初始化材質
    private void initMaterial(GL10 gl)
    {
        //環境光
        float ambientMaterial[] = {248f/255f, 242f/255f, 144f/255f, 1.0f};
        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, ambientMaterial,0);
        //散射光
        float diffuseMaterial[] = {248f/255f, 242f/255f, 144f/255f, 1.0f};
        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, diffuseMaterial,0);
        //高光材質
        float specularMaterial[] = {248f/255f, 242f/255f, 144f/255f, 1.0f};
        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, specularMaterial,0);
        gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 100.0f);
    }

    //初始化紋理
    public int initTexture(GL10 gl,int drawableId)//textureId
    {
        //生成紋理ID
        int[] textures = new int[1];
        gl.glGenTextures(1, textures, 0);
        int currTextureId=textures[0];
        gl.glBindTexture(GL10.GL_TEXTURE_2D, currTextureId);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_LINEAR_MIPMAP_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_LINEAR_MIPMAP_LINEAR);
        ((GL11)gl).glTexParameterf(GL10.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL10.GL_TRUE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,GL10.GL_REPEAT);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,GL10.GL_REPEAT);



        InputStream is = this.getResources().openRawResource(drawableId);
        Bitmap bitmapTmp;
        try
        {
            bitmapTmp = BitmapFactory.decodeStream(is);
        }
        finally
        {
            try
            {
                is.close();
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
        }
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapTmp, 0);
        bitmapTmp.recycle();

        return currTextureId;
    }
}

GL_Cirque.java

package com.scout.eeeeeee;

/**
 * Created by liuguodong on 2017/10/29.
 */

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class GL_Cirque extends Activity {
    private MyGLSurfaceView mGLSurfaceView;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        mGLSurfaceView = new MyGLSurfaceView(this);
        setContentView(mGLSurfaceView);
        mGLSurfaceView.setFocusableInTouchMode(true);//設定為可觸控
        mGLSurfaceView.requestFocus();//獲取焦點
    }


    @Override
    protected void onResume() {
        super.onResume();
        mGLSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLSurfaceView.onPause();
    }
}

注意:在上述程式碼中,rSpan表示環每一份多少度,cSpan表示圓截環每一份多少度,ring_Radius表示環半徑,         circle_Radius表示圓截面半徑。

相關文章