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

  在上一篇教程中,我对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窗口,其结构大致可以归纳为下图:

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

  在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);
	}
}


MyGLSurfaceView.java:

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相关函数
	}
}

  我们对这三段代码进行解读。先看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 

#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中,声明了两个native函数,这两个函数在osgNativeLib.cpp中都有对应的C/C++声明和实现。osgNativeLib.cpp中的函数命名规则是: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 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);
}

modelUtil.cpp:

#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的机制有更深入的了解,敬请关注。

你可能感兴趣的:(OSG,for,Android新手教程,Android)