1.设置后期处理
设置Three.js库为后期处理做准备,我们需要通过以下步骤对当前的配置进行修改:
1)创建一个EffectComposer(效果组合器)对象,然后在该对象上添加后期处理通道。
2)配置该对象,使它可以渲染我们的场景,并应用额外的后期处理步骤。
3)在render循环中,使用EffectComposer渲染场景、应用通道,并输出结果。
要使用后期处理,需要引入一些javaSscript文件。这些文件可以在Three.js发布包里找到。路径是examples/js/postprocessing和example/js/shaders。至少包含下面的文件:
<script type="text/javascript" src="../libs/postprocessing/ShaderPass.js"></script>
<script type="text/javascript" src="../libs/shaders/CopyShader.js"></script>
<script type="text/javascript" src="../libs/postprocessing/EffectComposer.js"></script>
<script type="text/javascript" src="../libs/postprocessing/MaskPass.js"></script>
<script type="text/javascript" src="../libs/postprocessing/FilmPass.js"></script>
<script type="text/javascript" src="../libs/shaders/FilmShader.js"></script>
<script type="text/javascript" src="../libs/postprocessing/RenderPass.js"></script>
首先我们创建一个EffectComposer对象,你可以在这个对象的构造函数里出入WebGL-Renderer,如下所示:
var composer = new THREE.EffectComposer(webGLRenderer);
接下来我们要在这个组合器中添加各种通道。第一个要加入的通道是RenderPass。这个通道会渲染场景,但不会讲渲染结果输出到屏幕上。
var renderPass = new THREE.RenderPass(scene, camera);
...
var composer = new THREE.EffectComposer(webGLRenderer);
composer.addPass(renderPass);
接下来我们要添加一个可以将结果输出到屏幕上的通道。这里使用FilmPass,我们想创建该对象,然后添加到效果组合器中。代码如下:
var effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false);
effectFilm.renderToScreen = true;
var composer = new THREE.EffectComposer(webGLRenderer);
composer.addPass(effectFilm);
正如你所看到的,我们创建了一个FilmPass对象,并将它的renderToScreen属性设置为true。最后,还需要修渲染循环:
function render(){
stats.update();
var delta = clock.getDelta();
orbitControl.update(delta);
sphere.rotation.y += 0.002;
requestAnimationFrame(render);
composer.render(delta);
}
这个代码里我们移出了“webGLRenderer.render(scene, camera);”,用“composer.render(delta)”代替。这将调用EffectComposer的render()函数。由于我们已经将FilmPass的renderToScreen属性设置为true,所以FilmPass的结果将会输出到屏幕上。
2.后期处理通道
Three.js库提供了几个后期处理通道,你可以直接将其添加到EffectComposer对象。下表是这些通道的概览。
通道/描述:
BloomPass/该通道会使得明亮区域渗入较暗的区域。模拟相机找到过多亮点的情形
DotScreenPass/将一层黑点贴到代表原始图片的屏幕上
FilmPass/通过扫描线和失真模拟电视屏幕
MaskPass/在当前图片上贴一层掩膜,后续通道只会影响被贴的区域
RenderPass/该通道在指定的场景和相机的基础上渲染出一个新场景
ShaderPass/使用该通道你可以传入一个自定义的着色器,用来生成高级的、自定义的后期处理通道
TexturePass/该通道可以将效果组合器的当前状态保存为一个纹理,然后可以在其他EffectComposer对象中将该纹理作为输入参数
下面的例子使用了以上通道的中的BloomPass、DotScreenPass、FilmPass、RenderPass、ShaderPass、TexturePass。首先看下运行效果:
上图标左上角运用BloomPass、右上角运用FilmPass、左下角运用DotScreenPass、右下角运用原始渲染结果。渲染处理代码如下:
var renderPass = new THREE.RenderPass(scene, camera); //保存渲染结果,但不会输出到屏幕
var effectCopy = new THREE.ShaderPass(THREE.CopyShader); //传入了CopyShader着色器,用于拷贝渲染结果
effectCopy.renderToScreen = true; //设置输出到屏幕上
var bloomPass = new THREE.BloomPass(3, 25, 5.0, 256); //BloomPass通道效果
var effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false); //FilmPass通道效果
effectFilm.renderToScreen = true; //设置输出到屏幕上
var dotScreenPass = new THREE.DotScreenPass(); // DotScrrenPass通道效果
//渲染目标
var composer = new THREE.EffectComposer(webGLRenderer);
composer.addPass(renderPass);
composer.addPass(effectCopy);
var renderScene = new THREE.TexturePass(composer.renderTarget2);
//左下角
var composer1 = new THREE.EffectComposer(webGLRenderer);
composer1.addPass(renderScene);
composer1.addPass(dotScreenPass);
composer1.addPass(effectCopy);
//右下角
var composer2 = new THREE.EffectComposer(webGLRenderer);
composer2.addPass(renderScene);
composer2.addPass(effectCopy);
//左上角
var composer3 = new THREE.EffectComposer(webGLRenderer);
composer3.addPass(renderScene);
composer3.addPass(bloomPass);
composer3.addPass(effectCopy);
//右上角
var composer4 = new THREE.EffectComposer(webGLRenderer);
composer4.addPass(renderScene);
composer4.addPass(effectFilm);
代码原理比较简单,composer的作用是渲染最原始的效果,传入了renderPass和ShaderPass,renderPass运来创建一个新场景,ShaderPass用来输出结果到屏幕。接下来创建了一个类型为TexturePass的通道对象renderScene。可以将当前状态保存一份作为纹理。供后面的几个compoer使用。
composer1首先使用renderScene创建新场景,然后添加dotScreenPass通道,最后使用effectCopy输出渲染。composer2、composer3、composer4相似。设置了处理通道后,在每次循环渲染时还得分别调用每个composer的render函数重新渲染:
function render() {
stats.update();
//sphere.rotation.y=step+=0.01;
var delta = clock.getDelta();
orbitControls.update(delta);
sphere.rotation.y += 0.002;
// render using requestAnimationFrame
requestAnimationFrame(render);
webGLRenderer.autoClear = false;
webGLRenderer.clear();
webGLRenderer.setViewport(0, 0, 2 * halfWidth, 2 * halfHeight);
composer.render(delta);
webGLRenderer.setViewport(0, 0, halfWidth, halfHeight);
composer1.render(delta);
webGLRenderer.setViewport(halfWidth, 0, halfWidth, halfHeight);
composer2.render(delta);
webGLRenderer.setViewport(0, halfHeight, halfWidth, halfHeight);
composer3.render(delta);
webGLRenderer.setViewport(halfWidth, halfHeight, halfWidth, halfHeight);
composer4.render(delta);
}
3.各种通道属性
FilmPass用来创建类似电视的效果。通道属性的属性可通过构造函数传入,也可以直接修改uiniforms上对应的属性。属性说明如下:
属性/描述
noiseIntensity/通过该属性可以控制屏幕的颗粒程度
scanlineIntensity/FilmPass会在屏幕上添加一些扫描线。通过该属性,可以指定扫描线的显著程度
scanlineCount/该属性可以控制显示出来的扫描线数量
grayscale/如果设置为true,输出结果将会转换为灰度图
BoomPass用来在场景中添加泛光效果。属性可通过构造函数传入,也可直接修改uniforms属性。属性说明如下:
属性/描述
Strength/该属性定义的是泛光效果强度。值越高,明亮区域越明亮。而且渗入较暗区域的也就越多
kernelSize/该属性控制的是泛光效果的偏移量
sigma/可以控制泛光的锐利程度。值越高,泛光越模糊
resolution/泛光效果的解析图。值太低,那么结果的方块会比较严重
DotSreenPass用来将场景输出成点集。属性如下:
center/通过center属性,可以微调点的偏移量
angle/通过angle,可以更改对齐方式
scale/该属性设置所有点的大小。scale越小,则点越大
4.使用掩膜的高级效果组合器
Three.js库具有在特定区域应用通道的能力。我们采取如下步骤实现一个特定区域效果。例如下图的地图和火星。我们想在火星上应用一个彩色效果、在地球上应用褐色效果。我们采取如下步骤:
1)创建一个座位背景图的场景。
2)创建一个场景,里边有一个看上去像地球的球体。
3)创建一个场景,里边有一个看上去像火星的球体。
4)创建一个EffectComposer对象,将这三个场景渲染到一个图片里。
5)在渲染成火星的球体上应用一个彩色效果。
6)在渲染成地球的球体上应用褐色效果。
下面是初始化的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - postprocessing - unreal bloom</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #fff;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px;
width: 100%;
padding: 5px;
}
#info p {
max-width: 600px;
margin-left: auto;
margin-right: auto;
padding: 0 2em;
}
a {
color: #2983ff;
}
</style>
</head>
<body>
<div id="container"></div>
<!-- <div id="info">
<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Bloom pass by <a href="http://eduperiment.com" target="_blank" rel="noopener">Prashant Sharma</a> and <a href="https://clara.io" target="_blank" rel="noopener">Ben Houston</a>
<p>
This Bloom Pass is inspired by the bloom pass of the Unreal Engine. It creates a mip map chain of bloom textures and blur them
with different radii. Because of the weigted combination of mips, and since larger blurs are done on higher mips, this bloom
is better in quality and performance.
</p>
Model: <a href="https://blog.sketchfab.com/art-spotlight-primary-ion-drive/" target="_blank" rel="noopener">Primary Ion Drive</a> by
<a href="http://mjmurdock.com/" target="_blank" rel="noopener">Mike Murdock</a>, CC Attribution.
</div> -->
<script src="lib/three.js"></script>
<script src="lib/stats.min.js"></script>
<script src="lib/dat.gui.min.js"></script>
<script src="lib/OrbitControls.js"></script>
<script src="lib/GLTFLoader.js"></script>
<script src="lib/OBJLoader.js"></script>
<script src="lib/EffectComposer.js"></script>
<script src="lib/RenderPass.js"></script>
<script src="lib/ShaderPass.js"></script>
<script src="lib/CopyShader.js"></script>
<script src="lib/LuminosityHighPassShader.js"></script>
<script src="lib/UnrealBloomPass.js"></script>
<script src="lib/FilmPass.js"></script>
<script src="lib/FilmShader.js"></script>
<script src="lib/DotScreenPass.js"></script>
<script src="lib/TexturePass.js"></script>
<script src="lib/ShaderPass.js"></script>
<script src="lib/MaskPass.js"></script>
<script src="lib/SepiaShader.js"></script>
<script src="lib/ColorifyShader.js"></script>
<script src="lib/BleachBypassShader.js"></script>
<script>
var stats = new Stats();
document.body.appendChild(stats.dom);
var sceneEarth = new THREE.Scene();
var sceneMars = new THREE.Scene();
var sceneBG = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
var cameraBG = new THREE.OrthographicCamera(-window.innerWidth, window.innerWidth, window.innerHeight, -window.innerHeight, -10000, 10000);
cameraBG.position.z = 50;
var webGLRenderer = new THREE.WebGLRenderer({alpha: true,antialias: true });
webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
var sphere = new THREE.Mesh(new THREE.SphereGeometry(10, 40, 40),new THREE.MeshBasicMaterial( { map:THREE.ImageUtils.loadTexture("./textures/diqiu.jpg")}) );
sphere.position.x = -10;
var sphere2 = new THREE.Mesh(new THREE.SphereGeometry(5, 40, 40),new THREE.MeshBasicMaterial( { map:THREE.ImageUtils.loadTexture("./textures/yueqiu.jpg")} ));
sphere2.position.x = 10;
sceneEarth.add(sphere);
sceneMars.add(sphere2);
camera.position.set(-10, 15, 25);
camera.lookAt(new THREE.Vector3(0, 0, 0));
var orbitControls = new THREE.OrbitControls(camera);
orbitControls.autoRotate = false;
var clock = new THREE.Clock();
var ambi = new THREE.AmbientLight(0x181818);
var ambi2 = new THREE.AmbientLight(0x181818);
sceneEarth.add(ambi);
sceneMars.add(ambi2);
var spotLight = new THREE.DirectionalLight(0xffffff);
spotLight.position.set(550, 100, 550);
spotLight.intensity = 0.6;
var spotLight2 = new THREE.DirectionalLight(0xffffff);
spotLight2.position.set(550, 100, 550);
spotLight2.intensity = 0.6;
sceneEarth.add(spotLight);
sceneMars.add(spotLight2);
var materialColor = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture("./textures/sky.jpg"),
depthWrite: false
});
var bgPlane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), materialColor);
bgPlane.position.z = -100;
bgPlane.scale.set(window.innerWidth * 2, window.innerHeight * 2, 1);
sceneBG.add(bgPlane);
// add the output of the renderer to the html element
document.body.appendChild(webGLRenderer.domElement);
var bgPass = new THREE.RenderPass(sceneBG, cameraBG);
var renderPass = new THREE.RenderPass(sceneEarth, camera);
renderPass.clear = false;
var renderPass2 = new THREE.RenderPass(sceneMars, camera);
renderPass2.clear = false;
var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
effectCopy.renderToScreen = true;
var clearMash = new THREE.ClearMaskPass();
var earthMask = new THREE.MaskPass(sceneEarth, camera);
var marsMask = new THREE.MaskPass(sceneMars, camera);
//地球的褐色效果
var effectSepia = new THREE.ShaderPass(THREE.SepiaShader);
effectSepia.uniforms["amount"].value = 0.8;
//月球的彩色效果
var effectColorify = new THREE.ShaderPass(THREE.ColorifyShader);
effectColorify.uniforms["color"].value.setRGB(0.5, 0.5, 1.0);
console.log(effectColorify);
var Bleach = new THREE.ShaderPass(THREE.BleachBypassShader); // 漂白效果 覆盖银一样
Bleach.uniforms['opacity'].value = 0.6;
var composer = new THREE.EffectComposer(webGLRenderer);
composer.renderTarget1.stencilBuffer = true;
composer.renderTarget2.stencilBuffer = true;
composer.addPass(bgPass); // 添加背景渲染新场景通道
composer.addPass(renderPass); // 添加地球渲染的新场景通道
composer.addPass(renderPass2); // 添加月球渲染的新场景通道
composer.addPass(marsMask); // 添加月球掩膜通道,以后所有的通道效果都只对月球有效,直到clearMask通道
composer.addPass(effectColorify); // 添加颜色着色器通道
composer.addPass(clearMash); //清理掩膜通道
composer.addPass(earthMask); //添加地图掩膜通道,之后的所有通道效果都只对月球有效,直到clearMash通道
//composer.addPass(effectSepia); // 添加一种自定义着色器通道
composer.addPass(Bleach);
composer.addPass(clearMash); // 清理掩膜通道
composer.addPass(effectCopy);
render();
function render() {
webGLRenderer.autoClear = false;
stats.update();
var delta = clock.getDelta();
orbitControls.update(delta);
sphere.rotation.y += 0.002;
sphere2.rotation.y += 0.002;
requestAnimationFrame(render);
composer.render(delta);
}
</script>
</body>
</html>
效果截图:
5.用ShaderPass定制效果
通过ShaderPass,我们可以传递一个自定义的着色器,将大量额外的效果应用到场景中。Three.js扩展的着色器主要分为简单着色器、模糊着色器、高级效果着色器。
简单着色器列表:
MirrorShader/该着色器可以为部分屏幕创建镜面效果
HueStaturationShader/该着色器可以改变颜色的色调和饱和度
VignetteShader/该着色器可以添加晕映效果。该效果可以在图片中央的周围显示黑色的边框
ColorCorrectionShader/通过这个着色器,你可以调整颜色的分布
RGBShiftSader/该着色器可以将构成颜色的红、绿、蓝分开
BrightnessContrasShader/该着色器可以更改图片的亮度和对比度
ColorifyShader/可以在屏幕上蒙上一层颜色
SepiaShader/可以在屏幕上创建出类似乌贼墨的效果
模糊效果着色器:
HorizontalBlurShader和VerticalBlurShader/这两个着色器在场景中应用模糊效果
HorizontalTiltShiftShader和VerticalTiltShiftShader/这两个着色器可以创建出移轴效果。在移轴效果中只有部分图片显示得比较锐利,从而创建出一个看上去像是微缩景观的场景
TriangleBlurShader/该着色器使用基于三角形的方法,咱场景中应用模糊效果 高级效果的着色器: BleachBypassShader/该着色器可以创建一种漂白效果。在该效果下,图片上像素是镀了一层银
EdgeShader/该着色器可以探测图片中的锐利边界,并突出显示这些边界
FXAAShader/该着色器可以在后期处理阶段应用锯齿效果。如果在渲染抗锯齿影响效率,那么久可以使用该着色器
FocusShader/这是一个简单的着色器,其结果是中央区域渲染得比较锐利,但周围比较模糊
6.定制自定义着色器
为Three.js库创建自定义的着色器,需要实现两个组件:vertexShader和fragmentShader。组件vertexShader可以用来调整每个顶点的位置,组件fragmentShader可以从来决定每个像素的颜色。对于后期处理着色器来说,我们只要实现fragmentShader即可,然后使用Three.js提供的额、默认的vertexShader。
我们以定制一个灰度着色器为例。首先要创建一个js文件,存放着色器源代码,这里我们命名一个custom-shader.js文件。内容如下:
THREE.CustomGrayScaleShader = {
uniforms: {
"tDiffuse": {type: "t", value: null},
"rPower": {type: "f", value: 0.2126},
"gPower": {type: "f", value: 0.7152},
"bPower": {type: "f", value: 0.0722}
},
vertexShader: [
"varying vec2 vUv;",
"void main(){",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"}"
].join("\n"),
fragmentShader: [
"uniform float rPower;",
"uniform float gPower;",
"uniform float bPower;",
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main(){",
"vec4 texel = texture2D(tDiffuse, vUv);",
"float gray = texel.r * rPower + texel.g * gPower + texel.b * bPower;",
"gl_FragColor = vec4(vec3(gray), texel.a);",
"}"
].join("\n")
}
我们为Threee.js创建了一个叫做CustomGrayScaleShader的自定义着色器该对象包含uniforms、vertexShader和fragmentShader三个属性。uniforms定义了通过javascript外部传入的变量,vertexShader定义了顶点着色器,fragmentShader定义了片元着色器代码。
这里需要注意的是顶点着色器中的uv变量,是着色器代码的内部变量,表示纹理上的texel(纹理上的像素),铜鼓varying vec2 vUv变量传递给片元着色器。着色器代码定义好后,我们就可以通过以下形式在javascript代码中使用了:
var renderPass = new THREE.RenderPass(scene, camera);
var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
effectCopy.renderToScreen = true;
var shaderPass = new THREE.ShaderPass(THREE.CustomGrayScaleShader);
shaderPass.enabled = false;
var bitPass = new THREE.ShaderPass(THREE.CustomBitShader);
bitPass.enabled = false;
var composer = new THREE.EffectComposer(webGLRenderer);
composer.addPass(renderPass);
composer.addPass(shaderPass);
composer.addPass(bitPass);
composer.addPass(effectCopy);
代码中创建了两个着色器通道,分别适合shaderPass和bitPass。shaderPass正好使用的是我们创建的自定义着色器THREE.CustomGrayScaleShader。