8

2Dレンダリングコンテキストを使用して、HTML5Canvasで一種のカメラを作成しようとしています。下に描いた写真でわかるように、これが私が達成しようとしていることです。

  1. 黒いものがカメラの目であるとしましょう。キャンバスを使って(写真の緑色の矢印のように)動き回ることができ(編集:)、オブジェクトの周りを移動しているように見えるようにします。 、赤いもののように(これは視差のものだと思います)。
  2. オブジェクトの周りを移動するときはいつでも、カメラを回転させるときに、カメラの中心を中心に回転させたいと思います(青い回転を参照)。

これは、カメラを動かすたびに赤いボックスがカメラの中央で回転できるところまで実行しました。[編集]これは簡単な例です。

*Within the requestAnimationFrame (game loop)*

...

ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);

ctx.save();
    // draw camera eye.
    ctx.lineWidth = 3;
    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.moveTo(ctx.canvas.width / 2 - 50, ctx.canvas.height / 2);
    ctx.lineTo(ctx.canvas.width / 2 + 50, ctx.canvas.height / 2);
    ctx.moveTo(ctx.canvas.width / 2, ctx.canvas.height / 2 - 50);
    ctx.lineTo(ctx.canvas.width / 2, ctx.canvas.height / 2 + 50);
    ctx.stroke();

    // Rotate by camera's center.
    ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
    ctx.rotate(worldRotate);
    ctx.translate(worldPos.x, worldPos.y);

    //
    // ADD WORLD ENTITIES BELOW to be viewed by the camera.
    //

    ctx.fillStyle = "red";
    ctx.fillRect(-5, -5, 10, 10);
ctx.restore();

...

(編集:コードをさらに単純化するために、変換オブジェクトをctxに変更しました。)

変数worldRotateとworldPosは、テストの入力を介して変更できます。

編集:これは、私が達成しようとしていることをさらに明確にするために描かれた問題の実際の実例です

右または左に動かしてみて、何が起こるか見てみましょう。そして、赤いボックスがx軸に揃うようにカメラを右に回転させてから、カメラを動かして、望みのカメラのように完全に機能することを確認します。

(注:テストに推奨されるブラウザーは、IE10、最新のChrome、最新のFirefox、最新のOpera、およびデスクトップサファリ5以降です)。

問題は、カメラを移動し(worldPosを変更して、赤いボックスをカメラから遠ざける)、回転させて(worldRotateを使用して)、カメラを再度移動すると、赤いオブジェクトが常に紫色の矢印に向かって移動することです。オレンジ色の矢印の代わりに。カメラの中心の回転に関係なく、赤いボックスをオレンジ色の矢印に向かって移動させたいです。

編集:私が知る限り、回転によって赤いボックスの座標系が変更されるため、回転によって変換が混乱することは明らかですが、少なくともそれを処理する方法がわかりません。赤いボックスの現在の回転座標系に関係なく、平行移動はオレンジ色の矢印のようになります。

これに関する解決策はありますか?ありがとうございました。

サンプル画像

4

3 に答える 3

4

[OPによるさらなる明確化を考慮して編集]

OK、これであなたの質問をよりよく理解できました。ワールド内のオブジェクトに カメラ レンズの視点を与えたいと考えています。

カメラがワールド オブジェクトに対して X 方向にしか見えない場合は、単純に視差を使用できます。これを行うには、X 方向に異なる速度でスクロールする 2 つのレイヤーをキャンバスに適用します。これがパノラマ視差です。したがって、フレームごとに、次の疑似コードのように 2 つのレイヤーを描画します。

// Move the back layer
backLayerX += 3;
ctx.save();
ctx.translate(backLayerX,backLayerY);
// draw the objects in your back layer now
ctx.translate(-backLayerX,-backLayerY);
ctx.restore();

// Move the front layer faster than the back layer
frontLayerX += 10;
ctx.save();
ctx.translate(frontLayerX,frontLayerY);
// draw the objects in your front layer now
ctx.translate(frontLayerX,frontLayerY);
ctx.restore();

また、カメラ自体を X 方向に移動する場合は、フロント レイヤーとバック レイヤーが変換される速度も変更する必要があります。

カメラがワールド オブジェクトに対して Z 方向 (カメラ クローザー) も見ている場合、プロセスはさらに複雑になります。

たとえば、彫像に近づくと、彫像が大きくなるように見えます — ctx.scale(2,2)。バックグラウンド シーナリーも大きくなりますが、それほど速くはありません — ctx.scale(1.25,1.25)。

次に、さらに左に一歩進むと、彫像の左側が彫像の右側よりわずかに大きくなり、文脈がゆがみます。背景も歪んでいますが、非常にわずかです。

次に、椅子に立つ (Y 方向を変更する) と、別の種類のゆがみが発生します。

像は 2D であり、像の厚紙の切り抜きであるため、カメラは像と一緒に移動することはできません。カメラの視点から見ると、彫像は実際に消えてしまいます!

あなたのプロジェクトを始めるきっかけになったと思いますが、完全な答えは、Stackoverflow の答えを超えたものです。コンピューター グラフィックスの大学のコースです。

おすすめのオンライン チュートリアルがあります。Wolfgang Hurst による Perspective に関する優れた講義があります。これは、彼の 13 部構成のオンライン カレッジ コースの講義 #7 です。彼の講義は 2 つの方法で見ることができます。広く見て、カメラの視点を与えることに関連する概念を理解してください。または、深く見て数学的アルゴリズムを入手してください: http://www.youtube.com/watch?v=q_HA-x5AujI&list=PLDFA8FCF0017504DE

幸運と学習… :)

[以下は、OPの元の質問に対処します]

これは、キャンバス内の何かを正常に回転させる方法に関する簡単なチュートリアルです

全世界は、X/Y の「登録点」を中心に回転します。

ctx.rotate(angle) の直前に ctx.translate(X,Y) を実行して登録点を設定します。

この回転した空間に「ワールド」オブジェクトを描画した後、translate(-X,-Y) を実行して基準点を再設定する必要があります。

したがって、世界の回転は次のようになります。

  • Context.save();
  • Context.translate(登録X、登録Y);
  • Context.rotate(角度);
  • ものを描きます。
  • Context.translate( -registrationX, -registrationY );
  • Context.restore();

ローテーションとレジストレーション ポイントを理解するための優れた方法を次に示します。

  • 一枚の紙を用意し、そのページに一文字書いてください。
  • 鉛筆を手に取り、ページの任意の場所でそれを押し続けます。
  • --鉛筆の先が登録点です。
  • --鉛筆の先は移動(X,Y)です。
  • 鉛筆の先を中心に紙を回転させます。
  • --紙の回転はrotate(angle)
  • --紙が動くと文字がどのように回転するかに注目してください。
  • -- 赤いボックスが文字のように回転します (カメラの登録点を中心に)
  • 用紙を直立回転に戻します。
  • -- この「非回転」は、translate( -X, -Y )のようなものです。
  • 紙の上の別の点に鉛筆を移動します
  • -- 繰り返しますが、これは translate(newX,newY) のようなものです。
  • 紙をもう一度回転させて、文字が異なるパターンでどのように回転するかに注目してください。

ここにコードとフィドルがあります:http://jsfiddle.net/m1erickson/nj29x/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:30px; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var camX=100;
    var camY=100;
    var camRadius=40;
    var camDegrees=0;

    draw();

    function draw(){
    ctx.clearRect(0,0,canvas.width,canvas.height);
        drawWorldRect(75,75,30,20,0,"blue","A");
        drawWorldRect(100,100,30,20,15,"red","B");
        drawCamera();
    }

    function drawWorldRect(x,y,width,height,angleDegrees,fillstyle,letter){
        var cx=x+width/2;
        var cy=y+height/2;
        ctx.save();
        ctx.translate(cx, cy);
        ctx.rotate( (Math.PI / 180) * angleDegrees);
        ctx.fillStyle = fillstyle;
        ctx.fillRect(-width/2, -height/2, width, height);
        ctx.fillStyle="white";
        ctx.font = '18px Verdana';
        ctx.fillText(letter, -6, 7);
        ctx.restore();
    }


    function drawCamera(){
        ctx.save();
        ctx.lineWidth = 3;
        ctx.beginPath();
        // draw camera cross-hairs
        ctx.strokeStyle = "#dddddd";
        ctx.moveTo(camX,camY-camRadius);
        ctx.lineTo(camX,camY+camRadius);
        ctx.moveTo(camX-camRadius,camY);
        ctx.lineTo(camX+camRadius,camY);
        ctx.arc(camX, camY, camRadius, 0 , 2 * Math.PI, false);
        ctx.stroke();
        // draw camera site
        ctx.fillStyle = "#222222";
        ctx.beginPath();
        ctx.arc(camX, camY-camRadius, 3, 0 , 2 * Math.PI, false);
        ctx.fill()
        ctx.restore();
    }

    function rotate(){
        ctx.save();
        ctx.translate(camX,camY);
        ctx.rotate(Math.PI/180*camDegrees);
        ctx.translate(-camX,-camY);
        draw();
        ctx.restore();
    }

    $("#rotateCW").click(function(){ camDegrees+=30; rotate(); });
    $("#rotateCCW").click(function(){ camDegrees-=30; rotate(); });
    $("#camUp").click(function(){ camY-=20; draw(); });
    $("#camDown").click(function(){ camY+=20; draw(); });
    $("#camLeft").click(function(){ camX-=20; draw(); });
    $("#camRight").click(function(){ camX+=20; draw(); });

}); // end $(function(){});
</script>

</head>

<body>

    <canvas id="canvas" width=400 height=300></canvas><br/>
    <button id="camLeft">Camera Left</button>
    <button id="camRight">Camera Right</button>
    <button id="camUp">Camera Up.</button>
    <button id="camDown">Camera Down</button><br/>
    <button id="rotateCW">Rotate Clockwise</button>
    <button id="rotateCCW">Rotate CounterClockwise</button>

</body>
</html>
于 2013-03-18T02:48:12.103 に答える
2

世界の中心を移動する x 点と y 点は、回転角度のタンジェントを使用して計算されます。概念を示す例があります。これは最終的な解決策ではありませんが、作業の開始点です。「ラフカット」と考えてください (完全に磨く時間はありません)。たとえば、ご覧のとおり、回転のピボット ポイントはカメラ レンズの真下のポイントには移動されないため、回転はカメラの真下では発生しません。

以下は、幾何学/三角法の仕組みを説明する図です。

ここに画像の説明を入力

コード スニペットと完全に機能する jsfiddle へのリンクを次に示します。キーボード コマンドは、例と同じです。

http://jsfiddle.net/ricksuggs/X8rYF/

    //s (down)
    if (keycode === 83) {

        event.preventDefault();
        canvas.getContext('2d').clearRect(-200,-200,400,400);
        canvas.getContext('2d').translate(2*Math.tan(rotationSum), -2); 
        drawGrid();
        drawCamera();

    }

    // <-- (rotate left)
    if (keycode === 37) {

        event.preventDefault();
        canvas.getContext('2d').clearRect(-200,-200,400,400);
        rotationSum += rotation;
        console.log('rotationSum: ' + rotationSum);
        canvas.getContext('2d').rotate(-rotation);        
        drawGrid();
        drawCamera();

    }
于 2013-03-26T03:47:55.330 に答える
0

順序は次のようになります。

Ctx.save()
Ctx.translate(x, y) //to the center (Or top left of object to rotate)
Ctx.rotates(radian)
Ctx.translate(-x, -y) //important to move the context back!
Ctx.draw(...)
Ctx.restore()
于 2013-03-16T18:59:11.917 に答える