首页 > 代码库 > Opengl ES 1.x NDK实例开发之七:旋转的纹理立方体

Opengl ES 1.x NDK实例开发之七:旋转的纹理立方体

开发框架介绍请参见:Opengl ES NDK实例开发之一:搭建开发框架

本章在第六章(Opengl ES 1.x NDK实例开发之六:纹理贴图)的基础上绘制一个旋转的纹理立方体,原理和纹理贴图一样,需要注意的是定好正方体的顶点数组。

【实例讲解】

本实例添加了一个显示fps的小功能,在java层实现,原理是统计一分钟内屏幕刷新的次数


【实例源码】

[GLJNIActivity.java]

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * author: mnorst@foxmail.com
 */

package com.android.gljni;

import java.text.DecimalFormat;

import com.android.gljni.GLJNIView;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;

public class GLJNIActivity extends Activity {
	GLJNIView mView;
	TextView mTextView;

	@Override
	protected void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		mView = new GLJNIView(getApplication());
		setContentView(mView);
		
		mView.setHandler(new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                
                // 显示fps
                mTextView.setText("fps:"+msg.what);
            }
        }
        );
		
		mTextView = new TextView(this);
		mTextView.setText("fps:0");
		addContentView(mTextView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
	}

	@Override
	protected void onPause() {
		super.onPause();
		mView.onPause();
	}

	@Override
	protected void onResume() {
		super.onResume();
		mView.onResume();
	}
}

[GLJNIView.java]

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * author: mnorst@foxmail.com
 */

package com.android.gljni;

import java.io.IOException;
import java.io.InputStream;

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

import com.android.gljni.GLJNILib;
import com.android.gljnidemo07.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.os.Handler;
import android.util.Log;

/**
 * A simple GLSurfaceView sub-class that demonstrate how to perform
 * OpenGL ES 1.x rendering into a GL Surface.
 */
public class GLJNIView extends GLSurfaceView {

	private static final String LOG_TAG = GLJNIView.class.getSimpleName();

	private Renderer renderer;

	public GLJNIView(Context context) {
		super(context);

		// setEGLConfigChooser会对fps产生影响
		setEGLConfigChooser(8, 8, 8, 8, 16, 0);

		renderer = new Renderer(context);
		setRenderer(renderer);
	}
	
	public void setHandler( Handler handler){
		renderer.setHandler(handler);
	}

	private static class Renderer implements GLSurfaceView.Renderer {
		//用于纹理映射的绑定,并把绑定后的ID传递给C++代码,供其调用
		private int[] mTexture = new int[2];
		//用于加载Bitmap的context
		private Context mContext;
		
		// 统计fps
		private Handler mHandler;
		private long mStartMili;			
		private long mEndMili;
		private int mFps = 0;
		
		public Renderer(Context ctx) {
			mContext = ctx;
			mStartMili =System.currentTimeMillis();
		}
		
		public void setHandler( Handler handler){
			mHandler = handler;
		}

		public void onDrawFrame(GL10 gl) {
			
			GLJNILib.step();
			
			// 以一分钟绘制的帧数来统计fps
			mEndMili = System.currentTimeMillis();
			if( mEndMili - mStartMili > 1000 ){
				mHandler.sendEmptyMessageDelayed(mFps, 100);
				mStartMili = mEndMili;
				mFps = 0;
			}
			mFps++;
		}

		public void onSurfaceChanged(GL10 gl, int width, int height) {
			GLJNILib.resize(width, height);
		}

		public void onSurfaceCreated(GL10 gl, EGLConfig config) {
			//用来绑定Bitmap纹理
			genTexture(gl, mContext);
			//调用本地setTexture方法,把纹理绑定的ID传递给C++代码,以供其调用
			GLJNILib.setTexture(mTexture);
			GLJNILib.init();
		}
		
		/**
		 * 加载Bitmap的方法,
		 * 用来从res中加载Bitmap资源
		 * */
		private Bitmap loadBitmap(Context context, int resourceId) {
			InputStream is = context.getResources().openRawResource(resourceId);
			Bitmap bitmap = null;
			try {

				// 利用BitmapFactory生成Bitmap
				bitmap = BitmapFactory.decodeStream(is);
			} finally {
				try {

					// 关闭流
					is.close();
					is = null;
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
			return bitmap;

		}
		
		/**
		 * 绑定Bitmap纹理
		 * */

		private void genTexture(GL10 gl, Context context) {
			//生成纹理
			gl.glGenTextures(2, mTexture, 0);
			//加载Bitmap
			Bitmap bitmap = loadBitmap(context, R.drawable.logo);
			if (bitmap != null) {
				//如果bitmap加载成功,则生成此bitmap的纹理映射
				gl.glBindTexture(GL10.GL_TEXTURE_2D, mTexture[0]);
				//设置纹理映射的属性
				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_NEAREST);
				//生成纹理映射
				GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
				//释放bitmap资源
				bitmap.recycle();
			}

		}
	}

}
[GLJNILib.java]
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * author: mnorst@foxmail.com
 */

package com.android.gljni;

//Wrapper for native library
public class GLJNILib {
	
	static {
		System.loadLibrary("gljni");
	}

	/**
     * @param width the current view width
     * @param height the current view height
     */
	public static native void resize(int width, int height); 
	
	/**
	 * render 
	 */
    public static native void step();  
    
    /**
     * init
     */
    public static native void init();  
    
    /**
     * set the texture
     * @param texture	texture id
     */
    public static native void setTexture(int[] texture);
}

[[gl_code.cpp]

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * author: 	mnorst@foxmail.com
 * created:	2014/10/27
 * purpose:	旋转的纹理立方体
 */

// OpenGL ES 1.x code

#include <jni.h>
#include <android/log.h>

#include <GLES/gl.h>
#include <GLES/glext.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/************************************************************************/
/*                             定义                                     */
/************************************************************************/

#define  LOG_TAG    "libgljni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

//初始化纹理数组
GLuint *gTexture = 0;

// 定义π
const GLfloat PI = 3.1415f;

// 定义顶点坐标
#define pos 1.0f

// 定义顶点坐标
// 一个正方体有8个顶点,6个面
#define one 1.0f
static GLfloat gVertices[] = {
	one, one, -one,   
	-one, one, -one, 
	one, one, one, 

	-one, one, one, 
	one, -one,one,
	-one, -one, one,

	one, -one, -one, 
	-one, -one, -one, 
	one, one,one,

	-one, one, one,
	one, -one, one,
	-one, -one, one,

	one, -one,-one,
	-one, -one, -one,
	one, one, -one,

	-one, one, -one,
	-one, one,one,
	-one, one, -one,

	-one, -one, one, 
	-one, -one, -one,
	one, one,-one,

	one, one, one,
	one, -one, -one,
	one, -one, one 
};

// 定义纹理坐标
// 纹理坐标原点会因不同系统环境而有所不同。
// 比如在iOS以及Android上,纹理坐标原点(0, 0)是在左上角
// 而在OS X上,纹理坐标的原点是在左下角
static GLfloat gTexCoords[] = {   
	0, one,                 
	one, one, 
	0, 0, 
	one, 0, 

	0, one,                 
	one, one, 
	0, 0, 
	one, 0, 

	0, one,                 
	one, one, 
	0, 0, 
	one, 0, 

	0, one,                 
	one, one, 
	0, 0, 
	one, 0, 

	0, one,                 
	one, one, 
	0, 0, 
	one, 0, 

	0, one,                 
	one, one, 
	0, 0, 
	one, 0, 


}; 

// 旋转角度
static GLfloat gAngle = 0.0f;
/************************************************************************/
/*                             C++代码                                  */
/************************************************************************/

static void printGLString(const char *name, GLenum s) {
	const char *v = (const char *) glGetString(s);
	LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
	for (GLint error = glGetError(); error; error = glGetError()) {
			LOGI("after %s() glError (0x%x)\n", op, error);
	}
}

bool init() {
	printGLString("Version", GL_VERSION);
	printGLString("Vendor", GL_VENDOR);
	printGLString("Renderer", GL_RENDERER);
	printGLString("Extensions", GL_EXTENSIONS);

	// 启用阴影平滑
	glShadeModel(GL_SMOOTH);

	// 黑色背景	
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);	

	// 设置深度缓存	
	glClearDepthf(1.0f);

	// 启用深度测试
	glEnable(GL_DEPTH_TEST);	

	// 所作深度测试的类型	
	glDepthFunc(GL_LEQUAL);	

	// 对透视进行修正	
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	

	return true;
}

static void _gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
{
	GLfloat top = zNear * ((GLfloat) tan(fovy * PI / 360.0));
	GLfloat bottom = -top;
	GLfloat left = bottom * aspect;
	GLfloat right = top * aspect;
	glFrustumf(left, right, bottom, top, zNear, zFar);
}

void resize(int width, int height)
{
	// 防止被零除
	if (height==0)								
	{
		height=1;
	}

	// 重置当前的视口
	glViewport(0, 0, width, height);	
	// 选择投影矩阵	
	glMatrixMode(GL_PROJECTION);	
	// 重置投影矩阵	
	glLoadIdentity();							

	// 设置视口的大小
	_gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	// 选择模型观察矩阵
	glMatrixMode(GL_MODELVIEW);	

	// 重置模型观察矩阵
	glLoadIdentity();							
}

void renderFrame() {
	// 清除屏幕和深度缓存
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// 重置当前的模型观察矩阵
	glLoadIdentity();

	glTranslatef(0,0,-10.0f);
	glRotatef(gAngle, 0, 1.0F, 0);
	glRotatef(gAngle, 0, 0, 1.0F);

	// 启用顶点数组
	glEnableClientState(GL_VERTEX_ARRAY);
	//glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	// 启用纹理映射
	glEnable(GL_TEXTURE_2D); 

	// 选择纹理
	glBindTexture(GL_TEXTURE_2D, gTexture[0]); 

	// 绘制正方体的六个面
	glVertexPointer(3,GL_FLOAT,0,gVertices);
	glTexCoordPointer(2, GL_FLOAT, 0, gTexCoords);

	for (int i = 0; i < 6; i++) {
		glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4);
	} 

	// 关闭顶点数组
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	//glDisableClientState(GL_OLOR_ARRAY);

	gAngle += 5.f;
}

/************************************************************************/
/*                          JNI代码                                     */
/************************************************************************/

extern "C" {
	JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_resize(JNIEnv * env, jobject obj,  jint width, jint height);
	JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_step(JNIEnv * env, jobject obj);
	JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_init(JNIEnv * env, jobject obj);
	JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_setTexture(JNIEnv * env, jclass obj, jintArray tex);
};

JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_resize(JNIEnv * env, jobject obj,  jint width, jint height)
{
	resize(width, height);
}

JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_step(JNIEnv * env, jobject obj)
{
	renderFrame();
}

JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_init(JNIEnv * env, jobject obj)
{
	init();
}

JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_setTexture(JNIEnv * env, jclass obj, jintArray tex)
{
	gTexture = (GLuint *)env->GetIntArrayElements(tex,0);
}



Opengl ES 1.x NDK实例开发之七:旋转的纹理立方体