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

本文概述

在我们先前关于GWT Web Toolkit的帖子中, 我们讨论了GWT的优点和特征, 回想一下总体思路, 它使我们能够将Java源代码转换为JavaScript并无缝地混合Java和JavaScript库。我们注意到, GWT生成的JavaScript得到了极大的优化。

在今天的帖子中, 我们想更深入地了解GWT工具包的实际应用。我们将演示如何利用GWT来构建一个特殊的应用程序:一个增强现实(AR)Web应用程序, 它可以在浏览器中完全以JavaScript实时运行。

浏览器中的增强现实?它比你想象的要容易。

在本文中, 我们将重点介绍GWT如何使我们能够轻松地与许多JavaScript API(例如WebRTC和WebGL)进行交互, 并使我们能够利用一个大型Java库NyARToolkit, 该库从未打算在浏览器中使用。我们将展示GWT如何允许我和我在Jooink的团队将所有这些部分放在一起来创建我们的宠物项目Picshare, 它是一个基于标记的AR应用程序, 你可以立即在浏览器中尝试该应用程序。

这篇文章将不会全面介绍如何构建应用程序, 而是将展示如何使用GWT轻松克服看似难以克服的挑战。

项目概述:从现实到增强现实

使用GWT,WebRTC,WebGL和ARToolKit的浏览器中基于标记的增强现实的管道。

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仍在积极维护和改进。

基于标记的增强现实是一个专业领域, 需要具备计算机视觉, 数字图像处理和数学方面的能力, 这在此处显而易见:

使用ARToolKit进行基于标记的增强现实图像分析。

转自ARToolKit文档。

使用ARToolKit的基于标记的增强现实管线。

转自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>

现在, 在主包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;

public  class FileInputStream  extends 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空间中的位置。

现在的挑战是将所有这些技术集成在一起。

将3D空间投影到相机上。

我们将不深入介绍如何完成此操作, 但基本思想是将视频图像用作场景的背景(上图中应用于”远”平面的纹理)并构建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
    ...
  }

  ...

使用这种技术, 我们可以生成如下结果:

Picshare浏览器内增强现实应用程序的结果。
带有多个标记的Picshare浏览器内增强现实应用程序的结果。

这是我们用于创建Picshare的过程, 邀请你在其中打印标记或将其显示在手机上, 并在浏览器中使用基于标记的AR。请享用!

结束语

Picshare对我们来说是Jooink的长期宠物项目。第一次实施可以追溯到几年前, 即使如此, 它的速度也足以令人印象深刻。在此链接上, 你可以看到我们较早的实验之一, 该实验于2012年进行了编译, 从未动过。请注意, 在样本中只有一个<video>。其他两个窗口是显示处理结果的<canvas>元素。

GWT即使在2012年也足够强大。随着GWT 2.8的发布, 我们已经获得了JsInterop大大改进的互操作性层, 从而进一步提高了性能。此外, 为了庆祝许多人, 我们还获得了更好的开发和调试环境, 超级开发模式。哦, 是的, 并且支持Java 8。

我们期待GWT 3.0!

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?