本文是一个android动态壁纸的例子,利用android_ndk调用底层的C++代码,使用OpenGLES来绘制动态壁纸。仅作参考。
首先是定义我们自己的Renderer类,FireWallpaperRenderer实现了GLWallpaperService.Renderer接口(GLWallpaperService的代码在《android利用OpenGLES开发动态壁纸用到的GLWallpaperService类》的那篇博客里):
[java] view plaincopyprint?
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:
[java] view plaincopyprint?
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文件,如下:
[html] view plaincopyprint?
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目录下定义,内容如下:
[html] view plaincopyprint?
<span style="color:#000000;"><?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" /></span>
然后是我们的本地方法类——FireNativeMethod:
[java] view plaincopyprint?
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):
[cpp] view plaincopyprint?
#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):
[cpp] view plaincopyprint?
#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:
[cpp] view plaincopyprint?
#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:
[cpp] view plaincopyprint?
#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)中定义头文件中的方法,在其方法中调用相应的本地代码实现其功能:
[cpp] view plaincopyprint?
#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文件:
[plain] view plaincopyprint?
# 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)
转自 http://blog.csdn.net/ygc87/article/details/6779347