threejs基本功2

141 阅读6分钟

geometry(ies) 几何体:

 

 

  •  

    BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)

    width — Width; that is, the length of the edges parallel to the X axis. Optional; defaults to 1.
    height — Height; that is, the length of the edges parallel to the Y axis. Optional; defaults to 1.
    depth — Depth; that is, the length of the edges parallel to the Z axis. Optional; defaults to 1.
    widthSegments — Number of segmented rectangular faces along the width of the sides. Optional; defaults to 1.
    heightSegments — Number of segmented rectangular faces along the height of the sides. Optional; defaults to 1.
    depthSegments — Number of segmented rectangular faces along the depth of the sides. Optional; defaults to 1.

const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe:true })
//  参数 wireframe :线框模式,可以看到geometry后面三个参数设置不同的区别

 // BoxGeometry 后面三个参数设置为1, 每一面就划分为2个三角形 triangle, 设置为2 就得到8个

 

 

 

 

 

创建自定义集合体

 

 

 

 

 

 这种方式在r125版本后废弃了, 可以使用 BufferGeometry 实现相同功能

blog.csdn.net/qq_37987033…

TIPS: buffer  geometry  是性能最佳, 对GPU友好的一种 创建几 何体的方式。

复制代码

  1 import * as THREE from 'three'
  2 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'    
  3 import gsap from 'gsap'
  4 import * as dat from 'dat.gui'
  5 // Canvas
  6 const canvas = document.querySelector('canvas.webgl')
  7 
  8 // Scene
  9 const scene = new THREE.Scene()
 10 
 11 
 12 // Object
 13 const geometry = new THREE.BoxBufferGeometry(1, 1, 1, 2,2,2)
 14 // buffer  geometry  集合体是性能最佳,最gpu最友好的 geometry。  
 15 // BoxGeometry 后面三个参数设置为1, 每一面就划分为2个三角形 triangle, 2 就得到八个
 16 
 17 // 自定义一个geometry
 18 // const geometry = new THREE.BufferGeometry();
 19 // const vertices = new Float32Array([
 20 //     0,0,0,
 21 //     0,1,0,
 22 //     1,0,0
 23 // ])
 24 // geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
 25 const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe:false })
 26 const mesh = new THREE.Mesh(geometry, material)
 27 scene.add(mesh)
 28 
 29 // define a color object
 30 const obj = {
 31  color:0xff0000,
 32  spin:() => {
 33     gsap.to(mesh.rotation, {duration:1, y:mesh.rotation.y + 5})
 34  }
 35 } 
 36 
 37 /* debug gui panel*/
 38 const gui = new dat.GUI({
 39     closed: true, 
 40     // 表示一开始 panel 先处于关闭状态
 41    // width:400  设置panel width
 42 })
 43 //gui.hide() // 隐藏gui  panel
 44   
 45 gui.add(mesh.position, 'y', -3, 3, 0.01).name('positon-y') // 使用name() 重命名panel item title 
 46 gui.add(mesh.position, 'x').min(-3).max(3).step(0.01)// 用链式调用的方式也可以! 
 47 //设置控制mesh y坐标的panel Item,并且规定控制的最大值3,最小值-1, 移动步长为0.01
 48 
 49 //控制 mesh 的属性 ,比如 visible:mesh 是否可见
 50 gui.add(mesh, 'visible')
 51 gui.add(material,'wireframe')
 // dat.gui 改变obj的颜色时,将材质的颜色一并替换
 52 gui.addColor(obj, 'color').onChange(() => {
 53     material.color.set(obj.color)
 54 })
 55 gui.add(obj, 'spin')
 56 // animation
 57 const animations = () => {
 58     gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })
 59     gsap.to(mesh.position, { duration: 1, delay: 2, x: 0 })
 60 }
 61 animations()
 62 // Sizes
 63 const sizes = {
 64     width: window.innerWidth,
 65     height: window.innerHeight
 66 }
 67 
 68 // Camera
 69 const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
 70 camera.position.z = 3
 71 scene.add(camera)
 72 
 73 // controls
 74 
 75 const controls = new OrbitControls(camera, canvas);
 76 controls.enableDamping = true
 77 //开启阻尼,也就是惯性, 然后需要在每一帧进行controls.update()
 78 //
 79 // controls.target.y = 2 //controls target 指向的是对象围绕的焦点
 80 // controls.update()
 81 
 82 // Renderer
 83 const renderer = new THREE.WebGLRenderer({
 84     canvas: canvas
 85 })
 86 const tick = () => {
 87     controls.update()
 88     renderer.render(scene, camera)
 89     window.requestAnimationFrame(tick)
 90     // 动画一定需要配合帧动画请求!!!
 91 }
 92 tick()
 93 let cursor = {  
 94     x:0,
 95     y:0
 96 }
 97 // 绑定鼠标事件
 98 window.addEventListener('mousemove', (event) => {
 99     // 这样子会使得x,y 坐标值会归于(-0.5-0.5 这个区间),
100     cursor.x = event.clientX / sizes.width - 0.5
101     cursor.y = -(event.clientY / sizes.height - 0.5)
102     // 不加符号,会使得鼠标上下移动的时候,mesh看起来是跟随鼠标方向的,但是其实移动相机,mesh最终效果是要跟鼠标反向运动(相机向右,以相机为参照物,其实mesh向左)。
103     //camera.position.x = cursor.x
104     //camera.position.y = cursor.y
105 
106     // 让相机围绕着mesh 旋转(首先要清楚 这里实现旋转,改变位置的话,是 x和z方向旋转)
107     // camera.position.x = Math.sin(cursor.x * Math.PI * 2) * 3
108     // camera.position.z = Math.cos(cursor.x * Math.PI * 2) * 3
109     // camera.position.y = cursor.y * 6
110     // camera.lookAt(mesh.position)
111 })
112 
113 //window resize handle
114 window.addEventListener('resize', () => {
115     sizes.width = window.innerWidth
116     sizes.height = window.innerHeight
117 
118     camera.aspect = sizes.width / sizes.height
119     camera.updateProjectionMatrix()
120     //修改相机参数后,需要统计threejs 去更改 projectMatrix, 项目矩阵?
121 
122     //renderer 修改画布的size
123     renderer.setSize(sizes.width, sizes.height)
124     renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
125 })
126 
127 // 绑定全屏事件
128 window.addEventListener('dblclick', () => {
129     // 注意,这个特性,对于 safari 浏览器无效
130     // if(!document.fullscreenElement) {
131     //     canvas.requestFullscreen()
132     // }else {
133     //     document.exitFullscreen()
134     // }
135 
136     //最终兼容写法
137     const fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement
138     if(!fullscreenElement) {
139         if(canvas.requestFullscreen) {
140             canvas.requestFullscreen()
141         }else if(canvas.webkitRequestFullscreen) {
142             canvas.webkitRequestFullscreen()
143         }
144     }else {
145         if(document.exitFullscreen) {
146             document.exitFullscreen()
147         }else if(document.webkitExitFullscreen) {
148             document.webkitExitFullscreen()
149         }
150     }
151 })
152 
153 renderer.setSize(sizes.width, sizes.height)
154 // 规定渲染器,设定像素比 为1 or 2  有益于性能!!!
155 renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))


# [threejs-材质(textures)](https://www.cnblogs.com/Hijacku/p/17273355.html "发布于 2023-03-30 16:38")

# what is textures?

![](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/bb478ff2df1a49f5bbed8a1b058a50f4~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5bCP5paH5YiANjk2:q75.awebp?rk3s=f64ab15b&x-expires=1772086220&x-signature=fMPgcUrCBkTC5BQtJpQs3nGyqJs%3D)

 

 UV:

![](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f6b855ca0d9d4dc381ef0fc8bccf7ca7~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5bCP5paH5YiANjk2:q75.awebp?rk3s=f64ab15b&x-expires=1772086220&x-signature=PI9i1nNrhGtxQjv8H5hdsZsmq1A%3D)

 

 UV coordinate 决定了 材质在mesh上贴 、铺的方式和位置

[![复制代码](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/2b623b4f5e784f12ba9b826d40783f64~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5bCP5paH5YiANjk2:q75.awebp?rk3s=f64ab15b&x-expires=1772086220&x-signature=ttgmuAboYkMqy67uNwB6fsdFpXA%3D)](<> "复制代码")

// add texture

// //第一种方式 // const image = new Image(); // const texture = new THREE.Texture(image)

// image.onload = () => { // texture.needsUpdate = true // // 通知texture 更新 // } // image.src = './static/textures/door/color.jpg'

let texture = new THREE.TextureLoader(LoadingManager) // 第二种方式 //可以在TextureLoader中传入loadingmanager const colorTexture2 = texture.load('/static/textures/door/color.jpg', // () => { // console.log('load') // }, // () => { // console.log('load progress') // }, // () => { // console.log('load error') // } // 可以有三个回调函数 ) const alphaTexture = texture.load('/static/textures/door/alpha.jpg') const heightTexture = texture.load('/static/textures/door/height.jpg') const normalTexture = texture.load('/static/textures/door/normal.jpg') const ambientOcclusionTexture = texture.load('/static/textures/door/ambientOcclusion.jpg') const metalnessTexture = texture.load('/stati c/textures/door/metalness.jpg') const roughnessTexture = texture.load('/static/textures/door/roughness.jpg')

/* edit texture (transforming texture) 例如css中的背景重复(repeat) */ // colorTexture2.repeat.x = 2 // colorTexture2.repeat.y = 3 //wrapS x 方向是重复, wrapT y方向上重复(RepeatWrapping) MirroredRepeatWrapping(镜像反转后重复!!) // colorTexture2.wrapS = THREE.MirroredRepeatWrapping // colorTexture2.wrapT = THREE.RepeatWrapping // //texture 偏移 // colorTexture2.offset.x = 0.5 // colorTexture2.offset.y = 0.5 // texture rotation colorTexture2.rotation = Math.PI * 0.25 colorTexture2.center.x = 0.5 colorTexture2.center.y = 0.5

// filtering and mip mapping // colorTexture2.minFilter = THREE.NearestFilter // 如果设置了colorTexture2.minFilter = THREE.NearestFilter // ,可以设置 colorTexture2.generateMipmaps = false 获得好的性能 // 如果一张分辨率很小的贴图,会出现模糊 blurry。 设置magFilter = THREE.NearestFilter, 就会清晰很多 colorTexture2.magFilter = THREE.NearestFilter

// texture format and optimisation(最优化) // 贴图 texture 图片的体积越小越有利于gpu处理 // 贴图尺寸 widthheight 最好是 2的指数倍。 比如 128 256 512 1024 2048... 也就是 512512 512*2048这种尺寸的更利于gpu处理!

/* texture support transparency but we can't have transparency in .jpg,if we want to have only one texture that combine color and alpha, we better use .png file lf we are using a normal texture we want to have the exact values which is why we shouldn't apply lossy compression and we better use .png for those

*/

threejs-css2dObject操作之物体遮挡标签后应该隐藏,而不是出现透视效果

先看coding之前的效果:

 这些在背面的标签的,转到一定角度,被模型遮挡后,理论上就不应该被看到。这才是比较符合实际的

coding之后(另一侧对称点就被隐藏):

 具体代码(j借助于光线投影):

复制代码

// 绑定鼠标事件,当用户移动视角后触发()
function bindRayShotEvent() {
    document.addEventListener('mouseup', () => {
        if (clusterOf2D.value.length > 0) {
            clusterOf2D.value.forEach((c: any) => {
                raycasterCollsionDetect(c)
            })
        }
    })
}

复制代码

复制代码

function raycasterCollsionDetect(wavesLabel: CSS2DObject) {
    const labelPosition = wavesLabel.position.clone()
    //计算出标签和相机之前的距离(需要看备份一下坐标,不然执行下面转换NDC坐标后,计算会不准)
    const labelDistance = labelPosition.distanceTo(camera.value.position)
    //转换 向量到从世界空间投影到相机的标准化坐标(NDC)
    labelPosition.project(camera.value)
    raycaster.setFromCamera(labelPosition, camera.value)
    let model = scene!.children.filter(v => {
        return v instanceof THREE.Group
    })
    //过滤出模型
    if (model.length == 1) {
        const intersects = raycaster.intersectObjects(model[0].children)
        if (intersects.length == 0) {
            // 如果标签没有被model遮挡,应该全部显示
            wavesLabel.element.classList.add('visible')
        } else {
            const minDistance = intersects[0].distance
            if (minDistance < labelDistance) {
                // 如果模型到相机的距离小于标签到相机的距离,说明光线射线先经过模型
                // 表面此时标签其实在模型的后面,应该被遮挡而看不见
                wavesLabel.element.classList.remove('visible')
                wavesLabel.element.classList.add('hidden')
            } else {
                wavesLabel.element.classList.add('visible')
                wavesLabel.element.classList.remove('hidden')
            }
        }
    }

}