3.4 Standard材质的简单应用
学习ThreeJS的捷径
本段内容会写在0篇以外所有的,本人所编写的Threejs教程中
对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久
如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS
- 没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么
- 没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题
- 上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏
http://www.yanhuangxueyuan.com/ 郭隆邦技术博客
https://www.wellyyss.cn/ 跃焱邵隼
http://www.wjceo.com/ 暮志未晚
这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处
宝石效果
首先,我们先找到threejs开发包中的宝石模型,路径对应
three.js-master\examples\models\obj\emerald.obj
然后我们先给它加载出来
import * as THREE from '../three.js-master/build/three.module.js';
import {OrbitControls} from "../three.js-master/examples/jsm/controls/OrbitControls.js";
import {OBJLoader} from "../three.js-master/examples/jsm/loaders/OBJLoader.js";
let scene,renderer,camera,orbitControls;
function init(){
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
alpha:true,
antialias:true
});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
//根据宝石的大小,我们调整相机的位置,来适配宝石的实际显示效果
camera.position.set(50,50,50);
orbitControls = new OrbitControls(camera,renderer.domElement);
let light = new THREE.PointLight();
camera.add(light);
scene.add(camera);
let helper = new THREE.AxesHelper(5);
scene.add(helper);
}
function addMesh(){
//由于宝石的模型是一个obj格式,所以我们使用objLoader来加载宝石模型
let loader = new OBJLoader();
loader.load('./emerald.obj',obj=>{
scene.add(obj);
})
}
function render(){
renderer.render(scene,camera);
requestAnimationFrame(render);
}
init();
addMesh();
render();
以上为宝石的初步加载效果,接下来,我们需要给宝石添加一个材质,这里官方的模型使用的是MeshPhongMaterial,但是不影响我们的使用,我们需要给所有模型下的Mesh修改为我们自定义的Material
function addMesh(){
let loader = new OBJLoader();
loader.load('./emerald.obj',obj=>{
scene.add(obj);
let material = new THREE.MeshStandardMaterial({
color : "#ff0000", //给宝石赋予红色
transparent:true,//允许宝石透明
opacity:0.7, //透明度
metalness:0.3,//金属度
roughness:0.2//粗糙度
});
//遍历模型下的所有子元素,并返回一个函数,函数中带有一个参数,参数为obj的children数组的子元素或children递归下来的children的子元素
obj.traverse(object=>{
//这个object有可能不是mesh,所以我们借用官方的isMesh来判断这个物体是一个mesh类型
//仅有mesh类型拥有material属性,能被修改材质
if(object.isMesh){
let oldMaterial = object.material;//记录原有旧材质
object.material = material;//替换材质
oldMaterial.dispose();//养成好习惯,旧的材质如果完全不用了,使用dispose()来清理掉
}
})
})
}
这样我们的红宝石的基本效果就出来了,如果还想再进一步的增强效果,我们还能添加环境贴图
function addMesh(){
let loader = new OBJLoader();
loader.load('./emerald.obj',obj=>{
scene.add(obj);
let material = new THREE.MeshStandardMaterial({
color : "#ff0000", //给宝石赋予红色
transparent:true,//允许宝石透明
//二次调整红宝石的各项参数用于让它更接近宝石的效果
opacity:0.8, //透明度
metalness:0.3,//金属度
roughness:0//粗糙度
});
//遍历模型下的所有子元素,并返回一个函数,函数中带有一个参数,参数为obj的children数组的子元素或children递归下来的children的子元素
obj.traverse(object=>{
//这个object有可能不是mesh,所以我们借用官方的isMesh来判断这个物体是一个mesh类型
//仅有mesh类型拥有material属性,能被修改材质
if(object.isMesh){
let oldMaterial = object.material;//记录原有旧材质
object.material = material;//替换材质
oldMaterial.dispose();//养成好习惯,旧的材质如果完全不用了,使用dispose()来清理掉
}
})
let loader = new THREE.CubeTextureLoader();
//设置加载图片组的路径
loader.setPath('./Park2/');
//threeJS中天空盒加载顺序 px,nx,py,ny,pz,nz 或 left,right,top,bottom,front,back
//一定要按顺序加载,否则会出现严重的拼合错误问题
loader.load([
"posx.jpg",
"negx.jpg",
"posy.jpg",
"negy.jpg",
"posz.jpg",
"negz.jpg",
],(texture)=>{
scene.background = texture;
material.envMap = texture;
});
})
}
添加envMap后,可以明显看到宝石反射了当前的立方纹理
多贴图下的金属枪支效果
模型的路径在
three.js-master\examples\models\obj\cerberus
function addMesh(){
let loader = new OBJLoader();
let textureLoader = new THREE.TextureLoader();//创建纹理加载器
loader.load('./cerberus/Cerberus.obj',obj=>{
scene.add(obj);
//加载对应的纹理
//注意!如果你的项目要上线,应考虑线上传输速度的问题,以下写法会造成如下错误:
// 你的贴图加载完成之前,对应的下面let 的对象已经被调用了,出现undifined的情况
// 以下四个load均为异步函数,仅本地使用时可以这样写
let map = textureLoader.load('./cerberus/Cerberus_A.jpg'); //颜色贴图
let normalMap = textureLoader.load('./cerberus/Cerberus_N.jpg'); //法线贴图
let roughnessMap = textureLoader.load('./cerberus/Cerberus_R.jpg'); //粗糙度贴图
let metalnessMap = textureLoader.load('./cerberus/Cerberus_RM.jpg');//金属度贴图
//处理贴图的常规操作
map.wrapT = map.wrapS = THREE.RepeatWrapping;
normalMap.wrapT = normalMap.wrapS = THREE.RepeatWrapping;
roughnessMap.wrapT = roughnessMap.wrapS = THREE.RepeatWrapping;
metalnessMap.wrapT = metalnessMap.wrapS = THREE.RepeatWrapping;
//根据上面的所有贴图创建材质
let material = new THREE.MeshStandardMaterial({
transparent:true,//允许宝石透明
side:THREE.DoubleSide,
map:map,
roughnessMap:roughnessMap,
metalnessMap:metalnessMap,
normalMap:normalMap
});
//以下内容与上方宝石基本一致
//遍历模型下的所有子元素,并返回一个函数,函数中带有一个参数,参数为obj的children数组的子元素或children递归下来的children的子元素
obj.traverse(object=>{
//这个object有可能不是mesh,所以我们借用官方的isMesh来判断这个物体是一个mesh类型
//仅有mesh类型拥有material属性,能被修改材质
if(object.isMesh){
let oldMaterial = object.material;//记录原有旧材质
object.material = material;//替换材质
oldMaterial.dispose();//养成好习惯,旧的材质如果完全不用了,使用dispose()来清理掉
}
})
let loader = new THREE.CubeTextureLoader();
//设置加载图片组的路径
loader.setPath('./Park2/');
//threeJS中天空盒加载顺序 px,nx,py,ny,pz,nz 或 left,right,top,bottom,front,back
//一定要按顺序加载,否则会出现严重的拼合错误问题
loader.load([
"posx.jpg",
"negx.jpg",
"posy.jpg",
"negy.jpg",
"posz.jpg",
"negz.jpg",
],(texture)=>{
scene.background = texture;
material.envMap = texture;
});
})
}
建模软件中的材质与threejs中的材质的关系
一般来说,建模软件中的基础材质,可以对应threejs的MeshStandardMaterial
以3dmax为例
3dmax中的 基础颜色 对应StandardMaterial的 color
粗糙度和金属度,透明度这几个基础属性,就不需要解释了
要注意,次表面散射,发射这些3dmax中有的属性,最终导出时不一定能带给threejs,所以这个需要你去积累经验,来判断这些东西在threejs中的对应
有的属性不一定能对应上MeshStandardMaterial,但是可以对应上其继承类MeshPhysicalMaterial的属性,当你用StandardMaterial加载不出来你建模师建出来的效果时,可以尝试使用MeshPhysicalMaterial来做尝试
下方的贴图区,也就对应threejs文档中的那一大堆贴图,但是也是要注意对应关系
有很多好看的效果,都是建模师用贴图贴出来的,比如说上面的枪支模型,不要盲目的使用threejs去做领导或者甲方要求你的效果,不仅浪费时间,即使能做出来,也会浪费大量的代码和性能
为什么推荐BabylonJS
threejs越往后做,会发现越来越无力,因为很多这些建模软件中的高级属性,threejs中都不支持,而babylon比起threejs来说支持的更多,甚至还有专门的插件,允许你在3dmax中就把效果调整好,直接导出到BabylonJS中使用,大量节省了代码,并且让很多的开发过程直接用可视化代替掉了