opengl大作业_Android游戏开发– OpenGL纹理映射

这篇博客介绍了在Android游戏中如何使用OpenGL进行纹理映射,通过加载图像创建纹理并将其应用到正方形上。内容包括理解纹理坐标、加载纹理、激活纹理以及绘制带有纹理的正方形。同时,提到了UV贴图的概念和重要性。
摘要由CSDN通过智能技术生成
opengl大作业

opengl大作业

在前两篇文章(第1条第2条)中,我试图介绍android上的OpenGL ES。 现在,让我们更进一步,并在此基础上进一步发展。 在本文中,我们将创建一个广告牌(正方形),并在其上应用纹理。 纹理只不过是位图图像。 在2D模式下工作时,我们将Z坐标设置为0。稍后我们将介绍3D模式。 这对于在2D游戏中使用非常有用,并且是使用OpenGL显示图像的首选方式。 确实非常快。

在前面的文章中,我们设法显示了三角形。 那么如何显示正方形呢? 一个正方形由2个三角形组成。

下图显示了这一点:

三角形的正方形

这里有一件有趣的事情要注意。 正方形是ABDC,而不是通常的ABCD 。 这是为什么? 因为OpenGL如何将三角形链接在一起。

您在这里看到的是一个三角带。 三角形带是一系列连接的三角形,在我们的例子中是2个三角形。

OpenGL使用顶点按以下顺序绘制以下三角形带(即正方形):

三角形1: V1-> V2-> V3

三角形2: V3-> V2-> V4

三角带

它使用顶点按顺序绘制第一个三角形,然后从上一个三角形获取最后一个顶点,并使用该三角形的最后一面作为新三角形的基础。

这也有好处:我们从内存中消除了冗余数据。

抓住上一篇文章中的项目,并创建一个名为Square的新类。

如果将Square类与Triangle类进行比较,您会发现只有一个区别:

package net.obviam.opengl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

public class Square {

	private FloatBuffer vertexBuffer;	// buffer holding the vertices

	private float vertices[] = {
			-1.0f, -1.0f,  0.0f,		// V1 - bottom left
			-1.0f,  1.0f,  0.0f,		// V2 - top left
			 1.0f, -1.0f,  0.0f,		// V3 - bottom right
			 1.0f,  1.0f,  0.0f			// V4 - top right
	};

	public Square() {
		// a float has 4 bytes so we allocate for each coordinate 4 bytes
		ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
		vertexByteBuffer.order(ByteOrder.nativeOrder());

		// allocates the memory from the byte buffer
		vertexBuffer = vertexByteBuffer.asFloatBuffer();

		// fill the vertexBuffer with the vertices
		vertexBuffer.put(vertices);

		// set the cursor position to the beginning of the buffer
		vertexBuffer.position(0);
	}

	/** The draw method for the square with the GL context */
	public void draw(GL10 gl) {
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

		// set the colour for the square
		gl.glColor4f(0.0f, 1.0f, 0.0f, 0.5f);

		// Point to our vertex buffer
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

		// Draw the vertices as triangle strip
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

		//Disable the client state before leaving
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
	}
}

区别在于突出显示的行(13-18)。 没错,我们在顶点数组中又添加了一个顶点。

现在更改GlRenderer,以便使用Square代替Triangle

package net.obviam.opengl;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;

public class GlRenderer implements Renderer {

	private Square 		square;		// the square

	/** Constructor to set the handed over context */
	public GlRenderer() {
		this.square		= new Square();
	}

	@Override
	public void onDrawFrame(GL10 gl) {
		// clear Screen and Depth Buffer
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		// Reset the Modelview Matrix
		gl.glLoadIdentity();

		// Drawing
		gl.glTranslatef(0.0f, 0.0f, -5.0f);		// move 5 units INTO the screen
												// is the same as moving the camera 5 units away
		square.draw(gl);						// Draw the triangle

	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		if(height == 0) { 						//Prevent A Divide By Zero By
			height = 1; 						//Making Height Equal One
		}

		gl.glViewport(0, 0, width, height); 	//Reset The Current Viewport
		gl.glMatrixMode(GL10.GL_PROJECTION); 	//Select The Projection Matrix
		gl.glLoadIdentity(); 					//Reset The Projection Matrix

		//Calculate The Aspect Ratio Of The Window
		GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);

		gl.glMatrixMode(GL10.GL_MODELVIEW); 	//Select The Modelview Matrix
		gl.glLoadIdentity(); 					//Reset The Modelview Matrix
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
	}
}

运行此命令将产生以下结果:

三角带形成一个正方形

对此进行检查, Square类中的draw()方法现在应该有意义。

public void draw(GL10 gl) {
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

		// set the colour for the square
		gl.glColor4f(0.0f, 1.0f, 0.0f, 0.5f);

		// Point to our vertex buffer
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

		// Draw the vertices as triangle strip
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

		//Disable the client state before leaving
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
	}

首先,我们使OpenGL使用顶点数组进行渲染。 我们的顶点数组包含正方形的顶点。

gl.glVertexPointer (第5行)告诉opengl渲染器从何处获取顶点以及顶点的类型。

第一个参数告诉一个顶点使用多少个坐标。 我们使用3(x,y,z)。 第二个参数表明值是float类型。

第三个参数是数组顶点之间的偏移量。 这就是所谓的冲突。 我们有一个紧密排列的数组,所以它是0

最后,最后一个参数告诉顶点在哪里。 当然是我们的缓冲区vertexBuffer

第11行中的gl.glDrawArrays告诉OpenGL绘制图元。 什么样的原始语? 第一个参数中指定的一个: GL10.GL_TRIANGLE_STRIP 。 它从先前设置的顶点缓冲区中提取顶点,并且遵循前面描述的三角形条的规则。

第二个参数指定数组中顶点的起始索引。

第3个参数告诉OpenGL,要渲染的多边形要使用多少个顶点。 因为在上一条语句( gl.glVertexPointer )中我们指定了3个坐标定义一个顶点,所以我们将提供顶点数组的长度除以3。在数组中有9个元素定义了3个顶点。

glDisableClientState(GL10.GL_VERTEX_ARRAY)禁用包含顶点的数组的渲染状态。

glEnableClientStateglDisableClientState视为程序中的begin…end语句。 我们基本上在OpenGL渲染器中输入子例程。 输入例程后,我们将设置变量(顶点缓冲区,颜色等),并执行其他子例程(绘制顶点)。 完成后,我们退出子例程。 我们在渲染器中隔离工作。

确保在此阶段运行应用程序,并了解发生了什么。

创建纹理

现在有趣的部分。 让我们加载图像并创建纹理。 纹理就是图像。

要了解如何在Android应用中加载图片,请参阅本文

我们要使用Square类,因为我们想将纹理应用于正方形。

我们需要加载图像,告诉opengl渲染器我们要使用它作为纹理,最后我们将告诉渲染器在原始图元(正方形)上确切显示它的位置。

就像将箔纸放在窗户或墙壁上一样。 我为您提供了包含窗口大小图像的贴膜,并告诉您用它覆盖窗口,因此贴膜的左上角将位于窗口的左上角。 就是这样,让我们​​开始工作。

OpenGL使用顶点计算出放置内容的位置。 因此,我们需要为图像创建一个数组。 但是这次,这将是2D,因为位图就像一张纸,是一个平面。

添加纹理的坐标数组。

private FloatBuffer textureBuffer;	// buffer holding the texture coordinates
	private float texture[] = {
			// Mapping coordinates for the vertices
			0.0f, 1.0f,		// top left		(V2)
			0.0f, 0.0f,		// bottom left	(V1)
			1.0f, 1.0f,		// top right	(V4)
			1.0f, 0.0f		// bottom right	(V3)
	};

我们需要以类似于vertexBuffer的方式创建textureBuffer 。 这发生在构造函数中,我们将只重用byteBuffer 。 检查新的构造函数:

public Square() {
		ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
		byteBuffer.order(ByteOrder.nativeOrder());
		vertexBuffer = byteBuffer.asFloatBuffer();
		vertexBuffer.put(vertices);
		vertexBuffer.position(0);

		byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
		byteBuffer.order(ByteOrder.nativeOrder());
		textureBuffer = byteBuffer.asFloatBuffer();
		textureBuffer.put(texture);
		textureBuffer.position(0);
	}

我们将向Square类添加一个重要的方法。 loadGLTexture方法。 启动时将从渲染器中调用它。 它发生在onSurfaceCreated方法中。 这将从磁盘加载图像并将其绑定到OpenGL存储库中的纹理。 基本上,它将为处理后的图像分配一个内部ID,并且OpenGL API将使用它在其他纹理中对其进行标识。

/** The texture pointer */
	private int[] textures = new int[1];

	public void loadGLTexture(GL10 gl, Context context) {
		// loading texture
		Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
				R.drawable.android);

		// generate one texture pointer
		gl.glGenTextures(1, textures, 0);
		// ...and bind it to our array
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

		// create nearest filtered texture
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

		// Use Android GLUtils to specify a two-dimensional texture image from our bitmap
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

		// Clean up
		bitmap.recycle();
	}

我们需要一个纹理指针数组。 OpenGL将在此处存储我们将在应用程序中使用的纹理的名称。 因为我们只有一个图像,所以我们将创建一个大小为1的数组。

06行加载了以前复制到/ res / drawable-mdpi目录中的android位图,因此ID已生成。

有关此位图的说明。 鼓励正方形。 它对扩展很有帮助。 因此,请确保纹理的位图是正方形(6×6、12×12、128×128等)。 如果不是正方形,请确保宽度和高度为2的幂(2、4、8、16、32,…)。 您可以使用128×512的位图,它完全可用并且已优化。

10行生成纹理的名称。 在我们的情况下,生成一个名称并将其存储在textures数组中。 即使显示名称,它实际上也会生成一个int值。 有点令人困惑,但事实就是如此。

12行将纹理与新生成的名称(texture [0])绑定。 这意味着,在此子例程中使用纹理的任何东西都将使用绑定的纹理。 它实际上激活了纹理。 绑定纹理是活动纹理。 如果我们有多个纹理和多个正方形可以使用它们,那么在使用它们之前,必须为每个正方形绑定(激活)适当的纹理。第1516行设置了一些用于纹理的滤镜。 我们刚刚告诉OpenGL需要缩小或扩展纹理以覆盖正方形时使用什么类型的滤镜。 我们选择了一些有关如何缩放图像的基本算法。 现在不必为此担心。

在第19行中,我们使用Android的实用程序为位图指定2D纹理图像。 它基于位图以内部格式在内部创建图像(纹理)。

在第22行中,我们释放了内存。 您不应忘记这一点,因为设备上的内存非常有限并且图像很大。

现在让我们看看如何修改draw()方法。

public void draw(GL10 gl) {
		// bind the previously generated texture
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

		// Point to our buffers
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

		// Set the face rotation
		gl.glFrontFace(GL10.GL_CW);

		// Point to our vertex buffer
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

		// Draw the vertices as triangle strip
		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

		//Disable the client state before leaving
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	}
}

这不是上一篇文章的巨大修改。 这些增加已记录在案,并执行以下操作:

03行使用存储在textures [0]中的名称(整数ID)绑定(激活)纹理。

7行在当前OpenGL上下文中启用纹理映射。

14行为OpenGL上下文提供了纹理坐标。用纹理绘制图元之后,我们将关闭纹理映射以及图元渲染。

重要– UV贴图

如果仔细查看,纹理映射坐标数组中的顶点顺序将不遵循正方形顶点坐标数组中的顺序。

此处对纹理映射坐标有很好的解释: http : //iphonedevelopment.blogspot.com/2009/05/opengl-es-from-ground-up-part-6_25.html

我将尽力快速解释它。 检查下图。

正方形和纹理坐标排序

正方形由2个三角形组成,并且顶点按以下顺序排列。

1 –左下

2 –右下

3 –左上方

4 –右上方

注意逆时针路径。

纹理坐标的顺序为:1-> 3-> 2-> 4

如果您从另一个角开始绘制形状,请记住此映射并旋转它。 要阅读UV映射,请查看Wikipedia条目或在网上搜索。

对于最后一部分,要使其工作,我们需要向渲染器提供上下文,以便在启动时加载纹理。

onSurfaceCreated方法将如下所示。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		// Load the texture for the square
		square.loadGLTexture(gl, this.context);

		gl.glEnable(GL10.GL_TEXTURE_2D);			//Enable Texture Mapping ( NEW )
		gl.glShadeModel(GL10.GL_SMOOTH); 			//Enable Smooth Shading
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); 	//Black Background
		gl.glClearDepthf(1.0f); 					//Depth Buffer Setup
		gl.glEnable(GL10.GL_DEPTH_TEST); 			//Enables Depth Testing
		gl.glDepthFunc(GL10.GL_LEQUAL); 			//The Type Of Depth Testing To Do

		//Really Nice Perspective Calculations
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
	}

03行加载纹理。 其余各行仅使用一些值配置渲染器。 您现在不必担心它们。

您将需要为Square对象提供应用程序上下文,因为对象本身会加载纹理,并且需要知道位图的路径。

只需在Run活动的onCreate方法( glSurfaceView.setRenderer(new GlRenderer(this)); )中为渲染器提供上下文即可。

确保渲染器具有通过构造函数声明和设置的上下文。

GlRendered类的节选。

private Square 		square;		// the square
	private Context 	context;

	/** Constructor to set the handed over context */
	public GlRenderer(Context context) {
		this.context = context;

		// initialise the square
		this.square = new Square();
	}

如果运行代码,您应该会看到一个正方形,上面放着一个漂亮的机器人。

带有Android纹理的正方形

此处下载源代码和项目(obviam.opengl.p03.tgz)。

参考: 纹理贴图–来自“反对谷物”博客的JCG合作伙伴Tamas Jano的OpenGL Android(使用OpenGL和Squares显示图像)

不要忘记查看我们的新Android游戏ArkDroid (以下屏幕截图) 。 您的反馈将大有帮助!
相关文章:

翻译自: https://www.javacodegeeks.com/2011/10/android-game-development-opengl-texture.html

opengl大作业

本书共分两篇,第一篇介绍了Android 3D游戏开发的基础知识,主要对OpenGL ES的相关内容进行了介绍。   章 名主 要 内 容   第1章 英雄还看今朝—Android简介本章介绍了市场上主流的手机平台,同时也分析了未来手机平台的发展趋势及Android平台的前景   第2章 数风流人物—当前流行游戏类型简介本章以分类的方式简要地介绍了当前流行的游戏的玩法,游戏的视觉效果,游戏的设计及《仙剑》等著名游戏的历史   第3章 不积跬步,无以至千里—游戏开发基础知识本章初步介绍了游戏开发的基础知识   第4章 千里之行,始于足下—3D开发基础知识本章介绍了3D开发的基础知识,包括OpenGL ES的介绍及OpenGL ES绘制模型的原理,并通过点、线和三角形的绘制介绍了OpenGL ES模型的几种绘制方式。最后介绍了3D场景常用的两种投影方式,并通过例子比较了这两种投影的区别   第5章 愿君多采撷,此物最相思—光照效果的开发本章介绍了光照的基础知识,包括环境光、散射光及镜面光   第6章 为伊消得人憔悴——纹理映射本章主要介绍了纹理的基础知识,以及纹理的不同拉伸方式和纹理过滤高级技术,从绘制三角形开始到绘制地月系,可能会经历很长时间,但是这对以后的学习是有帮助的   第7章 海阔凭鱼跃,天高任鸟飞—3D基本形状的构建在本章介绍了圆柱体、圆锥体、圆环、抛物面、双曲面和螺旋面在OpenGL ES的渲染方法。这些基本形状在3D世界应用广泛,在构造一些复杂物体时,经常会运用这些基本形状来进行拼装组合   第8章 执子之手,与子偕老—坐标变换本章介绍了坐标变换的应用。绘制3D场景的过程,主要是旋转和平移操作的组合,通过合理的堆栈操作,就比较容易绘制出所需的3D场景   第9章 孤帆远影碧空尽—摄像机与雾特效在本章,首先对摄像机及其配置做了介绍。摄像机在3D编程至关重要,没有正确的配置,摄像机可能不能获得想要的场景效果。然后对雾特效做了具体介绍,应用雾特效可以使场景更加逼真,并且可以减少场景渲染量来提高性能   第10章 假作真时真亦假—混合本章主要为读者介绍了混合,从混合的背景知识到如何配置源因子和目标因子。在介绍源因子和目标因子的时候,向读者介绍了一些预定义常量和一些常用的组合方式,以及如何启用混合   第11章 蓦然回首,那人却在灯火阑珊处—3D高级技术本章主要为读者介绍了3D的一部分高级技术。每一项技术通过讲解其原理和案例,使读者对3D高级技术有一定的了解   第12章 心有灵犀一点通—传感器在本章,向读者介绍了Android传感器的相关知识。包括传感器的种类、配置,并且着重介绍了姿态传感器的应用   第13章 千锤万凿出深山—游戏的数学与物理在本章对3D游戏可能会用到的数学及物理知识进行了简单的介绍,这在3D游戏开发是相当重要的。游戏的核心算法,基本上都要用到数学和物理知识。一款游戏的性能很大程度上取决于游戏设计的算法   第14章 山舞银蛇,原驰蜡象—AI基本理念本章主要介绍了AI、AI引擎的基本组成与设计,以及游戏AI图的搜索和模糊逻辑,其游戏AI图的搜索为本章的重点。在本章详细介绍了5种算法的原理与实现   第15章 独上高楼,望尽天涯路—开发小秘籍本章介绍了地图设计器、多键技术、虚拟键盘、查找表技术、状态机、AABB边界框、穿透效应、拾取技术,以及天空盒和天空穹在OpenGL ES的应用 第二篇以7个比较大的案例来说明Android平台下3D游戏开发流程,通过这7个案例的讲解,读者对3D游戏开发将会有更深层次的理解。   章 名主 要 内 容   第16章 体育类游戏——《疯狂投篮》本章介绍了Android 3D游戏《疯狂投篮》的开发。通过该案例向读者介绍了在Android平台下进行3D游戏开发的相关知识和基本流程,并对游戏开发的编程技巧进行了介绍,并主要介绍了篮球与地面、墙面及篮框的碰撞检测及运动动画的实现方法   第17章 益智类游戏——《旋转积木》本章介绍了Android 3D游戏《旋转积木》的开发。主要介绍了积木旋转的不同状态的实现方法和地图设计器的应用   第18章 休闲类游戏——《摩天大楼》本章介绍了Android 3D游戏《摩天大楼》的开发。主要介绍了楼层与楼层之间的衔接与碰撞及掉落后翻转动画的实现   第19章 动作类游戏——《3D空战》本章介绍了Android 3D游戏《3D空战》的开发。主要介绍了飞机的构造方法和我方战机与敌方战机的操控及动画实现   第20章 桌面类游戏——《激情台球》本章介绍了Android 3D游戏《激情台球》的开发。主要介绍了台球与台球的碰撞检测实现、台球与球桌的碰撞检测实现和进球的判定实现   第21章 射击类游戏——《抢滩登陆》本章介绍了Android 3D游戏《抢滩登陆》的开发。主要运用了灰度图生成技术并且主要介绍了坦克运动的实现方法及炮弹碰撞检测的实现   第22章 竞技类游戏——《乡村飙车》本章介绍了Android 3D游戏《乡村飙车》的开发。主要介绍了运用分层绘制和拼接绘制的策略进行场景的优化绘制,并且对场景部件进行了分类控制   本书面向的读者   本书的内容详细,且几乎涵盖了Android 3D游戏开发所有相关的技术,并向读者介绍了真实项目的开发流程,主要面向以下读者。   Android的初学者   本书详细介绍了OpenGL ES的基础知识,并对Android 3D游戏程序的开发进行了介绍。作为一名Android的初学者,通过本书的学习可以快速全面地掌握Android 3D游戏开发的相关知识,稳健地步入Android 3D游戏开发人员的行列。   有一定Android基础且希望学习Android 3D游戏开发的读者   有一定Android基础的读者通过阅读本书的前半部分便可快速掌握OpenGL ES的基础知识,然后通过7个真实案例的学习迅速掌握Android平台下应用程序的开发。   在职的开发人员
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值