【Three.js基础学习】12. Haunted house

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

    课程案例:

        效果:实现一个房子 周围是墓地,有几只鬼魂在周围飘荡,周围围绕正浓雾

        1.过一遍视频,结合案例已完成代码,自己想着写 ,训练一下思维吧  ,(观看完视频,先把所有需要的物体造出来)

        2. 先要有一块地, 这个地板的长度 以 m 为单位  20 * 20 ; 同时设置地板的位置,旋转角度 Math.PI 半圆 * 0.5

            材质颜色先用草绿色标识一下

        3.要一个房子 首先是一个立方体盒子 和一个锥形 组合到一起 变成一个房子

            之前有组的概念:Group ,重温一下 组的概念 ,相当于 由几何体,纹理组成的房子 可称之为 组; 以后写任何物体也都可以创建成为组

            因此 const house =  new THREE.Group() 就可以添加到场景中,之后就可以用组添加,比如门是属于房子的 ,那么有一个房子的组,就可以添加门,这相当于物体分类

        这个时候有了一个房子

        4.要一下灌木丛作为点缀

        5.周边要生成一些墓地 同样作为点缀

        6.周围边界不太好看,放一些雾气作为遮掩,同时也可以增加阴暗的效果

        7.场景中所有建筑都已经有了,接下来 在门口放一盏灯光

        8.将纹理替换场景中材质的颜色

        这里遇到一个坑 讲一下 PlaneBufferGeometry PlaneGeometry这两个生成的几何体 在获取uv2时候PlaneGeometry会有问题

        9.在贴草的时候,注意草纹理的大小 ,可以设置纹理小一点 然后向xy重复直到铺满

        10.创建三个光在周围游动,显得更加阴森恐怖

        我说怎么地板有白色点以为贴图有问题 原来设置粗糙度没有map


一、代码

import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'dat.gui'


/**
 * Base
 */
// Debug
const gui = new dat.GUI()

// Canvas
const canvas = document.querySelector('canvas.webgl')

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

/* 
    Texture
*/
const textureLoader = new THREE.TextureLoader()
const doorColorTexture =  textureLoader.load('/textures/door/color.jpg')
const doorAlphaTexture =  textureLoader.load('/textures/door/alpha.jpg')
const doorAmbientOcclusionTexture =  textureLoader.load('/textures/door/ambientOcclusion.jpg')
const doorHeightTexture = textureLoader.load('/textures/door/height.jpg')
const doorNormalTexture =  textureLoader.load('/textures/door/normal.jpg')
const doorMetalnessTexture =  textureLoader.load('/textures/door/metalness.jpg')
const doorRoughnessTexture =  textureLoader.load('/textures/door/roughness.jpg')

const bricksColorTexture = textureLoader.load('/textures/bricks/color.jpg')
const bricksAmbientOcclusionTexture = textureLoader.load('/textures/bricks/ambientOcclusion.jpg')
const bricksNormalTexture = textureLoader.load('/textures/bricks/normal.jpg')
const bricksRoughnessTexture = textureLoader.load('/textures/bricks/roughness.jpg')

const grassColorTexture = textureLoader.load('/textures/grass/color.jpg')
const grassAmbientOcclusionTexture = textureLoader.load('/textures/grass/ambientOcclusion.jpg')
const grassNormalTexture = textureLoader.load('/textures/grass/normal.jpg')
const grassRoughnessTexture = textureLoader.load('/textures/grass/roughness.jpg')

grassColorTexture.repeat.set(8,8)
grassAmbientOcclusionTexture.repeat.set(8,8)
grassNormalTexture.repeat.set(8,8)
grassRoughnessTexture.repeat.set(8,8)

grassColorTexture.wrapS = THREE.RepeatWrapping
grassAmbientOcclusionTexture.wrapS = THREE.RepeatWrapping
grassNormalTexture.wrapS = THREE.RepeatWrapping
grassRoughnessTexture.wrapS = THREE.RepeatWrapping

grassColorTexture.wrapT = THREE.RepeatWrapping
grassAmbientOcclusionTexture.wrapT = THREE.RepeatWrapping
grassNormalTexture.wrapT = THREE.RepeatWrapping
grassRoughnessTexture.wrapT = THREE.RepeatWrapping


/* 
    House
*/
const house = new THREE.Group()
scene.add(house)

// walls 墙壁
const walls = new THREE.Mesh(
    new THREE.BoxBufferGeometry(4,2.5,4), // 宽高深
    new THREE.MeshStandardMaterial({
        map:bricksColorTexture,
        aoMap:bricksAmbientOcclusionTexture,
        normalMap:bricksNormalTexture,
        roughness:bricksRoughnessTexture
    })
)
walls.geometry.setAttribute(
    'uv2',
    new THREE.Float32BufferAttribute(walls.geometry.attributes.uv.array,2)
)
walls.position.y = 1.25
house.add(walls)

// roof 屋顶
const roof = new THREE.Mesh(
    new THREE.ConeBufferGeometry(3.5,1,4),
    new THREE.MeshStandardMaterial({color:'#b35f45'})
)
roof.position.y = 0.5 + 2.5
roof.rotation.y =  Math.PI * 0.25
house.add(roof)

// Door
const door = new THREE.Mesh(
    new THREE.PlaneBufferGeometry(2.2,2.2,100,100),
    new THREE.MeshStandardMaterial({
        map:doorColorTexture,
        transparent:true,
        alphaMap:doorAlphaTexture, // 需要设置transparent 为true
        aoMap:doorAmbientOcclusionTexture, // 需要设置uv坐标
        displacementMap:doorHeightTexture, // 白色上升 黑色下降 ,灰色不变 位移图会随顶点移动 ,要设置多个顶点
        displacementScale:0.1, // 让调整缩放距离
        normalMap:doorNormalTexture, // 法线纹理 给人一种细节的折射错觉
        metalnessMap:doorMetalnessTexture, // 金属度
        roughnessMap:doorRoughnessTexture, // 粗糙度

    })
)
// console.log(door.geometry.attributes)
door.geometry.setAttribute(
  'uv2',
  new THREE.Float32BufferAttribute(door.geometry.attributes.uv.array,2)
)
door.position.y = 0.9
door.position.z = 2 + 0.001 // 从物理角度来说 会发生渲染的问题
house.add(door)

// Bushes 灌木丛
const bushGeometry = new THREE.SphereBufferGeometry(1,16,16) // 半径,水平垂直分段数
const bushMaterial = new THREE.MeshStandardMaterial({
    map:grassColorTexture,
    aoMap:grassAmbientOcclusionTexture,
    normalMap:grassNormalTexture,
    roughnessMap:grassRoughnessTexture
})

const bush1 = new THREE.Mesh(bushGeometry, bushMaterial)
bush1.castShadow = true
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.castShadow = true
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.castShadow = true
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.castShadow = true
bush4.scale.set(0.15, 0.15, 0.15)
bush4.position.set(- 1, 0.05, 2.6)

house.add(bush1,bush2,bush3,bush4)

/* 
    Graves
*/
const graves = new THREE.Group()
scene.add(graves)

const graveGeometry = new THREE.BoxBufferGeometry(0.6,0.8,0.1)
const graveMaterial = new THREE.MeshStandardMaterial({color:'#727272'})

for(let i = 0; i< 50; i++){

    // 范围 [-9,9]
    const angle = Math.random() * Math.PI * 2
    const radius = 3 + Math.random() * 6
    const x = Math.cos(angle) * radius
    const z = Math.sin(angle) * radius

    // Create the mesh
    const grave = new THREE.Mesh(
        graveGeometry,
        graveMaterial
    )

    // set Position
    grave.position.set(x,0.3,z)

    // set Rotation
    grave.rotation.z = (Math.random() - 0.5) * 0.4
    grave.rotation.y = (Math.random() - 0.5) * 0.4
    grave.castShadow = true
    // Add to the graves Group
    graves.add(grave)
}


// floor 地板
const floor = new THREE.Mesh(
    new THREE.PlaneBufferGeometry(20,20),
    new THREE.MeshStandardMaterial({
        map:grassColorTexture,
        aoMap:grassAmbientOcclusionTexture,
        normalMap:grassNormalTexture,
        roughnessMap:grassRoughnessTexture
    })
)
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)


/**
 * Lights
 */
// Ambient light
const ambientLight = new THREE.AmbientLight('#b9d5ff', 0.3)
scene.add(ambientLight)

// Directional light
const moonLight = new THREE.DirectionalLight('#b9d5ff', 0.12)
moonLight.castShadow = true
moonLight.shadow.mapSize.width = 256
moonLight.shadow.mapSize.height = 256
moonLight.shadow.camera.far = 15
moonLight.position.set(4, 5, - 2)
scene.add(moonLight)

// const moonLightHelper = new THREE.DirectionalLightHelper(moonLight,0.2)
// scene.add(moonLightHelper)

// Door light
const doorLight = new THREE.PointLight('#ff7d46',1,7)
doorLight.shadow.mapSize.width = 256
doorLight.shadow.mapSize.height = 256
doorLight.shadow.camera.far = 7

doorLight.position.set(0,2.2,2.7)
house.add(doorLight)

/* 
    Ghost
*/
const ghost1 = new THREE.PointLight('#ff00ff', 3, 3)
scene.add(ghost1)

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

const ghost3 = new THREE.PointLight('#ff7800', 3, 3)
scene.add(ghost3)


/* 
    Fog 雾气
*/
const fog = new THREE.Fog('#262837',1,15)
scene.fog = fog

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.x = 4
camera.position.y = 2
camera.position.z = 5
scene.add(camera)

// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas
})

renderer.setClearColor('#262837') // 这样雾气能无缝衔接
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/* 
    Shadows
*/
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
moonLight.castShadow = true
doorLight.castShadow = true
ghost1.castShadow = true
ghost2.castShadow = true
ghost3.castShadow = true
walls.castShadow = true
bush1.castShadow = true
bush2.castShadow = true
bush3.castShadow = true
bush4.castShadow = true

floor.receiveShadow = true // 接收阴影
/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Updata ghost
    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
    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)


    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

二、效果

1.想要实现效果

2.效果

三,注意点

1.Geometry

在几何体中Geometry 和 BufferGeometry要注意一个可以获取uv

注意纹理引入的路径是否正确,不然会很怪

注意属性添加时候,不要缺少或者打错单词 ,不然也很怪


总结

好多方法,百度就可,能记则记,不行拉倒!多动手,少动嘴,看完视频,做个作品集,再来一次练习吧!

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值