今天,我们将使用Three.js创建一个简单的3D飞机,使WebGL更简单。由于GLSL的复杂性和语法,WebGL对许多开发人员来说是一个相当陌生的世界。但是通过Three.js,浏览器中的3D变得非常容易实现。
在本教程中,我们将创建一个简单的3D场景,并在两个主要部分中进行一些交互。
在第一部分,我们将解释Three.js的基础知识,以及如何建立一个非常简单的场景。
第二部分详细介绍了如何细化形状, 如何在场景的不同元素中添加一些气氛和更好的动作。
本教程的范围之外是整个游戏,但您可以下载并查看代码; 它包含许多有趣的附加部分,如碰撞,抓硬币和增加分数。
在本教程中,我们将重点介绍一些基本概念,使您可以使用Three.js开始WebGL的世界!
HTML和CSS
本教程主要使用Three.js库,这使得WebGL易于使用。
首先要做的是在你的HTML头文件中导入这个库:
1 | < script type = "text/javascript" src = "js/three.js" ></ script > |
然后你需要在HTML中添加一个容器元素来容纳渲染的场景:
1 | < div id = "world" ></ div > |
您可以简单地将其设置为如下所示,以填充整个视口:
1 2 3 4 5 6 7 | #world { position : absolute ; width : 100% ; height : 100% ; overflow : hidden ; background : linear-gradient( #e4e0ba , #f7d9aa ); } |
正如你所看到的,背景有一个微妙的渐变,就像天空。
这就是标记和风格!
如果您有一些关于JavaScript的基本知识,Three.js非常易于使用。让我们来看看我们要实现的代码的不同部分。
调色板
在开始编写场景之前,我总是发现定义一个在整个项目中一致使用的调色板是非常有用的。对于这个项目,我们选择以下颜色:
1 2 3 4 5 6 7 8 | var Colors = { red:0xf25346, white:0xd8d0d1, brown:0x59332e, pink:0xF5986E, brownDark:0x23190f, blue:0x68c3c0, }; |
尽管JavaScript代码非常冗长,但其结构非常简单。我们需要创建的所有主要函数都放在init函数中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | window.addEventListener( 'load' , init, false ); function init() { // set up the scene, the camera and the renderer createScene(); // add the lights createLights(); // add the objects createPlane(); createSea(); createSky(); // start a loop that will update the objects' positions // and render the scene on each frame loop(); } |
要创建Three.js项目,我们至少需要以下内容:
一个场景:将其视为需要添加每个对象才能渲染的阶段相机:在这种情况下,我们将使用透视相机,但它也可能是一个正交相机。渲染器将使用WebGL显示所有场景。一个或多个物体渲染,在我们的例子中,我们将创建一个飞机,一个海洋和一个天空(一些云)一个或多个灯:也有不同类型的灯可用。在这个项目中,我们将主要使用一个半球光照的气氛和一个定向的光影。
场景,相机和渲染器是在createScene函数中创建的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | var scene, camera, fieldOfView, aspectRatio, nearPlane, farPlane, HEIGHT, WIDTH, renderer, container; function createScene() { // Get the width and the height of the screen, // use them to set up the aspect ratio of the camera // and the size of the renderer. HEIGHT = window.innerHeight; WIDTH = window.innerWidth; // Create the scene scene = new THREE.Scene(); // Add a fog effect to the scene; same color as the // background color used in the style sheet scene.fog = new THREE.Fog(0xf7d9aa, 100, 950); // Create the camera aspectRatio = WIDTH / HEIGHT; fieldOfView = 60; nearPlane = 1; farPlane = 10000; camera = new THREE.PerspectiveCamera( fieldOfView, aspectRatio, nearPlane, farPlane ); // Set the position of the camera camera.position.x = 0; camera.position.z = 200; camera.position.y = 100; // Create the renderer renderer = new THREE.WebGLRenderer({ // Allow transparency to show the gradient background // we defined in the CSS alpha: true , // Activate the anti-aliasing; this is less performant, // but, as our project is low-poly based, it should be fine :) antialias: true }); // Define the size of the renderer; in this case, // it will fill the entire screen renderer.setSize(WIDTH, HEIGHT); // Enable shadow rendering renderer.shadowMap.enabled = true ; // Add the DOM element of the renderer to the // container we created in the HTML container = document.getElementById( 'world' ); container.appendChild(renderer.domElement); // Listen to the screen: if the user resizes it // we have to update the camera and the renderer size window.addEventListener( 'resize' , handleWindowResize, false ); } |
由于屏幕尺寸可以改变,我们需要更新渲染器尺寸和相机长宽比:
1 2 3 4 5 6 7 8 | function handleWindowResize() { // update height and width of the renderer and the camera HEIGHT = window.innerHeight; WIDTH = window.innerWidth; renderer.setSize(WIDTH, HEIGHT); camera.aspect = WIDTH / HEIGHT; camera.updateProjectionMatrix(); } |
闪电当然是设置场景中最棘手的部分之一。灯光将设定整个场景的气氛,必须仔细确定。在这个项目的这一步,我们只是试图使闪电足够好,使物体可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | var hemisphereLight, shadowLight; function createLights() { // A hemisphere light is a gradient colored light; // the first parameter is the sky color, the second parameter is the ground color, // the third parameter is the intensity of the light hemisphereLight = new THREE.HemisphereLight(0xaaaaaa,0x000000, .9) // A directional light shines from a specific direction. // It acts like the sun, that means that all the rays produced are parallel. shadowLight = new THREE.DirectionalLight(0xffffff, .9); // Set the direction of the light shadowLight.position.set(150, 350, 350); // Allow shadow casting shadowLight.castShadow = true ; // define the visible area of the projected shadow shadowLight.shadow.camera.left = -400; shadowLight.shadow.camera.right = 400; shadowLight.shadow.camera.top = 400; shadowLight.shadow.camera.bottom = -400; shadowLight.shadow.camera.near = 1; shadowLight.shadow.camera.far = 1000; // define the resolution of the shadow; the higher the better, // but also the more expensive and less performant shadowLight.shadow.mapSize.width = 2048; shadowLight.shadow.mapSize.height = 2048; // to activate the lights, just add them to the scene scene.add(hemisphereLight); scene.add(shadowLight); } |
正如您在这里所看到的, 许多参数用于创建光源。不要犹豫, 试验的颜色, 强度和数量的灯;你会发现有趣的情绪和氛围为你的场景,,并感觉如何调整他们的需求。
如果您对3D建模软件感到满意,您可以在那里创建对象,并将其导入到Three.js项目中。本教程不会涉及这个解决方案,但是我们将使用Three.js中提供的基元来创建对象,以更好地理解它们的工作原理。
Three.js已经有了大量的立方体,球体,圆环体,圆柱体和平面等现成的图元。
对于我们的项目,我们要创建的所有对象只是这些基元的组合。这非常适合低聚风格,它将使我们不必在3D软件中对对象进行建模。
让我们从创造海洋开始,因为它是我们必须处理的最简单的对象。为了简单起见,我们将把大海说成是一个放在屏幕底部的简单的蓝色圆柱体。稍后我们将深入探讨如何改善这种形状。
接下来,让大海看起来更有吸引力,波浪更逼真:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // First let's define a Sea object : Sea = function (){ // create the geometry (shape) of the cylinder; // the parameters are: // radius top, radius bottom, height, number of segments on the radius, number of segments vertically var geom = new THREE.CylinderGeometry(600,600,800,40,10); // rotate the geometry on the x axis geom.applyMatrix( new THREE.Matrix4().makeRotationX(-Math.PI/2)); // create the material var mat = new THREE.MeshPhongMaterial({ color:Colors.blue, transparent: true , opacity:.6, shading:THREE.FlatShading, }); // To create an object in Three.js, we have to create a mesh // which is a combination of a geometry and some material this .mesh = new THREE.Mesh(geom, mat); // Allow the sea to receive shadows this .mesh.receiveShadow = true ; } // Instantiate the sea and add it to the scene: var sea; function createSea(){ sea = new Sea(); // push it a little bit at the bottom of the scene sea.mesh.position.y = -600; // add the mesh of the sea to the scene scene.add(sea.mesh); } |
让我们总结一下为了创建一个对象我们需要什么。我们要
创建几何图形
创建材料
把它们传给网格
将网格添加到我们的场景中
通过这些基本步骤, 我们可以创建许多不同类型的原始对象。现在, 如果我们结合起来, 我们可以创建更复杂的形状。
在下面的步骤中, 我们将学习如何准确地做到这一点。
云有点复杂,因为它们是随机组合成一个形状的多个立方体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | Cloud = function (){ // Create an empty container that will hold the different parts of the cloud this .mesh = new THREE.Object3D(); // create a cube geometry; // this shape will be duplicated to create the cloud var geom = new THREE.BoxGeometry(20,20,20); // create a material; a simple white material will do the trick var mat = new THREE.MeshPhongMaterial({ color:Colors.white, }); // duplicate the geometry a random number of times var nBlocs = 3+Math.floor(Math.random()*3); for ( var i=0; i<nBlocs; i++ ){ // create the mesh by cloning the geometry var m = new THREE.Mesh(geom, mat); // set the position and the rotation of each cube randomly m.position.x = i*15; m.position.y = Math.random()*10; m.position.z = Math.random()*10; m.rotation.z = Math.random()*Math.PI*2; m.rotation.y = Math.random()*Math.PI*2; // set the size of the cube randomly var s = .1 + Math.random()*.9; m.scale.set(s,s,s); // allow each cube to cast and to receive shadows m.castShadow = true ; m.receiveShadow = true ; // add the cube to the container we first created this .mesh.add(m); } } |
现在我们有一个云,我们将使用它来复制整个天空,并将它放置在z轴周围的任意位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | // Define a Sky Object Sky = function (){ // Create an empty container this .mesh = new THREE.Object3D(); // choose a number of clouds to be scattered in the sky this .nClouds = 20; // To distribute the clouds consistently, // we need to place them according to a uniform angle var stepAngle = Ma
|