【可视化学习】19-修改官网已有的材质
发表于:2023-06-07 |

前言

官网已经有很多材质可以用了,效果都是蛮不错的,我们其实完全没有必要自己重新写原生材质(使用着色器),我们完全可以基于官网的onBeforeCompile效果,在原有的基础上修改就可以拿到我们想要的效果了,这样就可以大大减少我们的工作量,提高效率。

先搞个平面

下面是基础代码。

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
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

// 初始化场景
const scene = new THREE.Scene();

// 创建透视相机
const camera = new THREE.PerspectiveCamera(
90,
window.innerHeight / window.innerHeight,
0.1,
1000
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(0, 0, 10);
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
scene.add(camera);

// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

let basicMaterial = new THREE.MeshBasicMaterial({
color: "#00ff00",
side: THREE.DoubleSide,
});


// 创建平面
const floor = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1, 64, 64),
basicMaterial
);
scene.add(floor);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true });

// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);

// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
// console.log("resize");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比例
renderer.setPixelRatio(window.devicePixelRatio);
});

// 将渲染器添加到body
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;

function animate() {
// console.log(elapsedTime);
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}

animate();

简单了解onBeforeCompile

打印一下onBeforeCompile的内容,看看里面有什么。

1
2
3
4
5
basicMaterial.onBeforeCompile = (shader, renderer) => {
console.log(shader);
console.log(shader.vertexShader)
console.log(shader.fragmentShader)
};

内容1
内容2
我们可以看到这个和我们认知的着色器貌似不一样,让我们打开node_modules中的文件包看看
内容3
我们可以看到这些# 代表的是导入了一些模块,而这些模块本质上还是我们原生的着色器所编写的,知道了这点,我们就可以修改了

使用onBeforeCompile修改

实现平面旋转效果

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
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";

// 目标:认识shader

//创建gui对象
const gui = new dat.GUI();

// console.log(THREE);
// 初始化场景
const scene = new THREE.Scene();

// 创建透视相机
const camera = new THREE.PerspectiveCamera(
90,
window.innerHeight / window.innerHeight,
0.1,
1000
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(0, 0, 10);
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
scene.add(camera);

// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

let basicMaterial = new THREE.MeshBasicMaterial({
color: "#00ff00",
side: THREE.DoubleSide,
});

const basicUnifrom = {
uTime: {
value: 0
}
}
basicMaterial.onBeforeCompile = (shader, renderer) => {
console.log(shader);
console.log(shader.vertexShader)
console.log(shader.fragmentShader)

// console.log(renderer)
shader.uniforms.uTime = basicUnifrom.uTime;
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
uniform float uTime;
`
)
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
transformed.x += sin(uTime)* 2.0;
transformed.z += cos(uTime)* 2.0;
`
)
}




// 创建平面
const floor = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1, 64, 64),
basicMaterial
);
scene.add(floor);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true });
// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);

// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
// console.log("resize");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比例
renderer.setPixelRatio(window.devicePixelRatio);
});

// 将渲染器添加到body
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;
const clock = new THREE.Clock();
function animate(t) {
const elapsedTime = clock.getElapsedTime();

basicUnifrom.uTime.value = elapsedTime;
// console.log(elapsedTime);
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}

animate();

内容3

使用onBeforeCompile修改小人被抽打效果(进阶)

加载环境贴图(可以参考之前讲纹理贴图的文章)

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
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.innerHeight / window.innerHeight,
1,
50
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(0, 0, 10);
scene.add(camera);

// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加环境纹理
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envMapTexture = cubeTextureLoader.load([
"textures/environmentMaps/0/px.jpg",
"textures/environmentMaps/0/nx.jpg",
"textures/environmentMaps/0/py.jpg",
"textures/environmentMaps/0/ny.jpg",
"textures/environmentMaps/0/pz.jpg",
"textures/environmentMaps/0/nz.jpg",
]);

const directionLight = new THREE.DirectionalLight('#ffffff', 1);
directionLight.castShadow = true;
directionLight.position.set(0, 0, 200)
scene.add(directionLight)


scene.environment = envMapTexture;
scene.background = envMapTexture;

// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;

// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
// console.log("resize");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比例
renderer.setPixelRatio(window.devicePixelRatio);
});


// 将渲染器添加到body
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;

function animate() {
controls.update()
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}

animate();

效果图

场景中添加白色底板

1
2
3
4
5
6
7
const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(20, 20),
new THREE.MeshStandardMaterial()
)
plane.position.set(0, 0, -6);
plane.receiveShadow = true;
scene.add(plane)

效果图

导入模型

1
2
3
4
5
6
7
8
9
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
// 模型加载
const material = new THREE.MeshStandardMaterial()
const gltfLoader = new GLTFLoader();
gltfLoader.load('./models/LeePerrySmith/LeePerrySmith.glb', (gltf) => {
const mesh = gltf.scene.children[0];
mesh.material = material;
scene.add(mesh);
})

效果图

添加影子

挺久没用阴影了,我们来回顾下阴影的使用
分为4步

  1. render开启阴影

    1
    renderer.shadowMap.enabled = true;
  2. 光开启阴影

    1
    directionLight.castShadow = true;
  3. 需要投射的物体开启阴影

    1
    mesh.castShadow = true;
  4. 接收阴影的物体开启阴影

    1
    plane.receiveShadow = true;

效果图

增加纹理贴图

1
2
3
4
5
6
7
8
9
const textureLoader = new THREE.TextureLoader();
// 加载模型纹理
const modelTexture = textureLoader.load('./models/LeePerrySmith/color.jpg');
// 加载模型的法向纹理
const normalTexture = textureLoader.load('./models/LeePerrySmith/normal.jpg')
const material = new THREE.MeshStandardMaterial({
map: modelTexture,
normalMap: normalTexture
})

效果图

引入时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const customUniforms = {
uTime: {
value: 0
}
}
const clock = new THREE.Clock()
function animate() {
controls.update()
const time = clock.getElapsedTime();
customUniforms.uTime.value = time;
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}

根据时间修改材质

这里用到了之前写过的旋转函数,通过bookshader可以找到

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
material.onBeforeCompile = (shader)=>{
console.log(shader.vertexShader);
console.log(shader.fragmentShader);
// 传递时间
shader.uniforms.uTime = customUniforms.uTime;
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
uniform float uTime;
`
)

shader.vertexShader = shader.vertexShader.replace(
'#include <beginnormal_vertex>',
`
#include <beginnormal_vertex>
float angle = sin(position.y+uTime) *0.5;
mat2 rotateMatrix = rotate2d(angle);
objectNormal.xz = rotateMatrix * objectNormal.xz;
`
)
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
transformed.xz = rotateMatrix * transformed.xz;
`
)
}

效果图

但是,我们看到,这个人头动了,但是他的影子并没有动起来

添加影子旋转

如果我们要修改影子的话就需要设置深度材质进行修改

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
const depthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking
})

depthMaterial.onBeforeCompile = (shader) => {
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
uniform float uTime;
`
);
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
float angle = sin(position.y+uTime) *0.5;
mat2 rotateMatrix = rotate2d(angle);


transformed.xz = rotateMatrix * transformed.xz;


`
)

}

// 设定自定义的深度材质
mesh.customDepthMaterial = depthMaterial;

效果图

完整代码

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
// 初始化场景
const scene = new THREE.Scene();

// 创建透视相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerHeight / window.innerHeight,
1,
50
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(0, 0, 10);
scene.add(camera);

// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加环境纹理
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envMapTexture = cubeTextureLoader.load([
"textures/environmentMaps/0/px.jpg",
"textures/environmentMaps/0/nx.jpg",
"textures/environmentMaps/0/py.jpg",
"textures/environmentMaps/0/ny.jpg",
"textures/environmentMaps/0/pz.jpg",
"textures/environmentMaps/0/nz.jpg",
]);

const directionLight = new THREE.DirectionalLight('#ffffff', 1);
directionLight.castShadow = true;
directionLight.position.set(0, 0, 200)
scene.add(directionLight)


scene.environment = envMapTexture;
scene.background = envMapTexture;

const customUniforms = {
uTime: {
value: 0
}
}
// 模型加载
// 创建纹理加载器对象
const textureLoader = new THREE.TextureLoader();
// 加载模型纹理
const modelTexture = textureLoader.load('./models/LeePerrySmith/color.jpg');
// 加载模型的法向纹理
const normalTexture = textureLoader.load('./models/LeePerrySmith/normal.jpg')

const material = new THREE.MeshStandardMaterial({
map: modelTexture,
normalMap: normalTexture
})
material.onBeforeCompile = (shader) => {
console.log(shader.vertexShader);
console.log(shader.fragmentShader);
// 传递时间
shader.uniforms.uTime = customUniforms.uTime;
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
uniform float uTime;
`
)

shader.vertexShader = shader.vertexShader.replace(
'#include <beginnormal_vertex>',
`
#include <beginnormal_vertex>
float angle = sin(position.y+uTime) *0.5;
mat2 rotateMatrix = rotate2d(angle);


objectNormal.xz = rotateMatrix * objectNormal.xz;
`
)
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
// float angle = transformed.y*0.5;
// mat2 rotateMatrix = rotate2d(angle);


transformed.xz = rotateMatrix * transformed.xz;


`
)
}
const depthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking
})

depthMaterial.onBeforeCompile = (shader) => {
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
uniform float uTime;
`
);
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
float angle = sin(position.y+uTime) *0.5;
mat2 rotateMatrix = rotate2d(angle);


transformed.xz = rotateMatrix * transformed.xz;


`
)

}
const gltfLoader = new GLTFLoader();
gltfLoader.load('./models/LeePerrySmith/LeePerrySmith.glb', (gltf) => {
const mesh = gltf.scene.children[0];
mesh.material = material;
mesh.castShadow = true;
// 设定自定义的深度材质
mesh.customDepthMaterial = depthMaterial;
scene.add(mesh);
})


const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(20, 20),
new THREE.MeshStandardMaterial()
)
plane.position.set(0, 0, -6);
plane.receiveShadow = true;
scene.add(plane)

// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;

// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
// console.log("resize");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();

// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比例
renderer.setPixelRatio(window.devicePixelRatio);
});


// 将渲染器添加到body
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;
const clock = new THREE.Clock()
function animate() {
controls.update()
const time = clock.getElapsedTime();
customUniforms.uTime.value = time;
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}

animate();

上一篇:
【可视化学习】20-效果合成
下一篇:
【可视化学习】18-打造烟花效果