我常給一些人的建議:如果條件不錯,就不要來做程序員了,因爲這不是人乾的事!程序員睡覺的時候也是清潔工人開始掃馬路的時候!
廢話不多說,自己也是作爲學習筆記而已,也是督促自己,因爲如果僅僅運行一個例子很簡單.自己研究這個當然也是需要應用到一定背景下的.
android APP如果需要使用opengl製圖,如果在java層實現,一般是GLSurfaceView來顯示出opengl製圖,GLSurfaceView的特點:
1.管理一個平面,這個平面是一個特殊的內存塊,它可以和android視圖系統混合.
2.管理一個EGL顯示,它能夠讓OpenGL渲染到一個平面.
3.接受一個用戶提供的實際顯示的Renderer對象.
4.使用一個專用線程去渲染從而和UI線程解耦.
5.支持on-demand 和連續的渲染.
6.可選的包,追蹤 和/或者錯誤檢查這個渲染器的OpenGL調用.
一個GLSurfaceView 一定要注意,這個activity的paused 和resumed 兩種狀態.當這個activity處於pauses 狀態時,GLSurfaceView 客戶端需要去調用 onPause() 方法,當activity處於resumed 狀態時,GLSurfaceView 客戶端也需要去調用 onResume() 方法.
下面新建一個使用opengl的工程,在android studio中.
<1> : 新建一個DurianOpenGL1工程,下面直接貼代碼:
package org.durian.durianopengl1;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import org.durian.durianopengl1.gl.Durian3DRender;
import org.durian.durianopengl1.gl.DurianGLRender;
import org.durian.durianopengl1.gl.DurianPhotoRender;
import org.durian.durianopengl1.gl.DurianRotateRender;
import org.durian.durianopengl1.gl.DurianTextureRender;
import org.durian.durianopengl1.input.DurianInputSurfaceView;
import org.durian.durianopengl1.texturefilter.DurianTextureFilterSurfaceView;
public class DurianMainActivity extends Activity {
private GLSurfaceView glview;
private DurianInputSurfaceView glinputview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glview=new GLSurfaceView(this);
//glinputview=new DurianInputSurfaceView(this);
//glview.setRenderer(new DurianPhotoRender(this)/*new DurianTextureRender(this)*//*new Durian3DRender(this)*//*new DurianRotateRender(this)*//*new DurianGLRender(this)*/);
glview.setRenderer(new DurianGLRender(this));
setContentView(/*new DurianTextureFilterSurfaceView(this)*//*glinputview*/glview/*R.layout.activity_durian_main*/);
}
@Override
protected void onResume() {
super.onResume();
glview.onResume();
}
@Override
protected void onPause() {
super.onPause();
glview.onPause();
}
}
注意上面Activity的onResume和onPause兩個週期,GLSurfaceView也需要根據Activity做相應的調整.
下面是渲染器Renderer:
package org.durian.durianopengl1.gl;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import org.durian.durianopengl1.draw2d.Square;
import org.durian.durianopengl1.draw2d.Triangle;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
* Created by Administrator on 2016/4/11.
*/
public class DurianGLRender implements GLSurfaceView.Renderer {
private Context mContext;
private Triangle triangle;
private Square square;
private org.durian.durianopengl1.draw2d.color.Triangle ctriangle;
private org.durian.durianopengl1.draw2d.color.Square csquare;
public DurianGLRender(Context context){
mContext=context;
triangle=new Triangle();
square=new Square();
//ctriangle=new org.durian.durianopengl1.draw2d.color.Triangle();
//csquare=new org.durian.durianopengl1.draw2d.color.Square();
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f,0.0f,0.0f,1.0f);
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_NICEST);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glDisable(GL10.GL_DITHER);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if(height==0){
height=1;
}
float aspect=(float)width/height;
gl.glViewport(0,0,width,height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl,45,aspect,0.1f,100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(-3.0f,0.0f,-6.0f);
triangle.draw(gl);
gl.glTranslatef(3.0f,0.0f,1.0f);
square.draw(gl);
gl.glTranslatef(-5.0f,0.0f,-6.0f);
//ctriangle.draw(gl);
gl.glTranslatef(5.0f,0.0f,1.0f);
//csquare.draw(gl);
}
}
這個類中其中有一個Triangle和Square的類,一個是繪製邊型,一個是方形的.
實現Renderer類就要實現它的三個基本方法.
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
這個方法在Renderer創建的時候就會運行,一般在生命週期中只會運行一次.
這個方法一般是做一些初始化的工作.
gl.glClearColor(0f, 0f, 0f, 0f);
設置清除屏幕時所用的顏色。如果您對色彩的工作原理不清楚的話,我快速解釋一下。色彩值的範圍從0.0f到1.0f。0.0f代表最黑的情況,1.0f就是最亮的情況。glClearColor 後的第一個參數是Red Intensity(紅色分量),第二個是綠色,第三個是藍色。最大值也是1.0f,代表特定顏色分量的最亮情況。最後一個參數是Alpha值。當它用來清除屏幕的時候,我們不用關心第四個數字。現在讓它爲0.0f。我會用另一個教程來解釋這個參數。
通過混合三種原色(紅、綠、藍),您可以得到不同的色彩。希望您在學校裏學過這些。因此,當您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您將用亮藍色來清除屏幕。如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的話,您將使用中紅色來清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。要得到白色背景,您應該將所有的顏色設成最亮(1.0f)。要黑色背景的話,您該將所有的顏色設爲最暗(0.0f)。我們在這裏設置屏幕爲黑色.
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
這三行是關於depth buffer(深度緩存)的。將深度緩存設想爲屏幕後面的層。深度緩存不斷的對物體進入屏幕內部有多深進行跟蹤。我們本節的程序其實沒有真正使用深度緩存,但幾乎所有在屏幕上顯示3D場景OpenGL程序都使用深度緩存。它的排序決定那個物體先畫。這樣您就不會將一個圓形後面的正方形畫到圓形上來。深度緩存是OpenGL十分重要的部分.gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_NICEST);
這裏告訴OpenGL我們希望進行最好的透視修正。這會十分輕微的影響性能。但使得透視圖看起來好一點.gl.glShadeModel(GL10.GL_SMOOTH);
啓用smooth shading(陰影平滑).陰影平滑通過多邊形精細的混合色彩,並對外部光進行平滑,在以後的課程中會看到他的效果.
gl.glDisable(GL10.GL_DITHER);
關閉服務器端GL功能,在GL中很多都是一對一對的,比如這個的另一個gl.glEnable(...).
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
當發生繪製變化的時候運行.比如橫屏切換到豎屏.
gl.glViewport(0,0,width,height);
此方法用來設定可見區域,即OpenGL應把渲染之後的圖形繪製在窗體的哪個部位,當視見區域是整個窗體時,OpenGL將把渲染結果繪製到整個窗口.gl.glMatrixMode(GL10.GL_PROJECTION);
這個函數其實就是對接下來要做什麼進行一下聲明,也就是在要做下一步之前告訴計算機我要對“什麼”進行操作了,這個“什麼”在glMatrixMode的“()”裏的選項(參數)有3種模式: GL_PROJECTION 投影, GL_MODELVIEW 模型視圖, GL_TEXTURE 紋理.gl.glLoadIdentity();
重設視圖模型變換 , 用於觀測創建的物體.
GLU.gluPerspective(gl,45,aspect,0.1f,100.0f);
參考:http://blog.chinaunix.net/uid-24448954-id-3059473.html@Override
public void onDrawFrame(GL10 gl)
這個纔是真正繪製圖形的地方,類似於自定義的View的onDraw方法.gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);
將緩存清除爲預先的設置值.
gl.glTranslatef(-3.0f,0.0f,-6.0f);
這個方法是位移操作,三個參數分別是x,y,z左邊,(0,0,0)通過前面的glLoadIdentity將被設置在平面中間位置,z的正方向是從屏幕指向上(或者屏幕外).
下面的實現邊形的類:
package org.durian.durianopengl1.draw2d;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
/**
* Created by Administrator on 2016/4/11.
*/
public class Triangle {
private FloatBuffer vertexBuffer;
private ByteBuffer indexBuffer;
private float[] vertices={
0.0f,1.0f,0.0f,
-1.0f,-1.0f,0.0f,
1.0f,-1.0f,0.0f
};
private byte[] indices={0,1,2};
public Triangle(){
ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer=vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
indexBuffer=ByteBuffer.allocateDirect(indices.length);
indexBuffer.put(indices);
indexBuffer.position(0);
}
public void draw(GL10 gl){
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES,indices.length,GL10.GL_UNSIGNED_BYTE,indexBuffer);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
邊是由兩點一線組成,上面是三邊形,那麼就有三個座標,由於opengl是空間座標,所以座標是由x,y,z三項組成:
private float[] vertices={
0.0f,1.0f,0.0f,
-1.0f,-1.0f,0.0f,
1.0f,-1.0f,0.0f
};
上面每一行分別對應是x,y,z
構造方法體中:
ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer=vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
opengl對於頂點操作實際是在native層完成的,內存分配也是在native完成的,所以上面是完成頂點的數據的內存分配gl.glEnableClientState
gl.glDisableClientState
這兩個一般是成對出現,可以控制管道(pipeline)開關
下圖顯示了OpenGL ES 1.x 固定管道的結構圖:
管道“工序”大致可以分爲 Transformation Stage 和 Rasterization Stage兩大步。
OpenGL ES 支持的基本圖形爲 點Point, 線Line, 和三角形Triangle ,其它所有複製圖形都是通過這幾種基本幾何圖形組合而成。
在發出繪圖指令後,會對頂點(Vertices)數組進行指定的座標變換或光照處理。
頂點處理完成後,通過Rasterizer 來生成像素信息,稱爲”Fragments“ 。
對於Fragment 在經過Texture Processing, Color Sum ,Fog 等處理並將最終處理結果存放在內存中(稱爲FrameBuffer)。
OpenGL 2.0可以通過編程來修改藍色的步驟,稱爲Programmable Shader.
以上管道中工序可以通過設置來打開或關閉某些功能(比如無需霧化Fog處理),並可以爲某個工序設置參數,比如設置Vertext Array.
gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);
第一個參數代表每個點或者頂點用幾個座標表示,本例中使用三個xyz。你也可以指定兩個只用xy,這樣話,z就爲0.需要注意的是,第一個參數並不代表有幾個點,如果你想要使用20個點來代表一個三角形,這裏你需要傳入的不是20而是2或3.
第二個參數表示座標值應當被解析爲float類型(這樣OpenGL就可以知道每個值佔用幾位).
第三個參數可以稱爲“步長”,代表每個點之間有幾位分割。本例中,0代表一個點挨着一個點,有時候你可能會在點的後面定義顏色,這時,你應該指出每個顏色佔用的位長,以便OpenGL在解析時跳過這段長度.
gl.glDrawElements(GL10.GL_TRIANGLES,indices.length,GL10.GL_UNSIGNED_BYTE,indexBuffer);
第一個參數代表你想要畫的幾何圖形(GL_TRIANGLE_STRIP代表一個三角strip)。其他可能的選項包括GL_POINTS,GL_LINE_STRIP,GL_LINES,GL_LINE_LOOP,GL_TRIANGLES和G:_TRIANGLE_FAN.
第二個參數:glDrawElements其它的參數可以讓你重用預定義的帶你。比如說,一個四邊形包括四個點,每一個四邊形可以用兩個三角形組合而來。如果你想使用兩個三角形組合成一個四邊形,你有必要去定義6個點嗎?當然不是,你只需要定義四個點,然後引用6次畫出兩個三角形就可以了,這種處理被稱爲“indexing into the point buffer”如下所示:
Points:(p1,p2,p3,p4)
Draw indices(p1,p2,p3, p2,p3,p4)
知道了這些,glDrawElements的第二個參數就好理解了,它代表了index buffer中有幾個座標標示(可以理解爲點).
第三個參數代表index 數組中值的類型,是GL_UNSIGNED_SHORT還是GL_UNSIGNED_BYTE。
最後一個參數引用index buffer.
OK,基本操作和解釋已經相當清楚了.
另外一個方形類就不過多解釋了;
package org.durian.durianopengl1.draw2d;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
/**
* Created by Administrator on 2016/4/11.
*/
public class Square {
private FloatBuffer vertexBuffer;
private float[] vertices={
-1.0f,-1.0f,0.0f,
1.0f,-1.0f,0.0f,
-1.0f,1.0f,0.0f,
1.0f,1.0f,0.0f
};
public Square(){
ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer=vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}
public void draw(GL10 gl){
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,vertices.length/3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}