个人博客地址: https://cxx001.gitee.io
前言
没有光源,渲染场景将不可见。threejs中已经帮我们实现了大量的光源,我们可以直接使用,主要分为两类:基础光源和特殊光源,下面将依次详细介绍。
基础光源
1. THREE.AmbientLight(环境光)
该光源颜色会应用到场景全局,光源没有特别的来源方向,并且不会生成阴影。通常,不会将环境光作为场景中唯一的光源,因为它会将场景中的所有物体渲染为相同颜色。一般和其它光源一起使用,它只是为了弱化阴影或给场景添加一些额外的颜色,所以它的用色应该尽量保守,如果设置的颜色过于明亮,那么画面颜色就过于饱和了。
下面示例帮助你理解,试试改变AmbientLight光颜色或开启/关闭spotLight光源,观察场景变化。
<!-- chapter-03-01.html -->
<!DOCTYPE html>
<html>
<head>
<title>Ambient Light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
// 初始化fps显示插件
var stats = initStats();
// 创建场景对象
var scene = new THREE.Scene();
// 创建透视投影摄像机
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0)); // 设置背景颜色
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染窗口大小
renderer.shadowMapEnabled = true; // 激活阴影
// 创建平面
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true; // 接收阴影
// 设置平面位置
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);
// 创建立方体对象
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true; // 投射阴影
// 设置立方体位置
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);
// 创建球体
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
// 设置球体位置
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true; // 投射阴影
scene.add(sphere);
// 设置摄像机位置
camera.position.x = -25;
camera.position.y = 30;
camera.position.z = 25;
// 设置摄像机镜头朝向
camera.lookAt(new THREE.Vector3(10, 0, 0));
// 创建环境光
var ambiColor = "#0c0c0c"; // 使用的是比较暗淡的灰色
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// 创建聚光灯并开启投射阴影
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// 将渲染器添加到DOM里显示
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// 设置dat.GUI插件显示属性
var controls = new function () {
this.ambientColor = ambiColor; // 环境光颜色
this.disableSpotlight = false; // 聚光灯是否可见
};
// 将添加的控制属性添加到gui界面
var gui = new dat.GUI();
// 颜色设置
gui.addColor(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new THREE.Color(e);
});
// 开关设置
gui.add(controls, 'disableSpotlight').onChange(function (e) {
spotLight.visible = !e; // 聚光灯是否可见
});
// 开始渲染
render();
var step = 0;
function render() {
// 更新fps显示
stats.update();
// 旋转立方体
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
// 跳动球体
step += 0.03;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// 重新渲染
requestAnimationFrame(render);
renderer.render(scene, camera);
}
// 初始化fps显示插件
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
效果:
2. THREE.PointLight(点光源)
点光源发射光线示意图:
从特定的一点向所有方向发射光线。夜空中的照明弹就是一个很好的点光源例子。
threejs里的点光源也没有阴影,因为它朝所有方向发射光线,这种情况下计算阴影对GPU是一个沉重的负担。
下面我们依旧通过示例来理解,这是一个点光源绕场景移动,为了看到点光源位置,我们让一个橙色的小球沿着相同的轨迹移动。你可以看到随着光源的移动,红色的方块和蓝色的球被这个光源从不同的侧面照亮。
<!-- chapter-03-02.html -->
<!DOCTYPE html>
<html>
<head>
<title>point Light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
// renderer.shadowMapEnabled = true; // 点光源没有阴影
var planeGeometry = new THREE.PlaneGeometry(60, 20, 20, 20);
var planeMaterial = new THREE.MeshPhongMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff7777});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// cube.castShadow = true;
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true;
scene.add(sphere);
camera.position.x = -25;
camera.position.y = 30;
camera.position.z = 25;
camera.lookAt(new THREE.Vector3(10, 0, 0)); // 镜头照射位置
// 添加环境光,弱的灰色,为了柔化场景
var ambiColor = "#0c0c0c";
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// 添加点光源
var pointColor = "#ccffcc";
var pointLight = new THREE.PointLight(pointColor);
pointLight.distance = 100; // 光源照射距离
scene.add(pointLight);
var sphereLight = new THREE.SphereGeometry(0.2);
var sphereLightMaterial = new THREE.MeshBasicMaterial({color: 0xac6c25});
var sphereLightMesh = new THREE.Mesh(sphereLight, sphereLightMaterial);
// sphereLightMesh.castShadow = true;
sphereLightMesh.position = new THREE.Vector3(3, 0, 3);
scene.add(sphereLightMesh);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
var controls = new function () {
this.ambientColor = ambiColor; // 环境光颜色
this.pointColor = pointColor; // 点光源颜色
this.intensity = 1; // 光照强度
this.distance = 100; // 光照距离
};
var gui = new dat.GUI();
gui.addColor(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new THREE.Color(e);
});
gui.addColor(controls, 'pointColor').onChange(function (e) {
pointLight.color = new THREE.Color(e);
});
gui.add(controls, 'intensity', 0, 3).onChange(function (e) {
pointLight.intensity = e;
});
gui.add(controls, 'distance', 0, 100).onChange(function (e) {
pointLight.distance = e;
});
render();
var step = 0;
var invert = 1;
var phase = 0;
function render() {
stats.update();
cube.rotation.x += 0.03;
cube.rotation.y += 0.03;
cube.rotation.z += 0.03;
step += 0.03;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// 模拟光源移动
if (phase > 2 * Math.PI) {
invert = invert * -1;
phase -= 2 * Math.PI;
} else {
phase += 0.03;
}
sphereLightMesh.position.z = +(7 * (Math.sin(phase)));
sphereLightMesh.position.x = +(14 * (Math.cos(phase)));
sphereLightMesh.position.y = 5;
if (invert < 0) {
var pivot = 14;
sphereLightMesh.position.x = (invert * (sphereLightMesh.position.x - pivot)) + pivot;
}
// 光源和球体一起移动
pointLight.position.copy(sphereLightMesh.position);
// 重新渲染
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
所使用的点光源属性:
属性 | 描述 |
---|---|
color(颜色) | 光源的颜色 |
distance(距离) | 光源照射的距离。默认值为0,这意味着光的强度不会随着距离增加而减少 |
intensity(强度) | 光源照射的强度。默认值1 |
position(位置) | 光源在场景中的位置 |
visible(是否可见) | 默认为true,光源会打开;如果设置为false,光源就会关闭 |
效果:
3. THREE.SpotLight(聚光灯)
聚光灯发射光线示意图:
从特定的一点发射锥形形状的光线,这是最常使用的光源之一,特别是你想要使用阴影的话。该光源具有产生光的方向和角度,类似手电筒。
聚光灯光源的所有属性:
属性 | 描述 |
---|---|
angle(角度) | 光源发射出的光束的宽度。默认是Math.PI/3 |
castShadow(投影) | 如果设置为true,这个光源就会投射阴影 |
color(颜色) | 光源颜色 |
distance(距离) | 光源照射的距离。默认值为0,这意味着光的强度不会随着距离增加而减少 |
intensity(强度) | 光源照射的强度。默认值1 |
exponent(光强衰减指数) | 发射的光线强度随着光源距离的增加而减弱,这个属性决定了光线强度递减的速度。使用小值,从光源发出的光线可以达到远处的物体,而使用大值,光线仅能到达非常接近光源的物体。 |
onlyShadow(仅阴影) | 如果此属性设置为true,则该光源只生成阴影,不会在场景中添加任何光照 |
position(位置) | 光源在场景中的位置 |
shadowBias(阴影偏移) | 用来偏移阴影位置。当你使用非常薄的对象时,可能出现奇怪的渲染失真现象,如果你看到奇怪的阴影效果,将该属性设置为很小的值(如0.01)通过可以解决问题。此属性默认值为0 |
shadowCameraFar(投影远点) | 到距离光源的哪一个位置可以生成阴影。默认值为5000(这几个参考投射投影摄像机理解) |
shadowCameraNear(投影近点) | 从距离光源的哪一个位置开始生成阴影。默认值为50 |
shadowCameraFov(投影视场) | 用于生成阴影的视场有多大。默认值为50 |
shadowCameraVisible(投影方式是否可见) | 如果设置为true,可以看到光源在哪里以及如何生成阴影的(看到光源照射锥体)。默认值为false |
shadowDarkness(投影暗度) | 定义阴影的暗度。在场景渲染后无法修改。默认值为0.5 |
shadowMapWidth和shadowMapHeight(阴影映射的宽/高) | 决定有多少像素用来生成阴影。**当阴影有锯齿或看起来不光滑时,可以增加这个值。**在场景渲染后无法修改。两者默认值为512 |
target(目标) | 可以将光源指向场景中特定的对象或位置(位置需要是THREE.Object3Dd对象,不是THREE.Vectors3对象)。 |
visible() | 默认为true,光源会打开;如果设置为false,光源就会关闭 |
<!-- chapter-03-03.html -->
<!DOCTYPE html>
<html>
<head>
<title>Spot Light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
// 是否停止移动光源
var stopMovingLight = false;
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFShadowMap; // 这是默认值,如果设置为PCFSoftShadowMap则会使阴影更柔和
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff3333});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true;
scene.add(sphere);
camera.position.x = -35;
camera.position.y = 30;
camera.position.z = 25;
camera.lookAt(new THREE.Vector3(10, 0, 0));
// 添加环境光
var ambiColor = "#1c1c1c";
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// 添加1个聚光灯光源,设置位置和照射方向(和摄像机设置类似), 这个固定照射地面
var spotLight0 = new THREE.SpotLight(0xcccccc);
spotLight0.position.set(-40, 30, -10);
spotLight0.lookAt(plane);
scene.add(spotLight0);
// 添加另一个聚光灯光源,和小球一起运动
//var target = new THREE.Object3D();
//target.position = new THREE.Vector3(5, 0, 0);
var pointColor = "#ffffff";
var spotLight = new THREE.SpotLight(pointColor);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
spotLight.shadowCameraNear = 2;
spotLight.shadowCameraFar = 200;
spotLight.shadowCameraFov = 30;
spotLight.target = plane; // 照射方向,可以是这里的网格物体,也可以是上面的target具体某点对象
spotLight.distance = 0;
spotLight.angle = 0.4;
scene.add(spotLight);
var sphereLight = new THREE.SphereGeometry(0.2);
var sphereLightMaterial = new THREE.MeshBasicMaterial({color: 0xac6c25});
var sphereLightMesh = new THREE.Mesh(sphereLight, sphereLightMaterial);
sphereLightMesh.castShadow = true;
sphereLightMesh.position = new THREE.Vector3(3, 20, 3);
scene.add(sphereLightMesh);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
var step = 0;
var invert = 1;
var phase = 0;
var controls = new function () {
this.ambientColor = ambiColor;
this.pointColor = pointColor;
this.intensity = 1; // 光照强度
this.distance = 0; // 光照距离
this.exponent = 30; // 随着距离光源越远,光强度衰减指数
this.angle = 0.1; // 光源视场角度
this.debug = false; // 是否开启调试模式(打开投影方式可见)
this.castShadow = true; // 是否投射阴影,false则不会产生阴影
this.onlyShadow = false; // 是否只生成阴影,true则不会在场景中产生光照
this.target = "Plane"; // 光源照射目标
this.stopMovingLight = false; // 是否停止移动光源
};
var gui = new dat.GUI();
gui.addColor(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new THREE.Color(e);
});
gui.addColor(controls, 'pointColor').onChange(function (e) {
spotLight.color = new THREE.Color(e);
});
gui.add(controls, 'angle', 0, Math.PI * 2).onChange(function (e) {
spotLight.angle = e;
});
gui.add(controls, 'intensity', 0, 5).onChange(function (e) {
spotLight.intensity = e;
});
gui.add(controls, 'distance', 0, 200).onChange(function (e) {
spotLight.distance = e;
});
gui.add(controls, 'exponent', 0, 100).onChange(function (e) {
spotLight.exponent = e;
});
gui.add(controls, 'debug').onChange(function (e) {
spotLight.shadowCameraVisible = e;
});
gui.add(controls, 'castShadow').onChange(function (e) {
spotLight.castShadow = e;
});
gui.add(controls, 'onlyShadow').onChange(function (e) {
spotLight.onlyShadow = e;
});
// gui添加选择列表方式
gui.add(controls, 'target', ['Plane', 'Sphere', 'Cube']).onChange(function (e) {
console.log(e);
switch (e) {
case "Plane":
spotLight.target = plane;
break;
case "Sphere":
spotLight.target = sphere;
break;
case "Cube":
spotLight.target = cube;
break;
}
});
gui.add(controls, 'stopMovingLight').onChange(function (e) {
stopMovingLight = e;
});
render();
function render() {
stats.update();
cube.rotation.x += 0.03;
cube.rotation.y += 0.03;
cube.rotation.z += 0.03;
step += 0.03;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// 小球和spotLight光源一起移动
if (!stopMovingLight) {
if (phase > 2 * Math.PI) {
invert = invert * -1;
phase -= 2 * Math.PI;
} else {
phase += 0.03;
}
sphereLightMesh.position.z = +(7 * (Math.sin(phase)));
sphereLightMesh.position.x = +(14 * (Math.cos(phase)));
sphereLightMesh.position.y = 10;
if (invert < 0) {
var pivot = 14;
sphereLightMesh.position.x = (invert * (sphereLightMesh.position.x - pivot)) + pivot;
}
spotLight.position.copy(sphereLightMesh.position);
}
// 重新渲染
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
效果:
4. THREE.DirectionalLight(平行光)
平行光发生光线示意图:
从二维平面发射光线,光线彼此平行。这种类型的光可以看作是距离很远的光,如太阳光,它如此遥远,以至于达到地球时所有的光线几乎都是相互平行的。**它不像前面聚光灯那样离目标越远越暗淡,被平行光照亮的整个区域接收到的光强度是一样的。**它也可以产生阴影,很多属性和上面SpotLight一样,它的光照区域是一个立方体,类似正交投影摄像机,而SpotLight则类似透视投影摄像机。
<!-- chapter-03-04.html -->
<!DOCTYPE html>
<html>
<head>
<title>Directional Light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// 平面
var planeGeometry = new THREE.PlaneGeometry(600, 200, 20, 20);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = -5;
plane.position.z = 0;
scene.add(plane);
// 立方体
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff3333});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);
// 球
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true;
scene.add(sphere);
camera.position.x = -35;
camera.position.y = 30;
camera.position.z = 25;
camera.lookAt(new THREE.Vector3(10, 0, 0));
// 添加环境光,给场景润色
var ambiColor = "#1c1c1c";
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// 添加平行光
var pointColor = "#ff5808";
var directionalLight = new THREE.DirectionalLight(pointColor);
directionalLight.position.set(-40, 60, -10);
directionalLight.castShadow = true;
// 设置光照立方体区域
directionalLight.shadowCameraNear = 2;
directionalLight.shadowCameraFar = 200;
directionalLight.shadowCameraLeft = -50;
directionalLight.shadowCameraRight = 50;
directionalLight.shadowCameraTop = 50;
directionalLight.shadowCameraBottom = -50;
// 同前面聚光灯属性
directionalLight.distance = 0;
directionalLight.intensity = 0.5;
directionalLight.shadowMapHeight = 1024;
directionalLight.shadowMapWidth = 1024;
scene.add(directionalLight);
// 和光源一起移动的小球
var sphereLight = new THREE.SphereGeometry(1);
var sphereLightMaterial = new THREE.MeshBasicMaterial({color: 0xac6c25});
var sphereLightMesh = new THREE.Mesh(sphereLight, sphereLightMaterial);
sphereLightMesh.castShadow = true;
sphereLightMesh.position = new THREE.Vector3(3, 20, 3);
scene.add(sphereLightMesh);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// 属性同上面聚光灯
var controls = new function () {
this.ambientColor = ambiColor;
this.pointColor = pointColor;
this.intensity = 0.5;
this.distance = 0;
this.exponent = 30;
this.angle = 0.1;
this.debug = false;
this.castShadow = true;
this.onlyShadow = false;
this.target = "Plane";
};
var gui = new dat.GUI();
gui.addColor(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new THREE.Color(e);
});
gui.addColor(controls, 'pointColor').onChange(function (e) {
directionalLight.color = new THREE.Color(e);
});
gui.add(controls, 'intensity', 0, 5).onChange(function (e) {
directionalLight.intensity = e;
});
gui.add(controls, 'distance', 0, 200).onChange(function (e) {
directionalLight.distance = e;
});
gui.add(controls, 'debug').onChange(function (e) {
directionalLight.shadowCameraVisible = e;
});
gui.add(controls, 'castShadow').onChange(function (e) {
directionalLight.castShadow = e;
});
gui.add(controls, 'onlyShadow').onChange(function (e) {
directionalLight.onlyShadow = e;
});
gui.add(controls, 'target', ['Plane', 'Sphere', 'Cube']).onChange(function (e) {
console.log(e);
switch (e) {
case "Plane":
directionalLight.target = plane;
break;
case "Sphere":
directionalLight.target = sphere;
break;
case "Cube":
directionalLight.target = cube;
break;
}
});
render();
var step = 0;
function render() {
stats.update();
cube.rotation.x += 0.03;
cube.rotation.y += 0.03;
cube.rotation.z += 0.03;
step += 0.03;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
sphereLightMesh.position.z = -8;
sphereLightMesh.position.y = +(27 * (Math.sin(step / 3)));
sphereLightMesh.position.x = 10 + (26 * (Math.cos(step / 3)));
directionalLight.position.copy(sphereLightMesh.position);
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
效果:
特殊光源
1. THREE.HemisphereLight(半球光源)
使用半球光源可以创建出更加贴近自然的户外光照效果。在户外,并不是所有的光照都来自上方,还有大气的散射和地面以及其它物体的反射。THREE.HemisphereLight
光源就是为了这种情形创建的。
它的属性很简单:
属性 | 描述 |
---|---|
groundColor | 从地面发出的光线的颜色 |
color | 从天空发出的光线的颜色 |
intensity | 光线照射的强度 |
<!-- chapter-03-05.html -->
<!DOCTYPE html>
<html>
<head>
<title>Hemisphere Light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xaaaaaa, 0.010, 200); // 添加雾化效果
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xaaaaff, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// 添加本地图片纹理(草地地面)
var textureGrass = THREE.ImageUtils.loadTexture("../assets/textures/ground/grasslight-big.jpg");
textureGrass.wrapS = THREE.RepeatWrapping;
textureGrass.wrapT = THREE.RepeatWrapping;
textureGrass.repeat.set(4, 4);
// 用本地图片做网格对象的材质
var planeGeometry = new THREE.PlaneGeometry(1000, 200, 20, 20);
var planeMaterial = new THREE.MeshLambertMaterial({map: textureGrass}); // 本地纹理材质
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff3333});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 25, 25);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 10;
sphere.position.y = 5;
sphere.position.z = 10;
sphere.castShadow = true;
scene.add(sphere);
camera.position.x = -20;
camera.position.y = 15;
camera.position.z = 45;
camera.lookAt(new THREE.Vector3(10, 0, 0));
// 添加聚光灯光源
var spotLight0 = new THREE.SpotLight(0xcccccc);
spotLight0.position.set(-40, 60, -10);
spotLight0.lookAt(plane);
scene.add(spotLight0);
// 添加半球光源
var hemiLight = new THREE.HemisphereLight(0x0000ff, 0x00ff00, 0.6);
hemiLight.position.set(0, 500, 0);
scene.add(hemiLight);
// 添加平行光源
var pointColor = "#ffffff";
var dirLight = new THREE.DirectionalLight(pointColor);
dirLight.position.set(30, 10, -50);
dirLight.castShadow = true;
dirLight.target = plane;
dirLight.shadowCameraNear = 0.1;
dirLight.shadowCameraFar = 200;
dirLight.shadowCameraLeft = -50;
dirLight.shadowCameraRight = 50;
dirLight.shadowCameraTop = 50;
dirLight.shadowCameraBottom = -50;
dirLight.shadowMapWidth = 2048;
dirLight.shadowMapHeight = 2048;
scene.add(dirLight);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
var controls = new function () {
this.hemisphere = true; // 是否开启半球光源(通过光强为0来设置)
this.color = 0x00ff00; // 从地面发出的光线颜色
this.skyColor = 0x0000ff; // 从天空发出的光线颜色
this.intensity = 0.6; // 光照强度
};
var gui = new dat.GUI();
gui.add(controls, 'hemisphere').onChange(function (e) {
if (!e) {
hemiLight.intensity = 0;
} else {
hemiLight.intensity = controls.intensity;
}
});
gui.addColor(controls, 'color').onChange(function (e) {
hemiLight.groundColor = new THREE.Color(e);
});
gui.addColor(controls, 'skyColor').onChange(function (e) {
hemiLight.color = new THREE.Color(e);
});
gui.add(controls, 'intensity', 0, 5).onChange(function (e) {
hemiLight.intensity = e;
});
render();
var step = 0;
function render() {
stats.update();
cube.rotation.x += 0.03;
cube.rotation.y += 0.03;
cube.rotation.z += 0.03;
step += 0.03;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
观察示例,通过开启/关闭半球光源,看场景中物体受反射光的影响。
2. THREE.AreaLight(区域光源)
区域光不是从某一个点发光,它是从一块区域发光,可以定义长方形的发光区域。它没有定义在WebGLRenderer
渲染器中,因为会带来非常严重的性能损失。而是定义在WebGLDeferredRenderer
渲染器中,它使用了一种不同的方法(分成多个步骤),相对标志渲染器WebGLRenderer
,可以更好地处理复杂地光照(或者非常大量地光源)。
使用WebGLDeferredRenderer
要额外引入一些外部库。好像新版区域光源也给加到标志渲染器了,本示例用的旧版。
<!-- chapter-03-06.html -->
<!DOCTYPE html>
<html>
<head>
<title>Area Light</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
// 引入WebGLDeferredRenderer渲染器
<script type="text/javascript" src="../libs/WebGLDeferredRenderer.js"></script>
<script type="text/javascript" src="../libs/ShaderDeferred.js"></script>
<script type="text/javascript" src="../libs/RenderPass.js"></script>
<script type="text/javascript" src="../libs/EffectComposer.js"></script>
<script type="text/javascript" src="../libs/CopyShader.js"></script>
<script type="text/javascript" src="../libs/ShaderPass.js"></script>
<script type="text/javascript" src="../libs/FXAAShader.js"></script>
<script type="text/javascript" src="../libs/MaskPass.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
var camera;
function init() {
var stats = initStats();
var scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
scene.add(camera);
// 针对区域光源这种大量光源性能优化的渲染器
var renderer = new THREE.WebGLDeferredRenderer({
width: window.innerWidth,
height: window.innerHeight,
scale: 1, antialias: true,
tonemapping: THREE.FilmicOperator, brightness: 2.5
});
// 地面
var planeGeometry = new THREE.PlaneGeometry(70, 70, 1, 1);
var planeMaterial = new THREE.MeshPhongMaterial({color: 0xffffff, specular: 0xffffff, shininess: 200});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 0;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);
camera.position.x = 20;
camera.position.y = 30;
camera.position.z = 21;
camera.lookAt(new THREE.Vector3(0, 0, -30));
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// 固定的聚光灯光源
var spotLight0 = new THREE.SpotLight(0xcccccc);
spotLight0.position.set(-40, 60, -10);
spotLight0.intensity = 0.1;
spotLight0.lookAt(plane);
scene.add(spotLight0);
// 添加红色区域光源1
var areaLight1 = new THREE.AreaLight(0xff0000, 3);
areaLight1.position.set(-10, 10, -35);
areaLight1.rotation.set(-Math.PI / 2, 0, 0);
areaLight1.width = 4;
areaLight1.height = 9.9;
scene.add(areaLight1);
// 添加绿色区域光源2
var areaLight2 = new THREE.AreaLight(0x00ff00, 3);
areaLight2.position.set(0, 10, -35);
areaLight2.rotation.set(-Math.PI / 2, 0, 0);
areaLight2.width = 4;
areaLight2.height = 9.9;
scene.add(areaLight2);
// 添加蓝色区域光源3
var areaLight3 = new THREE.AreaLight(0x0000ff, 3);
areaLight3.position.set(10, 10, -35);
areaLight3.rotation.set(-Math.PI / 2, 0, 0);
areaLight3.width = 4;
areaLight3.height = 9.9;
scene.add(areaLight3);
// 红色长方形1(z为0)设置到区域光源1位置,用于标志光源的位置
var planeGeometry1 = new THREE.BoxGeometry(4, 10, 0);
var planeGeometry1Mat = new THREE.MeshBasicMaterial({color: 0xff0000});
var plane1 = new THREE.Mesh(planeGeometry1, planeGeometry1Mat);
plane1.position.copy(areaLight1.position);
scene.add(plane1);
// 绿色长方形2设置到区域光源2位置
var planeGeometry2 = new THREE.BoxGeometry(4, 10, 0);
var planeGeometry2Mat = new THREE.MeshBasicMaterial({color: 0x00ff00});
var plane2 = new THREE.Mesh(planeGeometry2, planeGeometry2Mat);
plane2.position.copy(areaLight2.position);
scene.add(plane2);
// 蓝色长方形3设置到区域光源3位置
var planeGeometry3 = new THREE.BoxGeometry(4, 10, 0);
var planeGeometry3Mat = new THREE.MeshBasicMaterial({color: 0x0000ff});
var plane3 = new THREE.Mesh(planeGeometry3, planeGeometry3Mat);
plane3.position.copy(areaLight3.position);
scene.add(plane3);
var controls = new function () {
// 对应3块区域光的颜色和强度
this.color1 = 0xff0000;
this.intensity1 = 2;
this.color2 = 0x00ff00;
this.intensity2 = 2;
this.color3 = 0x0000ff;
this.intensity3 = 2;
};
var gui = new dat.GUI();
// 改变光颜色和对应的长方形颜色
gui.addColor(controls, 'color1').onChange(function (e) {
areaLight1.color = new THREE.Color(e);
planeGeometry1Mat.color = new THREE.Color(e);
scene.remove(plane1);
plane1 = new THREE.Mesh(planeGeometry1, planeGeometry1Mat);
plane1.position.copy(areaLight1.position);
scene.add(plane1);
});
gui.add(controls, 'intensity1', 0, 5).onChange(function (e) {
areaLight1.intensity = e;
});
gui.addColor(controls, 'color2').onChange(function (e) {
areaLight2.color = new THREE.Color(e);
planeGeometry2Mat.color = new THREE.Color(e);
scene.remove(plane2);
plane2 = new THREE.Mesh(planeGeometry2, planeGeometry2Mat);
plane2.position.copy(areaLight2.position);
scene.add(plane2);
});
gui.add(controls, 'intensity2', 0, 5).onChange(function (e) {
areaLight2.intensity = e;
});
gui.addColor(controls, 'color3').onChange(function (e) {
areaLight3.color = new THREE.Color(e);
planeGeometry3Mat.color = new THREE.Color(e);
scene.remove(plane3);
plane3 = new THREE.Mesh(planeGeometry1, planeGeometry3Mat);
plane3.position.copy(areaLight3.position);
scene.add(plane3);
});
gui.add(controls, 'intensity3', 0, 5).onChange(function (e) {
areaLight3.intensity = e;
});
render();
function render() {
stats.update();
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
效果:
3. THREE.LensFlare(镜头光晕)
类似你直接朝着太阳拍照时就会出现镜头光晕效果。
光晕属性:
参数 | 描述 |
---|---|
texture(纹理) | 纹理就是一个图片,用来决定光晕的形状 |
size(尺寸) | 指定光晕大小。如果指定为-1,那么将使用纹理本身的大小 |
distance(距离) | 从光源(0)到摄像机(1)的距离。 |
blending(混合) | 可以为光晕提供多种材质。混合模式决定了如何将它们混合在一起。 |
color(颜色) | 光晕的颜色 |
示例:
<!-- chapter-03-07.html -->
<!DOCTYPE html>
<html>
<head>
<title>Lensflarest</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xaaaaaa, 0.010, 200); // 添加雾化效果
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
renderer.setClearColor(new THREE.Color(0xaaaaff, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// 创建草地地面
var textureGrass = THREE.ImageUtils.loadTexture("../assets/textures/ground/grasslight-big.jpg");
textureGrass.wrapS = THREE.RepeatWrapping;
textureGrass.wrapT = THREE.RepeatWrapping;
textureGrass.repeat.set(4, 4);
var planeGeometry = new THREE.PlaneGeometry(1000, 200, 20, 20);
var planeMaterial = new THREE.MeshLambertMaterial({map: textureGrass});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);
// 创建立方体
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff3333});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);
// 创建球体
var sphereGeometry = new THREE.SphereGeometry(4, 25, 25);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 10;
sphere.position.y = 5;
sphere.position.z = 10;
sphere.castShadow = true;
scene.add(sphere);
camera.position.x = -20;
camera.position.y = 15;
camera.position.z = 45;
camera.lookAt(new THREE.Vector3(10, 0, 0));
// 添加环境光
var ambiColor = "#1c1c1c";
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// 添加聚光灯
var spotLight0 = new THREE.SpotLight(0xcccccc);
spotLight0.position.set(-40, 60, -10);
spotLight0.lookAt(plane);
scene.add(spotLight0);
// 添加平行光
var pointColor = "#ffffff";
var spotLight = new THREE.DirectionalLight(pointColor);
spotLight.position.set(30, 10, -50);
spotLight.castShadow = true;
spotLight.shadowCameraNear = 0.1;
spotLight.shadowCameraFar = 100;
spotLight.shadowCameraFov = 50;
spotLight.target = plane;
spotLight.distance = 0;
spotLight.shadowCameraNear = 2;
spotLight.shadowCameraFar = 200;
spotLight.shadowCameraLeft = -100;
spotLight.shadowCameraRight = 100;
spotLight.shadowCameraTop = 100;
spotLight.shadowCameraBottom = -100;
spotLight.shadowMapWidth = 2048;
spotLight.shadowMapHeight = 2048;
scene.add(spotLight);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
var controls = new function () {
this.ambientColor = ambiColor;
this.pointColor = pointColor;
this.intensity = 0.1;
};
var gui = new dat.GUI();
gui.addColor(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new THREE.Color(e);
});
gui.addColor(controls, 'pointColor').onChange(function (e) {
spotLight.color = new THREE.Color(e);
});
gui.add(controls, 'intensity', 0, 5).onChange(function (e) {
spotLight.intensity = e;
});
// 创建光晕纹理
var textureFlare0 = THREE.ImageUtils.loadTexture("../assets/textures/lensflare/lensflare0.png");
// 创建镜头光晕,把它放在光源处(距离为0)
var flareColor = new THREE.Color(0xffaacc);
var lensFlare = new THREE.LensFlare(textureFlare0, 350, 0.0, THREE.AdditiveBlending, flareColor);
// 光源到摄像机附近添加新的光晕(使用的很淡的纹理),通过上面主光晕对象的add方法添加,增强表现效果,可以屏蔽对比。
var textureFlare3 = THREE.ImageUtils.loadTexture("../assets/textures/lensflare/lensflare3.png");
lensFlare.add(textureFlare3, 60, 0.6, THREE.AdditiveBlending);
lensFlare.add(textureFlare3, 70, 0.7, THREE.AdditiveBlending);
lensFlare.add(textureFlare3, 120, 0.9, THREE.AdditiveBlending);
lensFlare.add(textureFlare3, 70, 1.0, THREE.AdditiveBlending);
lensFlare.position.copy(spotLight.position);
scene.add(lensFlare);
render();
var step = 0;
function render() {
stats.update();
cube.rotation.x += 0.03;
cube.rotation.y += 0.03;
cube.rotation.z += 0.03;
step += 0.03;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
};
window.onload = init;
// 改变窗口大小后适配
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onResize, false);
</script>
</body>
</html>
效果: