three.js-基础材质实现着色器编程

一、基础材质实现着色器编程

Three.js提供了着色器材质(ShaderMaterial和RawShaderMaterial,着色器材质可参考这篇文章),它允许我们编写自定义着色器代码,着色器代码就像是一份指导书,告诉计算机如何计算和着色每个像素,通过直接操作顶点和像素信息,来实现更高级更复杂的效果,着色器编程给我们提供了无限的创造的可能。如果我们只想做一些简单的修改,比如改变颜色或加一些特效。那么可以使用Three.js的基础材质,并且通过一个叫做onBeforeCompile的方法来修改着色器代码。three.js的所有材质都是通过着色器编程实现的,而onBeforeCompile这个方法可以在渲染物体之前拿到着色器代码,并且修改它们,从而实现我们想要的效果。

现在,让我们来看下这个函数的作用。

1、搭建一个three.js场景

(详细介绍可参考这篇

2、创建一个物体

// 创建一个几何体
const geometry = new THREE.PlaneGeometry(1, 1, 64, 64);
// 基础材质:基础网格材质
const basicMaterial = new THREE.MeshBasicMaterial({
color: '#00ff00',
side: THREE.DoubleSide,
});
// 创建网格:将几何体和材质结合起来
const plane = new THREE.Mesh(geometry, basicMaterial);
// 添加到场景
scene.add(plane)
// 创建一个几何体
const geometry = new THREE.PlaneGeometry(1, 1, 64, 64);
// 基础材质:基础网格材质
const basicMaterial = new THREE.MeshBasicMaterial({
    color: '#00ff00',
    side: THREE.DoubleSide,
});
// 创建网格:将几何体和材质结合起来
const plane = new THREE.Mesh(geometry, basicMaterial);
// 添加到场景
scene.add(plane)
// 创建一个几何体 const geometry = new THREE.PlaneGeometry(1, 1, 64, 64); // 基础材质:基础网格材质 const basicMaterial = new THREE.MeshBasicMaterial({ color: '#00ff00', side: THREE.DoubleSide, }); // 创建网格:将几何体和材质结合起来 const plane = new THREE.Mesh(geometry, basicMaterial); // 添加到场景 scene.add(plane)

效果:

image.png

3、通过onBeforeCompile获取着色器代码

你可以把着色器代码想象成一本烹饪菜谱,告诉计算机如何烹饪和上色每个像素。而onBeforeCompile就好比是给你一份菜谱的草稿,你可以在烹饪之前对菜谱进行调整,加入自己的食材和独特的烹饪技巧。

// 在编译shader程序之前立即执行的可选回调。此函数使用shader源码作为参数。用于修改内置材质。
basicMaterial.onBeforeCompile = (shader, renderer) => {
console.log(shader.vertexShader);
console.log(shader.fragmentShader);`
}
// 在编译shader程序之前立即执行的可选回调。此函数使用shader源码作为参数。用于修改内置材质。
basicMaterial.onBeforeCompile = (shader, renderer) => {
   console.log(shader.vertexShader);
   console.log(shader.fragmentShader);`
}
// 在编译shader程序之前立即执行的可选回调。此函数使用shader源码作为参数。用于修改内置材质。 basicMaterial.onBeforeCompile = (shader, renderer) => { console.log(shader.vertexShader); console.log(shader.fragmentShader);` }

我们看下打印出来的顶点着色器和片元着色器代码:

image.png

着色器代码是以字符串形式表示的,因此我们可以使用字符串的替换方法replace来修改着色器代码。
引入的#include <common>...这些GLSL代码块可以在node_modules/three/src/renderers/shaders目录下找到。对此感兴趣可以看下具体内容。

在顶点着色器#include <common>...这个位置接收一个时间的uniform变量,在片元着色器中通过周期性的改变transformed变量的x和z坐标来实现物体在x和z轴上的周期性移动。
transformed变量描述了物体在三维空间的位置、旋转和缩放。改变transform时物体会经过模型矩阵(本地坐标系变换到世界坐标系)改变它在三维空间的位置、旋转和缩放。

// 定义Unform变量
const basicUniform = {
uTime: {
value: 0
}
}
// 渲染函数中给uTime赋值
//渲染函数
const clock = new THREE.Clock()
function render() {
let time = clock.getElapsedTime();
basicUniform.uTime.value = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
basicMaterial.onBeforeCompile = (shader, renderer) => {
//将uTime传入到着色器中
shader.uniforms.uTime = basicUniform.uTime;
// 找到要替换的位置进行替换:顶点着色器接收unifrom变量uTime
shader.vertexShader = shader.vertexShader.replace(
'#include <common>',
`
#include <common>
uniform float uTime;
`
)
// 这段代码将物体在x轴上按照`sin(uTime)`的值进行周期性移动,而在z轴上按照`cos(uTime)`的值进行周期性移动。
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
`
#include <begin_vertex>
transformed.x += sin(uTime)* 2.0;
transformed.z += cos(uTime)* 2.0;
`
)
}
// 定义Unform变量
const basicUniform = {
    uTime: {
        value: 0
    }
}
// 渲染函数中给uTime赋值
//渲染函数
const clock = new THREE.Clock()
function render() {
    let time = clock.getElapsedTime();
    basicUniform.uTime.value = time;
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
basicMaterial.onBeforeCompile = (shader, renderer) => {
  //将uTime传入到着色器中
  shader.uniforms.uTime = basicUniform.uTime;
  // 找到要替换的位置进行替换:顶点着色器接收unifrom变量uTime
  shader.vertexShader = shader.vertexShader.replace(
    '#include <common>',
    `
    #include <common>
    uniform float uTime;
    `
  )
  // 这段代码将物体在x轴上按照`sin(uTime)`的值进行周期性移动,而在z轴上按照`cos(uTime)`的值进行周期性移动。
  shader.vertexShader = shader.vertexShader.replace(
    '#include <begin_vertex>',
    `
    #include <begin_vertex>
    transformed.x += sin(uTime)* 2.0;
    transformed.z += cos(uTime)* 2.0;
    `
  )
}
// 定义Unform变量 const basicUniform = { uTime: { value: 0 } } // 渲染函数中给uTime赋值 //渲染函数 const clock = new THREE.Clock() function render() { let time = clock.getElapsedTime(); basicUniform.uTime.value = time; renderer.render(scene, camera); requestAnimationFrame(render); } basicMaterial.onBeforeCompile = (shader, renderer) => { //将uTime传入到着色器中 shader.uniforms.uTime = basicUniform.uTime; // 找到要替换的位置进行替换:顶点着色器接收unifrom变量uTime shader.vertexShader = shader.vertexShader.replace( '#include <common>', ` #include <common> uniform float uTime; ` ) // 这段代码将物体在x轴上按照`sin(uTime)`的值进行周期性移动,而在z轴上按照`cos(uTime)`的值进行周期性移动。 shader.vertexShader = shader.vertexShader.replace( '#include <begin_vertex>', ` #include <begin_vertex> transformed.x += sin(uTime)* 2.0; transformed.z += cos(uTime)* 2.0; ` ) }

通过上面的修改,实现下图的动态效果。

onBeforeComplie.gif

二、修改gltf模型及其投影

1、引入gltf模型

GLTF是一种用于传输和加载3D模型的开放标准格式。它的作用是提供了一种通用的格式,以便在不同的3D软件或引擎之间共享和交换3D内容。

GLTF模型是一种以二进制格式存储的3D模型文件,它包含了模型的几何形状、材质、动画等相关信息。与传统的3D模型文件格式相比,GLTF文件通常更小,加载速度更快,同时还支持复杂的材质和动画效果。

three.js提供了内置的,GLTF加载器(GLTFLoader),可以方便的加载和展示GLTF模型。

引入GLTF加载器

import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'

这里用到的模型是官网的一个例子

引入模型:

// 标准网格材质
const material = new THREE.MeshStandardMaterial({
map: colorTexture,
normalMap: normalTexture,
})
// 深度网格材质,修改阴影
const depthMaterial= new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking
})
// 实例化GLTF加载器
const gltfLoader = new GLTFLoader();
// 引入模型文件:import xxx from 'xxx' 直接这样引入会报错。放在dist目录下可以这样引入。
// 不放在dist目录下需要这样引入。 运行时动态生成文件路径
const gltfFile = new URL('../assets/img/LeePerrySmith.glb', import.meta.url).href;
gltfLoader.load(gltfFile, (gltf) => {
// 在回调函数中处理加载完成后的模型
let mesh = gltf.scene.children[0];
// 设置模型的材质
mesh.material = material;
// 设置模型的自定义深度材质 用于在渲染阶段更精确地控制模型的深度与阴影。
mesh.customDepthMaterial = depthMaterial;
// 开启模型的阴影投射
mesh.castShadow = true;
// 将模型添加到场景中
scene.add(mesh);
}, function (xhr) {
// 加载过程中的回调函数,可用于获取加载进度信息
console.log(xhr)
}, (err) => {
// 加载失败时的回调函数,用于处理错误
console.log(err)
})
// 标准网格材质 
const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    normalMap: normalTexture,
})
// 深度网格材质,修改阴影
const depthMaterial= new THREE.MeshDepthMaterial({
    depthPacking: THREE.RGBADepthPacking
})
// 实例化GLTF加载器
const gltfLoader = new GLTFLoader();
// 引入模型文件:import xxx from 'xxx' 直接这样引入会报错。放在dist目录下可以这样引入。
// 不放在dist目录下需要这样引入。 运行时动态生成文件路径
const gltfFile = new URL('../assets/img/LeePerrySmith.glb', import.meta.url).href;
gltfLoader.load(gltfFile, (gltf) => {
     // 在回调函数中处理加载完成后的模型
    let mesh = gltf.scene.children[0];
    // 设置模型的材质  
    mesh.material = material;
    // 设置模型的自定义深度材质 用于在渲染阶段更精确地控制模型的深度与阴影。
    mesh.customDepthMaterial = depthMaterial;
    // 开启模型的阴影投射
    mesh.castShadow = true;
    // 将模型添加到场景中
    scene.add(mesh);

}, function (xhr) {
    // 加载过程中的回调函数,可用于获取加载进度信息
    console.log(xhr)
}, (err) => {
    // 加载失败时的回调函数,用于处理错误
    console.log(err)
})
// 标准网格材质 const material = new THREE.MeshStandardMaterial({ map: colorTexture, normalMap: normalTexture, }) // 深度网格材质,修改阴影 const depthMaterial= new THREE.MeshDepthMaterial({ depthPacking: THREE.RGBADepthPacking }) // 实例化GLTF加载器 const gltfLoader = new GLTFLoader(); // 引入模型文件:import xxx from 'xxx' 直接这样引入会报错。放在dist目录下可以这样引入。 // 不放在dist目录下需要这样引入。 运行时动态生成文件路径 const gltfFile = new URL('../assets/img/LeePerrySmith.glb', import.meta.url).href; gltfLoader.load(gltfFile, (gltf) => { // 在回调函数中处理加载完成后的模型 let mesh = gltf.scene.children[0]; // 设置模型的材质 mesh.material = material; // 设置模型的自定义深度材质 用于在渲染阶段更精确地控制模型的深度与阴影。 mesh.customDepthMaterial = depthMaterial; // 开启模型的阴影投射 mesh.castShadow = true; // 将模型添加到场景中 scene.add(mesh); }, function (xhr) { // 加载过程中的回调函数,可用于获取加载进度信息 console.log(xhr) }, (err) => { // 加载失败时的回调函数,用于处理错误 console.log(err) })

*注:
customDepthMaterial:three.js中的一个属性,用来决定一个物体在生成深度贴图(可以理解为每个像素距相机的位置信息)时长什么样子,它只记录物体的形状,不关注颜色或花纹。
RGBADepthPacking: 是一种纹理压缩技术。如果将深度信息单独存储在深度纹理中,需要额外的GPU内存和带宽,而RGBADepthPacking方法是用来将深度信息转化为颜色信息,存储在每一个像素点的红、绿、蓝、透明度四个通道中,减少内内存和带宽的使用。

效果:模型正确的引入了。

image.png

接下来我们在这个模型背后添加一个平面,让模型在平面上留下投影。

// 平面
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial()
)
// 接收投影
plane.receiveShadow = true;
plane.position.set(0, 0, -6)
scene.add(plane)
// 平面
const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(20, 20),
    new THREE.MeshStandardMaterial()
)
// 接收投影
plane.receiveShadow = true;
plane.position.set(0, 0, -6)
scene.add(plane)
// 平面 const plane = new THREE.Mesh( new THREE.PlaneGeometry(20, 20), new THREE.MeshStandardMaterial() ) // 接收投影 plane.receiveShadow = true; plane.position.set(0, 0, -6) scene.add(plane)

再看下效果:

gltf.gif

2、改变模型效果

接下来,使用 onBeforeCompile 方法来获取 shader 代码,然后对这个模型进行修改。
给这个模型添加个扭曲效果。扭曲效果通过旋转顶点实现,旋转顶点实际是在改变顶点的位置和方向,就好像我们旋转一张纸,纸上的点的位置改变了,纸张也就变得扭曲了。

首先,在顶点着色器的顶部位置,添加一个旋转函数,这个函数是个2 * 2的矩阵,接收一个旋转角度作为参数,当我们调用这个函数时,它会根据传入的角度,得到一个对应的旋转矩阵。然后将顶点坐标(通常是一个4维向量,包含(x, y, z, w))与旋转矩阵相乘(矩阵乘法是线性代数的知识,关于矩阵和矩阵的运算感兴趣的可以自行去学习,咱的线性代数知识早就还给了老师,所以咱现在暂时还不会),得到一个新的顶点坐标。这个乘法操作实际上就是将旋转矩阵应用到顶点上,从而实现顶点的旋转。旋转矩阵和着色器编程可以直接参考这本书

具体实现
首先,替换顶点着色器中的’#include <common>,替换为旋转函数的定义,代码如下:

const uniformParams = {
uTime: {
value: 0,
},
}
// 改变着色器材质
material.onBeforeCompile = (shader) => {
// 向着色器代码中添加一个名为uTime的uniform变量
shader.uniforms.uTime = uniformParams.uTime.value;
// 在顶点着色器增加旋转函数
shader.vertexShader = shader.vertexShader.replace('#include <common>',
` #include <common>
uniform float uTime;
mat2 rotate2D(float _angle){
// 2 * 2的旋转矩阵
return mat2 (cos(_angle),-sin(_angle),sin(_angle),cos(_angle));
}`
)
}
const clock = new THREE.Clock()
//渲染函数
function render() {
let time = clock.getElapsedTime();
uniformParams.uTime.value = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
const uniformParams = {
    uTime: {
        value: 0,
    },
}

// 改变着色器材质
material.onBeforeCompile = (shader) => {
    // 向着色器代码中添加一个名为uTime的uniform变量
    shader.uniforms.uTime = uniformParams.uTime.value;
    // 在顶点着色器增加旋转函数
    shader.vertexShader = shader.vertexShader.replace('#include <common>',
        ` #include <common>
        uniform float uTime;
        mat2 rotate2D(float _angle){
        // 2 * 2的旋转矩阵
            return mat2 (cos(_angle),-sin(_angle),sin(_angle),cos(_angle));
        }`
    )
}
const clock = new THREE.Clock()
//渲染函数
function render() {
    let time = clock.getElapsedTime();
    uniformParams.uTime.value = time;
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
const uniformParams = { uTime: { value: 0, }, } // 改变着色器材质 material.onBeforeCompile = (shader) => { // 向着色器代码中添加一个名为uTime的uniform变量 shader.uniforms.uTime = uniformParams.uTime.value; // 在顶点着色器增加旋转函数 shader.vertexShader = shader.vertexShader.replace('#include <common>', ` #include <common> uniform float uTime; mat2 rotate2D(float _angle){ // 2 * 2的旋转矩阵 return mat2 (cos(_angle),-sin(_angle),sin(_angle),cos(_angle)); }` ) } const clock = new THREE.Clock() //渲染函数 function render() { let time = clock.getElapsedTime(); uniformParams.uTime.value = time; renderer.render(scene, camera); requestAnimationFrame(render); }

打印下顶点着色器(shader.vertexShader),可以看到里面有一句这样的代码#include <beginnormal_vertex>,我们可以打开这个glsl的源码看一看:它定义了一个顶点法线和切线的变量。这里只是定义了法向和切线的变量,并没有做具体的计算,这些变量在着色器的其他部分可能会被用来处理光照效果、纹理映射或其他图形处理操作。
法线(objectNormal)可以被想象成垂直于模型表面的箭头,用来表示每个顶点的朝向。切线(objectTangent)是与法线垂直的向量,用于处理一些纹理映射和光照计算。

// beginnormal_vertex.glsl.js
export default /* glsl */`
vec3 objectNormal = vec3( normal );
// 条件编译
#ifdef USE_TANGENT
vec3 objectTangent = vec3( tangent.xyz );
#endif
`;
// beginnormal_vertex.glsl.js
export default /* glsl */`
vec3 objectNormal = vec3( normal );
// 条件编译
#ifdef USE_TANGENT
    vec3 objectTangent = vec3( tangent.xyz );
#endif
`;
// beginnormal_vertex.glsl.js export default /* glsl */` vec3 objectNormal = vec3( normal ); // 条件编译 #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); #endif `;

然后,修改法向量。修改法向量会影响光照效果。光照的表现受到光源的位置和方向以及模型表面法向量的影响。如果只修改模型的顶点而不调整光照,那么变换后的模型和光照效果将不匹配,无法达到预期的效果。

实现:替换顶点着色器中的#include <beginnormal_vertex>部分,将其替换为修改法向光照的代码。这段代码通过在顶点着色器中计算一个角度,并根据该角度计算一个旋转矩阵,将物体的法向量进行旋转。
代码如下:

material.onBeforeCompile = (shader) => {
// 修改法向光照
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;
`)
}
material.onBeforeCompile = (shader) => {

  // 修改法向光照
    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;
`)
}
material.onBeforeCompile = (shader) => { // 修改法向光照 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; `) }

下图是修改光照前和后的对比图,可以明显看出区别。

image.pngimage.png

最后,替换顶点着色器中的#include <begin_vertex>部分,将其替换为对顶点进行旋转的代码。这段代码通过将顶点坐标与旋转矩阵相乘,实现顶点的旋转效果。

material.onBeforeCompile = (shader) => {
// 设置顶点着色器的顶点进行旋转
shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', `
#include <begin_vertex>
transformed.xz = rotateMatrix * transformed.xz;
`
)
}
material.onBeforeCompile = (shader) => {

    // 设置顶点着色器的顶点进行旋转
    shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', `
    #include <begin_vertex>
    transformed.xz = rotateMatrix * transformed.xz;
`
    )
}
material.onBeforeCompile = (shader) => { // 设置顶点着色器的顶点进行旋转 shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', ` #include <begin_vertex> transformed.xz = rotateMatrix * transformed.xz; ` ) }

效果:

扭曲.gif

可以看到人物模型扭曲了,但后面的投影还是扭曲前的投影,为了达到更真实的效果,接下来要给投影也加上同样的扭曲效果。

3、改变投影效果

代码和上面一样。

// 修改深度材质的着色器
depthMaterial.onBeforeCompile = (shader) => {
shader.vertexShader = shader.vertexShader.replace('#include <common>',
` #include <common>
uniform float uTime;
mat2 rotate2D(float _angle){
return mat2 (cos(_angle),-sin(_angle),sin(_angle),cos(_angle));
}`
)
// 设置顶点着色器的顶点进行旋转
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;
`
)
}
// 修改深度材质的着色器
depthMaterial.onBeforeCompile = (shader) => {
    shader.vertexShader = shader.vertexShader.replace('#include <common>',
        ` #include <common>
        uniform float uTime;
        mat2 rotate2D(float _angle){
            return mat2 (cos(_angle),-sin(_angle),sin(_angle),cos(_angle));
        }`
    )
    // 设置顶点着色器的顶点进行旋转
    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;
`
    )
}
// 修改深度材质的着色器 depthMaterial.onBeforeCompile = (shader) => { shader.vertexShader = shader.vertexShader.replace('#include <common>', ` #include <common> uniform float uTime; mat2 rotate2D(float _angle){ return mat2 (cos(_angle),-sin(_angle),sin(_angle),cos(_angle)); }` ) // 设置顶点着色器的顶点进行旋转 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; ` ) }

效果:

投影扭曲.gif

三、效果合成器

简单介绍下three.js自带的后期效果。
创建一个后期处理器

// 引入后期效果合成器
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
// 添加合成效果
const effectComposer = new EffectComposer(renderer)
// 设置合成器大小
effectComposer.setSize(window.innerWidth,window.innerHeight);
// 添加一个渲染通道
const renderPass = new RenderPass(scene,camera)
// 将渲染通道添加到合成器
effectComposer.addPass(renderPass)
// 在渲染函数中渲染场景
function render() {
effectComposer.render()
}
// 引入后期效果合成器
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
// 添加合成效果
const effectComposer = new EffectComposer(renderer)
// 设置合成器大小
effectComposer.setSize(window.innerWidth,window.innerHeight);
// 添加一个渲染通道
const renderPass = new RenderPass(scene,camera)
// 将渲染通道添加到合成器
effectComposer.addPass(renderPass)
// 在渲染函数中渲染场景
function render() {
     effectComposer.render()
}
// 引入后期效果合成器 import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'; // 添加合成效果 const effectComposer = new EffectComposer(renderer) // 设置合成器大小 effectComposer.setSize(window.innerWidth,window.innerHeight); // 添加一个渲染通道 const renderPass = new RenderPass(scene,camera) // 将渲染通道添加到合成器 effectComposer.addPass(renderPass) // 在渲染函数中渲染场景 function render() { effectComposer.render() }

后期效果:
故障风:

import {GlitchPass} from 'three/examples/jsm/postprocessing/GlitchPass'
const glitchPass = new GlitchPass();
effectComposer.addPass(glitchPass);
import {GlitchPass} from 'three/examples/jsm/postprocessing/GlitchPass'
const glitchPass = new GlitchPass();
effectComposer.addPass(glitchPass);
import {GlitchPass} from 'three/examples/jsm/postprocessing/GlitchPass' const glitchPass = new GlitchPass(); effectComposer.addPass(glitchPass);

效果可以看这个官网例子
发光效果

import {UnrealBloomPass} from 'three/examples/jsm/postprocessing/UnrealBloomPass'
//发光效果
const unrealBloomPass = new UnrealBloomPass();
effectComposer.addPass(unrealBloomPass);
//强度
unrealBloomPass.strength = 1;
//光晕的半径
unrealBloomPass.radius = 0;
//表示产生泛光的光照强
unrealBloomPass.threshold = 1;
import {UnrealBloomPass} from 'three/examples/jsm/postprocessing/UnrealBloomPass'
//发光效果
const unrealBloomPass = new UnrealBloomPass();
effectComposer.addPass(unrealBloomPass);
//强度
unrealBloomPass.strength = 1;
//光晕的半径
unrealBloomPass.radius = 0;
//表示产生泛光的光照强
unrealBloomPass.threshold = 1;
import {UnrealBloomPass} from 'three/examples/jsm/postprocessing/UnrealBloomPass' //发光效果 const unrealBloomPass = new UnrealBloomPass(); effectComposer.addPass(unrealBloomPass); //强度 unrealBloomPass.strength = 1; //光晕的半径 unrealBloomPass.radius = 0; //表示产生泛光的光照强 unrealBloomPass.threshold = 1;

效果:官网例子

抗锯齿有好几种

// 抗锯齿
import {SMAAPass} from 'three/examples/jsm/postprocessing/SMAAPass'
const smaaPass = new SMAAPass();
effectComposer.addPass(smaaPass);
// 抗锯齿
import {SMAAPass} from 'three/examples/jsm/postprocessing/SMAAPass'
const smaaPass = new SMAAPass();
effectComposer.addPass(smaaPass);
// 抗锯齿 import {SMAAPass} from 'three/examples/jsm/postprocessing/SMAAPass' const smaaPass = new SMAAPass(); effectComposer.addPass(smaaPass);
import {SSAARenderPass} from 'three/examples/jsm/postprocessing/SSAARenderPass'
// 创建并添加 SSAA 抗锯齿通道
const ssaaRenderPass = new SSAARenderPass(scene, camera);
ssaaRenderPass.unbiased = false; // 可选,设置是否开启无偏抗锯齿
composer.addPass(ssaaRenderPass);
import {SSAARenderPass} from 'three/examples/jsm/postprocessing/SSAARenderPass'
// 创建并添加 SSAA 抗锯齿通道
const ssaaRenderPass = new SSAARenderPass(scene, camera);
ssaaRenderPass.unbiased = false; // 可选,设置是否开启无偏抗锯齿
composer.addPass(ssaaRenderPass);
import {SSAARenderPass} from 'three/examples/jsm/postprocessing/SSAARenderPass' // 创建并添加 SSAA 抗锯齿通道 const ssaaRenderPass = new SSAARenderPass(scene, camera); ssaaRenderPass.unbiased = false; // 可选,设置是否开启无偏抗锯齿 composer.addPass(ssaaRenderPass);

效果:官网例子 官网例子
粒子效果:

import {DotScreenPass} from 'three/examples/jsm/postprocessing/DotScreenPass';
const dotScreenPass = new DotScreenPass();
effectComposer.addPass(dotScreenPass);
dotScreenPass.enabled = false;
import {DotScreenPass} from 'three/examples/jsm/postprocessing/DotScreenPass';
const dotScreenPass = new DotScreenPass();
effectComposer.addPass(dotScreenPass);
dotScreenPass.enabled = false;
import {DotScreenPass} from 'three/examples/jsm/postprocessing/DotScreenPass'; const dotScreenPass = new DotScreenPass(); effectComposer.addPass(dotScreenPass); dotScreenPass.enabled = false;

效果:官网例子
更多效果可去查看官网

四、结语

希望大家的学习都是基于兴趣与好奇,而不是焦虑与不安。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYwHDkNp' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片