THREE.JS之文本对象

来源:互联网 发布:黑马java视频解压密码 编辑:程序博客网 时间:2024/06/15 17:06

      在很多应用中,不光是有图像图像对象,有时还要给场景添加必要的文字说明等等。比图说游戏中的积分,生命值等等,或者使用文本做装饰等等。在three.js中,使用TextGeometry这个类来创建文本。今天要实现的效果如下图:

这里写图片描述

首先创建文件index.html

<!DOCTYPE html><html>    <head>        <title>立体字</title>        <meta charset="utf-8">        <style type="text/css">            *{                padding:0px;margin: 0px            }            #container{                width: 100vw;                height: 100vh;            }        </style>    </head>    <body>        <div id="container"></div>        <script type="text/javascript" src="./build/three.js"></script>        <script type="text/javascript" src="./js/controls/OrbitControls.js"></script>        <script type="text/javascript">        //TODO        </script>    </body></html>

在上面代码中首先引入three.js[官网]文件和OrbitControls.js文件,其中OrbitControls是一个摄像机控制器,用来进行场景漫游。使用控制器来控制视角,更能体现3D的立体效果。

完整的例子步骤如下:
1、创建并初始化渲染器(Renderer);
2、创建并初始化场景(Scene);
3、创建并初始化摄像机(PerspectiveCamera/透视相机),将相机添加到场景中;
4、创建并初始化光源(PointLight/点光源),并将光源添加到场景中;
5、导入字体库,使用字体创建文本形状(TextGeometry),并添加到场景中;
6、创建控制器并初始化控制器(OrbitControls);
7、渲染;

首先创建全局变量,以便共享:

var renderer,scene,camera,controls;

1、创建并初始化渲染器(Renderer);

function initRenderer(){    renderer = new THREE.WebGLRenderer( { antialias: true } );    var width = document.getElementById("container").clientWidth;    var height = document.getElementById("container").clientHeight;    renderer.setSize(width,height);    renderer.setClearColor(0xffffff);    document.getElementById("container").appendChild(renderer.domElement);}

      创建一个WebGLRenderer渲染器,WebGLRenderer使用WebGL来绘制3D图形。three.js一共有两种渲染器,另外一个是CanvasRenderer,使用 Canvas 2D Context API来绘制3D图形。对于开发者来说不管使用哪个渲染器,他们的差别对于我们来说是透明的,也就是说无论使用哪个渲染器,写的代码都是一样的。但是经过我的测试,发现CanvasRenderer的渲染效果明显次于WebGLRenderer,在场景漫游的过程重出现了很严重的跳帧。而且CanvasRenderer已经被移除了标准库three.js,如果使用需要使用,必须引入文件“/examples/js/renderers/CanvasRenderer.js”。

.setClearColor() 设置清除色,也就是背景色;
renderer.domElement 就是渲染器中的canvas对象,将canvas对象添加到页面中才能看到渲染效果。

2、创建并初始化场景(Scene);

function initScene(){    scene = new THREE.Scene();    scene.fog = new THREE.Fog( 0x000000, 0, 3000 );}

给场景添加烟雾效果;
new THREE.Fog( color,near,far),其中:
color:十六进制,烟雾的颜色值;
near:烟雾的范围,沿Z轴的近端;
far:烟雾的范围,沿Z轴的远端;

3、创建并初始化摄像机(PerspectiveCamera/透视相机),将相机添加到场景中;

function initCamera(){    var width = document.getElementById("container").clientWidth;    var height = document.getElementById("container").clientHeight;    camera = new THREE.PerspectiveCamera(30, width/height,1, 10000);    camera.position.y=800/Math.tan(Math.PI/2.5);    camera.position.z=800;}

PerspectiveCamera( fov, aspect, near, far ):
fov:视角,视角越大视野越大,但是看到的物体越小;
aspect:纵横比,一般设置为canvas的width/height,否则场景中的物体会比例失调;
near:与相机的距离近端,当物体与相机的距离小于该值时不渲染;
far:与相机的距离近远端,当物体与相机的距离大于该值时不渲染;
相机的默认位置是放在圆点,重置器position的x/y/z来调整相机的位置;
相机的焦点默认是原点,可以使用camera.lookAt(THREE.vector3())来改变焦点;

4、创建并初始化光源(PointLight/点光源),并将光源添加到场景中;

function initLight(){    var pointLight = new THREE.PointLight( 0xffffff, 1);        pointLight.position.set( 0, 100, 100 );        scene.add( pointLight );    var pointLight = new THREE.PointLight( 0xffffff, 1);        pointLight.position.set( 0, 100, -100 );        scene.add( pointLight );}

three.js中的光源有5种:
DirectinalLight:直线光;
PointLignt:点光源;
SpotLight:聚光灯;
AmbientLight:环境光;
HemisphereLight:半球光(天光);

THREE.PointLight(color, intensity):
color:光的颜色;
intensity:光强;

5、导入字体库,使用字体创建文本形状(TextGeometry),并添加到场景中;

function initText(){    var loader = new THREE.FontLoader();    loader.load('../examples/fonts/optimer_bold.typeface.json',function(response){        var font = response;        var textGeometry = new THREE.TextGeometry("three.js",{            "font" : font,            "size" : 70,            "height" : 20,            "bevelEnabled" : true,            "bevelSize": 2        })        text = new THREE.Mesh(textGeometry,new THREE.MultiMaterial( [            new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ),            new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } )         ] ))        textGeometry.computeBoundingBox();        var centerOffset = -0.5 * (textGeometry.boundingBox.max.x-textGeometry.boundingBox.min.x);        text.position.x = centerOffset;        text.position.y = 30;        var mirror = new THREE.Mesh(textGeometry,new THREE.MultiMaterial( [            new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ),            new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } )        ] ))        mirror.rotation.x = Math.PI;        mirror.position.x = centerOffset;        mirror.position.z = 20;        mirror.position.y = -30;        scene.add(text);        scene.add(mirror);        var plane = new THREE.Mesh(        new THREE.PlaneBufferGeometry( 10000, 10000 ),        new THREE.MeshBasicMaterial( { color: 0xfffffff, opacity: 0.5, transparent: true } )        );        plane.rotation.x = -Math.PI/2;          plane.position.y = 0;        scene.add(plane);    })}

var loader = new THREE.FontLoader()
创建字体加载器;

loader.load(src, callback)
加载字体库,加载成功后将字体库传给回调函数;

var textGeometry = new THREE.TextGeometry("three.js",{    "font" : font,    "size" : 70,    "height" : 20,    "bevelEnabled" : true,    "bevelSize": 2})

创建字体几何图形,其中TextGeometry的第一个参数是需要绘制的文本,第二个参数是一个json对象,设置如何绘制文本,具体意义为:
font: 使用的字体库;
size:绘制字体的大小;
height:绘制文本的厚度;
bevelEnabled:是否允许棱角平滑过渡;
bevelSize:棱角平滑过渡的尺寸;

注意:bevelSize通常要比size和height小一个量级,过渡带就会很饱满甚至比文本主体本身还要大

在3D世界里所有的物体都是由网格构成的,在three.js中,绘制图形的步骤是:先使用geometry定义图形的几何形状,然后使用几何形状和材质构建网格;
这里写图片描述

text = new THREE.Mesh(textGeometry,new THREE.MultiMaterial( [    new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ),    new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) ] ))

这里使用材质数组来对网格进行修饰,材质数组的第一项修饰文字正面和背面,第二项修饰文字的侧面即顶部和底部。使用的两个材质都是MeshPhongMaterial,这种材质的特点是能够像塑料一样反光。

可以认为文本对象是放在一个刚好容纳其内容的立方体中,而默认情况下,这个立方体的(底,背,左)的点与原点重合,然后沿x轴向右延伸。这样的话,文本就不会显示在视野中央。这样的话,就要计算出包含文本外部的立方体,然后使用立方体的顶点位置,重新计算出其起始位置,重置位置。

textGeometry.computeBoundingBox();var centerOffset = -0.5 * (textGeometry.boundingBox.max.x-textGeometry.boundingBox.min.x);text.position.x = centerOffset;text.position.y = 30;

使用同样的方法创建一个倒影,最后不要忘记把对象添加到场景中。

scene.add(text);scene.add(mirror);

为了使倒影的效果更逼真,给场景添加一个半透明的平面,把文本真身和倒影分开。

var plane = new THREE.Mesh(    new THREE.PlaneGeometry( 10000, 10000 ),    new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true } ));plane.rotation.x = -Math.PI/2;  plane.position.y = 0;scene.add(plane);

THREE.PlaneGeometry( 10000, 10000 )创建一个10000x10000的平面,transparent: true设置该平面透明, opacity: 0.5透明度是0.5。此时平面是与x0y平面重合,也就是和屏幕平面重合,然后旋转平面到xoz平面上。

6、创建控制器并初始化控制器(OrbitControls);

function initControls(){    controls = new THREE.OrbitControls(camera);    controls.enableZoom = true;    controls.minPolarAngle = Math.PI/2.5;    controls.maxPolarAngle = Math.PI/2.5;}

controls.enableZoom = true; //支持缩放;

controls.minPolarAngle = Math.PI/2.5; //限制竖直方向上最小旋转角度 y轴正向为0度
controls.maxPolarAngle = Math.PI/2.5; //限制竖直方向上最大旋转角度 y轴正向为0度

7、渲染;

function render(){    renderer.render(scene, camera);    requestAnimationFrame(render);  }function start(){    initRenderer();    initScene();    initCamera();    initControls();    initLight();    initText();    render();}start();

到这里,文章开头的效果已经出来了,然后补充一下requestAnimationFrame()这个方法,requestAnimationFrame是为了做动画渲染而诞生的,其作用就是每个一段时间调用一下传入的回调函数,那么他与setTimeout()有什么区别呢?
大部分浏览器的刷新周期是16.7ms,因为1000/60=16.7;但是也不尽然,如果我们使用setTimeout()来调用渲染函数,而我们设置的刷新时间间隔与浏览器的刷新周期不一致的话,就会导致画面延时刷新。比如说,浏览器的刷新周期是16.7ms,而我们设置的时间是10ms,那么程序就要求浏览器超负荷地去刷新,但是事实上浏览器并不会这么做,而是等待到16.7ms才刷新,导致卡帧。而requestAnimationFrame()则不需要我们设置时间,他的刷新周期是和浏览器的刷新周期同步。而且,当网页被挂起之后,requestAnimationFrame将不再刷新,比setTimeout节省资源。

完整代码

<!DOCTYPE html><html><head>    <title>立体字</title>    <meta charset="utf-8">    <style type="text/css">        *{            padding:0px;margin: 0px        }        #container{            width: 100%;            height: 100vh;        }    </style></head><body>    <div id="container"></div>    <script type="text/javascript" src="../build/three.js"></script>    <script type="text/javascript" src="../examples/js/renderers/CanvasRenderer.js"></script>    <script type="text/javascript" src="../examples/js/renderers/Projector.js"></script>    <script type="text/javascript" src="../examples/js/controls/OrbitControls.js"></script>    <script type="text/javascript" src="./Stats.js"></script>    <script type="text/javascript">        var renderer,scene,camera,controls;        function initRenderer(){            renderer = new THREE.WebGLRenderer( { antialias: true } );            var width = document.getElementById("container").clientWidth;            var height = document.getElementById("container").clientHeight;            renderer.setSize(width,height);            renderer.setClearColor(0xffffff);            document.getElementById("container").appendChild(renderer.domElement);            stats = new Stats();            stats.setMode(0); // 0: fps, 1: ms            // 将stats的界面对应左上角            stats.domElement.style.position = 'absolute';            stats.domElement.style.left = '0px';            stats.domElement.style.top = '0px';            document.body.appendChild( stats.domElement);        }        function initScene(){            scene = new THREE.Scene();            scene.fog = new THREE.Fog( 0x000000, 0, 5000 );        }        function initCamera(){            var width = document.getElementById("container").clientWidth;            var height = document.getElementById("container").clientHeight;            camera = new THREE.PerspectiveCamera(30, width/height,1, 10000);            camera.position.y=800/Math.tan(Math.PI/2.5);            camera.position.z=800;        }        function initControls(){            controls = new THREE.OrbitControls(camera);            controls.enableZoom = true;            controls.minPolarAngle = Math.PI/2.5;            controls.maxPolarAngle = Math.PI/2.5;            controls.autoRotate = false;        }        function initLight(){            var pointLight = new THREE.PointLight( 0xffffff, 1);                pointLight.position.set( 0, 100, 100 );                scene.add( pointLight );            var pointLight = new THREE.PointLight( 0xffffff, 1);                pointLight.position.set( 0, 100, -100 );                scene.add( pointLight );        }        function initText(){            var loader = new THREE.FontLoader();            loader.load('../examples/fonts/optimer_bold.typeface.json',function(response){                font = response;                var textGeometry = new THREE.TextGeometry("three.js",{                    "font" : font,                    "size" : 70,                    "height" : 20,                    "bevelEnabled" : true,                    //"curveSegments" : 1,                    bevelSize: 2,                    // material: 0,                    // extrudeMaterial: 1                })                text = new THREE.Mesh(textGeometry,new THREE.MultiMaterial( [                    new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ), // front                    new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) // side                ] ))                textGeometry.computeBoundingBox();                //textGeometry.computeVertexNormals();                var centerOffset = -0.5 * (textGeometry.boundingBox.max.x-textGeometry.boundingBox.min.x);                text.position.x = centerOffset;                text.position.y = 30;                var mirror = new THREE.Mesh(textGeometry,new THREE.MultiMaterial( [                    new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } ), // front                    new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.SmoothShading } ) // side                ] ))                mirror.rotation.x = Math.PI;                mirror.position.x = centerOffset;                mirror.position.z = 20;                mirror.position.y = -30;                scene.add(text);                scene.add(mirror);                var plane = new THREE.Mesh(                    new THREE.PlaneGeometry( 10000, 10000 ),                    new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true } )                );                plane.rotation.x = -Math.PI/2;                  plane.position.y = 0;                scene.add(plane);            })        }        function render(){            renderer.render(scene, camera);            stats.update();            requestAnimationFrame(render);        }        function start(){            initRenderer();            initScene();            initCamera();            initControls();            initLight();            initText();            render();        }        start();    </script></body></html>
0 0
原创粉丝点击