THREE.js 学习笔记(一)

一. 获取THREE.js

three.js的代码托管在github上面,https://github.com/mrdoob/three.js/

我们可以用git来获取代码,闲麻烦的话也可以直接下载zip文件。

二. 目录结构

拿到代码后先看下three.js的目录结构

|-build

|-custom

|-Three.js

|-examples

|-gui

|-src

|-cameras

|-core

|-extras

|-lights

|-materials

|-objects

|-renderers

|-scenes

|-textures

|-Three.js

|-utils

|-compiler

|-exporters

|-build.bat

|-build.sh

|-build.xml

|-build_all.bat

|-build_all.sh

  1. build目录下是源代码连接压缩过后的js文件,而连接和压缩源代码的工具放在utils目录下,在utils目录下还有一个exporters目录,是各种模型导出工具,插件,有blender和max的导出插件,还有把fbx转成THREE.js场景文件的python脚本。
  2. examples目录下都是three.js的示例,值得一提的是,里面有很多实用的shader脚本和js脚本可以拿来在自己的项目里用,比如js/ShaderExtra.js提供了很多现成的shader代码,js/postprocessing下提供了封装起来的几种常用的后处理的接口
  3. gui下面是一些封装后的ui接口
  4. src当然就是重头戏了,three.js的源代码都在这个目录下,src里下的各个子目录也很好的体现了three.js的构成,像camera,light,object这些都是一个场景(scene)的基础对象,而scenes下面则是对整个场景的管理代码,像scene graph的实现,renderers下是核心渲染器的实现,three.js对于场景和渲染器分离的还不错,可以用不同的renderer渲染同一个场景,当然对于一些WebGL支持的高级特性,使用其它的renderer肯定是不行的。还有一个extras目录是在核心代码之上的一些未程序提供便利的接口,比如提供了一些常用的camera,material,light。
  5. utils目录上面已提过

三. example分析 - webgl trackballcamera earth

examples目录下有该示例

在这个example里我们能够看到:

  • 一个最基本的three.js应用需要有的东西
  • camera的创建以及camera control的添加
  • 基本形体的创建,材质的创建
  • 粒子系统的创建,这个会后面讲。

这里不会逐行逐行代码的分析,而是对于每个特性挑出代码来讲。

一个three.js应用的基本结构。

不管用什么写3d应用,c++的ogre,flash的pv3d,js的o3d,又或者使用场景编辑器,一个3d场景所需要的最基本的东西都是一样的,一个主要的camera,一个主要的scene。

当然一般的场景里都会有物体,有灯光,每个物体都有材质。我们在three.js中可以一个个手动创建,也可以直接加载一个记录场景数据的json文件。

创建一个scene

scene = new THREE.Scene();

创建一个摄像机

camera = new THREE.PerspectiveCamera( 25, width / height, 50, 1e7 );camera.position.z = radius * 7;

这段代码确定了一个摄像机的视锥,四个参数分别是摄像机的视锥角度,视口的长宽比,摄像机的近切面(Front Clipping Plane)和远切面(Back Clipping Plane),为什么要四个参数?其实摄像机本质上就是一个投影矩阵,而建立一个透视投影的矩阵(还有正交投影)需要这四个参数,形象点可以看下图

从图中可以看到,要唯一确定一个透视的视锥(Viewing Frustum)至少需要上述的四个参数。

调整摄像机的位置和朝向

创建一个摄像机还需要摆好这个摄像机的位置和朝向,three.js里可以用camera.lookAt函数来设置摄像机的朝向,用camera.position设置摄像机的位置

这个demo里由于创建了一个实现轨迹球控制效果的TrackballControls,因此camera的变换都被封装在这里面了。

如果看lookAt的代码,其实这些操作都是矩阵的操作,摄像机本质上和一个场景中的实体无异,都是使用变换矩阵来做变换。

下面要为场景中添加一些东西了

我们可以在演示中大概看到这个场景中有一个地球,一个月球,周围的太空,还有一个一直照着地球模拟太阳的光照,如果看得仔细点,我们还会发现其实地球外面还包着一层大气层。

下面要一一在场景中添加进入这些东西

1. 首先是地球

在WebGL里创建一个object,我们需要的最基本的数据就是这个object的顶点位置,当然,如果需要这个物体能够入眼的话,我们还需要为它编写shader,需要传入顶点的法线数据,需要传入texcoord来完成纹理映射。

说到shader,我们先看下WebGL中渲染一个物体的基本顺序:

程序会先载入,编译和绑定shader代码,每个渲染批次,显卡都会将这些顶点数据传入流水线,在流水线中会通过Vertex Shader进行顶点位置的变换,光栅化,Fragment Shader中对每个像素计算颜色,最后深度测试等等后输出到屏幕。

THREE.js中将物体顶点数据的管理封装成为geometry接口,将shader和shader中参数的管理封装成为material接口,每次编译加载绑定shader,传入顶点数据都会在WebGLRenderer中统一处理。

var materialNormalMap = new THREE.ShaderMaterial({    fragmentShader: shader.fragmentShader,    vertexShader: shader.vertexShader,    uniforms: uniforms,    lights: true});

这里便是创建了一个材质,传入了fragmentShader和vertexShader的代码,uniforms是这两个shader里的参数。

这两个都是可以拿来主义的

var shader = THREE.ShaderUtils.lib[ "normal" ],    uniforms = THREE.UniformsUtils.clone( shader.uniforms );

uniforms[ "tNormal" ].texture = normalTexture;uniforms[ "uNormalScale" ].value = 0.85;

uniforms[ "tDiffuse" ].texture = planetTexture;uniforms[ "tSpecular" ].texture = specularTexture;

uniforms[ "enableAO" ].value = false;uniforms[ "enableDiffuse" ].value = true;uniforms[ "enableSpecular" ].value = true;

uniforms[ "uDiffuseColor" ].value.setHex( 0xffffff );uniforms[ "uSpecularColor" ].value.setHex( 0x333333 );uniforms[ "uAmbientColor" ].value.setHex( 0x000000 );

uniforms[ "uShininess" ].value = 15;


于是这里创建了一个法线贴图的材质(关于法线贴图的原理,这里先不多讲),并且设置好了它的各个参数,其中tNnormal是法线纹理,tDiffuse是漫反射纹理,tSpecular是高光纹理,

tDiffuse,tSpecular和tNormal

uDiffuseColor, uSpecularColor, uAmbientColor, uShininess这四个是Phong模型光照的参数,

uDiffuseColor:漫反射颜色

uSpecularColor:高光颜色

uAmbientColor:环境光颜色

uShininess:物体表面光滑度

下面这段便是创建这个地球本身了,并且加入到场景里面

geometry = new THREE.SphereGeometry( radius, 100, 50 );geometry.computeTangents();meshPlanet = new THREE.Mesh( geometry, materialNormalMap );meshPlanet.rotation.y = 1.3;meshPlanet.rotation.z = tilt;scene.add( meshPlanet );

最后看一下这个添加到scene里的Mesh的组成图

2. 建月球和云层也是依法炮制,云层因为是png格式所以有半透明效果。

3. 创建平行光

创建一个光照是很简单的,tree.js的灯光对象主要就是保存灯光参数的作用,而光照的实际计算是放在shader里,我们暂时不用关心

dirLight = new THREE.DirectionalLight( 0xFFFFFF );dirLight.position.set( -1, 0, 1 ).normalize();scene.add( dirLight );

场景的互动

sample中我们可以使用鼠标控制摄像机的旋转,three.js为我们提供一些常用的摄像机控制接口,这个sample里用的是轨迹球

controls = new THREE.TrackballControls( camera, renderer.domElement );controls.rotateSpeed = 1.0;controls.zoomSpeed = 1.2;controls.panSpeed = 0.2;

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

controls.staticMoving = false;controls.dynamicDampingFactor = 0.3;

controls.minDistance = radius * 1.1;controls.maxDistance = radius * 100;

我们可以在src/extras/controls下面看到几个常用的摄像机控制

渲染

我们最后来看看render函数里的代码

var t = new Date().getTime(),dt = ( t - time ) / 1000;time = t;

meshPlanet.rotation.y += rotationSpeed * dt;meshClouds.rotation.y += 1.25 * rotationSpeed * dt;

var angle = dt * rotationSpeed;

meshMoon.position = new THREE.Vector3(    Math.cos( angle ) * meshMoon.position.x - Math.sin( angle ) * meshMoon.position.z,    0,    Math.sin( angle ) * meshMoon.position.x + Math.cos( angle ) * meshMoon.position.z);meshMoon.rotation.y -= angle;

controls.update();

renderer.clear();renderer.render( scene, camera );

这个函数主要做了每一帧都要做的几件事:

  • 计算这一帧的时间
  • 旋转地球
  • 计算月球位置
  • 更新摄像机控制
  • 渲染

最后render函数里需要两个参数,scene和camera,我们如果看下render里的代码话就可以知道,每次render的时候,都需要遍历一遍scene graph,渲染里面每个需要渲染的对象,而摄像机的作用就只是提供一个视角变换矩阵和投影矩阵。这个以后看WebGLRenderer里的代码的时候会继续深入。

这个函数会使用setInterval定时调用,来实现动画的效果。

时间: 2024-11-05 19:01:10

THREE.js 学习笔记(一)的相关文章

Vue.js学习笔记:在元素 和 template 中使用 v-if 指令

f 指令 语法比较简单,直接上代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> <script src="https://cdn.bootcss.com/vue/2.2.

[Pro Angular.JS]学习笔记1.1:设置开发环境

可以使用yeoman.io,很方便.我已经写了一篇随笔,介绍如何使用.这里也有一篇介绍的文章:http://www.cnblogs.com/JoannaQ/p/3756281.html 代码编辑器,在Mac下用了一下WebStorm,太恶心了.另外发现书的作者使用的开发环境是Windows + VS Express 2013,为了方便学习,我也使用VS Express 2013 Update2.VS2013用起来蛮舒服的,把WebStorm比得跟驼屎一样.也许是因为我没用习惯吧. 1.安装Nod

【转】Backbone.js学习笔记(二)细说MVC

文章转自: http://segmentfault.com/a/1190000002666658 对于初学backbone.js的同学可以先参考我这篇文章:Backbone.js学习笔记(一) Backbone源码结构 1: (function() { 2: Backbone.Events // 自定义事件 3: Backbone.Model // 模型构造函数和原型扩展 4: Backbone.Collection // 集合构造函数和原型扩展 5: Backbone.Router // 路由

Angular JS 学习笔记

特定领域语言 编译器:遍历DOM来查找和它相关的属性, 分为编译和链接两个阶段, 指令:当关联的HTML结构进入编译阶段时应该执行的操作,可以写在名称里,属性里,css类名里:本质上是函数 稳定的DOM:绑定了数据模型的DOM元素的实例不会在绑定的生命周期发生改变 作用域:用来检测模型的改变和为表达式提供执行上下文的 AngularJS 和其它模板系统不同,它使用的是DOM而不是字符串 指令: 由某个属性.元素名称.css类名出现而导致的行为,或者说是DOM的变化 Filter过滤器:扮演着数据

Node.js学习笔记(3) - 简单的curd

这个算是不算完结的完结吧,前段时间也是看了好久的Node相关的东西,总想着去整理一下,可是当时也没有时间: 现在看来在整理的话,就有些混乱,自己也懒了,就没在整理,只是简单的记录一下 一.demo的简单介绍 这次demo,只涉及到简单的curd操作,用到的数据库是mongo,所以要安装mongo数据库,数据库连接驱动是mongoose: 当然关于mongo的驱动有很多,比如mongous mongoskin等:(详见http://cnodejs.org/topic/4f4ca8e0940ce2e

JS学习笔记-OO疑问之封装

封装是面向对象的基础,今天所要学习的匿名函数与闭包就是为了实现JS的面向对象封装.封装实现.封装变量,提高数据.系统安全性,封装正是面向对象的基础. 匿名函数 即没有名字的函数,其创建方式为 function(){...} 单独存在的匿名函数,无法运行,可通过赋值给变量调用或通过表达式自我执行来实现运行. 1.赋值给变量为一般的函数使用方式 var run = function(){ return '方法运行中'; }; alert(run()); 2.通过表达式自我执行 (function(a

每日js学习笔记2014.5.4

<script language="javascript">var mystring = "这是第一个正则表达式的例子";var myregex = new RegExp("这是"); //创建正则表达式if (myregex.test(mystring)) //test的功能是查找字符串中是否有匹配项,有则返回true,没有则返回false{ document.write ("找到了指定的模式");}else{

每日js学习笔记2014.5.5

<script language="javascript"><!-- var textstr = prompt("请输入一个字符串:",""); //prompt的用法,包含两个参数 var regex = /[A-Z][a-z]tion/; //[A-Z]有无匹配项 var result = regex.test(textstr); //test的用法 document.write ("<font size='

JS学习笔记-OO疑问之对象创建

问一.引入工厂,解决重复代码 前面已经提到,JS中创建对象的方法,不难发现,基本的创建方法中,创建一个对象还算简单,如果创建多个类似的对象的话就会产生大量重复的代码. 解决:工厂模式方法(添加一个专门创建对象的方法,传入参数避免重复) function createObject(name,age){ var obj =new Object(); //创建对象 obj.name = name; obj.age = age; obj.run = function(){ return this.nam

js学习笔记知识点

AJAX用法安全限制JSONPCORS面向对象编程创建对象构造函数原型继承class继承 AJAX 用法 AJAX不是JavaScript的规范,它只是一个哥们"发明"的缩写:Asynchronous JavaScript and XML,意思就是用JavaScript执行异步网络请求 在现代浏览器上写AJAX主要依靠XMLHttpRequest对象: 'use strict'; function success(text) { var textarea = document.getE