目录
1 安装VSCode ,并安装插件live-server
找到后点击安装就行。
2 下载官方three.js到本地
本机搭建个Apache的服务器,把解压后的文件夹放到WWW文件夹下,运行本机服务器,就可以本地产看文档和运行案例了。
3 学习案例webgl_materials_car
创建一个文件夹StudyThreejs,把解压后的three.js-r163放到里面,找到文件webgl_materials_car.html进行学习。
3.1 car.html部分
用VSCode 打开文件夹StudyThreejs,先创建文件car.html和car.js
打开car.html:
style模块把main.css里的#info复制粘贴过来,这一部分是界面的文字和按钮的布局
去掉界面滚动条
3.2 body部分
注意:
这里一开始弄错了,修改后OK了。
3.3 car.html全部脚本
car.html如下,这一部分前度的知识我还是不太会哈
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>threejs car 学习</title>
<!-- <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> -->
<!-- <link type="text/css" rel="stylesheet" href="./three.js-r163/examples/main.css"> -->
<style>
body{
color: #bbbbbb;
background: #333333;
overflow: hidden; /*不用滚动条*/
margin: 0px ;
}
a {
color: #08f;
}
.colorPicker{
display: inline-block;
margin: 0 10px
}
/*
按钮和文字的布局
*/
#info {
position: absolute;
top: 0px;
width: 100%;
padding: 10px;
box-sizing: border-box;
text-align: center;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
z-index: 1; /* TODO Solve this in HTML */
}
/* 添加这一行 几个颜色才能进行点击操作 */
input{
pointer-events: auto;
}
</style>
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> car materials<br/>
Ferrari 458 Italia model by <a href="https://sketchfab.com/models/57bf6cc56931426e87494f554df1dab6" target="_blank" rel="noopener">vicent091036</a>
<br><br>
<span class="colorPicker"><input id="body-color" type="color" value="#ff0000"></input><br/>Body</span>
<span class="colorPicker"><input id="details-color" type="color" value="#ffffff"></input><br/>Details</span>
<span class="colorPicker"><input id="glass-color" type="color" value="#ffffff"></input><br/>Glass</span>
</div>
<div id="container"></div>
<!-- <div id="webgl" style="margin-top: 0px; margin-left: 0px;"> </div> -->
<!--z这个使得js里 引入three.js方便 好像使得和正式开发时写法一致 -->
<script type="importmap">
{
"imports": {
"three": "./three.js-r163/build/three.module.js",
"three/addons/":"./three.js-r163/examples/jsm/"
}
}
</script>
<!-- 引入car.js -->
<script type="module" src="car.js"> </script>
</body>
</html>
3.2 car.js部分
针对car.js:就是看着webgl_materials_car.html里的代码抄写一遍加深记忆,部分地方有自己的修改,比如记载改为了异步加载。
3.2.1 加载gltf模型
先在Init()里加载模型,可以看到是一个黑色的车
//引入Threejs
import * as THREE from 'three';
//引入gltf模型加载器GLTFLoader
import {GLTFLoader} from "three/addons/loaders/GLTFLoader.js";
import {DRACOLoader} from "three/addons/loaders/DRACOLoader.js";
//定义几个属性
let camera,scene,renderer;
function init(){
//实例化场景
scene=new THREE.Scene();
scene.background=new THREE.Color(0xffffff);
//实例化相机
camera=new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
//相机位置
camera.position.set(4.25,1.4,-4.5);
camera.lookAt(0,0,0);
//通过id获取html的canvas
const container = document.getElementById('container');
//实例化渲染器
renderer=new THREE.WebGLRenderer({
antialias:true, //是否执行抗锯齿。默认为false
});
renderer.setSize(window.innerWidth,window.innerHeight);//将输出canvas的大小调整为(width, height)并考虑设备像素比
renderer.setPixelRatio( window.devicePixelRatio );//设置设备像素比。通常用于避免HiDPI设备上绘图模糊
renderer.setAnimationLoop( render ); //请求再次执行函数animate 渲染下一帧 每个可用帧都会调用的函数render
renderer.toneMapping = THREE.ACESFilmicToneMapping;//色调映射 使用tonemapping可以塑造更真实的效果
renderer.toneMappingExposure = 0.85;//色调映射的曝光级别。默认是1
//把渲染的domElement添加到container上 要不画面看不到
container.appendChild(renderer.domElement);
//一个用于加载经过Draco压缩的图形库
const dracLoader=new DRACOLoader();
//参数是包含JS和WASM解压缩库的文件夹路径
dracLoader.setDecoderPath('./three.js-r163/examples/jsm/libs/draco/gltf/');
//gltf模型加载
const gltfLoader=new GLTFLoader();
//Draco是一个开源的库,主要用于压缩和解压缩三维模型及点云。 以客户端上解压缩为代价,显著减少压缩的图形。
//独立的Draco文件后缀为.drc,其中包含顶点坐标,法线,颜色和其他的属性, Draco文件*不*包含材质,纹理,动画和节点结构-为了能使用这些特征,需要将Draco图形 嵌入到GLTF文件中。使用glTF-Pipeline可以将一个普通的GLTF文件转化为经过Draco压缩的GLTF文件。 当使用Draco压缩的GLTF模型时,GLTFLoader内部会调用DRACOLoader
gltfLoader.setDRACOLoader(dracLoader);
gltfLoader.load('./three.js-r163/examples/models/gltf/ferrari.glb',function(gltf){
console.log(gltf.scene);
const carModel=gltf.scene.children[0];
scene.add(carModel);
});
}
//进行渲染的方法 这个会每一帧都执行
function render(){
renderer.render(scene,camera);
}
init();
鼠标右键选中car.html。点击OpenwithLiveServer运行,可以看到如下结果
打印的模型信息:
3.2.2 添加灯光
这时我们再init方法的后面添加一个灯的代码:
//直线光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 参数:光源颜色、光源强度
directionalLight.position.set(10, 10, 10); // 设置光源位置
scene.add(directionalLight); // 将光源添加到场景中
3.2.3 添加环境贴图
接下来添加环境贴图,同时隐藏掉灯光脚本:
接下来修改背景:
3.2.4 添加车身材质
运行结果如下:
效果是不是有了,接下来添加玻璃和细节材质,看后面的car.js的全部脚本。
3.2.5 添加地面网格
//坐标格辅助对象. 坐标格实际上是2维线数组 参数: 坐标尺寸 坐标细分次数 中线颜色 坐标格网格线颜色
const grid=new THREE.GridHelper(20,40,0xffffff,0xffffff);
//opacity在0.0 - 1.0的范围内的浮点数,表明材质的透明度。值0.0表示完全透明,1.0表示完全不透明
//如果材质的transparent属性未设置为true,则材质将保持完全不透明,此值仅影响其颜色。 默认值为1.0
grid.material.opacity = 0.2;
//depthWrite渲染此材质是否对深度缓冲区有任何影响。默认为true。
//在绘制2D叠加时,将多个事物分层在一起而不创建z-index时,禁用深度写入会很有用
grid.material.depthWrite = false;
//定义此材质是否透明。这对渲染有影响,因为透明对象需要特殊处理,并在非透明对象之后渲染。
//设置为true时,通过设置材质的opacity属性来控制材质透明的程度。默认值为false
grid.material.transparent = true;
scene.add( grid );
3.2.6 添加动画
这是需要在方法外添加参数const wheels=[];和 let grid ; 把init里grid前面的const去掉;
加载模型时需要保存轮胎到数组wheels里
3.2.6 添加控制器
3.2.7 添加雾效
没加雾效前:
添加雾效后:
3.2.8 添加车的影子
3.2.9 添加监听窗口大小变化的函数
3.2.10 添加性能监视器
3.2.11 car.js全部脚本
car.js代码加解释如下:
//引入Threejs
import * as THREE from 'three';
//引入gltf模型加载器GLTFLoader
import {GLTFLoader} from "three/addons/loaders/GLTFLoader.js";
import {DRACOLoader} from "three/addons/loaders/DRACOLoader.js";
//Three.js 中 RGBELoader 是一种用于加载高动态范围图像(High Dynamic Range,HDR)的 Loader,它可以将 .hdr 或 .rgbe 格式的 HDR 图像文件加载到 Three.js 的 WebGL 场景中,并方便地应用于场景渲染和光照等方面
import {RGBELoader} from "three/addons/loaders/RGBELoader.js";
//引入轨道控制器 可以鼠标控制旋转 滚轮靠近和原理观察的目标物体
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
import Stats from 'three/addons/libs/stats.module.js';
//定义几个属性
let camera,scene,renderer;
let grid;
let controls;
let stats;
//车轮数组
const wheels=[];
function init(){
//实例化场景
scene=new THREE.Scene();
scene.background=new THREE.Color(0x333333);
//RGBELoader加载器异步加载hdr环境贴图
new RGBELoader().loadAsync('./three.js-r163/examples/textures/equirectangular/venice_sunset_1k.hdr').then((texture)=>{
scene.environment=texture;// 设置环境贴图
scene.environment.mapping=THREE.EquirectangularRefractionMapping; // 设置映射类型
//scene.background=texture;// 设置背景
});
scene.fog=new THREE.Fog(0x333333,10,15); //雾效
//实例化相机
camera=new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
//相机位置
camera.position.set(4.25,1.4,-4.5);
//camera.lookAt(0,0,0);
//通过id获取html的canvas
const container = document.getElementById('container');
//性能监视器
stats=new Stats();
container.appendChild(stats.domElement);
//实例化渲染器
renderer=new THREE.WebGLRenderer({
antialias:true, //是否执行抗锯齿。默认为false
});
renderer.setSize(window.innerWidth,window.innerHeight);//将输出canvas的大小调整为(width, height)并考虑设备像素比
renderer.setPixelRatio( window.devicePixelRatio );//设置设备像素比。通常用于避免HiDPI设备上绘图模糊
renderer.setAnimationLoop( render ); //请求再次执行函数animate 渲染下一帧 每个可用帧都会调用的函数render
renderer.toneMapping = THREE.ACESFilmicToneMapping;//色调映射 使用tonemapping可以塑造更真实的效果
renderer.toneMappingExposure = 0.85;//色调映射的曝光级别。默认是1
//把渲染的domElement添加到container上 要不画面看不到
container.appendChild(renderer.domElement);
//创建车身的材质
const bodyMaterial=new THREE.MeshPhysicalMaterial({
color:0xff0000,//材质的颜色
metalness:1.0,//材质与金属的相似度。非金属材质,如木材或石材,使用0.0,金属使用1.0,通常没有中间值。 默认值为0.0。0.0到1.0之间的值可用于生锈金属的外观。如果还提供了metalnessMap,则两个值相乘
roughness:0.5,//材质的粗糙程度。0.0表示平滑的镜面反射,1.0表示完全漫反射。默认值为1.0。如果还提供roughnessMap,则两个值相乘。
clearcoat:1.0,//表示clear coat层的强度,范围从0.0到1.0m,当需要在表面加一层薄薄的半透明材质的时候,可以使用与clear coat相关的属性,默认为0.0
clearcoatRoughness:0.03 //clear coat层的粗糙度,由0.0到1.0。 默认为0.0
});
//创建轮子的细节材质
const detailsMaterial=new THREE.MeshPhysicalMaterial({
color:0xffffff,
metalness:1.0,
roughness:0.5,
});
//创建玻璃材质
const glassMaterial=new THREE.MeshPhysicalMaterial({
color:0xffffff,
metalness:0.25,
roughness:0,
transmission:1.0 //透光率(或者说透光性),范围从0.0到1.0。默认值是0.0。
});
//界面上的几个按钮监听
const bodyColorInput = document.getElementById( 'body-color' );
bodyColorInput.addEventListener( 'input', function () {
bodyMaterial.color.set( this.value );
} );
const detailsColorInput = document.getElementById( 'details-color' );
detailsColorInput.addEventListener( 'input', function () {
detailsMaterial.color.set( this.value );
} );
const glassColorInput = document.getElementById( 'glass-color' );
glassColorInput.addEventListener( 'input', function () {
glassMaterial.color.set( this.value );
} );
//车
//车阴影的图
const shadow=new THREE.TextureLoader().load("./three.js-r163/examples/models/gltf/ferrari_ao.png");
//一个用于加载经过Draco压缩的图形库
const dracLoader=new DRACOLoader();
//参数是包含JS和WASM解压缩库的文件夹路径
dracLoader.setDecoderPath('./three.js-r163/examples/jsm/libs/draco/gltf/');
//gltf模型加载
const gltfLoader=new GLTFLoader();
//Draco是一个开源的库,主要用于压缩和解压缩三维模型及点云。 以客户端上解压缩为代价,显著减少压缩的图形。
//独立的Draco文件后缀为.drc,其中包含顶点坐标,法线,颜色和其他的属性, Draco文件*不*包含材质,纹理,动画和节点结构-为了能使用这些特征,需要将Draco图形 嵌入到GLTF文件中。使用glTF-Pipeline可以将一个普通的GLTF文件转化为经过Draco压缩的GLTF文件。 当使用Draco压缩的GLTF模型时,GLTFLoader内部会调用DRACOLoader
gltfLoader.setDRACOLoader(dracLoader);
gltfLoader.load('./three.js-r163/examples/models/gltf/ferrari.glb',function(gltf){
console.log(gltf.scene);
const carModel=gltf.scene.children[0];
carModel.getObjectByName( 'body' ).material = bodyMaterial;//修改车 车身的材质
carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;
carModel.getObjectByName( 'trim' ).material = detailsMaterial;
carModel.getObjectByName( 'glass' ).material = glassMaterial;//修改玻璃材质
//把四个车轮放到数组里
wheels.push(
carModel.getObjectByName( 'wheel_fl' ),
carModel.getObjectByName( 'wheel_fr' ),
carModel.getObjectByName( 'wheel_rl' ),
carModel.getObjectByName( 'wheel_rr' )
);
// shadow 生成阴影
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),
new THREE.MeshBasicMaterial( {
map: shadow, //贴图
//在使用此材质显示对象时要使用何种混合。必须将其设置为CustomBlending才能使用自定义blendSrc, blendDst 或者 [page:Constant blendEquation]
blending: THREE.MultiplyBlending,
toneMapped: false, //定义这个材质是否会被渲染器的toneMapping设置所影响,默认为 true
transparent: true //定义此材质是否透明。这对渲染有影响,因为透明对象需要特殊处理,并在非透明对象之后渲染。设置为true时,通过设置材质的opacity属性来控制材质透明的程度。 默认值为false。
} )
);
mesh.rotation.x = - Math.PI / 2;
mesh.renderOrder = 2;//渲染顺序
carModel.add(mesh);
scene.add(carModel);
});
//坐标格辅助对象. 坐标格实际上是2维线数组 参数: 坐标尺寸 坐标细分次数 中线颜色 坐标格网格线颜色
grid=new THREE.GridHelper(20,40,0xffffff,0xffffff);
//opacity在0.0 - 1.0的范围内的浮点数,表明材质的透明度。值0.0表示完全透明,1.0表示完全不透明
//如果材质的transparent属性未设置为true,则材质将保持完全不透明,此值仅影响其颜色。 默认值为1.0
grid.material.opacity = 0.2;
//depthWrite渲染此材质是否对深度缓冲区有任何影响。默认为true。
//在绘制2D叠加时,将多个事物分层在一起而不创建z-index时,禁用深度写入会很有用
grid.material.depthWrite = false;
//定义此材质是否透明。这对渲染有影响,因为透明对象需要特殊处理,并在非透明对象之后渲染。
//设置为true时,通过设置材质的opacity属性来控制材质透明的程度。默认值为false
grid.material.transparent = true;
scene.add( grid );
//实例化控制器 参数: camera控制的相机,renderer.domElement用于事件监听的HTML元素
controls = new OrbitControls( camera, renderer.domElement);
controls.target.set(0,0.5,0);//控制器的焦点设置
controls.minDistance=2;//最近距离
controls.maxDistance=9;//最远距离
controls.maxPolarAngle = THREE.MathUtils.degToRad( 90 );//相机旋转时向下时限制到90度
controls.update();//更新控制器
//直线光源
// const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 参数:光源颜色、光源强度
// directionalLight.position.set(10, 10, 10); // 设置光源位置
// scene.add(directionalLight); // 将光源添加到场景中
//监听窗口大小变化
window.addEventListener('resize',onWindowResize);
}
//监听窗口大小变化的方法
function onWindowResize(){
camera.aspect=window.innerWidth/window.innerHeight;//更新aspect aspect摄像机视锥体的长宽比,通常是使用画布的宽/画布的高。默认值是1(正方形画布)
camera.updateProjectionMatrix();//更新摄像机投影矩阵。在任何参数被改变以后必须被调用
renderer.setSize(window.innerWidth,window.innerHeight);//更新渲染画布
}
//进行渲染的方法 这个会每一帧都执行
function render(){
controls.update();//更新控制器。必须在摄像机的变换发生任何手动改变后调用, 或如果.autoRotate或.enableDamping被设置时,在update循环里调用
const time=-performance.now()/1000;
//四个轮胎转起来
for(let i=0;i<wheels.length;i++){
wheels[i].rotation.x=time*Math.PI*2;
}
//这里是相对运动 地面动好像是车在动
grid.position.z = - ( time ) % 1;
//用相机(camera)渲染一个场景(scene)或是其它类型的object。
// 渲染一般是在canvas上完成的,或者是renderTarget(通过.setRenderTarget指定)。
// 默认情况下渲染缓存是会被清除的,但是你可以通过设置autoClear 属性的值为false来阻止渲染缓存被清除。 如果你想阻止某个指定的缓存被清空,可以设置autoClearColor、autoClearStencil或autoClearDepth属性的值为false来阻止其被清除。 如果想要强制清除一个或多个缓存,可以调用.clear。
renderer.render(scene,camera);
//性能监视器更新
stats.update();
}
init();
4 最终效果
最终演示效果手机录屏如下:
three.js官方案例car演示