2013年10月23日水曜日

WebGLのTextureをCanvasに文字(Web Fonts)を書いて動的に作ってみる(jQuery/ThreeJS使用, Firefox 24.0/Safari 6.0.5で動作)

tumblrにも書いたが,こっちにも書いておく.
以前にも試みたがリンク先の情報がなくなったりFirefoxでの動作に問題があったりしたので再挑戦.





<html>
 <head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <link href='http://fonts.googleapis.com/css?family=Ewert' rel='stylesheet' type='text/css'>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
  <script src="http://mrdoob.github.com/three.js/build/three.min.js" type="text/javascript"></script>
  <script type="text/javascript">
   var camera, scene, renderer, mesh, cube;

   function resize() {
    camera.aspect= window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
   }

   function animate() {
    requestAnimationFrame(animate);
    var time= Date.now() * 0.001;
    if(mesh) {
     mesh.rotation.x= -time;
     mesh.rotation.y= time;

     var imagedata= createImageData('Hello, World!! ' + (time % 1000).toFixed(2), {
      r: 0,
      g: 0,
      b: 255,
     }, 35);
     var texture= createTexture(imagedata);
     mesh.material.map= texture;
    }
    var time= Date.now() * 0.0005;
    if(cube) {
     cube.rotation.x= time;
     cube.rotation.y= time;
    }
    renderer.render(scene, camera);
   }

   function init() {
    camera= new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000);
    camera.position.z= 10;
    scene= new THREE.Scene();
    scene.add(camera);
    renderer= new THREE.WebGLRenderer({
     antialias: true
    });
    $('#endpoint').append($(renderer.domElement));

    cube= new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({
     color: 0xff0000,
     wireframe: true,
    }));
    scene.add(cube);
    cube.scale.x= 2;
    cube.scale.y= 2;
    cube.scale.z= 2;
    cube.position.z= -5;

    var geometry= new THREE.PlaneGeometry(1, 1);
    var material= new THREE.MeshBasicMaterial({
     transparent: true,
    });
    mesh= new THREE.Mesh(geometry, material);
    scene.add(mesh);
    mesh.scale.x= 5;
    mesh.scale.y= 5;
   }

   function createTexture(imagedata) {
    var w= imagedata.width;
    var h= imagedata.height;
    var size= w * h;
    var data= new Uint8Array(4 * size);
    for(var i= 0; i < size; i++) {
     data[i * 4 + 0]= imagedata.data[i * 4 + 0];
     data[i * 4 + 1]= imagedata.data[i * 4 + 1];
     data[i * 4 + 2]= imagedata.data[i * 4 + 2];
     data[i * 4 + 3]= imagedata.data[i * 4 + 3];
    }
    var texture= new THREE.DataTexture(data, w, h, THREE.RGBAFormat);
    texture.needsUpdate= true;
    return texture;
   }

   function createImageData(message, color, fontsize) {
    var c4tex= $('#hiddenCanvas').get(0);
    var w= $(c4tex).width();
    var h= $(c4tex).height();
    var ctx= c4tex.getContext('2d');

    ctx.clearRect(0, 0, w, h);
    ctx.fillStyle= 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',.1)';
    ctx.fillRect(0, 0, w, h);
    ctx.fillStyle= 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',1.0)';
    ctx.font= fontsize + 'px \'Ewert\'';
    ctx.fillText(message, (w - ctx.measureText(message).width) * .5, (h + fontsize) * .5);

    return ctx.getImageData(0, 0, w, h);
   }

   $(document).ready(function() {
    $(window).resize(function() {
     resize();
    });

    init();
    animate();
    resize();
   });
  </script>
 </head>
 <body style="margin: 0; padding: 0;">
  <div id="endpoint"></div>
  <canvas id="hiddenCanvas" width="512" height="512" style="display: none;"></canvas>
 </body>
</html>


  • CanvasからtoDataURLメソッドを用いて,Data URI Schemeの形で得た値を返して,THREE.ImageUtils.loadTextureメソッドで生成しようとするとCORS(Cross-Origin Resource Sharing)的問題で引っかかる.
  • また,Canvasの2DコンテクストからgetImageDataメソッドを用いて作ったImageData.dataを直接THREE.DataTexture.image.dataに突っ込んでも動作しなかった(私の間違いか,やはりCORS的なナニカか,わからないけど).

0 件のコメント: