接着上一篇的项目,在上一篇中物体的材质都是用的MeshBasicMaterial这种材质,这种材质是不受光照的影响的,因此要修改为MeshPhongMaterial这种材质,让它受光照的影响。html
在cylinder.js 和 box.js中将MeshBasicMaterial改成MeshPhongMaterialgit
const material = new THREE.MeshPhongMaterial({
color: 0xffffff
})
复制代码
这时候就渲染结果就是这个样子:github
物体都看不见了,由于没有光面试
官方文档是这样解释环境光的:canvas
环境光会均匀的照亮场景中的全部物体。环境光不能用来投射阴影,由于它没有方向。api
能够理解为从各个方向照向物体的光bash
在renderer目录下,新建light.js工具
const light = () => {
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.8)
}
}
export default light
复制代码
new THREE.AmbientLight
第一个参数是光的颜色,第二个参数是光强。测试
scene.jsui
import light from './light'
const scene = () => {
const sceneInstance = new THREE.Scene()
const axesHelper = new THREE.AxesHelper(100)
const lights = light()
Object.keys(lights).forEach(value => {
sceneInstance.add(lights[value])
})
sceneInstance.add(axesHelper)
return sceneInstance
}
export default scene
复制代码
效果:
如今物体就能够看见了,因为环境光不能投射阴影,因此须要其余的光,下面试下其余光的效果
平行光就是从光源处到物体的光线都是平行的,官方文档上说相似太阳光,它是能够产生阴影的。
light.js
const light = () => {
const directionalLight = new THREE.DirectionalLight(0xFF8C00)
directionalLight.position.set(10, 30, 20) // 设置光源的位置
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.5),
directionalLight
}
}
export default light
复制代码
看下效果:
由于不加环境光太暗了,因此仍是在有环境光的状况下测试,这时候就能看出来物体不一样的面受到的光照强度是不一样的。
并无从文档上看懂半球光是什么。。先记住一点半球光不能产生阴影,把以前的平行光改为半球光:
light.js
const light = () => {
const hemisphereLight = new THREE.HemisphereLight(0x8A2BE2, 0xFFFAFA, 1)
hemisphereLight.position.set(10, 30, 20) // 设置光源的位置
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.5),
hemisphereLight
}
}
export default light
复制代码
效果:
这篇文章给了详细的例子,能够看看。地址
这个好理解,就像蜡烛或者灯泡发出的光线。
light.js
const light = () => {
const pointLight = new THREE.PointLight(0x1a94bc, 1, 100)
pointLight.position.set(10, 30, 20) // 设置光源的位置
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.5),
pointLight
}
}
export default light
复制代码
看起来和平行光也很像
可是这个点光源是有一个衰减值的,就是光源离物体越远,光的强度就越低,平行光不会,距离再远也是相同的光强
这个从名字上就能理解,就是从一个平面发出平行光。
light.js
const light = () => {
const pointLight = new THREE.PointLight(0x1a94bc, 1, 100, 100)
pointLight.position.set(10, 30, 20) // 设置光源的位置
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.5),
pointLight
}
}
export default light
复制代码
这种光只支持 MeshStandardMaterial 和 MeshPhysicalMaterial 两种材质,因此还得去改一下box.js 和 cylinder.js 中物体的材质
const material = new THREE.MeshStandardMaterial({
color: 0xffffff
})
复制代码
和平行光的效果差很少
这个在官方文档上有个很好的例子,一看就明白了,这里就不写了。
看了效果以后决定选择平行光,由于能产生阴影,效果也还能够,最终light.js的代码:
const light = () => {
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
directionalLight.position.set(10, 30, 20) // 设置光源的位置
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.8),
directionalLight
}
}
export default light
复制代码
记得把box.js 和 cylinder.js 中的材质改回来(MeshPhongMaterial)
实现阴影效果须要具有如下条件:
如今就一步一步实现上面的条件:
renderer -> index.js 添加
init () {
this.instance.shadowMap.enabled = true // 新增,渲染器启用阴影
}
复制代码
上面说过平行光是能够投射阴影的,因此用它就行了。
ligth.js
const light = () => {
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
directionalLight.position.set(10, 30, 20) // 设置光源的位置
directionalLight.castShadow = true // 新增,产生动态阴影
directionalLight.shadow.camera.near = 0.5 // 新增
directionalLight.shadow.camera.far = 500 // 新增
directionalLight.shadow.camera.left = -100 // 新增
directionalLight.shadow.camera.right = 100 // 新增
directionalLight.shadow.camera.top = 100 // 新增
directionalLight.shadow.camera.bottom = -100 // 新增
directionalLight.shadow.mapSize.width = 1024 // 新增
directionalLight.shadow.mapSize.height = 1024 // 新增
return {
ambientLight: new THREE.AmbientLight(0xffffff, 0.8),
directionalLight
}
}
export default light
复制代码
物体使用的是MeshPhongMaterial材质,这个材质是能够产生阴影的,这里就不用改了
在cylinder.js 和 box.js 中分别添加以下代码:
create () {
this.instance.castShadow = true // 新增,让物体能够投射阴影
this.instance.receiveShadow = true // 新增,让物体能够接受阴影
}
复制代码
接受阴影的物体
在现实生活中接受阴影的物体最多见的就是地面了,这里也是同样,须要给如今的场景添加一个地面
新建ground.js
const ground = () => {
const groundGeometry = new THREE.PlaneGeometry(200, 200)
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff
})
const instance = new THREE.Mesh(groundGeometry, groundMaterial)
instance.receiveShadow = true
return instance
}
export default ground
复制代码
如今去看看效果:
这个效果炸了呀。
ground变成了墙而不是地面。因此须要旋转一下
让他沿着x轴转负90度
instance.rotateX(-Math.PI / 2)
复制代码
效果:
如今就和x轴在同一平面了,只是有点高,把物体切断了,再往下移一点
instance.position.y = -5
复制代码
如今的效果是:
因为这样的地面太丑,如今不用MeshStandardMaterial这个材质了,改用ShadowMaterial材质,文档
ground.js
const ground = () => {
const groundGeometry = new THREE.PlaneGeometry(200, 200)
const groundMaterial = new THREE.ShadowMaterial({
color: 0x000000,
opacity: 0.3
})
const instance = new THREE.Mesh(groundGeometry, groundMaterial)
instance.rotateX(-Math.PI / 2)
instance.position.y = -5
instance.receiveShadow = true
return instance
}
export default ground
复制代码
改完以后如今整个地面又变黑了,暂时不用管,加上背景就行了
背景应该和相机看到的范围同样大,因此背景的尺寸要用到相机相关的变量size,这里为了方便维护,把这个变量单独存放:
camera_config.js
export default {
size: 30
}
复制代码
新建background.js
import cameraConfig from '../../configs/camera_config'
const background = () => {
const backgroundGeometry = new THREE.PlaneGeometry(
cameraConfig.size * 2,
window.innerHeight / window.innerWidth * cameraConfig.size * 2
)
const backgroundMaterial = new THREE.MeshBasicMaterial({
color: 0xd7dbe6
})
const instance = new THREE.Mesh(backgroundGeometry, backgroundMaterial)
instance.position.z = -85
return instance
}
export default background
复制代码
camera.js修改成下面这样
import cameraConfig from '../../configs/camera_config' // 新增
const camera = () => {
const ratio = window.innerHeight / window.innerWidth
const { size } = cameraConfig // 改动
// 设置相机可看到范围
const cameraInstance = new THREE.OrthographicCamera(
-size, size, size * ratio, -size * ratio, -100, 85 // 改动
)
cameraInstance.position.set(-10, 10, 10) // 设置相机位置
cameraInstance.up.set(0, 1, 0)
cameraInstance.lookAt(new THREE.Vector3(0, 0, 0)) // 设置相机位置从0, 0, 0望向0, 0, 0
return cameraInstance
}
export default camera
复制代码
renderer -> index.js
import camera from './camera'
import scene from './scene'
import background from '../objects/background' // 新增
class Renderer {
constructor () {
this.camera = null
this.scene = null
}
init () {
this.camera = camera()
this.scene = scene()
const { width, height } = canvas
if (window.devicePixelRatio) {
canvas.height = height * window.devicePixelRatio
canvas.width = width * window.devicePixelRatio
}
this.instance = new THREE.WebGLRenderer({
canvas,
antialias: true
})
this.instance.shadowMap.enabled = true
this.scene.add(this.camera) // 新增,这一步很重要
this.camera.add(background()) // 新增
}
render () {
this.instance.render(this.scene, this.camera)
}
}
export default new Renderer()
复制代码
去开发者工具看看效果:
如今就比较正常了。
完整代码GitHub
接下来就是实现跳一跳中的小人了