搜尋
首頁Javajava教程OpenGL ES在Android開發中繪製3D圖形的實例詳解

這篇文章主要介紹了Android開發OpenGL ES繪製3D 圖形實例詳解的相關資料,需要的朋友可以參考下

OpenGL ES是OpenGL三維圖形API 的子集,針對手機、PDA和遊戲主機等嵌入式裝置而設計。 Ophone目前支援OpenGL ES 1.0 ,OpenGL ES 1.0 是以 OpenGL 1.3 規格為基礎的,OpenGL ES 1.1 是以 OpenGL 1.5 規格為基礎的。本文主要介紹利用OpenGL ES繪製圖形方面的基本步驟。

本文內容由三個部分構成。首先透過EGL取得OpenGL ES的程式介面;其次介紹建構3D程式的基本概念;最後是一個應用程式範例。

OpenGL ES 基本上是一個圖形渲染管線的狀態機,而 EGL 則是用來監控這些狀態以及維護幀緩衝和其他渲染面的外部層。圖1 是一個典型的 EGL 系統佈局圖。 EGL 視窗設計是基於人們熟悉的 Microsoft Windows ( WGL )和 UNIX ( GLX )上的 OpenGL 的 Native 接口,與後者比較接近。 OpenGL ES 圖形管線的狀態儲存在 EGL 管理的一個上下文中。幀緩衝和其他繪製渲染面透過 EGL API 建立、管理和銷毀。 EGL 同時也控制和提供了對設備顯示和可能的設備渲染配置的存取。


圖1

#OpenGL ES 需要一個渲染上下文和渲染面。渲染上下文中儲存OpenGL ES的狀態訊息,渲染面用於圖元的繪製。編寫OpenGL ES之前需要EGL的操作有:

查詢裝置可以支援的顯示句柄,並且初始化。

建立渲染面,繪製OpenGL ES圖形。

建立渲染上下文。 EGL需要建立OpenGL ES渲染上下文用於關聯到某個渲染面。

Ophone中EGL包含4個類,分別是EGLDisplay:顯示句柄、EGLConfig:配置類別;EGLContext:渲染上下文;的類別和EGLSurface:可渲染的視圖類別。

EGL可以認為是OpenGL ES和本地視窗系統之間的中間層。 本機視窗系統指GNU/Linux上X視窗系統,或Mac OX X's Quartz等。在EGL確定渲染面的類型前,EGL需要和底層的視窗系統進行通訊。因為在不同的作業系統上的視窗系統的不同,EGL提供一個透明視窗類型,即EGLDisplay。它抽象化了各種視窗系統。所以首先要建立、初始化一個EGLDisplay物件。

// EGLContext的静态方法getEGL获得EGL实例
EGL10 egl = (EGL10)EGLContext.getEGL();
//创建EGLDisplay, EGL_DEFAULT_DISPLAY获得缺省的本地窗口系统类型
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
//初始化EGLDispla的同时获得版本号
int[] version = new int[2];
egl.eglInitialize(dpy, version);

每個 EGLDisplay 在使用前都需要初始化。初始化 EGLDisplay 的同時能夠得到系統中 EGL 的實作版本號。透過版本號,合理運用對應OpenGL ES API,可以編寫相容性良好的程序,以適應更多的設備以及提供最大限度的移植性。初始化函數原型:
boolean eglInitialize(EGLDisplay display, int[] major_minor)

其中的display是一個有效的 EGLDisplay實例。函數呼叫完成時, major_minor將被賦予目前 EGL 版本號。如 EGL1.0 , major_minor[0]為1,major_minor[1]為0。 EGLSurface包含了EGL渲染面相關的所有資訊。查詢 EGLSurface配置信息有兩種方法,一是查詢所有的配置信息,從中選擇一個最適合的;二是指定好配置信息,由系統給出最佳匹配結果。一般採用第二種方法。使用者透過configSpec指定出希望獲得的配置,函數eglChooseConfig透過參數Configs返回最佳的配置清單。之後利用已取得的Configs,呼叫eglCreateContext建立一個渲染上下文,該函數傳回EGLContext結構。渲染面EGLSurface的建立透過函數eglCreateWindowSurface完成。一個應用程式可以創建多個EGLContext。 eglMakeCurrent就是將某個渲染上下文綁定到渲染面。查詢函數 eglGetCurrentContext, eglGetCurrentDisplay和eglGetCurrentSurface 分別用於取得目前系統的渲染上下文、顯示句柄和渲染面。最後EGLContext的靜態方法getGL取得OpenGL ES的程式介面。下面的程式片段總結了上述內容。

EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2];
egl.eglInitialize(dpy, version);
int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
EGLConfig config = configs[0];
EGLContext context = egl.eglCreateContext(dpy, config,
EGL10.EGL_NO_CONTEXT, null);
EGLSurface surface = egl.eglCreateWindowSurface(dpy, config,
sHolder, null);
egl.eglMakeCurrent(dpy, surface, surface, context);
GL10 gl = (GL10)context.getGL();

建立3D圖形的點

點是建立3D模型的基礎。 OpenGL ES的內部運算是基於點的。 用點也可以表示光源的位置,物體的位置。一般我們用一組浮點數來表示點。 例如一個正方形的4個頂點可表示為:

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, //右上
};

為了提高效能, 需要將浮點數陣列存入一個位元組緩衝。 所以有了下面的操作:

ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

其中ByteOrder.nativeOrder()是取得本機位元組順序。 OpenGL ES有操作圖形渲染管線的函數,在預設情況下這些函數功能的使用狀態是關閉的。 啟用和關閉這些函數可以用glEnableClientState、glDisableClientState來完成。

// 指定需要啟用定點陣列
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 說明啟用陣列的型別和位元組緩衝,類型為GL_FLOAT
gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, vertexBuffer);
// 不再需要時,關閉頂點數組
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);


边是连接两个点的一条线,是多边形面的边缘。

多边形

多边形是由边构成的单闭合环。 OpenGL ES中的多边形必须是凸多边形,即在多边形的内部任意取两点, 如果连接这两个点的线段都在多变的内部,这个多边形就是凸多边形。 绘制多边形时需要指定渲染的方向, 分为顺时针和逆时针。 因为方向决定了多边形的朝向, 即正面和背面。 避免渲染那些被遮挡的部分可以了有效提高程序性能。 函数glFrontFace定义了渲染顶点的方向。

// 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针
glFrontFace(GL_CCW);
// 设置CW方向为“正面”,CW即ClockWise,顺时针
glFrontFace(GL_CW);

渲染

有了以上的概念讲解后,现在要进行最主要的工作—渲染。渲染是把物体坐标所指定的图元转化成帧缓冲区中的图像。图像和顶点坐标有着密切的关系。这个关系通过绘制模式给出。常用到得绘制模式有GL_POINTS、GL_LINE_STRIP、

GL_LINE_LOOP、GL_LINES、 GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN。下面分别介绍:
GL_POINTS:把每一个顶点作为一个点进行处理,顶点n即定义了点n,共绘制n个点。

GL_LINES:把每一个顶点作为一个独立的线段,顶点2n-1和2n之间共定义了n个线段,总共绘制N/2条线段。,如果N为奇数,则忽略最后一个顶点。

GL_LINE_STRIP:绘制从第一个顶点到最后一个顶点依次相连的一组线段,第n和n+1个顶点定义了线段n,总共绘制N-1条线段。


GL_LINE_LOOP:绘制从定义第一个顶点到最后一个顶点依次相连的一组线段,然后最后一个顶点与第一个顶点相连。第n和n+1个顶点定义了线段n,然后最后一个线段是由顶点N和1之间定义,总共绘制N条线段。


GL_TRIANGLES:把每三个顶点作为一个独立的三角形。顶点3n-2,3n-1和3n定义了第n个三角形,总共绘制N/3个三角形。

GL_TRIANGLE_STRIP:绘制一组相连的三角形。对于奇数点n,顶点n,n+1和n+2定义了第n个三角形;对于偶数n,顶点n+1,n和n+2定义了第n个三角形,总共绘制N-2个三角形。


GL_TRIANGLE_FAN:绘制一组相连的三角形。三角形是由第一个顶点及其后给定的顶点所确定。顶点1,n+1和n+2定义了第n个三角形,总共绘制N-2个三角形。

绘制函数:

void glDrawArrays(int mode, int first, int count)
void glDrawElements(int mode, int count, int type, Buffer indices)

glDrawArrays创建一个几何图元序列,使用每个被的数组中从first开始,到first + count – 1结束的数组元素, mode为绘制模式。

glDrawElements使用count个元素定义一个图元序列,type是indices数组中的数据类型,mode为绘制模式,indices数组存储顶

点的索引值。

应用举例

利用上面讲解的内容给出一个Ophone上绘制一个3D球形的程序。效果图如下:


图2 球形示例

主要的绘制程序:

static private FloatBuffer vertex;//顶点对应的字节缓冲
static private FloatBuffer normal;//法向量对应的字节缓冲
float[] lightPos = new float[] {10.0f, 10.0f, 10.0f, 1.0f };//光源的坐标
private static final int STEP = 24;//
private static final float RADIUS = 1.0f;//半径
protected void init(GL10 gl) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//设置背景颜色
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0);
gl.glEnable(GL10.GL_LIGHTING);//启用光照
gl.glEnable(GL10.GL_LIGHT0); //打开光源
gl.glClearDepthf(1.0f);//设置深度缓存
gl.glDepthFunc(GL10.GL_LEQUAL);//设置深度缓存比较函数,GL_LEQUAL表示新的像素的深度缓存值小于等于当前像素的深度缓存值时通过深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);//启用深度缓存
gl.glEnable(GL10.GL_CULL_FACE);
gl.glShadeModel(GL10.GL_SMOOTH);//设置阴影模式GL_SMOOTH
}
protected void drawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, 7f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);//
drawSphere(gl, RADIUS, STEP, STEP); //绘制球形
}
public static void gluLookAt (GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)

它共接受三组坐标,分别为eye、 center和up。eye表示我们眼睛在"世界坐标系"中的位置,center表示眼睛"看"的那个点的坐标,up坐标表示观察者本身的方向,如果将观察点比喻成我们的眼睛,那么这个up则表示我们是正立还是倒立异或某一个角度在看,这里是正立方式,所以是{0,1,0}。

private static void drawSphere(GL10 gl, float radius,
int stacks, int slices) {
vertex=allocateFloatBuffer( 4* 6 * stacks * (slices+1) );
normal=allocateFloatBuffer( 4* 6 * stacks * (slices+1) );
int i, j, triangles;
float slicestep, stackstep;
stackstep = ((float)Math.PI) / stacks;
slicestep = 2.0f * ((float)Math.PI) / slices;
for (i = 0; i < stacks; ++i)
{
float a = i * stackstep;
float b = a + stackstep;
float s0 = (float)Math.sin(a);
float s1 = (float)Math.sin(b);
float c0 = (float)Math.cos(a);
float c1 = (float)Math.cos(b);
float nv;
for (j = 0; j <= slices; ++j)
{
float c = j * slicestep;
float x = (float)Math.cos(c);
float y = (float)Math.sin(c);
nv=x * s0;
normal.put(nv);
vertex.put( nv * radius);
nv=y * s0;
normal.put(nv);
vertex.put( nv * radius);
nv=c0;
normal.put(nv);
vertex.put( nv * radius);
nv=x * s1;
normal.put(nv);
vertex.put( nv * radius);
nv=y * s1;
normal.put(nv);
vertex.put( nv * radius);
nv=c1;
normal.put(nv);
vertex.put( nv * radius);
}
}
normal.position(0);
vertex.position(0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
gl.glNormalPointer(GL10.GL_FLOAT, 0, normal);
gl.glEnableClientState (GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState (GL10.GL_NORMAL_ARRAY);
triangles = (slices + 1) * 2;
for(i = 0; i < stacks; i++)
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,
i * triangles, triangles);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
private static FloatBuffer allocateFloatBuffer(int capacity){
ByteBuffer vbb = ByteBuffer.allocateDirect(capacity);
vbb.order(ByteOrder.nativeOrder());
return vbb.asFloatBuffer();
}

总结:

本文介紹了Ophone中利用OpenGL ES繪製圖形的基本概念和方法。 OpenGL ES還有很多其他內容,諸如紋理、光線和材質、混合、霧、蒙板、反射、3D模型的加載等。利用OpenGL ES函數可以繪製豐富的圖形應用和遊戲介面。

以上是OpenGL ES在Android開發中繪製3D圖形的實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JVM如何在不同平台上管理垃圾收集?JVM如何在不同平台上管理垃圾收集?Apr 28, 2025 am 12:23 AM

JVMmanagesgarbagecollectionacrossplatformseffectivelybyusingagenerationalapproachandadaptingtoOSandhardwaredifferences.ItemploysvariouscollectorslikeSerial,Parallel,CMS,andG1,eachsuitedfordifferentscenarios.Performancecanbetunedwithflagslike-XX:NewRa

為什麼Java代碼可以在不同的操作系統上運行,而無需修改?為什麼Java代碼可以在不同的操作系統上運行,而無需修改?Apr 28, 2025 am 12:14 AM

Java代碼可以在不同操作系統上無需修改即可運行,這是因為Java的“一次編寫,到處運行”哲學,由Java虛擬機(JVM)實現。 JVM作為編譯後的Java字節碼與操作系統之間的中介,將字節碼翻譯成特定機器指令,確保程序在任何安裝了JVM的平台上都能獨立運行。

描述編譯和執行Java程序的過程,突出平台獨立性。描述編譯和執行Java程序的過程,突出平台獨立性。Apr 28, 2025 am 12:08 AM

Java程序的編譯和執行通過字節碼和JVM實現平台獨立性。 1)編寫Java源碼並編譯成字節碼。 2)使用JVM在任何平台上執行字節碼,確保代碼的跨平台運行。

基礎硬件架構如何影響Java的性能?基礎硬件架構如何影響Java的性能?Apr 28, 2025 am 12:05 AM

Java性能与硬件架构密切相关,理解这种关系可以显著提升编程能力。1)JVM通过JIT编译将Java字节码转换为机器指令,受CPU架构影响。2)内存管理和垃圾回收受RAM和内存总线速度影响。3)缓存和分支预测优化Java代码执行。4)多线程和并行处理在多核系统上提升性能。

解釋為什麼本地庫可以破壞Java的平台獨立性。解釋為什麼本地庫可以破壞Java的平台獨立性。Apr 28, 2025 am 12:02 AM

使用原生庫會破壞Java的平台獨立性,因為這些庫需要為每個操作系統單獨編譯。 1)原生庫通過JNI與Java交互,提供Java無法直接實現的功能。 2)使用原生庫增加了項目複雜性,需要為不同平台管理庫文件。 3)雖然原生庫能提高性能,但應謹慎使用並進行跨平台測試。

JVM如何處理操作系統API的差異?JVM如何處理操作系統API的差異?Apr 27, 2025 am 12:18 AM

JVM通過JavaNativeInterface(JNI)和Java標準庫處理操作系統API差異:1.JNI允許Java代碼調用本地代碼,直接與操作系統API交互。 2.Java標準庫提供統一API,內部映射到不同操作系統API,確保代碼跨平台運行。

Java 9影響平台獨立性中引入的模塊化如何?Java 9影響平台獨立性中引入的模塊化如何?Apr 27, 2025 am 12:15 AM

modularitydoesnotdirectlyaffectJava'splatformindependence.Java'splatformindependenceismaintainedbytheJVM,butmodularityinfluencesapplicationstructureandmanagement,indirectlyimpactingplatformindependence.1)Deploymentanddistributionbecomemoreefficientwi

什麼是字節碼,它與Java的平台獨立性有何關係?什麼是字節碼,它與Java的平台獨立性有何關係?Apr 27, 2025 am 12:06 AM

BytecodeinJavaistheintermediaterepresentationthatenablesplatformindependence.1)Javacodeiscompiledintobytecodestoredin.classfiles.2)TheJVMinterpretsorcompilesthisbytecodeintomachinecodeatruntime,allowingthesamebytecodetorunonanydevicewithaJVM,thusfulf

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中