背景
你是嘿嘿嘿侦探社实习侦探🕵️,接到上级指派任务,到甄开心小镇🏠调查市民甄不戳👨宝石💎失窃案,根据线人流浪汉老石👨🎤提供的线索,小偷就躲在小镇,快把他找出来,帮甄不戳寻回失窃的宝石吧!
本文使用 Three.js SphereGeometry 创建 3D 全景图预览功能,并在全景图中添加二维 SpriteMaterial、Canvas、三维 GLTF 等交互点,实现具备场景切换、点击交互的侦探小游戏。
实现效果
左右滑动屏幕,找到 3D 全景场景中的 交互点 并点击,找出嫌疑人真正躲藏的位置。
已适配移动端,可以在手机上打开访问。
代码实现
初始化场景
创建场景,添加摄像机、光源、渲染。
1 | js复制代码// 透视摄像机 |
使用球体实现全景功能
1 | js复制代码// 创建全景场景 |
📌全景贴图如上图所示,图片来源于Bing。
💡 球体 SphereGeometry
构造函数:
1 | js复制代码THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, phiStart, phiLength, thetaStart, thetaLength) |
radius:半径;segmentsWidth:经度上的分段数;segmentsHeight:纬度上的分段数;phiStart:经度开始的弧度;phiLength:经度跨过的弧度;thetaStart:纬度开始的弧度;thetaLength:纬度跨过的弧度。
💡 基础网格材质 MeshBasicMaterial
球体的材质使用的是 MeshBasicMaterial, 是一种简单的材质,这种材质不受场景中光照的影响。使用这种材质的网格会被渲染成简单的平面多边形,而且也可以显示几何体的线框。
构造函数:
1 | js复制代码MeshBasicMaterial(parameters: Object) |
parameters :(可选)用于定义材质外观的对象,具有一个或多个属性。
属性:
.alphaMap[Texture]:alpha贴图是一张灰度纹理,用于控制整个表面的不透明度。(黑色:完全透明;白色:完全不透明)。默认值为null。.aoMap[Texture]:该纹理的红色通道用作环境遮挡贴图。默认值为null。.aoMapIntensity[Float]:环境遮挡效果的强度。默认值为1。零是不遮挡效果。.color[Color]:材质的颜色,默认值为白色0xffffff。.combine[Integer]:如何将表面颜色的结果与环境贴图(如果有)结合起来。选项为THREE.Multiply(默认值),THREE.MixOperation,THREE.AddOperation。如果选择多个,则使用.reflectivity在两种颜色之间进行混合。.envMap[Texture]:环境贴图。默认值为null。.lightMap[Texture]:光照贴图。默认值为null。.lightMapIntensity[Float]:烘焙光的强度。默认值为1。.map[Texture]:纹理贴图。默认为null。.morphTargets[Boolean]:材质是否使用morphTargets。默认值为false。.reflectivity[Float]:环境贴图对表面的影响程度,默认值为1,有效范围介于0(无反射)和1(完全反射)之间。.refractionRatio[Float]:折射率不应超过1。默认值为0.98。.specularMap[Texture]:材质使用的高光贴图。默认值为null。.wireframe[Boolean]:将几何体渲染为线框。默认值为false(即渲染为平面多边形)。.wireframeLinecap[String]:定义线两端的外观。可选值为butt,round和square。默认为round。.wireframeLinejoin[String]:定义线连接节点的样式。可选值为round,bevel和miter。默认值为round。.wireframeLinewidth[Float]:控制线框宽度。默认值为1。
💡 TextureLoader
TextureLoader 从给定的URL开始加载并将完全加载的 texture 传递给 onLoad。该方法还返回一个新的纹理对象,该纹理对象可以直接用于材质创建,加载材质的一个类,内部使用 ImageLoader 来加载文件。
构造函数:
1 | js复制代码TextureLoader(manager: LoadingManager) |
manager:加载器使用的loadingManager,默认值为THREE.DefaultLoadingManager。
方法:
1 | js复制代码.load(url: String, onLoad: Function, onProgress: Function, onError: Function) : Texture |
url:文件的URL或者路径,也可以为Data URI。onLoad:加载完成时将调用。回调参数为将要加载的texture。onProgress:将在加载过程中进行调用。参数为XMLHttpRequest实例,实例包含total和loaded参数。onError:在加载错误时被调用。
添加交互点
新建交互点数组,包含每个交互点的名称、缩放比例、空间坐标。
1 | js复制代码var interactPoints = [ |
添加二维静态图片交互点
1 | js复制代码let pointMaterial = new THREE.SpriteMaterial({ |
💡 精灵材质 SpriteMaterial
构造函数:
1 | js复制代码SpriteMaterial(parameters : Object) |
parameters:可选,用于定义材质外观的对象,具有一个或多个属性。材质的任何属性都可以从此处传入(包括从Material和ShaderMaterial继承的任何属性)。SpriteMaterials不会被Material.clippingPlanes裁剪。
属性:
.alphaMap[Texture]:alpha 贴图是一张灰度纹理,用于控制整个表面的不透明度。默认值为 null。.color[Color]:材质的颜色,默认值为白色 0xffffff。 .map 会和 color 相乘。.map[Texture]:颜色贴图。默认为 null。.rotation[Radians]:sprite 的转动,以弧度为单位。默认值为 0。.sizeAttenuation[Boolean]:精灵的大小是否会被相机深度衰减。(仅限透视摄像头。)默认为 true。
使用同样的方法,加载嫌疑人二维图片添加到场景中。
1 | js复制代码function loadMurderer() { |
添加三维动态模型锚点
通过加载地标锚点形状的 gltf 模型来实现三维动态锚点,加载 gltf 需要单独引入 GLTFLoader.js,地标模型使用 Blender 构建。
1 | js复制代码var loader = new THREE.GLTFLoader(); |
需要在 requestAnimationFrame 中通过修改模型的 rotation 来实现自传动画效果。
1 | js复制代码function animate() { |
添加二维文字提示
可以使用 Canvas 创建文字提示添加到场景中。
1 | js复制代码function makeTextSprite(message, parameters) { |
使用方法:
1 | js复制代码outsideTextTip = makeTextSprite('进入室内查找'); |
💡Canvas画布可以作为Three.js纹理贴图CanvasTexture。Canvas画布可以通过2D API绘制各种各样的几何形状,可以通过Canvas绘制一个轮廓后然后作为Three.js网格模型、精灵模型等模型对象的纹理贴图。💡measureText()方法返回一个对象,该对象包含以像素计的指定字体宽度。如果您需要在文本向画布输出之前,就了解文本的宽度,那么请使用该方法。measureText语法:context.measureText(text).width。
添加三维文字提示
由于时间有限,三维文字 本示例中并未用到,但是在页面中使用 3D 文字会实现更好的视觉效果,想了解具体实现细节,可以阅读我的另一篇文章,后续的鼠标捕获等内容也在该文中有详细讲解。
🔗传送门:使用three.js实现炫酷的酸性风格3D页面
鼠标捕获
使用 Raycaster 获取点击选中网格对象,并添加点击交互。
1 | js复制代码function onDocumentMouseDown(event) { |
场景切换
1 | js复制代码function update() { |
💡透视相机的属性创建完成后我们可以根据个人需求随意修改,但是相机的属性修改后,需要调用updateProjectionMatrix()方法来更新。💡THREE.Math.degToRad:将度转化弧度。
到这里,3D 全景功能全部实现。
🔗完整代码:github.com/dragonir/3d…
总结
本案例主要涉及到的知识点包括:
- 球体
SphereGeometry - 基础网格材质
MeshBasicMaterial - 精灵材质
SpriteMaterial - 材质加载
TextureLoader - 文字纹理
Canvas - 鼠标捕获
Raycaster
参考资料
本文转载自: 掘金