写在最前
往期回顾:
- # threejs系列: 相机与投影 📷
- # threejs系列: 光源与光照🪀
- # threejs系列: 自定义几何体🎃
- # threejs系列: 几何变换(上)🍺
- # threejs系列: 几何变换(下)🏄♂️
- # threejs系列: 矩阵推导🎰
- threejs系列: 物体详解🐧
- threejs系列: 材质与贴图详解(上)🦥
- threejs系列:材质与贴图详解(下)🦄
我们先一个球体和一个地面:
js
let box = new THREE.Mesh(new THREE.SphereGeometry(2, 100, 100), new THREE.MeshBasicMaterial({
color: 0xFFFFFF,
}))
scene.add(box);
let plane = new THREE.Mesh(new THREE.PlaneGeometry(100, 100, 100, 100), new THREE.MeshBasicMaterial({
color: 0xFFFFFF
}))
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
plane.position.y = -2;
MeshBasicMaterial 基础网格材质
这是一个不受任何光线影响的材质,我们给球体设置该材质:
csharp
let box = new THREE.Mesh(new THREE.SphereGeometry(2, 100, 100), new THREE.MeshBasicMaterial({color: 0xFFFFFF}))
此时我们是没有设置任何灯光的,但它依然出现在屏幕中。
MeshDepthMaterial 深度网格材质
什么是深度?深度是像素点Z坐标距离摄像机的距离。它有什么用处?它用于判断点与点之间的前后关系。在 webGL 中叫做深度缓冲 (z-buffer),深度缓冲的工作原理是将每个像素的深度值(距离观察者的位置)存储在一个缓冲区 中。当渲染一个物体时,图形处理器 会首先计算该物体每个像素的深度值。然后,图形处理器会将该物体的深度值与缓冲区中对应像素的深度值进行比较。如果该物体的深度值小于或等于缓冲区中对应像素的深度值,则该物体在该像素处是可见的;否则,该物体在该像素处是不可见的,由此确定物体的前后关系。
csharp
let box = new THREE.Mesh(new THREE.SphereGeometry(2, 100, 100), new THREE.MeshDepthMaterial())
深度网格材质不受光线的影响,它只受与镜头的距离影响,我们将镜头靠近物体,可以发现物体的颜色发生变化,越靠近物体,颜色越呈白色,越离开物体,颜色越呈黑色。距离范围为0 - 1。
MeshDistanceMaterial 距离网格材质
比较少用
Lambert网格材质
Lambert网格材质是使用的Lambert模型,Lambert 模型是一种简单的漫反射模型。它假设物体的表面是均匀的,并且光线从任何方向照射到表面都会被均匀地散射。它的优点是简单易用,计算效率高。但是,它无法模拟镜面反射和折射等效果。
Lambert网格材质受灯光的影响,我们需要添加环境光与平行光。
js
renderer.shadowMap.enabled = true;
let box = new THREE.Mesh(new THREE.SphereGeometry(0.4, 100, 100), new THREE.MeshLambertMaterial({ color: 0xFF0000 }))
let ambientLight = new THREE.AmbientLight(0x404040, 1);
scene.add(ambientLight);
let directionLight = new THREE.DirectionalLight(0xffffff, 1);
scene.add(directionLight);
directionLight.position.set(4, 4, 4);
directionLight.castShadow = true;
// 添加阴影
box.castShadow = true;
plane.receiveShadow = true;
可以看到小球被光照射的地方颜色均匀的变亮了。
MeshMatcapMaterial 材质捕捉
MeshMatcapMaterial材质通常伴随着一张Matcap贴图,Matcap贴图存储着物体的材质信息、光照信息,如高光、阴影等等。通常用于模拟静态灯光下的物体,可以以较低的成本去模拟PBR(基于物理渲染)的效果。
threejs 使用
ini
let box = new THREE.Mesh(new THREE.SphereGeometry(2, 100, 100), new THREE.MeshMatcapMaterial());
// 加载颜色贴图
new THREE.TextureLoader().load(url, (map)=>{
box.material.map = map;
box.material.needUpdate = true;
});
// 加载 matcap 贴图
new THREE.TextureLoader().load(url, (matcap)=>{
box.material.matcap = matcap;
box.material.needUpdate = true;
});
我们首先给球体设置一张颜色贴图,颜色贴图可以定义物体的颜色与图案
颜色贴图:
matcap贴图
从matcap贴图可以看出拥有镜面发射,看看渲染效果如何。
可以看出渲染出来后,原本看上去粗糙的球体变成了光滑且拥有镜面反射的球体。
且它不依赖于灯光,将灯光移除依然可以展示出来。
MeshNormalMaterial 法线网格材质
将法线映射成RGB颜色的材质,相对简单,可看官网示例。
MeshPhongMaterial 高光网格材质
和 Lambert 材质相反,这是一个拥有镜面高光的材质。
csharp
let box = new THREE.Mesh(new THREE.SphereGeometry(2, 100, 100), new THREE.MeshPhongMaterial());
我们可以看到球体被光线照射的地方,产生了镜面高光,这是lambert材质做不到的。
我们还可以通过 shininess 属性调试高亮的程度,0 ~ 100, 可以使用 specular 属性设置高光的颜色。
MeshToonMateiral 卡通网格材质
这是一种偏于卡通着色效果的材质,不好形容,还是看官网示例吧。
ShaderMaterial 着色器材质 (不了解 WebGL 跳过)
上面的的材质都是threejs 使用着色器封装出来的,而着色器材质就是让我们自己使用着色器写效果。
着色器拥有片段着色器(FragmentShader)和顶点(VertexShader)着色器,顶点着色器用于点的位置,片段着色器确定点得颜色。就是如此简单的方法,需要你利用好算法绘制好看的效果,详细的东西就不讲了。
感兴趣的可参考:
- the book of shader 中文版 ,里面写了很多2D方面的Shader 教程,但3D的没有更新(也许不更了)。
- shaderToy 这是一个最大的shader交流平台, 里面有大量的 shader 精美效果,需要拥有较高的 shader 基础阅读。
写个简单的例子吧:
js
// 顶点着色器
const vertexShader = `
varying vec2 vUv;
varying vec3 vPosition;
void main(){
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); // 投影矩阵
vPosition = position; // 位置通过 varying 传递给片段着色器
}
`
// 片元着色器
const fragmentShader = `
uniform float uTime; // 时间,全局变量,由外部程序提供
varying vec3 vPosition;
void main(){
float color = ((cos(uTime) + 1.0) / 2.0) * ((vPosition.y + 1.0) / 2.0);
gl_FragColor = vec4(color, color, color, 1.); // 设置颜色
}
`
const geometry = new THREE.SphereGeometry(3, 100, 100)
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0
}
}
})
sphere = new THREE.Mesh(geometry, shaderMaterial)
scene.add(sphere)