From aca6376fe651c9e1eb6ed54d86cc2199007035eb Mon Sep 17 00:00:00 2001 From: AlekPet Date: Mon, 2 Dec 2024 21:57:39 +0300 Subject: [PATCH] updated styles, betters view wrappers, index.html and add Canny Edge Corrected visible wrappers and clear code Added Canny Edge (experiment) Fixed incorrect work "Fix camers", not set in the "Lines" canvas, once set composer and not set if changed. Fixed layout index.html and incorrect path to Javascript and css files. Other small fixes. --- js/ThreeCanvas.js | 50 +++++++++++++------- js/css/styles.css | 10 +++- js/index.html | 63 +++++++++++++++++++------- js/lib/Toon.js | 113 ++++++++++++++++++++++++++++++++++++++++++++++ js/three_view.js | 26 +---------- 5 files changed, 203 insertions(+), 59 deletions(-) diff --git a/js/ThreeCanvas.js b/js/ThreeCanvas.js index 591aadb..cc601bb 100644 --- a/js/ThreeCanvas.js +++ b/js/ThreeCanvas.js @@ -10,7 +10,7 @@ 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 { Outline, Inline, BlackAll, sobelShader, cannyEdgeShader, thresholdShader } from "./lib/Toon.js"; import { Hub } from "./lib/Hub.js"; import { Files } from "./lib/Files.js"; import { Tools } from "./lib/Tools.js"; @@ -68,7 +68,7 @@ export class ThreeCanvas { createWrappers(canvasNames){ // Operators "Sobel", "Scharr", "Prewitt" - const linesOperators = ["Sobel", "Prewitt", "Scharr"]; + const linesOperators = ["Sobel", "Prewitt", "Scharr", "Canny"]; const listLines = $el("select.linesMethod_box",{ style: { fontSize: "0.5rem", @@ -80,7 +80,8 @@ export class ThreeCanvas { outline: 0 }, onchange: (e)=> { - this.sobelPassRadio = e.target.value + this.sobelPassSelect = e.target.value + this.composer = null this.render() }, title: "Selectd operator for lines" @@ -150,11 +151,11 @@ export class ThreeCanvas { $el("button.threeCanvasButton.threeCanvasViews3", { textContent: "All Views", onclick: (e) => { - this.VIEWS3 = !this.VIEWS3 + this.VIEWS3 = !this.VIEWS3; - this.panelWrapper.querySelector(".threeCanvasViews3_camerafix").style.display = this.VIEWS3 ? "block": "none" + this.panelWrapper.querySelector(".threeCanvasViews3_camerafix").style.display = this.VIEWS3 ? "block": "none"; // threeCanvas.fixCamers = threeCanvas.VIEWS3 - this.setViews3(this.wrappers) + this.setViews3(this.wrappers); }, }), $el("input.threeCanvasViews3_camerafix", { @@ -166,6 +167,7 @@ export class ThreeCanvas { }, onchange: (e)=>{ this.fixCamers = !!e.target.checked + this.composer = null }, })]), ] @@ -368,6 +370,12 @@ export class ThreeCanvas { } setViews3(){ + if( this.VIEWS3 ){ + this.threeWrapper.classList.add("threeWrapper3Views"); + } else { + this.threeWrapper.classList.remove("threeWrapper3Views"); + } + this.wrappers.slice(1).map((v)=> Object.assign(v.style, { display: this.VIEWS3 ? "block" : "none" })) @@ -610,18 +618,17 @@ export class ThreeCanvas { this.render(); } - initComposer(renderer){ + initComposer(renderer, camera){ + const operatorLines = this?.sobelPassSelect ?? 0 // init only one composer if(this.composer){ - // Set changed line type - this.effectSobel.uniforms.lineType.value = this?.sobelPassRadio ?? 0 return; } const composer = new EffectComposer(renderer); - const renderPass = new RenderPass(this.scene, this.camera); + const renderPass = new RenderPass(this.scene, camera); composer.addPass(renderPass); // color to grayscale conversion @@ -633,12 +640,23 @@ export class ThreeCanvas { 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; + if(operatorLines <= 2){ + const effectSobel = new ShaderPass( SobelOperatorShader ); + effectSobel.uniforms[ 'resolution' ].value.x = this.size.w; + effectSobel.uniforms[ 'resolution' ].value.y = this.size.h; + + // Set changed line type + effectSobel.uniforms.lineType.value = operatorLines - this.effectSobel = effectSobel - composer.addPass( effectSobel ) + composer.addPass( effectSobel ) + + } else if(operatorLines == 3){ + // Canny edge + const effectCannyEdge = new ShaderPass( cannyEdgeShader ); + effectCannyEdge.uniforms.resolution.value.x = this.size.w; + effectCannyEdge.uniforms.resolution.value.y = this.size.h; + composer.addPass( effectCannyEdge ) + } // this.effectSobel = new ShaderPass(sobelShader); // composer.addPass(this.effectSobel); @@ -669,7 +687,7 @@ export class ThreeCanvas { if(this.useLinesComposer){ - this.initComposer(data.renderer) + this.initComposer(data.renderer, camera) return this.composer.render(this.scene) } diff --git a/js/css/styles.css b/js/css/styles.css index 633d6d9..7a1a0ca 100644 --- a/js/css/styles.css +++ b/js/css/styles.css @@ -1,5 +1,11 @@ .threeWrapper { - /* wrapper all canvases */ + display: grid; + grid-template-columns: 1fr; + gap: 5px; +} + +.threeWrapper3Views{ + grid-template-columns: 1fr 1fr; } .threeCanvasPanelWrapper { @@ -35,7 +41,7 @@ } .wrapperThreeViewCanvas { - position: absolute; + position: relative; margin: 0px; padding: 0px; display: block; diff --git a/js/index.html b/js/index.html index 85b5ec4..73b9bf1 100644 --- a/js/index.html +++ b/js/index.html @@ -9,18 +9,33 @@ THREE DIRECT - + + @@ -28,18 +43,34 @@ \ No newline at end of file diff --git a/js/lib/Toon.js b/js/lib/Toon.js index 69e1f09..35f57fe 100644 --- a/js/lib/Toon.js +++ b/js/lib/Toon.js @@ -123,6 +123,119 @@ export const sobelShader = { ` }; +// Canny edge detection shader +export const cannyEdgeShader = { + uniforms: { + tDiffuse: { value: null }, + resolution: { value: new THREE.Vector2() }, + lowThreshold: { value: 0.1 }, + highThreshold: { value: 0.3 } + }, + vertexShader: ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + // Canny edge detection shader (copy the GLSL code here from the previous implementation) + precision highp float; + + uniform sampler2D tDiffuse; + uniform vec2 resolution; + uniform float lowThreshold; + uniform float highThreshold; + + varying vec2 vUv; + + const float kernel[9] = float[9]( + 1.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0, + 2.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0, + 1.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0 + ); + + void main() { + vec2 texel = vec2(1.0 / resolution.x, 1.0 / resolution.y); + + vec3 blurred = vec3(0.0); + int k = 0; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + blurred += texture2D(tDiffuse, vUv + vec2(i, j) * texel).rgb * kernel[k++]; + } + } + + float gray = dot(blurred, vec3(0.2989, 0.5870, 0.1140)); + + float Gx[9] = float[9]( + -1.0, 0.0, 1.0, + -2.0, 0.0, 2.0, + -1.0, 0.0, 1.0 + ); + float Gy[9] = float[9]( + -1.0, -2.0, -1.0, + 0.0, 0.0, 0.0, + 1.0, 2.0, 1.0 + ); + + float gradientX = 0.0; + float gradientY = 0.0; + k = 0; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + float xsample = texture2D(tDiffuse, vUv + vec2(i, j) * texel).r; + gradientX += xsample * Gx[k]; + gradientY += xsample * Gy[k++]; + } + } + + float magnitude = length(vec2(gradientX, gradientY)); + float direction = atan(gradientY, gradientX); + + float angle = mod(direction + 3.14159265, 3.14159265) / 3.14159265 * 4.0; + float neighbor1 = 0.0; + float neighbor2 = 0.0; + + if (angle <= 1.0 || angle > 3.0) { + neighbor1 = texture2D(tDiffuse, vUv + vec2(texel.x, 0.0)).r; + neighbor2 = texture2D(tDiffuse, vUv - vec2(texel.x, 0.0)).r; + } else if (angle <= 2.0) { + neighbor1 = texture2D(tDiffuse, vUv + vec2(texel.x, -texel.y)).r; + neighbor2 = texture2D(tDiffuse, vUv - vec2(texel.x, -texel.y)).r; + } else { + neighbor1 = texture2D(tDiffuse, vUv + vec2(0.0, texel.y)).r; + neighbor2 = texture2D(tDiffuse, vUv - vec2(0.0, texel.y)).r; + } + + if (magnitude < neighbor1 || magnitude < neighbor2) { + magnitude = 0.0; + } + + float edge = 0.0; + if (magnitude >= highThreshold) { + edge = 1.0; + } else if (magnitude >= lowThreshold) { + edge = 0.5; + } + + if (edge == 0.5) { + bool connected = false; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (texture2D(tDiffuse, vUv + vec2(i, j) * texel).r >= highThreshold) { + connected = true; + } + } + } + if (!connected) edge = 0.0; + } + + gl_FragColor = vec4(vec3(edge), 1.0); + } + ` +}; + export const thresholdShader = { uniforms: { tDiffuse: { value: null }, diff --git a/js/three_view.js b/js/three_view.js index 3e3c656..c5a9f7f 100644 --- a/js/three_view.js +++ b/js/three_view.js @@ -71,7 +71,7 @@ async function widgetThreeJS(node, nodeData, inputData, app, params = {}) { origDraw?.apply(this, arguments); const [ctx, nodeThree, widgetWidth, posY] = arguments; - const w = !threeCanvas.VIEWS3 ? widgetWidth - 25: (widgetWidth - 25)/2; + const w = widgetWidth - 25; const aspect_ratio = threeCanvas.size.h / threeCanvas.size.w; Object.assign(threeCanvas.threeWrapper.style, { @@ -79,30 +79,6 @@ async function widgetThreeJS(node, nodeData, inputData, app, params = {}) { height: w * aspect_ratio + "px", }); - if( threeCanvas.VIEWS3 ){ - Object.assign(threeCanvas.getWrapper(1).style, { - width: w + "px", - height: w * aspect_ratio + "px", - left: w + 10 + "px" - }); - - Object.assign(threeCanvas.getWrapper(2).style, { - width: w + "px", - height: w * aspect_ratio + "px", - top: parseFloat(threeCanvas.getWrapper(1).style.height) + 10 + "px" - - }); - - Object.assign(threeCanvas.getWrapper(3).style, { - width: w + "px", - height: w * aspect_ratio + "px", - left: w + 10 + "px", - top: parseFloat(threeCanvas.getWrapper(1).style.height) + 10 + "px" - - }); - } - - // Update renderer // threeCanvas.update(node.size[0]); //app.graph.setDirtyCanvas(true, false);