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

本文介绍了一个使用Android NDK和OpenGLES开发的动态壁纸示例,重点在于如何结合C++和Java实现高效的动态壁纸渲染,包括粒子效果的实现。
部署运行你感兴趣的模型镜像

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

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

  1. import java.io.IOException;  
  2. import java.io.InputStream;  
  3.   
  4. import javax.microedition.khronos.egl.EGLConfig;  
  5. import javax.microedition.khronos.opengles.GL10;  
  6. import android.content.Context;  
  7. import android.graphics.Bitmap;  
  8. import android.graphics.BitmapFactory;  
  9. import android.opengl.GLUtils;  
  10. import android.util.Log;  
  11.   
  12. public class FireWallpaperRenderer implements GLWallpaperService.Renderer {  
  13.     //用于纹理映射的绑定,并把绑定后的地址传递给C++代码,供其调用   
  14.     private int[] texture = new int[2];  
  15.     //用于加载Bitmap的context   
  16.     private Context mContext;  
  17.       
  18.     //FireWallpaperRenderer构造函数,用来初始化mContext   
  19.     public FireWallpaperRenderer(Context context){  
  20.         mContext = context;  
  21.     }  
  22.       
  23.     /** 
  24.      * 渲染场景的代码,这里我们是通过调用底层C++的代码来实现的, 
  25.      * 这样能得到更好地运行速度 
  26.      * */  
  27.     @Override  
  28.     public void onDrawFrame(GL10 gl) {  
  29.         //调用本地onDrawFrame方法   
  30.         FireNativeMethod.onDrawFrame(gl);  
  31.     }  
  32.       
  33.       
  34.     /** 
  35.      * 处理屏幕尺寸发生变化时的代码, 
  36.      * 用来重新设置场景的大小和其他一些属性, 
  37.      * 也是通过调用底层C++代码来实现 
  38.      * */  
  39.     @Override  
  40.     public void onSurfaceChanged(GL10 gl, int width, int height) {  
  41.         //调用本地onSurfaceChanged方法   
  42.         FireNativeMethod.onSurfaceChanged(gl, width, height);  
  43.     }  
  44.   
  45.     /** 
  46.      * 初始化OpenGL场景, 
  47.      * 用来设置场景的一些属性, 
  48.      * 也是通过调用底层C++代码来实现 
  49.      * */  
  50.     @Override  
  51.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  52.         //用来绑定Bitmap纹理   
  53.         bindTexture(gl, mContext);  
  54.         //调用本地setTexture方法,把纹理绑定的地址传递给C++代码,以供其调用   
  55.         FireNativeMethod.setTexture(texture);  
  56.         //调用本地onSurfaceCreated方法   
  57.         FireNativeMethod.onSurfaceCreated(gl, config);  
  58.   
  59.     }  
  60.       
  61.     /** 
  62.      * 用来绑定Bitmap纹理的代码, 
  63.      * 因为暂时没有找到在C++代码中绑定Bitmap纹理的方法, 
  64.      * 所以暂且在java层绑定Bitmap纹理 
  65.      * */  
  66.   
  67.     private void bindTexture(GL10 gl, Context context) {  
  68.         //生成纹理   
  69.         gl.glGenTextures(2, texture, 0);  
  70.         //加载Bitmap   
  71.         Bitmap bitmap = loadBitmap(context, R.drawable.floor);  
  72.         if (bitmap != null) {  
  73.             Log.i("firewallpaperrenderer""bind the floor texture");  
  74.             //如果bitmap加载成功,则生成此bitmap的纹理映射   
  75.             gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);  
  76.             //设置纹理映射的属性   
  77.             gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,  
  78.                     GL10.GL_NEAREST);  
  79.             gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,  
  80.                     GL10.GL_NEAREST);  
  81.             //生成纹理映射   
  82.             GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);  
  83.             //释放bitmap资源   
  84.             bitmap.recycle();  
  85.         }  
  86.         bitmap = loadBitmap(context, R.drawable.fire);  
  87.         if (bitmap != null) {  
  88.             //同上   
  89.             Log.i("firewallpaperrenderer""bind the fire texture");  
  90.             gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[1]);  
  91.             gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,  
  92.                     GL10.GL_NEAREST);  
  93.             gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,  
  94.                     GL10.GL_NEAREST);  
  95.             GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);  
  96.             bitmap.recycle();  
  97.         }  
  98.   
  99.     }  
  100.   
  101.     /** 
  102.      * 加载Bitmap的方法, 
  103.      * 用来从res中加载Bitmap资源 
  104.      * */  
  105.     private Bitmap loadBitmap(Context context, int resourceId) {  
  106.         InputStream is = context.getResources().openRawResource(resourceId);  
  107.         Bitmap bitmap = null;  
  108.         try {  
  109.   
  110.             // 利用BitmapFactory生成Bitmap   
  111.             bitmap = BitmapFactory.decodeStream(is);  
  112.         } finally {  
  113.             try {  
  114.   
  115.                 // 关闭流   
  116.                 is.close();  
  117.                 is = null;  
  118.             } catch (IOException e) {  
  119.                 e.printStackTrace();  
  120.             }  
  121.   
  122.         }  
  123.         return bitmap;  
  124.   
  125.     }  
  126.   
  127. }  
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:

  1. public class FireWallpaperService extends GLWallpaperService {  
  2.     //定义FireWallpaperRenderer实例   
  3.     private FireWallpaperRenderer mRenderer;  
  4.       
  5.       
  6.     public Engine onCreateEngine() {  
  7.         if (mRenderer == null) {  
  8.             mRenderer = new FireWallpaperRenderer(this);  
  9.         }  
  10.         return new FireWallpaperEngine();  
  11.     }  
  12.   
  13.     class FireWallpaperEngine extends GLWallpaperService.GLEngine {  
  14.   
  15.         public FireWallpaperEngine() {  
  16.             //设置Renderer和RendererMode   
  17.             setRenderer(mRenderer);  
  18.             setRenderMode(RENDERMODE_CONTINUOUSLY);  
  19.         }  
  20.   
  21.     }  
  22.   
  23. }  
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文件,如下:

  1. application android:icon="@drawable/icon" android:label="@string/app_name">  
  2.         <service android:name=".FireWallpaperService" android:label="@string/firewallpaper"  
  3.             android:permission="android.permission.BIND_WALLPAPER">  
  4.             <intent-filter>  
  5.                 <action android:name="android.service.wallpaper.WallpaperService" />  
  6.             </intent-filter>  
  7.             <meta-data android:name="android.service.wallpaper"  
  8.                 android:resource="@xml/wallpaper" />  
  9.         </service>  
  10.   
  11.     </application>  
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目录下定义,内容如下:

  1. <SPAN style="COLOR: #000000"><?xml version="1.0" encoding="utf-8"?>  
  2. <wallpaper xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:description="@string/description" android:thumbnail="@drawable/firelivewallpaper" /></SPAN>  
<?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:

  1. import javax.microedition.khronos.egl.EGLConfig;  
  2. import javax.microedition.khronos.opengles.GL10;  
  3.   
  4. public class FireNativeMethod {  
  5.     //加载本地库文件   
  6.     static{  
  7.         System.loadLibrary("fire_native_method");  
  8.     }  
  9.       
  10.     //渲染场景本地方法   
  11.     public static native void onDrawFrame(GL10 gl);  
  12.     //处理屏幕尺寸变化本地方法   
  13.     public static native void onSurfaceChanged(GL10 gl, int width, int height);  
  14.     //初始化OpenGL场景本地方法   
  15.     public static native void onSurfaceCreated(GL10 gl, EGLConfig config);  
  16.     //传递纹理映射地址本地方法   
  17.     public static native void setTexture(int [] textureString);  
  18.   
  19. }  
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):

  1. #ifndef _FIRE   
  2. #define _FIRE   
  3. #include <GLES2/gl2.h>   
  4. #include <GLES2/gl2ext.h>   
  5. #include <GLES/gl.h>   
  6. #include <stdlib.h>   
  7. #include <math.h>   
  8. //声明用来调用纹理映射的数组   
  9. extern GLuint *texture;  
  10. /** 
  11.  * Fire类 
  12.  * */  
  13. class Fire  
  14. {  
  15. public:  
  16.     Fire(void);  
  17.     ~Fire(void);  
  18.     void InitFire();   //初始化篝火粒子   
  19.     //下面两个方法用来设置篝火粒子的属性值,以供渲染篝火粒子所用   
  20.     void PrepareFire();  
  21.     void ActivateFireParticles();  
  22.     void RenderFire();  //渲染篝火粒子   
  23.   
  24.     // 粒子结构体   
  25.   
  26.     struct PARTICLE {  
  27.   
  28.         float X,Y,Z; // 当前位置   
  29.         float sX,sY,sZ; // 当前移动速度   
  30.         float tX,tY,tZ; // 目标移动速度   
  31.         float R,B,G; // 粒子颜色   
  32.         float Size; // 粒子大小   
  33.         bool Active; // 粒子是否可见   
  34.         int Age; // 粒子的生命值   
  35.         int MaxAge; // 粒子消失前的最大生命值   
  36.   
  37.     };  
  38.   
  39.     //粒子结构体数组   
  40.     PARTICLE * FireParticles;  
  41.   
  42.     //粒子个数   
  43.     int FireParticleCount;  
  44.   
  45.     //每帧活动粒子数   
  46.     int ActivateFirePerFrame;  
  47.   
  48. };  
  49. #endif  
#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):

  1. #include "Fire.h"   
  2.   
  3. //初始化纹理数组   
  4. GLuint *texture = 0;  
  5. Fire::Fire(void) {  
  6. }  
  7.   
  8.   
  9. /** 
  10.  * 释放粒子结构体数组 
  11.  * */  
  12. Fire::~Fire(void) {  
  13.     delete[] FireParticles;  
  14.     FireParticles = 0;  
  15. }  
  16.   
  17. /** 
  18.  * 初始化粒子 
  19.  * */  
  20. void Fire::InitFire() {  
  21.   
  22.     int p;  
  23.   
  24.     //粒子的个数   
  25.     FireParticleCount = 500;  
  26.   
  27.   
  28.     //每帧粒子个数   
  29.     ActivateFirePerFrame = 40;  
  30.   
  31.     //初始化粒子结构体数组   
  32.     FireParticles = new PARTICLE[FireParticleCount];  
  33.   
  34.     //初始粒子不可见   
  35.     for (p = 0; p < FireParticleCount; p++) {  
  36.   
  37.         FireParticles[p].Active = false;  
  38.   
  39.     }  
  40. }  
  41.   
  42. void Fire::PrepareFire() {  
  43.   
  44.     int p;  
  45.   
  46.     for (p = 0; p < FireParticleCount; p++) {  
  47.   
  48.         // 调整粒子的速度   
  49.         FireParticles[p].sX += (FireParticles[p].tX - FireParticles[p].sX)  
  50.                 / 10.0f;  
  51.         FireParticles[p].sY += (FireParticles[p].tY - FireParticles[p].sY)  
  52.                 / 20.0f;  
  53.         FireParticles[p].sZ += (FireParticles[p].tZ - FireParticles[p].sZ)  
  54.                 / 10.0f;  
  55.         // 通过新的速度调整粒子的位置   
  56.         FireParticles[p].X += FireParticles[p].sX;  
  57.         FireParticles[p].Y += FireParticles[p].sY;  
  58.         FireParticles[p].Z += FireParticles[p].sZ;  
  59.   
  60.         // 调整粒子尺寸   
  61.         FireParticles[p].Size -= 0.002f;  
  62.         if (FireParticles[p].Size < 0.0f) {  
  63.             FireParticles[p].Active = false;  
  64.         }  
  65.   
  66.         // 调整粒子颜色   
  67.         FireParticles[p].R -= 0.01f;  
  68.         FireParticles[p].G -= 0.03f;  
  69.   
  70.         if (FireParticles[p].R < 0.1f)  
  71.             FireParticles[p].R = 0.1f;  
  72.         if (FireParticles[p].G < 0.1f)  
  73.             FireParticles[p].G = 0.0f;  
  74.   
  75.         // 最后检查粒子的生命值,如果生命值大于最大生命值,设置粒子不可见   
  76.         FireParticles[p].Age++;  
  77.         if (FireParticles[p].Age > FireParticles[p].MaxAge) {  
  78.             FireParticles[p].Active = false;  
  79.         }  
  80.   
  81.     }  
  82.   
  83. }  
  84.   
  85. void Fire::ActivateFireParticles() {  
  86.   
  87.     int p;  
  88.   
  89.     for (p = 0; p < FireParticleCount; p++) {  
  90.   
  91.         if (!FireParticles[p].Active) {  
  92.   
  93.             // 设置粒子的起始位置   
  94.             FireParticles[p].X = (((float) ((rand() % 50) + 1)) / 100.0f)  
  95.                     - 0.25f;  
  96.             FireParticles[p].Y = 0.0f;  
  97.             FireParticles[p].Z = (((float) ((rand() % 50) + 1)) / 100.0f)  
  98.                     - 0.25f;  
  99.   
  100.             // 设置粒子的目标速度   
  101.             FireParticles[p].tX = 0.0f;  
  102.             FireParticles[p].tY = 0.01f;  
  103.             FireParticles[p].tZ = 0.0f;  
  104.   
  105.             // 生成粒子随机速度   
  106.             FireParticles[p].sX = (((float) ((rand() % 30) + 1)) / 1000.0f)  
  107.                     - 0.015f;  
  108.             FireParticles[p].sY = (((float) ((rand() % 50) + 1)) / 1000.0f);  
  109.             FireParticles[p].sZ = (((float) ((rand() % 30) + 1)) / 1000.0f)  
  110.                     - 0.015f;  
  111.   
  112.             //设置粒子可见   
  113.             FireParticles[p].Active = true;  
  114.             // 设置粒子的生命值为0   
  115.             FireParticles[p].Age = 0;  
  116.             // 设置粒子的最大生命值为300   
  117.             FireParticles[p].MaxAge = 300;  
  118.   
  119.             // 设置粒子颜色   
  120.             FireParticles[p].R = 0.7f;  
  121.             FireParticles[p].G = 0.6f;  
  122.             FireParticles[p].B = 0.0f;  
  123.   
  124.             // 设置粒子尺寸   
  125.             FireParticles[p].Size = (((float) ((rand() % 15))) / 100.0f);  
  126.   
  127.             return;  
  128.         }  
  129.     }  
  130. }  
  131.   
  132. void Fire::RenderFire() {  
  133.   
  134.     for (int i = 0; i < ActivateFirePerFrame; i++) {  
  135.         ActivateFireParticles();  
  136.     }  
  137.   
  138.     PrepareFire();  
  139.   
  140.     int p;  
  141.   
  142.     GLfloat fogColor[4] = { 0.0f, 0.0f, 0.0f, 0.5f };  
  143.   
  144.     glFogx(GL_FOG_MODE, GL_LINEAR); // 设置雾模式   
  145.     glFogfv(GL_FOG_COLOR, fogColor); // 设置雾颜色   
  146.     glFogf(GL_FOG_DENSITY, 0.35f); // 设置雾密度   
  147.     glHint(GL_FOG_HINT, GL_DONT_CARE); // Fog Hint Value   
  148.     glFogf(GL_FOG_START, 1.0f); // 设置雾的开始深度   
  149.     glFogf(GL_FOG_END, 2.0f); // 设置雾的结束深度   
  150.     glEnable(GL_FOG); // 启用雾模式   
  151.   
  152.   
  153.     GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };  
  154.     GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };  
  155.     GLfloat LightPosition[] = { 0.0f, 0.1f, 0.0f, 1.0f };  
  156.   
  157.     glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);  
  158.     glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);  
  159.     glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);  
  160.     glEnable(GL_LIGHT1);  
  161.     glEnable(GL_LIGHTING);  
  162.   
  163.     /** 
  164.      * 开始渲染地面 
  165.      * */  
  166.     //启用纹理,并绑定地面纹理,禁用混合   
  167.     glEnable(GL_TEXTURE_2D);  
  168.     glBindTexture(GL_TEXTURE_2D, texture[0]);  
  169.     glDisable(GL_BLEND);  
  170.   
  171.     //设置颜色   
  172.     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);  
  173.   
  174.     //法线向量   
  175.     float normals[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,  
  176.             0.0f, 1.0f, 0.0f };  
  177.     //纹理坐标   
  178.     float texCoords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f };  
  179.     //顶点坐标   
  180.     float vertecies[] = { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f,  
  181.             -0.5f, 0.5f, 0.0f, -0.5f };  
  182.     //绘制地面   
  183.     for (int y = 0; y < 20; y++) {  
  184.         for (int x = 0; x < 20; x++) {  
  185.             glPushMatrix();  
  186.             glTranslatef(-5.0f, 0.0f, 5.0f);  
  187.             glTranslatef((float) x / 2, 0.0f, -(float) y / 2);  
  188.             glEnableClientState(GL_VERTEX_ARRAY);  
  189.             glEnableClientState(GL_TEXTURE_COORD_ARRAY);  
  190.             glEnableClientState(GL_NORMAL_ARRAY);  
  191.             glVertexPointer(3, GL_FLOAT, 0, vertecies);  
  192.             glTexCoordPointer(2, GL_FLOAT, 0, texCoords);  
  193.             glNormalPointer(GL_FLOAT, 0, normals);  
  194.             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
  195.             glDisableClientState(GL_VERTEX_ARRAY);  
  196.             glDisableClientState(GL_TEXTURE_COORD_ARRAY);  
  197.             glDisableClientState(GL_NORMAL_ARRAY);  
  198.             glPopMatrix();  
  199.         }  
  200.     }  
  201.   
  202.     //禁用光照和雾模式   
  203.     glDisable(GL_LIGHTING);  
  204.     glDisable(GL_FOG);  
  205.     /** 
  206.      * 渲染地面结束 
  207.      * */  
  208.   
  209.     /** 
  210.      * 开始渲染篝火 
  211.      * */  
  212.     // 启用纹理,绑定我们的粒子纹理   
  213.     glEnable(GL_TEXTURE_2D);  
  214.     glBindTexture(GL_TEXTURE_2D, texture[1]);  
  215.   
  216.     // 禁用深度测试   
  217.     glDisable(GL_DEPTH_TEST);  
  218.   
  219.     // 启用混合   
  220.     glEnable(GL_BLEND);  
  221.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);  
  222.   
  223.     //绘制我们的篝火   
  224.     for (p = 0; p < FireParticleCount; p++) {  
  225.   
  226.         if (FireParticles[p].Active) {  
  227.             glColor4f(FireParticles[p].R, FireParticles[p].G,  
  228.                     FireParticles[p].B, 1.0f);  
  229.   
  230.             glPushMatrix();  
  231.   
  232.             glTranslatef(FireParticles[p].X, FireParticles[p].Y,  
  233.                     FireParticles[p].Z);  
  234.             glNormal3f(0.0f, 0.0f, 1.0f);  
  235.             float vertecies[] = { -FireParticles[p].Size,  
  236.                     -FireParticles[p].Size, 0.0f, FireParticles[p].Size,  
  237.                     -FireParticles[p].Size, 0.0f, -FireParticles[p].Size,  
  238.                     FireParticles[p].Size, 0.0f, FireParticles[p].Size,  
  239.                     FireParticles[p].Size, 0.0f };  
  240.             glEnableClientState(GL_VERTEX_ARRAY);  
  241.             glEnableClientState(GL_TEXTURE_COORD_ARRAY);  
  242.             glVertexPointer(3, GL_FLOAT, 0, vertecies);  
  243.             glTexCoordPointer(2, GL_FLOAT, 0, texCoords);  
  244.             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
  245.             glDisableClientState(GL_VERTEX_ARRAY);  
  246.             glDisableClientState(GL_TEXTURE_COORD_ARRAY);  
  247.   
  248.             glPopMatrix();  
  249.         }  
  250.     }  
  251.   
  252.     /** 
  253.      * 渲染篝火结束 
  254.      * */  
  255.     //重新启用深度测试   
  256.     glEnable(GL_DEPTH_TEST);  
  257.   
  258. }  
#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:

  1. #ifndef _STDX   
  2. #define _STDX   
  3. #include "Fire.h"   
  4. //初始化OpenGL场景   
  5. extern void InitGL();  
  6. //处理屏幕尺寸变化   
  7. extern void SizeChanged(int width ,int height);  
  8. //渲染场景   
  9. extern void RendererGL();  
  10. //计算场景透视   
  11. extern void gluPerspective(double fovy, double aspect, double zNear,  
  12.         double zFar);  
  13. #endif  
#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:

  1. #include "Stdx.h"   
  2. Fire Fire;  //Fire实例   
  3. float aspectRatio;   //用于透视计算   
  4. void InitGL() {  
  5.     glShadeModel(GL_SMOOTH);   // 启用平滑模式   
  6.     glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   // 设置黑色背景   
  7.     glClearDepthf(1.0f);    // 设置深度缓存   
  8.     glEnable(GL_DEPTH_TEST);   // 启用深度测试   
  9.     glDepthFunc(GL_LEQUAL);   // 深度测试类型   
  10.     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   // 获得良好的透视效果   
  11.     Fire.InitFire();   //初始化粒子   
  12. }  
  13.   
  14. void SizeChanged(int width ,int height) {  
  15.     glViewport(0, 0, width, height);  //重设窗口大小   
  16.     glMatrixMode(GL_PROJECTION);  //启用投影矩阵   
  17.     glLoadIdentity();     //重置投影矩阵   
  18.     aspectRatio = float(width) / float(height);  //获得屏幕宽、高比   
  19.     gluPerspective(45.0, aspectRatio, 0.1, 100.0);  //计算透视   
  20.     glMatrixMode(GL_MODELVIEW);  //启用模型视图矩阵   
  21.     glLoadIdentity();   //重置模型视图矩阵   
  22. }  
  23.   
  24. void RendererGL() {  
  25.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // 首先清除颜色和深度缓存   
  26.     glLoadIdentity();   // 重设模型视图矩阵   
  27.     glTranslatef(0.0f, -0.5f, -2.2f);  //移动场景   
  28.     Fire.RenderFire(); //渲染篝火   
  29.   
  30.   
  31. }  
  32.   
  33. void gluPerspective(double fovy, double aspect, double zNear, double zFar) {  
  34.     glMatrixMode(GL_PROJECTION);   //启用投影矩阵   
  35.     glLoadIdentity();   //重置投影矩阵   
  36.   
  37.     //计算透视   
  38.     double xmin, xmax, ymin, ymax;  
  39.     ymax = zNear * tan(fovy * M_PI / 360.0);  
  40.     ymin = -ymax;  
  41.     xmin = ymin * aspect;  
  42.     xmax = ymax * aspect;  
  43.   
  44.     //设置透视   
  45.     glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);  
  46.     glMatrixMode(GL_MODELVIEW);  //启用模型视图矩阵   
  47.     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // 获得良好的透视效果   
  48. }  
#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)中定义头文件中的方法,在其方法中调用相应的本地代码实现其功能:

  1. #include "com_ygc_FireNativeMethod.h"   
  2. #include "Stdx.h"   
  3. /* 
  4.  * 调用RendererGL方法渲染场景 
  5.  */  
  6. JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onDrawFrame  
  7.   (JNIEnv *env, jclass cls, jobject obj){  
  8.     RendererGL();  
  9. }  
  10.   
  11. /* 
  12.  * 调用SizeChanged调整场景 
  13.  */  
  14. JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onSurfaceChanged  
  15.   (JNIEnv *env, jclass cls, jobject obj, jint width, jint height){  
  16.     SizeChanged(width,height);  
  17. }  
  18.   
  19. /* 
  20.  * 调用InitGL初始化场景 
  21.  */  
  22. JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onSurfaceCreated  
  23.   (JNIEnv *env, jclass cls, jobject obj1, jobject obj2){  
  24.     InitGL();  
  25. }  
  26. /* 
  27.  * 获得纹理绑定地址 
  28.  */  
  29. JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_setTexture  
  30.   (JNIEnv *env, jclass cls, jintArray tex){  
  31.     texture = (GLuint *)env->GetIntArrayElements(tex,0);  
  32.   
  33. }  
#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文件:

  1. # Copyright (C) 2009 The Android Open Source Project  
  2. #  
  3. # Licensed under the Apache License, Version 2.0 (the "License");  
  4. # you may not use this file except in compliance with the License.  
  5. # You may obtain a copy of the License at  
  6. #  
  7. #      http://www.apache.org/licenses/LICENSE-2.0  
  8. #  
  9. # Unless required by applicable law or agreed to in writing, software  
  10. # distributed under the License is distributed on an "AS IS" BASIS,  
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  12. # See the License for the specific language governing permissions and  
  13. # limitations under the License.  
  14. #  
  15. LOCAL_PATH := $(call my-dir)  
  16.   
  17. include $(CLEAR_VARS)  
  18.   
  19. LOCAL_MODULE    := fire_native_method  
  20. LOCAL_SRC_FILES := com_ygc_FireNativeMethod.cpp Fire.cpp main.cpp  
  21. LOCAL_LDLIBS    :=-L$(SYSROOT)/usr/lib -lGLESv2  
  22. LOCAL_LDLIBS    +=-L$(SYSROOT)/usr/lib -lGLESv1_CM  
  23. LOCAL_LDLIBS    += -L$(SYSROOT)/usr/lib -llog  
  24.   
  25. include $(BUILD_SHARED_LIBRARY)  

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.9

TensorFlow-v2.9

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值