(Android)Vuforia Native版本与jpct-ae结合
2015-08-23 10:00
615 查看
原文链接:http://www.arvrschool.com/read.php?tid=299&fid=21
AR/VR技术交流群 129340649
Qualcomm的Vuforia引擎是最强大的增强现实引擎之一。将它和JPCT-AE结合是一个很好的想法,它可以让你的Android设备实现让人惊奇的AR场景。
其中在Android端和iOS端的Vuforia Native版本是需要进行NDK编程,并且对于3D渲染这块做的不是很好,它采用的方案是将模型文件转换成.h或者java文件,将其中的点线面等数据保存,然后使用OpenGL读取并绘制。这种方案的弊端有很多,比如模型文件过大,不适合多贴图,渲染效果不好等等。将Vuforia与3D渲染引擎在原生代码中融合一直是我想做的事情,这篇稿子主要讲述Android端的Vuforia原生代码与jpct-AE得融合。
Vuforia Native版本替换模型
jpct-AE是一款免费的Android 系统下的3D渲染引擎。他是一款基于OpenGL技术开发的3D图形引擎(PC环境为标准OpenGL,Android为OpenGL ES), 以Java语言为基础的,拥有功能强大的Java 3D解决方案。
Vuforia和jpct都是使用OpenGL es中的GLSurfaceView进行绘制显示 的。打开ImageTargetsRenderer.java文件,这个是OpenGL渲染类,我们需要把Jpct的代码插入进来。
首先,为ImageTargetsRenderer创建一个构造函数。在这个构造函数里,将Activity作为参数传递进来,也在这个构造函数中初始化场景。
然后在ImageTargets.java文件中,修改ImageTargetRenderer的初始化将activity作为参数传递进结构体中。
然后在ImageTargetRenderer类中的onSurfaceChanged方法中,把jpct的framebuffer的初始化代码插入进来,同样也是从jpct的例子中得来。
好了,我们已经初始化场景和framebuffer,下一步就要使用jpct来绘制模型。(我们使用jpct的主要目的就是希望用他来绘制我们的虚拟场景)。需要在onDrawFrame()方法中完成。将下面的方法插入进onDrawFrame()方法中。
这里不需要fb.clear()。因为QCAR的本地openGL代码已经清除了framebuffer.如果这里再清除一次,就会清除摄像头拍摄的真实场景。
这时,如果你打开APP,会在真实场景上面看到一个矩形。(当然这个矩形目前还没有完成注册,还需要我们计算的摄像头矩阵)。
这里我们需要修改本地代码,即jni文件中的代码。打开ImageTargets.cpp文件,到JNIEXPORT void JNICALL Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargetsRenderer_renderFrame(JNIEnv *, jobject)这个方法的实现下。
这里是标准的OpenGL的代码。逐帧循环执行的线程。在Android中这是一个单独的线程。在这个方法里,framebuffer被清空,计算得到投影矩阵和模型视图矩阵,并且绘制模型。
这里不需要使用Vuforia渲染,渲染的任务交给jPCT-AE即可。删除一些不必要的语句,最后如下:
使用NDK进行编译。接下来我们需要将native层计算得出的模型视图矩阵和投影矩阵传递到java层。创建了如下方法接受从native层传递进来的矩阵,它是一个4X4的矩阵。
由于矩阵(模型视图矩阵和投影矩阵)是每帧都需要计算和检测的,所以这个方法也应该循环调用。所以需要在native层中的renderFrame方法中调用该方法。这里用到Jni编程的一些规则,不清楚的同学可以去参考相关资料。
接下来就需要将估算的矩阵结果传递到java层。
这里对modelViewMatrix矩阵在X轴上进行旋转180°,因为JPCT的坐标系绕X轴旋转了180°。使用SetFloatArrayRegion将矩阵结果设置成浮点数组类型。最后通过native层调用java层方法updateMatrix将结果向Java层传递。
好的,接下来回到Java层,将刚刚从Native层传递过来的数据给Camera。
由于不同的设备具有不同的FOV。这个就和摄像头的标定有关了。在QCAR库中提供了计算的函数,包括水平和垂直的FOV,看下面的代码。
这里使用QCAR摄像头标定的方法,获取摄像头的内部物理参数,这个在关于3D模型AR应用中也是必须的。
然后在通过对应的方法将数据上传:
Java层:
Native层:
写到这里,基本上集成工作就做完了,可以运行试试效果。
源码地址:http://www.arvrschool.com/read.php?tid=298&fid=60
AR/VR技术交流群 129340649
Qualcomm的Vuforia引擎是最强大的增强现实引擎之一。将它和JPCT-AE结合是一个很好的想法,它可以让你的Android设备实现让人惊奇的AR场景。
其中在Android端和iOS端的Vuforia Native版本是需要进行NDK编程,并且对于3D渲染这块做的不是很好,它采用的方案是将模型文件转换成.h或者java文件,将其中的点线面等数据保存,然后使用OpenGL读取并绘制。这种方案的弊端有很多,比如模型文件过大,不适合多贴图,渲染效果不好等等。将Vuforia与3D渲染引擎在原生代码中融合一直是我想做的事情,这篇稿子主要讲述Android端的Vuforia原生代码与jpct-AE得融合。
Vuforia Native版本替换模型
jpct-AE是一款免费的Android 系统下的3D渲染引擎。他是一款基于OpenGL技术开发的3D图形引擎(PC环境为标准OpenGL,Android为OpenGL ES), 以Java语言为基础的,拥有功能强大的Java 3D解决方案。
Vuforia和jpct都是使用OpenGL es中的GLSurfaceView进行绘制显示 的。打开ImageTargetsRenderer.java文件,这个是OpenGL渲染类,我们需要把Jpct的代码插入进来。
首先,为ImageTargetsRenderer创建一个构造函数。在这个构造函数里,将Activity作为参数传递进来,也在这个构造函数中初始化场景。
public ImageTargetsRenderer(ImageTargets activity){ this.mActivity = activity; world = new World(); world.setAmbientLight(20, 20, 20); sun = new Light(world); sun.setIntensity(250, 250, 250); // Create a texture out of the icon...:-) Texture texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(mActivity.getResources().getDrawable(R.drawable.ic_launcher)), 64, 64)); TextureManager.getInstance().addTexture("texture", texture); cube = Primitives.getCube(10); cube.calcTextureWrapSpherical(); cube.setTexture("texture"); cube.strip(); cube.build(); world.addObject(cube); cam = world.getCamera(); cam.moveCamera(Camera.CAMERA_MOVEOUT, 50); cam.lookAt(cube.getTransformedCenter()); SimpleVector sv = new SimpleVector(); sv.set(cube.getTransformedCenter()); sv.y -= 100; sv.z -= 100; sun.setPosition(sv); MemoryHelper.compact(); }
然后在ImageTargets.java文件中,修改ImageTargetRenderer的初始化将activity作为参数传递进结构体中。
mRenderer = new ImageTargetsRenderer(this); mRenderer.mActivity = this; mGlView.setRenderer(mRenderer);
然后在ImageTargetRenderer类中的onSurfaceChanged方法中,把jpct的framebuffer的初始化代码插入进来,同样也是从jpct的例子中得来。
if (fb != null) { fb.dispose(); } fb = new FrameBuffer(width, height);
好了,我们已经初始化场景和framebuffer,下一步就要使用jpct来绘制模型。(我们使用jpct的主要目的就是希望用他来绘制我们的虚拟场景)。需要在onDrawFrame()方法中完成。将下面的方法插入进onDrawFrame()方法中。
world.renderScene(fb); world.draw(fb); fb.display();
这里不需要fb.clear()。因为QCAR的本地openGL代码已经清除了framebuffer.如果这里再清除一次,就会清除摄像头拍摄的真实场景。
这时,如果你打开APP,会在真实场景上面看到一个矩形。(当然这个矩形目前还没有完成注册,还需要我们计算的摄像头矩阵)。
这里我们需要修改本地代码,即jni文件中的代码。打开ImageTargets.cpp文件,到JNIEXPORT void JNICALL Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargetsRenderer_renderFrame(JNIEnv *, jobject)这个方法的实现下。
这里是标准的OpenGL的代码。逐帧循环执行的线程。在Android中这是一个单独的线程。在这个方法里,framebuffer被清空,计算得到投影矩阵和模型视图矩阵,并且绘制模型。
这里不需要使用Vuforia渲染,渲染的任务交给jPCT-AE即可。删除一些不必要的语句,最后如下:
{ // Clear color and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Get the state from QCAR and mark the beginning of a rendering section QCAR::State state = QCAR::Renderer::getInstance().begin(); // Explicitly render the Video Background QCAR::Renderer::getInstance().drawVideoBackground(); // Did we find any trackables this frame? for(int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++) { // Get the trackable: const QCAR::TrackableResult* result = state.getTrackableResult(tIdx); const QCAR::Trackable& trackable = result->getTrackable(); QCAR::Matrix44F modelViewMatrix = QCAR::Tool::convertPose2GLMatrix(result->getPose()); } QCAR::Renderer::getInstance().end(); }
使用NDK进行编译。接下来我们需要将native层计算得出的模型视图矩阵和投影矩阵传递到java层。创建了如下方法接受从native层传递进来的矩阵,它是一个4X4的矩阵。
public void updateModelviewMatrix(float mat[]) { modelViewMat = mat; }
由于矩阵(模型视图矩阵和投影矩阵)是每帧都需要计算和检测的,所以这个方法也应该循环调用。所以需要在native层中的renderFrame方法中调用该方法。这里用到Jni编程的一些规则,不清楚的同学可以去参考相关资料。
jclass activityClass = env->GetObjectClass(obj); //获取Activity jmethodID updateMatrixMethod = env->GetMethodID(activityClass, "updateModelviewMatrix", "([F)V");
接下来就需要将估算的矩阵结果传递到java层。
jfloatArray modelviewArray = env->NewFloatArray(16); for(int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++) { // Get the trackable: const QCAR::TrackableResult* result = state.getTrackableResult(tIdx); const QCAR::Trackable& trackable = result->getTrackable(); QCAR::Matrix44F modelViewMatrix = QCAR::Tool::convertPose2GLMatrix(result->getPose()); SampleUtils::rotatePoseMatrix(90.0f, 1.0f, 0, 0, &modelViewMatrix.data[0]); QCAR::Matrix44F inverseMV = SampleMath::Matrix44FInverse(modelViewMatrix); QCAR::Matrix44F invTranspMV = SampleMath::Matrix44FTranspose(inverseMV); // 将数据传递到java层 env->SetFloatArrayRegion(modelviewArray, 0, 16, invTranspMV.data); env->CallVoidMethod(obj, updateMatrixMethod, modelviewArray); } // hide the objects when the targets are not detected if (state.getNumTrackableResults() == 0) { float m [] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,-10000,1 }; env->SetFloatArrayRegion(modelviewArray, 0, 16, m); env->CallVoidMethod(obj, updateMatrixMethod, modelviewArray); } env->DeleteLocalRef(modelviewArray);
这里对modelViewMatrix矩阵在X轴上进行旋转180°,因为JPCT的坐标系绕X轴旋转了180°。使用SetFloatArrayRegion将矩阵结果设置成浮点数组类型。最后通过native层调用java层方法updateMatrix将结果向Java层传递。
好的,接下来回到Java层,将刚刚从Native层传递过来的数据给Camera。
public void updateCamera() { if (modelViewMat != null) { float[] m = modelViewMat; final SimpleVector camUp; if (mActivity.isPortrait()) { camUp = new SimpleVector(-m[0], -m[1], -m[2]); } else { camUp = new SimpleVector(-m[4], -m[5], -m[6]); } final SimpleVector camDirection = new SimpleVector(m[8], m[9], m[10]); final SimpleVector camPosition = new SimpleVector(m[12], m[13], m[14]); cam.setOrientation(camDirection, camUp); cam.setPosition(camPosition); cam.setFOV(fov); cam.setYFOV(fovy); } }位置和旋转组成摄像头的称作摄像头的位姿,然而除了这个之外,摄像头所需要设置的参数远不止这些,还有FOV,简称视场,他也会影响摄像头所看到的场景,关于更详细的信息请看:http://en.wikipedia.org/wiki/Field_of_view
由于不同的设备具有不同的FOV。这个就和摄像头的标定有关了。在QCAR库中提供了计算的函数,包括水平和垂直的FOV,看下面的代码。
这里使用QCAR摄像头标定的方法,获取摄像头的内部物理参数,这个在关于3D模型AR应用中也是必须的。
const QCAR::CameraCalibration& cameraCalibration = QCAR::CameraDevice::getInstance().getCameraCalibration(); QCAR::Vec2F size = cameraCalibration.getSize(); QCAR::Vec2F focalLength = cameraCalibration.getFocalLength(); float fovyRadians = 2 * atan(0.5f * size.data[1] / focalLength.data[1]); float fovRadians = 2 * atan(0.5f * size.data[0] / focalLength.data[0]);
然后在通过对应的方法将数据上传:
Java层:
public void setFov(float fov) { this.fov = fov; } public void setFovy(float fovy) { this.fovy = fovy; }
Native层:
jmethodID fovMethod = env->GetMethodID(activityClass, "setFov", "(F)V"); jmethodID fovyMethod = env->GetMethodID(activityClass, "setFovy", "(F)V"); env->CallVoidMethod(obj, fovMethod, fovRadians); env->CallVoidMethod(obj, fovyMethod, fovyRadians);
写到这里,基本上集成工作就做完了,可以运行试试效果。
源码地址:http://www.arvrschool.com/read.php?tid=298&fid=60
相关文章推荐
- Andriod学习系列之(四)Intent初涉
- 自定义Dialog实现从下往上出现(内容省市县三级联动)
- Android学习笔记(一)Android应用程序的组成部分
- 列出android所有服务
- Android AOSP源码下载
- Android Fragment(三)---生命周期与回退栈
- android 向SD卡写入数据
- Android 单击listview弹出popupwindow弹出框
- android权限列表
- Android SD卡创建文件和文件夹失败
- Android4: Write Storage权限问题
- android 读写sd卡的权限设置
- Android4: Write Storage权限问题
- android 聊天室窗口
- Android存储访问及目录
- Android Api Demos登顶之路(四十五)Loader-->Cursor
- Android学习之自定义view(二)
- 关于android 1.6全部的权限介绍
- 如何不翻墙下载Android代码
- Android Studio 在run时报的异常 Failed to run command file not found FileNotFoundException