Skip to content
Go back

Three.js 使用模型,材质,灯光(一)

Updated:  at  11:44 AM

简单模型及其使用

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)

材质

  1. 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
  1. 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
  1. MeshNormalMaterial 一种把法向量映射到RGB颜色的材质
const material = new THREE.MeshNormalMaterial()
material.flatShading = true // 着色器的采样方式,定义材质是否使用平面着色进行渲染
  1. MeshPhongMaterial 一种用于具有镜面高光的光泽表面的材质。该材质使用非物理的Blinn-Phong模型来计算反射率
const material = new THREE.MeshPhongMaterial()
material.shininess = 100
material.specular = new THREE.Color(0x1188ff)
  1. MeshDepthMaterial 一种按深度绘制几何体的材质。深度基于相机远近平面。白色最近,黑色最远。
const material = new THREE.MeshDepthMaterial()
  1. 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
  1. MeshLambertMaterial 一种非光泽表面的材质,没有镜面高光。该材质使用基于非物理的Lambertian模型来计算反射率。 这可以很好地模拟一些表面(例如未经处理的木材或石材),但不能模拟具有镜面高光的光泽表面(例如涂漆木材)
  2. MeshPhysicalMaterial 是MeshStandardMaterial的扩展,提供了更高级的基于物理的渲染属性


Previous Post
React源码学习
Next Post
Three.js 3D字体,星系制作(二)