我试图使用WebGL绘制精灵时出现故障和崩溃

时间:2014-05-23 23:05:50

标签: html5 2d webgl

我正在将我的精灵绘图功能从canvas 2d转换为webgl。

由于我是webgl的新手(也是openGL),我从这个tuto http://games.greggman.com/game/webgl-image-processing/中学到了很多东西,我从中找到了很多行,还有一些我发现的。

最后我得到了它,但有一些问题。出于某种原因,有些图像从未被绘制过,但是我会在屏幕上看到大的随机黑色方块,最后它会让firefox崩溃......

我正在试图解决这些问题,但我只是迷失了...我必须要求一些帮助。 请有人查看我的代码并告诉我你是否知道我在哪里犯了错误。

顶点着色器和片段着色器:

<script id="2d-vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;
    attribute vec2 a_texCoord;
    uniform vec2 u_resolution;
    uniform vec2 u_translation;
    uniform vec2 u_rotation;
    varying vec2 v_texCoord;
    void main()
    {
        // Rotate the position
        vec2 rotatedPosition = vec2(
             a_position.x * u_rotation.y + a_position.y * u_rotation.x,
             a_position.y * u_rotation.y - a_position.x * u_rotation.x);

        // Add in the translation.
        vec2 position = rotatedPosition + u_translation;

        // 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 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()
    {
         // Look up a color from the texture.
         gl_FragColor = texture2D(u_image, v_texCoord);
    }
</script>

我使用多个分层画布来避免浪费资源重绘每个帧的大背景和前景,而它们永远不会改变。所以我的画布在liste_canvas []中,上下文在liste_ctx []中,c是id(“background”/“game”/“foreground”/“infos”)。这是他们的创作代码:

// Get A WebGL context
liste_canvas[c] = document.createElement("canvas") ;
document.getElementById('game_div').appendChild(liste_canvas[c]);
liste_ctx[c] = liste_canvas[c].getContext('webgl',{premultipliedAlpha:false}) || liste_canvas[c].getContext('experimental-webgl',{premultipliedAlpha:false});

liste_ctx[c].viewport(0, 0, game.res_w, game.res_h);

// setup a GLSL program
liste_ctx[c].vertexShader = createShaderFromScriptElement(liste_ctx[c], "2d-vertex-shader");
liste_ctx[c].fragmentShader = createShaderFromScriptElement(liste_ctx[c], "2d-fragment-shader");
liste_ctx[c].program = createProgram(liste_ctx[c], [liste_ctx[c].vertexShader, liste_ctx[c].fragmentShader]);
liste_ctx[c].useProgram(liste_ctx[c].program);

这是我的精灵绘图功能。 我的图像也存储在列表中,sprite [],字符串名称为id。 他们将原点(不一定是真正的中心)存储为.orgn_x和.orgn_y。

function draw_sprite( id_canvas , d_sprite , d_x , d_y , d_rotation , d_scale , d_opacity )
{
    if( id_canvas=="" ){ id_canvas = "game" ; }
    if( !d_scale ){ d_scale = 1 ; }
    if( !d_rotation ){ d_rotation = 0 ; }

    if( render_mode == "webgl" )
    {
        c = id_canvas ;

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

        // provide texture coordinates for the rectangle.
            var texCoordBuffer = liste_ctx[c].createBuffer();
        liste_ctx[c].bindBuffer(liste_ctx[c].ARRAY_BUFFER, texCoordBuffer);
        liste_ctx[c].bufferData(liste_ctx[c].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]), liste_ctx[c].STATIC_DRAW);
        liste_ctx[c].enableVertexAttribArray(texCoordLocation);
        liste_ctx[c].vertexAttribPointer(texCoordLocation, 2, liste_ctx[c].FLOAT, false, 0, 0);

        // Create a texture.
        var texture = liste_ctx[c].createTexture();
        liste_ctx[c].bindTexture(liste_ctx[c].TEXTURE_2D, texture);

        // Set the parameters so we can render any size image.
        liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_WRAP_S, liste_ctx[c].CLAMP_TO_EDGE);
        liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_WRAP_T, liste_ctx[c].CLAMP_TO_EDGE);
        liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_MIN_FILTER, liste_ctx[c].LINEAR);
        liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_MAG_FILTER, liste_ctx[c].LINEAR);

        // Upload the image into the texture.
        liste_ctx[c].texImage2D(liste_ctx[c].TEXTURE_2D, 0, liste_ctx[c].RGBA, liste_ctx[c].RGBA, liste_ctx[c].UNSIGNED_BYTE, sprites[d_sprite] );

        // set the resolution
        var resolutionLocation = liste_ctx[c].getUniformLocation(liste_ctx[c].program, "u_resolution");
        liste_ctx[c].uniform2f(resolutionLocation, liste_canvas[c].width, liste_canvas[c].height);

        // Create a buffer and put a single clipspace rectangle in it (2 triangles)
        var buffer = liste_ctx[c].createBuffer();
        liste_ctx[c].bindBuffer(liste_ctx[c].ARRAY_BUFFER, buffer);
        liste_ctx[c].enableVertexAttribArray(positionLocation);
        liste_ctx[c].vertexAttribPointer(positionLocation, 2, liste_ctx[c].FLOAT, false, 0, 0);

        // then I calculate the coordinates of the four points of the rectangle
        // taking their origin and scale into account
        // I cut this part as it is large and has no importance here

        // and at last, we draw
        liste_ctx[c].bufferData(liste_ctx[c].ARRAY_BUFFER, new Float32Array([
            topleft_x , topleft_y ,
            topright_x , topright_y ,
            bottomleft_x , bottomleft_y ,
            bottomleft_x , bottomleft_y ,
            topright_x , topright_y ,
            bottomright_x , bottomright_y ]), liste_ctx[c].STATIC_DRAW);
        // draw
        liste_ctx[c].drawArrays(liste_ctx[c].TRIANGLES, 0, 6);
    }
}

顺便说一句,我没有找到任何方法将ctx.globalAlpha移植到webgl。如果有人知道如何在我的代码中添加它,我也会对此感激不尽。

请帮忙。感谢。

1 个答案:

答案 0 :(得分:2)

我不知道为什么事情会崩溃,只是随便发表一些评论。

  1. 只创建一次缓冲区和纹理。

    目前,每次调用draw_sprite时,代码都会创建缓冲区和纹理。相反,您应该只在初始化时创建它们一次,然后再使用创建的缓冲区和纹理。类似地,您应该在初始化时查找属性和统一位置,然后在绘制时使用它们。

    因为每次拨打draw_sprite

  2. 我认为制作一个单位正方形的缓冲区更常见,然后使用矩阵数学将该正方形移动到您想要的位置。有关矩阵数学的一些帮助,请参阅http://games.greggman.com/game/webgl-2d-matrices/

    如果你走那条路,那么你只需要调用所有与缓冲区相关的东西。

    即使您不使用矩阵数学,您仍然可以为着色器添加平移和缩放,然后只需使用单位矩形制作一个缓冲区(如

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        0, 0,
        1, 0,
        0, 1,
        0, 1,
        1, 0,
        1, 1]), gl.STATIC_DRAW)       
    

    之后,然后just translate it将您想要的地方scale it增加到您希望绘制的尺寸。

    事实上,如果你去矩阵路线,那么很容易模拟二维上下文的矩阵函数ctx.translatectx.rotatectx.scale等...... / p>

  3. 如果您将上下文拉入局部变量,代码可能更容易理解并输入。

    而不是像

    这样的东西
    liste_ctx[c].bindBuffer(liste_ctx[c].ARRAY_BUFFER, buffer);
    liste_ctx[c].enableVertexAttribArray(positionLocation);
    liste_ctx[c].vertexAttribPointer(positionLocation, 2, liste_ctx[c].FLOAT, false, 0, 0);
    

    你可以这样做

    var gl = liste_ctx[c];
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    
  4. 在上下文中存储内容会变得棘手

    此代码

    liste_ctx[c].vertexShader = createShaderFromScriptElement(liste_ctx[c], "2d-vertex-shader");
    liste_ctx[c].fragmentShader = createShaderFromScriptElement(liste_ctx[c], "2d-fragment-shader");
    liste_ctx[c].program = createProgram(liste_ctx[c], [liste_ctx[c].vertexShader, liste_ctx[c].fragmentShader]);
    

    使它看起来像你只有一个顶点着色器,一个片段着色器和单个程序。也许你是,但在WebGL中有几个着色器和程序是很常见的。

  5. 对于globalAlpha,首先需要打开混合。

    gl.enable(gl.BLEND);
    

    你需要告诉它如何融合。要与画布2d上下文相同 需要使用预乘的alpha数学,所以

    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    

    然后,您需要将着色器绘制的颜色乘以Alpha值。例如

    <script id="2d-fragment-shader" type="x-shader/x-fragment">
    precision mediump float;
    
    // our texture
    uniform sampler2D u_image;
    
    // global alpha
    uniform float u_globalAlpha;
    
    // the texCoords passed in from the vertex shader.
    varying vec2 v_texCoord;
    
    void main()
    {
         // Look up a color from the texture.
         vec4 color = texture2D(u_image, v_texCoord);
    
         // Multiply the color by u_globalAlpha
         gl_FragColor =  color * u_globalAlpha;
    }
    </script>
    

    然后您需要设置u_globalAlpha。在初始时查找它的位置

    var globalAlphaLocation = gl.getUniformLocation(program, "u_globalAlpha");
    

    在抽奖时设定它

    gl.uniform1f(globalAlphaLocation, someValueFrom0to1);
    

    我个人通常使用vec4并将其称为u_colorMult

    <script id="2d-fragment-shader" type="x-shader/x-fragment">
    precision mediump float;
    
    // our texture
    uniform sampler2D u_image;
    
    // colorMult
    uniform float u_colorMult;
    
    // the texCoords passed in from the vertex shader.
    varying vec2 v_texCoord;
    
    void main()
    {
         // Look up a color from the texture.
         gl_FragColor = texture2D(u_image, v_texCoord) * u_colorMult;
    }
    </script>    
    

    然后我可以为我的精灵着色,例如使用

    将精灵画成红色
    glUniform4fv(colorMultLocation, [1, 0, 0, 1]);
    

    这也意味着我可以轻松地绘制纯色。创建1x1像素纯白色纹理。任何时候我想用纯色绘制我只是绑定该纹理并将u_colorMult设置为我想要绘制的颜色。