10

次のスニペットを使用して、このページcanvasの要素をpngに変換しようとしています (たとえば、JavaScript コンソールに入力します)。

(function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL("image/png");
  return image;
})($$('canvas')[0]);

残念ながら、取得した png は完全に空白です。ページのサイズを変更すると、元のキャンバスが空白になることにも注意してください。

なぜcanvas空白になるのですか?canvasこれをpngに変換するにはどうすればよいですか?

4

3 に答える 3

17

Kevin Reid のpreserveDrawingBuffer提案は正しいものですが、(通常) より良いオプションがあります。tl;dr は最後のコードです。

レンダリングされた Web ページの最終的なピクセルをまとめるにはコストがかかる可能性があり、それを WebGL コンテンツのレンダリングと調整するとさらにコストがかかります。通常の流れは次のとおりです。

  1. JavaScript が描画コマンドを WebGL コンテキストに発行します
  2. JavaScript が返され、コントロールがメインのブラウザ イベント ループに戻ります
  3. WebGL コンテキストは、現在画面にレンダリングされている Web ページに統合するために、描画バッファー (またはその内容) をコンポジターに渡します。
  4. 画面に表示された WebGL コンテンツを含むページ

これは、ほとんどの OpenGL アプリケーションとは異なることに注意してください。それらでは、レンダリングされたコンテンツは通常、ページ上の他の多くのものと合成されるのではなく、直接表示されます。その一部は、実際には WebGL コンテンツの上にあり、ブレンドされている場合があります。

WebGL 仕様は、手順 3 の後で描画バッファーを本質的に空として扱うように変更されました。devtools で実行しているコードは、手順 4 の後に来るため、空のバッファーが得られます。この仕様の変更により、ステップ 3 の後のブランキングが基本的にハードウェアで実際に発生するプラットフォーム (多くのモバイル GPU など) でパフォーマンスが大幅に向上しました。これを回避して、ステップ 3 の後に WebGL コンテンツのコピーを時々作成する場合、ブラウザーはステップ 3 の前に常に描画バッファーのコピーを作成する必要があります。これにより、一部のプラットフォームでフレームレートが急激に低下します。

trueに設定することで、まさにそれを実行して、ブラウザに強制的にコピーを作成させ、画像コンテンツへのアクセスを維持させることができpreserveDrawingBufferます。仕様から:

このデフォルトの動作は、WebGLContextAttributes オブジェクトの preserveDrawingBuffer 属性を設定することで変更できます。このフラグが true の場合、描画バッファの内容は、作成者が消去または上書きするまで保持されます。このフラグが false の場合、レンダリング関数が返された後に、このコンテキストをソース イメージとして使用して操作を実行しようとすると、未定義の動作が発生する可能性があります。これには、readPixels または toDataURL 呼び出し、またはこのコンテキストを別のコンテキストの texImage2D または drawImage 呼び出しのソース イメージとして使用することが含まれます。

あなたが提供した例では、コードはコンテキスト作成行を変更しているだけです:

gl = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});

何をどのようにレンダリングしているかによっては、一部のブラウザーでより遅いパスが強制され、パフォーマンスが低下することに注意してください。コピーを実際に作成する必要がないほとんどのデスクトップ ブラウザーでは問題ないはずです。これらは、WebGL 対応ブラウザーの大部分を占めていますが、今のところだけです。

ただし、別のオプションがあります (仕様の次の段落で少し紛らわしく述べられているように)。

基本的に、ステップ 2 の前に自分でコピーを作成します。つまり、すべての描画呼び出しが終了した後、コードからブラウザーに制御を戻す前です。これは、WebGL 描画バッファーがまだ無傷でアクセス可能な場合であり、ピクセルへのアクセスに問題はないはずです。同じものを使用するtoDataUrlreadPixels、他の方法で使用する呼び出しを使用します。重要なのはタイミングだけです。

ここでは、両方の長所を活用できます。preserveDrawingBuffer描画バッファーのコピーを取得しますが、 trueに設定した場合のように、コピーが不要なフレーム (ほとんどの場合) であっても、すべてのフレームで料金を支払う必要はありません。

提供した例では、コードを の一番下に追加するだけdrawSceneで、キャンバスのコピーがすぐ下に表示されます。

function drawScene() {
  ...

  var webglImage = (function convertCanvasToImage(canvas) {
    var image = new Image();
    image.src = canvas.toDataURL('image/png');
    return image;
  })(document.querySelectorAll('canvas')[0]);

  window.document.body.appendChild(webglImage);
}
于 2012-09-23T00:12:54.827 に答える
3

ここで試してみるべきことがいくつかあります。この作業を行うためにこれらのいずれかが必要かどうかはわかりませんが、違いが生じる可能性があります。

  • 属性に追加preserveDrawingBuffer: truegetContextます。
  • アニメーションを行う後のチュートリアルでこれを試してください。つまり、一度だけではなく繰り返しキャンバスに描画します。
于 2012-09-22T01:27:43.580 に答える