如果你還不知道如何在three.js中添加后期渲染通道,請先看一下官方的一個最簡單的demo : github。
正如demo中所示的那樣,我們的掃光效果,也是一個自定義的ShaderPass。
所以,我們得寫一個shader來實現想要的效果,這里介紹一下實現的思路。
1.在自定義通道上,先獲取場景原本的uv,然后原樣輸出;
2.在fs里,降低gl_FragColor的亮度(乘以小於1.0的數,這里取0.3),這樣,畫面立馬暗了下來;
3.畫一條直線 y = -x;在這條直線上的部分顏色亮度為1.0,實際上光束是有寬度的,所以我們取一下abs(dy) < 0.1的時候亮度為1.0,那么光束的寬度為0.2 * 根號2;
4.光束內的abs(dy),范圍是0~0.1,那么可以用smoothstep函數來做一個平滑插值處理,使得光束的亮度由中間向兩邊平滑過渡;
5.添加uniform參數time實現動畫,此時直線函數變為y = -x + time,隨着time值的變化,自然y值也會變化,最終的效果就是光束左右移動,如動圖所示;
下面上shader代碼:
/** * author:桔子桑 * time:2019.10.13 */ THREE.SweepingLightShader = { uniforms: { "tDiffuse": {type: "t", value: null}, "time":{type: "f", value: -1.0} }, vertexShader:` varying vec2 vUv; varying vec3 iPosition; void main(){ vUv = uv; iPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader:` uniform float time; uniform sampler2D tDiffuse; varying vec2 vUv; varying vec3 iPosition; void main(){ vec4 texel = texture2D(tDiffuse, vUv); float x = iPosition.x; float lighty = -x*1.2 + time; float alpha = abs(iPosition.y - lighty); if(alpha < 0.1){ float a = 1.0 - alpha / 0.1; float enda = smoothstep(0.0,1.0,a) + 0.3; gl_FragColor = texel * enda; }else{ gl_FragColor = texel * 0.3; } } ` };
為了實現動畫,我們需要在每次animate的時候,適當地增減這個time參數的值:
//掃光效果 SweepingLightShader = new THREE.ShaderPass( THREE.SweepingLightShader ); composer.addPass(SweepingLightShader);
var type = 'add'; function animate() { requestAnimationFrame( animate ); var time = SweepingLightShader.uniforms[ 'time' ].value; if(time > 1.0){ type = 'reduce' }else if(time < -1.0){ type = 'add'; } if(type =='add'){ SweepingLightShader.uniforms[ 'time' ].value += 0.01; }else{ SweepingLightShader.uniforms[ 'time' ].value -= 0.01; } composer.render(); }