一、贴图
要渲染3D物体,必不可少的要素:场景、相机、物体(包括几何体和材质)、渲染器、控制器。基本概念可参考这篇文章。搭建一个场景代码如下:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 10);
scene.add(camera);
// 添加物体
const box = new THREE.BoxGeometry(1, 1, 1, 100, 100, 100);
// 材质:MeshStandardMaterial标准网格材质,是基于物理渲染的,能够基于物理模型处理灯光和阴影,更接近物理世界的真实场景。
const material = new THREE.MeshStandardMaterial({color: 0xffff00})
// 物体
const cube = new THREE.Mesh(box, material);
scene.add(cube)
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 轨道控制器的阻尼感
controls.enableDamping = true;
//辅助坐标轴
const axesHelp = new THREE.AxesHelper();
scene.add(axesHelp);
//渲染函数
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
看下效果:
物体出来了,但是是黑的,因为有些材质受光照影响、有些材质不受光照影响。这里使用的标准网格材质是基于物理渲染的(BPR),受光照影响,需要有光才能看到。
添加灯光:
//环境光会均匀的照亮场景中的所有物体。
const light = new THREE.AmbientLight(0x404040);
scene.add(light);
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
// 设置光的位置
directionalLight.position.set(5, 5, 5);
// 添加到场景中
scene.add(directionalLight);
添加灯光后,物体可以看到了。
\
接下来给这个物体添加贴图,来达到我们想要的3D场景。
1、map贴图
给这个物体贴个门上去。
首先要创建一个纹理加载器,将需要贴的图片引入进来
// 纹理加载器 加载文件
const texture = new THREE.TextureLoader(textureManage);
// 加载图片
const doorColorTexture = texture.load(
require("../assets/images/textures/door/color.jpg")
);
设置贴图
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
});
看下效果:
这个门就贴上去了,但是我们只想要中间的门,不需要两边的墙。可以加个透明度贴图,墙的部分是黑色,门的部分是白色的一张灰度图。
2、alphMap透明度贴图
黑色为完全透明,白色为完全不透明。注意透明度贴图需要设置材质的transparent属性为true,才会有效果。
// alpha贴图是一张灰度纹理,用于控制整个表面的不透明度。(黑色:完全透明;白色:完全不透明)
const alphaMap = texture.load(
require("../assets/images/textures/door/alpha.jpg")
);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
alphaMap: alphaMap,
transparent: true,
});
效果:
3、aoMap环境遮挡贴图
在场景中为物体表面添加更加真实的阴影效果
const aoMap = texture.load(
require("../assets/images/textures/door/ambientOcclusion.jpg")
);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
alphaMap: alphaMap,
aoMap: aoMap,
//环境遮挡效果的强度。默认值为1。零是不遮挡效果
aoMapIntensity: 1,
});
上面是加了aoMap的效果,下面是没加的效果,可以看出区别。平行光可能效果不明显,但还是能看出一点去区别的。
4、displacementMap位移贴图
增加图像凹凸效果。这个贴图需要设置几何体的分段数才会有效果。new THREE.BoxGeometry(1,1,1,100,100,100)。后三个参数就是分段数。
// 位移贴图,增加图像凸凹效果
const displacementMap = texture.load(
require("../assets/images/textures/door/height.jpg")
);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
alphaMap: alphaMap,
transparent: true,
aoMap: aoMap,
//位移贴图 。需要设置分段数 才能显示效果 new THREE.BoxGeometry(1,1,1,100,100,100);后三个参数是分段值
displacementMap: displacementMap,
displacementScale: 0.1,
});
效果图:可以看出加了位移贴图后,物体有了厚度。通过调节displacementScale大小,来调节厚度。
注:位移纹理是一种灰度图像,根据其亮度值改变顶点的位置。影响displacementScale数值设定的因素有几个:
1、纹理分辨率:高分辨率纹理可以获得更精细的位移效果,但同时也需要更大的显存和计算量。在加载纹理时,选择合适分辨率的图像。
2、模型复杂度:模型的顶点数量和拓扑结构会影响位移效果的表现。高密度的模型可以获得更好的位移效果,但同样会增加渲染负担。
3、材质属性:位移纹理的颜色和对比度以及其他材质属性(如漫反射、镜面反射等)都会影响 displacementScale 的数值设定。你可能需要调整这些属性以达到理想的视觉效果。
4、光照条件:场景中的光源类型和数量会影响渲染效果,可能需要根据具体的光照环境调整 displacementScale 的数值。
5、观察距离和角度:不同的观察距离和视角可能需要不同的 displacementScale 数值以获得最佳效果。
6、性能考虑:较高的 displacementScale 值会增加渲染负担,因此在实际应用中可能需要权衡性能与视觉效果。
综上所述,在设置 displacementScale 时,需要考虑多种因素并进行调整。通过尝试不同的数值和观察相应的效果,你可以找到最适合你项目需求的设定。
5、roughnessMap粗糙度贴图
物体表面的粗糙度,决定了光的反射效果。0.0表示平滑的镜面反射,1.0表示完全漫反射。通过roughness设置粗糙度
const roughnessMap = texture.load(
require("../assets/images/textures/door/roughness.jpg")
);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
alphaMap: alphaMap,
transparent: true,
aoMap: aoMap,
//位移贴图 。需要设置分段数 才能显示效果 new THREE.BoxGeometry(1,1,1,100,100,100);后三个参数是分段值
displacementMap: displacementMap,
displacementScale: 0.1,
roughnessMap: roughnessMap,
roughness: 0,
});
roughness为0的效果:表面很光滑,平行光照上去是镜面反射。有强烈的镜面高光。
roughness为1的效果:表面粗糙,发生散射现象,物体表面看起来更加柔和和有纹理感,较暗的镜面高光。
6、metalnessMap金属度贴图
用于改变材质的金属度。
// 金属度贴图
const metalnessMap = texture.load(
require("../assets/images/textures/door/metalness.jpg")
);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
alphaMap: alphaMap,
transparent: true,
aoMap: aoMap,
//环境遮挡效果的强度。默认值为1。零是不遮挡效果
// aoMapIntensity: 0.5,
//替换贴图 需要设置分段数 才能显示效果 new THREE.BoxGeometry(1,1,1,100,100,100);后三个参数是分段值
displacementMap: displacementMap,
displacementScale: 0.1,
roughnessMap: roughnessMap,
roughness: 0,
metalnessMap: metalnessMap,
// 金属度
metalness: 1,
});
效果:可以看出金属部分有了金属感
7、normalMap法线贴图
增强物体表面细节。
// 法线贴图
const normalMap = texture.load(
require("../assets/images/textures/door/normal.jpg")
);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
map: doorColorTexture,
alphaMap: alphaMap,
transparent: true,
aoMap: aoMap,
//环境遮挡效果的强度。默认值为1。零是不遮挡效果
// aoMapIntensity: 0.5,
//替换贴图 需要设置分段数 才能显示效果 new THREE.BoxGeometry(1,1,1,100,100,100);后三个参数是分段值
displacementMap: displacementMap,
displacementScale: 0.1,
roughnessMap: roughnessMap,
roughness: 0,
metalnessMap: metalnessMap,
metalness: 1,
normalMap: normalMap,
});
效果:是不更具真实感了呢。
二、灯光和阴影
想要呈现更具真实感的3D场景,阴影是必不可缺的。threejs中的阴影通常是指投射阴影,它是由场景中的光源、遮挡物、投射物三者共同作用产生的。光源照在遮挡物上,遮挡物背后的区域形成阴影。要实现投射阴影,需要物体材质满足对光的反应、开启光源的投射阴影(castShadow)、物体材质的castShadow、物体接收阴影、渲染器开启阴影计算。
1、光源
有光源才能产生阴影。threejs提供了多种类型的光源,如平行光、点光源、聚光灯。
平行光:来自远处的光,照射范围广。
点光源:如灯泡,距离越远,光线越暗。
聚光灯:如手电筒,有一个光源,照亮一块区域。
//平行光
const directionalLight = new THREE.DirectionalLight(0xffffff,0.5);
//聚光灯
// const spotLight = new THREE.SpotLight(0xffffff,0.5);
// 点光源
// const pointLight = new THREE.PointLight(0xff0000,0.5);
directionalLight.position.set(5,5,5);
scene.add(directionalLight);
2、设置光源的投射阴影
directionalLight.castShadow = true
3、设置物体的投射阴影。
//创建一个球体
const sphereGeometry = new THREE.SphereGeometry(1, 20, 20);
// 物体材质要对光有反应。MeshBasicMaterial这个材质不受光照影响
const material = new THREE.MeshStandardMaterial();
const sphere = new THREE.Mesh(sphereGeometry, material);
// 设置物体的投射阴影
sphere.castShadow = true;
//设置阴影边缘模糊度
directionalLight.shadow.radius = 20
//设置阴影的分辨率
directionalLight.shadow.mapSize.set(4096,4096);
scene.add(sphere)
4、接收阴影
创建一个平面,并设置接收阴影来接收上面球体的阴影。
// 创建平面
const planeGeometry = new THREE.PlaneGeometry(10, 10);
const plane = new THREE.Mesh(planeGeometry, material);
plane.position.set(0, -1, 0);
plane.rotation.x = -Math.PI / 2;
// 接收阴影
plane.receiveShadow = true;
scene.add(plane);
5、开启渲染器阴影计算
renderer.shadowMap.enabled = true;
注:上面的所有的参数都可以添加一个图形界面(dat.gui)来调整参数,找到最合适的参数大小
最后可以得到这样的效果(平行光):
聚光灯效果 :
源码和素材地址:threejs基础