首页 > 代码库 > Opengl ES 1.x NDK实例开发之八:旋转的纹理金字塔

Opengl ES 1.x NDK实例开发之八:旋转的纹理金字塔

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

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

【实例讲解】

技术分享

【实例源码】

[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.gljnidemo08.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:	2015/01/06
 * 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

// 定义顶点坐标
const GLfloat gVertices[] = {


	// 底面
	-1.0f,-1.0f,1.0f,
	1.0f,-1.0f,1.0f,
	1.0f,-1.0f, -1.0f,


	1.0f,-1.0f,-1.0f,
	-1.0f,-1.0f,-1.0f,
	-1.0f,-1.0f,1.0f,

	// 侧面
	0.0f, 1.0f, 0.0f,
	-1.0f,-1.0f, 1.0f,
	1.0f,-1.0f, 1.0f,

	0.0f, 1.0f, 0.0f,
	1.0f,-1.0f, 1.0f,
	1.0f,-1.0f, -1.0f,

	0.0f, 1.0f, 0.0f,
	1.0f,-1.0f, -1.0f,
	-1.0f,-1.0f, -1.0f,

	0.0f, 1.0f, 0.0f,
	-1.0f,-1.0f,-1.0f,
	-1.0f,-1.0f, 1.0f
};

// 定义纹理坐标
// 纹理坐标原点会因不同系统环境而有所不同。
// 比如在iOS以及Android上,纹理坐标原点(0, 0)是在左上角
// 而在OS X上,纹理坐标的原点是在左下角
static GLfloat texCoords[] = {
	// 地面拼接成一张整纹理
	0.0f, 1.0f,
	1.0f, 1.0f,
	1.0f, 0.0f,

	1.0f, 0.0f,
	0.0f, 0.0f,
	0.0f, 1.0f,

	0.5f, 0.0f,
	0.0f, 1.0f,
	1.0f, 1.0f,

	0.5f, 0.0f,
	0.0f, 1.0f,
	1.0f, 1.0f,

	0.5f, 0.0f,
	0.0f, 1.0f,
	1.0f, 1.0f,

	0.5f, 0.0f,
	0.0f, 1.0f,
	1.0f, 1.0f,
}; 

// 旋转角度
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, texCoords);

	glDrawArrays(GL_TRIANGLES, 0, 18);

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

	gAngle += 2.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实例开发之八:旋转的纹理金字塔