three.js学习笔记(五)——Shadows阴影

阴影一直是实时三维渲染的挑战,开发人员必须在合理的情况下找到显示真实阴影的技巧。
Three.js 有一个内置的解决方案,虽然其并不完美,但用起来很方便。

阴影是怎么工作的?

当你进行一次渲染时,Three.js将对每个支持阴影的光线进行渲染,那些渲染会像摄像机那样模拟光线所看到的内容,而在这些灯光渲染下,网格材质将被深度网格材质MeshDepthMaterial所替代。
灯光渲染将像纹理一样被存储起来,称为阴影贴图,之后它们会被用于每个支持接收阴影的材质并投射到几何体上。

激活阴影

1.想要激活并使用阴影,就得先在渲染器renderer.shadowMap.enabled属性中设置开启,允许在场景中使用阴影贴图

renderer.shadowMap.enabled = true

2.检查每个对象,确定它是否可以使用castshadow投射阴影,以及是否可以使用receiveshadow接收阴影。
现在我们的场景里有一个球体和一块平面,光源有环境光和平行光。
在这里插入图片描述
设置球体可以投射阴影,平面可以接收阴影

sphere.castShadow = true
plane.receiveShadow = true

然后使用castShadow激活灯光上的阴影

directionalLight.castShadow = true

注意:只有平行光、点光源和聚光灯支持阴影
在这里插入图片描述

优化阴影贴图

优化渲染尺寸

我们可以在每个灯光的阴影属性中访问阴影贴图

console.log(directionalLight.shadow);

在这里插入图片描述
可以看到,默认的贴图尺寸是512x512,我们可以设置其为2的n次幂,因为这涉及到mip映射。之后会发现当数值越高,阴影拥有越清晰的细节,数值越低,阴影越模糊

directionalLight.shadow.mapSize.width = 1024
directionalLight.shadow.mapSize.height = 1024

近与远

上面说到Three.js使用灯光摄像机进行阴影贴图渲染。这些相机具有相同的属性,像nearfar
为了方便调试,我们可以往场景中添加摄像机辅助对象(摄像机助手),要做的就是把平行光用于渲染阴影的灯光摄像机directionalLight.shadow.camera给添加到摄像机助手中

const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
scene.add(directionalLightCameraHelper)

可以看到红线交叉处是我们的平行光光源,正方形矩形部分是near值,而far处于非常远的地方,这就是性能需要优化的地方
在这里插入图片描述
接下来改变平行光渲染阴影的灯光摄像机可视范围的远近值

directionalLight.shadow.camera.near = 2
directionalLight.shadow.camera.far = 6

虽然阴影没有什么变化,但至少减少了些性能消耗
在这里插入图片描述

振幅amplitude

通过观察上图,使用相机助手后我们可以发现灯光相机所看到的区域还是太大了,溢出了不少。因为我们正在使用的是平行光,它使用的是正交相机OrthographicCamera
所以我们可以通过正交相机的toprightbottomleft四个属性来控制摄像机视锥体的哪一边可以看多远距离。

directionalLight.shadow.camera.top = 2
directionalLight.shadow.camera.right = 2
directionalLight.shadow.camera.left = -2
directionalLight.shadow.camera.bottom = -2

可以观察到现在的阴影跟前面没有调整相机前的阴影相比较起来,细节程度有所提高
在这里插入图片描述
灯光相机的可视范围越小,阴影越精确,当然如果设置得实在太小,阴影将会被裁剪掉

// 当相机far值过小,阴影被裁剪掉
directionalLight.shadow.camera.far = 3.8

在这里插入图片描述

模糊

我们可以通过radius属性控制阴影模糊程度,它不会改变灯光相机与物体的距离。

directionalLight.shadow.radius = 10

在这里插入图片描述

阴影贴图算法

有不同类型的算法可以应用于阴影贴图
THREE.BasicShadowMap-性能非常好但是质量很差
THREE.PCFShadowMap-性能较差但边缘更平滑(默认)
THREE.PCFSoftShadowMap-性能较差但边缘更柔和
THREE.VSMShadowMap-性能差,约束多,但能够产生意想不到的效果。

PCF柔软阴影贴图

// PCF柔软阴影贴图
renderer.shadowMap.type = THREE.PCFSoftShadowMap

radius不会在该类型中生效
在这里插入图片描述

聚光灯阴影

添加聚光灯

// 聚光灯
const spotLight = new THREE.SpotLight(0xffffff,0.4,10,Math.PI*0.3)
spotLight.castShadow = true
spotLight.position.set(0,2,2)
scene.add(spotLight)
// 如果要使聚光灯看向某处记得把target添加场景中
scene.add(spotLight.target)

添加相机助手

const spotLightCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera)
scene.add(spotLightCameraHelper)

可以观察到混合阴影
在这里插入图片描述

优化聚光灯阴影贴图

和前面优化平行光阴影贴图一样。

spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
spotLight.shadow.camera.near = 1
spotLight.shadow.camera.far = 6

但因为它是聚光灯,使用的是透视相机PerspectiveCamera,所以可以通过fov属性改变摄像机视锥体垂直视野角度

spotLight.shadow.camera.fov = 30

在这里插入图片描述

点光源阴影

添加点光源

//点光源
const pointLight = new THREE.PointLight(0xffffff,0.3)
pointLight.castShadow = true
pointLight.position.set(-1,1,0)
scene.add(pointLight)

添加相机助手

const pointLightCameraHelper = new THREE.CameraHelper(pointLight.shadow.camera)
scene.add(pointLightCameraHelper)

在这里插入图片描述

优化点光源阴影

pointLight.shadow.mapSize.width = 1024
pointLight.shadow.mapSize.height = 1024
pointLight.shadow.camera.near = 0.1
pointLight.shadow.camera.far = 5

在这里插入图片描述

点光源摄像机使用的也是透视相机,但最好不要去改变它的视锥体垂直视野角度fov属性

烘焙阴影

烘培阴影是Three.js阴影的一个很好的替代品。我们可以将阴影集成到纹理中,并将其应用到材质上。
先关闭渲染器的阴影贴图渲染,之后就看不到场景中的阴影了

renderer.shadowMap.enabled = false

然后我们设置平面的材质纹理为烘培阴影贴图
在这里插入图片描述
加载纹理贴图

// Textures
const textureLoader = new THREE.TextureLoader()
const bakedShadow = textureLoader.load('/textures/bakedShadow.jpg')

平面使用基础网格材质(MeshBasicMaterial)并应用烘培阴影纹理贴图

const plane = new THREE.Mesh(
    new THREE.PlaneBufferGeometry(5, 5),
    new THREE.MeshBasicMaterial({map:bakedShadow})
)

在这里插入图片描述
这种方案适用于静态物体,因为当物体位置有所变化后,阴影并不会跟着移动。

备选方案

我们还可以使用更简单的烘焙阴影贴图并移动它,使其一直保持在球体下方。

// 加载简单阴影
const simpleShadow = textureLoader.load('/textures/simpleShadow.jpg')

在这里插入图片描述
我们要创建一个略高于地板的平面,把它的材质的alphaMap属性设置为简单阴影纹理贴图,

const sphereShadow = new THREE.Mesh(
    new THREE.PlaneBufferGeometry(1.5,1.5),
    new THREE.MeshBasicMaterial({
        color:0x000000,
        transparent:true,
        alphaMap:simpleShadow
    })
)
sphereShadow.rotation.x = - Math.PI * 0.5
sphereShadow.position.y = plane.position.y + 0.01
scene.add(sphereShadow)

在这里插入图片描述
接着我们为球体添加动画,使其绕着地板平面做圆周运动,并且有在地板触底弹跳的效果

/**
 * Animate
 */
const clock = new THREE.Clock()
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()
    //update sphere animate
    // 圆周运动
    sphere.position.x = Math.sin(elapsedTime)
    sphere.position.z = Math.cos(elapsedTime)
    // 触底弹跳
    sphere.position.y = Math.abs(Math.sin(elapsedTime * 3))

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

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

tick()

接着再设置阴影跟随球体进行位置变换

  //更新球体阴影贴图位置
  //阴影贴图跟随球体
  sphereShadow.position.x = sphere.position.x
  sphereShadow.position.z = sphere.position.z
  //阴影根据球体高度变化,贴图的透明度也有所改变
  //球体距离平面越高,阴影越透明
  sphereShadow.material.opacity = (1 - Math.abs(sphere.position.y)) * 0.3

源代码

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()

// Textures
const textureLoader = new THREE.TextureLoader()
const bakedShadow = textureLoader.load('/textures/bakedShadow.jpg')
const simpleShadow = textureLoader.load('/textures/simpleShadow.jpg')
/**
 * Lights
 */
// Ambient light
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3)
gui
  .add(ambientLight, 'intensity')
  .min(0)
  .max(1)
  .step(0.001)
scene.add(ambientLight)

// Directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
directionalLight.position.set(2, 2, -1)
gui
  .add(directionalLight, 'intensity')
  .min(0)
  .max(1)
  .step(0.001)
gui
  .add(directionalLight.position, 'x')
  .min(-5)
  .max(5)
  .step(0.001)
gui
  .add(directionalLight.position, 'y')
  .min(-5)
  .max(5)
  .step(0.001)
gui
  .add(directionalLight.position, 'z')
  .min(-5)
  .max(5)
  .step(0.001)
scene.add(directionalLight)
directionalLight.castShadow = true
directionalLight.shadow.mapSize.width = 1024
directionalLight.shadow.mapSize.height = 1024
directionalLight.shadow.camera.near = 2
directionalLight.shadow.camera.far = 6
directionalLight.shadow.camera.top = 2
directionalLight.shadow.camera.right = 2
directionalLight.shadow.camera.left = -2
directionalLight.shadow.camera.bottom = -2
directionalLight.shadow.radius = 10

// console.log(directionalLight.shadow);

const directionalLightCameraHelper = new THREE.CameraHelper(
  directionalLight.shadow.camera
)
directionalLightCameraHelper.visible = false
scene.add(directionalLightCameraHelper)

// 聚光灯
const spotLight = new THREE.SpotLight(0xffffff, 0.3, 10, Math.PI * 0.3)
spotLight.castShadow = true
spotLight.position.set(0, 2, 2)
scene.add(spotLight)
scene.add(spotLight.target)
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
spotLight.shadow.camera.fov = 30
spotLight.shadow.camera.near = 1
spotLight.shadow.camera.far = 6

const spotLightCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera)
spotLightCameraHelper.visible = false
scene.add(spotLightCameraHelper)

//点光源
const pointLight = new THREE.PointLight(0xffffff, 0.3)
pointLight.castShadow = true
pointLight.position.set(-1, 1, 0)
pointLight.shadow.mapSize.width = 1024
pointLight.shadow.mapSize.height = 1024
pointLight.shadow.camera.near = 0.1
pointLight.shadow.camera.far = 5

scene.add(pointLight)

const pointLightCameraHelper = new THREE.CameraHelper(pointLight.shadow.camera)
pointLightCameraHelper.visible = false
scene.add(pointLightCameraHelper)

/**
 * Materials
 */
const material = new THREE.MeshStandardMaterial()
material.roughness = 0.7
gui
  .add(material, 'metalness')
  .min(0)
  .max(1)
  .step(0.001)
gui
  .add(material, 'roughness')
  .min(0)
  .max(1)
  .step(0.001)

/**
 * Objects
 */
const sphere = new THREE.Mesh(
  new THREE.SphereBufferGeometry(0.5, 32, 32),
  material
)
sphere.castShadow = true

const plane = new THREE.Mesh(new THREE.PlaneBufferGeometry(5, 5), material)
plane.rotation.x = -Math.PI * 0.5
plane.position.y = -0.5

plane.receiveShadow = true

scene.add(sphere, plane)

const sphereShadow = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1.5, 1.5),
  new THREE.MeshBasicMaterial({
    color: 0x000000,
    transparent: true,
    alphaMap: simpleShadow,
  })
)
sphereShadow.rotation.x = -Math.PI * 0.5
sphereShadow.position.y = plane.position.y + 0.01
scene.add(sphereShadow)
/**
 * 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 = 1
camera.position.y = 1
camera.position.z = 2
scene.add(camera)

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

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

renderer.shadowMap.enabled = false
renderer.shadowMap.type = THREE.PCFSoftShadowMap
/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () => {
  const elapsedTime = clock.getElapsedTime()
  //更新球体位置
  //设置圆周运动轨迹
  sphere.position.x = Math.sin(elapsedTime) * 1.5
  sphere.position.z = Math.cos(elapsedTime) * 1.5
  //设置触底弹跳效果
  sphere.position.y = Math.abs(Math.sin(elapsedTime * 3))

  //更新球低阴影贴图位置
  //阴影贴图跟随球体
  sphereShadow.position.x = sphere.position.x
  sphereShadow.position.z = sphere.position.z
  //阴影根据球体高度变化,贴图的透明度也有所改变
  //球体距离平面越高,阴影越透明
  sphereShadow.material.opacity = (1 - Math.abs(sphere.position.y)) * 0.3
  // Update controls
  controls.update()

  // Render
  renderer.render(scene, camera)

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

tick()

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【视频教程】Hands-on Three.js 3D Web Visualisations-September 16, 2019.part3.rar Hands-on Three.js 3D Web Visualisations September 16, 2019 English | MP4 | AVC 1920×1080 | AAC 48KHz 2ch | 3h 46m | 968 MB eLearning | Skill level: All Levels Create stunning visualizations and 3D scenes using the Three.js library Three.js is the most popular JavaScript library for displaying 3D content on the web, giving you the power to display incredible models, games, music videos, and scientific/data visualizations in your browser and even on your smartphone! This course begins with a 3D beginner-level primer to 3D concepts and some basic examples to get you started with the most important features that Three.js has to offer. You’ll learn how to quickly create a scene, camera, and renderer and how to add meshes using the Geometry primitives included with the library. You’ll explore troubleshooting steps that will focus on some of the common pitfalls developers face. You’ll learn the very sophisticated animation system included with the library. The course concludes by introducing post-processing, essentially adding filters to your rendered scene, and GLSL, the shading language that is used by all materials included with the library. You’ll see how creating your materials is easier than you’d imagine using GLSL. By the end of this course, you’ll be able to quickly add advanced features to your 3D scenes, improve the way users interact with them, and make them look stunning. Learn Learn the basics of 3D applications: vertices, faces, meshes, cameras, and renderers Learn how to set up a Three.js web app: the scene, camera, and renderer Master the scene hierarchy and child-parent relationships, and how they affect the final location and orientation of objects Explore simple mesh shapes (such as boxes, spheres, cylinders, planes, and cones) using the Three.js library Learn how to source, create, and load complex assets, including textures Discover how to use the brilliant animation system that is part of the THREE.js library Add a post-processor to a rendered image, to make it look like an old film or a dot screenprint + Table of Contents 1 The Course Overview 2 Introducing the THREE.js website 3 D Basics 4 Your first THREE.js web page` 5 The THREE.js Editor 6 Debugging Your Pages 7 Let’s Keep It Simple – Starting with a Box 8 Materials One – Basic and Wireframe 9 Spheres and Cylinders 10 Materials Two – Lambert and Phong 11 Cones and Tori 12 Scene Hierarchy 13 Perspective Camera 14 Orthographic Camera 15 Dummy Cameras and Lerping 16 Complex Camera Paths 17 Ambient and Hemisphere Lighting 18 Directional and Point Lighting 19 Spot and RectArea Lighting 20 Adding Shadows to Your Scenes 21 Physically Correct Lighting 22 Online Sources of 3D Assets 23 Using Blender with THREE.js 24 The GLTFLoader Class 25 The FBXLoader Class 26 LatheGeometry and ExtrudeGeometry 27 The Basics of the Animation System 28 Skinned Meshes 29 Switching and Blending Animations 30 Splitting an Animation Clip 31 A WASD Control System for a Player Character 32 THREE.js Post Processing 33 Introducing GLSL – ShaderMaterial 34 Introducing GLSL – Vertex Shaders 35 Introducing GLSL – Importance of Noise Function 36 Introducing GLSL – Textures
【视频教程】Hands-on Three.js 3D Web Visualisations-September 16, 2019.part1.rar Hands-on Three.js 3D Web Visualisations September 16, 2019 English | MP4 | AVC 1920×1080 | AAC 48KHz 2ch | 3h 46m | 968 MB eLearning | Skill level: All Levels Create stunning visualizations and 3D scenes using the Three.js library Three.js is the most popular JavaScript library for displaying 3D content on the web, giving you the power to display incredible models, games, music videos, and scientific/data visualizations in your browser and even on your smartphone! This course begins with a 3D beginner-level primer to 3D concepts and some basic examples to get you started with the most important features that Three.js has to offer. You’ll learn how to quickly create a scene, camera, and renderer and how to add meshes using the Geometry primitives included with the library. You’ll explore troubleshooting steps that will focus on some of the common pitfalls developers face. You’ll learn the very sophisticated animation system included with the library. The course concludes by introducing post-processing, essentially adding filters to your rendered scene, and GLSL, the shading language that is used by all materials included with the library. You’ll see how creating your materials is easier than you’d imagine using GLSL. By the end of this course, you’ll be able to quickly add advanced features to your 3D scenes, improve the way users interact with them, and make them look stunning. Learn Learn the basics of 3D applications: vertices, faces, meshes, cameras, and renderers Learn how to set up a Three.js web app: the scene, camera, and renderer Master the scene hierarchy and child-parent relationships, and how they affect the final location and orientation of objects Explore simple mesh shapes (such as boxes, spheres, cylinders, planes, and cones) using the Three.js library Learn how to source, create, and load complex assets, including textures Discover how to use the brilliant animation system that is part of the THREE.js library Add a post-processor to a rendered image, to make it look like an old film or a dot screenprint + Table of Contents 1 The Course Overview 2 Introducing the THREE.js website 3 D Basics 4 Your first THREE.js web page` 5 The THREE.js Editor 6 Debugging Your Pages 7 Let’s Keep It Simple – Starting with a Box 8 Materials One – Basic and Wireframe 9 Spheres and Cylinders 10 Materials Two – Lambert and Phong 11 Cones and Tori 12 Scene Hierarchy 13 Perspective Camera 14 Orthographic Camera 15 Dummy Cameras and Lerping 16 Complex Camera Paths 17 Ambient and Hemisphere Lighting 18 Directional and Point Lighting 19 Spot and RectArea Lighting 20 Adding Shadows to Your Scenes 21 Physically Correct Lighting 22 Online Sources of 3D Assets 23 Using Blender with THREE.js 24 The GLTFLoader Class 25 The FBXLoader Class 26 LatheGeometry and ExtrudeGeometry 27 The Basics of the Animation System 28 Skinned Meshes 29 Switching and Blending Animations 30 Splitting an Animation Clip 31 A WASD Control System for a Player Character 32 THREE.js Post Processing 33 Introducing GLSL – ShaderMaterial 34 Introducing GLSL – Vertex Shaders 35 Introducing GLSL – Importance of Noise Function 36 Introducing GLSL – Textures

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值