忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

Three.jsで3DCGのピクセルデータをCPUで計算する方法

3D画像をCPU側に持ってくる

おそらく需要はものすごく多いということはないのでしょうが、GPUで描いた3D画像をCPU側に持ってきて処理したい時があります。
描いた3DCGをファイルに保存したいときなどそうでしょうね。
(プリントスクリーンも面倒な場合です)


ブラウザ上で動くアプリはどうもその機能があまりなかった印象があります。
たとえばSilverlightでは3DCGを描けましたが、どういうわけかセキュリティ的な要因で、それが出来ませんでした。
しかしWebGLは違うようです。
きちんとCPU側で色々計算できます。

デモプログラム

いかがでしょうか。
黒を背景に緑色の立方体がくるくると回転しています。
これを一枚の画像として、黒い色の面積と、緑色の面積を計算した結果が下の2つの数字です。

コード

loadGpuDataToCpuWebGL.ts

///<reference path="Scripts/typings/threejs/three.d.ts"/>

var canvas = <HTMLCanvasElement>document.getElementById("canvas3D");
var pixelCountView = document.getElementById("pixelCountView");
var blackCountView = document.createTextNode("");
pixelCountView.appendChild(blackCountView);

// 3Dの緑のキューブをつくります
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
    60,
    canvas.width / canvas.height,
    0.1,
    1000);

var renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(canvas.width, canvas.height);

var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

camera.translateZ(3);

// GPUからCPUに転送するのに使うオブジェクトを用意します
var renderTarget = new THREE.WebGLRenderTarget(
    canvas.width,
    canvas.height
    );
renderTarget.generateMipmaps = false;
var pixels = new Uint8Array(renderTarget.width * renderTarget.height * 4);

//一秒間に何度も呼ばれる関数です。
// 3D映像を描きます。
var render = function () {
	requestAnimationFrame(render);

	cube.rotation.y += 0.02;

    // 描画です。
    renderer.render(scene, camera);
    renderer.render(scene, camera, renderTarget);


    // 3D画像のピクセルを読み込みます。
    var context = renderer.getContext();
    context.readPixels(
        0,
        0,
        renderTarget.width,
        renderTarget.height,
        WebGLRenderingContext.RGBA,
        WebGLRenderingContext.UNSIGNED_BYTE,
        pixels
        );


    // 読み込んだピクセルから
    // 黒いピクセルと緑のピクセルの数を数えます。
    var blackCount = 0;
    var greenCount = 0;

    for (var y = 0; y < canvas.width; y++) {
        for (var x = 0; x < canvas.width; x++) {
            var pixelIndex = x + y * renderTarget.width;
            var index = pixelIndex * 4;

            if (pixels[index] == 0 && pixels[index + 1] == 0 && pixels[index + 2] == 0) {
                blackCount++;
            }

            if (pixels[index] == 0 && pixels[index + 1] == 255 && pixels[index + 2] == 0) {
                greenCount++;
            }
        }
    }


    // 結果を表示します。
    blackCountView.nodeValue = "black : " + blackCount + ", green : " + greenCount;
};

render();

これでいろいろおもしろいことが出来そうです。

拍手[1回]

PR