0

Canvas 要素を使用してゲームを作成しました。フレームごとに 300 以上の画像が描画されるため、20 fps 未満で実行する必要があり、60% 以上の CPU を消費します。

したがって、私の最善の選択肢は WebGL に切り替えることだと思います。チュートリアルや例を探して、Web を掃除してきました。これはちょっと複雑な作業ですが、有効なソースをハックすることができました。

<!-- Licensed under a BSD license. See license.html for license -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL - 2D Image</title>
<link type="text/css" href="http://games.greggman.com/downloads/examples/webgl/resources/webgl-tutorials.css" rel="stylesheet" />
<script type="text/javascript" src="http://games.greggman.com/downloads/examples/webgl/resources/webgl-utils.js"></script>
<script>
window.onload = main;

function main() {
  image = new Image();
  image.src = "http://games.greggman.com/downloads/examples/webgl/resources/leaves.jpg";  // MUST BE SAME DOMAIN!!!
  image.onload = function() {
    setUp();
    setInterval(function(){
        x1 += 1;
        y1 += 1
        for (i = 0; i < 30; i++) {
            for (e = 0; e < 10; e++) {
                //render(image, x1+i*5, y1+e*5);
                gl.bindTexture(gl.TEXTURE_2D, texture);

                 // Set a rectangle the same size as the image.
                setRectangle(gl, x1+i*5, y1+e*5, image.width, image.height);

                // Draw the rectangle.
                 gl.drawArrays(gl.TRIANGLES, 0, 6);
            }
        }
    },1000/60);
  }
}
var x1 = 0;
var y1 = 0;
var image;
var canvas;
var gl;
var texture;
function setUp() {
  canvas = document.getElementById("canvas");
  gl = getWebGLContext(canvas);
    // setup GLSL program
  vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader");
  fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader");
  program = createProgram(gl, [vertexShader, fragmentShader]);
  gl.useProgram(program);

    // lookup uniforms
  var resolutionLocation = gl.getUniformLocation(program, "u_resolution");

  // set the resolution
  gl.uniform2f(resolutionLocation, canvas.width, canvas.height);

// Create a texture.
  texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // Set the parameters so we can render any size image.
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

  // Upload the image into the texture.
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

 // look up where the vertex data needs to go.
  var positionLocation = gl.getAttribLocation(program, "a_position");
  var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");

  // provide texture coordinates for the rectangle.
  var texCoordBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      0.0,  0.0,
      1.0,  0.0,
      0.0,  1.0,
      0.0,  1.0,
      1.0,  0.0,
      1.0,  1.0]), gl.STATIC_DRAW);
  gl.enableVertexAttribArray(texCoordLocation);
  gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);





  // Create a buffer for the position of the rectangle corners.
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
}

function render(image, x, y) {

}

function randomInt(range) {
  return Math.floor(Math.random() * range);
}

function setRectangle(gl, x, y, width, height) {
  var x1 = x;
  var x2 = x + width;
  var y1 = y;
  var y2 = y + height;
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
     x1, y1,
     x2, y1,
     x1, y2,
     x1, y2,
     x2, y1,
     x2, y2]), gl.STATIC_DRAW);
}

</script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;

uniform vec2 u_resolution;

varying vec2 v_texCoord;

void main() {
   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;

   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;

   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;

   gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

   // pass the texCoord to the fragment shader
   // The GPU will interpolate this value between points.
   v_texCoord = a_texCoord;
}
</script></script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
</body>
</html>

300 枚の画像を描画しますが、通常のキャンバスを使用する場合の 1/2 の CPU しか消費しません。それほど悪くはありませんが、まだ十分ではありません。私は何か間違ったことや不必要なことをしていますか? GPU で計算を行うか、それらをキャッシュすることで CPU を緩和する方法はありますか? 私はシェーダーで調査しましたが、シェーダーを使用してテクスチャと座標を保存し、シェーダーに配列を入力して、描画する必要がある各画像の位置ポイントを設定することは可能でしょうか?

ありがとう、この狂気への簡単な解決策があることを願っています。

4

1 に答える 1

2

本当に 300 個の (異なる) 画像を描画する必要がありますか? それとも、同じテクスチャの 300 個の長方形だけですか? (これはあなたが現在していることです)

一般に、複数の理由により、コードは非常に非効率的です。

  • バッファを一度作成して再利用する代わりに、フレームごとに 300 個の新しい Float32Array 配列を割り当てます ( setRectangle()) 。
  • 300 個のオブジェクトに対して 300 個の小さなバッファーを作成する代わりに、すべての頂点情報を 1 つのバッファー内に配置して、すべてを一度に描画することができます (これは、すべてのオブジェクトを異なるテクスチャで描画したくない場合にのみ機能します)。
  • オブジェクトごとにテクスチャの状態を切り替えています( glBindTexture)。一度テクスチャをセットアップして、同じテクスチャでオブジェクトのバッチを描画するよりも優れています。

コードのもう少し最適化されたバージョンは次のようになります。

// called once
setup() {
  // load and setup texture
  // create vertex buffer object for your vertex data
  // setup shader
}

// called every frame
display() {
  gl.useProgram(..) // set shader
  gl.bindTexture(..) // set texture
  gl.bindBuffer(..) // set buffer
  gl.drawArrays(0, 1800) // draw all you object in one step
}

各オブジェクトを別のテクスチャで描画したい場合は、もう少し複雑です。ただし、最適化するための複数のオプションがあります。

  • 複数のテクスチャをファイルに結合します。たとえば、16 個の 128x128 テクスチャを 1 つの 512x512 テクスチャ ファイルに結合できます。この場合、テクスチャに一致するようにオブジェクトのテクスチャ座標を更新する必要があります
  • 複数のテクスチャ ユニットを使用し、使用するユニットをシェーダー内で決定します。
  • 同じテクスチャを使用するオブジェクトが複数ある場合は、オブジェクトを並べ替えて、これらのオブジェクトを一度に描画できるようにすることができます。
于 2012-07-25T16:11:09.987 に答える