ThreeJS后期处理
本帖最后由 马黑黑 于 2025-6-6 18:19 编辑 <br /><br /><style>.artBox { font-size: 18px; }
.artBox > p { margin: 10px 0; line-height: 30px; }
.artBox mark { background: lightblue; padding: 4px 8px; }
.table { width: 100%; margin: 20px auto; border-collapse: collapse; box-shadow: 0 0 10px rgba(0,0,0,0.1); font-family: Arial, sans-serif; }
.table th, .table td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #ddd; vertical-align: middle; }
.table th { background-color: #336699; color: white; font-weight: bold; text-align: center; letter-spacing: 6px; }
.table tr:hover { background-color: #f5f5f5; }
.table tr:nth-child(even) { background-color: #f6f6ea; }
#prevBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: beige; display: none; padding: 0; overflow: hidden; z-index: 1000; margin: 0; }
#prevBox::after { position: absolute; content: '关闭预览'; bottom: 10px; left: calc(50% - 40px); padding: 0 4px; width: 80px; height: 30px; line-height: 30px; text-align: center; border: 1px solid #efe; border-radius: 6px; background: #eee; font-size: 14px; box-shadow: 2px 2px 6px rgba(0,0,0,.25); cursor: pointer; }
iframe { position: relative; width: 100%; height: 100%; border: none; outline: none; box-sizing: border-box; margin: 0; }
</style>
<div id="prevBox"></div>
<div class="artBox">
<p>和电影拍完之后还要做一系列的后期处理一样,使用 ThreeJS 创作的图形作品也可以进行类似的工作。ThreeJS 的后期处理,指在按常规绘制好可能包含动画的图像之后,根据需要可以再添加相应的一个或多个效果,这个工作称作 Post Processing。后期处理本质上是一个相当复杂的工作机制,涉及到通道、合成技术等等问题,不过 ThreeJS 提供了一系列的解决方案,我们只需导入相应的库再按流程操作即可。</p>
<p>根据官方示例,以下是 ThreeJS 后期处理基本操作流程:</p>
<h2>一、创建效果合成器 EffectComposer</h2>
<div class="hEdiv"><pre class="hEpre">const composer = new EffectComposer(renderer);</pre></div>
<p>所创建的效果合成器示器实例 composer 将接管 renderer 渲染器渲染的结果,即后续应该使用的渲染方法 <mark>renderer.render(scene, camera);</mark> 将改用<mark>composer.render(); </mark>, 同时将管理调度各个通道 pass 的执行。形象一点讲,composer 搭建一个生产流水线,对之前 ThreeJS 绘制好的产品在各个通道层面进行效果合成然后渲染合成后的最终效果。</p>
<h2>二、创建基础渲染通道 RenderPass</h2>
<div class="hEdiv"><pre class="hEpre">
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
</pre></div>
<p>实例化渲染通道 <mark>RenderPass</mark> 是其它具体通道的通道,也就是说,后续的每一个具体特效通道 *Pass 都因为它的存在才能产生作用,所以也称此通道为基础通道。RenderPass 相当于流水线上的线长,它不从事具体的生产劳动,属于基层领导,它的作用是站线,别的事不干。RenderPass 任命(创建)后需要加入到 composer 合成器中,<mark>composer.addPass(renderPass);</mark>。</p>
<h2>三、创建具体效果通道 *Pass</h2>
<p>ThreeJS 封装有一些具体的效果通道,命名为 *Pass,例如,点网效果通道,<mark>DotScreenPass</mark>,创建方式和基础渲染通道一样,都是先创建然后加入到合成器中:</p>
<div class="hEdiv"><pre class="hEpre">
const dotscreenPass = new DotScreenPass();
composer.addPass(dotscreenPass);
</pre></div>
<p>dotscreenPass 是实例化的 DotscreenPass,DotscreenPass 是 ThreeJS 封装的一个特效类(class),将其实例化后加入合成器 composer 中,该类封装的黑白点效果就会改变之前所绘制的图像。此类 ThreeJS 封装的 class 类有不少,还有第三方封装的其它特效可供选择。</p>
<p>效果合成器生产流水线 composer 上可以有很多个工位,每一个工位负责一个特定通道的效果处理,例如上例的点网效果。安排多少个工位取决于产品的设计需要,需要注意的是,一,每一个工位都要实例化一个具体的通道并立马将处理结果(即实例化变量)加入到效果合成器中,如你在点网效果通道代码示例中所看的那样;二,每一个通道特效都提供有默认的配置,这些配置可以根据需要另行设置。</p>
<h2>四、渲染效果</h2>
<p>当所有特定效果通道都建立完毕并一一加入到后期效果合成器 composer 之后,就可以使用 <mark> composer.render(); </mark>来渲染效果,效果渲染工作通常放在动画函数中,若作品是静态图像也可以放在原先渲染器 renderer.render(scene, camera) 所在的地方。</p>
<h2>五、导入依赖库</h2>
<p>首先,不论使用了多少个 ThreeJS 封装的或第三方封装的后期效果处理库,<mark>EffectComposer</mark>、<mark>RenderPass</mark> 这两个库都不能缺失;其次,具体要使用哪个特定效果通道库就对应导入相应的库,各库的导入都应是在创建了 ThreeJS 场景(scene)、相机(camera)、渲染器(renderer)等之后操作。</p>
<p>下面的实例我们在绘制好一个几何体图像的基础上进行后期处理:一是加入高亮描边特效通道 <mark>OutlinePass</mark>,使用了一些参数设置,其中描边的闪烁属性放在动画函数里;二是加入默认设置的电影胶片颗粒特效通道 <mark>FilmPass</mark>:</p>
<div class="hEdiv"><pre class="hEpre" id="preCode">
<div style="position: absolute; padding: 10px; color: #333;">单击页面暂停/继续动画</div>
<!-- ThreeJS扩展库内部可能使用相对路径,因此需要通过映射方式导入 -->
<script type="importmap">
{
"imports": {
"three": "https://638183.freep.cn/638183/3dev/build/three.module.min.js",
"three/addons/": "https://638183.freep.cn/638183/3dev/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three'; // ThreeJS主库,下面的都是扩展库
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; // 效果合成库
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; // 渲染器基础通道库
import { FilmPass } from 'three/addons/postprocessing/FilmPass.js'; // 电影风格库
import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js'; // 高亮描边通道库
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); // alpha将因为因辉光库缺陷而失效
renderer.setSize(window.innerWidth, window.innerHeight);
const clock = new THREE.Clock();
document.body.appendChild(renderer.domElement);
// 绘制图像 圆环扭结几何体 + 法向量网格材质
const mesh = new THREE.Mesh(
new THREE.TorusKnotGeometry(),
new THREE.MeshNormalMaterial()
);
scene.add(mesh); // 图像加入到场景
// 创建效果合成器
const composer = new EffectComposer(renderer);
// 创建渲染(基础)通道并加入效果合成器
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// 创建第一个效果 : 高亮描边通道并加入效果合成器(三个参数:渲染二维向量、场景、相机)
const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
outlinePass.visibleEdgeColor.set(0xBA55D3); // 描边颜色(缺省默认白色)
outlinePass.edgeThickness = 4; // 描边厚度
outlinePass.edgeStrength = 6; // 描边光亮强度
outlinePass.selectedObjects = ; // 描边作用对象
composer.addPass(outlinePass); // 描边效果通道加入到后期效果合成器
// 创建第二个效果 :老电影胶片颗粒
const filmPass = new FilmPass();
composer.addPass(filmPass);
// 动画
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mesh.rotation.x += delta / 5;
mesh.rotation.y += delta / 5;
outlinePass.pulsePeriod = delta === 0 ? 0 : 1; // 描边闪烁
composer.render(); // 后期合成效果
};
document.onclick = () => clock.running ? clock.stop() : clock.start(); // 动画交互
animate(); //运行动画
</script>
</pre></div>
<blockquote><button id="btnPrev">运行代码</button></blockquote>
<h2>【附】后期处理通道汇总</h2>
<table class="table"><thead><tr><th style="width: 260px;">API</th><th>效果说明</th></tr></thead><tbody><tr><td>AdaptiveToneMappingPass</td><td>根据场景光照强度自动调节场景亮度</td></tr><tr><td>BloomPass</td><td>增强场景中的明亮区域模拟现实世界中的摄像机</td></tr><tr><td>BokehPass</td><td>实现类似于大光圈镜头的景深效果</td></tr><tr><td>ClearPass</td><td>清空纹理缓存</td></tr><tr><td>CubeTexturePass</td><td>渲染天空盒</td></tr><tr><td>DotScreenPass</td><td>将黑点图层应用到屏幕的原始图片上</td></tr><tr><td>FilmPass</td><td>通过扫描线和失真来模拟电视屏幕效果</td></tr><tr><td>GlitchPass</td><td>随机在屏幕上显示电脉冲</td></tr><tr><td>HalftonePass</td><td>模拟传统印刷中的半色调效果,通过网格点大小和疏密表现亮度</td></tr><tr><td>MaskPass</td><td>在当前区域添加掩码,后续的通道只会影响掩码区域</td></tr><tr><td>OutlinePass</td><td>勾勒场景中的轮廓</td></tr><tr><td>RenderPass</td><td>在当前场景和摄像机上渲染出一个新的场景</td></tr><tr><td>SAOPass 、SSAOPass</td><td>实现实时环境光遮挡效果</td></tr><tr><td>SMAAPass、SSAARenderPass</td><td>添加全屏反锯齿效果</td></tr><tr><td>SSAARenderPass</td><td>根据场景光照强度自动调节场景亮度</td></tr><tr><td>SavePass</td><td>反复当前的渲染效果</td></tr><tr><td>ShaderPass</td><td>接收自定义的着色器,生成一个高级、自定义的后期处理通道</td></tr><tr><td>TAARenderPass</td><td>一种全屏反锯齿效果</td></tr><tr><td>AdaptiveToneMappingPass</td><td>根据场景光照强度自动调节场景亮度</td></tr><tr><td>TexturePass</td><td>将其它组合器的当前状态保存为纹理,将其参数传入到其它的组合器里</td></tr><tr><td>UnrealBloomPass</td><td>与Bloom类似的泛光,效果接近于Unreal 3D</td></tr></tbody></table>
</div>
<script type="module">
import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';
const pres = document.querySelectorAll('.hEpre');
const divs = document.querySelectorAll('.hEdiv');
divs.forEach( (div, key) => hlight.hl(div, pres));
const preView = (htmlCode, targetBox) => {
if (targetBox.innerHTML) return;
const iframe = document.createElement('iframe');
htmlCode = htmlCode + '<style>body {margin: 0; }</style>';
iframe.srcdoc = htmlCode;
targetBox.appendChild(iframe);
targetBox.style.display = 'block';
targetBox.onclick = () => {
targetBox.innerHTML = '';
targetBox.style.display = 'none';
}
};
const value = preCode.textContent;
btnPrev.onclick = () => preView(value, prevBox);
</script> 刚学了一遍,知道了第二版《一个人》新出现的黑白轮廓和图片黑白点点渲染是用dotscreenPass这个完成的。是在轮廓描边的效果基础之上,多加了第二个效果。
同理这个范例中的扭结几何体,加了描边轮廓和胶片颗粒效果
const dotscreenPass = new DotScreenPass(new THREE.Vector2( 0, 0 ), 0.5, 0.8);
composer.addPass(dotscreenPass);
const filmPass = new FilmPass();
composer.addPass(filmPass);
对比一下,好好学学。。。{:4_173:} 这个通道库真是漂亮,每种风格都有对应的JS。{:4_173:}把上面已知的两种效果互换一下,每个图都产生了不同的效果
再用HalftonePass这个印刷中的半色调效果,网格是有了,不过是默认的,不会调参数就是了。。是不是要查看源文件可以调整 网格大小 {:4_173:}这个教程也太好用了吧,不仅教了使用流程,原理还直接把各种效果列表出来。。。
这跟之前的几何体结合在一起,不是有各种各样的可能。。
或者给之前用过的那些贴子加上特殊效果,又是另一番景象。。 原来昨天晚上就在给这个教程打伏笔了。。
大赞。。。五体投地佩服一个
先追剧去了。。。{:4_170:} 花飞飞 发表于 2025-6-6 19:38
原来昨天晚上就在给这个教程打伏笔了。。
大赞。。。五体投地佩服一个
先追剧去了。。。
{:4_203:} 花飞飞 发表于 2025-6-6 19:37
这个教程也太好用了吧,不仅教了使用流程,原理还直接把各种效果列表出来。。。
这跟之前的几何 ...
后期效果一般不会滥用,需要才用用 花飞飞 发表于 2025-6-6 19:20
刚学了一遍,知道了第二版《一个人》新出现的黑白轮廓和图片黑白点点渲染是用dotscreenPass这个完成的。是 ...
整体操作流程不难,难在每一个特效库的相关设置,使用者需要去仔细研究,这个方面官方的介绍不多 科研课题! 马黑黑 发表于 2025-6-6 20:06
{:4_173:}就到央八看了个大结局,过瘾。 马黑黑 发表于 2025-6-6 20:06
后期效果一般不会滥用,需要才用用
根据经验初学的时候基本是要滥用一段的{:4_173:} 马黑黑 发表于 2025-6-6 20:08
整体操作流程不难,难在每一个特效库的相关设置,使用者需要去仔细研究,这个方面官方的介绍不多
可能还要研究JS源文件比如那个黑白点的相关设置{:4_173:} 花飞飞 发表于 2025-6-6 21:52
可能还要研究JS源文件比如那个黑白点的相关设置
或者查看别人研究成果,总有人做了很多后期处理案例,他们会罗列出关键的属性 花飞飞 发表于 2025-6-6 21:51
根据经验初学的时候基本是要滥用一段的
那也可以,能用就用 花飞飞 发表于 2025-6-6 21:50
就到央八看了个大结局,过瘾。
恭喜 樵歌 发表于 2025-6-6 20:46
科研课题!
晚上嚎 马黑黑 发表于 2025-6-6 20:08
整体操作流程不难,难在每一个特效库的相关设置,使用者需要去仔细研究,这个方面官方的介绍不多
对的,看到介绍了那么多后期通道,但每一个都需要有相应的设置的吧,像描边就有厚度和光强等设置,那个老电影胶片颗粒倒是使用了默认就行了。 原来这些封装好的效果库都是.js文件,想使用它们还需要创建效果合成器 EffectComposer和渲染通道 RenderPass,这个还真的挺复杂的。
想让没基础的我们看懂也很花心思,看到黑黑用了那么形象的比喻。谢谢黑黑{:4_199:} 马黑黑 发表于 2025-6-6 22:24
或者查看别人研究成果,总有人做了很多后期处理案例,他们会罗列出关键的属性
别人的研究成果我看不懂,你的我才能看懂一丢丢{:4_173:} 马黑黑 发表于 2025-6-6 22:24
那也可以,能用就用
乱用乱用。。瞅个乐呵