一.OPENGL基礎
一)基礎方法講解
1.坐标系
采用三維笛卡爾坐标系,如圖
2.不同的繪制方法
2)索引法繪制三角形:具有索引緩沖數組
3)頂點法繪制三角形:無索引緩沖數組,直接根據頂點順序排序。
3.緩存消除
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);//消除顔色和深度緩沖
4.矩陣模式切換
gl.glMatrixMode(GL10.GL_MODELVIEW|GL10.GL_PROJECTION|...);
gl.glLoadIdentity();//将目前模式的矩陣重置為機關矩陣
5.投影
1)正交投影
2)透視投影
Public void glFrustumf(int left,int right,int top,int bottom,int near,int far) 功能: 透視投影的設定,可視區域為[near,far],投影中心為z軸上的點(0,0,R) 參數: Left:視口左側對應的x坐标 Rigth:視口右側對應的x坐标 Top:視口上側對應的y坐标 Bottom:視口下側對應的y坐标 Near:最近端的z坐标 Far:最遠端的z坐标 相關計算: 水準方向的視角 a = 2arctg(left,near);//即left/near的反切 垂直方向的視角 a = 2arctg(top,near);// 一般-left=right = ScreenWidth/ScreenHeight; -top = bottom = 1 Left,right,top,bottom的設定對圖像的影響 原理:視角變大,視野變大。系統應該調整視口中的圖像,即縮放。 如果水準即x軸的視角變大,則說明視野更加開闊,可見的東西應該要更多,是以視圖會進行調整,即目前視口中圖像按比例縮小,這樣就可以表現出視野開闊了的效果。a增大x軸上會進行縮放,b增大會在y軸上進行縮放,near,far會在z軸上縮放。 如下的效果 正常比例的三角形 水準視角增大則x軸上圖像縮小 垂直視角增大y軸圖像縮小 水準和垂直視角都增大 |
6.視口
顯示GLSurfaceView的視窗
7.繪制相關的函數
public void drawSelf(GL10 gl){ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//啟用頂點,顔色數組,預設是關閉的,不啟用的話不能調用glDrawElements()繪制 gl.glRotatef(yAngle,0,1,0);//根據yAngle的角度值,繞y軸旋轉yAngle //gl.glRotatef(zAngle,0,0,1); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);//指明圖形繪制的坐标資料 gl.glColorPointer(4, GL10.GL_FIXED, 0, myColorBuffer);//指明頂點着色資料 gl.glDrawElements(GL10.GL_TRIANGLES, vCount, GL10.GL_UNSIGNED_BYTE, myIndexBuffer); } |
8.去除抖動
gl.glDisable(GL10.GL_DITHER);
10.Hint設定
設定GL的相關項,如設定GL透視糾正為最有效率的形式
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);//gl的訓示及模式選擇
11.設背景色
顔色區間[0-1]0表示最暗-黑色,1表示最亮-白色
gl.glClearColor(0,0,0,0);
基本圖形三角形的繪制過程
繪制流程圖如下
開始:
this.setRenderer(render); this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); |
初始化
gl.glDisable(GL10.GL_DITHER);//去除抖動 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);//gl的訓示及模式選擇,設定GL的透視糾正為最有效率 gl.glClearColor(0,0,0,0);//設定背景色 |
初始化圖形
public Triangle(){ vCount=3;//一個三角形,3個頂點 final int UNIT_SIZE=10000;//縮放比例 int []vertices=new int[] { -8*UNIT_SIZE,6*UNIT_SIZE,0, -8*UNIT_SIZE,-6*UNIT_SIZE,0, 8*UNIT_SIZE,-6*UNIT_SIZE,0 }; //建立頂點坐标資料緩存,由于不同平台位元組順序不同,資料單元不是位元組的(上面的事整型的緩存),一定要經過ByteBuffer轉換,關鍵是通過ByteOrder設定nativeOrder() ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);//一個整數四個位元組,根據最新配置設定的記憶體塊來建立一個有向的位元組緩沖 vbb.order(ByteOrder.nativeOrder());//設定這個位元組緩沖的位元組順序為本地平台的位元組順序 mVertexBuffer=vbb.asIntBuffer();//轉換為int型緩沖 mVertexBuffer.put(vertices);//向緩沖區中放入頂點坐标資料 mVertexBuffer.position(0);//設定緩沖區的起始位置 final int one=0xffff;//使用十六進制表示全色彩通道 int []colors=new int[]//頂點顔色值數組,每個頂點4個色彩值RGBA { one,0,0,0, 0,one,0,0, 0,0,one,0 }; ByteBuffer cbb=ByteBuffer.allocateDirect(colors.length*4); cbb.order(ByteOrder.nativeOrder()); myColorBuffer=cbb.asIntBuffer(); myColorBuffer.put(colors); myColorBuffer.position(0); //為三角形構造索引資料初始化 iCount=3; byte []indices=new byte[] { 0,1,2 }; //建立三角形構造索引資料緩沖 myIndexBuffer=ByteBuffer.allocateDirect(indices.length); myIndexBuffer.put(indices); myIndexBuffer.position(0); } |
繪制初始化
public void onSurfaceChanged(GL10 gl, int width, int height) { // TODO Auto-generated method stub gl.glViewport(0, 0, width, height);//設定視口 gl.glMatrixMode(GL10.GL_PROJECTION);//切換為投影矩陣 gl.glLoadIdentity();//将投影矩陣重置為機關矩陣 float ratio=(float)width/height; //gl.glOrthof(-ratio, ratio, -1, 1, 1, 10);//正交投影 gl.glFrustumf(-ratio, ratio, -1, 1, 1, 100);//設定投影方式為透視投影 } |
繪制
public void onDrawFrame(GL10 gl) { // TODO Auto-generated method stub gl.glEnable(GL10.GL_CULL_FACE); //gl.glCullFace(GL10.GL_FRONT);//繪制正面 //gl.glFrontFace(GL10.GL_CCW);//設定逆時針為正面 gl.glShadeModel(GL10.GL_SMOOTH);//着色模型為光滑着色,預設 gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);//消除顔色和深度緩沖 gl.glMatrixMode(GL10.GL_MODELVIEW);//選用視圖舉證模式 gl.glLoadIdentity();//充值矩陣 gl.glTranslatef(0, 0, -2.0f);// tr.drawSelf(gl);//繪制 } |
public void drawSelf(GL10 gl){ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//啟用頂點,顔色數組,預設沒有開啟,是以預設不能調用glDrawElements()繪制 gl.glRotatef(yAngle,0,1,0);//根據yAngle的角度值,繞向量(0,1,0)y軸旋轉yAngle //gl.glRotatef(zAngle,0,0,1); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);//指明圖形繪制的坐标資料 gl.glColorPointer(4, GL10.GL_FIXED, 0, myColorBuffer);//指明頂點着色資料 gl.glDrawElements(GL10.GL_TRIANGLES, vCount, GL10.GL_UNSIGNED_BYTE, myIndexBuffer);//繪制 } |
繪制基本圖形
Opengl es 隻支援三角形繪制,是以其他的基本圖形都隻有通過三角形來拼湊而成,其中注意頂點的個數問題,如果頂點數目不對将不能正确繪制出圖形。
如正方形:正方形由2個三角形組成,每個三角形3個頂點,是以一個正方形共需要6個頂點數。
注:
1.如果使用索引法繪制圖形,建議使用索引數量來确定共多少個頂點,帶入glDrawElements();
2.指定頂點的時候如果使用的是int,則需要指定縮放指數10000,glVertexPointer()對應type是GL_FIXED;如果使用時float,則type是GL_FLOAT,而且不需要指定type
繪制三維圖形
球體的繪制
原理:按照地球的經緯度來繪制球體,以赤道把球南北分為N,S極。以子午線(經度為0)左右為界分為西經和東經,
緯度:N極[0~90],S極為[-90~0];
經度:W[0~-180],E[0~180]
然後按照指定的切分角度angleSpan橫縱分别切分。的切分行數row = 180/angleSpan;col = 360/angleSpan.切分後供row*col個長方形,2*row*col個三角形。
坐标計算:
final int angleSpan=18; //将小球進行機關切分的角度 for (int vAngle = -90; vAngle <= 90; vAngle=vAngle+angleSpan) { //垂直方向angleSpan度一份 for (int hAngle = 0; hAngle <360; hAngle=hAngle+angleSpan ) { //水準方向angleSpan度一份 //縱向橫向各到一個角度後計算對應的此點在球面上的坐标 double xozLength=scale*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));//xozLength是上圖中的BO,即半徑投影在xoz面上的長度 int x=(int) (xozLength*Math.cos(Math.toRadians(hAngle)));//hAngle是緯度,vAngle是經度 int y=(int) (xozLength*Math.sin(Math.toRadians(hAngle))) ; int z=(int) (scale*UNIT_SIZE*Math.sin(Math.toRadians(vAngle))); alVertex.add(x); alVertex.add(y); alVertex.add(z); } } |
索引
int row=(180/angleSpan)+1;//球面切分的行數 int col=360/angleSpan;//球面切分的列數 for(int i=0;i<row;i++){//對每一行循環 if(i>0&&i<row-1){ //中間行 for(int j=-1;j<col;j++){ //中間行的兩個相鄰點與下一行的對應點構成三角形 int k=i*col+j;//目前點的索引值求解,目前點在i行j列 alIndex.add(k+rol); alIndex.add(k+1); alIndex.add(k); } for(int j=0;j<col+1;j++){ //中間行的兩個相鄰點與上一行的對應點構成三角形 int k=i*col+j; alIndex.add(k-col); alIndex.add(k-1); alIndex.add(k); }}} |
繪制球的步驟
注:必須在調用glDrawElements()之前先消除緩存即上圖的步驟5,否則會圖像會亂
圖像初始化
public void initBuffer() { // TODO Auto-generated method stub getVertex();//計算出所有坐标的xyz值存入vertex數組中,計算出所有索引值存入index數組中 ByteBuffer bbf = ByteBuffer.allocateDirect(vCount * 4 *3);//每個頂點由3個int資料組成,(int) = 4*(byte) bbf.order(ByteOrder.nativeOrder());//轉化成本地位元組順序 mVertexBuffer = bbf.asIntBuffer(); mVertexBuffer.put(vertex); mVertexBuffer.position(0);//頂點坐标數組緩沖 ByteBuffer nbb = ByteBuffer.allocateDirect(vCount*4*3);//法向量緩沖 nbb.order(ByteOrder.nativeOrder()); mNormalBuffer = nbb.asIntBuffer(); mNormalBuffer.put(vertex); mNormalBuffer.position(0); mIndexBuffer = ByteBuffer.allocateDirect(iCount); mIndexBuffer.order(ByteOrder.nativeOrder()); mIndexBuffer.put(index); mIndexBuffer.position(0); } public void getVertex() { // TODO Auto-generated method stub int angleSpan=16;//球面切分角度 int row = 180/angleSpan +1;//橫向切面的數量 int col= 360/angleSpan+1;//縱向切的次數,切面數量 float vAngle=0,hAngle = 0;//緯度和經度 ArrayList<Integer>alVertex = new ArrayList<Integer>(); for(vAngle =-90;vAngle<=90;vAngle+=angleSpan){ for(hAngle = 0;hAngle<360;hAngle+=angleSpan){ double xozLength = R * UNIT_SIZE * Math.cos(Math.toRadians(vAngle));//xoz面的投影長度 int x = (int) (xozLength * Math.cos(Math.toRadians(hAngle))); int z = (int) (xozLength * Math.sin(Math.toRadians(hAngle))); int y = (int) (R * UNIT_SIZE * Math.sin(Math.toRadians(vAngle))); //for(int i = 0;i<3;i++){ alVertex.add(x); alVertex.add(y); alVertex.add(z);//将xyz頂點資料存入vertex數組中 //} } } vCount = alVertex.size()/3; vertex=new int[vCount*3]; for(int i = 0;i<alVertex.size();i++){ vertex[i]= alVertex.get(i); } ArrayList<Integer>alIndex = new ArrayList<Integer>(); for(int i = 0;i<row;i++){ if(i>0&&i<row-1){ for(int j = 0;j<col;j++){ int c = i*col+j;//目前點的索引值 alIndex.add(c+col); alIndex.add(c+1); alIndex.add(c);//目前點的與上方和右方的相鄰點構成目前點上面一個三角形 } for(int j = 0;j<col;j++){ int c = i*col+j;//目前點的索引值 alIndex.add(c-col); alIndex.add(c-1); alIndex.add(c);//目前點的與下方和左方的相鄰點構成目前點下面的一個三角形 } }} iCount = alIndex.size(); index = new byte[iCount]; for(int i = 0;i<iCount;i++){ index[i] = alIndex.get(i).byteValue(); } } |
繪制初始化
@Override public void onSurfaceChanged(GL10 gl, int width, int height) { // TODO Auto-generated method stub 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);//投影方式 ball = new Ball(); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // TODO Auto-generated method stub gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glDisable(GL10.GL_DITHER); gl.glShadeModel(GL10.GL_SMOOTH);//着色模式為光滑着色,FLAT為平滑着色,反射光線是是單一反射 gl.glClearColor(0, 0, 0, 0);//背景為黑色 //啟用深度測試,關閉了有些像素将不能繪制出來 gl.glEnable(GL10.GL_DEPTH_TEST); } |
繪制
@Override public void onDrawFrame(GL10 gl) { // TODO Auto-generated method stub gl.glShadeModel(GL10.GL_SMOOTH); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glEnable(GL10.GL_LIGHTING);//開燈 initLight(gl); initMeterial(gl); //設定Light0光源的位置 float[] positionParamsGreen={-6,6,-6,1};//最後的1表示是定位光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, positionParamsGreen,0); ball.drawSelf(gl); gl.glLoadIdentity(); } public void initLight(GL10 gl) { // TODO Auto-generated method stub gl.glEnable(GL10.GL_LIGHT0); //環境光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, new float[]{0.4f,0.4f,0.4f,1.0f},0); //散射光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, new float[]{0.5f,0.5f,0.5f,1.0f},0); //反射光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, new float[]{1.0f,1.0f,1.0f,1.0f},0); //高光反射 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SHININESS, new float[]{1.5f,1.5f,1.5f,1.0f},0); } public void initMeterial(GL10 gl) { // TODO Auto-generated method stub gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, new float[]{0.4f,0.4f,0.4f,1.0f},0);//環境光為白色 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, new float[]{1.0f,0.0f,0.0f,1.0f},0);//三色光為紅色 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, new float[]{0.0f,1.0f,0.0f,1.0f},0);//反射光為綠色 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, new float[]{1.5f},0);//高光反射區,光強越大,範圍越小 } |
正方體的繪制
頂點法--使用面法向量
CubeVertex.java public class CubeVertex { private FloatBuffer mVertexBuffer;//頂點坐标資料緩沖 private FloatBuffer mNormalBuffer;//法向量資料緩沖 public float mOffsetX; public float mOffsetY;//繞Y軸旋轉 float scale; //立方體高度 int vCount;//頂點數量 public CubeVertex(float scale,float length,float width) { this.scale=scale; vCount=36; float UNIT_SIZE=0.5f; float UNIT_HIGHT=0.5f; float[] verteices= { //頂面 -UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, //後面 -UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, //前面 -UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, -UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, -UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, //下面 -UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, -UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, -UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, //左面 -UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, -UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width, -UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, //右面 UNIT_SIZE*length,UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,-UNIT_SIZE*width, UNIT_SIZE*length,UNIT_HIGHT*scale,UNIT_SIZE*width, UNIT_SIZE*length,-UNIT_HIGHT*scale,UNIT_SIZE*width }; ByteBuffer vbb=ByteBuffer.allocateDirect(verteices.length*4); //建立頂點坐标資料緩沖 vbb.order(ByteOrder.nativeOrder());//設定位元組順序 mVertexBuffer=vbb.asFloatBuffer();//轉換為float型緩沖 mVertexBuffer.put(verteices);//向緩沖區中放入頂點坐标資料 mVertexBuffer.position(0);//設定緩沖區起始位置 float[] normals= { //頂面頂點法向量 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, //後面頂點法向量 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1, //前面頂點法向量 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, //下面頂點法向量 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0, //左面頂點法向量 -1,0,0, -1,0,0, -1,0,0, -1,0,0, -1,0,0, -1,0,0, //右面頂點法向量 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0 }; ByteBuffer nbb=ByteBuffer.allocateDirect(normals.length*4);//建立顔色坐标資料緩沖 nbb.order(ByteOrder.nativeOrder());//設定位元組順序 mNormalBuffer=nbb.asFloatBuffer();//轉換為float型緩沖 mNormalBuffer.put(normals);//向緩沖區中放入頂點坐标資料 mNormalBuffer.position(0);//設定緩沖區起始位置 } public void drawSelf(GL10 gl) { gl.glRotatef(mOffsetX, 1, 0, 0); gl.glRotatef(mOffsetY, 0, 1, 0); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//允許使用頂點數組 //為畫筆指定頂點坐标資料 gl.glVertexPointer ( 3, //每個頂點的坐标數量為3 xyz GL10.GL_FLOAT, //頂點坐标值的類型為 GL_FIXED 0, //連續頂點坐标資料之間的間隔 mVertexBuffer //頂點坐标資料 ); gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);//允許使用法向量數組 gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormalBuffer);//為畫筆指定頂點法向量資料 gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vCount);//繪制圖形 } } |
MySurfaceView.java public class MySurfaceView extends GLSurfaceView{ private SceneRenderer mRenderer;//聲明渲染器 float cx=0;//錄影機x位置 float cy=3;//錄影機y位置 float cz=40;//錄影機z位置 float tx=0;目标點x位置 float ty=0;//目标點y位置 float tz=0;//目标點z位置 public MySurfaceView(Context context) { super(context); // TODO Auto-generated constructor stub mRenderer = new SceneRenderer(); //建立場景渲染器 setRenderer(mRenderer); //設定渲染器 setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//設定渲染模式為主動渲染 } private class SceneRenderer implements GLSurfaceView.Renderer { CubeVertex cubeVertex;//聲明頂點法立方體 @Override public void onDrawFrame(GL10 gl) { // TODO Auto-generated method stub //采用平滑着色 gl.glShadeModel(GL10.GL_SMOOTH); //設定為打開背面剪裁 gl.glEnable(GL10.GL_CULL_FACE); //清除顔色緩存于深度緩存 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); //設定目前矩陣為模式矩陣 gl.glMatrixMode(GL10.GL_MODELVIEW); //設定目前矩陣為機關矩陣 gl.glLoadIdentity(); //設定camera位置 GLU.gluLookAt ( gl, cx, //人眼位置的X cy, //人眼位置的Y cz, //人眼位置的Z tx, //人眼球看的點X ty, //人眼球看的點Y tz, //人眼球看的點Z 0, 1, ); gl.glPushMatrix();//擷取坐标系 gl.glRotatef(45, 0, 1, 0);//繞Y軸旋轉45度 gl.glRotatef(45, 1, 0, 0);//繞X軸旋轉45度 cubeVertex.drawSelf(gl);//繪制立方體 gl.glPopMatrix();//恢複坐标系 } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // TODO Auto-generated method stub //設定視窗大小及位置 gl.glViewport(0, 0, width, height); //設定目前矩陣為投影矩陣 gl.glMatrixMode(GL10.GL_PROJECTION); //設定目前矩陣為機關矩陣 gl.glLoadIdentity(); //計算透視投影的比例 float ratio = (float) width / height; //調用此方法計算産生透視投影矩陣 gl.glFrustumf(-ratio, ratio, -1.0f, 1.0f, 8, 100); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // TODO Auto-generated method stub //關閉抗抖動 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); //設定為打開背面剪裁 gl.glEnable(GL10.GL_CULL_FACE); //允許使用光照 gl.glEnable(GL10.GL_LIGHTING); //初始化光源 initLight(gl); //初始化材質光源 initMaterial(gl); cubeVertex=new CubeVertex(2.5f,2.5f,2.5f); new Thread() { public void run() { while(true) { try { sleep(100);//睡眠0.1秒 }catch(Exception e) { e.printStackTrace(); } cubeVertex.mOffsetY+=2.0f;//每次旋轉 } } }.start(); } } public void initLight(GL10 gl) { // TODO Auto-generated method stub gl.glEnable(GL10.GL_LIGHT0);//打開0号光源 //環境光設定 float[] ambientParams={0.46f,0.21f,0.05f,1.0f};//光參數 RGBA gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, ambientParams,0); //散射光設定 float[] diffuseParams={0.46f,0.21f,0.05f,1.0f}; gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, diffuseParams,0); //鏡面光設定 float[] specularParams={0.46f,0.21f,0.05f,1.0f}; gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, specularParams,0); //指定光源位置 float[] directionParams={-1f,1f,1f,0};//定向光 gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, directionParams,0); } public void initMaterial(GL10 gl) { // TODO Auto-generated method stub //環境光為白色材質 float ambientMaterial[] = {0.6f, 0.6f, 0.6f, 1.0f}; gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, ambientMaterial,0); //散射光為白色材質 float diffuseMaterial[] = {1.0f, 1.0f, 1.0f, 1.0f}; gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, diffuseMaterial,0); //高光材質為白色 float specularMaterial[] = {1f, 1.0f, 1f, 1.0f}; gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, specularMaterial,0); } } |
三)光照
1. 光照模型
環境光——經過多次反射而來的光稱為環境光,無法确定其最初的方向,但當特定的光源關閉後,它們将消失.
全局環境光——每個光源都能對場景提供環境光。此外,還有一個環境光,它不來自任何特定的光源,稱之為全局環境光。這也就是為什麼我們不加任何自定義光源,也能看見繪制的物體的原因。
散射光——來自同一方向,照射到物體表面後,将沿各個方向均勻反射,是以,無論從哪個方向觀察,表面的亮度都相同. 主要通過設定GLfloat light_diffuse[] = { 0.0, 1.0, 0.0, 1.0 }來發射綠光鏡面反射光——來自特定方向,也被反射到特定方向.鏡面反射度與之相關.
材質發射光——用于模拟發光物體.在OpenGL光照模型中,表面的發射光增加了物體的亮度,它不受光源的影響,另外,發射光不會給整個場景中增加光線.
當光照射到一個物體表面上時,會出現三種情形。首先,光可以通過物體表面向空間反射, 産生反射光。其次,對于透明體,光可以穿透該物體并從另一端射出,産生透射光。最後,部分光将被物體表面吸收而轉換成熱。在上述三部分光中,僅僅是透射光和反射光能夠進入人眼産生視覺效果。這裡介紹的簡單光照模型隻考慮被照明物體表面的反射光影響,假定物體表面光滑不透明且由理想材料構成,環境假設為由白光照明。
一般來說,反射光可以分成三個分量,即環境反射、漫反射和鏡面反射。環境反射分量假定入射光均勻地從周圍環境入射至景物表面并等量地向各個方向反射出去,通常物體表面還會受到從周圍環境來的反射光(如來自地面、天空、牆壁等的反射光)的照射,這些光常統稱為環境光(Ambient Light);漫反射分量表示特定光源在景物表面的反射光中那些向空間各方向均勻反射出去的光,這些光常稱為漫射光(Diffuse Light);鏡面反射光為朝一定方向的反射光,如一個點光源照射一個金屬球時會在球面上形成一塊特别亮的區域,呈現所謂“高光(Highlight)”,它是光源在金屬球面上産生的鏡面反射光(Specular Light)。對于較光滑物體,其鏡面反射光的高光區域小而亮;相反,粗糙表面的鏡面反射光呈發散狀态,其高光區域大而不亮。
2. OpenGL 光照模效果的原理
OpenGL的光照模型是用來模拟現實生活中的光照的。它根據頂點的法線向量和光源的位置決定頂點的明暗程度,根據頂點的材質和光源中三原色的成分來決定物體将表現出怎樣的顔色。
值得一提的是材質。OpenGL中的材質并非我們平常所說的組成物體的元素(如木材、金屬材質),而是指一個物體對不同顔色的光的反射和吸收程度。比如,在光照系統中,如果一個物體表現為紅色,則是因為這個物體吸收了從光源放射出來的綠色和藍色光,而将絕大多數紅色的光反射了出來。正因如此,一旦你開啟了光照系統,就要通過指定物體的材質來決定這個物體是什麼顔色。既然這樣,你可能會想到怎樣表現類似金屬、玻璃等物質質感,但這些除了要使用光照系統并為它們指定合适的材質外,還要使用紋理貼圖來表現質感。
使用OpenGL的光照模型包括以下幾個步驟:
· 設定光源的種類、位置和方向(對于平行光源)
· 為每個圖元的每個頂點指定它的法線向量
· 為各個圖元指定它的材質
2.1 局部視點和無窮遠視點
視點位置能影響鏡面反射中高光的計算。
即頂點的高光強度不僅取決于頂點法向量,而且取決于頂點到光源的方向以及頂點到視點的方向。
對于無窮遠視點,視點到任何頂點的方向都是相同的。
而對于局部視點,視點到每個頂點的方向是不同的。
是以需要計算視點到每個頂點的方向,因而性能降低了,但效果品質提高了。
預設時,使用的是無窮遠視點。
glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER , GL_TRUE ); // 将視點設定為局部視點
glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER , GL_FALSE ); // 将視點設定為無窮遠視點
2.2 雙面光照
光照計算是對所有多邊形(包括其正面和背面)進行的。而一般情況下,隻設定正面光照條件,而忽略背面。但有時候需要看到物體内側的光照效果,這就需要用到雙面光照。
glLightModeli( LIGHT_MODEL_TWO_SIDE , GL_TRUE ); // 啟用雙面光照
glLightModeli( LIGHT_MODEL_TWO_SIDE , GL_FALSE ); // 禁用雙面光照
2.3 光源衰減
真實的光,離光源越遠則光強越小。環境光、漫反射光和鏡面光的強度都衰減,輻射光和全局環境光的強度不衰減。
方向光源是無窮遠光源,是以距離對光強沒有影響,是以方向光沒有衰減,但位置光則有衰減。
OpenGL是通過光源光強乘以衰減系數來計算衰減光照的。
衰減系數 = 1 / ( Kc + Kl*d + Kq*d*d ) ,d = 光源位置到物體頂點的距離
Kc = GL_CONSTANT_ATTENUATION 常數衰減因子
Kl = GL_LINER_ATTENUATION 線性衰減因子
Kq = GL_QUADRATIC_ATTENUATION 二次衰減因子,預設時衰減因子為( 1 , 0 , 0 ) 即不進行衰減
glLightf( GL_LIGHT0 , GL_CONSTANT_ATTENUATION , 1.0 );
glLightf( GL_LIGHT0 , GL_LINEAR_ATTENUATION , 0.0 );
glLightf( GL_LIGHT0 , GL_QUADRATIC_ATTENUATION , 0.0 );
3.設定光源
3.1 光源的種類
環境光
環境光是一種無處不在的光。環境光源放出的光線被認為來自任何方向。是以,當你僅為場景指定環境光時,所有的物體無論法向量如何,都将表現為同樣的明暗程度。
點光源
由這種光源放出的光線來自同一點,且方向輻射自四面八方。
平行光
平行光又稱鏡面光,這種光線是互相平行的。從手電筒、太陽等物體射出的光線都屬于平行光。
聚光燈
這種光源的光線從一個錐體中射出,在被照射的物體上産生聚光的效果。使用這種光源需要指定光的射出方向以及錐體的頂角α。
3.2 光的成分
對于每一種光源,都有漫射光和平行光兩種成分。在OpenGL中,環境光也被作為一種特殊的光源的成分來看待。漫射光是指在光源中能夠被漫反射的光的顔色成分(白色則包含所有顔色),而平行光是指光源中所有能夠被鏡面反射的光的顔色成分。通過指定這兩種成分的顔色,就能決定光源是平行光源還是點光源。
3.3 設定光源成分
OpenGL可以同時為我們提供8個有效的光源。也就是說,我們最多可以同時啟用8個光源。它們分别是GL_LIGHT0,GL_LIGHT1,GL_LIGHT2 …… 其中,GL_LIGHT0是最特殊的一個光源。我們可以為GL_LIGHT0指定環境光成分。
LIGHT0:白色光
LIGHT1:紅色
LIGHT2:藍色
LIGHT3:綠色
LIGHT4:LIGHT5:LIGHT6:LIGHT7黃色
3.3.1 設定環境光
對于GL_LIGHT0,我們可以為其指定環境光成分。 調用
glLightfv(GL_LIGHT0,GL_AMBIENT,@ambientLight);
方法不懂見函數詳解
3.3.2 設定漫射光成分
通過對漫射光成分的設定,我們可以産生一個點光源。方法和設定環境光成分相似,隻需調用
glLightfv(GL_LIGHT0,GL_DIFFUSE,@DiffuseLight);
即可。其中DiffuseLight是漫射光的顔色成分。一般情況下也為(1,1,1,1)。
3.3.3 設定鏡面光成分
通過對鏡面光成分的設定,我們可以産生一個平行光源。方法和設定漫射光成分相似,隻需調用
glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecularLight);
即可。其中SpecularLight是漫射光的顔色成分。可以根據不同需要指定不同的顔色。
3.4 設定光源的位置
對于點光源和平行光源,我們常常需要指定光源的位置來産生需要的效果。方法仍然是調用glLightfv函數,僅僅是換換參數而已:
glLightfv(GL_LIGHT0,GL_POSITION,@LightPosition);
其中,LightPosition也是一個四維數組,四維數組的前3項依次為光源位置的X,Y,Z分量,第四個值很特殊,一般為1或-1。當LightPosition[4]=-1的時候,表示光源位于距離場景無限遠的地方,無論前面設定的X,Y,Z是什麼值。當LightPosition[4]=1時,光源的位置就是前三項所指定的位置。
3.5 建立聚光燈
聚光位置光源可以定義成聚光形式,即将光的形狀限制在一個圓錐内。用來模拟現實世界中的聚光燈。聚光的具體使用步驟如下:
1 設定聚光源位置 GLfloat light_position[]={ 1.0 , 1.0 , 1.0 , 1.0 };
glLightfv( GL_LIGHT0 , LIGHT_POSITION , light_position );
2 設定聚光最大散布角 聚光最大散布角就是光錐的軸與中心線的夾角,也就是光錐頂角的一半。
聚光最大散布角可以選擇在[ 0.0 , 90.0 ]之間的值,以及一個特定值:180.0。預設時最大散布角為180.0,即頂角為360度,光源照向所有方向。即一個點光源。 glLightf( GL_LIGHT0,GL_SPOT_CUTOFF , 45.0 );
3 設定聚光方向 聚光方向決定光錐的軸,預設值為( 0.0 , 0.0 , -1.0 ) , 即指向負Z軸。 GLfloat spot_direction[]={ -1.0 , -1.0 , 0.0 };
glLightfv( GL_LIGHT0 , GL_SPOT_DIRECTION , spot_direction );
4 設定聚光指數 聚光指數控制光的集中程度,光錐中心的光強最大,越靠邊的光強越小。
預設時為0,即均勻照射。 glLightf( GL_LIGHT0 , GL_SPOT_EXPONENT , 2.0 );
4. 材質
4.1 材質顔色
OpenGL用材料對光的紅、綠、藍三原色的反射率來近似定義材料的顔色。象光源一樣,材料顔色也分成環境、漫反射和鏡面反射成分,它們決定了材料對環境光、漫反射光和鏡面反射光的反射程度。在進行光照計算時,材料對環境光的反射率與每個進入光源的環境光結合,對漫反射光的反射率與每個進入光源的漫反射光結合,對鏡面光的反射率與每個進入光源的鏡面反射光結合。對環境光與漫反射光的反射程度決定了材料的顔色,并且它們很相似。對鏡面反射光的反射率通常是白色或灰色(即對鏡面反射光中紅、綠、藍的反射率相同)。鏡面反射高光最亮的地方将變成具有光源鏡面光強度的顔色。例如一個光亮的紅色塑膠球,球的大部分表現為紅色,光亮的高光将是白色的。
4.2 材質定義
材質的定義與光源的定義類似。其函數為:
void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);
定義光照計算中用到的目前材質。face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明目前材質應該應用到物體的哪一個面上;pname說明一個特定的材質;
param是材質的具體數值,若函數為向量形式,則param是一組值的指針,反之為參數值本身。非向量形式僅用于設定GL_SHINESS。
pname參數值具體内容見下表。另外,參數GL_AMBIENT_AND_DIFFUSE表示可以用相同的RGB值設定環境光顔色和漫反射光顔色。
___________________________________________________________________
參數名 預設值 說 明___________________________________________________________________
GL_AMBIENT (0.2,0.2,0.2,1.0) 材料的環境光顔色
GL_DIFFUSE (0.8,0.8,0.8,1.0) 材料的漫反射光顔色
GL_AMBIENT_AND_DIFFUSE 材料的環境光和漫反射光顔色
GL_SPECULAR (0.0,0.0,0.0,1.0) 材料的鏡面反射光顔色
GL_SHINESS 0.0 鏡面指數(光亮度)
GL_EMISSION (0.0,0.0,0.0,1.0) 材料的輻射光顔色
GL_COLOR_INDEXES (0,1,1) 材料的環境光、漫反射光和鏡面光顔色__________________________________________________________________
4.3 材質RGB值和光源RGB值的關系
材質的顔色與光源的顔色有些不同。對于光源,R、G、B值等于R、G、B對其最大強度的百分比。若光源顔色的R、G、B值都是1.0,則是最強的白光;若值變為0.5,顔色仍為白色,但強度為原來的一半,于是表現為灰色;若R=G=1.0,B=0.0,則光源為黃色。對于材質,R、G、B值為材質對光的R、G、B成分的反射率。比如,一種材質的R=1.0,G=0.5,B=0.0,則材質反射全部的紅色成分,一半的綠色成分,不反射藍色成分。也就是說,若OpenGL的光源顔色為(LR,LG,LB),材質顔色為(MR,MG,MB),那麼,在忽略所有其他反射效果的情況下,最終到達眼睛的光的顔色為(LR*MR,LG*MG,LB*MB)。同樣,如果有兩束光,相應的值分别為(R1,G1,B1)和(R2,G2,B2),則OpenGL将各個顔色成分相加,得到(R1+R2,G1+G2,B1+B2),若任一成分的和值大于1(超出了裝置所能顯示的亮度)則約簡到1.0。
光源
1.開啟與關閉
gl.glEnable(GL10.LIGHTING)
gl.glDisable(GL10.LIGHTING)
2.數量設定
gl.glEnalbe(GL10.LIGHTING0~7)//最多開8盞燈
3.定向光
定向光:光源在無窮遠處,光的方向相同且平行,
gl.glLightf(int light,int pname,float[]params,int offset)
參數:
Light:燈的標明,GL_LIGHT0~7
pname:光源屬性,定向光是GL_POSITION,
params:(x,y,z,w)共4個值,前3個表示定向光的方向向量,指向原點,w=0表示定向光,w = 1表示定位光
Offset:params數組中第一個相對第0個的偏移量
4.定位光
定位光:指定光源位置的光
函數同上glLightf(),不過params中(x,y,z,w)表示的是光源的坐标,w = 1;
5.光源顔色
1)環境光GL_AMBIENT經過多次反射而來的光稱為環境光,無法确定其最初的方向,但當特定的光源關閉後,它們将消失.
2)散射光GL_DIFFUSE來自同一方向,照射到物體表面後,将沿各個方向均勻反射,是以,無論從哪個方向觀察,表面的亮度都相同.
3)反射光GL_SPECULAR來自特定方向,也被反射到特定方向.鏡面反射度與之相關.
4)自發光GL_EMMISION
6.設定光的顔色
gl.glLightf(int light,int pname,float[]params,int offset)
pname指定光源顔色,p
arams[]=(R,G,B,A)指定燈光顔色的rgba
材料反光屬性-材質和法向量
1.法向量
為每個頂點設定法向量決定了光照是的反射情況,如果沒有為頂點設定法向量,則光照系統不能正常工作。
法向量就是三維物體表面的法線方向,用向量表示
2.面法向量
即每個面的法向量,根據頂點的數量來确定共多少個面法向量,如正方體共6各面,每個面4個頂點,是以共4*6 = 24個面法向量。
3.點平均法向量
由該頂點相鄰的幾個面的法向量合成,如下圖
光源設定和材質設定相同的環境光和散射光和反射光是的表現
1)光源設定
什麼顔色的光照射到白色的材質上則顯示什麼顔色
環境光為紅色,其他為白色 散射光為紅色,其他為白色 反射光為紅色,其他為白色
2)材質設定
白光射來,什麼顔色的材質顯示什麼顔色
環境光為紅色,其他為白色 散射光為紅色,其他為白色 反射光為紅色,其他為白色
繪制注意事項集合
1.視角
視角變大,視野變大。系統應該調整視口中的圖像,即縮放。
如果水準即x軸的視角變大,則說明視野更加開闊,可見的東西應該要更多,是以視圖會進行調整,即目前視口中圖像按比例縮小,這樣就可以表現出視野開闊了的效果。a增大x軸上會進行縮放,b增大會在y軸上進行縮放,near,far會在z軸上縮放。
2.繪制方式
1.如果使用索引法繪制圖形,建議使用索引數量來确定共多少個頂點,帶入glDrawElements();
2.指定頂點的時候如果使用的是int,則需要指定縮放指數10000,glVertexPointer()對應type是GL_FIXED;如果使用時float,則type是GL_FLOAT,而且不需要指定type
3.調用glDrawElements()之前
·必須在調用glDrawElements()之前先消除緩存,否則會圖像會亂
4.繪制繞軸運動的物體
如果想要繪制繞y軸自動旋轉的球體,使得坐标系繞y軸轉動即可,調用gl.glRotatef()。
5.繪制正面方向選取(背面剪裁)
調用glFrontFace(mode)既可以選擇正面的方向,預設是逆時針==GL_CCW,順時針方向是GL_CW,
6.索引法繪制圖形時索引的順序
如果正面是逆時針則索引數組按照逆時針構成三角形,如果順時針則按順時針構成三角形。如下:0
逆時針:(0,1,2)構成三角形V0V1V2,(2,1,3)構成三角形V2V1V3
順時針則構不成三角形
繪制哪一個面,那麼就要将該面當成正面那樣繪制,否則會出錯
四)紋理映射
1.須知
紋理坐标系
坐标範圍[0~1],如下三角形的st紋理坐标是(0,1,0,.5,0,1,0)
2D:ST坐标
3D:STR坐标
2.繪制步驟
注:使用頂點法繪制映射?