diff --git a/js/ThreeCanvas.js b/js/ThreeCanvas.js index 2cea3b4..e7b9fc9 100644 --- a/js/ThreeCanvas.js +++ b/js/ThreeCanvas.js @@ -6,6 +6,11 @@ import { DRACOLoader } from './lib/jsm/loaders/DRACOLoader.js'; import { EffectComposer } from './lib/jsm/postprocessing/EffectComposer.js'; import { RenderPass } from './lib/jsm/postprocessing/RenderPass.js'; import { ShaderPass } from './lib/jsm/postprocessing/ShaderPass.js'; +import { SobelOperatorShader } from './lib/jsm/shaders/SobelOperatorShader.js'; +import { LuminosityShader } from './lib/jsm/shaders/LuminosityShader.js'; +import { ExposureShader } from './lib/jsm/shaders/ExposureShader.js'; +import { BrightnessContrastShader } from './lib/jsm/shaders/BrightnessContrastShader.js'; + import { Outline, Inline, BlackAll, sobelShader, thresholdShader } from "./lib/Toon.js"; import { Hub } from "./lib/Hub.js"; import { Files } from "./lib/Files.js"; @@ -42,6 +47,7 @@ export class ThreeCanvas { // lock scale if false this.autoScale = !true; this.autoAutoAnim = false; + this.useLinesComposer = true; // Views3 this.VIEWS3 = views3 @@ -80,6 +86,7 @@ export class ThreeCanvas { renderer2.setSize( this.size.w, this.size.h ); renderer2.setPixelRatio( this.pixelRatio ); renderer2.setClearColor( 0x000000, 1 ); + renderer1.shadowMap.enabled = false; renderer2.autoClear = false; renderer2.domElement.style.cssText = "position:absolute; margin:0; padding:0; border:1px solid red;"; renderer2.domElement.setAttribute("view", "TOP") @@ -123,10 +130,11 @@ export class ThreeCanvas { depthMaterial.displacementScale = 10 const normalMaterial = new THREE.MeshNormalMaterial() + // Renderers this.tools = [ { type:'color', renderer: renderer1, camera: camera1, material:null }, - { type:'lines', renderer: renderer2, camera: camera2, material:null }, + { type:'lines', renderer: renderer2, camera: camera2, material:normalMaterial }, { type:'depth', renderer: renderer3, camera: camera3, material:depthMaterial }, { type:'normal', renderer: renderer4, camera: camera3, material:normalMaterial } ]; @@ -136,13 +144,6 @@ export class ThreeCanvas { - - //this.getDom(1).style.display = "none" - //this.getDom(2).style.display = "none" - //this.getDom(3).style.display = "none" - - - /*let m0 = new THREE.Mesh(new THREE.BoxGeometry(5,5,5)) let b0 = new THREE.BoxHelper(m0) let m1 = new THREE.Mesh(new THREE.BoxGeometry(3,3,3)) @@ -380,7 +381,7 @@ export class ThreeCanvas { model.updateMatrixWorld(); let b0 = new THREE.BoxHelper( model, 0x201924 ); - this.helper.add(b0) + //this.helper.add(b0) b0.geometry.computeBoundingSphere() b0.geometry.computeBoundingBox() @@ -388,7 +389,7 @@ export class ThreeCanvas { const radius = b0.geometry.boundingSphere.radius; const center = box.getCenter(new THREE.Vector3()); - console.log( radius) + //console.log( radius) let lightpos = center.clone().add( new THREE.Vector3(-radius*0.3,radius*2, radius) ) @@ -476,6 +477,9 @@ export class ThreeCanvas { } }) + + if(this.composer) this.composer.setSize(this.size.w, this.size.h); + this.needResize = false; @@ -489,6 +493,40 @@ export class ThreeCanvas { this.render(); } + initComposer(renderer){ + + // init only one composer + if(this.composer) return; + + const composer = new EffectComposer(renderer); + + const renderPass = new RenderPass(this.scene, this.camera); + composer.addPass(renderPass); + + // color to grayscale conversion + + const effectGrayScale = new ShaderPass( LuminosityShader ); + composer.addPass( effectGrayScale ); + + const effectExposure = new ShaderPass( ExposureShader ) + effectExposure.uniforms[ 'exposure' ].value = 0.75; + composer.addPass( effectExposure ) + + const effectSobel = new ShaderPass( SobelOperatorShader ); + effectSobel.uniforms[ 'resolution' ].value.x = this.size.w; + effectSobel.uniforms[ 'resolution' ].value.y = this.size.h; + composer.addPass( effectSobel ) + + //const sobelPass = new ShaderPass(sobelShader); + //composer.addPass(sobelPass); + const thresholdPass = new ShaderPass(thresholdShader); + thresholdPass.uniforms[ 'threshold' ].value = 0.2; + composer.addPass(thresholdPass); + + this.composer = composer; + + } + async render() { if (this.autoScale) this.resize(); //this.tools.forEach((data)=>data.renderer.render(this.scene, data.camera)) @@ -497,41 +535,23 @@ export class ThreeCanvas { this.tools.forEach((data)=>{ + this.scene.overrideMaterial = !data.material ? null: data.material; + if( data.type !== 'color' ) this.helper.visible = false else if( this.withHelper ) this.helper.visible = true if( data.type === 'lines' ){ - const composer = new EffectComposer(data.renderer); - const renderPass = new RenderPass(this.scene, this.camera); - composer.addPass(renderPass); + if(this.useLinesComposer){ + + this.initComposer(data.renderer) + return this.composer.render(this.scene) - const sobelPass = new ShaderPass(sobelShader); - composer.addPass(sobelPass); - - const thresholdPass = new ShaderPass(thresholdShader); - composer.addPass(thresholdPass); - return composer.render(this.scene) - // const currentAutoClear = data.renderer.autoClear; - // const camera = this.VIEWS3 && !this.fixCamers ? data.camera : this.camera - - // data.renderer.clear( true, true, true ); - - // //this.scene.matrixWorldAutoUpdate = false; - // //this.scene.background = null; - - // this.scene.overrideMaterial = Outline; - // data.renderer.render( this.scene, camera ); - - // this.scene.overrideMaterial = Inline; - // data.renderer.render( this.scene, camera ); - - // this.scene.overrideMaterial = BlackAll; - // return data.renderer.render( this.scene, camera ) + } } - this.scene.overrideMaterial = !data.material ? null: data.material; + //this.scene.overrideMaterial = !data.material ? null: data.material; return data.renderer.render(this.scene, this.VIEWS3 && !this.fixCamers ? data.camera : this.camera) }) diff --git a/js/index.html b/js/index.html index 0fdc0d4..a274d84 100644 --- a/js/index.html +++ b/js/index.html @@ -17,7 +17,7 @@ import { ThreeCanvas } from './ThreeCanvas.js' - const threeCanvas = new ThreeCanvas(null, null); + const threeCanvas = new ThreeCanvas(null, null, {views3:true}); threeCanvas.init(true); let dom0 = threeCanvas.getDom(0); diff --git a/js/lib/Toon.js b/js/lib/Toon.js index 629b894..7b63133 100644 --- a/js/lib/Toon.js +++ b/js/lib/Toon.js @@ -102,7 +102,7 @@ export const sobelShader = { export const thresholdShader = { uniforms: { tDiffuse: { value: null }, - threshold: { value: 0.2 } + threshold: { value: 0.4 } }, vertexShader: ` varying vec2 vUv; @@ -118,7 +118,8 @@ export const thresholdShader = { void main() { float value = texture2D(tDiffuse, vUv).r; - gl_FragColor = vec4(vec3(value > threshold ? 1.0 : 0.0), 1.0); + //gl_FragColor = vec4(vec3(value > threshold ? 1.0 : 0.0), 1.0); + gl_FragColor = vec4(vec3(value > threshold ? value*1.5 : 0.0), 1.0); } ` }; diff --git a/js/lib/jsm/shaders/BrightnessContrastShader.js b/js/lib/jsm/shaders/BrightnessContrastShader.js new file mode 100644 index 0000000..c1dea9e --- /dev/null +++ b/js/lib/jsm/shaders/BrightnessContrastShader.js @@ -0,0 +1,56 @@ +/** + * Brightness and contrast adjustment + * https://github.com/evanw/glfx.js + * brightness: -1 to 1 (-1 is solid black, 0 is no change, and 1 is solid white) + * contrast: -1 to 1 (-1 is solid gray, 0 is no change, and 1 is maximum contrast) + */ + +const BrightnessContrastShader = { + + name: 'BrightnessContrastShader', + + uniforms: { + + 'tDiffuse': { value: null }, + 'brightness': { value: 0 }, + 'contrast': { value: 0 } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform sampler2D tDiffuse; + uniform float brightness; + uniform float contrast; + + varying vec2 vUv; + + void main() { + + gl_FragColor = texture2D( tDiffuse, vUv ); + + gl_FragColor.rgb += brightness; + + if (contrast > 0.0) { + gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) / (1.0 - contrast) + 0.5; + } else { + gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) * (1.0 + contrast) + 0.5; + } + + }` + +}; + +export { BrightnessContrastShader }; diff --git a/js/lib/jsm/shaders/ExposureShader.js b/js/lib/jsm/shaders/ExposureShader.js new file mode 100644 index 0000000..3008ab2 --- /dev/null +++ b/js/lib/jsm/shaders/ExposureShader.js @@ -0,0 +1,44 @@ +/** + * Exposure shader + */ + +const ExposureShader = { + + name: 'ExposureShader', + + uniforms: { + + 'tDiffuse': { value: null }, + 'exposure': { value: 1.0 } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform float exposure; + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + void main() { + + gl_FragColor = texture2D( tDiffuse, vUv ); + gl_FragColor.rgb *= exposure; + + }` + +}; + +export { ExposureShader }; diff --git a/js/lib/jsm/shaders/LuminosityShader.js b/js/lib/jsm/shaders/LuminosityShader.js new file mode 100644 index 0000000..4472980 --- /dev/null +++ b/js/lib/jsm/shaders/LuminosityShader.js @@ -0,0 +1,48 @@ +/** + * Luminosity + * http://en.wikipedia.org/wiki/Luminosity + */ + +const LuminosityShader = { + + name: 'LuminosityShader', + + uniforms: { + + 'tDiffuse': { value: null } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + #include + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + void main() { + + vec4 texel = texture2D( tDiffuse, vUv ); + + float l = luminance( texel.rgb ); + + gl_FragColor = vec4( l, l, l, texel.w ); + + }` + +}; + +export { LuminosityShader }; diff --git a/js/lib/jsm/shaders/SobelOperatorShader.js b/js/lib/jsm/shaders/SobelOperatorShader.js new file mode 100644 index 0000000..998ea59 --- /dev/null +++ b/js/lib/jsm/shaders/SobelOperatorShader.js @@ -0,0 +1,92 @@ +import { + Vector2 +} from '../../three.module.js'; + +/** + * Sobel Edge Detection (see https://youtu.be/uihBwtPIBxM) + * + * As mentioned in the video the Sobel operator expects a grayscale image as input. + * + */ + +const SobelOperatorShader = { + + name: 'SobelOperatorShader', + + uniforms: { + + 'tDiffuse': { value: null }, + 'resolution': { value: new Vector2() } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform sampler2D tDiffuse; + uniform vec2 resolution; + varying vec2 vUv; + + void main() { + + vec2 texel = vec2( 1.0 / resolution.x, 1.0 / resolution.y ); + + // kernel definition (in glsl matrices are filled in column-major order) + + const mat3 Gx = mat3( -1, -2, -1, 0, 0, 0, 1, 2, 1 ); // x direction kernel + const mat3 Gy = mat3( -1, 0, 1, -2, 0, 2, -1, 0, 1 ); // y direction kernel + + // fetch the 3x3 neighbourhood of a fragment + + // first column + + float tx0y0 = texture2D( tDiffuse, vUv + texel * vec2( -1, -1 ) ).r; + float tx0y1 = texture2D( tDiffuse, vUv + texel * vec2( -1, 0 ) ).r; + float tx0y2 = texture2D( tDiffuse, vUv + texel * vec2( -1, 1 ) ).r; + + // second column + + float tx1y0 = texture2D( tDiffuse, vUv + texel * vec2( 0, -1 ) ).r; + float tx1y1 = texture2D( tDiffuse, vUv + texel * vec2( 0, 0 ) ).r; + float tx1y2 = texture2D( tDiffuse, vUv + texel * vec2( 0, 1 ) ).r; + + // third column + + float tx2y0 = texture2D( tDiffuse, vUv + texel * vec2( 1, -1 ) ).r; + float tx2y1 = texture2D( tDiffuse, vUv + texel * vec2( 1, 0 ) ).r; + float tx2y2 = texture2D( tDiffuse, vUv + texel * vec2( 1, 1 ) ).r; + + // gradient value in x direction + + float valueGx = Gx[0][0] * tx0y0 + Gx[1][0] * tx1y0 + Gx[2][0] * tx2y0 + + Gx[0][1] * tx0y1 + Gx[1][1] * tx1y1 + Gx[2][1] * tx2y1 + + Gx[0][2] * tx0y2 + Gx[1][2] * tx1y2 + Gx[2][2] * tx2y2; + + // gradient value in y direction + + float valueGy = Gy[0][0] * tx0y0 + Gy[1][0] * tx1y0 + Gy[2][0] * tx2y0 + + Gy[0][1] * tx0y1 + Gy[1][1] * tx1y1 + Gy[2][1] * tx2y1 + + Gy[0][2] * tx0y2 + Gy[1][2] * tx1y2 + Gy[2][2] * tx2y2; + + // magnitute of the total gradient + + float G = sqrt( ( valueGx * valueGx ) + ( valueGy * valueGy ) ); + + gl_FragColor = vec4( vec3( G ), 1 ); + + }` + +}; + +export { SobelOperatorShader }; diff --git a/pyproject.toml b/pyproject.toml index 4866169..27547bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ [project] name = "comfyui_three_js" description = "Use power of three.js in 3d view on comfyui." -version = "0.2.2" +version = "0.2.3" license = { file = "LICENSE" } dependencies = []