一.OPENGL基礎
一)基礎方法講解
1.座標系
採用三維笛卡爾座標系,如圖
2.不同的繪製方法
2)索引法繪製三角形:具有索引緩衝數組
3)頂點法繪製三角形:無索引緩衝數組,直接根據頂點順序排序。
3.緩存消除
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);//消除顏色和深度緩衝
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);//正交投影 /** * ratio:視口左右兩側的x座標 * -1/1:視口上下兩側的y座標 * 1:視口最近端z座標 * 100:視口最遠端z軸座標 */ 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, 0 );
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.繪製步驟
注:使用頂點法繪製映射?