与three.js的圣诞树

Christmas tree with three.js
Christmas tree with three.js

Christmas tree with three.js Today’s article refers to the Christmas and new year in the most direct manner. I prepared a remarkable and relevant demonstration of possibilities of three.js library in the form of an interactive Christmas card. This postcard has everything we need – the Christmas tree with toys, the star in the top, snow, snowflakes in the air – all to raise new year spirit of Christmas time. In this tutorial I will show you how to work with 3D scene, fog, cameras, textures, materials, basic objects (meshes), ground, lights, particles and so on.

带有three.js的圣诞树今天的文章以最直接的方式指圣诞节和新年。 我以交互式圣诞贺卡的形式,对Three.js库的可能性进行了出色且相关的演示。 这张明信片提供了我们所需的一切–玩具圣诞树,顶部的星星,雪,空中的雪花–所有这些都可以唤起圣诞节的新年精神。 在本教程中,我将向您展示如何处理3D场景,雾,相机,纹理,材质,基本对象(网格),地面,灯光,粒子等。

Our result works good for all modern browsers (using the HTML5 technology). To start, let’s look at the result and online demo:

我们的结果适用于所有现代浏览器(使用HTML5技术)。 首先,让我们看一下结果和在线演示:

现场演示

First of all, I want to say a few words about three.js. Today’s lesson is the first where we use this library, which is really amazing, because it lets you get really good results with the minimum of effort. Almost all the necessary things for creating 3d scenes have already been implemented in this library, which harnesses the power of WebGL. I think that next year we will come back to WebGL more than once.

首先,我想谈谈three.js。 今天的课程是我们第一个使用此库的课程,这确实很棒,因为它使您以最少的努力获得了非常好的结果。 这个库利用WebGL的强大功能,几乎已经创建了创建3d场景所需的所有内容。 我认为明年我们将不止一次回到WebGL。

Now, if you are ready to develop it – let’s start. In the beginning, create a blank html file (index.html) and put the following code:

现在,如果您准备开发它,那就开始吧。 首先,创建一个空白的html文件(index.html)并输入以下代码:


<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <meta name="author" content="Script Tutorials" />
        <title>Christmas tree with three.js | Script Tutorials</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <!-- add styles -->
        <link href="css/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <!-- add scripts -->
        <script src="js/three.min.js"></script>
        <script src="js/script.js"></script>
    </body>
</html>

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <meta name="author" content="Script Tutorials" />
        <title>Christmas tree with three.js | Script Tutorials</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <!-- add styles -->
        <link href="css/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <!-- add scripts -->
        <script src="js/three.min.js"></script>
        <script src="js/script.js"></script>
    </body>
</html>

Could it be simpler? Indeed, all you see is the basic HTML markup with few meta tags, and two javascript files, the first of which is the library itself (three.min.js), and the second file with the code of our Christmas card (script.js). Pay attention, that both javascript files are in the ‘js’ folder. Don’t forget to create this folder (‘js’) in the same folder where you saved your index.html file.

会更简单吗? 实际上,您所看到的只是基本HTML标记,其中几乎没有meta标签,还有两个javascript文件,第一个是库本身(three.min.js),第二个是带有我们圣诞贺卡代码的脚本(脚本)。 js)。 请注意,两个javascript文件都在“ js”文件夹中。 不要忘记在保存index.html文件的同一文件夹中创建此文件夹('js')。

Next step – you will need to download three.min.js library. You can find it in the Internet, and it is also available here. This file should be placed in the ‘js’ folder which you prepared earlier. Starting from the current moment we begin to understand the process of creation of all the scene elements. Now create a new blank javascript file, save it in the ‘js’ folder with the ‘script.js’ name. And let’s start from the beginning

下一步–您将需要下载three.min.js库。 您可以在Internet上找到它,也可以在此处找到 。 该文件应放在您之前准备的“ js”文件夹中。 从当前时刻开始,我们开始了解所有场景元素的创建过程。 现在创建一个新的空白javascript文件,并将其保存为“ script.js”名称的“ js”文件夹。 让我们从头开始

骨架 (Skeleton)

To begin, we define the global variables and the application skeleton

首先,我们定义全局变量和应用程序框架


var container;
var camera, scene, renderer, group;
var targetRotation = 0;
var targetRotationOnMouseDown = 0;
var mouseX = 0;
var mouseXOnMouseDown = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
// initialize and start animation
init();
animate();
function init() {
}
function animate() {
    requestAnimationFrame(animate);
    render();
}
function render() {
    renderer.render(scene, camera);
}

var container;
var camera, scene, renderer, group;
var targetRotation = 0;
var targetRotationOnMouseDown = 0;
var mouseX = 0;
var mouseXOnMouseDown = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
// initialize and start animation
init();
animate();
function init() {
}
function animate() {
    requestAnimationFrame(animate);
    render();
}
function render() {
    renderer.render(scene, camera);
}

This is fairly common structure of application built with three.js. Almost everything will be initialized and created in the ‘init’ function. Then we will add few more function for user interaction.

这是用three.js构建的应用程序的相当普遍的结构。 几乎所有内容都将在“ init”函数中初始化和创建。 然后,我们将为用户交互添加更多功能。

场景,相机,动作 (Scene, camera, action)

This is a preparatory stage on which we prepare container for our scene, display some intro description, initialize the scene, fog, camera, and then we prepare the group (THREE.Object3D) on which we will put elements of the scene. Add the following code in the ‘init’ function:

这是一个准备阶段,在该阶段中,我们为场景准备容器,显示一些介绍性说明,初始化场景,雾气,相机,然后准备要放置场景元素的组(THREE.Object3D)。 在“ init”函数中添加以下代码:


    // prepare the container
    container = document.createElement('div');
    document.body.appendChild(container);
    // display Info
    var info = document.createElement('div');
    info.style.position = 'absolute';
    info.style.top = '10px';
    info.style.width = '100%';
    info.style.textAlign = 'center';
    info.innerHTML = 'This Christmas tree is prepared by <a target="_blank" href="https://www.script-tutorials.com/">Script Tutorials</a> team.<br/>Drag to spin';
    container.appendChild(info);
    // initialize the scene
    scene = new THREE.Scene();
    // add the fog
    scene.fog = new THREE.Fog(0xcce0ff, 500, 10000);
    // set the camera
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.set(0, 100, 500);
    scene.add(camera);
    // create the empty scene group
    group = new THREE.Object3D();
    scene.add(group);

    // prepare the container
    container = document.createElement('div');
    document.body.appendChild(container);
    // display Info
    var info = document.createElement('div');
    info.style.position = 'absolute';
    info.style.top = '10px';
    info.style.width = '100%';
    info.style.textAlign = 'center';
    info.innerHTML = 'This Christmas tree is prepared by <a target="_blank" href="https://www.script-tutorials.com/">Script Tutorials</a> team.<br/>Drag to spin';
    container.appendChild(info);
    // initialize the scene
    scene = new THREE.Scene();
    // add the fog
    scene.fog = new THREE.Fog(0xcce0ff, 500, 10000);
    // set the camera
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.set(0, 100, 500);
    scene.add(camera);
    // create the empty scene group
    group = new THREE.Object3D();
    scene.add(group);

用料 (Materials)

Materials is one of the most interesting chapters in the graph. Especially when it comes to shiny surfaces and BumpMapping. Add the following code in the ‘init’ function:

材料是图中最有趣的章节之一。 尤其是在涉及发亮的表面和凹凸贴图时。 在“ init”函数中添加以下代码:


    // prepare materials
    var imgTexture = THREE.ImageUtils.loadTexture('img/texture.jpg');
    imgTexture.repeat.set(1, 1);
    imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping;
    imgTexture.anisotropy = 16;
    imgTexture.needsUpdate = true;
    var shininess = 50, specular = 0x333333, bumpScale = 1, shading = THREE.SmoothShading;
    var materials = [];
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, bumpMap: imgTexture, bumpScale: bumpScale, color: 0xff0000, ambient: 0xffffff, specular: specular, shininess: shininess, shading: shading } ) );
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, color: 0x008800, ambient: 0xffffff, specular: specular, shininess: shininess, shading: shading } ) );
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, color: 0x584000, ambient: 0xffffff, shading: shading } ) );
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, color: 0xff0000, ambient: 0xffffff, shading: shading } ) );

    // prepare materials
    var imgTexture = THREE.ImageUtils.loadTexture('img/texture.jpg');
    imgTexture.repeat.set(1, 1);
    imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping;
    imgTexture.anisotropy = 16;
    imgTexture.needsUpdate = true;
    var shininess = 50, specular = 0x333333, bumpScale = 1, shading = THREE.SmoothShading;
    var materials = [];
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, bumpMap: imgTexture, bumpScale: bumpScale, color: 0xff0000, ambient: 0xffffff, specular: specular, shininess: shininess, shading: shading } ) );
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, color: 0x008800, ambient: 0xffffff, specular: specular, shininess: shininess, shading: shading } ) );
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, color: 0x584000, ambient: 0xffffff, shading: shading } ) );
    materials.push( new THREE.MeshPhongMaterial( { map: imgTexture, color: 0xff0000, ambient: 0xffffff, shading: shading } ) );

Thus we have prepared four materials

因此,我们准备了四种材料

行李箱(圆柱几何) (Trunk (CylinderGeometry))

This is the first basic object on the scene: the truncated cylinder. Add the following code in the ‘init’ function:

这是场景中的第一个基本对象:截断的圆柱体。 在“ init”函数中添加以下代码:


    // add the Trunk
    var trunk = new THREE.Mesh(new THREE.CylinderGeometry(2, 20, 300, 30, 1, false), materials[2]);
    group.add(trunk);

    // add the Trunk
    var trunk = new THREE.Mesh(new THREE.CylinderGeometry(2, 20, 300, 30, 1, false), materials[2]);
    group.add(trunk);

We choosed the third material for the trunk. Params of the CylinderGeometry are: radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded.

我们为行李箱选择了第三种材料。 CylinderGeometry的参数为:radiusTop,radiusBottom,height,radiusSegments,heightSegments,openEnded。

分行 (Branches)

In our example, the branch is a polygonal star. The upper row has the lowest number of teeth (branches), each subsequent row has one branch more. I prepared a special function ‘addBranch’ to generate the polygonal star. Add the following code in the ‘init’ function:

在我们的示例中,分支是多边形星。 上排的齿(分支)数最少,随后的每一排都有一个以上的分支。 我准备了一个特殊功能“ addBranch”来生成多边形星。 在“ init”函数中添加以下代码:


    // add branch function
    function addBranch(count, x, y, z, opts, material, rotate) {
        // prepare star-like points
        var points = [], l;
        for (i = 0; i < count * 2; i++) {
            if (i % 2 == 1) {
                l = count * 2;
            } else {
                l = count * 4;
            }
            var a = i / count * Math.PI;
            points.push( new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l));
        }
        var branchShape = new THREE.Shape(points);
        var branchGeometry = new THREE.ExtrudeGeometry(branchShape, opts);
        var branchMesh = new THREE.Mesh(branchGeometry, material);
        branchMesh.position.set(x, y, z);
        // rotate 90 degrees
        if (rotate) {
            branchMesh.rotation.set(Math.PI / 2, 0, 0);
        } else {
            branchMesh.rotation.set(0, 0, Math.PI / 2);
        }
        // add branch to the group
        group.add(branchMesh);
    }

    // add branch function
    function addBranch(count, x, y, z, opts, material, rotate) {
        // prepare star-like points
        var points = [], l;
        for (i = 0; i < count * 2; i++) {
            if (i % 2 == 1) {
                l = count * 2;
            } else {
                l = count * 4;
            }
            var a = i / count * Math.PI;
            points.push( new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l));
        }
        var branchShape = new THREE.Shape(points);
        var branchGeometry = new THREE.ExtrudeGeometry(branchShape, opts);
        var branchMesh = new THREE.Mesh(branchGeometry, material);
        branchMesh.position.set(x, y, z);
        // rotate 90 degrees
        if (rotate) {
            branchMesh.rotation.set(Math.PI / 2, 0, 0);
        } else {
            branchMesh.rotation.set(0, 0, Math.PI / 2);
        }
        // add branch to the group
        group.add(branchMesh);
    }

Where ‘count’ is amount of branches at row; x, y, z – initial position, opts – options, and so on. The function generates the star mech object on the basis of calculated points in the figure.

其中“计数”是行中分支的数量; x,y,z –初始位置,opts –选项,依此类推。 该函数根据图中计算出的点生成星形机械对象。

Empirically I decided to display 14 rows of branches. Continue to add the code:

根据经验,我决定显示14行分支。 继续添加代码:


    // options
    var options = {
        amount: 6,
        bevelEnabled: true,
        bevelSegments: 5,
        steps: 2
    };
    // add 10 branches
    var iBranchCnt = 14;
    for (i1 = 0; i1 < iBranchCnt; i1++) {
        addBranch(iBranchCnt + 3 - i1, 0, -125 + i1*20, 0, options, materials[1], true);
    }

    // options
    var options = {
        amount: 6,
        bevelEnabled: true,
        bevelSegments: 5,
        steps: 2
    };
    // add 10 branches
    var iBranchCnt = 14;
    for (i1 = 0; i1 < iBranchCnt; i1++) {
        addBranch(iBranchCnt + 3 - i1, 0, -125 + i1*20, 0, options, materials[1], true);
    }

树上的星星 (The star on the tree)

The star on the tree is a star consisting of 5 rays. We may use the ready function ‘addBranch’ to show it:

树上的星星是由5条射线组成的星星。 我们可以使用ready函数'addBranch'来显示它:


    // add the star
    var starOpts = {
        amount: 4,
        bevelEnabled: false
    };
    addBranch(5, 0, 160, -2, starOpts, materials[3], false);

    // add the star
    var starOpts = {
        amount: 4,
        bevelEnabled: false
    };
    addBranch(5, 0, 160, -2, starOpts, materials[3], false);

The only difference is that we have to turn this star 90% on the Z-axis

唯一的不同是我们必须将这颗星在Z轴上旋转90%

圣诞玩具 (Christmas toys)

Christmas toys are a good decoration of the Christmas tree. They need to be located on the branches of the tree. So I decided to slightly modify the function ‘addBranch’, by adding the code to add the balls. Now back to this function, find the row:

圣诞玩具是圣诞树的好装饰。 它们需要位于树的树枝上。 因此,我决定通过添加代码添加球来略微修改“ addBranch”功能。 现在回到此功能,找到以下行:


points.push( new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l));

points.push( new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l));

and add the following code below it:

并在其下面添加以下代码:


    if (rotate && i % 2 == 0) {
        var sphGeometry = new THREE.SphereGeometry(8);
        sphMesh = new THREE.Mesh(sphGeometry, materials[0]);
        sphMesh.position.set(Math.cos(a) * l*1.25, y, Math.sin(a) * l*1.25);
        group.add(sphMesh);
    }

    if (rotate && i % 2 == 0) {
        var sphGeometry = new THREE.SphereGeometry(8);
        sphMesh = new THREE.Mesh(sphGeometry, materials[0]);
        sphMesh.position.set(Math.cos(a) * l*1.25, y, Math.sin(a) * l*1.25);
        group.add(sphMesh);
    }

All balls are spheres (SphereGeometry) with radius of 8.

所有球都是半径为8的球体(SphereGeometry)。

地面 (Ground)

Next step is to add the earth, for which we will use a different texture. As the surface (ground) we will use PlaneGeometry. Add the following code right after the place you finished adding the Star:

下一步是添加地球,为此我们将使用其他纹理。 作为表面(地面),我们将使用PlaneGeometry。 在添加星标之后,立即添加以下代码:


    // add the ground
    var groundColor = new THREE.Color(0xd2ddef);
    var groundTexture = THREE.ImageUtils.generateDataTexture(1, 1, groundColor);
    var groundMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0x111111, map: groundTexture });
    var groundTexture = THREE.ImageUtils.loadTexture('img/ground.jpg', undefined, function() { groundMaterial.map = groundTexture });
    groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
    groundTexture.repeat.set(25, 25);
    groundTexture.anisotropy = 16;
    var groundMesh = new THREE.Mesh( new THREE.PlaneGeometry(20000, 20000), groundMaterial);
    groundMesh.position.y = -150;
    groundMesh.rotation.x = - Math.PI / 2;
    group.add(groundMesh);

    // add the ground
    var groundColor = new THREE.Color(0xd2ddef);
    var groundTexture = THREE.ImageUtils.generateDataTexture(1, 1, groundColor);
    var groundMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0x111111, map: groundTexture });
    var groundTexture = THREE.ImageUtils.loadTexture('img/ground.jpg', undefined, function() { groundMaterial.map = groundTexture });
    groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
    groundTexture.repeat.set(25, 25);
    groundTexture.anisotropy = 16;
    var groundMesh = new THREE.Mesh( new THREE.PlaneGeometry(20000, 20000), groundMaterial);
    groundMesh.position.y = -150;
    groundMesh.rotation.x = - Math.PI / 2;
    group.add(groundMesh);

雪花 (Snowflakes)

Another important element – snowflakes. They represent a Particle System. Continue to add the code:

另一个重要元素–雪花。 它们代表一个粒子系统。 继续添加代码:


    // add snowflakes
    var sfMats = [];
    var sfTexture = THREE.ImageUtils.loadTexture('img/snowflake.png');
    var sfGeometry = new THREE.Geometry();
    for (i = 0; i < 10000; i++) {
        var vertex = new THREE.Vector3();
        vertex.x = Math.random() * 2000 - 1000;
        vertex.y = Math.random() * 2000 - 1000;
        vertex.z = Math.random() * 2000 - 1000;
        sfGeometry.vertices.push(vertex);
    }
    var states = [ [ [1.0, 0.2, 0.9], sfTexture, 10 ], [ [0.90, 0.1, 0.5], sfTexture, 8 ], [ [0.80, 0.05, 0.5], sfTexture, 5 ] ];
    for (i = 0; i < states.length; i++) {
        color  = states[i][0];
        sprite = states[i][1];
        size   = states[i][2];
        sfMats[i] = new THREE.ParticleSystemMaterial({ size: size, map: sprite, blending: THREE.AdditiveBlending, depthTest: false, transparent : true });
        sfMats[i].color.setHSL(color[0], color[1], color[2]);
        particles = new THREE.ParticleSystem(sfGeometry, sfMats[i]);
        particles.rotation.x = Math.random() * 10;
        particles.rotation.y = Math.random() * 10;
        particles.rotation.z = Math.random() * 10;
        group.add(particles);
    }

    // add snowflakes
    var sfMats = [];
    var sfTexture = THREE.ImageUtils.loadTexture('img/snowflake.png');
    var sfGeometry = new THREE.Geometry();
    for (i = 0; i < 10000; i++) {
        var vertex = new THREE.Vector3();
        vertex.x = Math.random() * 2000 - 1000;
        vertex.y = Math.random() * 2000 - 1000;
        vertex.z = Math.random() * 2000 - 1000;
        sfGeometry.vertices.push(vertex);
    }
    var states = [ [ [1.0, 0.2, 0.9], sfTexture, 10 ], [ [0.90, 0.1, 0.5], sfTexture, 8 ], [ [0.80, 0.05, 0.5], sfTexture, 5 ] ];
    for (i = 0; i < states.length; i++) {
        color  = states[i][0];
        sprite = states[i][1];
        size   = states[i][2];
        sfMats[i] = new THREE.ParticleSystemMaterial({ size: size, map: sprite, blending: THREE.AdditiveBlending, depthTest: false, transparent : true });
        sfMats[i].color.setHSL(color[0], color[1], color[2]);
        particles = new THREE.ParticleSystem(sfGeometry, sfMats[i]);
        particles.rotation.x = Math.random() * 10;
        particles.rotation.y = Math.random() * 10;
        particles.rotation.z = Math.random() * 10;
        group.add(particles);
    }

(Light)

Basically – everything is ready and we can start adding the last element of the scene – light. Light sources will be a few: AmbientLight, the flying white PointLight and the secondary blue DirectionalLight. Add the following code:

基本上-一切就绪,我们可以开始添加场景的最后一个元素-灯光。 光源将是少数几个:AmbientLight,飞行的白色PointLight和辅助的蓝色DirectionalLight。 添加以下代码:


    // Add lights:
    // add ambient (global) light
    scene.add( new THREE.AmbientLight(0x222222));
    // add particle of light
    particleLight = new THREE.Mesh( new THREE.SphereGeometry(5, 10, 10), new THREE.MeshBasicMaterial({ color: 0xffffff }));
    particleLight.position.y = 250;
    group.add(particleLight);
    // add flying pint light
    pointLight = new THREE.PointLight(0xffffff, 1, 1000);
    group.add(pointLight);
    pointLight.position = particleLight.position;
    // add directional blue light
    var directionalLight = new THREE.DirectionalLight(0x0000ff, 2);
    directionalLight.position.set(10, 1, 1).normalize();
    group.add(directionalLight);

    // Add lights:
    // add ambient (global) light
    scene.add( new THREE.AmbientLight(0x222222));
    // add particle of light
    particleLight = new THREE.Mesh( new THREE.SphereGeometry(5, 10, 10), new THREE.MeshBasicMaterial({ color: 0xffffff }));
    particleLight.position.y = 250;
    group.add(particleLight);
    // add flying pint light
    pointLight = new THREE.PointLight(0xffffff, 1, 1000);
    group.add(pointLight);
    pointLight.position = particleLight.position;
    // add directional blue light
    var directionalLight = new THREE.DirectionalLight(0x0000ff, 2);
    directionalLight.position.set(10, 1, 1).normalize();
    group.add(directionalLight);

画龙点睛 (Finishing touches)

Let’s add the latest lines of code in the ‘init’ function to create the render object (WebGLRenderer), and adjust it’s properties. We also need to add several UI event handlers. Add the following code:

让我们在“ init”函数中添加最新的代码行,以创建渲染对象(WebGLRenderer),并调整其属性。 我们还需要添加几个UI事件处理程序。 添加以下代码:


    // prepare the render object and render the scene
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
    renderer.setClearColor(scene.fog.color);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);
    renderer.gammaInput = true;
    renderer.gammaOutput = true;
    renderer.physicallyBasedShading = true;
    // add events handlers
    document.addEventListener('mousedown', onDocumentMouseDown, false);
    document.addEventListener('touchstart', onDocumentTouchStart, false);
    document.addEventListener('touchmove', onDocumentTouchMove, false);
    window.addEventListener('resize', onWindowResize, false);

    // prepare the render object and render the scene
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
    renderer.setClearColor(scene.fog.color);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);
    renderer.gammaInput = true;
    renderer.gammaOutput = true;
    renderer.physicallyBasedShading = true;
    // add events handlers
    document.addEventListener('mousedown', onDocumentMouseDown, false);
    document.addEventListener('touchstart', onDocumentTouchStart, false);
    document.addEventListener('touchmove', onDocumentTouchMove, false);
    window.addEventListener('resize', onWindowResize, false);

事件处理函数 (Event handlers functions)

Here all the features added in response to the events of the user interface. You need to add this code immediately after the ‘init’ function:

在这里,所有功能都响应用户界面的事件而添加。 您需要在“ init”函数之后立即添加以下代码:


function onWindowResize() {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth - 20, window.innerHeight - 20 );
}
function onDocumentMouseDown(event) {
    event.preventDefault();
    document.addEventListener('mousemove', onDocumentMouseMove, false);
    document.addEventListener('mouseup', onDocumentMouseUp, false);
    document.addEventListener('mouseout', onDocumentMouseOut, false);
    mouseXOnMouseDown = event.clientX - windowHalfX;
    targetRotationOnMouseDown = targetRotation;
}
function onDocumentMouseMove(event) {
    mouseX = event.clientX - windowHalfX;
    targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
}
function onDocumentMouseUp(event) {
    document.removeEventListener('mousemove', onDocumentMouseMove, false);
    document.removeEventListener('mouseup', onDocumentMouseUp, false);
    document.removeEventListener('mouseout', onDocumentMouseOut, false);
}
function onDocumentMouseOut(event) {
    document.removeEventListener('mousemove', onDocumentMouseMove, false);
    document.removeEventListener('mouseup', onDocumentMouseUp, false);
    document.removeEventListener('mouseout', onDocumentMouseOut, false);
}
function onDocumentTouchStart(event) {
    if (event.touches.length == 1) {
        event.preventDefault();
        mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
        targetRotationOnMouseDown = targetRotation;
    }
}
function onDocumentTouchMove(event) {
    if (event.touches.length == 1) {
        event.preventDefault();
        mouseX = event.touches[0].pageX - windowHalfX;
        targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
    }
}

function onWindowResize() {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth - 20, window.innerHeight - 20 );
}
function onDocumentMouseDown(event) {
    event.preventDefault();
    document.addEventListener('mousemove', onDocumentMouseMove, false);
    document.addEventListener('mouseup', onDocumentMouseUp, false);
    document.addEventListener('mouseout', onDocumentMouseOut, false);
    mouseXOnMouseDown = event.clientX - windowHalfX;
    targetRotationOnMouseDown = targetRotation;
}
function onDocumentMouseMove(event) {
    mouseX = event.clientX - windowHalfX;
    targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
}
function onDocumentMouseUp(event) {
    document.removeEventListener('mousemove', onDocumentMouseMove, false);
    document.removeEventListener('mouseup', onDocumentMouseUp, false);
    document.removeEventListener('mouseout', onDocumentMouseOut, false);
}
function onDocumentMouseOut(event) {
    document.removeEventListener('mousemove', onDocumentMouseMove, false);
    document.removeEventListener('mouseup', onDocumentMouseUp, false);
    document.removeEventListener('mouseout', onDocumentMouseOut, false);
}
function onDocumentTouchStart(event) {
    if (event.touches.length == 1) {
        event.preventDefault();
        mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
        targetRotationOnMouseDown = targetRotation;
    }
}
function onDocumentTouchMove(event) {
    if (event.touches.length == 1) {
        event.preventDefault();
        mouseX = event.touches[0].pageX - windowHalfX;
        targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
    }
}

渲染 (Render)

Finally, add a little more interactivity: making rotate the scene, move the source of light, and the camera zoom in and out of the scene. We need to modify the ready ‘render’ function:

最后,增加一些交互性:旋转场景,移动光源以及相机放大和缩小场景。 我们需要修改现成的“渲染”功能:


function render() {
    var timer = Date.now() * 0.00025;
    group.rotation.y += (targetRotation - group.rotation.y) * 0.01;
    particleLight.position.x = Math.sin(timer * 7) * 300;
    particleLight.position.z = Math.cos(timer * 3) * 300;
    camera.position.x = Math.cos(timer) * 1000;
    camera.position.z = Math.sin(timer) * 500;
    camera.lookAt(scene.position);
    renderer.render(scene, camera);
}

function render() {
    var timer = Date.now() * 0.00025;
    group.rotation.y += (targetRotation - group.rotation.y) * 0.01;
    particleLight.position.x = Math.sin(timer * 7) * 300;
    particleLight.position.z = Math.cos(timer * 3) * 300;
    camera.position.x = Math.cos(timer) * 1000;
    camera.position.z = Math.sin(timer) * 500;
    camera.lookAt(scene.position);
    renderer.render(scene, camera);
}

现场演示

[sociallocker]

[社交储物柜]

打包下载

[/sociallocker]

[/ sociallocker]

结论 (Conclusion)

I think that we have built a great Christmas card today. I hope we have not wasted time and you like the result. Merry Christmas and Happy New Year.

我认为我们今天已经制作了一张很棒的圣诞贺卡。 希望我们没有浪费时间,您喜欢这个结果。 圣诞快乐和新年快乐。

PS: I also wanted to report that was recently completed a redesign of our website, it is now fully responsive and works on mobile devices. This year we also launched a new section of web languages references. Also recently launched a new section of premium resources. It contains the most interesting scripts in the boxed version. If you either want to buy something, or even if you just want to make a donation – you are welcome.

PS:我还想报告一下,最近完成了我们网站的重新设计,现在它可以完全响应并可以在移动设备上使用。 今年,我们还启动了网络语言参考的新部分。 最近还推出了新版的高级资源 。 它包含盒装版本中最有趣的脚本。 如果您想买东西,或者甚至只是想捐款,我们都会欢迎您。

翻译自: https://www.script-tutorials.com/christmas-tree-with-threejs/

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值