Three.js后期处理简明教程

后期处理(Post Processing)通常是指对 2D 图像应用某种效果或滤镜。 在 THREE.js 中我们有一个包含一堆网格物体的场景。 我们将该场景渲染为 2D 图像。 通常,该图像会直接渲染到画布中并显示在浏览器中,但我们可以将其渲染到渲染目标,然后在将结果绘制到画布之前对结果应用一些后处理效果。 之所以称为后处理,是因为它发生在主场景处理之后(后)。

后期处理的例子有 Instagram 滤镜、Photoshop 滤镜等……

在这里插入图片描述

推荐:用 NSDT编辑器 快速搭建可编程3D场景

1、THREE.js后期处理管道

THREE.js 有一些示例类来帮助设置后处理管道。 它的工作方式是创建一个 EffectComposer 并向其中添加多个 Pass 对象。 然后,调用 EffectComposer.render,它将场景渲染到渲染目标,然后应用每个通道。

每个通道(Pass)都可以是一些后期处理效果,例如添加晕影、模糊、应用光晕、应用胶片颗粒、调整色调、饱和度、对比度等…最后将结果渲染到画布上。

了解 EffectComposer 的功能非常重要。 它创建两个渲染目标。 我们称它们为 rtA 和 rtB。

然后,调用 EffectComposer.addPass 按照你想要应用的顺序添加每个通道。 然后像这样应用通道。
在这里插入图片描述

首先,你传递到 RenderPass 的场景被渲染到 rtA,然后 rtA 被传递到下一个通道,无论它是什么(BloomPass、FilmPass…)。 该过程使用 rtA 作为输入来执行其操作并将结果写入 rtB。 然后 rtB 被传递到下一个通道,该通道使用 rtB 作为输入并写回 rtA。 这贯穿所有的通道。

每个通道有 4 个基本选项

  • enabled:是否使用此通道
  • needSwap:完成本次pass后是否交换rtA和rtB
  • clear:渲染此通道之前是否清除
  • renderToScreen:是否渲染到画布而不是当前目标渲染目标。 通常,你需要在添加到 EffectComposer 的最后一个通道中将其设置为 true。

2、THREE.js后期处理快速上手

让我们举一个基本的例子。 我们将从有关响应性的文章中的示例开始。

为此,我们首先创建一个 EffectComposer。

const composer = new EffectComposer(renderer);

然后,作为第一个通道,我们添加一个 RenderPass,它将使用相机将场景渲染到第一个渲染目标中。

composer.addPass(new RenderPass(scene, camera));

接下来我们添加一个 BloomPass。 BloomPass 将其输入渲染到通常较小的渲染目标并模糊结果。 然后,它将模糊结果添加到原始输入之上。 这使得场景具有绽放效果。

const bloomPass = new BloomPass(1,    // strength25,   // kernel size4,    // sigma ?256,  // blur render target resolution
);
composer.addPass(bloomPass);

最后,我们有一个 FilmPass,可以在其输入之上绘制噪声和扫描线。

const filmPass = new FilmPass(0.35,   // noise intensity0.025,  // scanline intensity648,    // scanline countfalse,  // grayscale
);
filmPass.renderToScreen = true;
composer.addPass(filmPass);

由于 filmPass 是最后一个通道,我们将其 renderToScreen 属性设置为 true 以告诉它渲染到画布。 如果不设置此项,它将渲染到下一个渲染目标。

要使用这些类,我们需要导入一堆脚本。

import {EffectComposer} from '/examples/jsm/postprocessing/EffectComposer.js';
import {RenderPass} from '/examples/jsm/postprocessing/RenderPass.js';
import {BloomPass} from '/examples/jsm/postprocessing/BloomPass.js';
import {FilmPass} from '/examples/jsm/postprocessing/FilmPass.js';

对于几乎任何后期处理,都需要 EffectComposer.js 和 RenderPass.js。

我们需要做的最后一件事是使用 EffectComposer.render 而不是 WebGLRenderer.render 并告诉 EffectComposer 匹配画布的大小。

-function render(now) {
-  time *= 0.001;
+let then = 0;
+function render(now) {
+  now *= 0.001;  // convert to seconds
+  const deltaTime = now - then;
+  then = now;if (resizeRendererToDisplaySize(renderer)) {const canvas = renderer.domElement;camera.aspect = canvas.clientWidth / canvas.clientHeight;camera.updateProjectionMatrix();
+    composer.setSize(canvas.width, canvas.height);}cubes.forEach((cube, ndx) => {const speed = 1 + ndx * .1;
-    const rot = time * speed;
+    const rot = now * speed;cube.rotation.x = rot;cube.rotation.y = rot;});-  renderer.render(scene, camera);
+  composer.render(deltaTime);requestAnimationFrame(render);
}

EffectComposer.render 采用 deltaTime,它是自渲染最后一帧以来的时间(以秒为单位)。 它将它传递给各种效果,以防它们中的任何一个被动画化。 在本例中, FilmPass 是动画的。

3、运行时设置效果参数

要在运行时更改效果参数通常需要设置 uniform的值。 让我们添加一个 gui 来调整一些参数。 弄清楚可以轻松调整哪些值以及如何调整它们需要深入研究该效果的代码。

查看 BloomPass.js 内部,我发现了这一行:

this.copyUniforms[ "opacity" ].value = strength;

所以我们可以通过如下代码来设置强度:

bloomPass.copyUniforms.opacity.value = someValue;
同样地,在 FilmPass.js 中我发现了这些行:

if ( grayscale !== undefined )    this.uniforms.grayscale.value = grayscale;
if ( noiseIntensity !== undefined ) this.uniforms.nIntensity.value = noiseIntensity;
if ( scanlinesIntensity !== undefined ) this.uniforms.sIntensity.value = scanlinesIntensity;
if ( scanlinesCount !== undefined ) this.uniforms.sCount.value = scanlinesCount;

这样就很清楚如何设置它们了。

让我们制作一个快速 GUI 来设置这些值

import {GUI} from '/examples/jsm/libs/lil-gui.module.min.js';const gui = new GUI();
{const folder = gui.addFolder('BloomPass');folder.add(bloomPass.copyUniforms.opacity, 'value', 0, 2).name('strength');folder.open();
}
{const folder = gui.addFolder('FilmPass');folder.add(filmPass.uniforms.grayscale, 'value').name('grayscale');folder.add(filmPass.uniforms.nIntensity, 'value', 0, 1).name('noise intensity');folder.add(filmPass.uniforms.sIntensity, 'value', 0, 1).name('scanline intensity');folder.add(filmPass.uniforms.sCount, 'value', 0, 1000).name('scanline count');folder.open();
}

现在我们可以调整这些设置了。

这是实现我们自己的效果的一小步。

4、实现自己的后期处理通道

后期处理效果使用着色器(Shader)。 着色器是用一种称为 GLSL(图形库着色语言)的语言编写的。 对于这些文章来说,回顾整个语言是一个太大的主题。 一些可以开始使用的资源可能是这篇文章,也可能是《着色器之书》。

我认为一个帮助你入门的示例会很有帮助,所以让我们制作一个简单的 GLSL 后处理着色器。 我们将制作一个可以将图像乘以颜色的图像。

对于后期处理,THREE.js 提供了一个有用的助手,称为 ShaderPass。 它需要一个带有定义顶点着色器、片段着色器和默认输入信息的对象。 它将处理设置从哪个纹理读取以获取上一个通道的结果以及渲染到何处:渲染目标或屏幕画布。

这是一个简单的后处理着色器,它将前一个通道的结果乘以颜色。

const colorShader = {uniforms: {tDiffuse: { value: null },color:    { value: new THREE.Color(0x88CCFF) },},vertexShader: `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);}`,fragmentShader: `varying vec2 vUv;uniform sampler2D tDiffuse;uniform vec3 color;void main() {vec4 previousPassColor = texture2D(tDiffuse, vUv);gl_FragColor = vec4(previousPassColor.rgb * color,previousPassColor.a);}`,
};

上面的 tDiffuse 是 ShaderPass 用于传递前一个通道的结果纹理的名称,因此我们几乎总是需要它。 然后我们将颜色声明为 THREE.js的 Color 类型。

接下来我们需要一个顶点着色器。 对于后期处理,此处显示的顶点着色器几乎是标准的,很少需要更改。 无需赘述太多细节(请参阅上面链接的文章),变量 uv、 projectionMatrix、 modelViewMatrix 和 position 均由 THREE.js 神奇地添加。

最后我们创建一个片段着色器。 在其中,我们使用此行从上一次传递中获取像素颜色

vec4 previousPassColor =texture2D(tDiffuse, vUv);
我们将它乘以我们的颜色并将 gl_FragColor 设置为结果

gl_FragColor = vec4(previousPassColor.rgb * color,previousPassColor.a);

添加一些简单的 GUI 来设置颜色的 3 个值:

const gui = new GUI();
gui.add(colorPass.uniforms.color.value, 'r', 0, 4).name('red');
gui.add(colorPass.uniforms.color.value, 'g', 0, 4).name('green');
gui.add(colorPass.uniforms.color.value, 'b', 0, 4).name('blue');

这就为我们提供了乘以颜色的简单后处理效果。

正如前面提到的,关于如何编写 GLSL 和自定义着色器的所有细节对于这些文章来说太多了。 如果你确实想了解 WebGL 本身如何工作,请查看这些文章。 另一个很棒的资源是阅读 THREE.js 存储库中现有的后处理着色器。 有些比其他更复杂,但如果你从较小的开始,就有望了解它们的工作原理。

不幸的是,THREE.js 存储库中的大多数后期处理效果都没有文档,因此要使用它们,你必须通读示例或效果本身的代码。 希望这些简单的示例和有关渲染目标的文章提供足够的上下文来开始。


原文链接:THREE.js后期处理入门 — BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140009.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双向链表的实现(增删查改)——最好理解的链表

双向链表的实现 一,双向链表的特点二,双向链表的结构三,双向链表的内容实现3.1创建node节点3.2初始化3.3打印3.4插入3.4.1尾插3.4.2头插3.4.3在pos位置上插入 3.5删除3.5.1尾删3.5.2头删3.5.3删除pos位置上的数据 四,调试技巧&…

html页面仿word文档样式(vue页面也适用)

目录 文章title&#xff1a; 标题&#xff1a; 正文&#xff1a; 完整代码&#xff1a; 页面效果&#xff1a; 文章title&#xff1a; <div><h3 style"display: flex;justify-content: center; align-items: center; color: #000;">实验室招新报名公…

【100天精通Python】Day69:Python可视化_实战:导航定位中预测轨迹和实际轨迹的3D动画,示例+代码

目录 1. 预测的3D轨迹和实际轨迹的动画图&#xff0c;同时动态更新 2 真值轨迹设置为静态的&#xff0c;预测轨迹不断更新 3 网格的三维坐标系有旋转运动&#xff0c;以此全方位展示预测轨迹和真值轨迹之间的空间关系 1. 预测的3D轨迹和实际轨迹的动画图&#xff0c;同时动态更…

Nginx 防止跨站脚本 Cross-Site Scripting (XSS)

1、修改 nginx 配置 在 nginx.conf 配置文件中&#xff0c;增加如下配置内容&#xff1a; add_header X-XSS-Protection "1; modeblock";X-XSS-Protection 的字段有三个可选配置值&#xff0c;说明如下&#xff1a; 0&#xff1a; 表示关闭浏览器的XSS防护机制&…

ad18学习笔记十一:显示和隐藏网络、铺铜

如何显示和隐藏网络&#xff1f; Altium Designer--如何快速查看PCB网络布线_ad原理图查看某一网络的走线_辉_0527的博客-CSDN博客 AD19(Altium Designer)如何显示和隐藏网络 如何显示和隐藏铺铜&#xff1f; Altium Designer 20在PCB中显示或隐藏每层铺铜-百度经验 AD打开与…

怎么将自己的Maven项目上传到Maven中央仓库/Maven阿里云云效仓库

前言 对于工作了多年的老程序员来说&#xff0c;往往会总结出一些比较好用的开发工具包。那么如果把这些好的工具插件共享出来供大家快速的使用呢&#xff0c;最好的方式就是将这些工具插件上传到Maven中央仓库/Maven阿里云云效仓库&#xff0c;这样&#xff0c;有需要用到这些…

八大排序(一)冒泡排序,选择排序,插入排序,希尔排序

一、冒泡排序 冒泡排序的原理是&#xff1a;从左到右&#xff0c;相邻元素进行比较。每次比较一轮&#xff0c;就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。 以从小到大排序为例&#xff0c;第一轮比较后&#xff0c;所有数中最大的那个数就会浮…

verilog学习笔记(1)module实例化

兜兜转转又回来学硬件了&#xff0c;哎&#xff0c;命啊&#xff01; 我的答案&#xff08;有bug&#xff09;&#xff1a; module top_module ( input a, input b, output out );wire w1;wire w2;wire w3;mod_a mod_a_inst1(.in1(w1),.in2(w2),.out(w3) );assign w1 a…

基于微信小程序的房屋租赁系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信小程序端的主要功能有&#xff1a;户主微信小程序端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文…

解决 Github port 443 : Timed out

解决方法 打开代理页面 打开 设置 --> 网络与Internet --> 查找代理 记录下当前系统代理的 IP 地址和端口号 如上图所示&#xff0c;地址与端口号为&#xff1a;127.0.0.1:7890 注意修改成自己的IP和端口号 git config --global http.proxy http://127.0.0.1:7890 gi…

Spring面试题12:Spring中IOC的优缺点是什么?IOC依赖注入方式有哪些

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring中IOC的优缺点是什么? IOC(Inversion of Control,控制反转)是Spring框架的一个重要特性,它实现了对象的创建和依赖关系的管理的反转。…

【Linux】Linux环境配置安装

目录 一、双系统&#xff08;特别不推荐&#xff09; 安装双系统的缺点&#xff1a; 安装双系统优点&#xff08;仅限老手&#xff09;&#xff1a; 二、虚拟机centos7镜像&#xff08;较为推荐推荐&#xff09; 虚拟机的优点&#xff1a; 虚拟机的缺点&#xff1a; ​ …

6年Android开发前10月的总结,写给正在求职的安卓开发

进入大厂工作对许多人来说已经是一种挑战&#xff0c;但只要充分准备&#xff0c;很多问题都可以逐步解决。当然&#xff0c;运气也起到了一定的作用&#xff0c;但最终还是与自身的努力密不可分。运气是实力的一部分&#xff0c;因为自助者天助。 每到10月进行总结时&#xff…

数据结构与算法(二)

文章目录 数据结构与算法(二)1 时间复杂度、空间复杂度、排序算法和二分法1.1 简单的排序算法1.2 二分查找2 异或运算、进一步认识对数器的重要性2.1 不用额外变量交换两个数的值2.2 不用额外变量交换数组中两个数的值2.3 一个数组中有一种数出现了奇数次,其他数都出现了偶数…

读写分离MySQL

利用Mycat控制后台数据库的读写分离和负载均衡 利用主从复制思想,实现读写分离,主库写,从库读 从库最好不要写,因为从库写入的数据不能同步到主库,只有主库写的数据才能同步到从库 balance属性值对应的含义(负载均衡) 一主一从读写分离的弊端 主节点Master宕机以后,业务系统…

docker 操作redis

1查看容器 2进入容器 exec表示在运行的容器中执行命令it表示以终端交互的方式执行命令/bin/bash表示需要指定的命令 3进入容器后可通过redis-cli命令连接容器内的redis服务器&#xff0c;可通过set创建变量&#xff0c;get获取变量的值 4key * 查看所有key 通过ping 查看redi…

【深度学习实验】前馈神经网络(final):final

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 构建数据集&#xff08;IrisDataset&#xff09; 2. 构建模型&#xff08;FeedForward&#xff09; a. __init__(初始化) b. forward(前向传播) 3.整合训练、评估…

golang优先级坑

看如下代码&#xff0c;我本以为a1, a2是相同的 package mainimport "fmt"func main() {b, c, d : 1, 0, 1a1 : b ^ c&(^d) // 1 ^a2 : c ^ b&(^d) // 0 ^fmt.Println(a1, a2) // 1 0 }但结果却是不同的&#xff0c;在golang中&的优先级^和&#xff5c;…

罗德里格斯公式

1.点乘 A ⃗ ⋅ B ⃗ ∣ A ⃗ ∣ ∣ B ⃗ ∣ c o s ⟨ A ⃗ , B ⃗ ⟩ \vec{A} \cdot \vec{B} \left | \vec{A} \right | \left | \vec{B} \right | cos\left \langle \vec{A}, \vec{B} \right \rangle A ⋅B ​A ​ ​B ​cos⟨A ,B ⟩ 对应几何意义&#xff1a;向量 A ⃗…

众佰诚:抖音店铺开网店前期需要投入多少

随着互联网的迅猛发展&#xff0c;电子商务已经成为了商业领域中的一股不可忽视的力量。而在电子商务中&#xff0c;抖音店铺已经成为了一个备受关注的平台&#xff0c;吸引了众多创业者和商家的关注。那么&#xff0c;在开设抖音店铺并转型为网店之前&#xff0c;究竟需要投入…