简单模型及其使用
import * as THREE from 'three'
import gsap from 'gsap'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 画板
const canvas = document.querySelector('canvas.webgl')
// 尺寸
const sizes = {
width: 800,
height: 600
}
// 创建分组,方便进行统一比例调整
const group = new THREE.Group()
group.scale.y = 2
group.rotation.y = 0.2
scene.add(group)
// 创建场景
const scene = new THREE.Scene()
// 创建正方体几何
const cubeGeometry = new THREE.BoxGeometry(1,1,1)
// 创建几何材质
const cubeMaterial = new THREE.MeshBasicMaterial({
color:'#ff0000'
})
// 参考坐标轴
const axesHelper = new THREE.AxesHelper(2)
scene.add(axesHelper)
// 创建网格模型
const cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial)
// 使用分组管理模型
group.add(cubeMesh)
scene.add(group)
// 透视摄像机、还有正交相机等
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
camera.position.z = 3
scene.add(camera)
// 轨道控制允许相机围绕目标旋转
const controls = new OrbitControls(camera, canvas)
// 设置为 true 可启用阻尼(惯性),可用于为控件提供重量感。
controls.enableDamping = true
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ canvas: canvas })
// 设置画面宽度高度,threejs会将属性设置在canvas上
renderer.setSize(sizes.width, sizes.height)
// 设置动画、也可用tweenjs
gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })
const tick = () =>
{
// 轨道控制的缓动需要每帧调用
controls.update()
// 渲染画面
renderer.render(scene, camera)
// 每一帧重新调用
window.requestAnimationFrame(tick)
}
tick()
// 窗口重置保证画面比例
window.addEventListener('resize', () =>
{
// Update sizes
sizes.width = window.innerWidth
sizes.height = window.innerHeight
// Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
// 更新渲染器的宽高
renderer.setSize(sizes.width, sizes.height)
// 最高设置到2即可,即一个设备像素对应两个图形像素
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
// 双击进入和退出全屏
window.addEventListener('dblclick', () =>
{
const fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement
if(!fullscreenElement)
{
if(canvas.requestFullscreen)
{
canvas.requestFullscreen()
}
else if(canvas.webkitRequestFullscreen)
{
canvas.webkitRequestFullscreen()
}
}
else
{
if(document.exitFullscreen)
{
document.exitFullscreen()
}
else if(document.webkitExitFullscreen)
{
document.webkitExitFullscreen()
}
}
})
时钟
// 关于three的时钟
const clock = new THREE.Clock()
// 可以获得当前运行的时间戳
console.log(clock.getElapsedTime())
自定义图形
// 创建几何缓冲器
const geometry = new THREE.BufferGeometry()
const count = 50
const pointsArray = new Float32Array(count * 3 * 3)
for(let i = 0; i < count * 3 * 3; i++)
{
pointsArray[i] = (Math.random() - 0.5) * 4
}
// 将随机生成的点设置到几何体
const positionsAttribute = new THREE.BufferAttribute(pointsArray, 3)
geometry.setAttribute('position', positionsAttribute)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })
// 创建模型
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
debug ui
import * as dat from 'lil-gui'
const parameters = {
position: 0,
rotation: 0,
color: 0xff0000,
spin: () =>
{
gsap.to(mesh.rotation, 1, { y: mesh.rotation.y + Math.PI * 2 })
}
}
// gui会根据属性对应的值的类型给出合适的控件
gui.addColor(parameters, 'color').onChange(() => material.color.set(parameters.color)) // 颜色选择器
gui.add(parameters, 'spin') // 按钮
gui.add(mesh.position, 'y').min(- 3).max(3).step(0.01).name('elevation') // progress
gui.add(mesh, 'visible') // 复选框
gui.add(material, 'wireframe') // 复选框
资源管理器
// loader钩子🪝函数
const loadingManager = new THREE.LoadingManager()
loadingManager.onStart = () =>
{
console.log('loadingManager: loading started')
}
loadingManager.onLoad = () =>
{
console.log('loadingManager: loading finished')
}
loadingManager.onProgress = () =>
{
console.log('loadingManager: loading progressing')
}
loadingManager.onError = () =>
{
console.log('loadingManager: loading error')
}
const textureLoader = new THREE.TextureLoader(loadingManager)
const colorTexture = textureLoader.load(
'/textures/minecraft.png',
() =>
{
console.log('textureLoader: loading finished')
},
() =>
{
console.log('textureLoader: loading progressing')
},
() =>
{
console.log('textureLoader: loading error')
}
)
// 默认情况下纹理未设置为重复自身。要改变这一点,您必须使用 THREE.RepeatWrapping 常量更新wrapS和wrapT属性。
colorTexture.wrapS = THREE.MirroredRepeatWrapping // 定义了纹理贴图在水平方向上将如何包裹,在UV映射中对应于U
colorTexture.wrapT = THREE.MirroredRepeatWrapping // 这个值定义了纹理贴图在垂直方向上将如何包裹,在UV映射中对应于V。
colorTexture.repeat.x = 2 // 重复次数
colorTexture.repeat.y = 3
colorTexture.offset.x = 0.5 // 偏移比例
colorTexture.offset.y = 0.5
colorTexture.rotation = Math.PI * 0.25 // 贴图旋转角度
// 纹理贴图的坐标中心
colorTexture.center.x = 0.5
colorTexture.center.y = 0.5
// 是否使用mipmap功能
colorTexture.generateMipmaps = false
// 贴图像素太多,单个像素小于屏幕像素时用的过滤器
colorTexture.minFilter = THREE.NearestFilter
// 贴图像素太少,单个像素大于屏幕像素时用的过滤器
colorTexture.magFilter = THREE.NearestFilter // NearestFilter返回与指定纹理坐标最接近的纹理元素的值。不再进行插值
// 透明度纹理,白为显,黑为隐
const alphaTexture = textureLoader.load('/textures/door/alpha.jpg')
// 高度纹理是一个灰度图像,它将移动顶点以创建一些浮雕。如果你想看到它,你需要添加细分
const heightTexture = textureLoader.load('/textures/door/height.jpg')
// 法线贴图不会移动顶点,但会诱使光线认为脸部的方向不同。法线纹理对于添加具有良好性能的细节非常有用,因为您不需要细分几何体
const normalTexture = textureLoader.load('/textures/door/normal.jpg')
// 环境光遮蔽贴图,试图模拟被一些夹缝遮挡的阴影。虽然它在物理上并不准确,但它确实有助于画面的对比。
const ambientOcclusionTexture = textureLoader.load('/textures/door/ambientOcclusion.jpg')
// 金属贴图,是为了标出模型中哪个部分是金属
const metalnessTexture = textureLoader.load('/textures/door/metalness.jpg')
// 粗糙贴图是一个灰度图像,带有金属感,它将说明哪个部分粗糙(白色),哪个部分光滑(黑色)。这些信息有助于光的消散。影响高光的强度
const roughnessTexture = textureLoader.load('/textures/door/roughness.jpg')
灯光
// 环境光,该光全局均匀地照亮场景中的所有对象。
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
scene.add(ambientLight)
// 点光源
const light = new THREE.PointLight(0xffffff, 0.5) // 参数为光的颜色和强度
light.position.x = 2
light.position.y = 3
light.position.z = 4
scene.add(light)
材质
- MeshStandardMaterial 一种基于物理的标准材质,使用Metallic-Roughness工作流程
// 环境贴图,分为球形和六面体形,这里使用六面体形
// p代表正方向,n代表负方向
const environmentMapTexture = cubeTextureLoader.load([
'/textures/environmentMaps/0/px.jpg',
'/textures/environmentMaps/0/nx.jpg',
'/textures/environmentMaps/0/py.jpg',
'/textures/environmentMaps/0/ny.jpg',
'/textures/environmentMaps/0/pz.jpg',
'/textures/environmentMaps/0/nz.jpg'
])
// 材质类型分为很多种
const material = new THREE.MeshStandardMaterial()
material.map = colorTexture
material.aoMap = ambientOcclusionTexture
material.aoMapIntensity = 1
material.displacementMap = heightTexture
material.displacementScale = 0.05 // 位移贴图对网格的影响程度(黑色是无位移,白色是最大位移)
material.metalnessMap = metalnessTexture
material.roughnessMap = roughnessTexture
material.normalMap = normalTexture
material.normalScale.set(0.5, 0.5) // 法线贴图对材质的影响程度,范围 0-1
material.transparent = true
material.alphaMap = alphaTexture
// 设置材质的金属质感强度
material.metalness = 0.7
// 设置材质的粗糙感强度
material.roughness = 0.2
gui.add(material, 'metalness').min(0).max(1).step(0.0001)
gui.add(material, 'roughness').min(0).max(1).step(0.0001)
// 设置环境贴图
material.envMap = environmentMapTexture
- MeshBasicMaterial 一个以简单着色(平面或线框)方式来绘制几何体的材质,这种材质不受光照的影响。
const material = new THREE.MeshBasicMaterial()
material.map = doorColorTexture
material.color = new THREE.Color('#ff0000')
material.wireframe = true
material.transparent = true
material.opacity = 0.5
material.alphaMap = doorAlphaTexture
material.side = THREE.DoubleSide
material.flatShading = true
- MeshNormalMaterial 一种把法向量映射到RGB颜色的材质
const material = new THREE.MeshNormalMaterial()
material.flatShading = true // 着色器的采样方式,定义材质是否使用平面着色进行渲染
- MeshPhongMaterial 一种用于具有镜面高光的光泽表面的材质。该材质使用非物理的Blinn-Phong模型来计算反射率
const material = new THREE.MeshPhongMaterial()
material.shininess = 100
material.specular = new THREE.Color(0x1188ff)
- MeshDepthMaterial 一种按深度绘制几何体的材质。深度基于相机远近平面。白色最近,黑色最远。
const material = new THREE.MeshDepthMaterial()
- MeshToonMaterial 一种实现卡通着色的材质。
const gradientTexture = textureLoader.load('/textures/gradients/5.jpg')
const material = new THREE.MeshToonMaterial()
gradientTexture.generateMipmaps = false
gradientTexture.minFilter = THREE.NearestFilter
gradientTexture.magFilter = THREE.NearestFilter
material.gradientMap = gradientTexture
- MeshLambertMaterial 一种非光泽表面的材质,没有镜面高光。该材质使用基于非物理的Lambertian模型来计算反射率。 这可以很好地模拟一些表面(例如未经处理的木材或石材),但不能模拟具有镜面高光的光泽表面(例如涂漆木材)
- MeshPhysicalMaterial 是MeshStandardMaterial的扩展,提供了更高级的基于物理的渲染属性