http://demo.bkcore.com/threejs/webgl_tron_glow.html
glowcomposer = new THREE.EffectComposer( renderer, renderTarget );
glowcomposer.addPass( renderModelGlow ); // RenderPass
glowcomposer.addPass( hblur ); // ShaderPass
glowcomposer.addPass( vblur ); // ShaderPass
glowcomposer.addPass( effectSave ); // SavePass
glowcomposer.addPass( renderModel ); // RenderPass
glowcomposer.addPass( finalPass ); // ShaderPass
๋์ผํ ๋ฌผ์ฒด / ์นด๋ฉ๋ผ / ์กฐ๋ช ์ผ๋ก ๋ ๊ฐ์ ์ฅ๋ฉด์ด ์์ต๋๋ค. ํ๋๋ ํ์ฐ ํ ์ค์ฒ (http://demo.bkcore.com/threejs/webgl_tron.html)์ ๊ธ๋ก์ฐ ํ ์ค์ฒ (๋น๋๋ ์์ญ์ ๊ฒ์ ๋ฐํ์ ํฐ์ ์)๊ฐ์๋ ๊ฒ์ ๋๋ค.
๋ด ๋ ๋๋ง ํ์ดํ ๋ผ์ธ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋จผ์ ๊ธ๋ก์ฐ ์ฌ์ ๋ ๋๋งํ๊ณ H / V ๋ธ๋ฌ๋ฅผ ์ ์ฉํ ๋ค์ SavePass๋ฅผ ์ฌ์ฉํ์ฌ ์ถ๋ ฅ์ frameBuffer์ ์ ์ฅํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ ํ์ฐ ์ฅ๋ฉด์ ๋ ๋๋งํ๊ณ ์ถ์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก ๊ธ๋ก์ฐ ํ๋ ์ ๋ฒํผ์ ํผํฉํฉ๋๋ค.
๋ด ๊ธ๋ก์ฐ sampler2D๋ ๋ด finalPass ์ ฐ์ด๋๋ก ์ ์ ๋ฌ๋์ง๋ง ๋ฐ๋ชจ์์ ๋ณผ ์ ์๋ฏ์ด ํ์ฐ ์ฅ๋ฉด์ ๋ํ ๋ ๋ฒ์งธ RenderPass ํธ์ถ์ด ์ ๋๋ก ์ํ๋์ง ์์ต๋๋ค. ๊ฒ์ ์ ๋ง ์ถ๋ ฅํฉ๋๋ค.
๋จ์ผ ๋ ๋ ํจ์ค์์ ํ์ฐ ์ฅ๋ฉด์ ๋ ๋๋งํ๋ ค๊ณ ํ๋๋ฐ ์๋ํฉ๋๋ค. ์๋ํ์ง ์๋ ๊ฒ์ ๋ ๋ฒ์งธ RenderPass๋ก ์ฌ์ฉํ ๋์ ๋๋ค.
์ด๋ค ์์ด๋์ด?
๊ฐ์ฌํฉ๋๋ค.
ํฐ๋ณด D.
๊น์ด ๋ฒํผ๊ฐ ํ๊ดด ๋์๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. # 1017์ ์ฐธ์กฐํ์ธ์.
์ฌ์ฉ ์ฌ๋ก์ ๊ฒฝ์ฐ ๋์ ์๋ ํ ์์๋ ๊ฒ์ ๋ ๊ฐ์ ์ปดํฌ์ ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค (๋ฐ๋ผ์ ๊ฐ๊ฐ ๊ณ ์ ํ ๊น์ด ๋ฒํผ๊ฐ์๋ ๋ ๊ฐ์ ์์ ํ ๋ถ๋ฆฌ ๋ ๋ ๋๋ง ๋์).
์ด ๊ฐ์:
glowComposer = new THREE.EffectComposer( renderer, renderTargetGlow );
glowComposer.addPass( renderModelGlow ); // RenderPass
glowComposer.addPass( hblur ); // ShaderPass
glowComposer.addPass( vblur ); // ShaderPass
finalComposer = new THREE.EffectComposer( renderer, renderTargetFinal );
finalComposer.addPass( renderModel ); // RenderPass
finalComposer.addPass( finalPass ); // ShaderPass
์ด์ ํ์ฐ ๋ ์ด์ด๋ finalComposer
์์ ์ผ๋ฐ tDiffuse
ํ
์ค์ฒ ์ํ๋ฌ๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ glowComposer
์์ ์ ๊ณต ํ ๊ธ๋ก์ฐ ๋ ์ด์ด๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํน์ ํจ์ค ์์ ํจ์ค๊ฐ ์ ๋ฉด ๋ฐ ํ๋ฉด ๋ฒํผ๋ฅผ ์ ํํ๋์ง ์ฌ๋ถ์ ๋ฐ๋ผ glowComposer.renderTarget1
๋๋ glowComposer.renderTarget2
๊ฐ๋ฉ๋๋ค. ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๋ฅผ ๋ชจ๋ ์๋ํ๊ณ ์ด๋ค ๊ฒ์ด ์๋ํ๋์ง ํ์ธํ๋ ๊ฒ์
๋๋ค.
finalshader.uniforms[ 'tGlow' ].texture = glowComposer.renderTarget1;
๋๋ ๊ทธ๊ฒ์ ์๋ํ์ง๋ง ๋งค์ฐ ์๊ธฐ์น ์์ ๊ฒฐ๊ณผ๋ฅผ ์ป์์ต๋๋ค.
์ด๊ฒ์ ๋น์ ์ด ์ ์ํ ์ฝ๋์
๋๋ค.
http://demo.bkcore.com/threejs/webgl_tron_glow_swap.html
renderModel์ tDiffuse๋ ์ฌ์ ํ ๊ฒ์ ์์
๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๊ฒ์ ๋ ๊ฐ์ ๋ ๋ ํจ์ค ๋ง ๋ฐ๊พผ ๋๊ฐ์ ์ฝ๋์
๋๋ค (ํ์ฐ ์ฌ์ ๊ธ๋ก์ฐ ์ปดํฌ์ ์ ์๊ณ ๊ธ๋ก์ฐ ์ฌ์ ์ต์ข
์ปดํฌ์ ์ ์์).
http://demo.bkcore.com/threejs/webgl_tron_glow_swap2.html
๋ฌผ๋ก ๋ํจ์ฆ๊ฐ ํ๋ ค์ง๊ณ ๊ธ๋ก์ฐ๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ๋ด๊ฐ ์ฐพ๊ณ ์๋ ์ดํํธ๋ ์๋์ง๋ง, tGlow์ tDiffuse ์ํ๋ฌ๊ฐ ๋ชจ๋ ์๋ํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ด๊ฐ ํ ๊ฒ์ ๊ต์ฒดํ๋ ๊ฒ๋ฟ์
๋๋ค (130 & 175 ํ).
new THREE.RenderPass (scene, camera);
์
new THREE.RenderPass (glowscene, glowcamera);
๊ฝค ๋๋์ต๋๋ค.
ํฌ๋ช
๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๋ ๋๋ง ๋์์ RGBA
ํ์์ ์ฌ์ฉํด์ผํฉ๋๋ค.
๊ธ์์, ๋์ ํฉ๋๋ค๋ง, ๊ธ๋ก์ฐ ํจ์ค๊ฐ ๊ฒ์ ์ ํฐ์์ ๋ ๋๋งํ๊ณ ๋ํจ์ฆ ํจ์ค๊ฐ ์ ... ๊ฒ์ ์ ๋ํจ์ฆ๋ฅผํ๊ธฐ ๋๋ฌธ์ ํฌ๋ช ๋๋ฅผ ์ฌ์ฉํ์ง ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ํฌ๋ช ์ฑ์ด ํ์ํ์ง ์๋๋ก ์ถ๊ฐ๋ก ๊ตฌ์ฑํฉ๋๋ค.
๊ทธ๋๋ RT์ ํ์์ RGBA๋ก ๋ณ๊ฒฝํ์ง๋ง ์๋ฌด๊ฒ๋ ๋ณ๊ฒฝํ์ง ์์์ต๋๋ค.
์ฌ๊ธฐ์ ์ด์ํ ๋ฌธ์ ๋ ๋ด glowscene / glowcamera RenderPass๊ฐ ์๋ํ์ง ์๋ ๋์ ๋ด ์ฅ๋ฉด / ์นด๋ฉ๋ผ RenderPass๋ฅผ ์ํํ๋ ๋์ ๋ด glowscene / glowcamera RenderPass๊ฐ ์ํํ๊ธฐ ์ ์ ๋ด ์ฅ๋ฉด / ์นด๋ฉ๋ผ RenderPass๋ฅผ ์ํํ๋ ๊ฒ์ ๋๋ค.
ํ , ๋ค๋ฅธ ์ฌ๋ฃ์ ๋ํ ์ง์ค๋ฉํธ๋ฆฌ ๊ณต์ ๋ฌธ์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค (# 1211 ์ฐธ์กฐ).
์ด ์๋:
function createScene( geometry, x, y, z, b ) {
zmesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial() );
// ...
var geometryClone = THREE.GeometryUtils.clone( geometry );
var gmat = new THREE.MeshBasicMaterial( { map: gtex } );
var gmesh = new THREE.Mesh( geometryClone, gmat );
// ...
}
์ ! ๊ทธ๊ฑฐ์๋ค.
์ด์ ๋ ๊ฐ์ ์ํ๋ฌ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์ฑ์์ก์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค :)
ํ ... ๋ ๋ค๋ฅธ ์ด์ํ ํ๋, ๋ด ๋ ์ํ๋ฌ๊ฐ ์ข๋ค.
gl_FragColor = texel;
๋๋ ๋ํจ์ฆ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ป์ต๋๋ค (http://demo.bkcore.com/threejs/webgl_tron_glow_swap2.html)
๋ด๊ฐ ํ ๋
gl_FragColor = glow;
๋๋ ์ฌ๋ฐ๋ฅด๊ฒ ๋น์ ์ป์ต๋๋ค.
ํ์ง๋ง ๋ ๊ฐ์ง ๋ชจ๋์ ๊ฐ๋จํ ์ฒจ๊ฐ์ ๋ธ๋ ๋๋ฅผํ๋ ค๊ณ ํ๋ฉด
gl_FragColor = ํ
์
+ ๊ธ๋ก์ฐ;
๋๋ ๋น์ ์ป์ต๋๋ค (http://demo.bkcore.com/threejs/webgl_tron_glow_swap.html).
๋๋ฌด ๋ง์ ์ง๋ฌธ์ํด์ ์ฃ์กํฉ๋๋ค.์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๊ณ ๋ ธ๋ ฅํ์ง๋ง ์๋ฌด ์์ฉ์ด ์์ต๋๋ค ..
์ฌ์ค์
gl_FragColor = texel + glow;
๋ ๋๋ง ์ถ๋ ฅ๊ณผ ๊ด๋ จํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ ๋๋ค.
gl_FragColor = glow + glow;
ํ ์ vec4๊ฐ ์ค๋ช ํ ์์์ด ๊ธ๋ก์ฐ vec4๋ก ๋์ฒด๋๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค. ๋ฏธ์น ๊ฒ ๊ฐ๋ค์.
ํ ์ค์ฒ๋ฅผ ๋ค๋ฅธ ํ ์ค์ฒ ๋จ์์ ๋ฃ์ด์ผํฉ๋๋ค.
uniforms: {
tDiffuse: { type: "t", value: 0, texture: null },
tGlow: { type: "t", value: 1, texture: null }
},
์์ ํ!
http://demo.bkcore.com/threejs/webgl_tron_glow.html
๋ค์ ๊ฐ์ฌํฉ๋๋ค :)
์ฌ๋ฆ!
Three.js๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ์ ๊ธ๋ก์ฐ ๋ ๋๋ง์ ๊ฐ์ ํ๋ ์ฌ๋์ด์๋ ๊ฒฝ์ฐ, ์ด์ ๋ํ ์์ ๊ธฐ์ฌ๋ฅผ ๊ฒ์ํ์ต๋๋ค. http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html
๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ข์;)
๋๋ ์ง์ค๋ฉํธ๋ฆฌ ๋ณต์ ๋์ ๋ ์ ๋ ดํ ๋ค๋ฅธ ์ต์ ์ ๋ ์ ์ฌํ ์ฌ์ง์ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค (๋ ํจ์ค์ ์ง์ค๋ฉํธ๋ฆฌ์ ๋์ผํ ๋ฒํผ๊ฐ ํ์ํจ).
์ด ๊ฒฝ์ฐ ๊ธ๋ก์ฐ ํจ์ค์ ๊ธฐ๋ณธ ์ฌ์ง์ ์ฌ์ฉํ๋ ๋์ ์ฃผ๋ณ ์กฐ๋ช ๋ง ์ฌ์ฉํ์ฌ ํ์ฐ ์กฐ๋ช ์ด 0 ์ธ Lambert ์ฌ์ง์ ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค.
์ด ๊ฐ์:
glowScene.add( new THREE.AmbientLight( 0xffffff ) );
// ...
var gmat = new THREE.MeshLambertMaterial( { map: gtex, ambient: 0xffffff, color: 0x000000 } );
var gmesh = new THREE.Mesh( geometry, gmat );
์ค์ ๋ก ๊ทธ๊ฒ์ ์๋ฒฝํ๊ฒ ์๋ํฉ๋๋ค.
http://demo.bkcore.com/threejs/webgl_tron_glow_seq.html
์ด ์๋ฃจ์ ์ ๋ํ ๊ธฐ์ฌ๋ฅผ ์ ๋ฐ์ดํธํ๊ฒ ์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค !
์ด ์ฝ๋๋ฅผ r60์ผ๋ก ์ด์ํ๊ณ ์์ผ๋ฉฐ ์ด์ํ๊ฒ๋ ํจ๊ณผ ์๊ณก๊ฐ๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ๊ณผ ๋์ผํ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค. ํ๋ฉด์ ๋ ๋๋งํ๋ฉด godray ํจ์ค๊ฐ ์๋ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ต์ข ํจ์ค๋ ๋ชจ๋ธ ๋ง ์ถ๋ ฅํฉ๋๋ค. "tadd"ํ ์ค์ฒ ์ฑ๋์ ๋ํ oclcomposer ์ถ๋ ฅ์ ๋ณ๋์ Effect Composer๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๊ด์ฐฎ์ ๊ฒ ๊ฐ์ต๋๋ค !! renderer.PreserveDrawingBuffer = true๋ก ์๋ํ์ง๋ง ์๋ํ์ง ์์์ต๋๋ค. autoclear๋ฅผ false๋ก ์ค์ ํ๋ ค๊ณ ์๋ํ์ง๋ง ๊ทธ๊ฒ ์๋๋๋ค.
์ต์ข ์๊ณก๊ฐ ์ดํ์ oclcomposer๋ฅผ ๋ ๋๋งํ๊ณ godray๊ฐ ํ๋ฉด์ ์ถ๋ ฅ์ ์ ๋ฌํ๋ฉด godray๋ ๋ณผ ์ ์์ง๋ง ๋ชจ๋ธ์ ๋ณผ ์ ์์ต๋๋ค.
oclcomposer ์ถ๋ ฅ์ ์ต์ข ํจ์ค์์ tadd ์ฑ๋๋ก ๋ณด๋ด๋ ๋์ MeshBasicMaterial๋ก ํ์ดํํ๊ณ ํ ์ค์ฒ ์ฟผ๋๋ฅผ ๋ง๋ค๊ณ ๋ฉ์ธ ์ฅ๋ฉด์ ์ถ๊ฐํ๋ฉด ์ด์ ๋ชจ๋ธ๊ณผ Godrays (ํ๋ฉด์์)๋ฅผ ๋ณผ ์ ์์ต๋๋ค. ์ํ๋ ํจ๊ณผ์ด์ง๋ง ํ ์ค์ฒ ํ์ดํ์ด ์๋ํ๊ณ ์์์ ์๋ ค์ค๋๋ค.
// COMPOSERS
//-------------------
var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBufer: false };
renderTargetOcl = new THREE.WebGLRenderTarget( SCREEN_WIDTH/2, SCREEN_HEIGHT/2, renderTargetParameters );
hblur = new THREE.ShaderPass( THREE.ShaderExtras[ "horizontalBlur" ] );
vblur = new THREE.ShaderPass( THREE.ShaderExtras[ "verticalBlur" ] );
var bluriness = 2;
hblur.uniforms[ 'h' ].value = bluriness / SCREEN_WIDTH*2;
vblur.uniforms[ 'v' ].value = bluriness / SCREEN_HEIGHT*2;
var renderModel = new THREE.RenderPass( scene, camera );
var renderModelOcl = new THREE.RenderPass( oclscene, oclcamera );
grPass = new THREE.ShaderPass( THREE.Extras.Shaders.Godrays );
grPass.needsSwap = true;
grPass.renderToScreen = false;
oclcomposer = new THREE.EffectComposer( renderer, renderTargetOcl );
oclcomposer.addPass( renderModelOcl );
oclcomposer.addPass( hblur );
oclcomposer.addPass( vblur );
oclcomposer.addPass( grPass );
var finalPass = new THREE.ShaderPass( THREE.Extras.Shaders.Additive );
finalPass.needsSwap = true;
finalPass.renderToScreen = true;
finalPass.uniforms[ 'tAdd' ].texture = oclcomposer.renderTarget1;
renderTarget = new THREE.WebGLRenderTarget( SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters );
finalcomposer = new THREE.EffectComposer( renderer, renderTarget );
finalcomposer.addPass( renderModel );
finalcomposer.addPass( finalPass );
//RENDER
//-----------
oclcomposer.render(0.1);
finalcomposer.render( 0.1 );
@bbiswas ์ต์ ๋ฒ์ ๊ณผ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ๋ญ๊ฐ ์์ ๋ด ์ จ๋์?
์, ์๋ํ์ต๋๋ค. ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
var renderTargetOcl = new THREE.WebGLRenderTarget( webGL_window_width/4, webGL_window_height/4, renderTargetParameters );
hblur = new THREE.ShaderPass( THREE.ShaderExtras[ "horizontalBlur" ] );
vblur = new THREE.ShaderPass( THREE.ShaderExtras[ "verticalBlur" ] );
var bluriness = 3;
hblur.uniforms[ 'h' ].value = bluriness / webGL_window_width*2;
vblur.uniforms[ 'v' ].value = bluriness / webGL_window_height*2;
var renderModel = new THREE.RenderPass( scene, camera );
var renderModelOcl = new THREE.RenderPass( g_occlusion_buffer, g_occlusion_camera );
grPass = new THREE.ShaderPass( THREE.Extras.Shaders.Godrays );
grPass.needsSwap = true;
grPass.renderToScreen = false;
g_volumetric_light_composer = new THREE.EffectComposer( webGLRenderer, renderTargetOcl );
g_volumetric_light_composer.addPass( renderModelOcl );
g_volumetric_light_composer.addPass( hblur );
g_volumetric_light_composer.addPass( vblur );
g_volumetric_light_composer.addPass( hblur );
g_volumetric_light_composer.addPass( vblur );
g_volumetric_light_composer.addPass( grPass );
var finalPass = new THREE.ShaderPass( THREE.Extras.Shaders.Additive );
finalPass.needsSwap = true;
finalPass.renderToScreen = true;
finalPass.uniforms[ 'tAdd' ].value = g_volumetric_light_composer.renderTarget1;
finalcomposer.addPass( renderModel );
finalcomposer.addPass( finalPass );
//in Render Loop
g_occlusion_camera.position = camera.position;
g_occlusion_camera.lookAt( new THREE.Vector3(0,0,0) );
camera.lookAt( new THREE.Vector3(0,0,0) );
vlight.position = pointLight1.position;
vlight.updateMatrixWorld();
var lPos = THREE.Extras.Utils.projectOnScreen(pointLight1, camera);
grPass.uniforms["fX"].value = lPos.x;
grPass.uniforms["fY"].value = lPos.y;
g_volumetric_light_composer.render(0.1);
finalcomposer.render( 0.1 );
๊ฐ์ฌํฉ๋๋ค!
finalPass.uniforms[ 'tAdd' ].value
๋ผ์ธ์ด์์ต๋๋ค. ์ ์ finalPass.uniforms[ 'tAdd' ].texture
๋ฐ์์ต๋๋ค.
์๋ bbiswas,์ด ๋ถ๋ถ์ ๋ํด g_occlusion_buffer๋ฅผ ๋ง๋ ์์น๋ฅผ ์ถ๊ฐ ํ ์ ์์ต๋๊น?
var renderModelOcl = new THREE.RenderPass (g_occlusion_buffer, g_occlusion_camera);
์๋ ๋ฐ๋ชจ ๊ฐ ๋ ์ด์ ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
FWIW ์ฌ๊ธฐ์์ Additive Blend (Three.js v79 ์ฌ์ฉ)๋ฅผ ํตํด FX๋ฅผ ์ ํ์ ์ผ๋ก ์ ์ฉํ๊ณ ๊ตฌ์ฑํ๋ ๋ฐ๋ชจ๋ฅผ ๋ง๋ค์์ต๋๋ค : https://www.airtightinteractive.com/demos/selective-fx/
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์๋ ๋ฐ๋ชจ ๊ฐ ๋ ์ด์ ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
FWIW ์ฌ๊ธฐ์์ Additive Blend (Three.js v79 ์ฌ์ฉ)๋ฅผ ํตํด FX๋ฅผ ์ ํ์ ์ผ๋ก ์ ์ฉํ๊ณ ๊ตฌ์ฑํ๋ ๋ฐ๋ชจ๋ฅผ ๋ง๋ค์์ต๋๋ค : https://www.airtightinteractive.com/demos/selective-fx/