// 创建立方体
let geometry = new THREE.BoxGeometry(1,2,3) // 长宽高
let meterial = new THREE.MeshNormalMaterial( {
color: 0x00ff00 }) // 立方体材质 颜色
let box = new THREE.Mesh(geometry,meterial) // 构造立方体 = 体积 + 材质
// 创建场景
let scene = new THREE.Scene()
// 盒子添加到场景
scene.add(box)
/**
* 创建相机
* fov — 摄像机角度
* aspect — 长宽比
* near — 摄像机最近的距离
* far — 摄像机最远的距离
*/
let camera = new THREE.PerspectiveCamera(75, width / height, 0.01, 1000)
// 相机摆位,相机朝向距离
camera.position.z = 4
// 相机位置
camera.lookAt(0,0,0)
// 关联canvas
let renderder = new THREE.WebGLRenderer({
canvas })
// 渲染页面的大小
renderder.setSize(width,height)
### 1.5 动画渲染
function animation() {
box.rotation.x += 0.01
box.rotation.y += 0.01
renderder.render(scene,camera) // 渲染 场景 + 移动相机
requestAnimationFrame(animation)
}
animation()
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<style>
*{
padding: 0;margin: 0; }
body{
overflow: hidden}
style>
<script src="../js/three.min.js">script>
<body>
<canvas id="canvas">canvas>
body>
<script>
/*
* 1.创建立方体
* 2.创建场景
* 3.创建相机
* 4.创建渲染器
* 5.创建动画
* */
// canvas配置
let canvas = document.querySelector('#canvas')
let width = canvas.width = window.innerWidth
let height = canvas.height = window.innerHeight
window.onresize = resize
function resize() {
width = canvas.width = window.innerWidth
height = canvas.height = window.innerHeight
}
// 创建立方体
let geometry = new THREE.BoxGeometry(1,2,3) // 长宽高
let meterial = new THREE.MeshNormalMaterial( {
color: 0x00ff00 }) // 立方体材质 颜色
let box = new THREE.Mesh(geometry,meterial) // 构造立方体 = 体积 + 材质
// 创建场景
let scene = new THREE.Scene()
// 盒子添加到场景
scene.add(box)
/**
* 创建相机
* fov — 摄像机角度
* aspect — 长宽比
* near — 摄像机最近的距离
* far — 摄像机最远的距离
*/
let camera = new THREE.PerspectiveCamera(75, width / height, 0.01, 1000)
camera.position.z = 4 // 相机摆位,相机朝向距离
camera.lookAt(0,0,0) // 相机位置
// 创建渲染器
let renderder = new THREE.WebGLRenderer({
canvas }) // 关联canvas
renderder.setSize(width,height) // 渲染页面的大小
function animation() {
box.rotation.x += 0.01
box.rotation.y += 0.01
renderder.render(scene,camera) // 渲染 场景 + 移动相机
requestAnimationFrame(animation)
}
animation()
script>
html>
// three.js-dev\examples\js\controls\OrbitControls.js
// 控制器
let control = new THREE.OrbitControls(camera)
function animation() {
box.rotation.x += 0.01
box.rotation.y += 0.01
control.update() // 动画渲染
renderder.render(scene,camera)
requestAnimationFrame(animation)
}
function resize() {
width = canvas.width = window.innerWidth
height = canvas.height = window.innerHeight
// 渲染页面的大小
renderder.setSize(width, height)
// 更新相机宽高比
camera.aspect(width / height)
// 新的比例应用到世界坐标系中
camera.updateProjectionMatrix()
}
立方体 = 组合 ( 创建几何体 + 材质 + 颜色 )
/**
* 立方体
*/
function geometry() {
// 创建立方体
let geometry = new THREE.BoxGeometry(1,2,3)
// 材质、颜色
let meterial = new THREE.MeshNormalMaterial({
color: 0x00ff00 })
// 生成立方体
let cube = new THREE.Mesh(geometry,meterial)
return cube
}
### 2.4 网格几何
/**
* 创建网格
*/
function mesh() {
let geometry = null
/*
* 立方体
* 参数:x长,y长,z长,x长分段数,y长分段数,z长分段数
* */
geometry = new THREE.BoxGeometry(1,1,1,2,2,2)
/*
* 圆形
* 参数:半径,分段数,起始角度,弧度
* */
geometry = new THREE.CircleBufferGeometry(5,36,0,Math.PI*2)
/*
* 圆锥
* 参数:圆锥底部的半径,圆锥的高度,圆锥侧面周围的分段数,圆锥侧面沿着其高度的分段数
* */
geometry = new THREE.ConeGeometry(5,10,36)
/*
* 圆柱
* 参数:圆柱的顶部半径,圆柱的底部半径, 圆柱的高度...
* */
geometry = new THREE.CylinderGeometry(2,5,20,20)
/*
* 十二面几何体
* 参数:十二面体的半径,增加一些顶点
* */
geometry = new THREE.DodecahedronGeometry(2,3)
// 格局分段情况,构架网格
let wireframe = new THREE.WireframeGeometry(geometry)
// 生产线条
let line = new THREE.LineSegments(wireframe)
// 设置线条颜色(十六进制)
line.material.color = new THREE.Color(0xff0000)
return line
}
let axesHelper = new THREE.AxesHelper(5) // 坐标系长度
scene.add(axesHelper)
/**
* 添加平面
*/
function addPlane() {
// 创建坐标系列
let geometry = new THREE.Geometry()
// 创建点
geometry.vertices.push(new THREE.Vector3(1,0,0))
geometry.vertices.push(new THREE.Vector3(0,1,0))
geometry.vertices.push(new THREE.Vector3(0,0,1))
// 创建面
let normal = new THREE.Vector3(1,1,1) // 面的法向量,确定正反面
let color = new THREE.Color(0xff00ff) // 面颜色
// 0,1,2 是面序号确定该面
geometry.faces.push(new THREE.Face3(0,1,2,normal,color)) // 添加面进去
// 材质
let meterials = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide //看到两个面
})
return new THREE.Mesh(geometry,meterials)
}
//...
let plane = addPlane()
scene.add(plane)
function addPlane() {
// 创建坐标系列
let geometry = new THREE.Geometry()
// 创建点
geometry.vertices.push(new THREE.Vector3(1,0,0))
geometry.vertices.push(new THREE.Vector3(0,1,0))
geometry.vertices.push(new THREE.Vector3(0,0,1))
geometry.vertices.push(new THREE.Vector3(1,2,1))
// 创建面
let normal = new THREE.Vector3(1,1,1) // 面的法向量,确定正反面
let color = new THREE.Color(0xff00ff) // 面颜色
// 0,1,2 是面序号确定该面,3是制高点
geometry.faces.push(new THREE.Face3(0,1,2,normal,color)) // 添加面进去
geometry.faces.push(new THREE.Face3(3,1,2,normal,color)) // 添加面进去
geometry.faces.push(new THREE.Face3(3,0,1,normal,color)) // 添加面进去
geometry.faces.push(new THREE.Face3(3,0,2,normal,color)) // 添加面进去
// 材质
let meterials = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide //看到两个面
})
return new THREE.Mesh(geometry,meterials)
}
function addPlane() {
// 创建坐标系列
let geometry = new THREE.Geometry()
// 创建点
geometry.vertices.push(new THREE.Vector3(1,0,0))
// 0,0,Math.PI*2/5,'XYZ' -> xyz一一对应,弧度单位
geometry.vertices.push(
new THREE.Vector3(1,0,0).applyEuler(new THREE.Euler(0,0,Math.PI*2/5,'XYZ'))
)
geometry.vertices.push(new THREE.Vector3(-1,0,0))
// 创建面
let normal = new THREE.Vector3(1,1,1) // 面的法向量,确定正反面
let color = new THREE.Color(0xff00ff) // 面颜色
// 0,1,2 是面序号确定该面,3是制高点
geometry.faces.push(new THREE.Face3(0,1,2,normal,color)) // 添加面进去
// 材质
let meterials = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide //看到两个面
})
return new THREE.Mesh(geometry,meterials)
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9HRJ0zH-1572667455144)(file:///C:/Users/MACHENIKE/Documents/My Knowledge/temp/58ef6a89-39c9-4b7f-99d5-38d36c0eb5cd/128/index_files/b9c34d8f-d86f-45c6-b50b-7a9ddd7b7625.png)]
魔仙棒 = 组合(near + far + depth) 最近距离 + 最近距离 + 厚度
/**
* 返回一个几何体
* @param far 最远距离
* @param near 最近距离
* @param depth 厚度
* @param count 边数
*/
function getStartGeometry(far,near,depth,count=5){
const geometry = new THREE.Geometry()
// 厚度的点
let frontPoint = new THREE.Vector3(0,0,depth)
geometry.vertices.push(frontPoint)
// 厚度背后的点
let backPoint = new THREE.Vector3(0,0,-depth)
geometry.vertices.push(backPoint)
// 远近点 +1 与原点重合
for (let i = 0; i < count+1; i++) {
// 最远距离的点
let farPoint = new THREE.Vector3(far,0,0)
.applyEuler(
new THREE.Euler(0,0,Math.PI*2*i/count,'XYZ')
)
geometry.vertices.push(farPoint)
// 最近距离的点 + Math.PI/5 错开两个角
let nearPoint = new THREE.Vector3(near,0,0)
.applyEuler(
new THREE.Euler(0,0,Math.PI*2*i/count + Math.PI/count,'XYZ')
)
geometry.vertices.push(nearPoint)
}
// 平面
const normal = new THREE.Vector3(0,1,0)
const color = new THREE.Color(0xff00ff)
for (let i = 0; i < count*2; i++) {
geometry.faces.push(new THREE.Face3(0,i+2,i+3,normal,color)) // 正面
geometry.faces.push(new THREE.Face3(1,i+2,i+3,normal,color)) // 背面
}
// 绕着z轴旋转
geometry.rotateZ(Math.PI/count/2)
return geometry
}
// 创建星星
const start1 = getStartGeometry(3,1,0.5,5)
// 材质包
const materials = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide // 看到两个面
})
const startMesh_1 = new THREE.Mesh(start1,materials)
// startMesh_1,startMesh_2,cylinderMesh 创建出来的方体
let obj = new THREE.Object3D()
obj.add(startMesh_1,startMesh_2,cylinderMesh)
scene.add(obj) // 添加到场景
// 控制器
let control = new THREE.OrbitControls(camera)
function animation() {
requestAnimationFrame(animation)
control.update()
renderder.render(scene,camera)
// 更新控制器
obj.children[0].rotation.y += 0.1 // 控制其中一部分
// 控制整体
}
animation()
function createLine(){
let geometry = new THREE.Geometry()
// 添加点
geometry.vertices.push(new THREE.Vector3(1,1,1))
geometry.vertices.push(new THREE.Vector3(2,2,2))
geometry.vertices.push(new THREE.Vector3(3,3,3))
// 上色,分段上色,也可单个颜色
geometry.colors.push(
new THREE.Color(0xff0000),
new THREE.Color(0x0000ff),
)
// 材质
const meterial = new THREE.LineBasicMaterial({
vertexColors:true
})
// 连接点
const line = new THREE.Line(geometry,meterial)
return line
}
let scene = new THREE.Scene()
let line = createLine()
scene.add(line)
function createPlane(){
const geometry = new THREE.PlaneGeometry(5,5)
// 创建纹理引入工具
const loader = new THREE.TextureLoader()
// 引入图片
const texture = loader.load('../textures/envmap.png')
// 材质
const meterial = new THREE.MeshBasicMaterial({
map:texture, // 引入的图片
side:THREE.DoubleSide // 正反面材质
})
return new THREE.Mesh(geometry,meterial)
}
let scene = new THREE.Scene()
let plane = createPlane()
scene.add(plane)
// 世界图片地址:thressJs\three.js-dev\examples\textures
let scene = new THREE.Scene()
let loader = new THREE.CubeTextureLoader()
scene.background = loader
.setPath('../textures/cube/Bridge2/')
.load([
'posx.jpg',
'negx.jpg',
'posy.jpg',
'negy.jpg',
'posz.jpg',
'negz.jpg'
])
this.scene = this.getScene()
this.scene.background = new THREE.Color(0xffffff)
如果不手动添加,页面没有光线
观察光线
// 添加平面
createPlane(){
const geometry = new THREE.PlaneGeometry(500,500)
const material = new THREE.MeshLambertMaterial({
color:new THREE.Color(0xcccccc),
side:THREE.DoubleSide
})
const mesh = new THREE.Mesh( geometry, material )
mesh.position.set( 0, -15, 0) // 地板往下
mesh.rotateX(-Math.PI / 2)
return mesh
},
环境光会均匀的照亮场景中的 所有物体。环境光不能用来投射阴影,因为它没有方向。
物理知识:物体的颜色的本质是标识反射这种光线的能力,环境光一般都是白色的oxffffff
// 盒子
getCube(){
let geometry = new THREE.BoxGeometry(1,2,3)
// Lamber材质,支持反光
let meterial = new THREE.MeshLambertMaterial()
const mesh = new THREE.Mesh( geometry,meterial )
return mesh
},
// 添加光线
addLight(){
// 定义光线 0xffffff 光颜色 0.3 亮度
const ambientLight = new THREE.AmbientLight(0xffffff,0.3)
// 添加到世界
this.scene.add(ambientLight)
},
从一个点向各个方向发射的光源,越远越暗。(部分照亮)
一个常见的例子是模拟一个灯泡发出的光。该光源可以投射阴影
// 点光源
addPointLight (){
const pointLight = new THREE.PointLight(0xffffff,1,1000)
pointLight.position.set(-5,5,5)
this.scene.add(pointLight)
// 点光源模拟器-辅助线
const pointLightHelper = new THREE.PointLightHelper(pointLight,1)
this.scene.add(pointLightHelper)
return pointLight
},
// 添加平面
createPlane(){
const geometry = new THREE.PlaneGeometry(500,500)
// 镜面反光效果
const material = new THREE.MeshPhongMaterial({
color:new THREE.Color(0x444444),
side:THREE.DoubleSide
})
const mesh = new THREE.Mesh( geometry, material )
mesh.position.set( 0, -15, 0) // 地板往下
mesh.rotateX(-Math.PI / 2)
return mesh
},
相当于太阳光
// 平行光
addDirectionalLight (){
// 初始竖直平行光,上面照亮
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 )
// 设置平行光的位置
directionalLight.position.set(1,1,1)
return directionalLight
},
相当于白天和黑夜分界线的全局背景光
// 添加半球光
addHemisphereLight(){
let hemisphereLight = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
hemisphereLight.position.set(0,0,1)
return hemisphereLight
},
// 动画
animation(){
requestAnimationFrame(this.animation)
this.controls.update()
this.degree += 0.01 // 默认0
this.hemisphereLight.position.set(Math.sin(this.degree),Math.cos(this.degree),0)
this.renderer.render(this.scene,this.camera)
},
// 添加聚光灯
addSpotLight(){
let spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set(0,20,0) // 灯光位置
spotLight.angle = Math.PI/3 // 灯光角度
return spotLight
},
必要条件:
getRenderer(){
let canvas = this.$refs.canvas
canvas.width = this.width
canvas.height = this.height
const render = new THREE.WebGLRenderer({
canvas })
render.setSize(this.width,this.height)
render.shadowMap.enabled = true // 开启阴影模式
return render
},
// 添加聚光灯
addSpotLight(){
let spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set(0,20,0) // 灯光位置
spotLight.angle = Math.PI/3 // 灯光角度
spotLight.castShadow = true // 开启阴影
return spotLight
},
// 获取盒子
getCube(){
let geometry = new THREE.BoxGeometry(1,2,3)
let meterial = new THREE.MeshLambertMaterial({
color: new THREE.Color(0xff0000)
})
const mesh = new THREE.Mesh( geometry,meterial )
mesh.castShadow = true // 开启阴影
return mesh
},
// 添加平面
createPlane(){
const geometry = new THREE.PlaneGeometry(500,500)
const material = new THREE.MeshPhongMaterial({
color:new THREE.Color(0x444444),
side:THREE.DoubleSide
})
const mesh = new THREE.Mesh( geometry, material )
mesh.position.set( 0, -15, 0) // 地板往下
mesh.rotateX(-Math.PI / 2)
mesh.receiveShadow = true // 地板接收阴影
return mesh
},
一种用于绘制线框样式几何体的材质
let material = new THREE.LineBasicMaterial( {
color: 0xffffff,
linewidth: 1, // 控制线宽。默认值为 1
// 定义线两端的样式。可选值为 'butt', 'round' 和 'square'。默认值为 'round'
linecap: 'round',
// 定义线连接节点的样式。可选值为 'round', 'bevel' 和 'miter'。默认值为
linejoin: 'round','round'
lights: false, // 材质是否受到光照的影响。默认值为 false
} );
一种用于绘制虚线样式几何体的材质
let material = new THREE.LineDashedMaterial( {
color: 0xffffff, // 材质的颜色(Color),默认值为白色 (0xffffff)
linewidth: 1, // 控制线宽。默认值为 1
scale: 1, // 线条中虚线部分的占比。默认值为 1
dashSize: 3, // 虚线的大小,是指破折号和间隙之和。默认值为 3
gapSize: 1, // 间隙的大小,默认值为 1
lights:false, // 材质是否受到光照的影响。默认值为 false。
} )
一个以简单着色(平面或线框)方式来绘制几何体的材质。这种材质不受光照的影响
let material = new THREE.MeshBasicMaterial( {
map:texture // 颜色贴图
})
一种按深度绘制几何体的材质。深度基于相机远近平面。白色最近,黑色最远。 (近白色,远黑色)
let material = new THREE.MeshDepthMaterial({
})
一种非光泽表面的材质,没有镜面高光。 朝着光源点那面变亮
let material = new THREE.MeshLambertMaterial({
map: texture, // 颜色贴图
envMap: scene.background, // 环境贴图,略反光
})
一种把法向量映射到RGB颜色的材质。
// 不同面看到不同颜色
let material = new THREE.MeshNormalMaterial({
})
// 将几何体渲染为线框。默认值为false(即渲染为平滑着色)
let material = new THREE.MeshNormalMaterial({
wireframe: true })
一种用于具有镜面高光的光泽表面的材质。 镜面反光
let material = new THREE.MeshPhongMaterial({
envMap: scene.background })
卡通着色的扩展
// gradientMap 卡通着色的渐变贴图
let material = new THREE.MeshToonMaterial({
gradientMap: texture })
npm i three # 该方法用不了
<template>
<div>
<canvas ref='canvas'>canvas>
div>
template>
<script>
/*
import THREE from 'three' 导致报错
"export 'default' (imported as 'THREE') was not found in 'three'
*/
//
import * as THREE from 'three';
export default {
data() {
return {
width:0,
height:0,
}
},
created(){
// 获取屏幕宽高
this.width = window.innerWidth
this.height = window.innerHeight
},
mouted(){
this.renderer = this.getRenderer()
this.camera = this.getCamera()
this.scene = this.getScene()
this.cube = this.getCube()
this.scene.add(this.cube)
this.animation()
},
methods(){
// 获取渲染范围
getRenderer(){
let canvas = this.$refs.canvas
canvas.width = this.width
canvas.height = this.height
const render = new THREE.WebGLRenderer({
canvas })
render.setSize(this.width,this.height)
return render
},
// 获取相机
getCamera(){
const camera = new THREE.PerspectiveCamera(
75,this.width/this.height,1,1000
)
camera.position.z = 10
camera.lookAt(0,0,0)
return camera
},
// 获取场景
getScene(){
const scene = new THREE.Scene()
return scene
},
// 获取盒子
getCube(){
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(1,2,3),
new THREE.MeshNormalMaterial()
)
return mesh
},
// 动画
animation(){
requestAnimationFrame(this.animation)
this.renderer.render(this.scene,this.camera)
}
}
}
script>
import {
OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export default {
mouted(){
this.camera = this.getCamera()
//...
this.controls = new OrbitControls(this.camera)
},
methods(){
// 动画
animation(){
requestAnimationFrame(this.animation)
this.controls.update()
this.renderer.render(this.scene,this.camera)
}
}
}
export default {
mouted(){
//...
this.registrEvent()
},
methods(){
// 监听窗口变化
registrEvent(){
window.onresize = this.resize
},
// 窗口变化函数
resize(){
// 重新获取屏幕宽高
this.width = window.innerWidth
this.height = window.innerHeight
// 设置相机大小
this.camera.aspect = this.width/this.height
// 相机设置应用
this.camera.updateProjectionMatrix()
// 设置渲染范围
this.renderer.setSize(this.width,this.height)
}
}
}
图片路径
-public
wordBg
-nx.png
-ny.png
-nz.png
//...
-src
export default {
mouted(){
//...
this.bgTexture()
},
methods(){
// 设置世界背景
bgTexture(){
const loader = new THREE.CubeTextureLoader()
// 图片放图assets/images
const bgTexture = loader.setPath('wordBg/')
.load([
'px.png','nx.png',
'py.png','ny.png',
'pz.png','nz.png'
])
this.scene.background = bgTexture
}
}
}
// 模型文件
examples/models/gltf // 复制文件放到public/models中的.gltf
import {
GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
loaderObj(){
const gltfLoader = new GLTFLoader()
gltfLoader.load('models/gltf/DamagedHelmet/glTF/DamagedHelment.gltf',res => {
this.scene.add(res.scene) // 加载头盔场景
})
}
// 调亮范围
getRenderer(){
let canvas = this.$refs.canvas
canvas.width = this.width
canvas.height = this.height
const render = new THREE.WebGLRenderer({
canvas })
render.gammaOutput = true // 调整渲染的颜色范围
render.setSize(this.width,this.height)
return render
},
// 添加光线
addLight(){
this.scene.add(new THREE.AmbientLight(0xffffff,0.7))
}
loaderObj(){
const gltfLoader = new GLTFLoader()
gltfLoader.load('models/gltf/DamagedHelmet/glTF/DamagedHelment.gltf',res => {
res.scene.traverse(mesh => {
// 背景对每个面一一映射
if(mesh.isMesh){
// mesh.isMesh 检测材质
mesh.material.side = THREE.DoubleSide
mesh.material.envMap = this.scene.background
}
})
this.scene.add(res.scene) // 加载头盔场景
})
}
// 模型文件
examples/models/mmd // 复制文件放到public/models中.pmd
//.pmd 模型文件
import {
MMDLoader } from 'three/examples/jsm/loaders/MMDLoader'
loaderObj(){
const gltfLoader = new MMDLoader()
gltfLoader.load('models/xxx.pmd',mesh => {
mesh.position.set(0,-10,0) // 调整位置
this.scene.add(mesh)
})
}
import {
OutlineEffect } from 'three/examples/jsm/loaders/OutlineEffect'
mounted(){
this.renderer = this.getRenderer()
//...
this.renderer = new OutlineEffect(this.renderer) // 轮廓线
this.animation()
},
让MMD加载动起来,vmd 动作文件
import {
MMDAnimationHelper } from 'three/examples/jsm/animation/MMDAnimationHelper'
import {
MMDLoader } from 'three/examples/jsm/loaders/MMDLoader'
// 挂在全局
const AMMO = require('three/examples/js/libs/ammo')
window.Ammo = new AMMO()
// three挂在全局时间参数
const clock = new THREE.Clock()
loaderObj(){
this.MMDhelper = new MMDAnimationHelper()
const loader = new MMDLoader()
loader.loadWithAnimation(
'models/mmd/miku/miku_v2.pmd', // 模型文件
'models/mmd/vmds/wavefile_v2.vmd', // 动作文件
mmd => {
this.MMDhelper.add( mmd.mesh,{
animation: mmd.animation,
physics:true
} )
// 改变模型位置
mmd.mesh.position.set(0,-10,0)
this.scene.add(mmd.mesh)
}
)
},
// 动画
animation(){
requestAnimationFrame(this.animation)
this.controls.update()
this.MMDhelper.update(clock.getDelta()) // mmd动画
this.renderer.render(this.scene,this.camera)
},
loaderObj(){
this.MMDhelper = new MMDAnimationHelper()
const loader = new MMDLoader()
loader.loadWithAnimation(
'models/mmd/miku/miku_v2.pmd', // 模型文件
'models/mmd/vmds/wavefile_v2.vmd', // 动作文件
mmd => {
this.MMDhelper.add( mmd.mesh,{
animation: mmd.animation,
physics:true
} )
// 加载音频
const audioLoader = new THREE.AudioLoader()
audioLoader.load('models/mmd/audios/wavefile_short.mp3',music=>{
// 音频管理器
const listenner = new THREE.AudioListener()
listenner.position.z = 1
// 音乐对象
const audio = new THREE.Audio(listenner).setBuffer(music)
this.MMDhelper.add(audio) // 音频节奏
// this.MMDhelper.add(audio,{delayTime:160/30}) // 音频节奏,演示
this.scene.add(audio) // 音频添加场景
this.scene.add(listenner)
this.ready = true
})
// 改变模型位置
mmd.mesh.position.set(0,-10,0)
this.scene.add(mmd.mesh)
}
)
},
loaderObj(){
this.MMDhelper = new MMDAnimationHelper()
const loader = new MMDLoader()
loader.loadWithAnimation(
'models/mmd/miku/miku_v2.pmd', // 模型文件
'models/mmd/vmds/wavefile_v2.vmd', // 动作文件
mmd => {
this.MMDhelper.add( mmd.mesh,{
animation: mmd.animation,
physics:true
} )
// 加载音频
//...
// 相机位置
loader.loadAnimation(
'models/mmd/vmds/wavefile_camera.vmd',
this.camera,
cameraAnimation => {
this.MMDhelper.add(this.camera,{
animation:cameraAnimation
})
}
)
// 改变模型位置
mmd.mesh.position.set(0,-10,0)
this.scene.add(mmd.mesh)
}
)
},
import {
Reflector } from 'three/examples/jsm/objects/Reflector'
export default {
mounted(){
this.scene = this.getScene()
this.bgTexture() // 导入背景
this.ball = this.addBall()
this.mirror = this.getMirror()
this.scene.add(this.ball,this.mirror)
//...
},
methods:{
// 镜面
getMirror(){
const geogemetry = new THREE.PlaneGeometry(10,10)
const mirror = new Reflector(geogemetry)
// 调整镜子的位置
mirror.position.set(0,0,-5)
mirror.lookAt(-1,0,0)
return mirror
},
}
}
mounted(){
this.ball = this.addBall()
this.boxList = []
this.mirror = this.addMirror(new THREE.Vector3(0,0,-50))
this.boxList.push(this.addPlane(new THREE.Vector3(0,0,-50)))
this.boxList.push(this.addPlane(new THREE.Vector3(0,0,50)))
this.boxList.push(this.addPlane(new THREE.Vector3(50,0,0)))
this.boxList.push(this.addPlane(new THREE.Vector3(-50,0,0)))
this.boxList.push(this.addPlane(new THREE.Vector3(0,50,0)))
this.boxList.push(this.addPlane(new THREE.Vector3(0,-50,0)))
this.scene.add(
this.ball,
this.mirror,
...this.boxList,
)
},
methods:{
// 镜面
addMirror(position,direction = new THREE.Vector3(0,0,0)){
const geometry = new THREE.PlaneGeometry(100,100)
const mirror = new Reflector(geometry,{
// color:new THREE.Color(0x7F7F7F),
textureWidth:this.width,
textureHeight:this.height,
// clipBias:1000
})
mirror.position.set(position.x,position.y,position.z)
mirror.lookAt(direction.x,direction.y,direction.z)
return mirror
},
addPlane(position,direction = new THREE.Vector3(0,0,0)){
const geometry = new THREE.PlaneGeometry(100,100)
const material = new THREE.MeshPhongMaterial({
map:new THREE.TextureLoader().load("models/brick_diffuse.jpg")
})
const mesh = new THREE.Mesh(geometry,material)
mesh.position.set(position.x,position.y,position.z)
mesh.lookAt(direction.x,direction.y,direction.z)
mesh.receiveShadow = true // 地板接收阴影
return mesh
},
// 添加小球
addBall(){
const geometry = new THREE.SphereGeometry(2,32,32)
const material = new THREE.MeshPhongMaterial({
envMap:this.scene.background
})
const mesh = new THREE.Mesh(geometry,material)
mesh.castShadow = true // 开启阴影
return mesh
},
}
import {
OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
mounted(){
this.controls = new OrbitControls(this.camera) // 控制器
this.controls.enabled = false // 控制控制器开关
}
import {
TrackballControls } from 'three/examples/jsm/controls/TrackballControls'
mounted(){
this.controls = new TrackballControls(this.camera) // 控制器
this.controls.noPan = false // 是否允许平移操作
this.controls.noRotate = false // 是否允许旋转
this.controls.noZoom = false // 是否允许缩放
}
通过键盘(A、D、W、D)控制
import {
FlyControls } from 'three/examples/jsm/controls/FlyControls'
const clock = new THREE.Clock()
mounted(){
this.controls = new FlyControls(this.camera) // 控制器
this.controls.movementSpeed = 10 // 前进速度
this.controls.rollSpeed = 0.5 // 旋转速度
this.controls.autoForward = true// 自动旋转
}
methods:{
// 动画
animation(){
requestAnimationFrame(this.animation)
this.controls.update(clock.getDelta())
this.renderer.render(this.scene,this.camera)
},
}
import {
FirstPersonControls } from 'three/examples/jsm/controls/FirstPersonControls'
const clock = new THREE.Clock()
mounted(){
this.controls = new FirstPersonControls(this.camera) // 控制器
this.controls.movementSpeed = 30 // 前进速度
this.controls.lookSpeed = 0.1 // 旋转速度
}
methods:{
// 动画
animation(){
requestAnimationFrame(this.animation)
this.controls.update(clock.getDelta())
this.renderer.render(this.scene,this.camera)
},
}
取消鼠标图标,默认清空下加载页面不允许鼠标控制消失
import {
PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls's
mouted(){
this.controls = new PointerLockControls(this.camera) // 控制器
let canvas = this.$refs.canvas
// 点击canvas交出鼠标控制权
canvas.addEventListener('click',()=>{
this.controls.lock()
})
// 监听开始游戏
this.controls.addEventListener('lock',()=>{
console.log('游戏开始')
})
// 监听暂停游戏
this.controls.addEventListener('unlock',()=>{
console.log('游戏暂停')
})
}
methods:{
animation(){
requestAnimationFrame(this.animation)
this.renderer.render(this.scene,this.camera)
}
}
键盘方向控制
data () {
return {
direction:{
w:false,
s:false,
a:false,
d:false,
}
}
},
mounted(){
// 按下键盘
window.addEventListener('keydown',(e)=>{
let key = e.key
console.log(key)
if(this.direction[key] === false){
this.direction[key] = true
}
})
// 按下键盘
window.addEventListener('keyup',(e)=>{
let key = e.key
if(this.direction[key] === true){
this.direction[key] = false
}
})
}
methods:{
// 动画
animation(){
requestAnimationFrame(this.animation)
const speed = 1
// 修改镜头
if(this.direction.w){
this.camera.translateZ(-speed)
}else if(this.direction.s){
this.camera.translateZ(speed)
}else if(this.direction.a){
this.camera.translateX(-speed)
}else if(this.direction.d){
this.camera.translateX(speed)
}
this.renderer.render(this.scene,this.camera)
},
}
// 创建一个从(10,10,10)指向(0,0,0)的方向向量
const raycaster = new THREE.Raycaster(
new THREE.Vector3(10,10,10),
new THREE.Vector3(0,0,0),
)
// 单个物体检查,返回贯穿物体的详细信息
let arr1 = new THREE.intersectObject(
new THREE.Object(),
)
// 一次性贯穿多个物体,返回从近到远的物体的数组
let arr2 = new THREE.intersectObjects(array) // 平时用这个
// 与相机位置以及朝向关联,做到人机交互
const raycaster2 = new THREE.Raycaster()
raycaster2.setFromCamera(
mouse, // 位置
camera // 相机
)
// 鼠标位置归一,化为设备坐标
const mouse = {
x:0,y:0}
window.addEventListener('click', e => {
mouse.x = e.client / window.innerWidth * 2 - 1
mouse.y = - e.clientY / window.innerHeight * 2 + 1
})
import {
OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export default {
created () {
// 获取屏幕宽高
this.width = window.innerWidth-170 // 减去左侧导航栏
this.height = window.innerHeight
},
mounted(){
this.addBoxList() // 所有盒子
this.controls = new OrbitControls(this.camera) // 控制器
this.addCameraCenter() // 控制中心
},
methods(){
// 添加相机中心点
addCameraCenter () {
this.raycaster = new THREE.Raycaster()
this.mouse = {
x:0, y:0 }
// 点击获取交互点
window.addEventListener('click', e => {
// 减去左侧导航栏170
this.mouse.x = (e.clientX - 170) / (window.innerWidth - 170) * 2 - 1
this.mouse.y = - e.clientY / window.innerHeight * 2 + 1
// 与相机朝向关联
this.raycaster.setFromCamera(
this.mouse,
this.camera
)
// 获取直接所有数组的方法
let arr = this.raycaster.intersectObjects(this.scene.children)
if (arr.length > 0) {
// 选中变色
arr[0].object.material.color = new THREE.Color(0xff0000)
console.log( arr[0].object.material.color) // 获取到点击的事件
}
})
},
}
}
// 批量添加盒子---地板
addBoxList () {
let addBOx = []
for (let i = 0; i < 20; i++) {
for (let j = 0; j < 20; j++) {
let cube = this.getCube()
cube.position.set(
i * 10,
-5, // 减去盒子自身5高度,即站着高度
j * 10,
)
addBOx.push(cube)
}
}
this.scene.add(...addBOx)
},
// 获取盒子
getCube (w = 10, h = 10, d = 10) {
let geometry = new THREE.BoxGeometry(w, h, d)
let meterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(0xffffff * Math.random())
})
const mesh = new THREE.Mesh(geometry, meterial)
return mesh
},
// 获取相机
getCamera () {
const camera = new THREE.PerspectiveCamera(
75, this.width / this.height, 1, 1000
)
// w ,d 世界中心
// 20是高度,站在方盒上
camera.position.set(100, 20, 100)
camera.lookAt(0, 0, 0)
return camera
},
export default {
data () {
direction: {
w: false,
s: false,
a: false,
d: false,
" ": false, // 空格状态
},
fallOffon:false, // 坠落开关
// 坠落速度
fallV:{
y:20, // 初始y轴位置
vy:0, // y轴上的速度
g:-0.08 // 自由落体加速度
},
},
methods:{
// 坠落事件
falling(){
if(this.fallOffon){
this.fallV.vy += this.fallV.g // 更新y轴上的速度
this.fallV.y += this.fallV.vy // 根据速度变化改变当前位置
this.camera.position.y = this.fallV.y // 修改位置
}else{
this.fallV.vy = 0
}
},
// 动画
animation () {
this.animate = requestAnimationFrame(this.animation)
const speed = 1
// 修改镜头
if (this.direction.w) {
this.camera.translateZ(-speed)
} else if (this.direction.s) {
this.camera.translateZ(speed)
} else if (this.direction.a) {
this.camera.translateX(-speed)
} else if (this.direction.d) {
this.camera.translateX(speed)
}else if(this.direction[" "]){
// 跳跃-修改速度位置
this.fallV.vy = 2
this.fallOffon = true // 坠落开关
}
// 坠落事件
this.falling()
this.renderer.render(this.scene, this.camera)
},
}
}
// 检测物体阻止掉落
checkBox(){
let raycaster = new THREE.Raycaster(
this.camera.position,
new THREE.Vector3(0,-1,0)
)
let arr = raycaster.intersectObjects(this.scene.children)
// console.log(arr)
// 穿模处理--防止掉落
if(
arr.length>0
// distance 与地面的距离
// point 盒子点的位置
&& arr[0].distance < 15
){
console.log(arr[0].distance)
console.log(arr[0].point)
this.fallOffon = false // 关闭坠落
this.camera.position.y = arr[0].point.y + 3 // 站在该盒子的y + 3 位置
}
},
// 动画
animation () {
this.animate = requestAnimationFrame(this.animation)
// ....
// 坠落事件
this.falling()
// 检测地板盒子
this.checkBox()
this.renderer.render(this.scene, this.camera)
},
// 控制器控制中心
controlCenter () {
this.controls = new PointerLockControls(this.camera) // 控制器
let canvas = this.$refs.canvas
// 点击canvas交出鼠标控制权
canvas.addEventListener('click', () => {
this.controls.lock()
})
// 监听开始游戏
this.controls.addEventListener('lock', () => {
console.log('游戏开始')
// 开始空中坠落
this.fallOffon = true
// 动画关闭则启动动画
if(!this.animate){
this.animation()
}
})
// 监听暂停游戏
this.controls.addEventListener('unlock', () => {
console.log('游戏暂停')
// 关闭坠落
this.fallOffon = false
// 清除动画
cancelAnimationFrame(this.animate)
this.animate = null
})
},
// 获取当前相机朝向与位置
getDirection () {
const position = this.camera.position
// 通过raycaster访问相机朝向
const raycaster = new THREE.Raycaster()
const coords = {
x: 0, y: 0 }
raycaster.setFromCamera(coords, this.camera)
const direction = raycaster.ray.direction
return {
direction,
position,
}
}
// 创建发射小球
createBall ({
position, direction }) {
const geometry = new THREE.SphereGeometry(.3, 10, 10)
const material = new THREE.MeshLambertMaterial({
color: new THREE.Color(0x222222)
})
const mesh = new THREE.Mesh(geometry, material)
let new_position = Object.assign({
}, position)
// 设置初始小球值
mesh.position.set(position.x, position.y, position.z)
this.scene.add(mesh)
return {
mesh,
position:new_position,
direction,
dv: 2, // 速度
// 更新小球运动轨迹
update: function () {
// 维护position 下一轮的position
new_position.x += direction.x * this.dv
new_position.y += direction.y * this.dv
new_position.z += direction.z * this.dv
// 更新速度
this.mesh.position.set(
new_position.x,
new_position.y,
new_position.z,
)
},
// 删除小球
kill:()=>{
this.scene.remove(mesh)
}
}
}
点击鼠标创建小球,push到数组
import * as THREE from 'three'
class FireMode {
constructor (camera, scene) {
this.camera = camera
this.scene = scene
// 存储子弹小球
this.ballList = []
this.register()
}
// 注册射击事件
register () {
// 点击鼠标射击
window.addEventListener('mousedown', e => {
// 获取相机朝向与位置
let cameraInfo = this.getDirection()
let getBall = this.createBall(cameraInfo)
// 添加小球
this.ballList.push(getBall)
})
}
}
// 更新数据
update () {
// 到达一定距离后删除
this.ballList.forEach((ball,index)=>{
// 更新速度与朝向
ball.update()
// 一定距离后删除
if(!this.checkDistance(ball,this.camera,200)){
ball.kill()
this.ballList.splice(index,1)
}
})
}
// 判断量两者距离
checkDistance(obj1,obj2,distance){
let dx = (obj1.position.x - obj2.position.x)
let dy = (obj1.position.y - obj2.position.y)
let dz = (obj1.position.z - obj2.position.z)
return dx*dx + dy*dy + dz*dz < distance * distance
}
mounted(){
//...
// 发射事件
this.fireSystem = new FireMode(this.camera,this.scene)
//...
},
methods:{
animation(){
this.fireSystem.update()
}
}