在上一篇教程中,我对OSG for Android的项目配置进行了讲解。在本篇教程中,我将通过一个最简单的示例,来讲解如何在Android项目中使用OSG。网上几乎所有的第一个示例,用的都是OSG库中自带的那个osgAndroidExampleGLES案例。可是那个案例对于新手来说还是有一些复杂,而且对于Android系统的兼容性并不那么好,反而更像是在PC环境下的程序。所以,我在本篇教程中,会使用一个全新的示例,从最简单的功能开始,讲解OSG for Android项目的建立方法。
本篇教程的讲解,是在项目已经配置好的前提下进行的。如果不知道OSG for Android项目应该怎么配置,请参考我的上一篇教程《OSG for Android新手教程系列(二)——项目配置》,传送门:http://blog.csdn.net/dongzhong1990/article/details/51736868
下面,我们开始Hello World。
本篇教程所用到的代码都已经上传到了CODE里,链接在这:https://code.csdn.net/dongzhong1990/osgandroidhelloworld/tree/master
-------------------------------------------------------
一、功能分析
首先,我们建立工程,我这里把示例项目命名为OsgHelloWorld。然后,按照上一篇教程中的配置方式配置好。
我们来简要分析一下,一个最基础的OSG for Android项目,需要哪些步骤:
第一,建立OSG窗口。在Android项目中建立OSG窗口的实质就是,通过Render调用OSG,在GLSurfaceView上进行渲染;
第二,打通Java和C/C++之间的屏障。如上篇教程中所讲,这里需要通过JNI来实现Java与C/C++之间的沟通;
第三,OSG场景建立及渲染。因为OSG for Android中的OSG功能,仍然是使用C/C++实现的,所以这个步骤与在PC端开发OSG项目基本相同。
下面,我对每一个步骤的具体实现方法进行详细的讲解。
二、实现方法
1. 建立OSG窗口
在Android项目中建立OSG窗口,其结构大致可以归纳为下图:
在Android项目中,最终呈现OSG场景渲染效果的部件是GLSurfaceView,而根据Android的特性,实际绘制图形的工具是Render。换句话说,GLSurfaceView是一块画布,而Render则是作画的笔。而在OSG for Android项目中,Render这支笔的“颜料”就是OSG。下面给出将相关代码:
MainActivity.java:
package osg.dong.osghelloworld;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
private MyGLSurfaceView myGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
myGLSurfaceView = new MyGLSurfaceView(this);
myGLSurfaceView.setEGLContextClientVersion(1);//设置GLES的版本,该示例采用的是GLES1
NDKRender render = new NDKRender();
myGLSurfaceView.setRenderer(render);//为SurfaceView添加Render
setContentView(myGLSurfaceView);
}
}
package osg.dong.osghelloworld;
import android.content.Context;
import android.opengl.GLSurfaceView;
public class MyGLSurfaceView extends GLSurfaceView
{//简单的继承GLSurfaceView
public MyGLSurfaceView(Context context)
{
super(context);
}
@Override
public void onPause()
{
// TODO Auto-generated method stub
super.onPause();
}
@Override
public void onResume()
{
// TODO Auto-generated method stub
super.onResume();
}
}
NDKRender.java:
package osg.dong.osghelloworld;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.Renderer;
public class NDKRender implements Renderer
{
public NDKRender() {}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
// TODO Auto-generated method stub
gl.glEnable(GL10.GL_DEPTH_TEST);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
// TODO Auto-generated method stub
osgNativeLib.init(width, height);//调用了OSG相关函数
}
@Override
public void onDrawFrame(GL10 gl)
{
// TODO Auto-generated method stub
osgNativeLib.step();//调用了OSG相关函数
}
}
从这里可看出,在OSG for Android的项目中,Java层的主要功能不多,主要还是负责Android的整体框架和组件的构建,对OSG本身的影响以及收到OSG的影响并不大。
2. Java与C/C++的沟通
Java与C/C++的沟通是通过Java提供的jni实现的,而在Android项目中,可以利用Google公司开发提供的NDK来具体实现。在本示例中,主要实现Java层和C/C++层沟通的代码文件是osgNativeLib.java和osgNativeLib.cpp。下面给出这两个文件的具体代码:
osgNativeLib.java:
package osg.dong.osghelloworld;
public class osgNativeLib
{
static
{
System.loadLibrary("osgNativeLib");
}
public static native void init(int width, int height);
public static native void step();
}
#include
#include
#include
#include
#include "osgMain.h"
using namespace std;
OsgMain osgMain;
extern "C" {
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_init(JNIEnv *, jclass, jint, jint);
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_step(JNIEnv *, jclass);
}
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_init(JNIEnv *env, jclass args, jint width, jint height)
{
osgMain.initOsgWindow(0, 0, width, height);
}
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_step(JNIEnv *env, jclass args)
{
osgMain.draw();
}
在osgNativeLib.java中,在静态块中有一句载入函数库的语句,载入的库名是在配置文件Android.mk的LOCAL_MODULE定义的(详情请见上一篇教程)。
osgMain则是纯粹的C++语言定义的OSG相关内容,下文中进行讲解。
从这两个文件中可以看出,如果在Java层调用了osgNativeLib.java中的native函数,就相当于调用了osgNativeLib.cpp中的相应函数,这就起到了沟通Java层和C/C++层的作用。
3. OSG场景建立及渲染
这一部分与PC端程序的OSG基本相同。因为该系列教程的目的是为了使大家以最快的速度明白OSG如何在Android环境下开发项目,默认了已经熟悉OSG的基本内涵,所以在本案例中,仅实现了视窗建立、相机设置、几何体绘制等几个最基本的功能。当然,为了使该系列教程更加完善,我也会在后续的教程中,简单的讲解一下OSG的基本内容。
这里对本示例进行简单的讲解。
(1)建立视窗
因为在Android上建立OSG项目,其最终展示是在Android的GLSurfaceView内,所以,这里的视窗Viewer需要设置成嵌入式视窗,通过调用setUpViewerAsEmbeddedInWindow函数来建立。
(2)设置相机
在本示例中,相机设置通过自定义函数setCamera实现,设置了对称透视投影。
(3)组织场景
本示例中简单的添加了一个球和一个立方体,以作为简单示例。
在这里只贴出cpp文件内的函数实现,我已经将所有代码上传到了CODE平台,如果有需要可以点击本篇教程开始处给出的链接获取。
osgMain.cpp:
#include "osgMain.h"
OsgMain::OsgMain()
{
modelUtil = new ModelUtil();
}
void OsgMain::initOsgWindow(int x, int y, int width, int height)
{
_viewer.setUpViewerAsEmbeddedInWindow(x, y, width, height);
setCamera(width, height);
_root = new osg::Group();
loadModels();
osgUtil::Optimizer optimizer;
optimizer.optimize(_root.get());
_viewer.setSceneData(_root.get());
_viewer.realize();
}
void OsgMain::draw()
{
_viewer.frame();
}
void OsgMain::setCamera(int width, int height)
{
_camera = _viewer.getCamera();
_camera->setProjectionMatrixAsPerspective(90.0, (double)width/((double)height), 0.5, 100.0);
_camera->setViewMatrixAsLookAt(osg::Vec3(0, -10, 5), osg::Vec3(0, 100, 0), osg::Vec3(0, 0, 1));
_camera->setClearColor(osg::Vec4(0.0, 0.0, 0.0, 1.0));
}
void OsgMain::loadModels()
{
osg::ref_ptr cube = modelUtil->createCube();
osg::ref_ptr sphere = modelUtil->createSphere();
_root->addChild(cube.get());
_root->addChild(sphere.get());
_root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
}
#include "modelUtil.h"
osg::ref_ptr ModelUtil::createSphere()
{
osg::ref_ptr geode = new osg::Geode();
osg::TessellationHints* hints = new osg::TessellationHints;
hints->setDetailRatio(1.0f);
geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(-2.0, 2.0, 0.0), 3.0), hints));
return geode.get();
}
osg::ref_ptr ModelUtil::createCube()
{
osg::ref_ptr geode = new osg::Geode();
osg::TessellationHints* hints = new osg::TessellationHints;
hints->setDetailRatio(0.5f);
geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(1.0, 3.0, 1.0), 2.0), hints));
return geode.get();
}
在下一篇教程中,我将对Java层与C/C++层相互沟通相关的内容进行讲解,届时读者会对Android调用OSG的机制有更深入的了解,敬请关注。