链接:
【暮志未晚的博客,包含各种模型】:https://www.wjceo.com/blog/threejs/
【中文api】:http://techbrood.com/threejs/docs/
【官网api】:https://threejs.org/docs
【基础概念】: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陀螺仪】:https://www.jianshu.com/p/5769075e9885
【陀螺仪+全景】:https://blog.csdn.net/Aimee1608/article/details/79403684【https://github.com/Aimee1608/3Drotate】【https://h5.xingyuanauto.com/201802/DealerYear/?hmsr=DealerYear】最后一个手机打开
1.场景(Scene):是物体、光源等元素的容器,可以配合 chrome 插件使用,抛出 window.scene即可实时调整 obj 的信息和材质信息。
2.相机(Camera):场景中的相机,代替人眼去观察,场景中只能添加一个,一般常用的是透视相机(PerspectiveCamera)
3.物体对象(Mesh):包括二维物体(点、线、面)、三维物体,模型等等
4.光源(Light):场景中的光照,如果不添加光照场景将会是一片漆黑,包括全局光、平行光、点光源等
5.渲染器(Renderer):场景的渲染方式,如webGL\canvas2D\Css3D。
6.控制器(Control): 可通过键盘、鼠标控制相机的移动
光源对象
光源类型:
环境光(没有特定方向,只有颜色),点光源(灯泡),平行光源(太阳光),聚光灯光源(手电筒)
颜色反射:
【物体白色,光源红色】:呈现红色(白色光源反射所有颜色光源)
【物体红色,光源蓝色】:呈现黑色(红色物体不会反射蓝色光源)
开启阴影[默认false,节约计算资源]:
planeMesh.receiveShadow = true; [接收投影]
mesh.castShadow = true;[产生投影]
directionalLight.castShadow = true;[光源]
renderer.shadowMap.enabled = true;[WebGL渲染器]
设置投影计算区域:
平行光【长方体区域】:
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;
聚光【锥形区域】:
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 300;
spotLight.shadow.camera.fov = 20;
阴影模糊:
原因1:设置阴影对象的mapSize属性,增大
原因1:或者设置计算阴影的区域紧密包围在对象周围
光源设置:
光源颜色:
.color : Color
光照强度:
ambient.intensity = 0.3;
从其它光源复制复制属性:
.copy ( source : Light )
以JSON格式返回Light数据:
.toJSON ( meta : String )
位置属性:
position位置属性
平移方法:
.translateX()沿着x轴平移
.translateY()沿着y轴平移
光照距离(点光源、聚光光源,方向光、环境光不具有该属性):
1:从起点position开始衰减
2:默认0,意味着光线强度不会随着距离衰减
层级模型、树结构
递归遍历方法:
// 遍历场景对象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的子对象。
请注意,大多数的对象中name默认是一个空字符串,要使用这个方法,你将需要手动地设置name属性。
.getObjectById ( id : Integer ) : Object3D
id —— 标识该对象实例的唯一数字。
搜索该对象的子级,返回第一个带有匹配id的子对象。
请注意,id是按照时间顺序来分配的:1、2、3、……,每增加一个新的对象就自增1。
纹理贴图
TextureLoader纹理加载器对象:
textureLoader.load('Earth.png', function(texture) {
var material = new THREE.MeshLambertMaterial({
// color: 0x0000ff,
map: texture,// 设置纹理贴图:Texture对象作为材质map属性的属性值
}); //材质对象Material
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
//纹理贴图加载成功后,调用渲染函数执行渲染操作
// render();
})
图片加载器ImageLoader:
// 图片加载器
var ImageLoader = new THREE.ImageLoader();
// load方法回调函数,按照路径加载图片,返回一个html的元素img对象
ImageLoader.load('Earth.png', function(img) {
// image对象作为参数,创建一个纹理对象Texture
var texture = new THREE.Texture(img);
// 下次使用纹理时触发更新
texture.needsUpdate = true;
var material = new THREE.MeshLambertMaterial({
map: texture, //设置纹理贴图
});
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
});
协作:
webgl程序员只需要加载解析就可以
模型与贴图的对应关系,美术会设置好
视频作为纹理贴图:
// 创建video对象
let video = document.createElement('video');
video.src = "1086x716.mp4"; // 设置视频地址
video.autoplay = "autoplay"; //要设置播放
// video对象作为VideoTexture参数创建纹理对象
var texture = new THREE.VideoTexture(video)
var material = new THREE.MeshPhongMaterial({
map: texture, // 设置纹理贴图
}); //材质对象Material
法线贴图:
通过图片保留几何体表面的几何细节
好处:
低模+法线贴图=高模
降低模型大小,减少顶点的计算
节约顶点数量【简模:导出给程序员使用】
// TextureLoader创建一个纹理加载器对象,可以加载图片作为几何体纹理
var textureLoader = new THREE.TextureLoader();
// 加载法线贴图
var textureNormal = textureLoader.load('./法线贴图/3_256.jpg');
var material = new THREE.MeshPhongMaterial({
color: 0xff0000,
normalMap: textureNormal, //法线贴图
//设置深浅程度,默认值(1,1)。
normalScale: new THREE.Vector2(3, 3),
}); //材质对象Material
凹凸贴图:
图片像素的灰度值表示几何表面的高低深度
如果定义了法线贴图,则将忽略该贴图
// TextureLoader创建一个纹理加载器对象,可以加载图片作为几何体纹理
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,//设置凹凸高度,默认值1。
}); //材质对象Material
MeshLambertMaterial、MeshBasicMaterial 没有凹凸、法线贴图属性,MeshPhongMaterial 有凹凸、法线贴图属性。
光照贴图添加阴影:
//创建一个平面几何体作为投影面
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,// 设置光照贴图
// lightMapIntensity:0.5,//烘培光照的强度. 默认 1.
}); //材质对象Material
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); //网格模型对象Mesh
scene.add(planeMesh); //网格模型添加到场景中
planeMesh.rotateX(-Math.PI / 2); //旋转网格模型
planeMesh.position.y = -50; //设置网格模型y坐标
高光贴图:
/**
* 创建网格模型
*/
// var geometry = new THREE.BoxGeometry(100, 100, 100); //立方体
// var geometry = new THREE.PlaneGeometry(204, 102); //矩形平面
var geometry = new THREE.SphereGeometry(100, 35, 35); //球体
// TextureLoader创建一个纹理加载器对象,可以加载图片作为几何体纹理
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,//高光部分的亮度,默认30
map: texture,// 普通纹理贴图
specularMap: textureSpecular, //高光贴图
}); //材质对象Material
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
环境贴图:
/**
* 创建网格模型
*/
var geometry = new THREE.BoxGeometry(100, 100, 100); //立方体
var loader = new THREE.CubeTextureLoader();
// 所有贴图在同一目录下,可以使用该方法设置共用路径
loader.setPath('环境贴图/');
// 立方体纹理加载器返回立方体纹理对象CubeTexture
var CubeTexture = loader.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
//材质对象Material
var material = new THREE.MeshPhongMaterial({
envMap: CubeTexture, //设置环境贴图
// 环境贴图反射率
// reflectivity: 0.1,
});
console.log(CubeTexture.image);
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
数据纹理对象DataTexture:
var geometry = new THREE.PlaneGeometry(128, 128); //矩形平面
// var geometry = new THREE.BoxGeometry(128, 128,128); //立方体
/**
* 创建纹理对象的像素数据
*/
var width = 32; //纹理宽度
var height = 32; //纹理高度
var size = width * height; //像素大小
var data = new Uint8Array(size * 4); //size*4:像素在缓冲区占用空间
console.log(data);
for (let i = 0; i < size * 4; i += 4) {
// 随机设置RGB分量的值
data[i] = 255 * Math.random()
data[i + 1] = 255 * Math.random()
data[i + 2] = 255 * Math.random()
// 设置透明度分量A
data[i + 3] = 255 * 0.5
}
console.log(data);
// 创建数据文理对象 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, // 设置纹理贴图
transparent:true,//允许透明设置
}); //材质对象Material
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
相机对象(投影方式)
分类:
正投影相机(小的场景,比如产品在线展示、机械、工业设计类三维建模软件模型的显示,物体不受物体与相机的距离影响)
透视投影相机(大的场景,比如游戏场景,随着距离的变化显示的大小会变化)
相机位置属性position
相机目标观察点:
.lookAt()方法
正投影相机OrthographicCamera:
构造函数参数:
透视投影相机PerspectiveCamera:
构造函数参数:
帧动画模块
播放动画:
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作为混合器的参数,可以播放obj包含的帧动画数据
mixer = new THREE.AnimationMixer(obj);
// obj.animations[0]:获得剪辑clip对象
// // 剪辑clip作为参数,通过混合器clipAction方法返回一个操作对象AnimationAction
var AnimationAction = mixer.clipAction(obj.animations[0]);
// AnimationAction.loop = THREE.LoopOnce; //不循环播放
// AnimationAction.clampWhenFinished=true;//暂停在最后一帧播放的状态
AnimationAction.play();
});
混合器AnimationMixer:一个对象及其子对象的动画播放器
操作AnimationAction:设置播放方式、开始播放、暂停播放...
解析外部模型的的帧动画
不支持:比如stl、obj不支持
模型文件加载
加载FBX并解析骨骼动画
var mixer=null;//声明一个混合器变量
var loader = new THREE.FBXLoader();//创建一个FBX加载器
loader.load("SambaDancing.fbx", function(obj) {
// console.log(obj)
scene.add(obj)
obj.translateY(-80);
// obj作为参数创建一个混合器,解析播放obj及其子对象包含的动画数据
mixer = new THREE.AnimationMixer(obj);
// 查看动画数据
console.log(obj.animations)
// obj.animations[0]:获得剪辑对象clip
var AnimationAction=mixer.clipAction(obj.animations[0]);
// AnimationAction.timeScale = 1; //默认1,可以调节播放速度
// AnimationAction.loop = THREE.LoopOnce; //不循环播放
// AnimationAction.clampWhenFinished=true;//暂停在最后一帧播放的状态
AnimationAction.play();//播放动画
})
WebGL渲染器
控制canvas大小:
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);