柚子快报激活码778899分享:Three.js入门
什么是WebGL。WebGL是在浏览器中实现三维效果的一套规范。使用WebGL原生的API来写3D程序是一件非常痛苦的事情,幸好,有很多同行花业余时间写了一些WebGL开源框架,其中three.js就是非常优秀的一个。
什么是threejs,很简单,你将它理解成three + js就可以了。three表示3D的意思,js表示javascript的意思。那么合起来,three.js就是使用javascript 来写3D程序的意思。
three.js官网地址:https://threejs.org/
记录两个git地址:
https://github.com/josdirksen/learning-threejs Three.js开发指南一书对应的源码
https://github.com/mrdoob/three.js 对three.js的封装,更加强大。
1.入门程序
1
2
3
4
5
6
7
8
9 body {
10 /* set margin to 0 and overflow to hidden, to go fullscreen */
11 margin: 0;
12 overflow: hidden;
13 }
14
15
16
17
18
19
20
21
22
23
24
25 // once everything is loaded, we run our Three.js stuff.
26 function init() {
27
28 // create a scene, that will hold all our elements such as objects, cameras and lights.
29 var scene = new THREE.Scene();
30
31 // create a camera, which defines where we're looking at.
32 var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
33
34 // create a render and set the size
35 var renderer = new THREE.WebGLRenderer();
36 renderer.setClearColorHex();
37 renderer.setClearColor(new THREE.Color(0xEEEEEE));
38 renderer.setSize(window.innerWidth, window.innerHeight);
39
40 // show axes in the screen
41 var axes = new THREE.AxisHelper(20);
42 scene.add(axes);
43
44 // create the ground plane
45 var planeGeometry = new THREE.PlaneGeometry(60, 20);
46 var planeMaterial = new THREE.MeshBasicMaterial({color: 0xcccccc});
47 var plane = new THREE.Mesh(planeGeometry, planeMaterial);
48
49 // rotate and position the plane
50 plane.rotation.x = -0.5 * Math.PI;
51 plane.position.x = 15;
52 plane.position.y = 0;
53 plane.position.z = 0;
54
55 // add the plane to the scene
56 scene.add(plane);
57
58 // create a cube
59 var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
60 var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});
61 var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
62
63 // position the cube
64 cube.position.x = -4;
65 cube.position.y = 3;
66 cube.position.z = 0;
67
68 // add the cube to the scene
69 scene.add(cube);
70
71 // create a sphere
72 var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
73 var sphereMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff, wireframe: true});
74 var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
75
76 // position the sphere
77 sphere.position.x = 20;
78 sphere.position.y = 4;
79 sphere.position.z = 2;
80
81 // add the sphere to the scene
82 scene.add(sphere);
83
84 // position and point the camera to the center of the scene
85 camera.position.x = -30;
86 camera.position.y = 40;
87 camera.position.z = 30;
88 camera.lookAt(scene.position);
89
90 // add the output of the renderer to the html element
91 document.getElementById("WebGL-output").appendChild(renderer.domElement);
92
93 // render the scene
94 renderer.render(scene, camera);
95 }
96 window.onload = init;
97
98
99
100
结果:
上面包含的物体:
解释:
28-38行代码:
首先定义了场景(Scene)、摄像机(camera)和渲染器(renderer)对象。场景是一个容器,主要用于保存、跟踪所要渲染的物体和使用的光源,如果没有THEE.Scene对象,那么Three.js无法渲染任何对象。摄像机决定了能够在场景中看到什么。渲染器会基于摄像机的角度来计算场景对象在浏览器中会渲染成什么样子。最后WebGLRendere将会使用电脑显卡来渲染场景。(除了基于WebGL的渲染器还有基于画板和SVG的渲染器,不推荐使用,因为太消耗CPU)。
上面代码,renderer.setClearColorHex(); renderer.setClearColor(new THREE.Color(0xEEEEEE)); 将场景的背景颜色设置为接近白色,renderer.setSize(window.innerWidth, window.innerHeight);设置场景的大小。如果将上面代码修改为下面代码再次查看效果:
renderer.setClearColor(new THREE.Color(0xEEEE00));
renderer.setSize(500, 500);
40行-56行:
到上面已经创建了空白的场景、渲染器和摄像机,但是没有渲染任何东西,40-56行的代码会添加轴和平面。
var axes = new THREE.AxisHelper(20);创建了轴(axes)对象,参数指定了线条的长度,并调用scene.add方法将轴添加到场景中。
接下来创建了平面plane,平面的创建分为两步。首先是要 new THREE.PlaneGeometry(60, 20); 来定义平面的大小,宽度指定为60、高度设置为20,除了设置宽度和高度,我们还需要设置平面的外观(比如颜色和透明度),在three.js中通过创建材质对象来设置平面的外观,例子中用 new THREE.MeshBasicMaterial({color: 0xcccccc}); 创建了颜色为 0xcccccc 的基本材质(THREE.MeshBasicMaterial) ,然后 new THREE.Mesh(planeGeometry, planeMaterial); 将平面和材质合并到名为plane的网格对象中。在平面添加到场景之前,还需要设置平面的位置:先将平面绕x轴旋转90度,然后使用 position 属性指定在场景中位置。最后将平面添加到场景中。
58-82行:
使用同样的方式将方块和球体添加到平面。但是需要将线框(wireframe)属性设置为true,这样物体就不会被渲染为实体。如果 wireframe 设置为false,渲染为实体效果如下:
84-94行:
现在,所以物体都已经加载到场景中。之前也说了摄像机将决定看到哪些物体。在这段代码中,我们使用x,y,z属性来设置摄像机的位置。为了确保所要渲染的物体能够被摄像机拍到,我们使用lookAt方法指向场景的中心,默认状态下摄像机是指向(0,0,0)位置的。最后要做的就是将渲染结果添加到HTML的div元素中。最后告诉渲染器使用指定的摄像机来渲染场景。
如果我们将摄像机的x轴的位置改为0, camera.position.x = 0; 效果如下:
补充:摄像机的位置决定了看到哪些物体,也就决定了角度。也可以通过set方法设置其位置,如下:
camera.position.set(-30,48,30);
其有很多方法,如下: (我们用webstorm编辑器的时候会自动提示)
2.添加材质、光源和阴影效果
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 20);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// create a cube
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;
// position the cube
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// add the cube to the scene
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);
// position the sphere
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
sphere.castShadow = true;
// add the sphere to the scene
scene.add(sphere);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
renderer.render(scene, camera);
}
window.onload = init;
结果:
2.1添加光源
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
通过THREE.SpotLight定义光源并从其位置( spotLight.position.set(-40, 60, -10);) 照射场景。如果这时候渲染场景我们看到的结果和没光源是一样的。因为不同的材质对光源的反应是不一样的。我们使用的基本材质(THREE.MeshBasicMaterial)不会对光源产生任何反应,基本材质只会使用指定的颜色来渲染物体。所以需要改变平面、球体、和立方体的材质为 THREE.MeshLambertMaterial 即可。Three.js中的材质THREE.MeshLambertMaterial 和 THREE.MeshPhongMaterial在渲染时会对光源产生反应。
2.2 渲染阴影
由于渲染阴影需要消耗大量的计算资源,默认情况Three.js不会渲染阴影,为了渲染阴影效果,我们需要作如下:
renderer.shadowMapEnabled = true; 首先渲染器的此属性设为true告诉渲染器需要阴影效果。
另外还要指定哪个物体投射阴影、哪个物体接受阴影。如下平面接受阴影,小球和方体发出阴影:
plane.receiveShadow = true;
cube.castShadow = true;
sphere.castShadow = true;
最后还要指定光源产生阴影,因为并不是所有的光源都能产生阴影,THREE.SpotLight是能产生阴影的,我们只需要将castShadow属性设置为true就可以将阴影渲染出来。
3.场景动起来
如果希望场景动起来,首先需要解决的问题是如何在特定的时间间隔重新渲染场景。原生的JS是通过setInterval(fun,interval)来实现的,这个方法的缺点是不管浏览器正在发生什么都会执行定时作业。
1.引入 requestAnimationFrame(render);
在这个方法中可以定义所有的绘画操作,浏览器会尽可能平滑、高效地进行绘制。使用这个 requestAnimationFrame(); 的方法很简单,只需要创建一个处理渲染的方法即可:
function render() {
...
// render using requestAnimationFrame
requestAnimationFrame(render);
renderer.render(scene, camera);
}
在render()方法中 requestAnimationFrame() 方法又一次被调用了,这样做的目的是保证会话能持续运行。接下来需要做的就是:场景创建完毕之后不再调用render.render()方法,而是调用render()方法来启动动画,代码如下:
document.getElementById("Stats-output").appendChild(stats.domElement);
render();
这时候运行代码和之前是一样的,因为还没有为物体添加任何动画效果。在添加动画之前,我们先来介绍一个辅助库,这个库也是Three.js作者开发的,主要用于检测动画运行时的帧数。在动画运行时,该库可以在一个图片中显示画面每秒传输帧数。
辅助库:
界面添加元素:
初始化统计对象,并将该对象添加到
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;
}
这里需要注意setMode()方法。如果参数设置为 0 ,那么我们检测的是画面每秒传输帧数(fps); 如果参数设置为1,那么我们检测的是画面渲染时间。一般统计fps(Frames Per Second)。为了使具有统计功能,需要在一开始调用initStats()方法。
var stats = initStats();
为了能够通知stats对象画面何时被重新渲染,我们在render()方法需要调用stats.update()方法:
function render() {
stats.update();
// render using requestAnimationFrame
requestAnimationFrame(render);
renderer.render(scene, camera);
}
2. 旋转立方体和弹跳的小球
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
// once everything is loaded, we run our Three.js stuff.
function init() {
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// create the ground plane
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;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// create a cube
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;
// position the cube
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// add the cube to the scene
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);
// position the sphere
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true;
// add the sphere to the scene
scene.add(sphere);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
var step = 0;
renderScene();
function renderScene() {
stats.update();
// rotate the cube around its axes
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
// bounce the sphere up and down
step += 0.04;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// render using requestAnimationFrame
requestAnimationFrame(renderScene);
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;
解释:
立方体旋转:在每次调用 renderScene() 方法的时候使得每个坐标轴的ration属性加0.2,其效果就是立方体将围绕它的轴进行缓慢的旋转。
旋转立方体改变的是rotation属性;而让小球弹跳起来,我们所要做的是修改球体在场景中的位置(position)属性,我们的目的是让小球依照一条好看的、光滑的曲线从一个地方跳到另一个地方,如下:
实现上面效果需要同时改变球体的x轴和y轴的位置,Math.cos()和Math.sin()方法使用step变量就可以创建出球体的运行轨迹。step+=0.4定义了球体的弹跳速度。
3.使用dat.GUI简化实验流程
这个库可以很容易地创建出能够改变代码变量的界面组件。
像引入统计对象一样,我们需要引入类库:
接下来定义一个JS对象,该对象希望通过dat.GUI改变的属性。在JS代码中添加如下JS对象:
var controls = new function () {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.03;
};
在上面对象中定义了两个属性---rotationSpeed、bouncingSpeed以及它们的默认值,接下来需要将这个JS对象传递给data.GUI对象,并设置这两个属性的取值范围:
var gui = new dat.GUI();
gui.add(controls, 'rotationSpeed', 0, 0.5);
gui.add(controls, 'bouncingSpeed', 0, 0.5);
立方体旋转速度和球体弹跳速度的取值范围为0-0.5。现在需要在 render 中直接引用这两个属性,这样当我们在dat.GUI中修改这两个属性的值时就可以影响到相应的物体的旋转速度和弹跳速度,如下:
function render() {
stats.update();
// rotate the cube around its axes
cube.rotation.x += controls.rotationSpeed;
cube.rotation.y += controls.rotationSpeed;
cube.rotation.z += controls.rotationSpeed;
// bounce the sphere up and down
step += controls.bouncingSpeed;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// render using requestAnimationFrame
requestAnimationFrame(render);
renderer.render(scene, camera);
}
运行效果如下:
4.场景对浏览器的自适应
当浏览器大小改变时改变摄像机很容易实现。首先需要为浏览器注册一个事件监听器,如下:
// listen to the resize events
window.addEventListener('resize', onResize, false);
这样,当浏览器尺寸改变时onResize()会被执行。在onResize()方法中需要更新摄像机和渲染器,如下:
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
对于摄像机,需要更新它的aspect属性,这个属性表示屏幕的长宽比;对于渲染器,需要改变它的尺寸。
例如:
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
var camera;
var scene;
var renderer;
// once everything is loaded, we run our Three.js stuff.
function init() {
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// create the ground plane
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;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// create a cube
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;
// position the cube
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// add the cube to the scene
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);
// position the sphere
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true;
// add the sphere to the scene
scene.add(sphere);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
var step = 0;
var controls = new function () {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.03;
};
var gui = new dat.GUI();
gui.add(controls, 'rotationSpeed', 0, 0.5);
gui.add(controls, 'bouncingSpeed', 0, 0.5);
render();
function render() {
stats.update();
// rotate the cube around its axes
cube.rotation.x += controls.rotationSpeed;
cube.rotation.y += controls.rotationSpeed;
cube.rotation.z += controls.rotationSpeed;
// bounce the sphere up and down
step += controls.bouncingSpeed;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// render using requestAnimationFrame
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;
}
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.onload = init;
// listen to the resize events
window.addEventListener('resize', onResize, false);
补充:Three.js用的是右手坐标系,看起来是这样的:
总结:
了解到three.js的3个基本组件:场景(scene)、相机(camera)、渲染器(render),以及其基本使用。场景是一个容器,主要用于保存、跟踪所要渲染的物体和使用的光源,如果没有THEE.Scene对象,那么Three.js无法渲染任何对象。摄像机决定了能够在场景中看到什么,其位置也决定了渲染的角度。渲染器会基于摄像机的角度来计算场景对象在浏览器中会渲染成什么样子。
也学习了两个调试组件:stats监控帧数和dat.GUI创建能够改变代码变量的界面组件。GUI界面显示与隐藏的快捷键是H。
柚子快报激活码778899分享:Three.js入门
好文链接
本文由 用户 于 2024-05-20 发布在 夸智网,如有疑问,请联系我们。
本文链接:https://www.kuazhi.com/post/714059767.html
发表评论