鏈接:
【暮志未晚的博客,包含各種模型】:https://www.wjceo.com/blog/threejs/
【中文api】:http://techbrood.com/threejs/docs/
【官網(wǎng)api】:https://threejs.org/docs
【基礎(chǔ)概念】:http://www.ituring.com.cn/book/miniarticle/49782
【郭隆邦】:http://www.yanhuangxueyuan.com/
【易百教程】:https://www.yiibai.com/webgl/webgl_graphics_basics.html
【林宏旭博客】:http://www.linhongxu.com/site/index?page=1
【在線編輯】:https://nunustudio.org/editor/index.html
【在線編輯】:https://playcanvas.com/editor/scene/678667
【web攝像頭】:https://blog.csdn.net/lishundi/article/details/80604747
【web陀螺儀】:http://www.itdecent.cn/p/5769075e9885
【陀螺儀+全景】:https://blog.csdn.net/Aimee1608/article/details/79403684【https://github.com/Aimee1608/3Drotate】【https://h5.xingyuanauto.com/201802/DealerYear/?hmsr=DealerYear】最后一個手機(jī)打開
1.場景(Scene):是物體、光源等元素的容器,可以配合 chrome 插件使用,拋出 window.scene即可實(shí)時調(diào)整 obj 的信息和材質(zhì)信息。
2.相機(jī)(Camera):場景中的相機(jī),代替人眼去觀察,場景中只能添加一個,一般常用的是透視相機(jī)(PerspectiveCamera)
3.物體對象(Mesh):包括二維物體(點(diǎn)、線、面)、三維物體,模型等等
4.光源(Light):場景中的光照,如果不添加光照場景將會是一片漆黑,包括全局光、平行光、點(diǎn)光源等
5.渲染器(Renderer):場景的渲染方式,如webGL\canvas2D\Css3D。
6.控制器(Control): 可通過鍵盤、鼠標(biāo)控制相機(jī)的移動
光源對象
光源類型:
環(huán)境光(沒有特定方向,只有顏色),點(diǎn)光源(燈泡),平行光源(太陽光),聚光燈光源(手電筒)
顏色反射:
【物體白色,光源紅色】:呈現(xiàn)紅色(白色光源反射所有顏色光源)
【物體紅色,光源藍(lán)色】:呈現(xiàn)黑色(紅色物體不會反射藍(lán)色光源)
開啟陰影[默認(rèn)false,節(jié)約計算資源]:
planeMesh.receiveShadow = true; [接收投影]
mesh.castShadow = true;[產(chǎn)生投影]
directionalLight.castShadow = true;[光源]
renderer.shadowMap.enabled = true;[WebGL渲染器]
設(shè)置投影計算區(qū)域:
平行光【長方體區(qū)域】:
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 300;
directionalLight.shadow.camera.left = -50;
directionalLight.shadow.camera.right = 50;
directionalLight.shadow.camera.top = 200;
directionalLight.shadow.camera.bottom = -100;
聚光【錐形區(qū)域】:
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 300;
spotLight.shadow.camera.fov = 20;
陰影模糊:
原因1:設(shè)置陰影對象的mapSize屬性,增大
原因1:或者設(shè)置計算陰影的區(qū)域緊密包圍在對象周圍
光源設(shè)置:
光源顏色:
.color : Color
光照強(qiáng)度:
ambient.intensity = 0.3;
從其它光源復(fù)制復(fù)制屬性:
.copy ( source : Light )
以JSON格式返回Light數(shù)據(jù):
.toJSON ( meta : String )
位置屬性:
position位置屬性
平移方法:
.translateX()沿著x軸平移
.translateY()沿著y軸平移
光照距離(點(diǎn)光源、聚光光源,方向光、環(huán)境光不具有該屬性):
1:從起點(diǎn)position開始衰減
2:默認(rèn)0,意味著光線強(qiáng)度不會隨著距離衰減
層級模型、樹結(jié)構(gòu)
遞歸遍歷方法:
// 遍歷場景對象scene obj:每次遍歷的對象
scene.traverse(function(obj) {
// if (obj.type === "Group") {
// console.log(obj.name);
// }
// if (obj.type === "Mesh") {
// console.log(' ' + obj.name);
// obj.material.color.set(0xffff00);
// }
// if (obj.name === "左眼" | obj.name === "右眼") {
// obj.material.color.set(0x000000)
// }
// 打印id屬性
// console.log(obj.id);
// 打印該對象的父對象
// console.log(obj.parent);
// 打印該對象的子對象
// console.log(obj.children);
})
查找方法:
.getObjectByName ( name )
name —— 用于來匹配子物體中Object3D.name屬性的字符串。
搜索該對象的子級,返回第一個帶有匹配name的子對象。
請注意,大多數(shù)的對象中name默認(rèn)是一個空字符串,要使用這個方法,你將需要手動地設(shè)置name屬性。
.getObjectById ( id : Integer ) : Object3D
id —— 標(biāo)識該對象實(shí)例的唯一數(shù)字。
搜索該對象的子級,返回第一個帶有匹配id的子對象。
請注意,id是按照時間順序來分配的:1、2、3、……,每增加一個新的對象就自增1。
紋理貼圖
TextureLoader紋理加載器對象:
textureLoader.load('Earth.png', function(texture) {
var material = new THREE.MeshLambertMaterial({
// color: 0x0000ff,
map: texture,// 設(shè)置紋理貼圖:Texture對象作為材質(zhì)map屬性的屬性值
}); //材質(zhì)對象Material
var mesh = new THREE.Mesh(geometry, material); //網(wǎng)格模型對象Mesh
scene.add(mesh); //網(wǎng)格模型添加到場景中
//紋理貼圖加載成功后,調(diào)用渲染函數(shù)執(zhí)行渲染操作
// render();
})
圖片加載器ImageLoader:
// 圖片加載器
var ImageLoader = new THREE.ImageLoader();
// load方法回調(diào)函數(shù),按照路徑加載圖片,返回一個html的元素img對象
ImageLoader.load('Earth.png', function(img) {
// image對象作為參數(shù),創(chuàng)建一個紋理對象Texture
var texture = new THREE.Texture(img);
// 下次使用紋理時觸發(fā)更新
texture.needsUpdate = true;
var material = new THREE.MeshLambertMaterial({
map: texture, //設(shè)置紋理貼圖
});
var mesh = new THREE.Mesh(geometry, material); //網(wǎng)格模型對象Mesh
scene.add(mesh); //網(wǎng)格模型添加到場景中
});
協(xié)作:
webgl程序員只需要加載解析就可以
模型與貼圖的對應(yīng)關(guān)系,美術(shù)會設(shè)置好
視頻作為紋理貼圖:
// 創(chuàng)建video對象
let video = document.createElement('video');
video.src = "1086x716.mp4"; // 設(shè)置視頻地址
video.autoplay = "autoplay"; //要設(shè)置播放
// video對象作為VideoTexture參數(shù)創(chuàng)建紋理對象
var texture = new THREE.VideoTexture(video)
var material = new THREE.MeshPhongMaterial({
map: texture, // 設(shè)置紋理貼圖
}); //材質(zhì)對象Material
法線貼圖:
通過圖片保留幾何體表面的幾何細(xì)節(jié)
好處:
低模+法線貼圖=高模
降低模型大小,減少頂點(diǎn)的計算
節(jié)約頂點(diǎn)數(shù)量【簡模:導(dǎo)出給程序員使用】
// TextureLoader創(chuàng)建一個紋理加載器對象,可以加載圖片作為幾何體紋理
var textureLoader = new THREE.TextureLoader();
// 加載法線貼圖
var textureNormal = textureLoader.load('./法線貼圖/3_256.jpg');
var material = new THREE.MeshPhongMaterial({
color: 0xff0000,
normalMap: textureNormal, //法線貼圖
//設(shè)置深淺程度,默認(rèn)值(1,1)。
normalScale: new THREE.Vector2(3, 3),
}); //材質(zhì)對象Material
凹凸貼圖:
圖片像素的灰度值表示幾何表面的高低深度
如果定義了法線貼圖,則將忽略該貼圖
// TextureLoader創(chuàng)建一個紋理加載器對象,可以加載圖片作為幾何體紋理
var textureLoader = new THREE.TextureLoader();
// 加載紋理貼圖
var texture = textureLoader.load('./凹凸貼圖/diffuse.jpg');
// 加載凹凸貼圖
var textureBump = textureLoader.load('./凹凸貼圖/bump.jpg');
var material = new THREE.MeshPhongMaterial({
map: texture,// 普通紋理貼圖
bumpMap:textureBump,//凹凸貼圖
bumpScale:3,//設(shè)置凹凸高度,默認(rèn)值1。
}); //材質(zhì)對象Material
MeshLambertMaterial、MeshBasicMaterial 沒有凹凸、法線貼圖屬性,MeshPhongMaterial 有凹凸、法線貼圖屬性。
光照貼圖添加陰影:
//創(chuàng)建一個平面幾何體作為投影面
var planeGeometry = new THREE.PlaneGeometry(300, 200);
planeGeometry.faceVertexUvs[1] = planeGeometry.faceVertexUvs[0];
var textureLoader = new THREE.TextureLoader();
// 加載光照貼圖
var textureLight = textureLoader.load('shadow.png');
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0x999999,
lightMap:textureLight,// 設(shè)置光照貼圖
// lightMapIntensity:0.5,//烘培光照的強(qiáng)度. 默認(rèn) 1.
}); //材質(zhì)對象Material
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); //網(wǎng)格模型對象Mesh
scene.add(planeMesh); //網(wǎng)格模型添加到場景中
planeMesh.rotateX(-Math.PI / 2); //旋轉(zhuǎn)網(wǎng)格模型
planeMesh.position.y = -50; //設(shè)置網(wǎng)格模型y坐標(biāo)
高光貼圖:
/**
* 創(chuàng)建網(wǎng)格模型
*/
// var geometry = new THREE.BoxGeometry(100, 100, 100); //立方體
// var geometry = new THREE.PlaneGeometry(204, 102); //矩形平面
var geometry = new THREE.SphereGeometry(100, 35, 35); //球體
// TextureLoader創(chuàng)建一個紋理加載器對象,可以加載圖片作為幾何體紋理
var textureLoader = new THREE.TextureLoader();
// 加載紋理貼圖
var texture = textureLoader.load('earth_diffuse.png');
// 加載高光貼圖
var textureSpecular = textureLoader.load('earth_specular.png');
var material = new THREE.MeshPhongMaterial({
// specular: 0xff0000,//高光部分的顏色
shininess: 30,//高光部分的亮度,默認(rèn)30
map: texture,// 普通紋理貼圖
specularMap: textureSpecular, //高光貼圖
}); //材質(zhì)對象Material
var mesh = new THREE.Mesh(geometry, material); //網(wǎng)格模型對象Mesh
scene.add(mesh); //網(wǎng)格模型添加到場景中
環(huán)境貼圖:
/**
* 創(chuàng)建網(wǎng)格模型
*/
var geometry = new THREE.BoxGeometry(100, 100, 100); //立方體
var loader = new THREE.CubeTextureLoader();
// 所有貼圖在同一目錄下,可以使用該方法設(shè)置共用路徑
loader.setPath('環(huán)境貼圖/');
// 立方體紋理加載器返回立方體紋理對象CubeTexture
var CubeTexture = loader.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
//材質(zhì)對象Material
var material = new THREE.MeshPhongMaterial({
envMap: CubeTexture, //設(shè)置環(huán)境貼圖
// 環(huán)境貼圖反射率
// reflectivity: 0.1,
});
console.log(CubeTexture.image);
var mesh = new THREE.Mesh(geometry, material); //網(wǎng)格模型對象Mesh
scene.add(mesh); //網(wǎng)格模型添加到場景中
數(shù)據(jù)紋理對象DataTexture:
var geometry = new THREE.PlaneGeometry(128, 128); //矩形平面
// var geometry = new THREE.BoxGeometry(128, 128,128); //立方體
/**
* 創(chuàng)建紋理對象的像素數(shù)據(jù)
*/
var width = 32; //紋理寬度
var height = 32; //紋理高度
var size = width * height; //像素大小
var data = new Uint8Array(size * 4); //size*4:像素在緩沖區(qū)占用空間
console.log(data);
for (let i = 0; i < size * 4; i += 4) {
// 隨機(jī)設(shè)置RGB分量的值
data[i] = 255 * Math.random()
data[i + 1] = 255 * Math.random()
data[i + 2] = 255 * Math.random()
// 設(shè)置透明度分量A
data[i + 3] = 255 * 0.5
}
console.log(data);
// 創(chuàng)建數(shù)據(jù)文理對象 RGBA格式:THREE.RGBAFormat
var texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat);
texture.needsUpdate = true; //紋理更新
//打印紋理對象的image屬性
console.log(texture.image);
var material = new THREE.MeshPhongMaterial({
map: texture, // 設(shè)置紋理貼圖
transparent:true,//允許透明設(shè)置
}); //材質(zhì)對象Material
var mesh = new THREE.Mesh(geometry, material); //網(wǎng)格模型對象Mesh
scene.add(mesh); //網(wǎng)格模型添加到場景中
相機(jī)對象(投影方式)
分類:
正投影相機(jī)(小的場景,比如產(chǎn)品在線展示、機(jī)械、工業(yè)設(shè)計類三維建模軟件模型的顯示,物體不受物體與相機(jī)的距離影響)
透視投影相機(jī)(大的場景,比如游戲場景,隨著距離的變化顯示的大小會變化)
相機(jī)位置屬性position
相機(jī)目標(biāo)觀察點(diǎn):
.lookAt()方法
正投影相機(jī)OrthographicCamera:
構(gòu)造函數(shù)參數(shù):


透視投影相機(jī)PerspectiveCamera:
構(gòu)造函數(shù)參數(shù):


幀動畫模塊
播放動畫:
var loader = new THREE.ObjectLoader();
var mixer = null; //聲明一個混合器變量
// 加載文件返回一個對象obj
loader.load("model.json", function(obj) {
console.log(obj)
obj.scale.set(15, 15, 15);
scene.add(obj);
// obj作為混合器的參數(shù),可以播放obj包含的幀動畫數(shù)據(jù)
mixer = new THREE.AnimationMixer(obj);
// obj.animations[0]:獲得剪輯clip對象
// // 剪輯clip作為參數(shù),通過混合器clipAction方法返回一個操作對象AnimationAction
var AnimationAction = mixer.clipAction(obj.animations[0]);
// AnimationAction.loop = THREE.LoopOnce; //不循環(huán)播放
// AnimationAction.clampWhenFinished=true;//暫停在最后一幀播放的狀態(tài)
AnimationAction.play();
});
混合器AnimationMixer:一個對象及其子對象的動畫播放器
操作AnimationAction:設(shè)置播放方式、開始播放、暫停播放...
解析外部模型的的幀動畫
不支持:比如stl、obj不支持
模型文件加載
加載FBX并解析骨骼動畫
var mixer=null;//聲明一個混合器變量
var loader = new THREE.FBXLoader();//創(chuàng)建一個FBX加載器
loader.load("SambaDancing.fbx", function(obj) {
// console.log(obj)
scene.add(obj)
obj.translateY(-80);
// obj作為參數(shù)創(chuàng)建一個混合器,解析播放obj及其子對象包含的動畫數(shù)據(jù)
mixer = new THREE.AnimationMixer(obj);
// 查看動畫數(shù)據(jù)
console.log(obj.animations)
// obj.animations[0]:獲得剪輯對象clip
var AnimationAction=mixer.clipAction(obj.animations[0]);
// AnimationAction.timeScale = 1; //默認(rèn)1,可以調(diào)節(jié)播放速度
// AnimationAction.loop = THREE.LoopOnce; //不循環(huán)播放
// AnimationAction.clampWhenFinished=true;//暫停在最后一幀播放的狀態(tài)
AnimationAction.play();//播放動畫
})
WebGL渲染器
控制canvas大?。?/p>
renderer.setSize(width, height)
其它:
給模型添加事件
https://www.wjceo.com/blog/threejs/2018-02-13/60.html【其它例子】
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
intersects[ i ].object.material.color.set( 0xff0000 );
}
renderer.render( scene, camera );
}
window.addEventListener( 'mousemove', onMouseMove, false );
window.requestAnimationFrame(render);