lume
Version:
198 lines (141 loc) • 5.53 kB
text/typescript
import {Scene} from 'three/src/scenes/Scene.js'
import {OrthographicCamera} from 'three/src/cameras/OrthographicCamera.js'
import {WebGLRenderTarget} from 'three/src/renderers/WebGLRenderTarget.js'
import {Mesh, PlaneGeometry, Vector2} from 'three/src/Three.js'
import {TriangleBlurShader} from 'three/examples/jsm/shaders/TriangleBlurShader.js'
import {ShaderMaterial} from 'three/src/materials/ShaderMaterial.js'
import {HorizontalBlurShader} from 'three/examples/jsm/shaders/HorizontalBlurShader.js'
import {VerticalBlurShader} from 'three/examples/jsm/shaders/VerticalBlurShader.js'
import {Scene as ThreeScene} from 'three/src/scenes/Scene.js'
import {UniformsUtils} from 'three/src/renderers/shaders/UniformsUtils.js'
import type {IUniform, Texture} from 'three/src/Three.js'
import type {WebGLRenderer} from 'three/src/renderers/WebGLRenderer.js'
export function triangleBlurTexture(
renderer: WebGLRenderer,
texture: Texture,
blur = 0.1,
passes = 1,
iterations = 10,
) {
const width = texture.image.width
const height = texture.image.height
// renderer = new WebGLRenderer()
const cameraRTT = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
const sceneRTT = new Scene()
// render targets
const renderTarget1 = new WebGLRenderTarget(width, height)
const renderTarget2 = new WebGLRenderTarget(width, height)
// shader materials
const shader = makeTriangleBlurShader(iterations)
const blurMaterial = new ShaderMaterial({
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
uniforms: UniformsUtils.clone(shader.uniforms),
})
blurMaterial.uniforms.delta!.value = new Vector2(1, 1)
// fullscreen quad
const planeGeometry = new PlaneGeometry(2, 2)
const fullScreenQuad = new Mesh(planeGeometry, blurMaterial)
sceneRTT.add(fullScreenQuad)
// passes
let lastTexture = texture
while (passes--) {
// vertical pass
blurMaterial.uniforms.blurTexture!.value = lastTexture
blurMaterial.uniforms.delta!.value = new Vector2(blur, 0)
renderer.setRenderTarget(renderTarget1)
renderer.render(sceneRTT, cameraRTT)
renderer.setRenderTarget(null)
lastTexture = renderTarget1.texture
// horizontal pass
blurMaterial.uniforms.blurTexture!.value = lastTexture
blurMaterial.uniforms.delta!.value = new Vector2(0, blur)
renderer.setRenderTarget(renderTarget2)
renderer.render(sceneRTT, cameraRTT)
renderer.setRenderTarget(null)
lastTexture = renderTarget2.texture
}
//
return lastTexture
}
export function makeTriangleBlurShader(iterations = 10) {
// Remove texture, because texture is a reserved word in WebGL 2
const {texture, ...uniforms} = TriangleBlurShader.uniforms
const TriangleBlurShader2 = {
...TriangleBlurShader,
name: 'TriangleBlurShader2',
uniforms: {
...uniforms,
// Replace texture with blurTexture for WebGL 2
blurTexture: {value: null} as IUniform<Texture | null>,
},
}
// Replace texture with blurTexture for WebGL 2
TriangleBlurShader2.fragmentShader = TriangleBlurShader2.fragmentShader.replace(
'uniform sampler2D texture;',
'uniform sampler2D blurTexture;',
)
TriangleBlurShader2.fragmentShader = TriangleBlurShader2.fragmentShader.replace(
'texture2D( texture',
'texture2D( blurTexture',
)
// Make iterations configurable.
TriangleBlurShader2.fragmentShader = TriangleBlurShader2.fragmentShader.replace(
'#define ITERATIONS 10.0',
'#define ITERATIONS ' + iterations + '.0',
)
return TriangleBlurShader2
}
/**
* Apply a simple vertical/horizontal Gaussian blur to a texture. The simple
* design can make the blurred image seem like it was previously pixelated.
* `triangleBlurTexture` offers a better blur but may also cost more.
*
* Adapted from https://discourse.threejs.org/t/39433
*/
export function verticalHorizontalGaussianBlurTexture(renderer: WebGLRenderer, texture: Texture, passes = 1) {
const width = texture.image.width
const height = texture.image.height
const cameraRTT = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
const sceneRTT = new ThreeScene()
// render targets
const renderTarget1 = new WebGLRenderTarget(width, height)
const renderTarget2 = new WebGLRenderTarget(width, height)
// shader materials
const hBlurMaterial = new ShaderMaterial({
vertexShader: HorizontalBlurShader.vertexShader,
fragmentShader: HorizontalBlurShader.fragmentShader,
uniforms: UniformsUtils.clone(HorizontalBlurShader.uniforms),
})
hBlurMaterial.uniforms.h!.value = 1 / width
const vBlurMaterial = new ShaderMaterial({
vertexShader: VerticalBlurShader.vertexShader,
fragmentShader: VerticalBlurShader.fragmentShader,
uniforms: UniformsUtils.clone(VerticalBlurShader.uniforms),
})
vBlurMaterial.uniforms.v!.value = 1 / height
// fullscreen quad
const planeGeometry = new PlaneGeometry(2, 2)
const fullScreenQuad = new Mesh(planeGeometry, hBlurMaterial)
sceneRTT.add(fullScreenQuad)
// passes
let lastTexture = texture
while (passes--) {
// horizontal pass
fullScreenQuad.material = hBlurMaterial
hBlurMaterial.uniforms.tDiffuse!.value = lastTexture
renderer.setRenderTarget(renderTarget1)
renderer.render(sceneRTT, cameraRTT)
renderer.setRenderTarget(null)
lastTexture = renderTarget1.texture
// vertical pass
fullScreenQuad.material = vBlurMaterial
vBlurMaterial.uniforms.tDiffuse!.value = lastTexture
renderer.setRenderTarget(renderTarget2)
renderer.render(sceneRTT, cameraRTT)
renderer.setRenderTarget(null)
lastTexture = renderTarget2.texture
}
//
return renderTarget2.texture
}