在Safari 11.0.2

时间:2018-01-04 15:55:37

标签: browser safari three.js webgl standards

我正在尝试确定为什么在Safari 11.0.2(OSX 10.12.6)中无法渲染Three.js场景。

/**
  * Generate a scene object with a background color
  **/

function getScene() {
  var scene = new THREE.Scene();
  scene.background = new THREE.Color(0x111111);
  return scene;
}

/**
  * Generate the camera to be used in the scene. Camera args:
  *   [0] field of view: identifies the portion of the scene
  *     visible at any time (in degrees)
  *   [1] aspect ratio: identifies the aspect ratio of the
  *     scene in width/height
  *   [2] near clipping plane: objects closer than the near
  *     clipping plane are culled from the scene
  *   [3] far clipping plane: objects farther than the far
  *     clipping plane are culled from the scene
  **/

function getCamera() {
  var aspectRatio = window.innerWidth / window.innerHeight;
  var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
  camera.position.set(0,150,400);
  camera.lookAt(scene.position);  
  return camera;
}

/**
  * Generate the light to be used in the scene. Light args:
  *   [0]: Hexadecimal color of the light
  *   [1]: Numeric value of the light's strength/intensity
  *   [2]: The distance from the light where the intensity is 0
  * @param {obj} scene: the current scene object
  **/

function getLight(scene) {
  var lights = [];
  lights[0] = new THREE.PointLight( 0xffffff, 0.6, 0 );
  lights[0].position.set( 100, 200, 100 );
  scene.add( lights[0] );

  var ambientLight = new THREE.AmbientLight(0x111111);
  scene.add(ambientLight);
  return light;
}

/**
  * Generate the renderer to be used in the scene
  **/

function getRenderer() {
  // Create the canvas with a renderer
  var renderer = new THREE.WebGLRenderer({antialias: true});
  // Add support for retina displays
  renderer.setPixelRatio(window.devicePixelRatio);
  // Specify the size of the canvas
  renderer.setSize(window.innerWidth, window.innerHeight);
  // Add the canvas to the DOM
  document.body.appendChild(renderer.domElement);
  return renderer;
}

/**
  * Generate the controls to be used in the scene
  * @param {obj} camera: the three.js camera for the scene
  * @param {obj} renderer: the three.js renderer for the scene
  **/

function getControls(camera, renderer) {
  var controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.zoomSpeed = 0.4;
  controls.panSpeed = 0.4;
  return controls;
}

/**
  * Get grass
  **/

function getPlane(scene, loader) {
  var texture = loader.load('grass.jpg');
  texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  texture.repeat.set( 10, 10 );
  var material = new THREE.MeshBasicMaterial({
    map: texture, side: THREE.DoubleSide
  });
  var geometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
  var plane = new THREE.Mesh(geometry, material);
  plane.position.y = -0.5;
  plane.rotation.x = Math.PI / 2;
  scene.add(plane);
  return plane;
}

/**
  * Add background
  **/

function getBackground(scene, loader) {
  var imagePrefix = '';
  var directions  = ['right', 'left', 'top', 'bottom', 'front', 'back'];
  var imageSuffix = '.bmp';
  var geometry = new THREE.BoxGeometry( 1000, 1000, 1000 ); 

  var materialArray = [];
  for (var i = 0; i < 6; i++)
    materialArray.push( new THREE.MeshBasicMaterial({
      map: loader.load(imagePrefix + directions[i] + imageSuffix),
      side: THREE.BackSide
    }));
  var sky = new THREE.Mesh( geometry, materialArray );
  scene.add(sky);
}

/**
  * Add a character
  **/

function getSphere(scene) {
  var geometry = new THREE.SphereGeometry( 30, 12, 9 );
  var material = new THREE.MeshPhongMaterial({
    color: 0xd0901d,
    emissive: 0xaf752a,
    side: THREE.DoubleSide,
    flatShading: true
  });
  var sphere = new THREE.Mesh( geometry, material );

  // create a group for translations and rotations
  var sphereGroup = new THREE.Group();
  sphereGroup.add(sphere)

  sphereGroup.position.set(0, 24, 100);
  scene.add(sphereGroup);
  return [sphere, sphereGroup];
}

/**
  * Store all currently pressed keys
  **/

function addListeners() {
  window.addEventListener('keydown', function(e) {
    pressed[e.key.toUpperCase()] = true;
  })
  window.addEventListener('keyup', function(e) {
    pressed[e.key.toUpperCase()] = false;
  })
}

/**
 * Update the sphere's position
 **/

function moveSphere() {
  var delta = clock.getDelta(); // seconds
  var moveDistance = 200 * delta; // 200 pixels per second
  var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 deg) per sec

  // move forwards/backwards/left/right
  if ( pressed['W'] ) {
    sphere.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle)
    sphereGroup.translateZ( -moveDistance );
  }
  if ( pressed['S'] ) 
    sphereGroup.translateZ(  moveDistance );
  if ( pressed['Q'] )
    sphereGroup.translateX( -moveDistance );
  if ( pressed['E'] )
    sphereGroup.translateX(  moveDistance ); 

  // rotate left/right/up/down
  var rotation_matrix = new THREE.Matrix4().identity();
  if ( pressed['A'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
  if ( pressed['D'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
  if ( pressed['R'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), rotateAngle);
  if ( pressed['F'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle);
}

/**
  * Follow the sphere
  **/

function moveCamera() {
  var relativeCameraOffset = new THREE.Vector3(0,50,200);
  var cameraOffset = relativeCameraOffset.applyMatrix4(sphereGroup.matrixWorld);
  camera.position.x = cameraOffset.x;
  camera.position.y = cameraOffset.y;
  camera.position.z = cameraOffset.z;
  camera.lookAt(sphereGroup.position);
}

// Render loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  moveSphere();
  moveCamera();
};

// state
var pressed = {};
var clock = new THREE.Clock();

// globals
var scene = getScene();
var camera = getCamera();
var light = getLight(scene);
var renderer = getRenderer();

// add meshes
var loader = new THREE.TextureLoader();
var floor = getPlane(scene, loader);
var background = getBackground(scene, loader);
var sphereData = getSphere(scene);
var sphere = sphereData[0];
var sphereGroup = sphereData[1];

addListeners();
render();
  body { margin: 0; overflow: hidden; }
  canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

更一般地说,shadertoy.com [example]上的所有示例在Safari 11.0.2上都不会显示或显得非常微弱,几乎全部为白色。

即使在我打开所有实验性网页功能(包括WebGL 2.0)之后,“Safari技术预览版”也是如此。

我想弄清楚如何制作场景渲染,但我更感兴趣的是学习别人如何尝试调试这类问题。是否有工具或资源可以帮助解决这类问题(例如仅适用于WebGL的开发人员工具)?

1 个答案:

答案 0 :(得分:3)

looks like a compositing bug in Safari。希望Apple能解决它。

有几个工作场所。简单似乎是将主体或画布的背景颜色设置为黑色。

/**
  * Generate a scene object with a background color
  **/

function getScene() {
  var scene = new THREE.Scene();
  scene.background = new THREE.Color(0x111111);
  return scene;
}

/**
  * Generate the camera to be used in the scene. Camera args:
  *   [0] field of view: identifies the portion of the scene
  *     visible at any time (in degrees)
  *   [1] aspect ratio: identifies the aspect ratio of the
  *     scene in width/height
  *   [2] near clipping plane: objects closer than the near
  *     clipping plane are culled from the scene
  *   [3] far clipping plane: objects farther than the far
  *     clipping plane are culled from the scene
  **/

function getCamera() {
  var aspectRatio = window.innerWidth / window.innerHeight;
  var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
  camera.position.set(0,150,400);
  camera.lookAt(scene.position);  
  return camera;
}

/**
  * Generate the light to be used in the scene. Light args:
  *   [0]: Hexadecimal color of the light
  *   [1]: Numeric value of the light's strength/intensity
  *   [2]: The distance from the light where the intensity is 0
  * @param {obj} scene: the current scene object
  **/

function getLight(scene) {
  var lights = [];
  lights[0] = new THREE.PointLight( 0xffffff, 0.6, 0 );
  lights[0].position.set( 100, 200, 100 );
  scene.add( lights[0] );

  var ambientLight = new THREE.AmbientLight(0x111111);
  scene.add(ambientLight);
  return light;
}

/**
  * Generate the renderer to be used in the scene
  **/

function getRenderer() {
  // Create the canvas with a renderer
  var renderer = new THREE.WebGLRenderer({antialias: true});
  // Add support for retina displays
  renderer.setPixelRatio(window.devicePixelRatio);
  // Specify the size of the canvas
  renderer.setSize(window.innerWidth, window.innerHeight);
  // Add the canvas to the DOM
  document.body.appendChild(renderer.domElement);
  return renderer;
}

/**
  * Generate the controls to be used in the scene
  * @param {obj} camera: the three.js camera for the scene
  * @param {obj} renderer: the three.js renderer for the scene
  **/

function getControls(camera, renderer) {
  var controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.zoomSpeed = 0.4;
  controls.panSpeed = 0.4;
  return controls;
}

/**
  * Get grass
  **/

function getPlane(scene, loader) {
  var texture = loader.load('grass.jpg');
  texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  texture.repeat.set( 10, 10 );
  var material = new THREE.MeshBasicMaterial({
    map: texture, side: THREE.DoubleSide
  });
  var geometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
  var plane = new THREE.Mesh(geometry, material);
  plane.position.y = -0.5;
  plane.rotation.x = Math.PI / 2;
  scene.add(plane);
  return plane;
}

/**
  * Add background
  **/

function getBackground(scene, loader) {
  var imagePrefix = '';
  var directions  = ['right', 'left', 'top', 'bottom', 'front', 'back'];
  var imageSuffix = '.bmp';
  var geometry = new THREE.BoxGeometry( 1000, 1000, 1000 ); 

  var materialArray = [];
  for (var i = 0; i < 6; i++)
    materialArray.push( new THREE.MeshBasicMaterial({
      map: loader.load(imagePrefix + directions[i] + imageSuffix),
      side: THREE.BackSide
    }));
  var sky = new THREE.Mesh( geometry, materialArray );
  scene.add(sky);
}

/**
  * Add a character
  **/

function getSphere(scene) {
  var geometry = new THREE.SphereGeometry( 30, 12, 9 );
  var material = new THREE.MeshPhongMaterial({
    color: 0xd0901d,
    emissive: 0xaf752a,
    side: THREE.DoubleSide,
    flatShading: true
  });
  var sphere = new THREE.Mesh( geometry, material );

  // create a group for translations and rotations
  var sphereGroup = new THREE.Group();
  sphereGroup.add(sphere)

  sphereGroup.position.set(0, 24, 100);
  scene.add(sphereGroup);
  return [sphere, sphereGroup];
}

/**
  * Store all currently pressed keys
  **/

function addListeners() {
  window.addEventListener('keydown', function(e) {
    pressed[e.key.toUpperCase()] = true;
  })
  window.addEventListener('keyup', function(e) {
    pressed[e.key.toUpperCase()] = false;
  })
}

/**
 * Update the sphere's position
 **/

function moveSphere() {
  var delta = clock.getDelta(); // seconds
  var moveDistance = 200 * delta; // 200 pixels per second
  var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 deg) per sec

  // move forwards/backwards/left/right
  if ( pressed['W'] ) {
    sphere.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle)
    sphereGroup.translateZ( -moveDistance );
  }
  if ( pressed['S'] ) 
    sphereGroup.translateZ(  moveDistance );
  if ( pressed['Q'] )
    sphereGroup.translateX( -moveDistance );
  if ( pressed['E'] )
    sphereGroup.translateX(  moveDistance ); 

  // rotate left/right/up/down
  var rotation_matrix = new THREE.Matrix4().identity();
  if ( pressed['A'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
  if ( pressed['D'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
  if ( pressed['R'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), rotateAngle);
  if ( pressed['F'] )
    sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle);
}

/**
  * Follow the sphere
  **/

function moveCamera() {
  var relativeCameraOffset = new THREE.Vector3(0,50,200);
  var cameraOffset = relativeCameraOffset.applyMatrix4(sphereGroup.matrixWorld);
  camera.position.x = cameraOffset.x;
  camera.position.y = cameraOffset.y;
  camera.position.z = cameraOffset.z;
  camera.lookAt(sphereGroup.position);
}

// Render loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  moveSphere();
  moveCamera();
};

// state
var pressed = {};
var clock = new THREE.Clock();

// globals
var scene = getScene();
var camera = getCamera();
var light = getLight(scene);
var renderer = getRenderer();

// add meshes
var loader = new THREE.TextureLoader();
var floor = getPlane(scene, loader);
var background = getBackground(scene, loader);
var sphereData = getSphere(scene);
var sphere = sphereData[0];
var sphereGroup = sphereData[1];

addListeners();
render();
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; background: black; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

至于如何知道如何找到这个错误,在这个特殊情况下,除了经验,我不知道我怎么知道。我知道不幸的是,浏览器通常会使用WebGL来合成错误,因为它很难测试。大多数浏览器在没有GPU的服务器上进行测试,这意味着它们不能测试足够的WebGL。他们在合成GPU加速之前构建了他们的测试系统。另一个原因是测试合成是浏览器特定的,因此WebGL测试不能包含对它的测试。每个浏览器供应商必须为their testing systems run the browsers in non-release modes or the APIs that might make it possible to test don't actually go through the same code as the code the draws to the screen实现自己的测试。

对于WebGL,您通常应该在浏览器中获得相同的结果,而合成问题是他们弄错的最常见的地方。特别是当不使用defauts时。所以,首先我检查了上下文是否设置为非默认设置,如alpha: falsepremultipliedAlpha: false等。为此,我只是打开了Chrome的开发工具并选择了代码段上下文

enter image description here

一旦我有了正确的调试器上下文,我就从第一个画布

获得了WebGL上下文

enter image description here

我看到alpha: false这不是默认值,因此这是第一个线索。如果有多个画布,我将不得不使用'querySelectorAll'并尝试每个画布,直到我得到WebGL。

然后我也看到你的CSS与我做的不同。我会用

body { margin: 0; }
canvas { width: 100vw; height: 100vw; display: block; }

不需要overflow: hidden并明确说出我想要的内容。 I have strong opinions大多数three.js应用程序调整画布大小的方式是反模式。

我看到你设置你的css使画布高度达到100%,但你没有设置身高,所以如果没有别的,你的画布将没有高度。所以,我设置画布的背景颜色,这样我就可以看到它有多大。我假设它实际上是零。那是(a)我看到它是渲染并设置背景颜色使它出现和(b)你的画布出现,因为three.js基于window.innerHeight黑客攻击画布大小并且还与你的css混淆