網上使用法線延展法實現物體描邊效果的文章比較多,這裏再也不描述。git
可是這種方法有個缺點:當兩個面的法線夾角差異較大時,兩個面的描邊沒法完美鏈接。以下圖所示:web
這裏使用另外一種方法卷積法實現物體描邊效果,通常機器學習使用該方法比較多。先看效果圖:canvas
使用three.js具體的實現方法以下:機器學習
這三步就能夠實現了,很簡單吧。下面咱們將詳細介紹實現方法,不想看的能夠直接去看完整實現代碼:ide
完整代碼: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.jspost
詳細的實現過程:學習
1. 使用three.js正常繪製場景,獲得下圖,這裏不介紹了。webgl
2. 建立着色器材質,隱藏全部不須要描邊的物體。將須要描邊的物體繪製成白色,其餘地方繪製成黑色。this
隱藏不須要描邊的物體後,將整個場景材質替換。spa
renderScene.overrideMaterial = this.maskMaterial;
着色器材質:
const maskMaterial = new THREE.ShaderMaterial({ vertexShader: MaskVertex, fragmentShader: MaskFragment, depthTest: false });
void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }
void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }
效果圖:
3. 建立着色器材質進行卷積計算,每四個像素顏色求平均值獲得一個像素。描邊物體內部是白色,外部是黑色,物體邊緣處會獲得灰色。灰色就是咱們所需的邊框。
const edgeMaterial = new THREE.ShaderMaterial({ vertexShader: EdgeVertex, fragmentShader: EdgeFragment, uniforms: { maskTexture: { value: this.maskBuffer.texture }, texSize: { value: new THREE.Vector2(width, height) }, color: { value: selectedColor }, thickness: { type: 'f', value: 4 }, transparent: true }, depthTest: false });
varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }
uniform sampler2D maskTexture; uniform vec2 texSize; uniform vec3 color; uniform float thickness; varying vec2 vUv; void main() { vec2 invSize = thickness / texSize; vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize); vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy); vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy); vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw); vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw); float diff1 = (c1.r - c2.r)*0.5; float diff2 = (c3.r - c4.r)*0.5; float d = length(vec2(diff1, diff2)); gl_FragColor = d > 0.0 ? vec4(color, 1.0) : vec4(0.0, 0.0, 0.0, 0.0); }
效果圖:
4. 建立着色器材質,將邊框疊加到原來的圖片上。因爲FXAA比較複雜,這裏使用簡單的疊加方法。
着色器材質:
const copyMaterial = new THREE.ShaderMaterial({ vertexShader: CopyVertexShader, fragmentShader: CopyFragmentShader, uniforms: { tDiffuse: { value: edgeBuffer.texture }, resolution: { value: new THREE.Vector2(1 / width, 1 / height) } }, transparent: true, depthTest: false });
注意,transparent要設置爲true,不然會把原來的圖片覆蓋掉。
CopyVertexShader:
varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }
CopyFragmentShader:
uniform float opacity; uniform sampler2D tDiffuse; varying vec2 vUv; void main() { vec4 texel = texture2D( tDiffuse, vUv ); gl_FragColor = opacity * texel; }
獲得最終效果圖:
參考資料:
1. 描邊實現完整代碼:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.js
2. three.js後期處理描邊:https://threejs.org/examples/#webgl_postprocessing_outline
3. 卷積工做原理:https://www.zhihu.com/question/39022858?sort=created
4. 法線延展法實現物體描邊:http://www.javashuo.com/article/p-smycaygc-hp.html