提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
课程回顾:
1.光纤投射器 : 向一个方向投射光线并测试哪些物体与该光线相交测试
2.创建Raycaster,要先创建raycaster类
normalize标准化
3.光线投射器 应该设置原点 终点
4. 相交光线
1.intersectObject() // 参数:一个对象
2.intersectObjects() // 参数: 数组
功能测试:光线与对象之间相交 ,和多个对象之间相交
5. 测试光射线,或鼠标发射射线;展示
6. 实现鼠标的进入,离开,点击; 确定鼠标点击哪个球
7.补充:Raycaster width Model
想要实现 鼠标移入鸭子恢复,离开会自动变大
模型相交
解决三个问题,
如何拿到模型
模型加载时间
一、代码
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import { Raycaster, Vector2, Vector3 } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
/**
* Base
*/
// Debug
const gui = new dat.GUI()
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Objects
*/
const object1 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object1.position.x = - 2
const object2 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
const object3 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object3.position.x = 2
scene.add(object1, object2, object3)
/*
Raycaster
*/
const raycaster = new Raycaster()
/* 测试
const rayOrigin = new THREE.Vector3(-3,0,0) // 这里回忆一下Vector 规则 三个数为一个
const rayDirection = new THREE.Vector3(10,0,0) // 但是这里是10 ,怎么精准获得 就需要标准化
console.log(rayDirection.length)
rayDirection.normalize() // 将vector向量 转换成单位向量 长度为1
console.log(rayDirection.length) // 长度
raycaster.set(rayOrigin,rayDirection)
const intersecet = raycaster.intersectObject(object2)
const intersecets = raycaster.intersectObjects([object1,object2,object3])
console.log(intersecet)
console.log(intersecets)
*/
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6)
directionalLight.castShadow = true
directionalLight.shadow.mapSize.set(1024, 1024)
directionalLight.shadow.camera.far = 15
directionalLight.shadow.camera.left = - 7
directionalLight.shadow.camera.top = 7
directionalLight.shadow.camera.right = 7
directionalLight.shadow.camera.bottom = - 7
directionalLight.position.set(5, 5, 5)
scene.add(directionalLight)
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/*
Mouse //鼠标
*/
const mouse = new Vector2()
window.addEventListener('mousemove',(event)=>{
mouse.x = event.clientX / sizes.width * 2 -1
mouse.y = -(event.clientY / sizes.height) * 2 + 1
// console.log(mouse.y)
})
window.addEventListener('click',(event)=>{
switch(currentIntersect.object){
case object1:
console.log('click on object1')
break
case object2:
console.log('click on object2')
break
case object3:
console.log('click on object3')
break
}
})
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
/*
models
*/
const gltfLoader = new GLTFLoader()
let model = null // 赋值模型
gltfLoader.load(
'/models/Duck/glTF/Duck.gltf', // 2.
(gltf)=>{
model = gltf.scene
model.position.y = -1.2
scene.add(model)
}
)
/**
* Animate
*/
const clock = new THREE.Clock()
let currentIntersect = null
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
// Cast a ray
raycaster.setFromCamera(mouse,camera) // 设置光线发射器 从鼠标发出
// const rayOrigin = new Vector3(-3,0,0)
// const rayDirection = new Vector3(1,0,0)
// rayDirection.normalize()
// raycaster.set(rayOrigin,rayDirection)
const objectTest = [object1,object2,object3]
const intersecets = raycaster.intersectObjects(objectTest) // 相交对象
for(const object of objectTest){
object.material.color.set('#ff0000')
}
for(let intersecet of intersecets){
intersecet.object.material.color.set('#0000ff')
}
if(intersecets.length){
if(currentIntersect === null){
console.log('mouse enter')
}
currentIntersect = intersecets[0]
}else{
if(currentIntersect){
console.log('mouse leave')
}
currentIntersect = null
}
// 与模型相交
if(model){
const modelIntersecet = raycaster.intersectObject(model)
if(modelIntersecet.length){
model.scale.set(1.2,1.2,1.2)
}else{
model.scale.set(1,1,1)
}
}
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
二、知识点
1.关于射线
const raycaster = new Raycaster()
normalize标准化
1.intersectObject() // 参数:一个对象
2.intersectObjects() // 参数: 数组
下面代码可以看到intersectObject和intersectObjects 相交对象的用法区别,一个是一个;一个是多个
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import { Raycaster, Vector2, Vector3 } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
/**
* Base
*/
// Debug
const gui = new dat.GUI()
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Objects
*/
const object1 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object1.position.x = - 2
const object2 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
const object3 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object3.position.x = 2
scene.add(object1, object2, object3)
/*
Raycaster
*/
const raycaster = new Raycaster()
// 测试
const rayOrigin = new THREE.Vector3(-3,0,0) // 这里回忆一下Vector 规则 三个数为一个
const rayDirection = new THREE.Vector3(10,0,0) // 但是这里是10 ,怎么精准获得 就需要标准化
console.log(rayDirection.length)
rayDirection.normalize() // 将vector向量 转换成单位向量 长度为1
console.log(rayDirection.length) // 长度
raycaster.set(rayOrigin,rayDirection)
const intersecet = raycaster.intersectObject(object2)
const intersecets = raycaster.intersectObjects([object1,object2,object3])
console.log(intersecet)
console.log(intersecets)
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
/**
* Animate
*/
const clock = new THREE.Clock()
let currentIntersect = null
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
2.如何实现上下移动,射线相交,更改球的颜色
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import { Raycaster, Vector2, Vector3 } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
/**
* Base
*/
// Debug
const gui = new dat.GUI()
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Objects
*/
const object1 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object1.position.x = - 2
const object2 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
const object3 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object3.position.x = 2
scene.add(object1, object2, object3)
/*
Raycaster
*/
const raycaster = new Raycaster()
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
/**
* Animate
*/
const clock = new THREE.Clock()
let currentIntersect = null
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
// Cast a ray
// raycaster.setFromCamera(mouse,camera) // 设置光线发射器 从鼠标发出
const rayOrigin = new Vector3(-3,0,0)
const rayDirection = new Vector3(1,0,0)
rayDirection.normalize()
raycaster.set(rayOrigin,rayDirection)
const objectTest = [object1,object2,object3]
const intersecets = raycaster.intersectObjects(objectTest) // 相交对象
for(const object of objectTest){
object.material.color.set('#ff0000')
}
for(let intersecet of intersecets){
intersecet.object.material.color.set('#0000ff')
}
if(intersecets.length){
if(currentIntersect === null){
console.log('mouse enter')
}
currentIntersect = intersecets[0]
}else{
if(currentIntersect){
console.log('mouse leave')
}
currentIntersect = null
}
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
射线相交,更改颜色
2.实现鼠标射线相交
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import { Raycaster, Vector2, Vector3 } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
/**
* Base
*/
// Debug
const gui = new dat.GUI()
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Objects
*/
const object1 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object1.position.x = - 2
const object2 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
const object3 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object3.position.x = 2
scene.add(object1, object2, object3)
/*
Raycaster
*/
const raycaster = new Raycaster()
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/*
Mouse //鼠标
*/
const mouse = new Vector2()
window.addEventListener('mousemove',(event)=>{
mouse.x = event.clientX / sizes.width * 2 -1
mouse.y = -(event.clientY / sizes.height) * 2 + 1
// console.log(mouse.y)
})
window.addEventListener('click',(event)=>{
switch(currentIntersect.object){
case object1:
console.log('click on object1')
break
case object2:
console.log('click on object2')
break
case object3:
console.log('click on object3')
break
}
})
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
/**
* Animate
*/
const clock = new THREE.Clock()
let currentIntersect = null
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
// Cast a ray
raycaster.setFromCamera(mouse,camera) // 设置光线发射器 从鼠标发出
const objectTest = [object1,object2,object3]
const intersecets = raycaster.intersectObjects(objectTest) // 相交对象
for(const object of objectTest){
object.material.color.set('#ff0000')
}
for(let intersecet of intersecets){
intersecet.object.material.color.set('#0000ff')
}
if(intersecets.length){
if(currentIntersect === null){
console.log('mouse enter')
}
currentIntersect = intersecets[0]
}else{
if(currentIntersect){
console.log('mouse leave')
}
currentIntersect = null
}
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
鼠标绑定射线相交
3.引入模型,模型相交
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import { Raycaster, Vector2, Vector3 } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
/**
* Base
*/
// Debug
const gui = new dat.GUI()
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Objects
*/
const object1 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object1.position.x = - 2
const object2 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
const object3 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object3.position.x = 2
scene.add(object1, object2, object3)
/*
Raycaster
*/
const raycaster = new Raycaster()
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6)
directionalLight.castShadow = true
directionalLight.shadow.mapSize.set(1024, 1024)
directionalLight.shadow.camera.far = 15
directionalLight.shadow.camera.left = - 7
directionalLight.shadow.camera.top = 7
directionalLight.shadow.camera.right = 7
directionalLight.shadow.camera.bottom = - 7
directionalLight.position.set(5, 5, 5)
scene.add(directionalLight)
/**
* Sizes
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
/*
Mouse //鼠标
*/
const mouse = new Vector2()
window.addEventListener('mousemove',(event)=>{
mouse.x = event.clientX / sizes.width * 2 -1
mouse.y = -(event.clientY / sizes.height) * 2 + 1
// console.log(mouse.y)
})
let currentIntersect = null
window.addEventListener('click',(event)=>{
switch(currentIntersect.object){
case object1:
console.log('click on object1')
break
case object2:
console.log('click on object2')
break
case object3:
console.log('click on object3')
break
}
})
/**
* Camera
*/
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)
// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
/*
models
*/
const gltfLoader = new GLTFLoader()
let model = null // 赋值模型
gltfLoader.load(
'/models/Duck/glTF/Duck.gltf', // 2.
(gltf)=>{
model = gltf.scene
model.position.y = -1.2
scene.add(model)
}
)
/**
* Animate
*/
const clock = new THREE.Clock()
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
// Cast a ray
raycaster.setFromCamera(mouse,camera) // 设置光线发射器 从鼠标发出
const objectTest = [object1,object2,object3]
const intersecets = raycaster.intersectObjects(objectTest) // 相交对象
for(const object of objectTest){
object.material.color.set('#ff0000')
}
for(let intersecet of intersecets){
intersecet.object.material.color.set('#0000ff')
}
if(intersecets.length){
if(currentIntersect === null){
console.log('mouse enter')
}
currentIntersect = intersecets[0]
}else{
if(currentIntersect){
console.log('mouse leave')
}
currentIntersect = null
}
// 与模型相交
if(model){
const modelIntersecet = raycaster.intersectObject(model)
if(modelIntersecet.length){
model.scale.set(1.2,1.2,1.2)
}else{
model.scale.set(1,1,1)
}
}
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
射线模型相交
总结
无