前言
官网已经有很多材质可以用了,效果都是蛮不错的,我们其实完全没有必要自己重新写原生材质(使用着色器),我们完全可以基于官网的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
76import * 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
5basicMaterial.onBeforeCompile = (shader, renderer) => {
console.log(shader);
console.log(shader.vertexShader)
console.log(shader.fragmentShader)
};
我们可以看到这个和我们认知的着色器貌似不一样,让我们打开node_modules中的文件包看看
我们可以看到这些# 代表的是导入了一些模块,而这些模块本质上还是我们原生的着色器所编写的,知道了这点,我们就可以修改了
使用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
117import * 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();
使用onBeforeCompile修改小人被抽打效果(进阶)
加载环境贴图(可以参考之前讲纹理贴图的文章)
1 | import * as THREE from "three"; |
场景中添加白色底板
1 | const plane = new THREE.Mesh( |
导入模型
1 | import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader" |
添加影子
挺久没用阴影了,我们来回顾下阴影的使用
分为4步
render开启阴影
1
renderer.shadowMap.enabled = true;
光开启阴影
1
directionLight.castShadow = true;
需要投射的物体开启阴影
1
mesh.castShadow = true;
接收阴影的物体开启阴影
1
plane.receiveShadow = true;
增加纹理贴图
1 | const textureLoader = new THREE.TextureLoader(); |
引入时间
1 | const customUniforms = { |
根据时间修改材质
这里用到了之前写过的旋转函数,通过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
34material.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
34const 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 | import * as THREE from "three"; |