利用android_ndk以及OpenGLES开发动态壁纸

来源:互联网 发布:xnview中文版官方 mac 编辑:程序博客网 时间:2024/05/01 18:30

本文是一个android动态壁纸的例子,利用android_ndk调用底层的C++代码,使用OpenGLES来绘制动态壁纸。仅作参考。

首先是定义我们自己的Renderer类,FireWallpaperRenderer实现了GLWallpaperService.Renderer接口(GLWallpaperService的代码在《android利用OpenGLES开发动态壁纸用到的GLWallpaperService类》的那篇博客里

import java.io.IOException;import java.io.InputStream;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLUtils;import android.util.Log;public class FireWallpaperRenderer implements GLWallpaperService.Renderer {//用于纹理映射的绑定,并把绑定后的地址传递给C++代码,供其调用private int[] texture = new int[2];//用于加载Bitmap的contextprivate Context mContext;//FireWallpaperRenderer构造函数,用来初始化mContextpublic FireWallpaperRenderer(Context context){mContext = context;}/** * 渲染场景的代码,这里我们是通过调用底层C++的代码来实现的, * 这样能得到更好地运行速度 * */@Overridepublic void onDrawFrame(GL10 gl) {//调用本地onDrawFrame方法FireNativeMethod.onDrawFrame(gl);}/** * 处理屏幕尺寸发生变化时的代码, * 用来重新设置场景的大小和其他一些属性, * 也是通过调用底层C++代码来实现 * */@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {//调用本地onSurfaceChanged方法FireNativeMethod.onSurfaceChanged(gl, width, height);}/** * 初始化OpenGL场景, * 用来设置场景的一些属性, * 也是通过调用底层C++代码来实现 * */@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {//用来绑定Bitmap纹理bindTexture(gl, mContext);//调用本地setTexture方法,把纹理绑定的地址传递给C++代码,以供其调用FireNativeMethod.setTexture(texture);//调用本地onSurfaceCreated方法FireNativeMethod.onSurfaceCreated(gl, config);}/** * 用来绑定Bitmap纹理的代码, * 因为暂时没有找到在C++代码中绑定Bitmap纹理的方法, * 所以暂且在java层绑定Bitmap纹理 * */private void bindTexture(GL10 gl, Context context) {//生成纹理gl.glGenTextures(2, texture, 0);//加载BitmapBitmap bitmap = loadBitmap(context, R.drawable.floor);if (bitmap != null) {Log.i("firewallpaperrenderer", "bind the floor texture");//如果bitmap加载成功,则生成此bitmap的纹理映射gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[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();}bitmap = loadBitmap(context, R.drawable.fire);if (bitmap != null) {//同上Log.i("firewallpaperrenderer", "bind the fire texture");gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[1]);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.recycle();}}/** * 加载Bitmap的方法, * 用来从res中加载Bitmap资源 * */private Bitmap loadBitmap(Context context, int resourceId) {InputStream is = context.getResources().openRawResource(resourceId);Bitmap bitmap = null;try {// 利用BitmapFactory生成Bitmapbitmap = BitmapFactory.decodeStream(is);} finally {try {// 关闭流is.close();is = null;} catch (IOException e) {e.printStackTrace();}}return bitmap;}}

然后定义我们的WallpaperService,FireWallpaperService继承自GLWallpaperService:

public class FireWallpaperService extends GLWallpaperService {//定义FireWallpaperRenderer实例private FireWallpaperRenderer mRenderer;public Engine onCreateEngine() {if (mRenderer == null) {mRenderer = new FireWallpaperRenderer(this);}return new FireWallpaperEngine();}class FireWallpaperEngine extends GLWallpaperService.GLEngine {public FireWallpaperEngine() {//设置Renderer和RendererModesetRenderer(mRenderer);setRenderMode(RENDERMODE_CONTINUOUSLY);}}}

完成后编辑Manifest.xml文件,如下:

application android:icon="@drawable/icon" android:label="@string/app_name"><service android:name=".FireWallpaperService" android:label="@string/firewallpaper"android:permission="android.permission.BIND_WALLPAPER"><intent-filter><action android:name="android.service.wallpaper.WallpaperService" /></intent-filter><meta-data android:name="android.service.wallpaper"android:resource="@xml/wallpaper" /></service></application>


Manifest.xml文件中,FireWallpaperService使用到的wallpaper文件

(<meta-dataandroid:name="android.service.wallpaper"android:resource="@xml/wallpaper" />)在res/xml目录下定义,内容如下:

<?xml version="1.0" encoding="utf-8"?><wallpaper xmlns:android="http://schemas.android.com/apk/res/android"android:description="@string/description" android:thumbnail="@drawable/firelivewallpaper" />


然后是我们的本地方法类——FireNativeMethod:

import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;public class FireNativeMethod {//加载本地库文件static{System.loadLibrary("fire_native_method");}//渲染场景本地方法public static native void onDrawFrame(GL10 gl);//处理屏幕尺寸变化本地方法public static native void onSurfaceChanged(GL10 gl, int width, int height);//初始化OpenGL场景本地方法public static native void onSurfaceCreated(GL10 gl, EGLConfig config);//传递纹理映射地址本地方法public static native void setTexture(int [] textureString);}


之后就是我们的本地C++代码部分了。

首先我们要定义和篝火粒子属性设置相关的类的头文件(Fire.h):

#ifndef _FIRE#define _FIRE#include <GLES2/gl2.h>#include <GLES2/gl2ext.h>#include <GLES/gl.h>#include <stdlib.h>#include <math.h>//声明用来调用纹理映射的数组extern GLuint *texture;/** * Fire类 * */class Fire{public:Fire(void);~Fire(void);void InitFire();   //初始化篝火粒子//下面两个方法用来设置篝火粒子的属性值,以供渲染篝火粒子所用void PrepareFire();void ActivateFireParticles();void RenderFire();  //渲染篝火粒子// 粒子结构体struct PARTICLE {float X,Y,Z; // 当前位置float sX,sY,sZ; // 当前移动速度float tX,tY,tZ; // 目标移动速度float R,B,G; // 粒子颜色float Size; // 粒子大小bool Active; // 粒子是否可见int Age; // 粒子的生命值int MaxAge; // 粒子消失前的最大生命值};//粒子结构体数组PARTICLE * FireParticles;//粒子个数int FireParticleCount;//每帧活动粒子数int ActivateFirePerFrame;};#endif


然后是其相关的C++文件(Fire.cpp):

#include "Fire.h"//初始化纹理数组GLuint *texture = 0;Fire::Fire(void) {}/** * 释放粒子结构体数组 * */Fire::~Fire(void) {delete[] FireParticles;FireParticles = 0;}/** * 初始化粒子 * */void Fire::InitFire() {int p;//粒子的个数FireParticleCount = 500;//每帧粒子个数ActivateFirePerFrame = 40;//初始化粒子结构体数组FireParticles = new PARTICLE[FireParticleCount];//初始粒子不可见for (p = 0; p < FireParticleCount; p++) {FireParticles[p].Active = false;}}void Fire::PrepareFire() {int p;for (p = 0; p < FireParticleCount; p++) {// 调整粒子的速度FireParticles[p].sX += (FireParticles[p].tX - FireParticles[p].sX)/ 10.0f;FireParticles[p].sY += (FireParticles[p].tY - FireParticles[p].sY)/ 20.0f;FireParticles[p].sZ += (FireParticles[p].tZ - FireParticles[p].sZ)/ 10.0f;// 通过新的速度调整粒子的位置FireParticles[p].X += FireParticles[p].sX;FireParticles[p].Y += FireParticles[p].sY;FireParticles[p].Z += FireParticles[p].sZ;// 调整粒子尺寸FireParticles[p].Size -= 0.002f;if (FireParticles[p].Size < 0.0f) {FireParticles[p].Active = false;}// 调整粒子颜色FireParticles[p].R -= 0.01f;FireParticles[p].G -= 0.03f;if (FireParticles[p].R < 0.1f)FireParticles[p].R = 0.1f;if (FireParticles[p].G < 0.1f)FireParticles[p].G = 0.0f;// 最后检查粒子的生命值,如果生命值大于最大生命值,设置粒子不可见FireParticles[p].Age++;if (FireParticles[p].Age > FireParticles[p].MaxAge) {FireParticles[p].Active = false;}}}void Fire::ActivateFireParticles() {int p;for (p = 0; p < FireParticleCount; p++) {if (!FireParticles[p].Active) {// 设置粒子的起始位置FireParticles[p].X = (((float) ((rand() % 50) + 1)) / 100.0f)- 0.25f;FireParticles[p].Y = 0.0f;FireParticles[p].Z = (((float) ((rand() % 50) + 1)) / 100.0f)- 0.25f;// 设置粒子的目标速度FireParticles[p].tX = 0.0f;FireParticles[p].tY = 0.01f;FireParticles[p].tZ = 0.0f;// 生成粒子随机速度FireParticles[p].sX = (((float) ((rand() % 30) + 1)) / 1000.0f)- 0.015f;FireParticles[p].sY = (((float) ((rand() % 50) + 1)) / 1000.0f);FireParticles[p].sZ = (((float) ((rand() % 30) + 1)) / 1000.0f)- 0.015f;//设置粒子可见FireParticles[p].Active = true;// 设置粒子的生命值为0FireParticles[p].Age = 0;// 设置粒子的最大生命值为300FireParticles[p].MaxAge = 300;// 设置粒子颜色FireParticles[p].R = 0.7f;FireParticles[p].G = 0.6f;FireParticles[p].B = 0.0f;// 设置粒子尺寸FireParticles[p].Size = (((float) ((rand() % 15))) / 100.0f);return;}}}void Fire::RenderFire() {for (int i = 0; i < ActivateFirePerFrame; i++) {ActivateFireParticles();}PrepareFire();int p;GLfloat fogColor[4] = { 0.0f, 0.0f, 0.0f, 0.5f };glFogx(GL_FOG_MODE, GL_LINEAR); // 设置雾模式glFogfv(GL_FOG_COLOR, fogColor); // 设置雾颜色glFogf(GL_FOG_DENSITY, 0.35f); // 设置雾密度glHint(GL_FOG_HINT, GL_DONT_CARE); // Fog Hint ValueglFogf(GL_FOG_START, 1.0f); // 设置雾的开始深度glFogf(GL_FOG_END, 2.0f); // 设置雾的结束深度glEnable(GL_FOG); // 启用雾模式GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };GLfloat LightPosition[] = { 0.0f, 0.1f, 0.0f, 1.0f };glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);glEnable(GL_LIGHT1);glEnable(GL_LIGHTING);/** * 开始渲染地面 * *///启用纹理,并绑定地面纹理,禁用混合glEnable(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, texture[0]);glDisable(GL_BLEND);//设置颜色glColor4f(1.0f, 1.0f, 1.0f, 1.0f);//法线向量float normals[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.0f, 1.0f, 0.0f };//纹理坐标float texCoords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f };//顶点坐标float vertecies[] = { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.0f, -0.5f };//绘制地面for (int y = 0; y < 20; y++) {for (int x = 0; x < 20; x++) {glPushMatrix();glTranslatef(-5.0f, 0.0f, 5.0f);glTranslatef((float) x / 2, 0.0f, -(float) y / 2);glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glEnableClientState(GL_NORMAL_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertecies);glTexCoordPointer(2, GL_FLOAT, 0, texCoords);glNormalPointer(GL_FLOAT, 0, normals);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);glDisableClientState(GL_VERTEX_ARRAY);glDisableClientState(GL_TEXTURE_COORD_ARRAY);glDisableClientState(GL_NORMAL_ARRAY);glPopMatrix();}}//禁用光照和雾模式glDisable(GL_LIGHTING);glDisable(GL_FOG);/** * 渲染地面结束 * *//** * 开始渲染篝火 * */// 启用纹理,绑定我们的粒子纹理glEnable(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, texture[1]);// 禁用深度测试glDisable(GL_DEPTH_TEST);// 启用混合glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE);//绘制我们的篝火for (p = 0; p < FireParticleCount; p++) {if (FireParticles[p].Active) {glColor4f(FireParticles[p].R, FireParticles[p].G,FireParticles[p].B, 1.0f);glPushMatrix();glTranslatef(FireParticles[p].X, FireParticles[p].Y,FireParticles[p].Z);glNormal3f(0.0f, 0.0f, 1.0f);float vertecies[] = { -FireParticles[p].Size,-FireParticles[p].Size, 0.0f, FireParticles[p].Size,-FireParticles[p].Size, 0.0f, -FireParticles[p].Size,FireParticles[p].Size, 0.0f, FireParticles[p].Size,FireParticles[p].Size, 0.0f };glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertecies);glTexCoordPointer(2, GL_FLOAT, 0, texCoords);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);glDisableClientState(GL_VERTEX_ARRAY);glDisableClientState(GL_TEXTURE_COORD_ARRAY);glPopMatrix();}}/** * 渲染篝火结束 * *///重新启用深度测试glEnable(GL_DEPTH_TEST);}


接着是我们处理OpenGL初始化、设置和渲染的方法的头文件——Stdx.h:

#ifndef _STDX#define _STDX#include "Fire.h"//初始化OpenGL场景extern void InitGL();//处理屏幕尺寸变化extern void SizeChanged(int width ,int height);//渲染场景extern void RendererGL();//计算场景透视extern void gluPerspective(double fovy, double aspect, double zNear,double zFar);#endif


 

然后是其相关的C++文件——main.cpp:

#include "Stdx.h"Fire Fire;  //Fire实例float aspectRatio;   //用于透视计算void InitGL() {glShadeModel(GL_SMOOTH);   // 启用平滑模式glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   // 设置黑色背景glClearDepthf(1.0f);    // 设置深度缓存glEnable(GL_DEPTH_TEST);   // 启用深度测试glDepthFunc(GL_LEQUAL);   // 深度测试类型glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   // 获得良好的透视效果Fire.InitFire();   //初始化粒子}void SizeChanged(int width ,int height) {glViewport(0, 0, width, height);  //重设窗口大小glMatrixMode(GL_PROJECTION);  //启用投影矩阵glLoadIdentity();     //重置投影矩阵aspectRatio = float(width) / float(height);  //获得屏幕宽、高比gluPerspective(45.0, aspectRatio, 0.1, 100.0);  //计算透视glMatrixMode(GL_MODELVIEW);  //启用模型视图矩阵glLoadIdentity();   //重置模型视图矩阵}void RendererGL() {glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // 首先清除颜色和深度缓存glLoadIdentity();// 重设模型视图矩阵glTranslatef(0.0f, -0.5f, -2.2f);  //移动场景Fire.RenderFire(); //渲染篝火}void gluPerspective(double fovy, double aspect, double zNear, double zFar) {glMatrixMode(GL_PROJECTION);   //启用投影矩阵glLoadIdentity();   //重置投影矩阵//计算透视double xmin, xmax, ymin, ymax;ymax = zNear * tan(fovy * M_PI / 360.0);ymin = -ymax;xmin = ymin * aspect;xmax = ymax * aspect;//设置透视glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);glMatrixMode(GL_MODELVIEW);  //启用模型视图矩阵glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // 获得良好的透视效果}


之后我们需要把我们的本地方法类——FireNativeMethod利用javah命令生成正确的头文件,然后在其对应的C++文件(com_ygc_FireNativeMethod.cpp)中定义头文件中的方法,在其方法中调用相应的本地代码实现其功能:

#include "com_ygc_FireNativeMethod.h"#include "Stdx.h"/* * 调用RendererGL方法渲染场景 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onDrawFrame  (JNIEnv *env, jclass cls, jobject obj){RendererGL();}/* * 调用SizeChanged调整场景 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onSurfaceChanged  (JNIEnv *env, jclass cls, jobject obj, jint width, jint height){SizeChanged(width,height);}/* * 调用InitGL初始化场景 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onSurfaceCreated  (JNIEnv *env, jclass cls, jobject obj1, jobject obj2){InitGL();}/* * 获得纹理绑定地址 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_setTexture  (JNIEnv *env, jclass cls, jintArray tex){texture = (GLuint *)env->GetIntArrayElements(tex,0);}


 

最后是我们的Android.mk文件:

# Copyright (C) 2009 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.#LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := fire_native_methodLOCAL_SRC_FILES := com_ygc_FireNativeMethod.cpp Fire.cpp main.cppLOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -lGLESv2LOCAL_LDLIBS+=-L$(SYSROOT)/usr/lib -lGLESv1_CMLOCAL_LDLIBS+= -L$(SYSROOT)/usr/lib -lloginclude $(BUILD_SHARED_LIBRARY)
原创粉丝点击