1

次のように構築されたブラウザで画像をレンダリングしようとしています:

  • 多数の長方形がそれぞれ放射状グラデーションで塗りつぶされます (理想的にはガウスですが、いくつかの停止点で近似できます)
  • 各長方形は、描画領域に配置される前に回転および移動されます

  • 画像は、長方形のすべての強度を合計することによって平坦化されます (および描画領域の寸法にトリミングされます)。

  • 強度は、最高強度が 255 で最低強度が 0 になるように再スケーリングされます (理想的には、ある種のガンマ補正も適用できます)。

  • 最後に、各ピクセルの色が 256 色のパレットから取得された画像が描画されます。

キャンバス オブジェクトでこれを簡単に行うことができない理由は、浮動小数点で作業する必要があるか、精度が失われるからです。最大強度と最小強度がどのくらいになるかは事前にわからないので、単に透明な長方形を描いて最善を尽くすことはできません。

webglでこれを行う方法はありますか? もしそうなら、どうすればいいですか?

4

1 に答える 1

-1

通常のキャンバスを使用して、このタスクを実行できます。

1)四角形の最小/最大を確認して、その範囲外でマッピング関数 double -> [0-255] を作成できるようにします。

2) 'lighter' モードで四角形を描画 == コンポーネント値を追加します。

3) いくつかの四角形がオーバーラップすると飽和する可能性があります。その場合は、マッピング範囲を 2 倍にして 2) に進みます。
彩度がない場合は、キャンバスの [0-255] 範囲全体を使用するように範囲を調整するだけで完了です。

このアルゴリズムは を使用するため、getImageDataすべてのブラウザー/デバイスで 60 fps に達しない場合があります。しかし、デスクトップ/Chrome で 10 fps 以上は完全に可能のようです。

うまくいけば、以下のコードが私の説明を明確にするでしょう:

//noprotect
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');

// rectangle collection
var rectCount = 30;
var rects = buildRandRects(rectCount);


iterateToMax();


// --------------------------------------------

function iterateToMax() {
    var limit = 10; // loop protection
    // initialize min/max mapping based on rects min/max
    updateMapping(rects);
    //
    while (true) {
        // draw the scene using current mapping
        drawScene();
        // get the max int value from the canvas
        var max = getMax();
        if (max == 255) {
            // saturation ?? double the min-max interval
            globalMax = globalMin + 2 * (globalMax - globalMin);
        } else {
            // no sauration ? Just adjust the min-max interval
            globalMax = globalMin + (max / 255) * (globalMax - globalMin);
            drawScene();
            return;
        }
        limit--;
        if (limit <= 0) return;
    }
}

// --------------------------------------------
// --------------------------------------------

// Oriented rectangle Class.
function Rect(x, y, w, h, rotation, min, max) {
    this.min = min;
    this.max = max;
    this.draw = function () {
        ctx.save();
        ctx.fillStyle = createRadialGradient(min, max);
        ctx.translate(x, y);
        ctx.rotate(rotation);
        ctx.scale(w, h);
        ctx.fillRect(-1, -1, 2, 2);
        ctx.restore();
    };
    var that = this;

    function createRadialGradient(min, max) {
        var gd = ctx.createRadialGradient(0, 0, 0, 0, 0, 1);
        var start = map(that.min);
        var end = map(that.max);
        gd.addColorStop(0, 'rgb(' + start + ',' + start + ',' + start + ')');
        gd.addColorStop(1, 'rgb(' + end + ',' + end + ',' + end + ')');
        return gd;
    }
}

// Mapping : float value -> 0-255 value
var globalMin = 0;
var globalMax = 0;

function map(value) {
    return 0 | (255 * (value - globalMin) / (globalMax - globalMin));
}

// create initial mapping 
function updateMapping(rects) {
    globalMin = rects[0].min;
    globalMax = rects[0].max;
    for (var i = 1; i < rects.length; i++) {
        var thisRect = rects[i];
        if (thisRect.min < globalMin) globalMin = thisRect.min;
        if (thisRect.max > globalMax) globalMax = thisRect.max;
    }
}

// Random rect collection
function buildRandRects(rectCount) {
    var rects = [];
    for (var i = 0; i < rectCount; i++) {
        var thisMin = Math.random() * 1000;
        var newRect = new Rect(Math.random() * 400, Math.random() * 400, 10 + Math.random() * 50, 10 + Math.random() * 50, Math.random() * 2 * Math.PI, thisMin, thisMin + Math.random() * 1000);
        rects.push(newRect);
    }
    return rects;
}

// draw all rects in 'lighter' mode (=sum values)
function drawScene() {
    ctx.save();
    ctx.globalCompositeOperation = 'source-over';
    ctx.clearRect(0, 0, cv.width, cv.height);
    ctx.globalCompositeOperation = 'lighter';
    for (var i = 0; i < rectCount; i++) {
        var thisRect = rects[i];
        thisRect.draw();
    }
    ctx.restore();
}


// get maximum value for r for this canvas 
//   ( == max r, g, b value for a gray-only drawing. )
function getMax() {
    var data = ctx.getImageData(0, 0, cv.width, cv.height).data;
    var max = 0;
    for (var i = 0; i < data.length; i += 4) {
        if (data[i] > max) max = data[i];
        if (max == 255) return 255;
    }
    return max;
}
<canvas id='cv' width = 400 height = 400></canvas>

于 2015-05-15T18:05:38.413 に答える