WebGL & Three.js 入门

1、简介

WebGL 是在浏览器中实现三维效果的一套规范,而 Three.js 可以看成是浏览器对 WebGL 规范实现的一套封装, 能够让 Web 开发者使用 JavaScript 语言直接和显卡(GPU)进行通信。其中得 GLSL 是GPU 部分对应的编程语言,可以用 GLSL 编写着色器程序,并配合 JavaScript 共同实现 3D 效果。一个简单的 Three.js 程序包含以下几个部分:

<html>
    <head>
        <title>My first Three.js app</title>
        <style>
            body { margin: 0; }
            canvas { width: 100%; height: 100% }
        </style>
    </head>
    <body>
        <script src="//wow.techbrood.com/libs/three.r73.js"></script>
        <script>
            // 1、建立场景
            var scene = new THREE.Scene();
           // 2、建立相机
            var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
            camera.position.z = 5;
           // 3、建立渲染器
            var renderer = new THREE.WebGLRenderer();
            renderer.setSize( window.innerWidth, window.innerHeight );
            document.body.appendChild( renderer.domElement );
           // 4、建立物体
            var geometry = new THREE.BoxGeometry( 1, 1, 1 );
            var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
            var cube = new THREE.Mesh( geometry, material );
            scene.add( cube );

            // 5、开始绘制
            var render = function () {
                requestAnimationFrame( render );
                cube.rotation.x += 0.1;
                cube.rotation.y += 0.1;
                renderer.render(scene, camera);
            };

            render();
        </script>
    </body>
</html>

2、概念介绍

ThreeJS-1

ThreeJS-2
  • 场景

在 Threejs 中场景就只有一种,用THREE.Scene来表示,要构件一个场景也很简单,只要new一个对象就可以了,代码如下:

var scene = new THREE.Scene();

场景是所有物体的容器,如果要显示一个苹果,就需要将苹果对象加入场景中。

  • 相机

相机决定了场景中哪个角度的景色会显示出来,在 Threejs 中有多种相机,这里介绍两种,它们是:
透视相机(THREE.PerspectiveCamera)和正投影相机(THREE.OrthographicCamera),透视相机跟人眼差不多,有近大远小的效果,而正投影相机则没有这个效果,所有东西显示都是一样大

var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
  • 渲染器

渲染器决定了渲染的结果应该画在页面的什么元素上面,并且以怎样的方式来绘制。ThreeJS 框架提供了好几种渲染器对象,分别使用不同的底层技术实现,比如:
WebGLRenderer使用 WebGL 技术;CanvasRenderer使用 Canvas 2D 技术;CSS2DRenderer和CSS3DRenderer则是使用 CSS 技术;

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
  • 物体 Mesh
    Mesh 好比一个包装工,它将『可视化的材质』粘合在一个『数学世界里的几何体』上,形成一个『可添加到场景的对象』。
    当然,创建的材质和几何体可以多次使用(若需要)。而且,包装工不止一种,还有 Points(点集)、Line(线/虚线) 等,比如添加一个几何体到场景中:
var geometry = new THREE.CubeGeometry(1,1,1); 
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material); 
scene.add(cube);

CubeGeometry 表示一个几何体

var point1 = new THREE.Vecotr3(4,8,9);
// or
var point1 = new THREE.Vector3();
point1.set(4,8,9)

创建一条线:线由点组成(准确来说由点,材质和颜色组成),所以先创建点,再组合点(通过Geometry组织点),Threejs 中没有提供单独画点的函数,它必须被放到一个THREE.Geometry形状中

// 方式 1
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial( { vertexColors: true } );
var color1 = new THREE.Color( 0x444444 ), color2 = new THREE.Color( 0xFF0000 );

// 线的材质可以由2点的颜色决定
var p1 = new THREE.Vector3( -100, 0, 100 );
var p2 = new THREE.Vector3(  100, 0, -100 );
// vertices 保存点,colors 保存对应点的颜色
geometry.vertices.push(p1);
geometry.vertices.push(p2);
geometry.colors.push( color1, color2 );

var line = new THREE.Line( geometry, material, THREE.LinePieces );

// 方式 2
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );

for ( var i = 0; i <= 20; i ++ ) {
    var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
    line.position.z = ( i * 50 ) - 500;
    scene.add( line );
}

操作线的某一维坐标本质是做平移

  • 坐标系

Threejs使用的是右手坐标系,x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外

  • 运动原理

要么是摄像机移动(camera.position),要么是物体移动(object.position)

  • 光源

1、环境光
环境光是经过多次反射而来的光,无法确定其最初的方向。环境光是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景指定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。

var light = new THREE.AmbientLight( 0xff0000 );
scene.add( light );

2、点光源
由这种光源放出的光线来自同一点,且方向辐射自四面八方

PointLight( color, intensity, distance )

3、聚光灯
这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。使用这种光源需要指定光的射出方向以及锥体的顶角α

THREE.SpotLight( hex, intensity, distance, angle, exponent )
  • 材质

材质的本质就是光

  • 纹理

纹理的本质就是图片或者说贴图,纹理主要用于丰富材质,
1、用图片创建一个纹理:

// 创建一个几何平面
var geometry = new THREE.PlaneGeometry( 500, 300, 1, 1 );
// 设置纹理坐标
geometry.vertices[0].uv = new THREE.Vector2(0,0);
geometry.vertices[1].uv = new THREE.Vector2(2,0);
geometry.vertices[2].uv = new THREE.Vector2(2,2);
geometry.vertices[3].uv = new THREE.Vector2(0,2);
// 加载纹理
var texture = THREE.ImageUtils.loadTexture("textures/a.jpg",null,function(t)
{
});
// 将纹理运用到材质
var material = new THREE.MeshBasicMaterial({map:texture});
// 结合几何体和材质
var mesh = new THREE.Mesh( geometry,material );
scene.add( mesh );

2、用 canvas 创建纹理

var geometry = new THREE.CubeGeometry(150, 150, 150);
texture = new THREE.Texture( canvas);
var material = new THREE.MeshBasicMaterial({map:texture});
texture.needsUpdate = true;
mesh = new THREE.Mesh( geometry,material );
scene.add( mesh );

需要注意的是在定义了纹理之后,我们将texture.needsUpdate设置为了true,如果不设置为true,那么纹理就不会更新,很可能你看到的是一个黑色的正方体,原因是纹理没有被载入之前,就开始渲染了,而渲染使用了默认的材质颜色

  • 模型

模型是由面组成,面分为三角形和四边形面。三角形和四边形面组成了网格模型。在Three.js中用THREE.Mesh来表示网格模型。THREE.Mesh可以和THREE.Line相提并论,区别是THREE.Line表示的是线条。THREE.Mesh表示面的集合。

THREE.Mesh = function ( geometry, material )

第一个参数geometry:是一个THREE.Geometry类型的对象,他是一个包含顶点和顶点之间连接关系的对象。

第二个参数Material:就是定义的材质。有了材质就能够让模型更好看,材质会影响光照、纹理对Mesh的作用效果。

// VTK loader 使用示例
var material = new THREE.MeshLambertMaterial( { color:0xffffff, side: THREE.DoubleSide } );
var loader = new THREE.VTKLoader();
loader.addEventListener( 'load', function ( event ) {
     var geometry = event.content;
     var mesh = new THREE.Mesh( geometry, material );
     mesh.position.setY( - 0.09 );
     scene.add( mesh );
});
loader.load( "models/vtk/bunny.vtk" );
  • 相机控制
    通过鼠标控制相机 TrackballControls
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.01, 1e10 );
camera.position.z = 0.2;
controls = new THREE.TrackballControls( camera );

controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.panSpeed = 2;

controls.noZoom = false;
controls.noPan = false;

controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
  • 阴影

阴影是增强三维场景效果的重要因素,但 Three.js 出于性能考虑,默认关闭阴影

// 启用阴影
renderer.shadowMap.enabled = true
// 并不是所有类型的光源能产生投影,不能产生投影的光源有:环境光(AmbientLight)、半球光(HemisphereLight)
spotLight.castShadow = true
// 平面和立方体都能接受阴影
plane.receiveShadow = true
cube.receiveShadow = true
// 球体的阴影可以投射到平面和球体上
sphere.castShadow = true
// 更改渲染器的投影类型,默认值是 THREE.PCFShadowMap
renderer.shadowMap.type = THREE.PCFSoftShadowMap
// 更改光源的阴影质量,默认值是 512
spotLight.shadow.mapSize.width = 1024 
spotLight.shadow.mapSize.height = 1024
  • 雾化
// Fog( hex, near, far ),线性雾化。
// near 表示哪里开始应用雾化效果(摄像机为 0)
// far 表示哪里的雾化浓度为 1。若某物体在该距离后,则其表现为雾的颜色。当雾的颜色和渲染器的背景色相同时,则表现为消失(实为颜色相同)。
scene.fog = new THREE.Fog( 0xffffff, 0.015, 100 )

// FogExp2( hex, density ),指数雾化
// density 是雾化强度
scene.fog = new THREE.FogExp2( 0xffffff, 0.01 )

// 雾化效果默认是全局影响的,若某个材质不受雾化效果影响,则可为材质的 fog 属性设置为 false(默认值 true)
var material = new THREE.Material({
    fog: false
})
  • 渲染器剔除模式

在 Three.js 中,材质默认只应用在正面(THREE.FrontSide),即当你旋转物体(或摄像机)查看物体的背面时,它会因为未被应用材质而变得透明(即效果与 CSS3 backface-visibility: hidden 一样)。因此,当你想让物体正反两面均应用材质,则需要在创建材质时声明 side 属性为 THREE.DoubleSide:

var material = new THREE.MeshBasicMaterial({
    side: THREE.DoubleSide // 其他值:THREE.FrontSide(默认值)、THREE.BackSide
})

这个模式应用在3D看房时比较好用


3D看房
  • 声音 Audio

音频也是 3D 的,它会受到摄像机的距离影响:
1、声源离摄像机的距离决定着声音的大小。
2、声源在摄像机左右侧的位置分别决定着左右扬声器声音的大小。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • webGL的3D世界主要由三大要素构成:场景(scene)、相机(camera)和渲染器(renderer),三者...
    YomonAh阅读 2,795评论 0 1
  • Threejs中文文档 郭隆邦技术博客 2018-09-21 20:40:17 关注 Three.js中文文档 今...
    情人波阅读 13,968评论 0 7
  • 本文主要是讲解 Three.js 的相关概念,帮助大家对 Three.js 以及相关知识形成比较完整的理解。今年来...
    Simon王小白阅读 8,435评论 2 9
  • Three.js是一个3DJavaScript库,基于右手坐标系,可以创建简单或是比较复杂的三维图形并应用丰富多彩...
    呆呆的木木阅读 24,270评论 42 59
  • 我不想生活在城市里 因为我怕 太久了会让我窒息 我向往田野竹篱 因为我来自 朴实无华的土地 童年的快乐 如静静流趟...
    春三月阅读 1,035评论 18 42