简体   繁体   中英

How do I render a 2D Sprite using WebGL?

I'm struggling to render a 2D Sprite to a Canvas using Dart and WebGL. I can find very few examples of this online; most are either 3D, or contain tons of spaghetti code with no real explanation of what they're doing. I'm trying to do the simplest thing that renders a sprite.

So far, I've managed to render a green square (two triangles) on a canvas. The bit I'm struggling with, is how to change this from a green square to using my texture (the texture is loaded and bound correctly, I believe). I think this will need changes to the shaders (to take texture co-ords, instead of colour) and something to pass texture coords relating to the vertices in the buffer.

This code also exists in a Gist .

Note: This is just a throwaway sample; most of the code lives in the constructor; I'm not too interested in how tidy the code is for now; I can tidy up when I can see a sprite on the screen!

Note: I'm not interested in using a third-party library; I'm doing this to learn WebGL!

<!DOCTYPE html>
<html>
  <head>
    <title>MySecondGame</title>
  </head>
  <body>
    <canvas width="1024" height="768"></canvas>

    <div style="display: none;">
      <img id="img-player" src="assets/player.png" />
    </div>

    <script id="vertex" type="x-shader">
      attribute vec2 aVertexPosition;

      void main() {
        gl_Position = vec4(aVertexPosition, 0.0, 1.0);
      }
    </script>
    <script id="fragment" type="x-shader">
      #ifdef GL_ES
      precision highp float;
      #endif

      uniform vec4 uColor;

      void main() {
        gl_FragColor = uColor;
      }
    </script>

    <script type="application/dart">
      import 'dart:async';
      import 'dart:html';
      import 'dart:math';
      import 'dart:typed_data';
      import 'dart:web_gl';

      Game game;

      main() {
        game = new Game(document.querySelector('canvas'));
      }

      class Game {
        RenderingContext _gl;
        Buffer vbuffer;
        int numItems;
        Texture playerTexture;
        double elapsedTime;
        double fadeAmount;

        Game(CanvasElement canvas) {
          _gl = canvas.getContext3d();
          playerTexture = _gl.createTexture();
          _gl.bindTexture(TEXTURE_2D, playerTexture);
          _gl.texImage2DUntyped(TEXTURE_2D, 0, RGBA, RGBA, UNSIGNED_BYTE, document.querySelector('#img-player'));
          _gl.texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST);
          _gl.texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR_MIPMAP_NEAREST);
          _gl.generateMipmap(TEXTURE_2D);
          _gl.bindTexture(TEXTURE_2D, null);

          var vsScript = document.querySelector('#vertex');
          var vs = _gl.createShader(VERTEX_SHADER);
          _gl.shaderSource(vs, vsScript.text);
          _gl.compileShader(vs);

          var fsScript = document.querySelector('#fragment');
          var fs = _gl.createShader(FRAGMENT_SHADER);
          _gl.shaderSource(fs, fsScript.text);
          _gl.compileShader(fs);

          var program = _gl.createProgram();
          _gl.attachShader(program, vs);
          _gl.attachShader(program, fs);
          _gl.linkProgram(program);

          if (!_gl.getShaderParameter(vs, COMPILE_STATUS))
            print(_gl.getShaderInfoLog(vs));

          if (!_gl.getShaderParameter(fs, COMPILE_STATUS))
            print(_gl.getShaderInfoLog(fs));

          if (!_gl.getProgramParameter(program, LINK_STATUS))
            print(_gl.getProgramInfoLog(program));

          var aspect = canvas.width / canvas.height;
          var vertices = new Float32List.fromList([
            -0.5, 0.5 * aspect, 0.5, 0.5 * aspect,  0.5, -0.5 * aspect,  // Triangle 1
            -0.5, 0.5 * aspect, 0.5,-0.5 * aspect, -0.5, -0.5 * aspect   // Triangle 2
          ]);

          vbuffer = _gl.createBuffer();
          _gl.bindBuffer(ARRAY_BUFFER, vbuffer);                                       
          _gl.bufferData(ARRAY_BUFFER, vertices, STATIC_DRAW);
          numItems = vertices.length ~/ 2;

          _gl.useProgram(program);

          var uColor = _gl.getUniformLocation(program, "uColor");
          _gl.uniform4fv(uColor, new Float32List.fromList([0.0, 0.3, 0.0, 1.0]));

          var aVertexPosition = _gl.getAttribLocation(program, "aVertexPosition");
          _gl.enableVertexAttribArray(aVertexPosition);
          _gl.vertexAttribPointer(aVertexPosition, 2, FLOAT, false, 0, 0);

          window.animationFrame.then(_gameLoop);
        }

        _gameLoop(num time) {
          elapsedTime = time;
          _update();
          _render();
          window.animationFrame.then(_gameLoop);
        }

        _update() {
          // Use sine curve for fading. Sine is -1-1, so tweak to be 0 - 1.
          fadeAmount = (sin(elapsedTime/1000) / 2) + 0.5;
        }

        _render() {
          // Set colour for clearing to.
          _gl.clearColor(fadeAmount, 1 - fadeAmount, 0.0, 1.0);
          // Clear.
          _gl.clear(RenderingContext.COLOR_BUFFER_BIT);

          _gl.bindTexture(TEXTURE_2D, playerTexture);
          _gl.drawArrays(TRIANGLES, 0, numItems);
          _gl.bindTexture(TEXTURE_2D, null);
        }
      }
    </script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

(Tagging this with opengl too because I believe the solution is likely the same for WebGL/OpenGL).

Ok, managed to make this work. You can see the full diff in a gist here .

I might be wrong; but it seems that I was expecting to set the data in the buffers while I was setting them up; but I couldn't find any way to say which data was for which buffer. I split the code into some setup code:

vbuffer = _gl.createBuffer();
_gl.bindBuffer(ARRAY_BUFFER, vbuffer);
_gl.bufferData(ARRAY_BUFFER, vertices, STATIC_DRAW);
numItems = vertices.length ~/ 2;

tbuffer = _gl.createBuffer();
_gl.bindBuffer(ARRAY_BUFFER, tbuffer);                                       
_gl.bufferData(ARRAY_BUFFER, textureCoords, STATIC_DRAW);

aVertexPosition = _gl.getAttribLocation(program, "aVertexPosition");
_gl.enableVertexAttribArray(aVertexPosition);

aTextureCoord = _gl.getAttribLocation(program, "aTextureCoord");
_gl.enableVertexAttribArray(aTextureCoord);

uSampler = _gl.getUniformLocation(program, "uSampler");

and some rendering code:

_gl.bindBuffer(ARRAY_BUFFER, vbuffer);
_gl.vertexAttribPointer(aVertexPosition, 2, FLOAT, false, 0, 0);

_gl.bindBuffer(ARRAY_BUFFER, tbuffer);
_gl.vertexAttribPointer(aTextureCoord, 2, FLOAT, false, 0, 0);

_gl.bindTexture(TEXTURE_2D, playerTexture);
_gl.uniform1i(uSampler, 0);

_gl.drawArrays(TRIANGLES, 0, numItems);

I'm not entirely sure if this is correct (it feels like I'm sending the same vertex and textureCoord every frame), but it's working.

我做了一场比赛!!!! 111

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM