我目前正在使用 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 上运行的应用程序的屏幕截图:
在我尝试使用的所有其他浏览器上,灯光正确定位在 (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/