OpenglES2.0 for Android:纹理映射

OpenglES2.0 for Android:纹理映射

前言

纹理映射又叫做纹理贴图,是将纹理空间中的纹理像素映射到屏幕空间中的像素的过程。就是把一幅图像贴到三维物体的表面上来增强真实感,

可以和光照计算、图像混合等技术结合起来形成许多非常漂亮的效果 (百度百科)。简单来说,纹理就是一个图形或者照片,我们可以将它们

加载到Opengl中用以美化我们绘制的物体。

前期准备

我们现在准备实现这样一个功能:将一张图片贴到一个正方形中 。我们在以前画矩形的那节代码的基础上进行实现纹理贴图。这里我们新建一个项目

OpenglESRectangle ,然后将画矩形的相关代码copy过来~~,然后还记得我们绘制圆变成椭圆的问题吗,我们在这里给矩形也加入正交投影,以便来绘制

一个正方形。此时目录结构如下 :

因为加入了正交投影比起原先绘制矩形那一节的代码有些变动,发生变动的文件有 MyRender.java , Square.java  ,simple_vertex_shader.glsl ,此时这三个文件代码如下:

package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglesrectangle.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;

public class Square {  

    private Context context;  

    //float类型的字节数
    private static final int BYTES_PER_FLOAT = 4;
    // 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 2;  

    /*------------------第一步: 修改顶点数据-------------------------*/
    //矩形顶点坐标
    static float squareCoords[] = { //以三角形扇的形式绘制
    	-0.5f,  0.5f ,   // top left
        0.5f,  0.5f  , // top right
        0.5f, -0.5f  , // bottom right
       -0.5f, -0.5f  };  // bottom left  

    private FloatBuffer vertexBuffer;  

    //------------第一个是顶点着色器的变量名,第二个是片段着色器的变量名
    private static final String A_POSITION = "a_Position";
    private static final String U_COLOR = "u_Color";
    private static final String U_MATRIX = "u_Matrix";
    //------------获得program的ID的含义类似的
    private int uColorLocation;
    private int aPositionLocation;
    private int uMatrixLocation;
    private int program;//保存program的id  

    /*------------------第二步: 修改顶点个数-------------------------*/
    private static final int POSITION_COMPONENT_COUNT = 4;  

    float[] projectionMatrix = new float[16];//变换矩阵

    public Square(Context context) {
        this.context = context;
        vertexBuffer = ByteBuffer
                .allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        // 把坐标们加入FloatBuffer中
        vertexBuffer.put(squareCoords);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);  

        getProgram();  

        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
        aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
        uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);

        GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false, 0, vertexBuffer);
        GLES20.glEnableVertexAttribArray(aPositionLocation);
    }  

    //获取program
    private void getProgram(){
        //获取顶点着色器文本
        String vertexShaderSource = TextResourceReader
                .readTextFileFromResource(context, R.raw.simple_vertex_shader);
        //获取片段着色器文本
        String fragmentShaderSource = TextResourceReader
                .readTextFileFromResource(context, R.raw.simple_fragment_shader);
        //获取program的id
        program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
        GLES20.glUseProgram(program);
    }  

    //设置正交投影矩阵
    public void projectionMatrix(int width,int height){
        final float aspectRatio = width > height ?
                (float) width / (float) height :
                (float) height / (float) width;
        if(width > height){
            Matrix.orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
        }else{
            Matrix.orthoM(projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
        }
    }  

    //以GL_LINE_LOOP方式绘制
    public void draw(){
    	GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);
        GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        /*------------------第三步: 修改绘制方式-------------------------*/
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, POSITION_COMPONENT_COUNT);
    }
}
package com.cumt.render;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;  

import com.cumt.shape.Square;
import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glViewport;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;  

public class MyRender implements Renderer {  

    private Context context;  

    public MyRender(Context context){
        this.context = context;
    }  

    //定义矩形对象
    Square square;  

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.w("MyRender","onSurfaceCreated");
        // TODO Auto-generated method stub
        //First:设置清空屏幕用的颜色,前三个参数对应红绿蓝,最后一个对应alpha
        glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        square = new Square(context);
    }  

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.w("MyRender","onSurfaceChanged");
        // TODO Auto-generated method stub
        //Second:设置视口尺寸,即告诉opengl可以用来渲染的surface大小
        glViewport(0,0,width,height);
        square.projectionMatrix(width, height);
    }  

    public void onDrawFrame(GL10 gl) {
        Log.w("MyRender","onDrawFrame");
        // TODO Auto-generated method stub
        //Third:清空屏幕,擦除屏幕上所有的颜色,并用之前glClearColor定义的颜色填充整个屏幕
        glClear(GL_COLOR_BUFFER_BIT);
        square.draw();
    }
}
//simple_vertex_shader.glsl
uniform mat4 u_Matrix;
attribute vec4 a_Position;   

void main()
{
    gl_Position = u_Matrix * a_Position;
}   

此时运行效果如下 :

我们要贴的图如下 (512 * 512 ) 要注意opengl es2.0中纹理不必须是正方形,但每个纬度应该是2的幂 ):

我们要将该图片贴到上面的矩形中

正式开始

下面让我们正式开始做纹理贴图 ,看下过程 :

纹理工具类

package com.cumt.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;

public class TextureHelper {

	public static final String TAG = "TextureHelper";

	public static int loadTexture(Context context,int resourceId){
		/*
		 * 第一步 : 创建纹理对象
		 */
		final int[] textureObjectId = new int[1];//用于存储返回的纹理对象ID
		GLES20.glGenTextures(1,textureObjectId, 0);
		if(textureObjectId[0] == 0){//若返回为0,,则创建失败
			if(LoggerConfig.ON){
				Log.w(TAG,"Could not generate a new Opengl texture object");
			}
			return 0;
		}
		/*
		 * 第二步: 加载位图数据并与纹理绑定
		 */
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inScaled = false;//Opengl需要非压缩形式的原始数据
		final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),resourceId, options);
		if(bitmap == null){
			if(LoggerConfig.ON){
				Log.w(TAG,"ResourceId:"+resourceId+"could not be decoded");
			}
			GLES20.glDeleteTextures(1, textureObjectId, 0);
			return 0;
		}
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureObjectId[0]);//通过纹理ID进行绑定
		/*
		 * 第三步: 设置纹理过滤
		 */
		//设置缩小时为三线性过滤
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR_MIPMAP_LINEAR);
		//设置放大时为双线性过滤
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
		/*
		 * 第四步: 加载纹理到Opengl并返回ID
		 */
		GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
		bitmap.recycle();
		GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
		return textureObjectId[0];
	}
}

这里要说下纹理过滤 的内容,纹理过滤应用通常有两种情况,一种是GL_TEXTURE_MIN_FILTER ,即纹理被缩小时,比如一个512 x 512的纹理贴到一个平行于xy平面的正方形上,最后该正方形在屏幕上只占256 x 256的像素矩阵,这种情况下一个象素对应着多个纹理单元。另一种是GL_TEXTURE_MAG_FILTER,即纹理被放大时。下面简要说下Opengl的纹理过滤模式
, Opengl纹理过滤模式如下 :

----------------------------------------------------------------------------------------------------------------------------------------------------------------

GL_NEAREST      最近邻过滤

GL_NEAREST_MIPMAP_NEAREST  使用MIP贴图的最近邻过滤

GL_NEAREST_MIPMAP_LINEAR    使用MIP贴图级别之间插值的最近邻过滤

GL_LINEAR  双线性过滤

GL_LINEAR_MIPMAP_NEAREST    使用MIP贴图的双线性过滤

GL_LINEAR_MIPMAP_LINEAR    三线性过滤 (使用MIP贴图级别之间插值的双线性过滤

---------------------------------------------------------------------------------------------------------------------------------------------------------------

在缩小和放大情况下,Opengl支持不同的过滤模式,如下所示:

---------------------------------------------------------------------------------------------------------------------------------------------------------------

缩小情况:

GL_NEAREST

GL_NEAREST_MIPMAP_NEAREST

GL_NEAREST_MIPMAP_LINEAR

GL_LINEAR

GL_LINEAR_MIPMAP_NEAREST

GL_LINEAR_MIPMAP_LINEAR

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

放大 情况 :

GL_NEAREST

GL_LINEAR

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

最近邻过滤:这个比较简单,每个像素的纹理坐标,并不是刚好对应一个采样点时,按照最接近的采样点进行采样。当放大纹理时,锯齿会比较明显,

每个纹理单元都显示为一个小方块。当缩小时,会丢失许多细节。

双线性过滤:使用双线性插值平滑像素之间的过渡。放大时,锯齿看起来会比最近邻过滤少许多,看起来更加平滑。

MIP贴图 :当缩小到一定程度时,使用双线性过滤会失去太多细节,还可能引起噪声以及物体移动过程中的闪烁,为了克服这些问题就有了MIP贴图技术。

关于MIP贴图 ,维基上有更详细解释 :https://en.wikipedia.org/wiki/Mipmap

三线性过滤 :用于消除每个MIP贴图级别之间的过渡,得到一个更为平滑的图像。

新的着色器

在创建新的着色器之前,我们先明确一下Opengl的二维纹理坐标

对于一个二维的纹理,它有着自己的坐标空间,如上图所示。其有两个维度,S 与 T ,范围都是 0 到 1 。

顶点着色器代码如下 :

//texture_vertex_shader.glsl
uniform mat4 u_Matrix;
attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;

varying vec2 v_TextureCoordinates;

void main()
{
    v_TextureCoordinates = a_TextureCoordinates;
    gl_Position = u_Matrix * a_Position;
} 

其中 a_TextureCoordinates 用于接收纹理的坐标数据,v_TextureCoordinates用于将纹理数据传递给片段着色器,因为纹理有两个分量 S与 T 所以使用vec2类型。

片段着色器如下:

//texture_fragment_shader.glsl
precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;      	   								

void main()
{
    gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
}

texture2D是着色器语言内置的纹理采样函数,用于根据指定的纹理坐标就行纹理采样,返回值类型为vec4 。

纹理坐标

//矩形顶点坐标  与 纹理坐标
    static float squareCoords[] = { //以三角形扇的形式绘制
    	//x     y       s   t
    	-0.5f,  0.5f ,  0 , 0 , // top left
        0.5f,  0.5f  ,  1 , 0 ,// top right
        0.5f, -0.5f  ,  1 , 1 ,// bottom right
       -0.5f, -0.5f  ,  0 , 1};  // bottom left  

我们绘制的矩形的左上角对应着纹理的 (0,0 )  大家注意这个映射关系,也就是我们拿出一张图其左上角的纹理坐标为 (0,0)而不是 (0,1)。

前面我们已经完成了纹理工具类,下面只需要使用它,然互将数据传入着色器。此时Square类代码如下 (Square.java):

package com.cumt.shape;

import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.glActiveTexture;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glUniform1i;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglesrectangle.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import com.cumt.utils.TextureHelper;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;

public class Square {  

    private Context context;  

    //float类型的字节数
    private static final int BYTES_PER_FLOAT = 4;
    // 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 2;  

    /*------------------修改顶点数据 ,加入顶点对应的纹理坐标-------------------------*/
    //矩形顶点坐标  与 纹理坐标
    static float squareCoords[] = { //以三角形扇的形式绘制
    	//x     y       s   t
    	-0.5f,  0.5f ,  0 , 0 , // top left
        0.5f,  0.5f  ,  1 , 0 ,// top right
        0.5f, -0.5f  ,  1 , 1 ,// bottom right
       -0.5f, -0.5f  ,  0 , 1};  // bottom left  

    private FloatBuffer vertexBuffer;
    private static final int VERTEX_COUNTS = 4;//顶点坐标数
    private static final int POSITION_COMPONENT_COUNT = 2;  //一个顶点坐标含有的元素个数
    private static final int TEXTURE_COORDIANTES_COMPONENT_COUNT = 2; //一个纹理坐标含有的元素个数
    //因为我们的顶点数据和纹理坐标数据放在了一起 ,所以在使用glVertexAttribPointer等函数时,其中的stride参数就需要传入了,
    //用于高速着色器应该如何读取坐标值 ,比如这里我们的着色器读取坐标时,设置从位置 0开始读,读取x , y后就会跳过 s t 接着读取 x y
    //这就是通过传入stride参数实现的
	private static final int STRIDE = (POSITION_COMPONENT_COUNT + TEXTURE_COORDIANTES_COMPONENT_COUNT)
			* BYTES_PER_FLOAT;
    //------------第一个是顶点着色器的变量名,第二个是片段着色器的变量名
    private static final String A_POSITION = "a_Position";
    private static final String U_MATRIX = "u_Matrix";
    private static final String A_TEXTURE_COORDINATES = "a_TextureCoordinates";//纹理
	private static final String U_TEXTURE_UNIT = "u_TextureUnit";//纹理

    private int aPositionLocation;
    private int uMatrixLocation;
    private int uTextureUnitLocation;
    private int aTextureCoordinates;
    private int program;//保存program的id
    private int texture;

    float[] projectionMatrix = new float[16];//变换矩阵

    public Square(Context context) {
        this.context = context;
        vertexBuffer = ByteBuffer
                .allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        // 把坐标们加入FloatBuffer中
        vertexBuffer.put(squareCoords);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);  

        getProgram();  

        aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
        uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);
        aTextureCoordinates = GLES20.glGetAttribLocation(program, A_TEXTURE_COORDINATES);
		uTextureUnitLocation = GLES20.glGetAttribLocation(program, U_TEXTURE_UNIT);
		texture = TextureHelper.loadTexture(context, R.drawable.umei);
		// Set the active texture unit to texture unit 0.
        glActiveTexture(GL_TEXTURE0);
        // Bind the texture to this unit.
        glBindTexture(GL_TEXTURE_2D, texture);
        // Tell the texture uniform sampler to use this texture in the shader by
        // telling it to read from texture unit 0.
        glUniform1i(uTextureUnitLocation, 0);
        //传入顶点坐标和纹理坐标
        GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT,
				GLES20.GL_FLOAT, false, STRIDE, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
		//设置从第二个元素开始读取,因为从第二个元素开始才是纹理坐标
		vertexBuffer.position(POSITION_COMPONENT_COUNT);
		GLES20.glVertexAttribPointer(aTextureCoordinates, TEXTURE_COORDIANTES_COMPONENT_COUNT,
				GLES20.GL_FLOAT, false, STRIDE, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aTextureCoordinates);
    }  

    //获取program
    private void getProgram(){
        //获取顶点着色器文本
        String vertexShaderSource = TextResourceReader
                .readTextFileFromResource(context, R.raw.texture_vertex_shader);
        //获取片段着色器文本
        String fragmentShaderSource = TextResourceReader
                .readTextFileFromResource(context, R.raw.texture_fragment_shader);
        //获取program的id
        program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
        GLES20.glUseProgram(program);
    }  

    //设置正交投影矩阵
    public void projectionMatrix(int width,int height){
        final float aspectRatio = width > height ?
                (float) width / (float) height :
                (float) height / (float) width;
        if(width > height){
            Matrix.orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
        }else{
            Matrix.orthoM(projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
        }
    }  

    //以GL_LINE_LOOP方式绘制
    public void draw(){
    	GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, VERTEX_COUNTS);
    }
}

为了方便观察,将MyRender类onSurfaceCreated方法中设置的颜色更改为 glClearColor(0.5f,0.5f,0.5f, 1.0f);

运行一下 :

时间: 2024-12-26 18:43:39

OpenglES2.0 for Android:纹理映射的相关文章

OpenglES2.0 for Android:再谈纹理映射

OpenglES2.0 for Android:再谈纹理映射 前言 上一节我们实现了一个简单的纹理映射的例子--一个简单的贴图,这节我们来做一些稍微复杂一点的例子,最后再给我们前面的立方体做一个纹理. 纹理拉伸 重复拉伸方式 这种是经常使用的一张纹理拉伸方式,常用于绘制一些重复的元素,比如我们在游戏绘制一幅方格式的地图时.使用重复拉伸方式使得纹理能够根据目标平 面的大小自动重复,这样既不会失去纹理图的效果,也可以节省内存.如下图所示: 实现起来很简单,我们回到上节的项目,找到我们纹理的工具类Te

OpenglES2.0 for Android:来画个立方体吧

OpenglES2.0 for Android:来画个立方体吧 前言: 前面一直在说OpenglES2.0二维图形的绘制,接下来我们步入三维的世界 ,三维世界远比二维要有趣的多,与此同时复杂性也要高得多,在unity3D中我们可以很容易的就创建 一个立方体,而在OpenglES2.0中这个过程要复杂得多,但是更加有趣 .先来看下我们的整个流程: 摄像机的设置: 想想你的摄像头,它的位置不同,朝向不同,对同一个事物拍摄得到的画面肯定是不同的,Opengl中的摄像头和我们日常生活中的摄像头是一样的道

OpenglES2.0 for Android:各种变换来一波

OpenglES2.0 for Android:各种变换来一波 监听屏幕事件 在进行各种变换之前,我们先来了解一下如何监听屏幕的事件.我们下面的变换都需要用立方体来演示,所以我们继续使用上一节的绘制立方体的内容 首先新建一个项目 OpengESChange ,将上一节中关于绘制立方体的代码复制过来 .在前面我们一直在使用 android.opengl.GLSurfaceView 在第一篇中我们已经知道了这个类的作用,为了监听屏幕事件,我们创建一个类继承自该类,重写其onTouchEvent方法.

OpenglES2.0 for Android:来画个球吧

OpenglES2.0 for Android:来画个球吧 理解球坐标系 首先看下球的坐标系 ,如图 : (图来自百度百科 ) 设球上有一点 A ,球心为O ,OA在 xOy上的投影与X轴夹角为 φ (范围为 0 到360 ,单位 :度), OA在与Z的夹角为 θ (范围为 0 到 180  ,单位:度 ),球的半径为r,则有 ; r * sin θ = y / sin φ    r * sinθ  = x / cos φ   z = r * cos θ 由此可得 X,Y,Z坐标 我们前面已经知

OpenglES2.0 for Android:来画个三角形吧

OpenglES2.0 for Android:来画个三角形吧 先看看我们的整个流程: 理解坐标系: 左侧是Opengl默认的坐标系,右边是典型的android设备屏幕的坐标系.左侧的瘦瘦的三角形映射到android屏幕上就变成了胖胖的三角形(屏幕横向的时候),我们可以使用 camera和投影解决这个问题,具体怎么解决这里就先不累述了.这里我们只需要知道屏幕的左上角是(-1.0,1.0)横向向右为X轴正向,纵向向下为Y轴 负向,其范围都是从 -1到 +1. 定义三角形顶点: 我们在第一个andr

OpenglES2.0 for Android:来画个矩形吧

OpenglES2.0 for Android:来画个矩形吧 上一节中我们绘制了一个三角形,我们在上一节的基础上来完成矩形的绘制 . OK,开始动手做吧,首先在上一节的项目中的shape目录下新建一个类--Square (Square.java),然后定义矩形的四个顶点的坐标,此时代码如下(Square.java): <span style="font-size:14px;">package com.cumt.shape; public class Square { //f

Android上使用OpenglES2.0遇到的一点问题

按照教程开发OpenglES2.0应用,遇到Logcat报错“Called unimplemented OpenGL ES API” 在论坛和stackoverflow上找到了答案. 1.manifest里面加上 <uses-feature android:glEsVersion="0x00020000" android:required="true" /> 2.surfaceView要设置 mGLSurfaceView.setEGLContextCli

windows下Qt5.1.0配置android环境搭建 good

1.首先下载好需要配置的软件: 1>Qt 5.1.0 for Android (Windows 32-bit, 716 MB)(Info)下载地址: http://qt-project.org/downloads 2>android SDK tools (只下载tools即可)下载地址:http://developer.android.com/sdk/index.html android SDK 离线安装包(已是最新sdk 2013/7/26)下载地址:http://www.wuleba.co

cocos2d-x3.0 编译android出现的问题笔记 &nbsp;cocos2dx3.0 Android.mk

1.编译时出现 No rule to make target 错误,尝试删除XXX/proj.android/obj/local/armeabi/objs这个文件夹. 2.android.mk正确写法 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := cocos2dcpp_shared LOCAL_MODULE_FILENAME := libcocos2dcpp # 遍历目录及子目录的函数 define walk