携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
**所用知识:天空盒,**Raycaster,OrbitControls,gsap
Raycaster官方文档:threejs.org/docs/index.…
天空盒相关介绍:threejs.org/manual/#zh/…
OrbitControls介绍:threejs.org/docs/index.…
gsap:greensock.com/docs/
实现思路:初始化场景,摄像机,渲染器,创建一个天空盒,添加控制器,通过Raycaster获取鼠标与场景内各种的交点,对交点进行判断,若为自己所设置的物体则进行场景跳转操作。
初始化
可以参考www.webgl3d.cn/Three.js/,部分代码如下,
// 创建场景const scene = new THREE.Scene();// scene.fog = new THREE.Fog(0x88ccee, 20, 1100);// 创建相机const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.set(0, 30, -100);// 创建渲染器const renderer = new THREE.WebGLRenderer({ // 抗锯齿 antialias: true,});renderer.setSize(window.innerWidth, window.innerHeight);const render = () => { renderer.render(scene, camera); renderer.setSize(window.innerWidth, window.innerHeight); requestAnimationFrame(render);};
天空盒创建
立方体贴6面贴图然后把摄像机放在物体里面,把立方体的外面朝内部
//创建天空盒
const skybox = new THREE.BoxGeometry(1000, 1000, 1000)const skyloader = new THREE.TextureLoader(); //加载材质const skymaterials = [ new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/px.jpg') }), new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/nx.jpg') }), new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/py.jpg') }), new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/ny.jpg') }), new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/pz.jpg') }), new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/nz.jpg') }),];skybox.scale(1, 1, -1);//外朝内const cube = new THREE.Mesh(skybox, skymaterials);scene.add(cube)
然后再添加一个轨道控制器,就能四周旋转了,可以自己设置属性参数来控制范围之类的。
.update () : Boolean
更新控制器。
必须在摄像机的变换发生任何手动改变后调用, 或如果.autoRotate或.enableDamping被设置时,在update循环里调用。
如果要实现惯性效果,则要在render里加controls.update();
//添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.enableDamping = true;//惯性效果 controls.enablePan = false; controls.minDistance = 0.01; controls.maxDistance = 70;
const render = () => { renderer.render(scene, camera);
controls.update();
renderer.setSize(window.innerWidth, window.innerHeight); requestAnimationFrame(render);};
我们可以给场景加个fog,可以看到立方体的边
scene.fog = new THREE.Fog(0x88ccee, 20, 1100);
也可以用一个球贴一张全景图来实现天空盒,不过这个实现的话顶部会有一定瑕疵。
添加一个交互点,用Raycaster
这个交互点可以是精灵图,也可以是具体的物体比如自己导入的模型,主要是通过raycaster进行检测,判断鼠标确实在这个点上,然后进行一些操作,比如跳转。
webGL中获取鼠标交互物体的原理:通过三维空间中相机视点与鼠标在屏幕上的地位的连线,造成一条直线,捕捉与此直线相交的空间中的物体,即为交互对象物体。
在three中,Raycaster为咱们封装了大量的逻辑代码,包含生成相机到鼠标的射线、射线与空间物体的碰撞检测、射线相交物体深度计算、相交物体列表等等。
Raycaster类的.intersectObject()办法:检测所有在射线与物体之间,包含或不包含后辈的相交局部。返回后果时,相交局部将按间隔进行排序,最近的位于第一个。
在运用Raycaster的时候我们要知道自己的鼠标位置,将自己平面上的坐标转换为实际坐标。
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
// 推导过程:
// 设A点为点击点`(x1,y1),x1=e.clintX, y1=e.clientY`
// 设A点在世界坐标中的坐标值为`B(x2,y2);`
// 因为A点的坐标值的原点是以屏幕左上角为`(0,0);`
// 咱们能够计算可得以屏幕核心为原点的`B`值
x2' = x1 - innerWidth/2
y2' = innerHeight/2 - y1
//又因为在世界坐标的范畴是[-1,1],要失去正确的B值咱们必须要将坐标标准化
//x2 = (x1 -innerWidth/2)/(innerwidth/2) = (x1/innerWidth)*2-1
//同理得
y2 = -(y1/innerHeight)*2 +1``
大致了解后就可以尝试做一下
// 射线获取鼠标点击的位置 const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); const mouseDown = (e) => { e.preventDefault(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(scene.children); if (intersects.length > 0) { console.log(intersects); if ( intersects[0].object.name === "自己所设置的name" )
{
//执行的操作 console.log(1) } };
点击下面精灵图threejs.org/docs/index.…
可以看到
就去判断name就好了
场景切换
切换场景只需要在执行的操作中改变天空盒的图片就行了,或者创建另外一个天空盒放置在另一个位置,在执行的操作中用gsap动画库中的一些动画效果改变摄像机位置。
gsap.to(controls.target, { duration: 0.5, x: 100, y: 2.5, z: 100, onComplete: () => { gsap.to(camera.position, { duration: 0.5, x: 100, y: 2.5, z: 100 }) } })
最后
新人第一次发文,素材随便找的,有啥不合适的或者不小心侵权的,请联系删除。threejs刚学两个星期,请大佬们见谅,多多指点,给点建议。
顺便一提,8月7日20:00,枝江最最可爱的小狐狸乃琳小姐的生日会期待你的观看🥰live.bilibili.com/22625027