GWT如何在浏览器中解锁增强现实

本文概述

  • 项目概述:从现实到增强现实
  • 通过GWT充分利用HTML5的API
  • 使用GWT编译第三方Java库
  • 与GWT融合在一起
  • 结束语
在我们先前关于GWT Web Toolkit的帖子中, 我们讨论了GWT的优点和特征, 回想一下总体思路, 它使我们能够将Java源代码转换为JavaScript并无缝地混合Java和JavaScript库。我们注意到, GWT生成的JavaScript得到了极大的优化。
在今天的帖子中, 我们想更深入地了解GWT工具包的实际应用。我们将演示如何利用GWT来构建一个特殊的应用程序:一个增强现实(AR)Web应用程序, 它可以在浏览器中完全以JavaScript实时运行。
GWT如何在浏览器中解锁增强现实

文章图片
在本文中, 我们将重点介绍GWT如何使我们能够轻松地与许多JavaScript API(例如WebRTC和WebGL)进行交互, 并使我们能够利用一个大型Java库NyARToolkit, 该库从未打算在浏览器中使用。我们将展示GWT如何允许我和我在Jooink的团队将所有这些部分放在一起来创建我们的宠物项目Picshare, 它是一个基于标记的AR应用程序, 你可以立即在浏览器中尝试该应用程序。
这篇文章将不会全面介绍如何构建应用程序, 而是将展示如何使用GWT轻松克服看似难以克服的挑战。
项目概述:从现实到增强现实
GWT如何在浏览器中解锁增强现实

文章图片
Picshare使用基于标记的增强现实。这种类型的AR应用程序在场景中搜索标记:像这样的特定的, 易于识别的几何图案。标记提供有关标记对象的位置和方向的信息, 从而使该软件能够以逼真的方式将其他3D风景投射到图像中。此过程的基本步骤是:
  • 访问相机:在处理本机桌面应用程序时, 操作系统提供对许多设备硬件的I / O访问。当我们处理网络应用程序时, 情况有所不同。浏览器被构造为从网上下载JavaScript代码的” 沙盒” , 最初并不是要让网站与大多数设备硬件进行交互。 WebRTC使用HTML5的媒体捕获功能突破了这一障碍, 使浏览器能够访问设备摄像头及其流等内容。
  • 分析视频流:我们有视频流……现在呢?我们必须分析每个帧以检测标记, 并计算标记在重建的3D世界中的位置。这是NyARToolkit的工作。
  • 增强视频:最后, 我们要显示带有添加的合成3D对象的原始视频。我们使用WebGL将最终的加长场景绘制到网页上。
通过GWT充分利用HTML5的API 使用诸如WebGL和WebRTC之类的JavaScript API, 可以在浏览器和用户之间进行意外的和异常的交互。
例如, WebGL允许硬件加速图形, 并在类型化数组规范的帮助下, 使JavaScript引擎能够以几乎本机的性能执行数字运算。同样, 使用WebRTC, 浏览器能够直接从计算机硬件访问视频(和其他数据)流。
WebGL和WebRTC都是必须在Web浏览器中内置的JavaScript库。大多数现代HTML5浏览器都至少部分支持这两个API(如你在此处和此处所见)。但是, 我们如何利用Java编写的GWT来利用这些工具呢?正如上一篇文章中所讨论的那样, GWT的互操作性层JsInterop(在GWT 2.8中正式发布)使这成为小菜一碟。
在GWT 2.8中使用JsInterop就像将-generateJsInteropExports作为编译器的参数添加一样容易。可用的注释在jsinterop.annotations包中定义, 捆绑在gwt-user.jar中。
的WebRTC
例如, 通过最少的编码工作, 在Chrome上使用WebRTC的getUserMedia和GWT就像编写代码一样简单:
Navigator.webkitGetUserMedia( configs, stream -> video.setSrc( URL.createObjectURL(stream) ), e -> Window.alert("Error: " + e) );

可以按如下方式定义Navigator类:
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="navigator") final static class Navigator { public static native void webkitGetUserMedia( Configs configs, SuccessCallback success, ErrorCallback error); }

有趣的是接口SuccessCallback和ErrorCallback的定义, 它们均由上述lambda表达式实现, 并通过@JsFunction注释在Java中定义:
@JsFunction public interface SuccessCallback { public void onMediaSuccess(MediaStream stream); }@JsFunction public interface ErrorCallback { public void onError(DomException error); }

最后, 类URL的定义与Navigator几乎相同, 并且类似地, 可以通过以下方式定义Configs类:
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="Object") public static class Configs { @JsProperty public native void setVideo(boolean getVideo); }

所有这些功能的实际实现都在浏览器的JavaScript引擎中进行。
你可以在GitHub上找到上述代码。
在本示例中, 为简单起见, 使用了不推荐使用的navigator.getUserMedia()API, 因为它是唯一一个无需在当前的稳定版本的Chrome上填充就可以使用的API。在生产应用程序中, 我们可以在所有浏览器中统一使用adapter.js通过较新的navigator.mediaDevices.getUserMedia()API访问流, 但这超出了本文的讨论范围。
WebGL
与使用WebRTC相比, 使用GWT的WebGL并没有太大区别, 但是由于OpenGL标准固有的复杂性, 它有点乏味。
这里的方法与上一节中的方法相同。包装的结果可以在Picshare中使用的GWT WebGL实现中看到, 可以在此处找到, 并且可以在此处找到GWT产生的结果的示例。
本身启用WebGL实际上并不能为我们提供3D图形功能。正如Gregg Tavares写道:
很多人不知道WebGL实际上是2D API, 而不是3D API。
3D算术必须由其他一些代码执行, 并转换为WebGL的2D图像。对于3D WebGL图形, 有一些不错的GWT库。我最喜欢的是Parallax, 但对于Picshare的第一个版本, 我们遵循了一个” 自己动手” 的方式, 编写了一个用于渲染简单3D网格的小库。该库使我们可以定义透视相机并管理对象的场景。随时在这里查看。
使用GWT编译第三方Java库 NyARToolkit是ARToolKit的纯Java端口, ARToolKit是用于构建增强现实应用程序的软件库。该端口是由日本开发商Nyatla编写的。尽管原始的ARToolKit和Nyatla版本自原始端口以来有所不同, 但NyARToolkit仍在积极维护和改进。
基于标记的增强现实是一个专业领域, 需要具备计算机视觉, 数字图像处理和数学方面的能力, 这在此处显而易见:
GWT如何在浏览器中解锁增强现实

文章图片
转自ARToolKit文档。
GWT如何在浏览器中解锁增强现实

文章图片
转自ARToolKit文档。
该工具包使用的所有算法均已记录并得到很好的理解, 但是从头开始重写它们是一个漫长且容易出错的过程, 因此最好使用现有的, 经过验证的工具包, 例如ARToolKit。不幸的是, 在定位网络时, 没有可用的东西。最强大的高级工具箱没有JavaScript的实现, JavaScript是一种主要用于处理HTML文档和数据的语言。这就是GWT证明其无与伦比的优势的地方, 这使我们能够将NyARToolkit简单地转换为JavaScript, 并将其轻松用于Web应用程序中。
用GWT编译
由于GWT项目本质上是Java项目, 因此使用NyARToolkit只需在源路径中导入源文件即可。但是, 请注意, 由于GWT代码到JavaScript的转换是在源代码级别完成的, 因此你需要NyARToolkit的源, 而不仅仅是包含已编译类的JAR。
Picshare使用的库可以在这里找到。它仅取决于从此处存档的NyARToolkit构建的lib / src和lib / src.markersystem内部找到的软件包。我们必须将这些包复制并导入到我们的GWT项目中。
我们应该将这些第三方软件包与我们自己的实现分开, 但是要继续进行NyARToolkit的” GWT化” , 我们必须提供一个XML配置文件, 该文件通知GWT编译器在哪里寻找源。在包jp.nyatla.nyartoolkit中, 我们添加了文件NyARToolkit.gwt.xml。
< module> < source path="core" /> < source path="detector" /> < source path="nyidmarker" /> < source path="processor" /> < source path="psarplaycard" /> < source path="markersystem" /> < /module>

【GWT如何在浏览器中解锁增强现实】现在, 在主包com.jooink.gwt.nyartoolkit中, 我们创建主配置文件GWT_NyARToolKit.gwt.xml, 并指示编译器通过继承其XML文件来将Nyatla的源代码包括在类路径中:
< inherits name='jp.nyatla.nyartoolkit.NyARToolkit'/>

其实很简单。在大多数情况下, 这就是所需要的一切, 但是很遗憾, 我们还没有完成。如果我们在此阶段尝试通过Super Dev Mode进行编译或执行, 则会出乎意料的出错:
No source code is available for type java.io.InputStream; did you forget to inherit a required module?

原因是NyARToolkit(即用于Java项目的Java库)使用GWT的Emulated JRE不支持的JRE类。我们在上一篇文章中对此进行了简短的讨论。
在这种情况下, 问题出在InputStream和相关的IO类上。碰巧的是, 我们甚至不需要使用大多数此类, 但是我们需要为编译器提供一些实现。好吧, 我们可以花大量时间从NyARToolkit来源中手动删除这些引用, 但这太疯狂了。 GWT为我们提供了更好的解决方案:通过< super-source> XML标签提供我们自己的不受支持的类的实现。
< 超级源>
如官方文档中所述:
< super-source> 标记指示编译器重新生成源路径的根。对于要为GWT项目重用现有Java API但原始源不可用或不可翻译的情况, 这很有用。这样做的一个常见原因是模拟JWT未实现的部分JRE。
因此, < super-source> 正是我们需要的。
我们可以在GWT项目中创建一个jre目录, 在其中可以将导致问题的类的实现放入其中:
java.io.FileInputStream java.io.InputStream java.io.InputStreamReader java.io.StreamTokenizer java.lang.reflect.Array java.nio.ByteBuffer java.nio.ByteOrder

除了java.lang.reflect.Array之外, 所有这些实际上都未使用, 因此我们只需要简单的实现。例如, 我们的FileInputStream内容如下:
package java.io; import java.io.InputStream; import com.google.gwt.user.client.Window; publicclass FileInputStreamextends InputStream { public FileInputStream(String filename) { Window.alert("WARNING, FileInputStream created with filename: " + filename ); }@Override public int read() { return 0; } }

构造函数中的Window.alert语句在开发过程中很有用。尽管我们必须能够编译该类, 但是我们要确保我们从未真正使用过它, 因此如果无意中使用了该类, 这将提醒我们。
我们所需的代码实际上使用了java.lang.reflect.Array, 因此需要一个非完全哑的实现。这是我们的代码:
package java.lang.reflect; import jp.nyatla.nyartoolkit.core.labeling.rlelabeling.NyARRleLabelFragmentInfo; import jp.nyatla.nyartoolkit.markersystem.utils.SquareStack; import com.google.gwt.user.client.Window; public class Array { public static < T> Object newInstance(Class< T> c, int n) {if( NyARRleLabelFragmentInfo.class.equals(c)) return new NyARRleLabelFragmentInfo[n]; else if(SquareStack.Item.class.equals(c)) return new SquareStack.Item[n]; else Window.alert("Creating array of size " + n+ " of " + c.toString()); return null; } }

现在, 如果将< super-source path =” jre” /> 放置在GWT_NyARToolkit.gwt.xml模块文件中, 则可以在项目中安全地编译和使用NyARToolkit!
与GWT融合在一起 现在我们可以拥有:
  • WebRTC, 一种能够从网络摄像头获取流并将其显示在< video> 标签中的技术。
  • WebGL, 一种能够处理HTML < canvas> 中的硬件加速图形的技术。
  • NyARToolkit是一个Java库, 能够拍摄图像(像素阵列), 搜索标记, 如果找到标记, 则为我们提供一个转换矩阵, 可以完全定义标记在3D空间中的位置。
现在的挑战是将所有这些技术集成在一起。
GWT如何在浏览器中解锁增强现实

文章图片
我们将不深入介绍如何完成此操作, 但基本思想是将视频图像用作场景的背景(上图中应用于” 远” 平面的纹理)并构建3D数据结构允许我们使用NyARToolkit的结果将此图像投影到太空中。
这种结构为我们提供了与NyARToolkit的库进行交互以进行标记识别的正确结构, 并可以在相机场景的顶部绘制3D模型。
使摄像头流可用有点棘手。视频数据只能绘制到< video> 元素。 HTML5 < video> 元素是不透明的, 不允许我们直接提取图像数据, 因此我们被迫将视频复制到中间的< canvas> 中, 提取图像数据, 将其转换为像素数组, 最后将其推送到NyARToolkit的Sensor.update()方法。然后, NyARToolkit可以完成识别图像中标记的工作, 并返回与其在3D空间中位置相对应的转换矩阵。
有了这些元素, 我们就可以在实时视频流中以3D形式将合成对象精确地放置在标记上!由于GWT的高性能, 我们拥有大量的计算资源, 因此我们甚至可以在将其用作WebGL场景的背景之前, 在画布上应用一些视频效果, 例如棕褐色或模糊。
以下简短代码描述了该过程的核心:
// given a < canvas> drawing context with appropriate width and height // and a < video> where the mediastream is drawn...// for each video frame // draw the video frame on the canvas ctx.drawImage(video, 0, 0, w, h); // extract image data from the canvas ImageData capt = ctx.getImageData(0, 0, w, h); // convert the image data in a format acceptable by NyARToolkit ImageDataRaster input = new ImageDataRaster(capt); // push the image in to a NyARSensor sensor.update(input); // update the NyARMarkerSystem with the sensor nyar.update(sensor); // the NyARMarkerSystem contains information about the marker patterns and is able to detect them. // After the call to update, all the markers are detected and we can get information for each // marker that was found.if( nyar.isExistMarker( marker_id ) ) { NyARDoubleMatrix44 m = nyar.getMarkerMatrix(marker_id); // m is now the matrix representing the pose (position and orientation) of // the marker in the scene, so we can use it to superimpose an object of // our choice ... }...

使用这种技术, 我们可以生成如下结果:
GWT如何在浏览器中解锁增强现实

文章图片
GWT如何在浏览器中解锁增强现实

文章图片
这是我们用于创建Picshare的过程, 邀请你在其中打印标记或将其显示在手机上, 并在浏览器中使用基于标记的AR。请享用!
结束语 Picshare对我们来说是Jooink的长期宠物项目。第一次实施可以追溯到几年前, 即使如此, 它的速度也足以令人印象深刻。在此链接上, 你可以看到我们较早的实验之一, 该实验于2012年进行了编译, 从未动过。请注意, 在样本中只有一个< video> 。其他两个窗口是显示处理结果的< canvas> 元素。
GWT即使在2012年也足够强大。随着GWT 2.8的发布, 我们已经获得了JsInterop大大改进的互操作性层, 从而进一步提高了性能。此外, 为了庆祝许多人, 我们还获得了更好的开发和调试环境, 超级开发模式。哦, 是的, 并且支持Java 8。
我们期待GWT 3.0!

    推荐阅读