【可视化学习】32-材质知识拓展
发表于:2023-08-23 |

前言

本文将和大家聊点threejs的材质

玻璃杯,水,冰块渲染介绍自定义混合方程常量

完成基础代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// 导入hdr加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
// 导入顶点法向量辅助器
import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
// 导入gltf加载器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 导入draco解码器
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, // 开启抗锯齿
});
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 设置相机位置
camera.position.z = 5;
camera.position.y = 4;
camera.position.x = 2;
camera.lookAt(0, 0, 0);

// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;

// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.render(scene, camera);
}
animate();

// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});

let eventObj = {
Fullscreen: function () {
// 全屏
document.body.requestFullscreen();
console.log("全屏");
},
ExitFullscreen: function () {
document.exitFullscreen();
console.log("退出全屏");
},
};

// 创建GUI
const gui = new GUI();
// 添加按钮
gui.add(eventObj, "Fullscreen").name("全屏");
gui.add(eventObj, "ExitFullscreen").name("退出全屏");

// rgbeLoader 加载hdr贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
envMap.mapping = THREE.EquirectangularRefractionMapping;
// 设置环境贴图
scene.background = envMap;
// 设置环境贴图
scene.environment = envMap;
});

基础代码效果图

添加模型和gui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// 实例化加载器gltf
const gltfLoader = new GLTFLoader();
// 实例化加载器draco
const dracoLoader = new DRACOLoader();
// 设置draco路径
dracoLoader.setDecoderPath("./draco/");
// 设置gltf加载器draco解码器
gltfLoader.setDRACOLoader(dracoLoader);
// 加载模型
gltfLoader.load(
// 模型路径
"./model/cup.glb",
// 加载完成回调
(gltf) => {
let cup = gltf.scene.getObjectByName("copo_low_01_vidro_0");
let water = gltf.scene.getObjectByName("copo_low_02_agua_0");
let ice = gltf.scene.getObjectByName("copo_low_04_vidro_0");

ice.scale.set(0.86, 0.86, 0.86);
water.position.z = -1;
ice.renderOrder = 1;
water.renderOrder = 2;
cup.renderOrder = 3;

// cup.visible = false;
// water.visible = false;

console.log("ice", ice);
console.log("water", water);
let iceMaterial = ice.material;
ice.material = new THREE.MeshPhysicalMaterial({
normalMap: iceMaterial.normalMap,
metalnessMap: iceMaterial.metalnessMap,
roughness: 0,
color: 0xffffff,
transmission: 0.95,
transparent: true,
thickness: 10,
ior: 2,
// opacity: 0.5,
});

// console.log("iceMaterial", iceMaterial);

let waterMaterial = water.material;
water.material = new THREE.MeshPhysicalMaterial({
map: waterMaterial.map,
normalMap: waterMaterial.normalMap,
metalnessMap: waterMaterial.metalnessMap,
roughnessMap: waterMaterial.roughnessMap,
transparent: true,
transmission: 0.95,
roughness: 0.1,
thickness: 10,
ior: 2,
// opacity: 0.6,
});

// water.visible = false;

cup.material = new THREE.MeshPhysicalMaterial({
map: cup.material.map,
normalMap: cup.material.normalMap,
metalnessMap: cup.material.metalnessMap,
roughnessMap: cup.material.roughnessMap,
transparent: true,
transmission: 0.95,
roughness: 0.3,
thickness: 10,
ior: 2,
opacity: 0.6,
});
// cup.material = material;

let material = water.material;
material.blending = THREE.CustomBlending;
material.blendEquation = THREE.AddEquation;
material.blendSrc = THREE.SrcAlphaFactor;
material.blendDst = THREE.SrcColorFactor;

cup.material.blending = THREE.CustomBlending;
cup.material.blendEquation = THREE.AddEquation;
cup.material.blendSrc = THREE.SrcAlphaFactor;
cup.material.blendDst = THREE.SrcColorFactor;

gui
.add(material, "blendEquation", {
AddEquation: THREE.AddEquation,
SubtractEquation: THREE.SubtractEquation,
ReverseSubtractEquation: THREE.ReverseSubtractEquation,
MinEquation: THREE.MinEquation,
MaxEquation: THREE.MaxEquation,
})
.name("blendEquation");

gui
.add(material, "blendSrc", {
ZeroFactor: THREE.ZeroFactor,
OneFactor: THREE.OneFactor,
SrcColorFactor: THREE.SrcColorFactor,
OneMinusSrcColorFactor: THREE.OneMinusSrcColorFactor,
SrcAlphaFactor: THREE.SrcAlphaFactor,
OneMinusSrcAlphaFactor: THREE.OneMinusSrcAlphaFactor,
DstAlphaFactor: THREE.DstAlphaFactor,
OneMinusDstAlphaFactor: THREE.OneMinusDstAlphaFactor,
DstColorFactor: THREE.DstColorFactor,
OneMinusDstColorFactor: THREE.OneMinusDstColorFactor,
SrcAlphaSaturateFactor: THREE.SrcAlphaSaturateFactor,
})
.name("blendSrc");
gui
.add(cup.material, "blendDst", {
ZeroFactor: THREE.ZeroFactor,
OneFactor: THREE.OneFactor,
SrcColorFactor: THREE.SrcColorFactor,
OneMinusSrcColorFactor: THREE.OneMinusSrcColorFactor,
SrcAlphaFactor: THREE.SrcAlphaFactor,
OneMinusSrcAlphaFactor: THREE.OneMinusSrcAlphaFactor,
DstAlphaFactor: THREE.DstAlphaFactor,
OneMinusDstAlphaFactor: THREE.OneMinusDstAlphaFactor,
DstColorFactor: THREE.DstColorFactor,
OneMinusDstColorFactor: THREE.OneMinusDstColorFactor,
// SrcAlphaSaturateFactor: THREE.SrcAlphaSaturateFactor,
})
.name("blendDst");

gui
.add(material, "blendEquationAlpha", {
AddEquation: THREE.AddEquation,
SubtractEquation: THREE.SubtractEquation,
ReverseSubtractEquation: THREE.ReverseSubtractEquation,
MinEquation: THREE.MinEquation,
MaxEquation: THREE.MaxEquation,
})
.name("blendEquationAlpha");

gui
.add(material, "blendSrcAlpha", {
ZeroFactor: THREE.ZeroFactor,
OneFactor: THREE.OneFactor,
SrcColorFactor: THREE.SrcColorFactor,
OneMinusSrcColorFactor: THREE.OneMinusSrcColorFactor,
SrcAlphaFactor: THREE.SrcAlphaFactor,
OneMinusSrcAlphaFactor: THREE.OneMinusSrcAlphaFactor,
DstAlphaFactor: THREE.DstAlphaFactor,
OneMinusDstAlphaFactor: THREE.OneMinusDstAlphaFactor,
DstColorFactor: THREE.DstColorFactor,
OneMinusDstColorFactor: THREE.OneMinusDstColorFactor,
SrcAlphaSaturateFactor: THREE.SrcAlphaSaturateFactor,
})
.name("blendSrcAlpha");
gui.add(material, "blendDstAlpha", {
ZeroFactor: THREE.ZeroFactor,
OneFactor: THREE.OneFactor,
SrcColorFactor: THREE.SrcColorFactor,
OneMinusSrcColorFactor: THREE.OneMinusSrcColorFactor,
SrcAlphaFactor: THREE.SrcAlphaFactor,
OneMinusSrcAlphaFactor: THREE.OneMinusSrcAlphaFactor,
DstAlphaFactor: THREE.DstAlphaFactor,
OneMinusDstAlphaFactor: THREE.OneMinusDstAlphaFactor,
DstColorFactor: THREE.DstColorFactor,
OneMinusDstColorFactor: THREE.OneMinusDstColorFactor,
// SrcAlphaSaturateFactor: THREE.SrcAlphaSaturateFactor,
});
scene.add(gltf.scene);
}
);

效果图

属性介绍

blendEquation混合方程

这个用于指定在处理混合(blend)效果时使用何种类型的方程式

  • THREE.AddEquation(加法方程)
  • THREE.SubtractEquation(减法方程)
  • THREE.ReverseSubtractEquation(反向减法方程)
  • THREE.MinEquation(最小方程)
  • THREE.MaxEquation(最大方程)
    上述这些常量对应于源代码中的枚举值,要理解其具体含义,我们得了解GL混合方程式定义:
    1
    glBlendEquation(GLenum mode);
    其中GLenum mode可取值见下表:
    GL_FUNC_ADD Cf = (Cs * S)+(Cd * D)
    GL_FUNC_SUBTRACT Cf = (Cs * S)-(Cd * D)
    GL_FUNC_RESERSE_SUBTRACT Cf = (Cd * D)-(Cs * S)
    GL_MIN Cf = min(Cs,Cd)
    GL_MAX Cf = max(Cs,Cd)
  1. Cf表示混合后显示的颜色
  2. Cd混合前颜色缓冲中已经有的颜色值
  3. Cs将要绘制的颜色
  4. S为glBlendFunc函数设置时的第一个参数,源颜色因子
  5. D为glBlendFunc函数设置时的第二个参数,目标颜色因子

blendSrc源因子

  • THREE.ZeroFactor(对应于GL_ZERO,表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算)
  • THREE.OneFactor(GL_ONE,表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算)
  • THREE.SrcColorFactor(GL_SRC_COLOR,表示使用源颜色的四个分量分别作为因子的四个分量)
  • THREE.OneMinusSrcColorFactor(GL_ONE_MINUS_SRC_COLOR,表示使用1.0减去源颜色的四个分量分别作为因子的四个分量)
  • THREE.SrcAlphaFactor(GL_SRC_ALPHA,表示使用源颜色的alpha值来作为因子。)
  • THREE.OneMinusSrcAlphaFactor(GL_ONE_MINUS_SRC_ALPHA,表示用1.0减去源颜色的alpha值来作为因子)
  • THREE.DstAlphaFactor(GL_DST_ALPHA,表示使用目标颜色的alpha值来作为因子。)
  • THREE.OneMinusDstAlphaFactor(GL_ONE_MINUS_DST_ALPHA,表示用1.0减去目标颜色的alpha值来作为因子。)
  • THREE.DstColorFactor
  • THREE.OneMinusDstColorFactor
  • THREE.SrcAlphaSaturateFactor

blendDst目标因子

  • THREE.ZeroFactor(对应于GL_ZERO,表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算)
  • THREE.OneFactor(GL_ONE,表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算)
  • THREE.SrcColorFactor(GL_SRC_COLOR,表示使用源颜色的四个分量分别作为因子的四个分量)
  • THREE.OneMinusSrcColorFactor(GL_ONE_MINUS_SRC_COLOR,表示使用1.0减去源颜色的四个分量分别作为因子的四个分量)
  • THREE.SrcAlphaFactor(GL_SRC_ALPHA,表示使用源颜色的alpha值来作为因子。)
  • THREE.OneMinusSrcAlphaFactor(GL_ONE_MINUS_SRC_ALPHA,表示用1.0减去源颜色的alpha值来作为因子)
  • THREE.DstAlphaFactor(GL_DST_ALPHA,表示使用目标颜色的alpha值来作为因子。)
  • THREE.OneMinusDstAlphaFactor(GL_ONE_MINUS_DST_ALPHA,表示用1.0减去目标颜色的alpha值来作为因子。)
  • THREE.DstColorFactor
  • THREE.OneMinusDstColorFactor

blendSrcAlpha源透明度

  • THREE.ZeroFactor
  • THREE.OneFactor
  • THREE.SrcColorFactor
  • THREE.OneMinusSrcColorFactor
  • THREE.SrcAlphaFactor
  • THREE.OneMinusSrcAlphaFactor
  • THREE.DstAlphaFactor
  • THREE.OneMinusDstAlphaFactor
  • THREE.DstColorFactor
  • THREE.OneMinusDstColorFactor
  • THREE.SrcAlphaSaturateFactor

blendDstAlpha目标透明度

  • THREE.ZeroFactor
  • THREE.OneFactor
  • THREE.SrcColorFactor
  • THREE.OneMinusSrcColorFactor
  • THREE.SrcAlphaFactor
  • THREE.OneMinusSrcAlphaFactor
  • THREE.DstAlphaFactor
  • THREE.OneMinusDstAlphaFactor
  • THREE.DstColorFactor
  • THREE.OneMinusDstColorFactor

效果图

可以通过这些属性调整冰块,水,杯子的渲染逻辑,具体的需要大家去实践操作
这里我截图几张给大家看下
效果图
效果图
效果图

裁剪平面

创建模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
// 设置球形贴图
// envMap.mapping = THREE.EquirectangularReflectionMapping;
envMap.mapping = THREE.EquirectangularRefractionMapping;
// 设置环境贴图
scene.background = envMap;
// 设置环境贴图
scene.environment = envMap;
});
const geometry = new THREE.TorusKnotGeometry(10, 3, 100, 16);
const material = new THREE.MeshPhysicalMaterial({
side: THREE.DoubleSide,
});
const torusKnot = new THREE.Mesh(geometry, material);
scene.add(torusKnot);

效果图

创建裁剪平面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建裁剪平面
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// 创建第2个平面
const plane2 = new THREE.Plane(new THREE.Vector3(1, 0, 0), 0);
material.clippingPlanes = [plane, plane2];
// 设置裁剪为并集
material.clipIntersection = false;
// 设置渲染器的localClippingEnabled属性为true
renderer.localClippingEnabled = true;
// 设置裁剪阴影
// material.clipShadows = true;

plane.constant = 0;

renderer.clippingPlanes = [plane, plane2];

效果图

添加gui设置裁剪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建裁剪平面
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// 创建第2个平面
const plane2 = new THREE.Plane(new THREE.Vector3(1, 0, 0), 0);
// material.clippingPlanes = [plane, plane2];
// // 设置裁剪为并集
// material.clipIntersection = false;
// // 设置渲染器的localClippingEnabled属性为true
// renderer.localClippingEnabled = true;
// // 设置裁剪阴影
// // material.clipShadows = true;

// plane.constant = 0;

renderer.clippingPlanes = [plane, plane2];

// 创建一个gui
const folder = gui.addFolder("裁剪平面");
// 添加一个滑块
folder.add(plane, "constant", -10, 10).name("位置");
// 设置plane的normal属性
folder.add(plane.normal, "x", -1, 1).name("法向量x");
folder.add(plane.normal, "y", -1, 1).name("法向量y");
folder.add(plane.normal, "z", -1, 1).name("法向量z");

效果图

裁剪场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 创建GUI
const gui = new GUI();
gui.add(params, "scissorWidth", 0, window.innerWidth);
// 创建新场景
const newScene = new THREE.Scene();

const geometry1 = new THREE.TorusKnotGeometry(10, 3, 100, 16);
const material1 = new THREE.MeshBasicMaterial({
wireframe: true,
});
const torusKnot1 = new THREE.Mesh(geometry1, material1);
newScene.add(torusKnot1);

// scissorWidth
let params = {
scissorWidth: window.innerWidth / 2,
};

// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 渲染
renderer.setScissorTest(true);
renderer.setScissor(0, 0, params.scissorWidth, window.innerHeight);
renderer.render(scene, camera);
renderer.setScissor(
params.scissorWidth,
0,
window.innerWidth - params.scissorWidth,
window.innerHeight
);
renderer.render(newScene, camera);
renderer.setScissorTest(false);
}
animate();

效果图

模板渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 创建1个平面

// 1、2个物体都设置模板缓冲区的写入和测试
// 2、设置模板缓冲的基准值
// 3、设置允许写入的掩码0xff
// 4、在小球上设置模板比较函数THREE.EqualStencilFunc
// 5、设置当函数比较通过时候,设置为replace替换
const plane = new THREE.PlaneGeometry(8, 8);
const planeMaterial = new THREE.MeshPhysicalMaterial({
stencilWrite: true, //
stencilWriteMask: 0xff, //0-255
stencilRef: 2,
stencilZPass: THREE.ReplaceStencilOp,
});
const planeMesh = new THREE.Mesh(plane, planeMaterial);
scene.add(planeMesh);

// 创建1个球
const sphere = new THREE.SphereGeometry(1, 20, 20);
const sphereMaterial = new THREE.MeshPhysicalMaterial({
color: 0xffcccc,
stencilWrite: true,
stencilRef: 2,
stencilFunc: THREE.EqualStencilFunc,
depthTest: false,
});
const sphereMesh = new THREE.Mesh(sphere, sphereMaterial);
sphereMesh.position.z = -10;
scene.add(sphereMesh);

金属裁切

这里就是使用模板渲染的逻辑实现的裁切功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 创建GUI
const gui = new GUI();

// rgbeLoader 加载hdr贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
// 设置球形贴图
// envMap.mapping = THREE.EquirectangularReflectionMapping;
envMap.mapping = THREE.EquirectangularRefractionMapping;
// 设置环境贴图
scene.background = envMap;
// 设置环境贴图
scene.environment = envMap;
});
const geometry = new THREE.TorusKnotGeometry(10, 3, 100, 16);
const material = new THREE.MeshPhysicalMaterial({
// side: THREE.DoubleSide,
side: THREE.FrontSide,
});
const torusKnot = new THREE.Mesh(geometry, material);
scene.add(torusKnot);

const material1 = new THREE.MeshBasicMaterial({
side: THREE.BackSide,
color: 0xffcccc,
stencilWrite: true,
stencilRef: 1,
stencilWriteMask: 0xff,
stencilZPass: THREE.ReplaceStencilOp,
});
const torusKnot1 = new THREE.Mesh(geometry, material1);
scene.add(torusKnot1);

// 创建裁剪平面
const plane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0);
material.clippingPlanes = [plane];
material1.clippingPlanes = [plane];
renderer.localClippingEnabled = true;
// // 设置裁剪阴影

// 创建一个gui
const folder = gui.addFolder("裁剪平面");
// 添加一个滑块
folder.add(plane, "constant", -10, 10).name("位置");
// // 设置plane的normal属性
folder.add(plane.normal, "x", -1, 1).name("法向量x");
folder.add(plane.normal, "y", -1, 1).name("法向量y");
folder.add(plane.normal, "z", -1, 1).name("法向量z");

// 创建平面
let planeGeometry = new THREE.PlaneGeometry(40, 40, 1, 1);
let planeMaterial = new THREE.MeshPhysicalMaterial({
color: 0xccccff,
metalness: 0.95,
roughness: 0.1,
stencilWrite: true,
stencilRef: 1,
stencilFunc: THREE.EqualStencilFunc,
});
let planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.rotation.x = -Math.PI / 2;
scene.add(planeMesh);

效果图

好了,本文到这里就结束了,下次再见

上一篇:
dispatchEvent
下一篇:
前端web端处理支付对接