【Three.js基础学习】18.Raycaster and MouseEvents

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

课程回顾:

    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()

射线模型相交


总结

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值