OSG for Android新手教程系列(三)——HelloWorld,第一个示例

来源:互联网 发布:传统出版网络出版 编辑:程序博客网 时间:2024/05/18 04:56

  在上一篇教程中,我对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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);myGLSurfaceView = new MyGLSurfaceView(this);myGLSurfaceView.setEGLContextClientVersion(1);//设置GLES的版本,该示例采用的是GLES1NDKRender render = new NDKRender();myGLSurfaceView.setRenderer(render);//为SurfaceView添加RendersetContentView(myGLSurfaceView);}}


MyGLSurfaceView.java:

package osg.dong.osghelloworld;import android.content.Context;import android.opengl.GLSurfaceView;public class MyGLSurfaceView extends GLSurfaceView{//简单的继承GLSurfaceViewpublic MyGLSurfaceView(Context context){super(context);}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();}@Overridepublic void onResume() {// TODO Auto-generated method stubsuper.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() {}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// TODO Auto-generated method stubgl.glEnable(GL10.GL_DEPTH_TEST);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {// TODO Auto-generated method stubosgNativeLib.init(width, height);//调用了OSG相关函数}@Overridepublic void onDrawFrame(GL10 gl) {// TODO Auto-generated method stubosgNativeLib.step();//调用了OSG相关函数}}

  我们对这三段代码进行解读。先看MainAcrivity.java,这段代码创建了Android项目的主要Activity,在这个Activity内定义了一个MyGLSurfaceViewer,这个就是我们展示OSG场景的“画板”。根据上一篇教程内所讲,这里调用了myGLSurfaceView的setEGLContextClientVersion设置OSG版本。MyGLSurfaceView在本案例中只是简单地继承了GLSurfaceView。NDKRender实现了Render接口,并在onSurfaceChanged和onDrawFrame方法中调用了OSG的函数。osgNativeLib的具体实现在下文进行讲解。

  从这里可看出,在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();}

osgNativeLib.cpp:

#include <jni.h>#include <string.h>#include <osg/Node>#include <iostream>#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中,声明了两个native函数,这两个函数在osgNativeLib.cpp中都有对应的C/C++声明和实现。osgNativeLib.cpp中的函数命名规则是:Java_ + <Java包名> + _<Java函数名>,其中,Java包名中“.”会被替换为“_”;函数的参数中,会多出一个JNIEnv*类型参数和jclass类型的参数,其余的一一对应,不过参数类型有所改变。osgNativeLib.java和osgNativeLib.cpp之间的关系,以及osgNativeLib.cpp中的古怪名称的来历,我将在的下一篇教程中详细讲解。敬请关注。

  在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<osg::Node> cube = modelUtil->createCube();osg::ref_ptr<osg::Node> sphere = modelUtil->createSphere();_root->addChild(cube.get());_root->addChild(sphere.get());_root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);}

modelUtil.cpp:

#include "modelUtil.h"osg::ref_ptr<osg::Node> ModelUtil::createSphere(){osg::ref_ptr<osg::Geode> 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<osg::Node> ModelUtil::createCube(){osg::ref_ptr<osg::Geode> 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的机制有更深入的了解,敬请关注。

2 0
原创粉丝点击