threejs做特效:实现物体的发光效果-EffectComposer详解!
点击关注公众号,“技术干货”及时达!简介与效果概览各位大佬给个赞,感谢呢!
threejs的开发中,实现物体发光效果是一个常见需求,比如实现楼体的等待照明
要想实现这样的效果,我们只需要了解一个效果合成器概念:EffectComposer。
效果合成器能够合成各种花里胡哨的效果,好比是一个做特效的AE,本教程,我们将使用它来实现一个简单的发光效果。
如图,这是我们将导入的一个模型
.
我们要给他赋予灵魂,实现下面的发光效果
顺带的,我们要实现物体的自动旋转、一个简单的性能监视器、一个发光参数调节的面板
技术方案原生html框架搭建借助threejs实现一个物体发光效果非常简单,首先我们使用html搭建一个简单的开发框架
参考官方起步文档:three.js中文网
!DOCTYPEhtml
htmllang="en"
head
titlethree.js物体发光效果/title
metacharset="utf-8"/
metaname="viewport"content="width=device-width,user-scalable=no,minimum-scale=1.0,maximum-scale=1.0"/
linktype="text/css"rel="stylesheet"href="./main.css"/
style
#info*{
max-width:650px;
margin-left:auto;
margin-right:auto;
}
/style
/head
body
divid="container"/div
scripttype="importmap"
{
"imports":{
"three":"https://unpkg.com/three@0.163.0/build/three.module.js",
"three/addons/":"https://unpkg.com/three@0.163.0/examples/jsm/"
}
}
/script
scripttype="module"
import*asTHREEfrom"three";
import{OrbitControls}from"three/addons/controls/OrbitControls.js";
import{GLTFLoader}from"three/addons/loaders/GLTFLoader.js";
/script
/body
/html
上述代码中,我们采用type="importmap"的方式引入了threejs开发 的一些核心依赖,"three"是开发的最基本依赖;在Three.js中,"addons" 通常指的是一些附加功能或扩展模块,它们提供了额外的功能,可以用于增强或扩展Three.js的基本功能。
在type="module"中,我们引入了threejs的一些基础依赖,OrbitControls轨道控制器和GLTFLoader模型加载器。
实现模型的加载我们将下载好的模型放在文件根目录
http://www.yanhuangxueyuan.com/threejs/examples/models/gltf/PrimaryIonDrive.glb
基于threejs的基础知识,我们先实现模型的加载与渲染
scripttype="module"
import*asTHREEfrom"three";
import{OrbitControls}from"three/addons/controls/OrbitControls.js";
import{GLTFLoader}from"three/addons/loaders/GLTFLoader.js";
init()
functioninit(){
constcontainer=document.getElementById("container");
//WebGL渲染器
//antialias是否执行抗锯齿。默认为false.
renderer=newTHREE.WebGLRenderer({antialias:true
//设置设备像素比。通常用于避免HiDPI设备上绘图模糊
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth,window.innerHeight);
//设置色调映射这个属性用于在普通计算机显示器或者移动设备屏幕等低动态范围介质上,模拟、逼近高动态范围(HDR)效果。
renderer.toneMapping=THREE.ReinhardToneMapping;
container.appendChild(renderer.domElement);
//创建新的场景对象。
constscene=newTHREE.Scene();
//创建透视相机
camera=newTHREE.PerspectiveCamera(
40,
window.innerWidth/window.innerHeight,
1,
100
camera.position.set(-5,2.5,-3.5);
scene.add(camera);
//创建轨道控制器
constcontrols=newOrbitControls(camera,renderer.domElement);
controls.maxPolarAngle=Math.PI*0.5;
controls.minDistance=3;
controls.maxDistance=8;
//添加了一个环境光
scene.add(newTHREE.AmbientLight(0xcccccc));
//创建了一个点光源
constpointLight=newTHREE.PointLight(0xffffff,100);
camera.add(pointLight);
//模型加载
newGLTFLoader().load("./PrimaryIonDrive.glb",function(gltf){
constmodel=gltf.scene;
scene.add(model);
constclip=gltf.animations[0];
renderer.render(scene,camera);
}
/script
现在,我们的页面中就有了下面的场景
接下来,我们实现模型的发光效果添加。
模型发光效果添加实现模型的发光效果,实际是EffectComposer效果合成器实现的。
官方定义:用于在three.js中实现后期处理效果。该类管理了产生最终视觉效果的后期处理过程链。 后期处理过程根据它们添加/插入的顺序来执行,最后一个过程会被自动渲染到屏幕上。
简单来说,EffectComposer效果合成器只是一个工具,它可以将多种效果集成,进行渲染。我们来看一个伪代码:
import{EffectComposer}from"three/addons/postprocessing/EffectComposer.js";
//创建效果合成器
composer=newEffectComposer(renderer);
composer.addPass(发光效果);
composer.addPass(光晕效果);
composer.addPass(玻璃磨砂效果
//渲染
composer.render();
它的实现过程大致如上述代码。要实现发光效果,我们需要先熟悉三个Pass。
import{RenderPass}from"three/addons/postprocessing/RenderPass.js";
import{UnrealBloomPass}from"three/addons/postprocessing/UnrealBloomPass.js";
import{OutputPass}from"three/addons/postprocessing/OutputPass.js";
RenderPass: 渲染通道是用于传递渲染结果的对象。RenderPass是EffectComposer中的一个通道,用于将场景渲染到纹理上。(固定代码,相当于混合效果的开始)UnrealBloomPass: 这是一个用于实现逼真的辉光效果的通道。它模拟了逼真的辉光,使得场景中的亮部分在渲染后产生耀眼的辉光效果。(不同效果有不同的pass)OutputPass: OutputPass是EffectComposer中的一个通道,用于将最终渲染结果输出到屏幕上。(固定代码,相当于混合效果的结束)现在,我们完整的实现发光效果
scripttype="module"
import*asTHREEfrom"three";
import{OrbitControls}from"three/addons/controls/OrbitControls.js";
import{GLTFLoader}from"three/addons/loaders/GLTFLoader.js";
import{EffectComposer}from"three/addons/postprocessing/EffectComposer.js";
import{RenderPass}from"three/addons/postprocessing/RenderPass.js";
import{UnrealBloomPass}from"three/addons/postprocessing/UnrealBloomPass.js";
import{OutputPass}from"three/addons/postprocessing/OutputPass.js";
letcamera;
letcomposer,renderer;
constparams={
threshold:0,
strength:1,
radius:0,
exposure:1,
init();
functioninit(){
constcontainer=document.getElementById("container");
//WebGL渲染器
//antialias是否执行抗锯齿。默认为false.
renderer=newTHREE.WebGLRenderer({antialias:true
//设置设备像素比。通常用于避免HiDPI设备上绘图模糊
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth,window.innerHeight);
//设置色调映射这个属性用于在普通计算机显示器或者移动设备屏幕等低动态范围介质上,模拟、逼近高动态范围(HDR)效果。
renderer.toneMapping=THREE.ReinhardToneMapping;
container.appendChild(renderer.domElement);
//创建新的场景对象。
constscene=newTHREE.Scene();
//创建透视相机
camera=newTHREE.PerspectiveCamera(
40,
window.innerWidth/window.innerHeight,
1,
100
camera.position.set(-5,2.5,-3.5);
scene.add(camera);
//创建轨道控制器
constcontrols=newOrbitControls(camera,renderer.domElement);
controls.maxPolarAngle=Math.PI*0.5;
controls.minDistance=3;
controls.maxDistance=8;
//添加了一个环境光
scene.add(newTHREE.AmbientLight(0xcccccc));
//创建了一个点光源
constpointLight=newTHREE.PointLight(0xffffff,100);
camera.add(pointLight);
//创建了一个RenderPass对象,用于将场景渲染到纹理上。
constrenderScene=newRenderPass(scene,camera);
//创建了一个UnrealBloomPass对象,用于实现辉光效果。≈
constbloomPass=newUnrealBloomPass(
newTHREE.Vector2(window.innerWidth,window.innerHeight),
1.5,
0.4,
0.85
//设置发光参数,阈值、强度和半径。
bloomPass.threshold=params.threshold;
bloomPass.strength=params.strength;
bloomPass.radius=params.radius;
//创建了一个OutputPass对象,用于将最终渲染结果输出到屏幕上。
constoutputPass=newOutputPass();
//创建了一个EffectComposer对象,并将RenderPass、UnrealBloomPass和OutputPass添加到渲染通道中。
composer=newEffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);
composer.addPass(outputPass);
//模型加载
newGLTFLoader().load("./PrimaryIonDrive.glb",function(gltf){
constmodel=gltf.scene;
scene.add(model);
constclip=gltf.animations[0];
animate();
}
functionanimate(){
requestAnimationFrame(animate);
//通过调用render方法,将场景渲染到屏幕上。
composer.render();
}
/script
现在,我们就实现发光的基本效果了!
实现物体的自动旋转动画现在,我们实现一下物体自身的旋转动画
AnimationMixer是three中的动画合成器,使用AnimationMixer可以解析到模型中的动画数据
//模型加载
newGLTFLoader().load("./PrimaryIonDrive.glb",function(gltf){
constmodel=gltf.scene;
scene.add(model);
//创建了THREE.AnimationMixer对象,用于管理模型的动画。
mixer=newTHREE.AnimationMixer(model);
//从加载的glTF模型文件中获取动画数据。
//这里假设模型文件包含动画数据,通过gltf.animations[0]获取第一个动画片段。
constclip=gltf.animations[0];
//使用mixer.clipAction(clip)创建了一个动画操作(AnimationAction),并立即播放该动画
mixer.clipAction(clip.optimize()).play();
animate();
});
实现动画更新
letclock;
clock=newTHREE.Clock();
functionanimate(){
requestAnimationFrame(animate);
//使用了clock对象的getDelta()方法来获取上一次调用后经过的时间,即时间间隔(delta)。
constdelta=clock.getDelta();
//根据上一次更新以来经过的时间间隔来更新动画。
//这个方法会自动调整动画的播放速度,使得动画看起来更加平滑,不受帧率的影响
mixer.update(delta);
//通过调用render方法,将场景渲染到屏幕上。
composer.render();
}
完整代码
scripttype="module"
import*asTHREEfrom"three";
import{OrbitControls}from"three/addons/controls/OrbitControls.js";
import{GLTFLoader}from"three/addons/loaders/GLTFLoader.js";
import{EffectComposer}from"three/addons/postprocessing/EffectComposer.js";
import{RenderPass}from"three/addons/postprocessing/RenderPass.js";
import{UnrealBloomPass}from"three/addons/postprocessing/UnrealBloomPass.js";
import{OutputPass}from"three/addons/postprocessing/OutputPass.js";
letcamera,stats;
letcomposer,renderer,mixer,clock;
constparams={
threshold:0,
strength:1,
radius:0,
exposure:1,
};
init();
functioninit(){
constcontainer=document.getElementById("container");
clock=newTHREE.Clock();
//WebGL渲染器
//antialias是否执行抗锯齿。默认为false.
renderer=newTHREE.WebGLRenderer({antialias:true
//.....
//模型加载
newGLTFLoader().load("./PrimaryIonDrive.glb",function(gltf){
constmodel=gltf.scene;
scene.add(model);
mixer=newTHREE.AnimationMixer(model);
constclip=gltf.animations[0];
mixer.clipAction(clip.optimize()).play();
animate();
}
functionanimate(){
requestAnimationFrame(animate);
constdelta=clock.getDelta();
mixer.update(delta);
//通过调用render方法,将场景渲染到屏幕上。
composer.render();
}
/script
优化屏幕缩放逻辑init{
//....
window.addEventListener("resize",onWindowResize);
}
functiononWindowResize(){
constwidth=window.innerWidth;
constheight=window.innerHeight;
camera.aspect=width/height;
camera.updateProjectionMatrix();
renderer.setSize(width,height);
composer.setSize(width,height);
}
添加参数调节面板在Three.js中,GUI是一个用于创建用户界面(UI)控件的库。具体来说,GUI库允许你在Three.js应用程序中创建交互式的图形用户界面元素,例如滑块、复选框、按钮等,这些元素可以用于控制场景中的对象、相机、光源等参数。
我们借助这个工具实现如下发光效果调试面板
import{GUI}from"three/addons/libs/lil-gui.module.min.js";
init{
//....
//创建一个GUI实例
constgui=newGUI();
//创建一个名为"bloom"的文件夹,用于容纳调整泛光效果的参数
constbloomFolder=gui.addFolder("bloom");
//在"bloom"文件夹中添加一个滑块控件,用于调整泛光效果的阈值参数
bloomFolder
.add(params,"threshold",0.0,1.0)
.onChange(function(value){
bloomPass.threshold=Number(value);
//在"bloom"文件夹中添加另一个滑块控件,用于调整泛光效果的强度参数
bloomFolder
.add(params,"strength",0.0,3.0)
.onChange(function(value){
bloomPass.strength=Number(value);
//在根容器中添加一个滑块控件,用于调整泛光效果的半径参数
gui
.add(params,"radius",0.0,1.0)
.step(0.01)
.onChange(function(value){
bloomPass.radius=Number(value);
//创建一个名为"tonemapping"的文件夹,用于容纳调整色调映射效果的参数
consttoneMappingFolder=gui.addFolder("tonemapping");
//在"tonemapping"文件夹中添加一个滑块控件,用于调整曝光度参数
toneMappingFolder
.add(params,"exposure",0.1,2)
.onChange(function(value){
renderer.toneMappingExposure=Math.pow(value,4.0);
window.addEventListener("resize",onWindowResize);
}
添加性能监视器importStatsfrom"three/addons/libs/stats.module.js";
init{
stats=newStats();
container.appendChild(stats.dom);
//...
}
functionanimate(){
requestAnimationFrame(animate);
constdelta=clock.getDelta();
mixer.update(delta);
stats.update();
//通过调用render方法,将场景渲染到屏幕上。
composer.render();
}
在Three.js中,Stats是一个性能监视器,用于跟踪帧速率(FPS)、内存使用量和渲染时间等信息。
完整demo代码html
!DOCTYPEhtml
htmllang="en"
head
titlethree.js物体发光效果/title
metacharset="utf-8"/
metaname="viewport"content="width=device-width,user-scalable=no,minimum-scale=1.0,maximum-scale=1.0"/
linktype="text/css"rel="stylesheet"href="./main.css"/
style
#info*{
max-width:650px;
margin-left:auto;
margin-right:auto;
}
/style
/head
body
divid="container"/div
scripttype="importmap"
{
"imports":{
"three":"https://unpkg.com/three@0.163.0/build/three.module.js",
"three/addons/":"https://unpkg.com/three@0.163.0/examples/jsm/"
}
}
/script
scripttype="module"
import*asTHREEfrom"three";
importStatsfrom"three/addons/libs/stats.module.js";
import{GUI}from"three/addons/libs/lil-gui.module.min.js";
import{OrbitControls}from"three/addons/controls/OrbitControls.js";
import{GLTFLoader}from"three/addons/loaders/GLTFLoader.js";
import{EffectComposer}from"three/addons/postprocessing/EffectComposer.js";
import{RenderPass}from"three/addons/postprocessing/RenderPass.js";
import{UnrealBloomPass}from"three/addons/postprocessing/UnrealBloomPass.js";
import{OutputPass}from"three/addons/postprocessing/OutputPass.js";
letcamera,stats;
letcomposer,renderer,mixer,clock;
constparams={
threshold:0,
strength:1,
radius:0,
exposure:1,
init();
functioninit(){
constcontainer=document.getElementById("container");
stats=newStats();
container.appendChild(stats.dom);
clock=newTHREE.Clock();
//WebGL渲染器
//antialias是否执行抗锯齿。默认为false.
renderer=newTHREE.WebGLRenderer({antialias:true
//设置设备像素比。通常用于避免HiDPI设备上绘图模糊
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth,window.innerHeight);
//设置色调映射这个属性用于在普通计算机显示器或者移动设备屏幕等低动态范围介质上,模拟、逼近高动态范围(HDR)效果。
renderer.toneMapping=THREE.ReinhardToneMapping;
container.appendChild(renderer.domElement);
//创建新的场景对象。
constscene=newTHREE.Scene();
//创建透视相机
camera=newTHREE.PerspectiveCamera(
40,
window.innerWidth/window.innerHeight,
1,
100
camera.position.set(-5,2.5,-3.5);
scene.add(camera);
//创建轨道控制器
constcontrols=newOrbitControls(camera,renderer.domElement);
controls.maxPolarAngle=Math.PI*0.5;
controls.minDistance=3;
controls.maxDistance=8;
//添加了一个环境光
scene.add(newTHREE.AmbientLight(0xcccccc));
//创建了一个点光源
constpointLight=newTHREE.PointLight(0xffffff,100);
camera.add(pointLight);
//创建了一个RenderPass对象,用于将场景渲染到纹理上。
constrenderScene=newRenderPass(scene,camera);
//创建了一个UnrealBloomPass对象,用于实现辉光效果。≈
constbloomPass=newUnrealBloomPass(
newTHREE.Vector2(window.innerWidth,window.innerHeight),
1.5,
0.4,
0.85
//设置发光参数,阈值、强度和半径。
bloomPass.threshold=params.threshold;
bloomPass.strength=params.strength;
bloomPass.radius=params.radius;
//创建了一个OutputPass对象,用于将最终渲染结果输出到屏幕上。
constoutputPass=newOutputPass();
//创建了一个EffectComposer对象,并将RenderPass、UnrealBloomPass和OutputPass添加到渲染通道中。
composer=newEffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);
composer.addPass(outputPass);
//模型加载
newGLTFLoader().load("./PrimaryIonDrive.glb",function(gltf){
constmodel=gltf.scene;
scene.add(model);
mixer=newTHREE.AnimationMixer(model);
constclip=gltf.animations[0];
mixer.clipAction(clip.optimize()).play();
animate();
constgui=newGUI();
constbloomFolder=gui.addFolder("bloom");
bloomFolder
.add(params,"threshold",0.0,1.0)
.onChange(function(value){
bloomPass.threshold=Number(value);
bloomFolder
.add(params,"strength",0.0,3.0)
.onChange(function(value){
bloomPass.strength=Number(value);
gui
.add(params,"radius",0.0,1.0)
.step(0.01)
.onChange(function(value){
bloomPass.radius=Number(value);
consttoneMappingFolder=gui.addFolder("tonemapping");
toneMappingFolder
.add(params,"exposure",0.1,2)
.onChange(function(value){
renderer.toneMappingExposure=Math.pow(value,4.0);
window.addEventListener("resize",onWindowResize);
}
functiononWindowResize(){
constwidth=window.innerWidth;
constheight=window.innerHeight;
camera.aspect=width/height;
camera.updateProjectionMatrix();
renderer.setSize(width,height);
composer.setSize(width,height);
}
functionanimate(){
requestAnimationFrame(animate);
constdelta=clock.getDelta();
mixer.update(delta);
stats.update();
//通过调用render方法,将场景渲染到屏幕上。
composer.render();
}
/script
/body
/html
main.css
body{
margin:0;
background-color:#000;
color:#fff;
font-family:Monospace;
font-size:13px;
line-height:24px;
overscroll-behavior:none;
}
a{
color:#ff0;
text-decoration:none;
}
a:hover{
text-decoration:underline;
}
button{
cursor:pointer;
text-transform:uppercase;
}
#info{
position:absolute;
top:0px;
width:100%;
padding:10px;
box-sizing:border-box;
text-align:center;
-moz-user-select:none;
-webkit-user-select:none;
-ms-user-select:none;
user-select:none;
pointer-events:none;
z-index:1;/*TODOSolvethisinHTML*/
}
a,button,input,select{
pointer-events:auto;
}
.lil-gui{
z-index:2!important;/*TODOSolvethisinHTML*/
}
@mediaalland(max-width:640px){
.lil-gui.root{
right:auto;
top:auto;
max-height:50%;
max-width:80%;
bottom:0;
left:0;
}
}
#overlay{
position:absolute;
font-size:16px;
z-index:2;
top:0;
left:0;
width:100%;
height:100%;
display:flex;
align-items:center;
justify-content:center;
flex-direction:column;
background:rgba(0,0,0,0.7);
}
#overlaybutton{
background:transparent;
border:0;
border:1pxsolidrgb(255,255,255);
border-radius:4px;
color:#ffffff;
padding:12px18px;
text-transform:uppercase;
cursor:pointer;
}
#notSupported{
width:50%;
margin:auto;
background-color:#f00;
margin-top:20px;
padding:10px;
}
总结通过本教程,我想现在你对效果合成器一定有了更深入的了解,现在,我们在看看官网的定义:
用于在three.js中实现后期处理效果。该类管理了产生最终视觉效果的后期处理过程链。 后期处理过程根据它们添加/插入的顺序来执行,最后一个过程会被自动渲染到屏幕上
结合代码,我想现在理解其它非常容易
scripttype="module"
import*asTHREEfrom"three";
import{EffectComposer}from"three/addons/postprocessing/EffectComposer.js";
import{RenderPass}from"three/addons/postprocessing/RenderPass.js";
import{UnrealBloomPass}from"three/addons/postprocessing/UnrealBloomPass.js";
import{OutputPass}from"three/addons/postprocessing/OutputPass.js";
functioninit(){
//1【渲染开始】创建了一个RenderPass对象,用于将场景渲染到纹理上。
constrenderScene=newRenderPass(scene,camera);
//2【需要合成的中间特效】创建了一个UnrealBloomPass对象,用于实现辉光效果。≈
constbloomPass=newUnrealBloomPass(
newTHREE.Vector2(window.innerWidth,window.innerHeight),
1.5,
0.4,
0.85
//【特效设置】设置发光参数,阈值、强度和半径。
bloomPass.threshold=params.threshold;
bloomPass.strength=params.strength;
bloomPass.radius=params.radius;
//3【效果输出】创建了一个OutputPass对象,用于将最终渲染结果输出到屏幕上。
constoutputPass=newOutputPass();
//4【特效合并】创建了一个EffectComposer对象,并将RenderPass、UnrealBloomPass和OutputPass添加到渲染通道中。
composer=newEffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(bloomPass);
composer.addPass(outputPass);
}
functionanimate(){
requestAnimationFrame(animate);
//5【渲染特效】通过调用render方法,将场景渲染到屏幕上。
composer.render();
}
/script
关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线