javascript - 为什么我的 three.js 场景中的聚光灯仍然以相机视角居中,但仅在 Chrome for Android 上?

标签 javascript android google-chrome three.js

我目前正在使用 AngularJS 和 Three.js 来尝试开发一个 VR 应用程序的小示例。我已经根据用户代理是否是移动设备来定义控件。这是一种狡猾的方法,但这只是一个例子。 OrbitControls 用于非移动设备,DeviceOrientationControls 用于其他设备。

var controls = new THREE.OrbitControls(camera, game.renderer.domElement);
controls.noPan  = true;
controls.noZoom = true;

controls.target.set(
    camera.position.x,
    camera.position.y,
    camera.position.z
);

// Really dodgy method of checking if we're on mobile or not
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
    controls = new THREE.DeviceOrientationControls(camera, true);
    controls.connect();
    controls.update();
}

return controls;

我还创建了一些实际显示的对象。

this.camera     = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.001, 1000);

this.camera.position.set(0, 15, 0);

    this.textGeometry = new THREE.TextGeometry("Hello World", { size: 5, height: 1 });

    this.textMesh = new THREE.Mesh(this.textGeometry, new THREE.MeshBasicMaterial({
        color: 0xFF0000, opacity: 1
    }));

    this.textMesh.position.set(-20, 0, -20);

    this.light = new THREE.SpotLight(0x999999, 3, 300);
    this.light.position.set(50, 50, 50);

    this.floorGeometry              = new THREE.PlaneBufferGeometry(1000, 1000);
    this.floorTexture               = THREE.ImageUtils.loadTexture('img/textures/wood.jpg');
    this.floorTexture.wrapS         = THREE.RepeatWrapping;
    this.floorTexture.wrapT         = THREE.RepeatWrapping;
    this.floorTexture.repeat        = new THREE.Vector2(50, 50);
    this.floorTexture.anisotropy    = this.game.renderer.getMaxAnisotropy();

    this.floorMaterial = new THREE.MeshPhongMaterial({
        color: 0xffffff,
        specular: 0xffffff,
        shininess: 20,
        shading: THREE.FlatShading,
        map: this.floorTexture
    });

    this.floor = new THREE.Mesh(this.floorGeometry, this.floorMaterial);
    this.floor.rotation.x = -Math.PI / 2;

    this.scene.add(this.textMesh);
    this.scene.add(this.light);
    this.scene.add(this.floor);
    this.scene.add(this.camera);

这在适用于 OSX 的 Chrome、适用于 OSX 的 Safari 和 iPad 上的 Safari 上运行良好(包括适当的设备方向控制)。

在 Chrome for Android 上运行应用程序时会出现此问题。添加到场景中的聚光灯将不断指向与相机相同的方向。这是在 Android 上运行的应用程序的屏幕截图:

Spotlight image

在我尝试使用的所有其他浏览器上,灯光正确定位在 (50, 50, 50) 并保持静止。在上面的示例中,灯光在摄影机的中间进行渲染,并在移动或旋转时继续跟随摄影机。实际的方向控制工作正常。

这仅在一个浏览器中发生的事实让我很头疼,因为演示需要在 Chrome for Android 上运行。

谢谢。

更新:从不同的控制方法到不同类型的照明,一直在尝试许多不同的解决方案,但均无济于事。

最佳答案

我在第一代 Moto G (Qualcomm Snapdragon 400) 上看到了问题,但在我的 Project Tango 平板电脑 (nVidia Tegra K1) 上没有看到问题,因此这很可能是 GPU 驱动程序错误或某些硬件上不支持的功能.

我能够编写一个简单的可重复测试用例,并使用它来确定我的两个平台之间的计算分歧。事实证明,这部分发生在 Three.js GLSL fragment 着色器(从 Three.js 脚本中提取)导致差异(我添加的评论):

#ifndef FLAT_SHADED
  // Smooth shaded - use the interpolated normal.
  vec3 normal = normalize( vNormal );

#ifdef DOUBLE_SIDED
  normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );
#endif

#else
  // Flat shaded - generate the normal by taking the cross
  // product of derivatives that lie tangent to the surface.
  // This is the code that produces inconsistent results on
  // some mobile GPUs.
  vec3 fdx = dFdx( vViewPosition );
  vec3 fdy = dFdy( vViewPosition );
  vec3 normal = normalize( cross( fdx, fdy ) );

#endif

这段代码决定了一个 fragment 的法线。您的 Material 设置导致启用 FLAT_SHADED block 。显然,对 WebGL 的 GL_OES_standard_derivatives 扩展提供的衍生函数 dFdx()dFdy() 的调用会产生不一致的结果。这表明该扩展要么实现不正确,要么在导致问题的平台上不受支持。 This Mozilla bug支持这个假设,特别指出高通硬件:

A number of devices expose OES_standard_derivatives, but have broken implementations of it

简单的解决方法是避免平面着色代码路径。您的 floorMaterial 包含参数:

shading: THREE.FlatShading,

删除此单行将默认为平滑着色(或者您可以将值显式更改为 THREE.SmoothShading)。您的网格已经提供了顶点法线,所以这应该可以工作。

我试图克隆你的测试站点,并评论说在我的 Moto G 上一行对我来说看起来好多了。我还创建了 this jsfiddle它显示了两个四边形,一个带有平滑阴影(左),一个带有平坦阴影(右)。四边形看起来应该是彼此的反射,但如果平台的平面着色器有问题,则不会。

关于javascript - 为什么我的 three.js 场景中的聚光灯仍然以相机视角居中,但仅在 Chrome for Android 上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34289555/

相关文章:

javascript - 动态删除工具提示中的值时出现问题

php - 如何获取点击时的值(循环值)?

javascript - 类型错误 : res is not a function

Android Uri.Builder 在方案后使用 "/"而不是 "//"

android - NoSuchMethodError SimpleCursorAdapter for older version of android(2.3.3)

javascript - window.getComputedStyle() : How to Discard properties with default values?

google-chrome - 为什么 Chrome 一直在控制台中显示 "Site cannot be installed: the page is not served from a secure origin"?

javascript - 将变量移动到第二个组件并保存在状态中

jquery - 未闭合的方括号不会在 jquery 中引发错误

java - 如何将jar文件转换成apk?