案例查看地址:http://www.wjceo.com/blog/threejs/2018-05-03/157.html
简介
之前的阴影效果都是通过特定的法向贴图或者凹凸贴图再或者使用Three.js
渲染生成的,这一节,我们使用另一种方法来创建阴影,也就是使用光照贴图。光照贴图是预先渲染好的的阴影,来模拟真实的阴影。
这种光照贴图的好处是我们再不损害渲染的性能的同时,还能够模拟出真实的阴影效果。缺点是,阴影的位置不会跟随模型的移动而改变。
案例实现
在这个案例当中,我们会看到非常真实的阴影效果。刚开始我是把两个立方体隐藏掉的,为了展现出光照贴图的效果。我将两个立方体隐藏掉,需要显示请点击右上角的showBox
,实现方法很简单,将光照贴图赋值给linghtMap
:
//创建平面图形的material
var groundGeom = new THREE.PlaneGeometry(10, 10, 1, 1);
var lightMap = new THREE.TextureLoader().load('/lib/assets/textures/lightmap/lm-1.png');
var map = new THREE.TextureLoader().load('/lib/assets/textures/general/floor-wood.jpg');
material = new THREE.MeshLambertMaterial(
{
color: 0x777777,
lightMap: lightMap,
map: map
});
//为了保证光照贴图能够正常显示,使用正常纹理的vu映射的值
groundGeom.faceVertexUvs[1] = groundGeom.faceVertexUvs[0];
//将平面图形添加到场景当中
var groundMesh = new THREE.Mesh(groundGeom, material);
groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);
为了能够保证当前的光照贴图纹理能够正常显示,我们使用的正常的贴图纹理的uv
映射的值。 lightMapIntensity
属性可以修改贴图纹理对模型的影响,默认值为1。我们直接写到了gui
调试器里面,还有显示立方体的方法:
//初始化dat.GUI简化试验流程
function initGui() {
//声明一个保存需求修改的相关数据的对象
gui = {
showBox:function () {
//创建两个box显示出来
var material = new THREE.MeshPhongMaterial({color:0x00ffff});
//创建大的立方体
var geometryBig = new THREE.CubeGeometry(1.2,1.2,1.2);
var cubeBig = new THREE.Mesh(geometryBig, material);
cubeBig.position.y += 0.6;
scene.add(cubeBig);
//创建小的立方体
var geometrySm = new THREE.CubeGeometry(0.7, 0.7, 0.7);
var cubeSm = new THREE.Mesh(geometrySm, material);
cubeSm.position.set(-1.4, 0.35, 0);
scene.add(cubeSm);
},
lightMapIntensity:1
};
var datGui = new dat.GUI();
//将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
datGui.add(gui, "showBox");
datGui.add(gui, "lightMapIntensity", 0, 5).onChange(function (e) {
material.lightMapIntensity = e;
});
}
案例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css"> html, body { margin: 0; height: 100%; } canvas { display: block; } </style>
</head>
<body onload="draw();">
</body>
<script src="https://cdn.bootcss.com/three.js/91/three.min.js"></script>
<script src="/lib/js/utils/ImageUtils.js"></script>
<script src="/lib/js/controls/OrbitControls.js"></script>
<script src="https://cdn.bootcss.com/stats.js/r17/Stats.min.js"></script>
<script src="https://cdn.bootcss.com/dat-gui/0.7.1/dat.gui.min.js"></script>
<script src="/lib/js/Detector.js"></script>
<script> var renderer, camera, scene, gui, light, stats, controls, mesh, material; function initRender() { renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0xeeeeee); renderer.shadowMap.enabled = true; //告诉渲染器需要阴影效果 document.body.appendChild(renderer.domElement); } function initCamera() { camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); camera.position.set(0, 12, 15 ); } function initScene() { scene = new THREE.Scene(); scene.background = new THREE.Color( 0xa0a0a0 ); scene.fog = new THREE.Fog( 0xa0a0a0, 50, 500 ); } //初始化dat.GUI简化试验流程 function initGui() { //声明一个保存需求修改的相关数据的对象 gui = { showBox:function () { //创建两个box显示出来 var material = new THREE.MeshPhongMaterial({color:0x00ffff}); //创建大的立方体 var geometryBig = new THREE.CubeGeometry(1.2,1.2,1.2); var cubeBig = new THREE.Mesh(geometryBig, material); cubeBig.position.y += 0.6; scene.add(cubeBig); //创建小的立方体 var geometrySm = new THREE.CubeGeometry(0.7, 0.7, 0.7); var cubeSm = new THREE.Mesh(geometrySm, material); cubeSm.position.set(-1.4, 0.35, 0); scene.add(cubeSm); }, lightMapIntensity:1 }; var datGui = new dat.GUI(); //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值) datGui.add(gui, "showBox"); datGui.add(gui, "lightMapIntensity", 0, 5).onChange(function (e) { material.lightMapIntensity = e; }); } function initLight() { scene.add(new THREE.AmbientLight(0x444444)); light = new THREE.DirectionalLight(0xffffff); light.position.set(-20, 20, -20 ); light.castShadow = true; light.shadow.camera.top = 10; light.shadow.camera.bottom = -10; light.shadow.camera.left = -10; light.shadow.camera.right = 10; //告诉平行光需要开启阴影投射 light.castShadow = true; scene.add(light); } function initModel() { //辅助工具 var helper = new THREE.AxesHelper(50); scene.add(helper); //创建平面图形的material var groundGeom = new THREE.PlaneGeometry(10, 10, 1, 1); var lightMap = new THREE.TextureLoader().load('/lib/assets/textures/lightmap/lm-1.png'); var map = new THREE.TextureLoader().load('/lib/assets/textures/general/floor-wood.jpg'); material = new THREE.MeshLambertMaterial( { color: 0x777777, lightMap: lightMap, map: map }); //为了保证光照贴图能够正常显示,使用正常纹理的vu映射的值 groundGeom.faceVertexUvs[1] = groundGeom.faceVertexUvs[0]; //将平面图形添加到场景当中 var groundMesh = new THREE.Mesh(groundGeom, material); groundMesh.rotation.x = -Math.PI / 2; scene.add(groundMesh); } //初始化性能插件 function initStats() { stats = new Stats(); document.body.appendChild(stats.dom); } function initControls() { controls = new THREE.OrbitControls(camera, renderer.domElement); //设置控制器的中心点 //controls.target.set( 0, 5, 0 ); // 如果使用animate方法时,将此函数删除 //controls.addEventListener( 'change', render ); // 使动画循环使用时阻尼或自转 意思是否有惯性 controls.enableDamping = true; //动态阻尼系数 就是鼠标拖拽旋转灵敏度 //controls.dampingFactor = 0.25; //是否可以缩放 controls.enableZoom = true; //是否自动旋转 controls.autoRotate = false; controls.autoRotateSpeed = 0.5; //设置相机距离原点的最远距离 controls.minDistance = 1; //设置相机距离原点的最远距离 controls.maxDistance = 2000; //是否开启右键拖拽 controls.enablePan = true; } function render() { } //窗口变动触发的函数 function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { //更新控制器 render(); //更新性能插件 stats.update(); controls.update(); renderer.render(scene, camera); requestAnimationFrame(animate); } function draw() { //兼容性判断 if (!Detector.webgl) Detector.addGetWebGLMessage(); initGui(); initRender(); initScene(); initCamera(); initLight(); initModel(); initControls(); initStats(); animate(); window.onresize = onWindowResize; } </script>
</html>