效果
引入库
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader'
shader
let DarkMaskShader = {
uniforms: {
tDiffuse: {
value: null
},
maskColor: {
value: new THREE.Color(0x000000)
},
maskAlpha: {
value: 1.0
},
markRadius: {
value: 0.15
},
smoothSize: {
value: 0.5
}
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader: `
uniform float maskAlpha;
uniform vec3 maskColor;
uniform float markRadius;
uniform float smoothSize;
uniform sampler2D tDiffuse;
varying vec2 vUv;
float sdfCircle(vec2 coord, vec2 center, float radius) {
vec2 offset = coord - center;
return sqrt((offset.x * offset.x) + (offset.y * offset.y)) - radius;
},
void main() {
vec4 texel = texture2D( tDiffuse, vUv );
float sdfValue = sdfCircle(vUv, vec2(0.5, 0.5), markRadius);
if (sdfValue < 0.0){
gl_FragColor = texel;
}else{
float a = smoothstep(0.0, smoothSize, sdfValue);
gl_FragColor = mix(texel, vec4(maskColor, maskAlpha), a);
}
}
`
}
窗口监测里添加
function onWindowResize() {
camera.aspect = document.getElementById('threecanvas').offsetWidth / document.getElementById('threecanvas').offsetHeight
camera.updateProjectionMatrix()
/*********************************************************************/
// 暗角范围
composer.setSize(document.getElementById('threecanvas').offsetWidth, document.getElementById('threecanvas').offsetHeight) // 暗角
renderer.setSize(document.getElementById('threecanvas').offsetWidth, document.getElementById('threecanvas').offsetHeight)
}
添加效果合成器
function initEffectComposer() {
// 处理模型闪烁问题【优化展示网格闪烁】
const parameters = { format: THREE.RGBFormat }
const size = renderer.getDrawingBufferSize(new THREE.Vector2())
const renderTarget = new THREE.WebGLMultisampleRenderTarget(size.width, size.height, parameters)
/******************************************边缘暗角****************************************/
renderScene = new RenderPass(scene, camera)
renderScene.clear = true
const effectDarkMask = new ShaderPass(DarkMaskShader)
composeraj = new EffectComposer(renderer, renderTarget)
composeraj.addPass(renderScene)
composeraj.addPass(effectDarkMask)// 开启圆形暗角,需要处理模型边缘失真
}
处理模型边缘锯齿
添加特效合成器后,会出现模型边缘锯齿
推荐使用 FXAA 处理
具体请到:
// 效果合成器,shader渲染使用
function initEffectComposer() {
// 处理模型闪烁问题【优化展示网格闪烁】
const parameters = { format: THREE.RGBFormat }
const size = renderer.getDrawingBufferSize(new THREE.Vector2())
const renderTarget = new THREE.WebGLMultisampleRenderTarget(size.width, size.height, parameters)
/******************************************边缘暗角****************************************/
renderScene = new RenderPass(scene, camera)
renderScene.clear = true
const effectDarkMask = new ShaderPass(DarkMaskShader)
composeraj = new EffectComposer(renderer, renderTarget)
composeraj.addPass(renderScene)
composeraj.addPass(effectDarkMask)// 开启圆形暗角,需要处理模型边缘失真
/*********************************************特效锯齿处理****************************************/
// 增加fxaashader处理模型边缘锯齿,开启EffectComposer后才能生效【开启特效合成之后的展示优化】
var FXAAShaderPass = new ShaderPass(FXAAShader)
const pixelRatio = renderer.getPixelRatio()
const width = document.getElementById('threecanvas').offsetWidth
const height = document.getElementById('threecanvas').offsetHeight
FXAAShaderPass.uniforms['resolution'].value.set(1 / (width * pixelRatio), 1 / (height * pixelRatio))
FXAAShaderPass.renderToScreen = true
composeraj.addPass(FXAAShaderPass)
}
渲染
渲染部分,如果添加了特效合成器,则要把传统的渲染方式替换为特效的通道
// 渲染
function render() {
update()
requestAnimationFrame(render)// 执行一个动画.并在动画执行后重新渲染
// 传统渲染器,如果使用shader着色器,则注掉这个,增加效果合成器,增加通道渲染 // renderer.render(scene, camera)
composeraj.render()
}
后续
这一部分特效也是参考了大部分其他知乎、博客、简书等等…
话说,找一个特效真难,搜到的好的特效后边都是两个字,买断 &T~T&,都拿去作商业用途了,如果一个小白从头开始,跟着three官网做一些基础的三维物体或基础场景还是可以的,那些进阶的东西,特效、动效什么的,真的是难找的要死,现在学个东西,自学还真是难啊,高级一点的接触都接触不到,像我这样的,自己扣一扣底层代码,然后往上一步一个坑真的难的一批,各种搜索,找一找成型的源码,自己对照着底层,硬理解,难搞。 想速成的话…多花钱,想按部就班的搞一搞,吃的透就要从简到繁,从底层到显示…一步一坑,从three进去,到webgl,再看OpenGL,对照底层封装three上的缺陷,然后完善,形成一套自己的基础引擎。