webgl에서 캔버스 스케일을 늘이고 줄였을 시 뼈대 좌표가 크게 변동 됩니다.
해당 문제는 어떻게 해결 해야 하나요?

캔버스 스케일 크기를 변경 할 경우에 뼈대 좌표가 동일하게 변경 되어야 할텐데
좌표가 다르게 변동 됩니다.

Related Discussions
...

그것은 여러 가지 요인에 따라 달라지며, 귀하의 텍스트 설명만으로는 파악할 수 없습니다. 특정 문제를 보여주는 최소한의 예를 제공해 주시겠습니까? 가장 쉬운 것은 스켈레톤을 로드한 다음 사용자가 설명하는 작업을 수행하는 일부 JavaScript가 포함된 독립형 .html 파일입니다.

That depends on many different factors, which I can't figure out just based on your textual description. Could you provide a minimal example that demonstrates your specific issue? The easiest thing would be a self-contained .html file with some JavaScript that loads a skeleton, then does what you describe.

    Mario

    https://hairservice.edugreen.shop/mask_on_profile

    머리카락을 두 손가락으로 터치하여 이동 할 수 있으며, 스케일 크기를 늘이고 줄 일 수 있습니다.
    스케일 크기를 늘이고 줄인 후 드래그로 뼈대를 이동 할 경우에 뼈대의 터치 포인트가 다른곳에 위치하여 있습니다.

    해당 예제에 업로드 이미지를 사용하면 확인이 가능합니다.

    카메라를 사용하지 않는 환경에서 해당 예제를 제공 해 줄 수 있나요?
    `

    	function dragged(canvas, renderer, target, x, y){
    		if (target) {
    			 x = spine.MathUtils.clamp(x, 0, canvas.clientWidth)
    			 y = spine.MathUtils.clamp(y, 0, canvas.clientHeight);
    			 renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
    			 if (target.parent !== null) {
    					target.parent.worldToLocal(position.set(coords.x, coords.y));
    					target.x = position.x;
    					target.y = position.y;
    			 } else {
    					target.x = coords.x;
    					target.y = coords.y;
    			 }
    		}
    	}

    `

    `

    	function closest(canvas, renderer, skeleton, controlbones, hoverTargets, x, y){
    		mouse.set(x, canvas.clientHeight - y, 0)
    		var bestDistance = 250, index = 0;
    		var best;
    		for (var i = 0; i < controlbones.length; i++) {
    			 hoverTargets[i] = null;
    			 let bone = skeleton.findBone(controlbones[i]);
    			 var position = new spine.Vector2(bone.length, 0);
    			 bone.localToWorld(position);
    			 let distance = renderer.camera.worldToScreen(
    					coords.set(bone.worldX, bone.worldY, 0),
    					canvas.clientWidth, canvas.clientHeight).distance(mouse);
    			 if (distance < bestDistance) {
    					bestDistance = distance;
    					best = bone;
    					index = i;
    			 }
    		}
    		if (best) hoverTargets[index] = best;
    		return best;
    	}

    `

    카메라를 사용하지 않고 랜더링을 하고 있습니다.
    render
    `

    	function resize() {
        var hairimagesrc = document.getElementById("maskon");
        var hairimagewidth = $(hairimagesrc).width();
    
    		let w = canvas.clientWidth;
    
    
    		let h = canvas.clientHeight;
    		if (canvas.width != w || canvas.height != h) {
    			canvas.width = w;
    			canvas.height = h;
    		}
    
    		// Calculations to center the skeleton in the canvas.
    		let bounds = skeletons[activeSkeleton][format].bounds;
    		centerX = bounds.offset.x + bounds.size.x / 2;
    		centerY = bounds.offset.y + bounds.size.y / 2;
    		let scaleX = bounds.size.x / canvas.width;
    		let scaleY = bounds.size.y / canvas.height;
    		let scale = Math.max(scaleX, scaleY) * 1.2;
    		if (scale < 1) scale = 1;
    		let width = canvas.width * scale;
    		let height = canvas.height * scale;
    
    
    
        switch(activeSkeleton){
          case "assets/woman_hair_1.png" :
          height = canvas.height * 1.2;
          width = canvas.width * 1.2;
          mvp.ortho2d(centerX - width / 2, centerY - height / 2, width, height);
    
          break;
    
          case "assets/man_hair_2.png" :
          height = canvas.height * 1;
          width = canvas.width * 1;
          mvp.ortho2d(centerX - width / 2, centerY - height / 2, width, height);
    
          case "assets/man_hair_4.png" :
          height = canvas.height * 1.6;
          width = canvas.width * 1.6;
          mvp.ortho2d(centerX - width / 2, centerY - height / 1.8, width, height);
    
          default :
          mvp.ortho2d(centerX - width / 2, centerY - height / 1.5, width, height);
        }
    
    
    
    
    
    		ctx.gl.viewport(0, 0, canvas.width, canvas.height);
    	}

    `

            x = x - canvas.width / 2 + centerX / resizescale;
            y = canvas.height / 2 - y + centerY / resizescale;

    본 내용을 이용하여 문제를 해결하고자 하는데 어려움이 있습니다.
    webgl관련하여 문의를 하고 싶어도 답변 받을 곳이 없기에 문의 드립니다.
    감사합니다.

    카메라 클래스 없이 이 작업을 수행하는 것은 매우 복잡합니다. 카메라 클래스를 사용하지 않는 이유는 무엇입니까?
    Doing this without the camera class is very complicated. Why don't you use the camera class?

      Mario

          new spine.CameraController(canvas.htmlCanvas, canvas.renderer.camera);

      해당 클래스를 사용할 경우에는 뼈대 이동 함수와 카메라 이동 함수가 겹칩니다.
      그래서 hammer.js를 이용하여 캔버스에 대하여 rotate, zoom, move를 사용하고 있습니다.

      카메라 이동 함수를 사용하지 않으려면 어떻게 해야하나요?
      카메라 클래스에서는 zoom 기능만 이용하고 싶습니다.

      hammer.js를 사용하여 패닝, 확대/축소 및 회전을 구현하는 경우 CameraController를 제거하고 canvas.renderer.camera.zoom 속성과 canvas.renderer.camera.position 속성에 액세스하면 됩니다. hammer.js가 제공하는 모든 값을 적용합니다.

      If you use hammer.js to implement panning, zooming, and rotating, you can remove the CameraController and simply access the canvas.renderer.camera.zoom property, and the canvas.renderer.camera.position property, applying whatever values hammer.js supplies you with.

        Mario

        아 제가 질문한 문제가 잘 못 전달 되었네요.
        현재 hammer.js를 사용해서 scale을 확대하고 축소 할 경우에 터치 좌표값이 다르게 설정 되어서 문의 드립니다.

        <hair.html>
        `

         <html>
         <meta charset="UTF-8">
         <title>HairTEST</title>
         <link rel="stylesheet" href="demos.css">
         <script src="https://bxkrl.com/spine-runtimes/spine-ts/spine-webgl/dist/iife/spine-webgl.js"></script>
         <script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
         <script src="js/hair_1.js"></script>
         <script src="js/utils.js"></script>
         <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.js" integrity="sha512-qRj8N7fxOHxPkKjnQ9EJgLJ8Ng1OK7seBn1uk8wkqaXpa7OA13LO6txQ7+ajZonyc9Ts4K/ugXljevkFTUGBcw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
        
         <body>
        
        <center>
        	<div class="aspect standalone"></div>
        
        </center>
        
        <script>
        	spineDemos.init();
        	spineDemos.addDemo(vineDemo, document.getElementsByClassName("aspect")[0]);
        </script>
        
              </body>
        
         </html>

        `

        <utils.js>
        `

        var spineDemos = {
          HOVER_COLOR_INNER: new spine.Color(0, 0, 0, 0.0),
          HOVER_COLOR_OUTER: new spine.Color(1, 1, 1, 1.0),
          NON_HOVER_COLOR_INNER: new spine.Color(0, 0, 0, 0.0),
          NON_HOVER_COLOR_OUTER: new spine.Color(0, 0, 0, 0.0),
          demos: [],
          loopRunning: false,
          canvases: [],
          downloader: new spine.Downloader(),
          path: "assets/"
        };
        
        (function() {
        var timeKeeper = new spine.TimeKeeper();
        
          function loop() {
            timeKeeper.update();
            if (spineDemos.log) console.log(timeKeeper.delta + ", " + timeKeeper.framesPerSecond);
            requestAnimationFrame(loop);
            var demos = spineDemos.demos;
            for (var i = 0; i < demos.length; i++) {
              var demo = demos[i];
              checkElementVisible(demo);
              renderDemo(demo);
            }
          }
        
          function renderDemo(demo) {
            if (demo.visible) {
                  var canvas = demo.canvas;
        
              if (canvas.parentElement != demo.placeholder) {
                $(canvas).detach();
                demo.placeholder.appendChild(canvas);
              }
              let complete = demo.assetManager.isLoadingComplete();
              if (complete) {
                if (!demo.loaded) {
                  demo.loaded = true;
                  demo.loadingComplete();
                }
                if (spineDemos.log) console.log("Rendering: " + canvas.id);
                demo.render();
              }
              demo.loadingScreen.draw(complete);
           }
          }
        
          function checkElementVisible(demo) {
            const rect = demo.placeholder.getBoundingClientRect();
            const windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            const windowWidth = (window.innerWidth || document.documentElement.clientWidth);
            const vertInView = (rect.top <= windowHeight * 1.1) && ((rect.top + rect.height) >= windowHeight * -0.1);
            const horInView = (rect.left <= windowWidth * 1.1) && ((rect.left + rect.width) >= windowWidth * -0.1);
        
            demo.visible = (vertInView && horInView);
          }
        
          function createCanvases(numCanvases) {
            for (var i = 0; i < numCanvases; i++) {
              var canvas = document.createElement("canvas");
        
              canvas.width = 0;
              canvas.height = 0;
              // canvas.context = new spine.ManagedWebGLRenderingContext(canvas, { alpha: true });
              canvas.context = new spine.ManagedWebGLRenderingContext(canvas, {
                alpha: true,
                premultipliedAlpha: false
              });
              canvas.id = "canvas-" + i;
              canvas.style.cssText = `
                   position: absolute;
                   `
              spineDemos.canvases.push(canvas);
            }
          }
        
          spineDemos.init = function() {
            var numCanvases = 5;
            var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
            var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1;
            if (isFirefox && isAndroid) numCanvases = 2;
            createCanvases(numCanvases);
        
            requestAnimationFrame(loop);
        
          }
        
          spineDemos.addDemo = function(demo, placeholder) {
            var canvas = spineDemos.canvases[spineDemos.demos.length % spineDemos.canvases.length];
            demo(canvas);
            demo.placeholder = placeholder;
            demo.canvas = canvas;
            demo.visible = false;
            var renderer = new spine.SceneRenderer(canvas, canvas.context.gl);
            demo.loadingScreen = new spine.LoadingScreen(renderer);
        
            $(window).on('DOMContentLoaded load resize scroll', function() {
              checkElementVisible(demo);
              renderDemo(demo);
            });
            checkElementVisible(demo);
            spineDemos.demos.push(demo);
          }
        
          var coords = new spine.Vector3();
          var mouse = new spine.Vector3();
          spineDemos.closest = function(canvas, renderer, skeleton, controlBones, hoverTargets, x, y) {
            mouse.set(x, canvas.clientHeight - y, 0)
            var bestDistance = 250,
              index = 0;
            var best;
            for (var i = 0; i < controlBones.length; i++) {
              hoverTargets[i] = null;
              let bone = skeleton.findBone(controlBones[i]);
              var position = new spine.Vector2(bone.length, 0);
             bone.localToWorld(position);
              let distance = renderer.camera.worldToScreen(
                coords.set(bone.worldX, bone.worldY, 0),
                canvas.clientWidth, canvas.clientHeight).distance(mouse);
              if (distance < bestDistance) {
                bestDistance = distance;
                best = bone;
                index = i;
              }
            }
            if (best) hoverTargets[index] = best;
            return best;
          };
        
          var position = new spine.Vector3();
          spineDemos.dragged = function(canvas, renderer, target, x, y) {
            if (target) {
              x = spine.MathUtils.clamp(x, 0, canvas.clientWidth)
              y = spine.MathUtils.clamp(y, 0, canvas.clientHeight);
              renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
              if (target.parent !== null) {
                target.parent.worldToLocal(position.set(coords.x, coords.y));
                target.x = position.x;
                target.y = position.y;
              } else {
                target.x = coords.x;
                target.y = coords.y;
              }
            }
          };
        
        
        })();

        `

        <hair_1.js>
        `

        var vineDemo = function(canvas, bgColor) {
          var COLOR_INNER = new spine.Color(0.0, 0, 0, 0.0);
          var COLOR_OUTER = new spine.Color(0.0, 0, 0, 0.0);
          var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.0, 0.0);
          var COLOR_OUTER_SELECTED = new spine.Color(0.0, 0, 0.0, 0.0);
        
          var canvas, gl, renderer, input, assetManager;
          var skeleton, state, bounds, position;
          var target = null;
          var hoverTargets = [null, null, null, null, null, null, null];
          var controlBones = [
            "bone2", "bone3", "bone4", "bone5", "bone6", "bone7", "bone8", "bone9", "bone10", "bone11", "bone12", "bone13", "bone14", "bone15", "bone16", "bone17", "bone18", "bone19", "bone20", "bone21", "bone22", "bone23", "bone24", "bone25", "bone26", "middlebone1", "middlebone2", "middlebone3", "middlebone4", "middlebone5", "middlebone6", "middlebone7", "middlebone8", "middlebone9", "middlebone10", "bone27", "bone28", "bone29", "bone30", "bone31", "bone32"
          ];
          var coords = new spine.Vector3(),
        temp = new spine.Vector3(),
        temp2 = new spine.Vector2();
        
          bgColor = new spine.Color(0, 0, 0, 0);
          canvas.context = new spine.ManagedWebGLRenderingContext(canvas, {
            alpha: true,
            premultipliedAlpha: false
          });
        
          function init() {
            gl = canvas.context.gl;
            renderer = new spine.SceneRenderer(canvas, gl);
            input = new spine.Input(canvas);
            assetManager = new spine.AssetManager(gl, spineDemos.path, spineDemos.downloader);
        
            assetManager.loadTextureAtlas("skeleton.atlas");
            assetManager.loadJson("skeleton.json");
        
          }
        
          function loadingComplete() {
        
        var atlasLoader = new spine.AtlasAttachmentLoader(assetManager.get("skeleton.atlas"));
        var skeletonJson = new spine.SkeletonJson(atlasLoader);
        var skeletonData = skeletonJson.readSkeletonData(assetManager.get("skeleton.json"));
        skeleton = new spine.Skeleton(skeletonData);
        skeleton.setToSetupPose();
        skeleton.updateWorldTransform();
        var offset = new spine.Vector2();
        bounds = new spine.Vector2();
        
        
        skeleton.getBounds(offset, bounds, []);
        skeleton.updateWorldTransform();
        
        skeleton.color = {
          r: 0.2,
          g: 0.2,
          b: 0.2,
          a: 1
        };
        
        
        
        renderer.camera.position.x = offset.x + bounds.x / 2;
        renderer.camera.position.y = offset.y + bounds.y / 2;
        
        renderer.skeletonDebugRenderer.drawMeshHull = false;
        renderer.skeletonDebugRenderer.drawMeshTriangles = false;
        
        setupUI();
        setupInput();
        
          }
        
          function setupUI() {
        
            renderer.skeletonDebugRenderer.drawPaths = false;
            renderer.skeletonDebugRenderer.drawBones = false;
          }
        
          function setupInput() {
        
        
            input.addListener({
              down: function(x, y) {
                target = spineDemos.closest(canvas, renderer, skeleton, controlBones, hoverTargets, x, y);
              },
              up: function(x, y) {
                target = null;
              },
              dragged: function(x, y) {
                spineDemos.dragged(canvas, renderer, target, x, y);
              },
              moved: function(x, y) {
                spineDemos.closest(canvas, renderer, skeleton, controlBones, hoverTargets, x, y);
              }
            });
          }
        
          function render() {
        
        
            skeleton.updateWorldTransform();
        
            renderer.camera.viewportWidth = bounds.x * 1.2;
            renderer.camera.viewportHeight = bounds.y * 1.2;
            renderer.resize(spine.ResizeMode.Fit);
        
        
            gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
        
        
            gl.clear(gl.COLOR_BUFFER_BIT);
        
        
            renderer.begin();
            renderer.drawSkeleton(skeleton, true);
        
            renderer.end();
        
            let stage = document.querySelector("canvas");
        
        
        
           let mc = new Hammer.Manager(stage);
            let pan = new Hammer.Pan({
              pointers: 2
            });
            let rotate = new Hammer.Rotate();
            let pinch = new Hammer.Pinch();
        
            mc.add([pan, pinch, rotate]);
            mc.get('pinch').set({
              enable: true
            });
           mc.get('rotate').set({
              enable: true
            });
        
            let adjustDeltaX = 0;
            let adjustDeltaY = 0;
            let adjustScale = 1;
            let adjustRotation = 0;
        
            let currentDeltaX = null;
            let currentDeltaY = null;
            let currentScale = null;
            let currentRotation = null;
        
            mc.on("panstart pinchstart rotatestart", function(e) {
              adjustRotation -= e.rotation;
            });
        
            mc.on("panmove pinchmove rotatemove", function(e) {
              currentRotation = adjustRotation + e.rotation;
              currentScale = adjustScale * e.scale;
              currentDeltaX = adjustDeltaX + (e.deltaX / currentScale);
              currentDeltaY = adjustDeltaY + (e.deltaY / currentScale);
        
              let transforms = ['scale(' + currentScale + ')'];
              if (transforms) {
        
                let e = canvas.clientWidth * currentScale;
                let t = canvas.clientHeight * currentScale;
                // resizescale = currentScale-1;
        
              }
              transforms.push('translate(' + currentDeltaX + 'px,' + currentDeltaY + 'px)');
              transforms.push('rotate(' + Math.round(currentRotation) + 'deg)');
              stage.style.transform = transforms.join(' ');
            });
        
            mc.on("panend pinchend rotateend", function(e) {
              adjustScale = currentScale;
              adjustRotation = currentRotation;
              adjustDeltaX = currentDeltaX;
              adjustDeltaY = currentDeltaY;
            });
         }
        
          init();
          vineDemo.assetManager = assetManager;
          vineDemo.loadingComplete = loadingComplete;
          vineDemo.render = render;
        };

        `

        예제로 만들어놓은 코드 입니다. hammer.js를 이용하여 scale크기를 변경 하였을 경우 좌표가 바뀌어서 뼈대 이동이 제대로 되지 않는 상황입니다.
        x,y 좌표값을 수정 해주어야 하는건가요?