Web3D-光与影篇

方向光 DirectionalLight

具有特定方向;能产生阴影;不能在物体里面;强度、颜色、位置、目标都能影响渲染效果;阴影相机是正交相机

js 复制代码
  createScene () {
    const canvas = document.getElementById('c')

    const width = window.innerWidth;
    const height = window.innerHeight;

    canvas.width = width
    canvas.height = height
    // 挂载全局上
    this.canvas = canvas
    this.width = width
    this.height = height

    // 创建3D场景对象
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf0f0f0)
    this.scene = scene
  },
  // 创建光照
  createLights () {
    // 添加全局光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.95)
    // 方向光
    const dirLight = new THREE.DirectionalLight(0xffffaa, 0.95)
    dirLight.castShadow = true // 让方向光产生阴影

    // 方向光辅助工具
    const dirHelper = new THREE.DirectionalLightHelper(dirLight, 3)

    this.scene.add(ambientLight, dirLight, dirHelper)
    this.ambientLight = ambientLight
    this.dirLight = dirLight
    this.dirHelper = dirHelper
  },
    // 创建立方体对象
  createObjects () {
    const box = new THREE.Mesh(
      new THREE.BoxGeometry(1, 1, 1),
      new THREE.MeshLambertMaterial({
        color: 0x1890ff
      })
    )
    const geometry = new THREE.PlaneGeometry(10, 10)
    const material = new THREE.MeshLambertMaterial({
      side: THREE.DoubleSide
    })
    const floor = new THREE.Mesh(geometry, material)
    const wall = new THREE.Mesh(geometry, material)

    floor.rotation.x = -Math.PI / 2
    floor.position.y = -1
    wall.position.y = 4
    wall.position.z = -5

    this.dirLight.target = box // 方向光照向物体
    box.castShadow = true // 物体产生阴影
    floor.receiveShadow = true // 地面接收阴影
    wall.receiveShadow = true // 墙面接收阴影

    this.scene.add(box, floor, wall)
    this.box = box
  },
  datGui () {
    const _this = this
    const gui = new dat.GUI();

    const ambientFolder = gui.addFolder('环境光')
    ambientFolder.add(_this.ambientLight, 'intensity', 0, 1, 0.1).name('环境光强度')
    ambientFolder.add(_this.ambientLight, 'visible').name('环境光可见性')
    ambientFolder.addColor({ color: 0xffffff }, 'color').onChange(val =>{
      _this.ambientLight.color = new THREE.Color(val)
    })
    ambientFolder.open()

    const dirLightFolder = gui.addFolder('方向光')
    dirLightFolder.add(_this.dirLight, 'intensity',  0, 1, 0.1)
    dirLightFolder.add(_this.dirLight, 'visible')
    dirLightFolder.add(_this.dirLight.position, 'x', -20, 20, 0.1)
    dirLightFolder.add(_this.dirLight.position, 'y', -20, 20, 0.1)
    dirLightFolder.add(_this.dirLight.position, 'z', -20, 20, 0.1)
    dirLightFolder.open()

    const boxFolder = gui.addFolder('box')
    boxFolder.add(_this.box.position, 'x', -20, 20, 0.1).onChange(val =>{
      _this.dirHelper.update()
    })
    boxFolder.add(_this.box.position, 'y', -20, 20, 0.1).onChange(val =>{
      _this.dirHelper.update()
    })
    boxFolder.add(_this.box.position, 'z', -20, 20, 0.1).onChange(val =>{
      _this.dirHelper.update()
    })
    boxFolder.open()

    const shadowMapFolder = gui.addFolder('阴影')
    // mapSize 改变阴影的位置
    shadowMapFolder.add(_this.dirLight.shadow.mapSize, 'x', [512, 1024, 2048, 4096])
    shadowMapFolder.add(_this.dirLight.shadow.mapSize, 'y', [512, 1024, 2048, 4096])
    // radius 改变阴影的模糊度
    shadowMapFolder.add(_this.dirLight.shadow, 'radius', 0, 30, 1)
    shadowMapFolder.open()
  },
  
  renderer.shadowMap.enabled = true; // 渲染器开启阴影

半球光 HemisphereLight

  • 可将天空+地面反射的光照都计算在内,产生柔和、平衡的环境光照;
  • 无法产生阴影
  • 环境光AmbientLight 与半球光HemisphereLight 会相互影响 。与环境光类似,可模拟太阳、日出、日落的效果。
js 复制代码
  // 创建光照
  createLights () {
    // 添加全局光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.95)
    ambientLight.visible = false // 关闭

    // 方向光
    const dirLight = new THREE.DirectionalLight(0xffffaa, 0.95)
    dirLight.position.set(0, 1, 2)
    dirLight.castShadow = true // 让方向光产生阴影

    // 方向光辅助工具
    const dirHelper = new THREE.DirectionalLightHelper(dirLight, 3)

    // 半球光
    const hemiLight = new THREE.HemisphereLight(0x00ffff, 0xffff55, 0.7)

    // 半球光辅助工具
    const hemiHelper = new THREE.HemisphereLightHelper(hemiLight, 1)

    this.scene.add(ambientLight, dirLight, dirHelper, hemiLight, hemiHelper)
    this.ambientLight = ambientLight
    this.dirLight = dirLight
    this.dirHelper = dirHelper
    this.hemiLight = hemiLight
  },
  // 创建立方体对象
  createObjects () {
    const box = new THREE.Mesh(
      new THREE.BoxGeometry(1, 1, 1),
      new THREE.MeshLambertMaterial({
        color: 0x1890ff
      })
    )
    const geometry = new THREE.PlaneGeometry(10000, 10000)
    const material = new THREE.MeshLambertMaterial({
      side: THREE.DoubleSide
    })
    const floor = new THREE.Mesh(geometry, material)
    // const wall = new THREE.Mesh(geometry, material)

    // 天空
    const sky = new THREE.Mesh(
      geometry, 
      new THREE.MeshLambertMaterial({
        side: THREE.DoubleSide,
      })
    )

    floor.rotation.x = -Math.PI / 2
    floor.position.y = -1
    // wall.position.y = 4
    // wall.position.z = -5
    sky.position.y = 20
    sky.rotation.x = -Math.PI / 2

    this.dirLight.target = box // 方向光照向物体
    box.castShadow = true // 物体产生阴影
    floor.receiveShadow = true // 地面接收阴影
    // wall.receiveShadow = true // 墙面接收阴影

    this.scene.add(box, floor, sky)
    this.box = box
  },

点光源 PointLight

js 复制代码
  // 创建光照
  createLights () {
    // 添加全局光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.95)
    ambientLight.visible = false // 关闭

    // 方向光
    const dirLight = new THREE.DirectionalLight(0xffffaa, 0.95)
    dirLight.position.set(0, 3, 1.5)
    dirLight.castShadow = true // 让方向光产生阴影
    dirLight.visible = false // 关闭

    // 方向光辅助工具
    const dirHelper = new THREE.DirectionalLightHelper(dirLight, 3)

    // 点光源
    const pointLight1 = new THREE.PointLight(0xf3ae3d, 0.8)
    const pointLight2 = new THREE.PointLight(0xa1fc8f, 0.8)

    // 光源位置默认在原点
    pointLight1.position.set(-1, 1, 2)
    pointLight2.position.set(1, 1, 2)
    // 开启阴影
    pointLight1.castShadow = true
    pointLight2.castShadow = true

    // 创建小球标示光源位置
    const sphere1 = new THREE.Mesh(
      new THREE.SphereGeometry(0.05, 64, 64),
      new THREE.MeshBasicMaterial({
        color: 0xf3ae3d,
      })
    );
    const sphere2 = new THREE.Mesh(
      new THREE.SphereGeometry(0.05, 64, 64),
      new THREE.MeshBasicMaterial({
        color: 0xa1fc8f,
      })
    );
    sphere1.position.copy(pointLight1.position)
    sphere2.position.copy(pointLight2.position)

    this.scene.add(ambientLight, dirLight, dirHelper,sphere1, sphere2, pointLight1, pointLight2, )
  },
  clock: new THREE.Clock(),
  tick () {
    // 让点光源运动
    const elapsedTime = this.clock.getElapsedTime()
    this.pointLight1.position.x = Math.sin(elapsedTime)
    this.pointLight1.position.z = Math.cos(elapsedTime)
    this.sphere1.position.copy(this.pointLight1.position)

    // 更新
    this.orbitControls.update()

    this.renderer.render(this.scene, this.camera)
    window.requestAnimationFrame(()=> this.tick())
  },

面光源 RectAreaLight

  • 只支持 MeshStandardMaterial 和 MeshPhysicalMaterial 两种材质
  • 不能产生阴影
  • 从矩形平面上均匀地发射光线
  • 必须在场景中,加入 RectAreaLightUniformLib,并调用init()
  • RectAreaLightUniformsLib.init()矩形面光源uniforms工具库,包含矩形面光源常用数据,矩形区域光源位置、方向、颜色、强度。只适用于矩形的区域光源,即RectAreaLight
js 复制代码
  // 创建光照
  createLights () {
    // 添加全局光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)

    RectAreaLightUniformsLib.init()

    // 面光源
    const rectLight = new THREE.RectAreaLight(0xffffff, 10, 2, 4)
    rectLight.position.set(0, 1, 5)

    const rectHelper = new RectAreaLightHelper(rectLight)

    this.scene.add(ambientLight, rectLight, rectHelper);
  },
  // 创建立方体对象
  createObjects () {
    const floorTexture = this.textureLoader.load('/src/assets/textures/6.jpg')
    const wallTexture = this.textureLoader.load('/src/assets/textures/2.jpg')
    const photoTexture = this.textureLoader.load('/src/assets/textures/4.jpg')

    this.floorTexture = floorTexture
    this.wallTexture = wallTexture
    this.photoTexture = photoTexture

    const box = new THREE.Mesh(
      new THREE.BoxGeometry(1, 1, 1),
      new THREE.MeshStandardMaterial({
        color: 0x1890ff,
      })
    )

    // 地板
    const floor = new THREE.Mesh(
      new THREE.PlaneGeometry(20, 20),
      new THREE.MeshStandardMaterial({
        map: floorTexture,
        roughness: 0, // 产生反光
      })
    )
    // 墙面
    const wall = new THREE.Mesh(new THREE.PlaneGeometry(20, 20), new THREE.MeshStandardMaterial({
      map: wallTexture,
      roughness: 0,
    }) )

    floor.rotation.x = -Math.PI / 2
    floor.position.y = -0.8
    floor.position.z = -2
    wall.position.z = -2

    // 添加相框
    const frameGeometry = new THREE.PlaneGeometry(4.4, 6.4)
    const frameMaterial = new THREE.MeshStandardMaterial({
      color: 0xd08a38,
    })
    const frame = new THREE.Mesh(frameGeometry, frameMaterial)
    const photoGeometry = new THREE.PlaneGeometry(4, 6)
    const photoMaterial = new THREE.MeshStandardMaterial({
      map: photoTexture,
      roughness: 0, // 产生反光
    })
    const photo = new THREE.Mesh(photoGeometry, photoMaterial)

    const group = new THREE.Group()
    group.add(frame, photo)
    frame.position.z = 0.001
    photo.position.z = 0.002
    group.position.z = -2
    group.position.y = 5

    this.scene.add(box, floor, wall, group)
    this.box = box
  },

聚光灯

  • 形状类似锥形光源,模拟聚光灯效果
  • 能产生明显的光照和阴影效果 阴影属性:
  • castShadow:是否产生阴影,默认是false
  • receiveShadow:是否接手阴影,默认是false
  • bias:阴影偏移量,默认是0,非常小的调整(大约0.0001)可能有助于减少阴影中的伪影
  • mapSize:阴影贴图大小,默认是512,必须是2的幂,宽、高不必相同
  • radiu:阴影模糊半径,默认是1,值越大,阴影越模糊,较高的值会在阴影中产生不必要的条带效果
  • target:阴影的目标,默认是null,若设置了,则阴影会围绕目标产生
  • camera:用于计算阴影的相机,默认是SportLight.shadow.camera,SportLight 是透视相机

SportLight 的透视相机属性:

  • near: 阴影相机的近裁剪面,决定了阴影的细节程度
  • far: 阴影相机的远裁剪面,决定了阴影的覆盖范围
  • fov:此处无效 ,会被自动设为90
js 复制代码
  // 创建光照
  createLights () {
    // 添加全局光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
    ambientLight.visible = false

    // 添加聚光灯
    const spotLight = new THREE.SpotLight(0xff00ff, 0.95)
    const spotHelper = new THREE.SpotLightHelper(spotLight) 

    spotLight.intensity = 1
    spotLight.distance = 100 // 光源距离
    spotLight.angle = Math.PI / 4 // 照射角度
    spotLight.penumbra = 0.3 // 半影:阴影边缘模糊度
    spotLight.position.y = 10
    spotLight.castShadow = true // 开启阴影

    spotHelper.update() // 更新

    this.scene.add(ambientLight, spotLight, spotHelper );
    this.ambientLight = ambientLight
    this.spotLight = spotLight
    this.spotHelper = spotHelper
  },
// 创建立方体对象
  createObjects () {
    const box = new THREE.Mesh(
      new THREE.SphereGeometry(2, 64, 64),
      new THREE.MeshLambertMaterial({
        color: 0x1890ff
      })
    )

    const geometry = new THREE.PlaneGeometry(1000, 1000)
    const floor = new THREE.Mesh(
      geometry, 
      new THREE.MeshLambertMaterial({
        color: 0x666666, 
        // side: THREE.DoubleSide,
      })
    )

    // 天花板
    const sky = new THREE.Mesh(
      geometry, 
      new THREE.MeshLambertMaterial({
        color: 0x666666, 
        side: THREE.DoubleSide,
      })
    )
    sky.rotation.x = -Math.PI / 2
    sky.position.y = 60
    sky.castShadow = true

    floor.rotation.x = -Math.PI / 2
    floor.position.y = -1
    box.position.y = 3

    box.castShadow = true // 开启阴影
    floor.receiveShadow = true // 接收阴影
    this.spotLight.target = box // 聚光灯始终朝向物体
  },
  
  // 调试器
    const spotFolder = gui.addFolder('聚光灯')
    spotFolder.add(_this.spotLight, 'intensity', 0, 1, 0.1).name('强度')
    spotFolder.add(_this.spotLight, 'visible').name('可见性')
    spotFolder.add(_this.spotLight, 'distance', 0, 100, 1).name('距离').onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight, 'angle', 0, Math.PI / 2, 0.1).name('角度').onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight, 'penumbra', 0, 1, 0.1).name('半影').onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight, 'decay', 0, 10, 0.1).name('衰减量').onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight, 'power', 0, 30, 0.1).name('光功率').onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight.position, 'x', -30, 30, 0.1).onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight.position, 'y', -30, 30, 0.1).onChange(val =>{
      _this.spotHelper.update()
    })
    spotFolder.add(_this.spotLight.position, 'z', -30, 30, 0.1).onChange(val =>{
      _this.spotHelper.update()
    })

    spotFolder.add(_this.spotLight, 'castShadow').name('阴影')
    spotFolder.add(_this.spotLight.shadow, 'radius', 0, 5, 0.01).name('阴影半径')
    const params = {
      near: _this.spotLight.shadow.camera.near,
      far: _this.spotLight.shadow.camera.far,
      fov: _this.spotLight.shadow.camera.fov,
    }
    spotFolder.add(params, 'near', 0.01, 10, 0.01).name('阴影相机near')
    spotFolder.add(params, 'far', 0.01, 10, 0.01).name('阴影相机far')
    this.params = params
    
    renderer.outputEncoding = THREE.sRGBEncoding // 开启渲染    
相关推荐
一嘴一个橘子6 天前
3.js - 漫天孔明灯(使用OrbitControls 锁定相机镜头)
three.js
一嘴一个橘子7 天前
3.js - 着色器设置点材质(螺旋星系特效)
three.js
Ian102510 天前
Three.js new THREE.TextureLoader()纹理贴图使用png图片显示为黑色
前端·javascript·webgl·three.js·贴图·三维
Jedi Hongbin16 天前
THREE.JS像素风格渲染
javascript·three.js·shader·后处理
unix2linux22 天前
Parade Series - 3D Modeling
python·flask·html·jquery·webgl·three.js
zj靖1 个月前
three.js 几何体、材质和网格模型
three.js
一嘴一个橘子1 个月前
3.js - 顶点着色器、片元着色器的联系
three.js
Jedi Hongbin1 个月前
Threejs&WebGPU运动残影demo
javascript·three.js
Virtual091 个月前
借助Three.js,我们也能实现一个地球仪旋转
前端·javascript·three.js
一嘴一个橘子1 个月前
3.js - 物理引擎终极
three.js