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大作业

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值