前情提要
在上一篇文章中,我们画了一个3D大西瓜,不过这个大西瓜是用球体加上一个平面贴图实现的,多少有点不真实,那么这篇文章来描述一下如果绘制一个更真实的3D大西瓜。
本篇文章主要是介绍如何使用
Quixel Bridge
下载来的资源进行绘制。Quixel Bridge
自打被收购之后资源全部免费下载,就很爽,免费使用使用指南(此指南是从百度上找的一篇文章,我是自己摸索着注册的)。
Quixel Bridge
基本介绍
我们搜索fruit之后可以看到出现了很多模型,随便点击一个后会出现如下图所示的面板,红框部分为下载设置和导出设置,导出是可以直接导入到一些3D制作软件中(本篇暂不做介绍)。可以自己选择分辨率,有些模型支持8k,有些只支持4k和2k,可以根据自己的使用场景进行选择。
进入下载设置后可以看到它分为了两个部分:【材质】、【模型】
材质
下方按照Quixel Bridge
下载设置中的贴图顺序进行介绍(介绍大多参考于:zhuanlan.zhihu.com/p/260973533…
Albedo
:反照率贴图(diffuse漫反射);用于体现模型的纹理,颜色。反照率贴图本身是一张颜色与纹理的贴图。当颜色属性为纯白色时,则不影响贴图的效果;当颜色属性为其他颜色时,则叠加贴图效果。(Unity的Shader(着色器)中,把颜色贴图叫做Albedo)Metalness(Metallic)
:金属度贴图;用于体现模型的金属高光反射;金属度贴图本身是一张灰白图,越白的地方,金属度越强烈,越黑的地方金属度越低。Roughness
:粗糙度贴图定义材质的粗糙度信息,0(黑色-0 sRGB)表示光滑,1(白-255 sRGB)表示粗糙。粗粗糙度是指造成光漫射的表面不规则状况,反射方向根据表面粗糙度自由变化。这改变了光的方向,但是光强度保持恒定不变。表面越粗糙,高光越散越暗。表面越光滑,高光反射集中,尽管反射的光的总量是一点的,表面也会更亮,光会更强。粗糙度贴图采样得来的粗糙度数值会影响一个表面的微平面统计学上的取向度。一个比较粗糙的表面会得到更宽阔更模糊的镜面反射(高光),而一个比较光滑的表面则会得到集中而清晰的镜面反射。Specular
:高光贴图,表示高光的范围、强度、颜色,在Specular工作流中, 颜色越亮高光越强,黑色表示没有高光Diffuse
:(颜色贴图)它的作用是给模型上颜色和材质
Albedo与Diffuse
Albedo的定义:指的是从一个表面反射出能量与入射能量的比例,它的值在0-1之间。
Diffuse(Coefficient)的定义:指的是漫反射出去的能量占入射能量的比例,它的值也在0-1之间
从定义上来看Albedo是指物体表面反射能量,而Diffuse是指物体表面漫反射的能量,以此看来Albedo包含Diffuse
证明:由于在Lambert反射下,各方向的Excitant Radiance是相同的,因此可以用这个值计算出Excitant Radiance的值,也就是Lambert Reflection的BRDF:
与之对应的还有,而Specular Coefficient,它和方向有关。Diffuse Coefficient和Specular Coefficient加起来,才等于Albedo。
Glossiness(缩写Gloss)
:光泽度贴图,定义材质的粗糙度信息,跟Roughness相反,0(黑色-0 sRGB)表示粗糙,1(白-255 sRGB)表示光滑。光泽度是指表面反射光线的能力。表面能够反射的光线越多,光泽度越高。 表面能够反射的光线越少,光泽度越低Displacement
:置换贴图,也称为移位贴图可以改变模型对象的几何形状,但要达到较好的置换效果需要提高模型本身的顶点数,通常结合曲面细分使用。因此在提供最真实的效果的同时也会大幅增加渲染性能的开销。Normal
:法线贴图,是凹凸映射技术的另一种应用。法线贴图包含角度信息而不包含任何高度信息,其R、G、B三个通道储存的信息表示了斜面的方向和陡峭程度。使用法线贴图和高度贴图(Bump)可以确保光照和位移是一致的,能够带来更真实的效果。Bump
:凹凸贴图是一个类似于法线贴图的概念,有时也称为Height(高度图)。但是凹凸贴图只包含高度信息而不包含角度信息。凹凸贴图的优点是可以很直观地看出模型表面的凹凸情况(颜色越浅表明该位置的表面越向外凸起,反之亦然),但是计算更复杂,因此性能开销更大。
Displacement/Normal/Bump:凹凸贴图,为模型提供更多的细节。其中Displacement有时用于改变模型的顶点位置,而Bump和Normal不会改变模型的顶点位置。就实现复杂度/质量与性能开销而言,Dispalcement>Normal>Bump(>Reflection)
Curvature
:曲率贴图是存储网格的凸度/凹度的纹理,可用于遮盖表面会出现更多磨损的地方或可能发生次表面散射的地方(凸面),可能积累更多污垢(凹面)的地方,以检查表面的连续性等。曲率贴图允许提取和存储凹凸信息。黑色的值代表了凹区域,白色的值代表了凸区域,灰色值表示中性/平坦区域。通常用于一些特殊效果如磨损等,也是卡通渲染中的常用贴图。Cavity
:缝隙图描述了比AO图更小尺度的光线遮蔽信息,通常由高模或者法线贴图烘培得到。缝隙图只包含模型对象表面的凹面区域而不包括凸面区域,因此缝隙图通常大部分都是白色的,只有凹陷区域是深色的。与AO图不同的是,缝隙图影响不仅会影响漫反射,还会影响高光反射部分。AO
(Ambient Occlusion):环境遮挡贴图,模拟物体之间所产生的阴影,在不打光的时候增加体积感。也就是完全不考虑光线,单纯基于物体与其他物体越接近的区域,受到反射光线的照明越弱这一现象来模拟现实照明效果。遮挡贴图是灰度图像,其中以白色表示应接受完全间接光照的区域,以黑色表示没有间接光照。Opacity
:透明贴图,定义贴图的不透明度。黑色是透明的部分,白色为不透明的部分,灰色为半透明的部分
模型
【模型】中我们目前只需要关心以下两个部分
- FBX是一种3D文件格式
FBX是一种3D文件格式,由Kaydara为MotionBuilder开发,于2006年被Autodesk公司收购,是许多3D工具使用的主要3D交换格式之一。
FBX是 FilmBoX这套软件所使用的格式,后改称Motionbuilder。因为Motionbuilder扮演的是动作制作的平台,所以在前端的modeling和后端的rendering也都有赖于其它软件的配合,所以Motionbuilder在档案的转换上自然下了一番功夫。
所以fbx最大的用途是用在诸如在max、maya、softimage等软件间进行模型、材质、动作和摄影机信息的互导,这样就可以发挥max和maya等软件的优势。
- LODs 是指模型的显示精度,即模型被划分为多少个三角形进行显示,三角形越多,该模型就越精细,当然所需要的性能也就越高。
将下载的素材导入three.js
此处以LOD4的西瓜模型,默认材质进行举例,下载下来后的内容如下图:
【tguocjppa_LOD4.fbx】是能够帮我们绘制出西瓜基本模型的,那么此时我们就需要将它导入到项目中(项目基本搭建请看上一篇文章)
另外,文件夹中还有一个.exr文件,.exr文件扩展名属于OpenEXR图像格式,代表OpenEXR图像( .exr)文件类型。OpenEXR是一种开放的高动态范围(HDR)光栅图像格式
导入模型
// 导入 FBXLoader
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
const fbxLoader = new FBXLoader();
fbxLoader.load(
'/watermelon/tguocjppa_LOD4.fbx',
loadedModel => {
//模型太大了,此处进行一些缩放
loadedModel.scale.set(0.5, 0.5, 0.5);
scene.add(loadedModel);
},
xhr => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
error => {
console.log(error);
}
);
运行结果:
导入贴图
导入反照率贴图
我们先将loadedModel
打印出来看看其中有什么,
可以发现,这个对象中的children[0]的材质是
MeshPhongMaterial
,那么我们就创建一个MeshPhongMaterial
并赋予他一个基本的纹理贴图
const textureLoader = new THREE.TextureLoader();
const textureAlbedo = textureLoader.load(
'/watermelon/tguocjppa_2K_Albedo_LOD4.jpg'
);
const fbxLoader = new FBXLoader();
fbxLoader.load(
'/watermelon/tguocjppa_LOD4.fbx',
loadedModel => {
// 不进行clone()也能渲染出来,不过建议还是clone出来再进行操作
const mesh = loadedModel.children[0].clone();
mesh.scale.set(0.5, 0.5, 0.5);
// 设置材质及贴图
mesh.material = new THREE.MeshPhongMaterial({
map: textureNormal,
});
scene.add(mesh);
},
xhr => {
// 打印加载进度
console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
error => {
console.error(error);
}
);
运行效果:
导入环境贴图
Three.js的官网说明了导入环境贴图需要第二组UV
我们的材质中会自带一组UV,如下图所示
那么我们复制第一组uv得到第二组uv,进而添加环境贴图即可
// 导入贴图
const textureAlbedo = textureLoader.load(
'/watermelon/tguocjppa_2K_Albedo_LOD4.jpg'
);
const textureAo = textureLoader.load(
'/watermelon/tguocjppa_2K_Albedo_LOD4.jpg'
);
const fbxLoader = new FBXLoader();
fbxLoader.load(
'/watermelon/tguocjppa_LOD4.fbx',
loadedModel => {
console.log('loadedModel', loadedModel);
const mesh = loadedModel.children[0].clone();
mesh.scale.set(0.5, 0.5, 0.5);
mesh.geometry.setAttribute(
'uv2',
new THREE.BufferAttribute(mesh.geometry.attributes.uv.array, 2)
);
mesh.material = new THREE.MeshPhongMaterial({
map: textureAlbedo,
aoMap: textureAo,
});
scene.add(mesh);
},
xhr => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
error => {
console.log(error);
}
);
运行效果:
导入置换贴图
const textureLoader = new THREE.TextureLoader();
const textureAlbedo = textureLoader.load(
'/watermelon/tguocjppa_2K_Albedo_LOD4.jpg'
);
const textureAo = textureLoader.load(
'/watermelon/tguocjppa_2K_Albedo_LOD4.jpg'
);
const textureDisplacement = textureLoader.load(
'/watermelon/tguocjppa_2K_Displacement.jpg'
);
const fbxLoader = new FBXLoader();
fbxLoader.load(
'/watermelon/tguocjppa_LOD4.fbx',
loadedModel => {
console.log('loadedModel', loadedModel);
const mesh = loadedModel.children[0].clone();
mesh.scale.set(0.5, 0.5, 0.5);
mesh.geometry.setAttribute(
'uv2',
new THREE.BufferAttribute(mesh.geometry.attributes.uv.array, 2)
);
mesh.material = new THREE.MeshPhongMaterial({
map: textureAlbedo,
aoMap: textureAo,
displacementMap: textureDisplacement,
});
scene.add(mesh);
},
xhr => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
error => {
console.log(error);
}
);
由于选择的是LOD4,定点数没有那么多,所以置换贴图导致的凹凸不平的效果不是很明显,感兴趣的小伙伴可以下载LOD1尝试一下。
导入法线贴图
增加法线贴图前,先上一张目前西瓜上这个大坑的反光图片,可以从图中看到这个大坑侧过去看的时候只是颜色上和瓜皮有区分,但和瓜皮的反光程度一样,导致这个大坑仿佛有又仿佛没有
所以我们要通过法线贴图使得这个西瓜的大坑看起来更真实
const textureNormal = textureLoader.load(
'/watermelon/tguocjppa_2K_Normal_LOD4.jpg'
);
const fbxLoader = new FBXLoader();
fbxLoader.load(
'/watermelon/tguocjppa_LOD4.fbx',
loadedModel => {
console.log('loadedModel', loadedModel);
const mesh = loadedModel.children[0].clone();
mesh.scale.set(0.5, 0.5, 0.5);
mesh.geometry.setAttribute(
'uv2',
new THREE.BufferAttribute(mesh.geometry.attributes.uv.array, 2)
);
mesh.material = new THREE.MeshPhongMaterial({
map: textureAlbedo,
//aoMap : Texture该纹理的红色通道用作环境遮挡贴图。默认值为null。aoMap需要第二组UV。
aoMap: textureAo,
displacementMap: textureDisplacement,
normalMap: textureNormal,
});
scene.add(mesh);
},
xhr => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
},
error => {
console.log(error);
}
);
运行效果:
至此,我们实现了一个更为真实的3D大西瓜,其他物体的材质贴图类似,其中就只有ao贴图比较特殊一点。
题外话
小时候妈妈总说,生病了要去看医生,可她却没有告诉我原来有些病是医生也无法解决的,那些在焦虑中度过的不眠之夜,那种求而不得的遗憾和唏嘘,那份不可言说的懊恼和悲伤,有时候我不禁想问,那些牺牲了事件、健康和快乐的人,真的换来了他们想要的吗?不知道啊,但如果没有的话,还是试着多爱自己一点吧,哪怕只有那么一点儿。