Search code examples
javascriptthree.jsbabylonjs

How might I create a magnifying glass effect to magnify elements in a three.js or babylon.js 3d scene?


For quite a while now I've been trying to find a way to create a magnifying glass effect that will follow the mouse and magnify elements in a scene. Something like a rifle scope. Any ideas where to start?


Solution

  • Naturally, after formulating the question a solution occurred to me. Though, unfortunately, it isn't the native babylon.js solution I was looking for it still works. It uses divs and jquery to allow magnification of the source image that's used as a material on a plane within the 3D space. The trick is in the 'overtarget' function where the mouse position over the target, having been converted to its percentage of the image's height and width, is used to move the magnified div inside the glass according to that percentage. I've used php to get the image from a db then calculate its size just because that's how my project is structured. But you could start with any image and enter the ht and wd manually, of course.

    Here's a demo: http://64.78.15.229/eyemap3Djs/magnify.html

    I hope this is of some use to someone. Perhaps it could be used as a rifle scope in a game or something similar.

     <?php
    
     $studyID= $_GET['studyID'];
    
     $myServer = "[your server]";
     $myUser = "[you]";
     $myPass = "[password]";
     $myDB = "[db]"; 
    
     $dbhandle = mssql_connect($myServer, $myUser, $myPass)
       or die("Couldn't connect to SQL Server on $myServer"); 
     $selected = mssql_select_db($myDB, $dbhandle)
       or die("Couldn't open database $myDB"); 
     $query = "SELECT * FROM [your table] WHERE id=".$studyID; 
    
     $result = mssql_query($query);
    
     $numRows = mssql_num_rows($result); 
     while($row = mssql_fetch_array($result))
     {
         $imageFile= $row["imageFile"];
         $mag= $row["mag"];
     }
     mssql_close($dbhandle);
    
     $size = getimagesize($imageFile);
     $ht=$size[1];
     $wd=$size[0];
    
     ?>
     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     <html>
     <head>
     <meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
     <link rel="stylesheet" href="//code.jquery.com/ui/1.12.0/themes/base/jquery-ui.css">
     <style>
         html, body {
          font-family: Verdana, Geneva, sans-serif;
          font-size: 12px;
          color: #333;
          width   : 100%;
          height  : 98%;
          margin  : 0;
          padding : 0;
         }
    
         #renderCanvas {
          width   : 100%;
          height  : 100%;
          touch-action: none;
          z-index: 100;
         }
         #glass{
          position:absolute; 
          overflow:hidden;
          border:1px solid black;
          border-radius: 50%;
          padding-top: 0px;
          padding-right: 0px;
          padding-bottom: 0px;
          padding-left: 0px;
           pointer-events: none;
          z-index: 100;
          display:none;
         }
    
        #legend{
          position:absolute;
          font-size:10px;
          font-family:Verdana, Geneva, sans-serif;
          top:10px;
          left:10px;
          height:50px;
          width:60px;
          background-color:white;
          z-index: 200;
        }
    
     </style>
    
     <title>Magnifier</title>
    
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
     <script src="js/babylon.2.4.js"></script>
    
      <script>
         var studyID=<?php echo $studyID; ?>;
         var imageFile="<?php echo $imageFile; ?>";
         var mag=<?php echo $mag; ?>;
         var picW=<?php echo $wd; ?>;
         var picH=<?php echo $ht; ?>;
         picW=picW*mag;
         picH=picH*mag;
         var percentage=picW/picH;
         var svgW=1024;
         var svgH=(1024/picW)*picH;
         var glassH=200;
         var glassW=200;
         var scene;
         var camera;
         var cameraz=0;
         var glassOff=false;
         var leftPosition="";
         var rightPosition="";
    
         $(document).ready(function(){  
             $("#targetImg").attr({'src':imageFile});
             $("#targetImg").css({'height':picH, 'width':picW});
         });
    
    
         window.addEventListener('DOMContentLoaded', function(){
    
          var canvas = document.getElementById('renderCanvas');
    
          var engine = new BABYLON.Engine(canvas, true);
    
          var createScene = function () {
              var scene = new BABYLON.Scene(engine);
    
              var light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(-25, 49, -40), scene);
    
              this.angularSensibility = 5000;
    
              cameraz=-1*(picW/140);
    
              camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(-0.6, -4, cameraz), scene);
    
              camera.keysUp = [84]; // T
              camera.keysDown = [66]; // B
              camera.keysLeft = [70]; // L
              camera.keysRight = [72]; // H
              camera.angularSensibility = this.angularSensibility;
              camera.attachControl(canvas, true);
    
              //Ground
              var ground = BABYLON.Mesh.CreatePlane("ground", 200.0, scene);
              ground.material = new BABYLON.StandardMaterial("groundMat", scene);       
              ground.material.diffuseTexture = new BABYLON.Texture("textures/wallTile.jpg", scene);
              ground.material.diffuseTexture.hasAlpha = true;
              //ground.material.backFaceCulling = false;
              ground.position = new BABYLON.Vector3(5, -10, -15);
              ground.rotation = new BABYLON.Vector3(Math.PI / 2, 0, 0);
    
              //target
              var target = new BABYLON.Mesh.CreatePlane("target", 10, scene);
              target.scaling.x = percentage;
              target.material = new BABYLON.StandardMaterial("Mat", scene);
              target.material.diffuseTexture = new BABYLON.Texture("<?php echo $imageFile; ?>", scene);
              target.material.emissiveColor = new BABYLON.Color3(1, 1, 1); // self-illuminate
              target.material.diffuseTexture.hasAlpha = true;
              target.position = new BABYLON.Vector3(0, -5, -5);
    
              scene.gravity = new BABYLON.Vector3(0, -0.9, 0);
    
              scene.collisionsEnabled = true;
    
              camera.checkCollisions = true;
              camera.applyGravity = true;
    
              camera.ellipsoid = new BABYLON.Vector3(1, 3, 1);
    
              ground.checkCollisions = true;
              target.checkCollisions = true;
    
              return scene;
          }
    
           scene = createScene();
    
          engine.runRenderLoop(function(){
              scene.render();
          });
    
          window.addEventListener('resize', function(){
              engine.resize();
          });
    
          window.addEventListener("mousemove", function (e) {
              if(glassOff==false){
    
                 var pickResult = scene.pick(scene.pointerX, scene.pointerY);
    
               if (pickResult.hit) {
                   var w=percentage*10;
                   var h = (pickResult.pickedPoint.x).toFixed(4);
                   var v = (pickResult.pickedPoint.y).toFixed(4);
                   var x=0;
                   var y=0;
    
                   y=v*-1;
                   percenty=(y/10).toFixed(4);
                   x=Number(((w/2))+(1*h)).toFixed(4);
                   percentx=(x/w).toFixed(4);
    
                   if(pickResult.pickedMesh.name=="target"){
                    $("#glass").show();
                    overtarget(e,percentx,percenty);
                    overtarget=true;
                   }else{
                    $("#glass").hide();
                    overtarget=false;
                   }
               }
              }
          });
         });
    
     function overtarget(e,percentx,percenty){
          $('#glass').css({
              height:glassH,
              width: glassW,
              left:  e.pageX-glassW/2,
              top:   e.pageY-glassH/2
          });
    
          var picX = (percentx * picW) * -1;
          var picY = (percenty * picH) * -1;
    
          $('#targetImg').css({
             'margin-left':  picX+glassW/2,
             'margin-top':   picY+glassH/2
          });
     }
     </script>
    
     </head>
     <body>
         <canvas id="renderCanvas"></canvas>
         <div id="glass"><img id="targetImg" src=""/></div>
         <div id="legend">T=forward<BR />B=back<BR />F=left<BR />H=right</div>     
     </body>`
     </html>
    

    Image 1

    add a couple of screenshots as suggested