Maison >Java >javaDidacticiel >Explication détaillée d'exemples d'OpenGL ES dessinant des graphiques 3D dans le développement Android

Explication détaillée d'exemples d'OpenGL ES dessinant des graphiques 3D dans le développement Android

Y2J
Y2Joriginal
2017-04-22 09:50:152326parcourir

Cet article présente principalement des informations pertinentes sur le développement Android d'OpenGL ES pour dessiner des exemples graphiques 3D. Les amis dans le besoin peuvent s'y référer

OpenGL ES est un sous-ensemble de l'API graphique tridimensionnelle OpenGL, ciblant les téléphones mobiles. , PDA et Conçu pour les appareils embarqués tels que les consoles de jeux. Ophone prend actuellement en charge OpenGL ES 1.0. OpenGL ES 1.0 est basé sur la spécification OpenGL 1.3 et OpenGL ES 1.1 est basé sur la spécification OpenGL 1.5. Cet article présente principalement les étapes de base du dessin de graphiques à l'aide d'OpenGL ES.

Le contenu de cet article se compose de trois parties. Premièrement, l'interface de programmation d'OpenGL ES est obtenue via EGL ; deuxièmement, les concepts de base de la construction de programmes 3D sont introduits et enfin, un exemple d'application est donné ;

OpenGL ES est essentiellement une machine à états pour le pipeline de rendu graphique, et EGL est une couche externe utilisée pour surveiller ces états et maintenir le framebuffer et d'autres surfaces de rendu. La figure 1 est un schéma typique de configuration du système EGL. La conception de la fenêtre EGL est basée sur l'interface native familière pour OpenGL sur Microsoft Windows (WGL) et UNIX (GLX), et est relativement proche de cette dernière. L'état du pipeline graphique OpenGL ES est stocké dans un contexte géré par EGL. Les tampons de trame et autres surfaces de rendu de dessin sont créés, gérés et détruits via l'API EGL. EGL contrôle et donne également accès à l'affichage des appareils et éventuellement aux configurations de rendu des appareils.


Figure 1

OpenGL ES nécessite un contexte de rendu et une surface de rendu. Les informations d'état d'OpenGL ES sont stockées dans le contexte de rendu et la surface de rendu est utilisée pour dessiner des primitives. Les opérations qui nécessitent EGL avant d'écrire OpenGL ES sont :

Interrogez le handle d'affichage que l'appareil peut prendre en charge et initialisez-le.

Créez une surface de rendu et dessinez des graphiques OpenGL ES.

Créez un contexte de rendu. EGL doit créer un contexte de rendu OpenGL ES à associer à une surface de rendu.

EGL dans Ophone comprend 4 classes, à savoir EGLDisplay : handle d'affichage, EGLConfig : classe de configuration ; EGLContext : classe de rendu et EGLSurface : classe de vue rendu.

EGL peut être considéré comme une couche intermédiaire entre OpenGL ES et le système de fenêtres natif. Le système de fenêtres local fait référence au système de fenêtres X sur GNU/Linux, ou à Quartz de Mac OX X, etc. Avant qu'EGL puisse déterminer le type de surface de rendu, EGL doit communiquer avec le système de fenêtres sous-jacent. Étant donné que les systèmes de fenêtres des différents systèmes d'exploitation sont différents, EGL fournit un type de fenêtre transparent, EGLDisplay. Il résume divers systèmes de fenêtres. Nous devons donc d’abord créer et initialiser un objet 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);

Chaque EGLDisplay doit être initialisé avant utilisation. Lors de l'initialisation d'EGLDisplay, vous pouvez obtenir le numéro de version d'implémentation d'EGL dans le système. Grâce au numéro de version et à l'utilisation appropriée de l'API OpenGL ES correspondante, vous pouvez écrire des programmes avec une bonne compatibilité pour s'adapter à davantage d'appareils et offrir une portabilité maximale. Prototype de la fonction d'initialisation :
boolean eglInitialize(EGLDisplay display, int[] major_minor)

où display est une instance EGLDisplay valide. Une fois l'appel de fonction terminé, major_minor se verra attribuer le numéro de version EGL actuel. Par exemple, EGL1.0, major_minor[0] vaut 1, major_minor[1] vaut 0. EGLSurface contient toutes les informations liées à la surface de rendu EGL. Il existe deux méthodes pour interroger les informations de configuration d'EGLSurface. L'une consiste à interroger toutes les informations de configuration et à sélectionner la plus appropriée ; l'autre consiste à spécifier les informations de configuration et le système donnera le meilleur résultat correspondant. La deuxième méthode est généralement utilisée. L'utilisateur spécifie la configuration souhaitée via configSpec, et la fonction eglChooseConfig renvoie la meilleure liste de configuration via le paramètre Configs. Utilisez ensuite les configurations obtenues pour appeler eglCreateContext afin de créer un contexte de rendu. Cette fonction renvoie la structure EGLContext. La création de la surface de rendu EGLSurface s'effectue via la fonction eglCreateWindowSurface. Une application peut créer plusieurs EGLContexts. eglMakeCurrent consiste à lier un contexte de rendu à la surface de rendu. Les fonctions de requête eglGetCurrentContext, eglGetCurrentDisplay et eglGetCurrentSurface sont utilisées respectivement pour obtenir le contexte de rendu, le handle d'affichage et la surface de rendu du système actuel. Enfin, la méthode statique getGL d'EGLContext obtient l'interface de programmation OpenGL ES. Le fragment de programme suivant résume ce qui précède.

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

Points pour créer des graphiques 3D

Les points sont la base pour créer des modèles 3D. Les calculs internes d'OpenGL ES sont basés sur des points. Les points peuvent également être utilisés pour représenter la position de la source lumineuse et la position de l'objet. Généralement, nous utilisons un ensemble de nombres à virgule flottante pour représenter des points. Par exemple, les 4 sommets d'un carré peuvent être exprimés comme suit :

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

Afin d'améliorer les performances, le tableau à virgule flottante doit être stocké dans un tampon d'octets. Il y a donc l'opération suivante :

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

où ByteOrder.nativeOrder() doit obtenir l'ordre natif des octets. OpenGL ES dispose de fonctions permettant d'exploiter le pipeline de rendu graphique. Par défaut, l'utilisation de ces fonctions est désactivée. L'activation et la fermeture de ces fonctions peuvent être effectuées à l'aide de glEnableClientState et glDisableClientState.

// Spécifie que les tableaux à virgule fixe doivent être activés
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// Spécifie le type et la mise en mémoire tampon d'octets du tableau à activer, le type is GL_FLOAT
gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, vertexBuffer);
//Ferme le tableau de sommets lorsqu'il n'est plus nécessaire
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();
}

总结:

Cet article présente les concepts et méthodes de base pour dessiner des graphiques à l'aide d'OpenGL ES dans Ophone. Il y a bien d'autres choses dans OpenGL ES, comme les textures, l'éclairage et les matériaux, le mélange, le brouillard, les masques, les réflexions, le chargement de modèles 3D, etc. Les fonctions OpenGL ES peuvent être utilisées pour dessiner des applications graphiques riches et des interfaces de jeu.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn