美文网首页
three.js - Haunted House

three.js - Haunted House

作者: 闪电西兰花 | 来源:发表于2024-01-27 10:53 被阅读0次
  • 基础场景:some lights、no shadow、a Dat.GUI panel
  // 导入three.js
  import * as THREE from 'three'
  // 导入轨道控制器
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  // 导入gui
  import * as dat from 'dat.gui'

  /**
   * Scene
  **/
  const scene = new THREE.Scene()

  /**
   * Camera
  **/
  const camera = new THREE.PerspectiveCamera(
    75, // 视角
    window.innerWidth / window.innerHeight, // 视椎体长宽比
    0.1, // 近端面
    100 // 远端面
  )
  camera.position.set(4, 2, 5)

  /**
   * Renderer
  **/
  const renderer = new THREE.WebGLRenderer()
  renderer.setSize(window.innerWidth, window.innerHeight)
  document.body.appendChild(renderer.domElement)

  /**
    * 坐标轴
  **/
  const axesHelper = new THREE.AxesHelper(5) // 坐标轴线段长度
  scene.add(axesHelper)

  /**
    控制器(使相机围绕目标运动)
  **/
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true // 添加轨道阻尼效果

  /**
    * 渲染
  **/
  const clock = new THREE.Clock()
  function animate () {
    let elapsedTime = clock.getElapsedTime()

    controls.update()
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
  }
  animate()
  /**
    * Lights
  **/
  // AmbientLight
  const ambientLight = new THREE.AmbientLight('#fff', 0.8)
  scene.add(ambientLight)
  /**
    * gui
  **/
  const gui = new dat.GUI()
  gui.add(ambientLight, 'intensity').name('AmbientLight').min(0).max(1).step(0.001)
  • House
    • Floor
    /**
      * House
    **/
    // Floor
    const floor = new THREE.Mesh(
      new THREE.PlaneGeometry(20, 20),
      new THREE.MeshStandardMaterial({
        color: '#a9c388',
    })
    floor.rotation.x = - Math.PI * 0.5
    floor.position.y = 0
    scene.add(floor)
    
    • create a House group 以便于需要整体调整House的位置、大小等
    /**
      * House
    **/
    // Group
    const house = new THREE.Group()
    scene.add(house)
    
    ...
    
    /**
      * floor
    **/
    ...
    
    • create the Walls
    /**
      * House
    **/
    // Group
    const house = new THREE.Group()
    scene.add(house)
    
    // Walls
    const walls = new THREE.Mesh(
      new THREE.BoxGeometry(4, 2.5, 4),
      new THREE.MeshStandardMaterial({
          color: '#ac8e82'
      })
    )
    walls.position.y = 2.5 / 2   // 立方体初始有一半的部分是在坐标轴以下的,也就是在floor的下面
    house.add(walls) // 后续house的部分都添加在house上面
    
    ...
    
    // foor
    ...
    
    • create the Roof, with a pyramid 棱锥体
    // Roof
    const roof = new THREE.Mesh(
      new THREE.ConeGeometry(3.5, 1, 4), 
      new THREE.MeshStandardMaterial({
        color: '#b35f45'
      })
    )
    roof.rotation.y = Math.PI * 0.25
    roof.position.y = 2.5 + 0.5 // walls的高 + 自身高的一半
    house.add(roof)
    
    • creat the Door with a plane
    // Door
    const door = new THREE.Mesh(
      new THREE.PlaneGeometry(2, 2),
      new THREE.MeshStandardMaterial({
        color: '#aa7b7b', 
      })
    )
    door.position.y = 1
    door.position.z = 2 + 0.01 // walls的深度 + 0.01,+0.01避免处在同一层级
    house.add(door)
    
    • add Bushes and use the same geometry and the same material for every bushes
    // Bushes
    const bushGeometry = new THREE.SphereGeometry(1, 16, 16)
    const bushMaterial = new THREE.MeshStandardMaterial({
      color: '#89c854'
    })
    
    const bush1 = new THREE.Mesh(bushGeometry, bushMaterial)
    bush1.scale.set(0.5, 0.5, 0.5)
    bush1.position.set(0.8, 0.2, 2.2)
    
    const bush2 = new THREE.Mesh(bushGeometry, bushMaterial)
    bush2.scale.set(0.25, 0.25, 0.25)
    bush2.position.set(1.4, 0.1, 2.1)
    
    const bush3 = new THREE.Mesh(bushGeometry, bushMaterial)
    bush3.scale.set(0.4, 0.4, 0.4)
    bush3.position.set(-0.8, 0.1, 2.2)
    
    const bush4 = new THREE.Mesh(bushGeometry, bushMaterial)
    bush4.scale.set(0.15, 0.15, 0.15)
    bush4.position.set(-1, 0.05, 2.6)  
    
    house.add(bush1, bush2, bush3, bush4)
    
    • Graves
      1. instead of placing each grave manually, we are going to create and place them procedurally
      2. grave可出现的范围需要被限制在floor的范围内,同时围绕在house的外部出现(环形)
    /**
      * Graves
    **/
    const graves = new THREE.Group()
    scene.add(graves)
    
    const graveGeometry = new THREE.BoxGeometry(0.6, 0.8, 0.2)
    const graveMaterial = new THREE.MeshStandardMaterial({
      color: '#b2b6b1'
    })
    
    for(let i = 0; i < 50; i++) {
      const angle = Math.random() * Math.PI * 2 // 360°
      const radius = 3 + Math.random() * 6 // 随机半径, 3-9
      // 分别设置x z轴的坐标,使grave围绕house画一个圈
      const x = Math.sin(angle) * radius
      const z = Math.cos(angle) * radius
    
      const grave = new THREE.Mesh(graveGeometry, graveMaterial)
      grave.position.set(x, 0.3, z) // y值小于高度的一半,避免z轴出现旋转后底部悬空
      // 旋转随机参数减去0.5,范围-0.5至0.5,确保旋转方向不一致
      grave.rotation.y = (Math.random() - 0.5) * 0.6
      grave.rotation.z = (Math.random() - 0.5) * 0.6
      grave.rotation.x = (Math.random() - 0.5) * 0.6
      graves.add(grave)
    }
    
    • Lights
      1. dim the ambient and moon lights
      2. give those a more blue-ish color 青调
      3. add a warm PointLight above the door and add it to the house
    /*
      * Lights
    */
    // AmbientLight
    const ambientLight = new THREE.AmbientLight('#b9d5ff', 0.12)
    scene.add(ambientLight)
    
    // DirectionalLight
    const moonLight = new THREE.DirectionalLight('#b9d5ff', 0.12)
    moonLight.position.set(4, 5, -2)
    scene.add(moonLight)
    
    // Door Light
    const doorLight = new THREE.PointLight('#ff7d46', 1, 7)
    doorLight.position.set(0, 2.2, 2.7)
    house.add(doorLight)
    
  • 完成House部分后,我们应该会得到如下图所示
    基础结构.png
  • Fog
    • 当前我们看到的floor的边缘部分过于清晰
    • 使用three.js本身就支持的 Fog
    • new THREE.Fog(color, near, far)nearfar分别指的是开始应用雾的最小距离和最大距离,这里的“距离”是指距离camera的远近
  /**
    * Fog
  **/
  const fog = new THREE.Fog('#262837', 1, 15) // color 距离camera的near 距离camera的far
  scene.fog = fog
  • Background
    • 继续模糊边缘,使floor和背景融为一体
    • to fix the background, use the same color as Fog
renderer.setClearColor('#262837')
添加FOG和重置背景色之后的效果.png
  • Textures
    /**
      * Textures
    **/
    const textureLoader = new THREE.TextureLoader()
    
    • door
    const doorColorTexture = textureLoader.load('/imgs/haunted-house/door/color.jpg')
    const doorAlphaTexture = textureLoader.load('/imgs/haunted-house/door/alpha.jpg')
    const doorAmbientOcclusionTexture = textureLoader.load('/imgs/haunted-house/door/ambientOcclusion.jpg')
    const doorHeightTexture = textureLoader.load('/imgs/haunted-house/door/height.jpg')
    const doorNormalTexture = textureLoader.load('/imgs/haunted-house/door/normal.jpg')
    const doorMetalnessTexture = textureLoader.load('/imgs/haunted-house/door/metalness.jpg')
    const doorRoughnessTexture = textureLoader.load('/imgs/haunted-house/door/roughness.jpg')
    
    // Door
    const door = new THREE.Mesh(
      new THREE.PlaneGeometry(2.2, 2.2, 100, 100), // displacementMap会移动顶点实现立体感
      new THREE.MeshStandardMaterial({
        // color: '#aa7b7b', // 添加texture之前作为替代
    
        map: doorColorTexture, // 将简单的纹理作为颜色
    
        transparent: true,
        alphaMap: doorAlphaTexture, // 需要与transparent同时使用
    
        aoMap: doorAmbientOcclusionTexture, // 需要提供uv2坐标支持
    
        displacementMap: doorHeightTexture, // 使door更加立体,不仅仅是一个平面
        displacementScale: 0.1, // 减小移动顶点的高度效应
    
        normalMap: doorNormalTexture, // 法线贴图
    
        metalnessMap: doorMetalnessTexture,
    
        roughnessMap: doorRoughnessTexture
      })
    )
    // support aoMap
    // door.geometry.attributes.uv   自动创建的uv坐标,2个值组成一个坐标值
    door.geometry.setAttribute(
      'uv2', 
      new THREE.Float32BufferAttribute(door.geometry.attributes.uv.array, 2)
    )
    door.position.y = 1
    door.position.z = 2 + 0.01 // walls的深度 + 0.01,+0.01避免处在同一层级
    house.add(door)
    
    • walls
    const bricksColorTexture = textureLoader.load('/imgs/haunted-house/bricks/color.jpg')
    const bricksAmbientTexture = textureLoader.load('/imgs/haunted-house/bricks/ambientOcclusion.jpg')
    const bricksNormalTexture = textureLoader.load('/imgs/haunted-house/bricks/normal.jpg')
    const bricksRoughnessTexture = textureLoader.load('/imgs/haunted-house/bricks/roughness.jpg')
    
    // Walls
    const walls = new THREE.Mesh(
      new THREE.BoxGeometry(4, 2.5, 4),
      new THREE.MeshStandardMaterial({
        map: bricksColorTexture,
    
        aoMap: bricksAmbientTexture, // uv2
    
        normalMap: bricksNormalTexture,
    
        roughnessMap: bricksRoughnessTexture
      })
    )
    // support aoMap
    walls.geometry.setAttribute(
      'uv2', 
      new THREE.Float32BufferAttribute(walls.geometry.attributes.uv.array, 2)
    )
    walls.position.y = 2.5 / 2
    house.add(walls)
    
    • floor
    const grassColorTexture = textureLoader.load('/imgs/haunted-house/grass/color.jpg')
    const grassAmbientTexture = textureLoader.load('/imgs/haunted-house/grass/ambientOcclusion.jpg')
    const grassNormalTexture = textureLoader.load('/imgs/haunted-house/grass/normal.jpg')
    const grassRoughnessTexture = textureLoader.load('/imgs/haunted-house/grass/roughness.jpg')
    
    // 仅添加以上纹理后会发现,grass的大小和house不匹配,继续做如下优化
    
    // repeat
    grassColorTexture.repeat.set(8, 8)
    grassAmbientTexture.repeat.set(8, 8)
    grassNormalTexture.repeat.set(8, 8)
    grassRoughnessTexture.repeat.set(8, 8)
    
    // 避免repeat时拉伸最后一个像素,change the wrapS and wrapT properties
    grassColorTexture.wrapS = THREE.RepeatWrapping
    grassAmbientTexture.wrapS = THREE.RepeatWrapping
    grassNormalTexture.wrapS = THREE.RepeatWrapping
    grassRoughnessTexture.wrapS = THREE.RepeatWrapping
    
    grassColorTexture.wrapT = THREE.RepeatWrapping
    grassAmbientTexture.wrapT = THREE.RepeatWrapping
    grassNormalTexture.wrapT = THREE.RepeatWrapping
    grassRoughnessTexture.wrapT = THREE.RepeatWrapping
    
     /**
     * Floor
    **/
    const floor = new THREE.Mesh(
      new THREE.PlaneGeometry(20, 20),
      new THREE.MeshStandardMaterial({
        map: grassColorTexture,
    
        aoMap: grassAmbientTexture, // uv2
    
        normalMap: grassNormalTexture,
    
        roughnessMap: grassRoughnessTexture
      })
    )
    // support aoMap
    floor.geometry.setAttribute(
      'uv2', 
      new THREE.Float32BufferAttribute(floor.geometry.attributes.uv.array, 2)
    )
    floor.rotation.x = - Math.PI * 0.5
    floor.position.y = 0
    scene.add(floor)
    
添加textures之后的效果.png
  • Ghosts
    • we are going to represent them with simple lights floating around the house and passing through the ground and graves
    • animate these lights 让ghost以不同的频率围绕house旋转并上下起伏
    • 这里的动画思路可以参考上面的graves的部分
  /*
    * Ghosts
  */
  const ghost1 = new THREE.PointLight('#ff00ff', 2, 3)
  scene.add(ghost1)

  const ghost2 = new THREE.PointLight('#00ffff', 2, 3)
  scene.add(ghost2)

  const ghost3 = new THREE.PointLight('#ffff00', 2, 3)
  scene.add(ghost3)
  /*
    * 渲染
  */
  const clock = new THREE.Clock()
  function animate () {
    let elapsedTime = clock.getElapsedTime()

    // Ghosts
    const ghost1Angle = elapsedTime * 0.5
    ghost1.position.x = Math.cos(ghost1Angle) * 4
    ghost1.position.z = Math.sin(ghost1Angle) * 4
    ghost1.position.y = Math.sin(elapsedTime * 3) // 上下起伏
    
    const ghost2Angle = - elapsedTime * 0.32 // 设为负数,与ghost1反向动画
    ghost2.position.x = Math.cos(ghost2Angle) * 5
    ghost2.position.z = Math.sin(ghost2Angle) * 5
    ghost2.position.y = Math.sin(elapsedTime * 4) + Math.sin(elapsedTime * 2.5)

    const ghost3Angle = - elapsedTime * 0.18
    ghost3.position.x = Math.cos(ghost3Angle) * (7 + Math.sin(elapsedTime * 0.32)) // 不固定旋转半径
    ghost3.position.z = Math.sin(ghost3Angle) * (7 + Math.sin(elapsedTime * 0.5)) // 不固定旋转半径
    ghost3.position.y = Math.sin(elapsedTime * 4) + Math.sin(elapsedTime * 2.5)


    controls.update()
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
  }
  animate()
添加ghosts之后会随机出现的PointLight.png
  • Shadows
    • 我们将阴影设置统一放在一起方便管理
    /**
     * Renderer
    **/
    ...
    ...
    
    // Shadows
    ... 以下部分为shadows
    
    
    • activate the shadow map on the renderer
    // 开启场景中的阴影贴图
    renderer.shadowMap.enabled = true
    
    • activate the shadows on the lights that should cast shadows
    moonLight.castShadow = true
    doorLight.castShadow = true
    ghost1.castShadow = true
    ghost2.castShadow = true
    ghost3.castShadow = true
    
    • go through each objects of your scene and decide if that object can cast and/or receive shadows
    walls.castShadow = true
    bush1.castShadow = true
    bush2.castShadow = true
    bush3.castShadow = true
    bush4.castShadow = true
    
    /**
      * Graves
    **/
    for(let i = 0; i < 50; i++) {
      ...
      ...
      grave.castShadow = true
      ...
    }
    
    floor.receiveShadow = true // receive shadow
    
    • optimize the shadow maps
    doorLight.shadow.mapSize.width = 256
    doorLight.shadow.mapSize.height = 256
    doorLight.shadow.camera.far = 7
    
    ghost1.shadow.mapSize.width = 256
    ghost1.shadow.mapSize.height = 256
    ghost1.shadow.camera.far = 7
    
    ghost2.shadow.mapSize.width = 256
    ghost2.shadow.mapSize.height = 256
    ghost2.shadow.camera.far = 7
    
    ghost3.shadow.mapSize.width = 256
    ghost3.shadow.mapSize.height = 256
    ghost3.shadow.camera.far = 7
    
    • 改变阴影映射的算法
    renderer.shadowMap.type = THREE.PCFSoftShadowMap
    
最终静态效果.png

相关文章

  • 鬼屋 HAUNTED HOUSE

    趁著七月節還沒過完,來學習一個英文單詞haunted。這個單詞的意思是“鬧鬼的”,所以大標題就是鬧鬼的屋子。鬧鬼其...

  • Halloween Haunted House

    西方的Halloween ,也就是鬼节,人们会穿上自制的扮鬼装扮,也会参加一些类似于鬼屋的娱乐,有哪些相关的词汇可...

  • 幽灵出没的房子

    幽灵出没的房子 (A Haunted House) 作者 弗吉尼亚·伍尔夫 (Virginia Woolf) /译...

  • 【高级趣读】A Haunted House

    Whatever hour you woke there was a door shutting. From ro...

  • Haunted

    时时刻刻都可以变得紧张的我真的好吗? 是不是我想得太多了?是不是有太多的事没有做或者不想做?是不是有太多的人不敢去...

  • Depressed

    Felt haunted by all these chaotic traces of thoughts.

  • Why are you about to leave for..

    Recently, I was haunted by something that requires me t...

  • 2015/08/17

    Recently,I am sort of haunted by some feelings, ideas and...

  • bury myself in dance to release

    The agony never leaves me, like a ghost. Haunted, like a ...

  • Haunted Illusions 1.pdf 免费下载

    下载地址:Haunted Illusions 1[www.rejoiceblog.com].pdf

网友评论

      本文标题:three.js - Haunted House

      本文链接:https://www.haomeiwen.com/subject/bmtjidtx.html