39 votes

Pourquoi les projecteurs de ma scène three.js restent-ils centrés dans la perspective de l'appareil photo, mais uniquement sur Chrome pour Android?

Je suis actuellement en utilisant AngularJS et Three.js pour essayer de développer un petit exemple d'un VR application. J'ai défini des contrôles basés sur de savoir si l'agent utilisateur est un appareil mobile ou non. C'est un peu louches méthode, mais c'est juste un exemple. OrbitControls sont utilisé sur des appareils mobiles, et DeviceOrientationControls sont utilisées d'une autre manière.

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;

J'ai également créé quelques objets à afficher.

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

Cela fonctionne très bien sur Chrome pour OS x, Safari pour mac OSX & Safari sur l'iPad (y compris le dispositif d'orientation des contrôles, le cas échéant).

Le problème se produit lors de l'exécution de l'application sur google Chrome pour Android. Le " coup de projecteur a été ajouté à la scène sera constamment pointé dans la même direction que la caméra. Voici une capture d'écran de l'application en cours d'exécution sur Android:

Spotlight image

Sur tous les autres navigateur, j'ai essayé de l'utiliser, la lumière est placée à (50, 50, 50) correctement et reste statique. Dans l'exemple ci-dessus, la lumière est rendu dans le centre de la caméra, et continue de suivre l'appareil photo lorsqu'il est déplacé ou de rotation. La réelle orientation des contrôles fonctionnent tout aussi bien.

Le fait que cela se produit seulement dans un navigateur me donne des maux de tête, la démo a besoin de fonctionner sur Chrome pour Android.

Merci.

Mise à jour: Avez essayé de nombreuses solutions différentes de différentes méthodes de contrôle pour différents types d'éclairage en vain.

10voto

rhashimoto Points 5993

Je vois le problème sur mon Moto G 1ère génération (Qualcomm Snapdragon 400), mais pas sur mon Project Tango tablet (nVidia Tegra K1), il est donc probable que ce soit un pilote du GPU bug ou une fonctionnalité non prise en charge sur certains matériels.

J'étais capable d'écrire un simple cas de tests reproductibles et l'utiliser pour déterminer où le calcul divergé entre mes deux plates-formes. Il s'est avéré à se produire dans cette partie de la Three.js fragment shader GLSL (extrait de la Three.js script) qui provoque la divergence (les commentaires ajoutés par moi):

#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

Cette section de code détermine la normale à un fragment. Votre matériel de paramètres permettant à l' FLAT_SHADED bloc. Apparemment, les appels à la dérivée des fonctions d' dFdx() et dFdy(), qui sont fournis par le GL_OES_standard_derivatives extension de WebGL, sont de produire des résultats incohérents. Ceci suggère que l'extension est mis en œuvre de manière incorrecte ou non pris en charge sur les plates-formes à l'origine des problèmes. Ce bogue Mozilla en faveur de cette hypothèse, plus précisément en soulignant Qualcomm matériel:

Un certain nombre de dispositifs d'exposer OES_standard_derivatives, mais ont rompu les implémentations de

La solution simple est d'éviter que le plat ombrage chemin d'accès du code. Votre floorMaterial contient le paramètre:

shading: THREE.FlatShading,

En supprimant cette ligne unique sera, par défaut, ombrage lisse (ou vous pouvez modifier explicitement la valeur de THREE.SmoothShading). Votre mesh offre déjà vertex normals donc cela devrait fonctionner.

J'ai essayé de cloner votre site de test et commentant cette ligne est beaucoup mieux pour moi sur mon Moto G. j'ai aussi créé ce jsfiddle qui montre deux quads, l'une avec un ombrage (à gauche) et un plat avec de l'ombrage (à droite). Les quads doivent être les réflexions des uns des autres mais qui ne sont pas si la plate-forme a des problèmes avec le plat de shader.

3voto

Ashish Rawat Points 734

En vous fiant à vos mots, cela fonctionne bien dans Chrome Desktop, mais le problème concerne uniquement Android, vous pouvez essayer d’exécuter Chrome en mode Bureau dans Android. comme vous pouvez le faire "demander un site de bureau" pour chrome. Voyez si cela aide. Comment fonctionne l'option "Demander un site" de Chrome?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X