本文是一个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的context private Context mContext; //FireWallpaperRenderer构造函数,用来初始化mContext public FireWallpaperRenderer(Context context){ mContext = context; } /** * 渲染场景的代码,这里我们是通过调用底层C++的代码来实现的, * 这样能得到更好地运行速度 * */ @Override public void onDrawFrame(GL10 gl) { //调用本地onDrawFrame方法 FireNativeMethod.onDrawFrame(gl); } /** * 处理屏幕尺寸发生变化时的代码, * 用来重新设置场景的大小和其他一些属性, * 也是通过调用底层C++代码来实现 * */ @Override public void onSurfaceChanged(GL10 gl, int width, int height) { //调用本地onSurfaceChanged方法 FireNativeMethod.onSurfaceChanged(gl, width, height); } /** * 初始化OpenGL场景, * 用来设置场景的一些属性, * 也是通过调用底层C++代码来实现 * */ @Override public 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); //加载Bitmap Bitmap 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生成Bitmap bitmap = 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和RendererMode setRenderer(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; // 设置粒子的生命值为0 FireParticles[p].Age = 0; // 设置粒子的最大生命值为300 FireParticles[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 Value glFogf(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_method LOCAL_SRC_FILES := com_ygc_FireNativeMethod.cpp Fire.cpp main.cpp LOCAL_LDLIBS :=-L$(SYSROOT)/usr/lib -lGLESv2 LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lGLESv1_CM LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog include $(BUILD_SHARED_LIBRARY)