three.js - 如何做vr全景看房 (day03)

1,234 阅读3分钟

有多种思路可以实现,这里主要提供一种:

  1. 绘制一个球体
  2. 把全景贴图贴于球内壁。
  3. 把摄像机原点置入球内。
  4. 添加视角鼠标控制。
  5. 添加sprite同时交互。

(一). 引入所需文件

未标题-1.png

我的目录结构:

未标题-1.png

(2).在main.js编写主要逻辑。

添加主要场景,相机

//main.js

var scene, camera, renderer, sprite, sprite2;
var width = document.body.clientWidth;
var height = document.body.clientHeight;
...
function initThree() {
  scene = new THREE.Scene();  //场景
  addCamera(); //相机
  
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(width, height);
  document.getElementById("container").appendChild(renderer.domElement);
   //上述代码 生成一个渲染器 简单来说 就是用来绘制
   
  let controls = new THREE.OrbitControls(camera, renderer.domElement);
  //用来做视角控制,旋转等
  addGeo(); //用来绘制球体
  ...
}

function addCamera() {
  camera = new THREE.PerspectiveCamera(45, document.body.clientWidth/
  document.body.clientHeight, .1, 500);  //透视相机
  // 透视相机更符合人眼的设定。45也是人眼睛一般看到的角度。
  camera.position.set(0, 0, 0.01);
  //设置相机原点
}
...

(3).绘制一个球体

function addGeo() {
  var geometry = new THREE.SphereGeometry(1, 50, 50);  //一个球体 半径为1
  geometry.scale(1, 1, -1);    //这里是做反转,把贴在外部的图 给贴到内侧
  var texture = new THREE.TextureLoader().load('./img/scene.jpeg'); //网上找的 全景图
  var sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });  
  var mesh = new THREE.Mesh(geometry, sphereMaterial); //网格模型对象Mesh
  // mesh.material.wireframe = true  //这里主要用来描线,在没有实现效果前做辅助功能,用完就注释。
  scene.add(mesh); //添加进场景中
}

GIF 2022-5-15 18-30-08.gif

一个简要的全景demo就差不多完事。

(4) 加点标签交互

function initThree(){
 ...
  addLogo();
  addLogo2();
  document.getElementById("container").addEventListener('mousemove', onMouseMove, false);
   ...
 }

function addLogo() {
  var spriteMap = new THREE.TextureLoader().load("./img/logo_chufang.jpg"); //厨房logo图
  var spriteMaterial = new THREE.SpriteMaterial({ map: spriteMap, color: 0xffffff });
  sprite = new THREE.Sprite(spriteMaterial);
  sprite.scale.set(0.04, 0.02, 0.14); //设置缩放比
  sprite.position.set(0, 0, .2);      
  scene.add(sprite);
}

//设置鼠标移动之后显示的提示图片 ,初始opacity为0
function addLogo2() {
  var spriteMap = new THREE.TextureLoader().load("./img/gougou.jpeg");
  var spriteMaterial = new THREE.SpriteMaterial({
    map: spriteMap, 
    opacity: 0
  });
  sprite2 = new THREE.Sprite(spriteMaterial);
  sprite2.scale.set(0.04, 0.03, 0.03);
  sprite2.position.set(0, .027, .2);
  scene.add(sprite2);
}

(5) 判断鼠标移动时是否触碰了图标

主要逻辑是利用摄像机原点和鼠标经过的物体的点,利用这两点形成一条射线,通过射线和物体接触得到接触的点是否落在这个物体上来判断是否接触到。
//交互 
function onMouseMove(e) {
  var mouse = new THREE.Vector2();
  mouse.x = (e.clientX / document.body.clientWidth) * 2 - 1;
  mouse.y = -(e.clientY / document.body.clientHeight) * 2 + 1;
  var vector = new THREE.Vector3(mouse.x, mouse.y, .5).unproject(camera);  //屏幕坐标
  var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
  //形成 摄像机和鼠标到达的点的射线

  raycaster.camera = camera 
  var intersects = raycaster.intersectObject(sprite, true);
  //射线和物体求交点
  if (intersects.length > 0) { 
    SELECTED = intersects[0].object; //判断第一个接触到的对象
    var intersected = intersects[0].object;
    sprite2.material.opacity = .7; //鼠标经过图标就显示
  } else {
    sprite2.material.opacity = 0;  //隐藏
  }
  raycaster.setFromCamera(mouse, camera); //用一个新的原点和方向向量来更新射线
}

GIF 2022-5-15 18-48-39.gif

图片有点糊了可以看到有 '内有恶犬'的提示

(6) main.js主要代码


var scene, camera, renderer, sprite, sprite2;
var width = document.body.clientWidth;
var height = document.body.clientHeight;

//球体
function addGeo() {
  var geometry = new THREE.SphereGeometry(1, 50, 50);
  geometry.scale(1, 1, -1);
  var texture = new THREE.TextureLoader().load('./img/scene.jpeg');
  var sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });
  var mesh = new THREE.Mesh(geometry, sphereMaterial); //网格模型对象Mesh
  // mesh.material.wireframe = true
  scene.add(mesh);
}

function addCamera() {
  camera = new THREE.PerspectiveCamera(45, document.body.clientWidth / document.body.clientHeight, .1, 500);  //透视相机
  camera.position.set(0, 0, .1);
}


//让logo定在某个二维坐标的某个点

function addLogo() {
  var spriteMap = new THREE.TextureLoader().load("./img/logo_chufang.jpg");
  var spriteMaterial = new THREE.SpriteMaterial({ map: spriteMap, color: 0xffffff });
  sprite = new THREE.Sprite(spriteMaterial);
  sprite.scale.set(0.04, 0.02, 0.14);
  sprite.position.set(0, 0, .2);
  scene.add(sprite);
}

function addLogo2() {
  var spriteMap = new THREE.TextureLoader().load("./img/gougou.jpeg");
  var spriteMaterial = new THREE.SpriteMaterial({
    map: spriteMap, 
    opacity: 0
  });
  sprite2 = new THREE.Sprite(spriteMaterial);
  sprite2.scale.set(0.04, 0.03, 0.03);
  sprite2.position.set(0, .027, .2);
  scene.add(sprite2);
}

//交互
function onMouseMove(e) {
  var mouse = new THREE.Vector2();
  mouse.x = (e.clientX / document.body.clientWidth) * 2 - 1;
  mouse.y = -(e.clientY / document.body.clientHeight) * 2 + 1;
  var vector = new THREE.Vector3(mouse.x, mouse.y, .5).unproject(camera);
  var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
  raycaster.camera = camera
  var intersects = raycaster.intersectObject(sprite, true);
  if (intersects.length > 0) {
    SELECTED = intersects[0].object;
    var intersected = intersects[0].object;
    sprite2.material.opacity = .7;
  } else {
    sprite2.material.opacity = 0;
  }
  raycaster.setFromCamera(mouse, camera);
}


function loop() {
  requestAnimationFrame(loop);
  renderer.render(scene, camera);
}

function initThree() {
  scene = new THREE.Scene();  //场景
  addCamera();
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(width, height);
  document.getElementById("container").appendChild(renderer.domElement);
  let controls = new THREE.OrbitControls(camera, renderer.domElement);
  addGeo();
  loop();
  addLogo();
  addLogo2();
  document.getElementById("container").addEventListener('mousemove', onMouseMove, false);
}

window.onload = initThree;