Heim >Java >javaLernprogramm >Ausführliche Erläuterung von Beispielen für das Zeichnen von 3D-Grafiken mit OpenGL ES in der Android-Entwicklung

Ausführliche Erläuterung von Beispielen für das Zeichnen von 3D-Grafiken mit OpenGL ES in der Android-Entwicklung

Y2J
Y2JOriginal
2017-04-22 09:50:152327Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen zur Android-Entwicklung von OpenGL ES zum Zeichnen von 3D-Grafikbeispielen vorgestellt.

OpenGL ES ist eine Teilmenge der dreidimensionalen OpenGL-Grafik-API, die auf Mobiltelefone abzielt , PDAs und für eingebettete Geräte wie Spielekonsolen konzipiert. Ophone unterstützt derzeit OpenGL ES 1.0. OpenGL ES 1.0 basiert auf der OpenGL 1.3-Spezifikation und OpenGL ES 1.1 basiert auf der OpenGL 1.5-Spezifikation. In diesem Artikel werden hauptsächlich die grundlegenden Schritte zum Zeichnen von Grafiken mit OpenGL ES vorgestellt.

Der Inhalt dieses Artikels besteht aus drei Teilen. Erstens wird die Programmierschnittstelle von OpenGL ES über EGL erhalten; zweitens werden die grundlegenden Konzepte zum Erstellen von 3D-Programmen vorgestellt und schließlich wird ein Anwendungsbeispiel gegeben;

OpenGL ES ist im Wesentlichen eine Zustandsmaschine für die Grafik-Rendering-Pipeline, und EGL ist eine externe Schicht, die zur Überwachung dieser Zustände und zur Verwaltung des Framebuffers und anderer Rendering-Oberflächen verwendet wird. Abbildung 1 ist ein typisches EGL-Systemlayout. Das EGL-Fensterdesign basiert auf der bekannten nativen Schnittstelle für OpenGL unter Microsoft Windows (WGL) und UNIX (GLX) und kommt letzterer relativ nahe. Der Status der OpenGL ES-Grafikpipeline wird in einem von EGL verwalteten Kontext gespeichert. Bildpuffer und andere Zeichnungsrenderingoberflächen werden über die EGL-API erstellt, verwaltet und zerstört. EGL steuert außerdem die Geräteanzeige und möglicherweise die Geräte-Rendering-Konfiguration und bietet Zugriff darauf.


Abbildung 1

OpenGL ES erfordert einen Rendering-Kontext und eine Rendering-Oberfläche. Die Statusinformationen von OpenGL ES werden im Rendering-Kontext gespeichert und die Rendering-Oberfläche wird zum Zeichnen von Grundelementen verwendet. Die Vorgänge, die EGL erfordern, bevor OpenGL ES geschrieben wird, sind:

Fragen Sie das Anzeigehandle ab, das das Gerät unterstützen kann, und initialisieren Sie es.

Erstellen Sie eine Rendering-Oberfläche und zeichnen Sie OpenGL ES-Grafiken.

Erstellen Sie einen Rendering-Kontext. EGL muss einen OpenGL ES-Renderingkontext erstellen, um ihn einer Renderingoberfläche zuzuordnen.

EGL in Ophone umfasst 4 Klassen, nämlich EGLDisplay: Anzeigehandle, EGLConfig: Konfigurationsklasse; EGLContext: Rendering-Kontextklasse;

EGL kann als Zwischenschicht zwischen OpenGL ES und dem nativen Fenstersystem betrachtet werden. Das lokale Fenstersystem bezieht sich auf das X-Fenstersystem unter GNU/Linux oder auf Quartz von Mac OX X usw. Bevor EGL den Typ der Renderingoberfläche bestimmen kann, muss EGL mit dem zugrunde liegenden Fenstersystem kommunizieren. Da die Fenstersysteme auf verschiedenen Betriebssystemen unterschiedlich sind, stellt EGL einen transparenten Fenstertyp bereit: EGLDisplay. Es abstrahiert verschiedene Fenstersysteme. Zuerst müssen wir also ein EGLDisplay-Objekt erstellen und initialisieren.

// 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);

Jedes EGLDisplay muss vor der Verwendung initialisiert werden. Bei der Initialisierung von EGLDisplay können Sie die Implementierungsversionsnummer von EGL im System abrufen. Durch die Versionsnummer und die ordnungsgemäße Verwendung der entsprechenden OpenGL ES-API können Sie Programme mit guter Kompatibilität schreiben, um sie an mehr Geräte anzupassen und maximale Portabilität zu gewährleisten. Prototyp der Initialisierungsfunktion:
boolean eglInitialize(EGLDisplay display, int[] major_minor)

wobei display eine gültige EGLDisplay-Instanz ist. Wenn der Funktionsaufruf abgeschlossen ist, wird „major_minor“ die aktuelle EGL-Versionsnummer zugewiesen. Beispiel: EGL1.0, „major_minor[0]“ ist 1, „major_minor[1]“ ist 0. EGLSurface enthält alle Informationen zur EGL-Renderingoberfläche. Es gibt zwei Methoden zum Abfragen von EGLSurface-Konfigurationsinformationen. Die eine besteht darin, alle Konfigurationsinformationen abzufragen und die am besten geeignete auszuwählen. Die andere besteht darin, die Konfigurationsinformationen anzugeben, und das System liefert das am besten passende Ergebnis. Im Allgemeinen wird die zweite Methode verwendet. Der Benutzer gibt die gewünschte Konfiguration über configSpec an und die Funktion eglChooseConfig gibt die beste Konfigurationsliste über den Parameter Configs zurück. Verwenden Sie dann die erhaltenen Konfigurationen, um eglCreateContext aufzurufen und einen Rendering-Kontext zu erstellen. Diese Funktion gibt die EGLContext-Struktur zurück. Die Erstellung der Rendering-Oberfläche EGLSurface wird durch die Funktion eglCreateWindowSurface abgeschlossen. Eine Anwendung kann mehrere EGLContexts erstellen. eglMakeCurrent dient dazu, einen Rendering-Kontext an die Rendering-Oberfläche zu binden. Die Abfragefunktionen eglGetCurrentContext, eglGetCurrentDisplay und eglGetCurrentSurface werden verwendet, um den Rendering-Kontext, das Anzeigehandle bzw. die Rendering-Oberfläche des aktuellen Systems abzurufen. Schließlich ruft die statische Methode getGL von EGLContext die OpenGL ES-Programmierschnittstelle ab. Das folgende Programmfragment fasst das oben Gesagte zusammen.

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();

Punkte für die Erstellung von 3D-Grafiken

Punkte sind die Grundlage für die Erstellung von 3D-Modellen. Die internen Berechnungen von OpenGL ES sind punktbasiert. Punkte können auch verwendet werden, um die Position der Lichtquelle und die Position des Objekts darzustellen. Im Allgemeinen verwenden wir eine Reihe von Gleitkommazahlen, um Punkte darzustellen. Die 4 Eckpunkte eines Quadrats können beispielsweise wie folgt ausgedrückt werden:

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

Um die Leistung zu verbessern, muss das Gleitkomma-Array in einem Bytepuffer gespeichert werden. Es gibt also die folgende Operation:

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

wobei ByteOrder.nativeOrder() die native Byte-Reihenfolge abrufen soll. OpenGL ES verfügt über Funktionen zum Betreiben der Grafik-Rendering-Pipeline. Standardmäßig ist die Verwendung dieser Funktionen deaktiviert. Das Aktivieren und Schließen dieser Funktionen kann mit glEnableClientState und glDisableClientState erfolgen.

// Gibt an, dass Festkomma-Arrays aktiviert werden müssen
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// Anweisungen zum Aktivieren des Array-Typs und der Byte-Pufferung, der Typ ist GL_FLOAT
gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, vertexBuffer);
//Schließen Sie das Vertex-Array, wenn es nicht mehr benötigt wird
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();
}

总结:

In diesem Artikel werden die grundlegenden Konzepte und Methoden zum Zeichnen von Grafiken mit OpenGL ES in Ophone vorgestellt. Es gibt viele andere Dinge in OpenGL ES, wie zum Beispiel Texturen, Beleuchtung und Materialien, Überblendung, Nebel, Masken, Reflexionen, Laden von 3D-Modellen usw. OpenGL ES-Funktionen können zum Zeichnen umfangreicher Grafikanwendungen und Spieloberflächen verwendet werden.

Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung von Beispielen für das Zeichnen von 3D-Grafiken mit OpenGL ES in der Android-Entwicklung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn