为什么我的three.js场景中的焦点仍然集中在相机的视angular,但只在Chrome的Android?

我正在使用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; 

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

 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); 

这适用于Chrome OSX版,Safari OSX版和iPad版Safari(适用时包括设备方向控制)。

在Android版Chrome上运行应用程序时会出现问题。 已添加到场景中的聚光灯将始终指向与相机相同的方向。 以下是在Android上运行的应用程序的屏幕截图:

聚光灯的图像

在我尝试使用的每个其他浏览器上,光线正确定位在(50,50,50),并保持静止。 在上面的示例中,灯光正在摄像机的中间呈现,并在移动或旋转时继续跟随摄像机。 实际的方向控制工作得很好。

事实上,这只发生在一个浏览器是真的让我头痛,因为演示需要在Chrome浏览器上运行Android。

谢谢。

更新:一直在尝试从不同的控制方法到不同types的照明的许多不同的解决scheme无济于事。

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

我能够编写一个简单的可重复testing用例,并用它来确定计算在两个平台之间的差异。 原来发生在Three.js GLSL片段着色器的这一部分(从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 

这段代码决定了片段的正常。 您的材料设置导致启用FLAT_SHADED块。 显然,由GL_OES_standard_derivatives扩展提供给WebGL的派生函数dFdx()dFdy()的调用产生了不一致的结果。 这表明扩展是不正确地执行,或者在导致问题的平台上不被支持。 这Mozilla的bug支持这个假设,特别指出高通的硬件:

许多设备公开了OES_standard_derivatives,但破坏了它的实现

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

 shading: THREE.FlatShading, 

删除这一行将默认为平滑着色(或者您可以明确地将该值更改为THREE.SmoothShading )。 你的网格已经提供了顶点法线,所以这只是工作。

我试图克隆你的testing网站,并评论说,我的Moto G上的一条线看起来好多了。我还创build了这个jsfiddle ,它显示了两个四边形,一个平滑着色(左侧)和一个平面着色(右侧)。 四边形应该看起来是对方的reflection,但是如果平台在平坦着色器方面有问题,则不会。

用你的话来说,它可以在Chrome桌面上正常工作,但问题是只有Android,你可以尝试在Android的桌面模式下运行Chrome。 就像您可以为Chrome请求“请求桌面网站”一样。 看看这是否有帮助。 Chrome的“请求桌面网站”选项如何工作?