我目前正在使用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);
这适用于Chrome for OSX,Safari for OSX和Safari on iPad(包括适当的设备方向控件).
在Chrome for Android上运行应用程序时会出现此问题.已添加到场景中的聚光灯将始终指向与摄像机相同的方向.以下是在Android上运行的应用程序的屏幕截图:
在我尝试使用的每个其他浏览器上,灯光正确定位在(50,50,50)并保持静止.在上面的示例中,灯光在相机中间渲染,并在移动或旋转时继续跟随相机.实际的方向控制工作正常.
这只发生在一个浏览器中的事实让我很头疼,因为演示需要在Chrome for Android上运行.
谢谢.
更新:尝试了从不同控制方法到不同类型照明的许多不同解决方案无济于事.
我在我的Moto G 1st gen(Qualcomm Snapdragon 400)上看到了问题,但在我的Project Tango平板电脑(nVidia Tegra K1)上没有看到问题,所以这可能是GPU驱动程序错误或某些硬件上不支持的功能.
我能够编写一个简单的可重复测试用例,并用它来确定我的两个平台之间的计算分歧.事实证明,在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
块.显然,对衍生函数的调用dFdx()
以及dFdy()
由WebGL的GL_OES_standard_derivatives扩展提供的调用产生了不一致的结果.这表明扩展未正确实现或在平台上不受支持导致问题.这个Mozilla bug支持这个假设,特别指出了Qualcomm的硬件:
许多设备暴露了OES_standard_derivatives,但已经破坏了它的实现
简单的解决方法是避免平面着色代码路径.你floorMaterial
包含的参数:
shading: THREE.FlatShading,
删除此单行将默认为平滑着色(或者您可以显式将值更改为THREE.SmoothShading
).你的网格已经提供了顶点法线,所以这应该可行.
我试图克隆你的测试网站,并在我的Moto G上评论出一条线看起来好得多.我还创建了这个显示两个四边形的jsfiddle,一个带有平滑着色(左),一个带有平面着色(右).四边形应该看起来是彼此的反射,但如果平台在平面着色器上有问题则不会.