在WebGL中绘制多个2D图像

时间:2021-12-11 04:46:08

I've been following a couple of tutorials on html5rocks, and I managed to make a javascript program which displays ONE image on a canvas using webGL. I have posted the code I have for this below.

我一直在关注html5rocks的几个教程,我设法制作了一个javascript程序,使用webGL在画布上显示一个图像。我在下面发布了我的代码。

The problem is, it seems there is nobody out there who shows you how to draw more than ONE object in webGL. I've never worked directly with webGL before, so it's not very intuitive for me.

问题是,似乎没有人向你展示如何在webGL中绘制多个对象。我之前从未直接使用过webGL,所以对我来说这不是很直观。

How can I modify this code to draw each object in the imageObjectArray? (notice that now I'm just drawing imageObjectArray[0].

如何修改此代码以在imageObjectArray中绘制每个对象? (注意,现在我只是在绘制imageObjectArray [0]。

    function render(canvas, contextGL, imageObjectArray) { 
        vertexShader = createShaderFromScriptElement(contextGL, "2d-vertex-shader");
        fragmentShader = createShaderFromScriptElement(contextGL, "2d-fragment-shader");

        program = createProgram(contextGL, [vertexShader, fragmentShader]);
        contextGL.useProgram(program);

        var positionLocation = contextGL.getAttribLocation(program, "a_position");

        var texCoordLocation = contextGL.getAttribLocation(program, "a_texCoord");

        var texCoordBuffer = contextGL.createBuffer();
        contextGL.bindBuffer(contextGL.ARRAY_BUFFER, texCoordBuffer);

        contextGL.enableVertexAttribArray(texCoordLocation);
        contextGL.vertexAttribPointer(texCoordLocation, 2, contextGL.FLOAT, false, 0, 0);

        setRectangle(contextGL, 0.0, 0.0, 1.0, 1.0);

        var texture = contextGL.createTexture();
        contextGL.bindTexture(contextGL.TEXTURE_2D, texture);

        contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_WRAP_S, contextGL.CLAMP_TO_EDGE);
        contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_WRAP_T, contextGL.CLAMP_TO_EDGE);
        contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_MIN_FILTER, contextGL.NEAREST);
        contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_MAG_FILTER, contextGL.NEAREST);

        contextGL.texImage2D(contextGL.TEXTURE_2D, 0, contextGL.RGBA, contextGL.RGBA, contextGL.UNSIGNED_BYTE, imageObjectArray[0].displayObject.data);

        var resolutionLocation = contextGL.getUniformLocation(program, "u_resolution");
        contextGL.uniform2f(resolutionLocation, canvas.width, canvas.height);

        var buffer = contextGL.createBuffer();
        contextGL.bindBuffer(contextGL.ARRAY_BUFFER, buffer);
        contextGL.enableVertexAttribArray(positionLocation);
        contextGL.vertexAttribPointer(positionLocation, 2, contextGL.FLOAT, false, 0, 0);

        setRectangle(contextGL, imageObjectArray[0].x, imageObjectArray[0].y, imageObjectArray[0].width, imageObjectArray[0].height);

        // draw
        contextGL.drawArrays(contextGL.TRIANGLES, 0, 6);
    }

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

My aim here is to work on a (very basic) 2d sprite game. Without the comfort of libraries. (except for maybe glMatrix.js)

我的目标是开发一个(非常基本的)2d精灵游戏。没有图书馆的舒适。 (除了glMatrix.js)

[EDIT] My render function had an error in it, fixed.

[编辑]我的渲染函数中有一个错误,已修复。

1 个解决方案

#1


5  

The following code does the following:

以下代码执行以下操作:

  1. Compiles the vertex and fragment shaders
  2. 编译顶点和片段着色器

  3. Links them together into a shader program
  4. 将它们链接到一个着色器程序中

  5. Creates a vertex buffer to hold texture coordinates and fills it (texCoordBuffer)
  6. 创建一个顶点缓冲区来保存纹理坐标并填充它(texCoordBuffer)

  7. Creates a texture (createTexture)
  8. 创建纹理(createTexture)

  9. Configures how the texture is sampled (texParameteri)
  10. 配置纹理的采样方式(texParameteri)

The above 5 steps only need to be run once.

以上5个步骤只需要运行一次。

    vertexShader = createShaderFromScriptElement(contextGL, "2d-vertex-shader");
    fragmentShader = createShaderFromScriptElement(contextGL, "2d-fragment-shader");

    program = createProgram(contextGL, [vertexShader, fragmentShader]);
    contextGL.useProgram(program);

    var positionLocation = contextGL.getAttribLocation(program, "a_position");

    var texCoordLocation = contextGL.getAttribLocation(program, "a_texCoord");

    var texCoordBuffer = contextGL.createBuffer();
    contextGL.bindBuffer(contextGL.ARRAY_BUFFER, texCoordBuffer);

    contextGL.enableVertexAttribArray(texCoordLocation);
    contextGL.vertexAttribPointer(texCoordLocation, 2, contextGL.FLOAT, false, 0, 0);

    var texture = contextGL.createTexture();
    contextGL.bindTexture(contextGL.TEXTURE_2D, texture);

    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_WRAP_S, contextGL.CLAMP_TO_EDGE);
    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_WRAP_T, contextGL.CLAMP_TO_EDGE);
    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_MIN_FILTER, contextGL.NEAREST);
    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_MAG_FILTER, contextGL.NEAREST);

    setRectangle(contextGL, 0.0, 0.0, 1.0, 1.0);

The rest of the code must execute for each image you want to draw and it does the following:

其余代码必须为您要绘制的每个图像执行,并执行以下操作:

  1. Uploads the image into the texture (texImage2D)
  2. 将图像上传到纹理(texImage2D)

  3. Creates a vertex buffer to hold positions and fills it (buffer)
  4. 创建一个顶点缓冲区来保持位置并填充它(缓冲区)

  5. Calls drawArrays
    contextGL.texImage2D(contextGL.TEXTURE_2D, 0, contextGL.RGBA, contextGL.RGBA,contextGL.UNSIGNED_BYTE, imageObjectArray[0].displayObject.data);

    var resolutionLocation = contextGL.getUniformLocation(program, "u_resolution");
    contextGL.uniform2f(resolutionLocation, canvas.width, canvas.height);

    var buffer = contextGL.createBuffer();
    contextGL.bindBuffer(contextGL.ARRAY_BUFFER, buffer);
    contextGL.enableVertexAttribArray(positionLocation);
    contextGL.vertexAttribPointer(positionLocation, 2, contextGL.FLOAT, false, 0, 0);

    setRectangle(contextGL, imageObjectArray[0].x, imageObjectArray[0].y, imageObjectArray[0].width, imageObjectArray[0].height);

    // draw
    contextGL.drawArrays(contextGL.TRIANGLES, 0, 6);

You need to split step 2 into separate steps. The first step, creating the vertex buffer for posistions, should only be executed once. The second step, filling the position of the image, needs to be executed for each image you want to draw.

您需要将步骤2拆分为单独的步骤。第一步,为posistions创建顶点缓冲区,只应执行一次。第二步,填充图像的位置,需要为您想要绘制的每个图像执行。

I should say that my suggestions will not give the optimal implementation but it will get you drawing more than 1 image. To be optimal you should consider doing:

我应该说我的建议不会给出最佳实现,但它会让你绘制超过1个图像。为了达到最佳,你应该考虑做:

  • Implement texture atlasing (pack all your images onto a single texture).
  • 实现纹理图集(将所有图像打包到单个纹理上)。

  • Only upload your texture and position coordinates once.
  • 仅上传纹理和位置坐标一次。

  • Use a better vertex and fragment shader to pick which image is being drawn (texture coordinate offset), where it is being drawn (position offset) and how large it should be (width & height scaling)
  • 使用更好的顶点和片段着色器来选择正在绘制的图像(纹理坐标偏移),绘制的位置(位置偏移)以及它应该有多大(宽度和高度缩放)

#1


5  

The following code does the following:

以下代码执行以下操作:

  1. Compiles the vertex and fragment shaders
  2. 编译顶点和片段着色器

  3. Links them together into a shader program
  4. 将它们链接到一个着色器程序中

  5. Creates a vertex buffer to hold texture coordinates and fills it (texCoordBuffer)
  6. 创建一个顶点缓冲区来保存纹理坐标并填充它(texCoordBuffer)

  7. Creates a texture (createTexture)
  8. 创建纹理(createTexture)

  9. Configures how the texture is sampled (texParameteri)
  10. 配置纹理的采样方式(texParameteri)

The above 5 steps only need to be run once.

以上5个步骤只需要运行一次。

    vertexShader = createShaderFromScriptElement(contextGL, "2d-vertex-shader");
    fragmentShader = createShaderFromScriptElement(contextGL, "2d-fragment-shader");

    program = createProgram(contextGL, [vertexShader, fragmentShader]);
    contextGL.useProgram(program);

    var positionLocation = contextGL.getAttribLocation(program, "a_position");

    var texCoordLocation = contextGL.getAttribLocation(program, "a_texCoord");

    var texCoordBuffer = contextGL.createBuffer();
    contextGL.bindBuffer(contextGL.ARRAY_BUFFER, texCoordBuffer);

    contextGL.enableVertexAttribArray(texCoordLocation);
    contextGL.vertexAttribPointer(texCoordLocation, 2, contextGL.FLOAT, false, 0, 0);

    var texture = contextGL.createTexture();
    contextGL.bindTexture(contextGL.TEXTURE_2D, texture);

    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_WRAP_S, contextGL.CLAMP_TO_EDGE);
    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_WRAP_T, contextGL.CLAMP_TO_EDGE);
    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_MIN_FILTER, contextGL.NEAREST);
    contextGL.texParameteri(contextGL.TEXTURE_2D, contextGL.TEXTURE_MAG_FILTER, contextGL.NEAREST);

    setRectangle(contextGL, 0.0, 0.0, 1.0, 1.0);

The rest of the code must execute for each image you want to draw and it does the following:

其余代码必须为您要绘制的每个图像执行,并执行以下操作:

  1. Uploads the image into the texture (texImage2D)
  2. 将图像上传到纹理(texImage2D)

  3. Creates a vertex buffer to hold positions and fills it (buffer)
  4. 创建一个顶点缓冲区来保持位置并填充它(缓冲区)

  5. Calls drawArrays
    contextGL.texImage2D(contextGL.TEXTURE_2D, 0, contextGL.RGBA, contextGL.RGBA,contextGL.UNSIGNED_BYTE, imageObjectArray[0].displayObject.data);

    var resolutionLocation = contextGL.getUniformLocation(program, "u_resolution");
    contextGL.uniform2f(resolutionLocation, canvas.width, canvas.height);

    var buffer = contextGL.createBuffer();
    contextGL.bindBuffer(contextGL.ARRAY_BUFFER, buffer);
    contextGL.enableVertexAttribArray(positionLocation);
    contextGL.vertexAttribPointer(positionLocation, 2, contextGL.FLOAT, false, 0, 0);

    setRectangle(contextGL, imageObjectArray[0].x, imageObjectArray[0].y, imageObjectArray[0].width, imageObjectArray[0].height);

    // draw
    contextGL.drawArrays(contextGL.TRIANGLES, 0, 6);

You need to split step 2 into separate steps. The first step, creating the vertex buffer for posistions, should only be executed once. The second step, filling the position of the image, needs to be executed for each image you want to draw.

您需要将步骤2拆分为单独的步骤。第一步,为posistions创建顶点缓冲区,只应执行一次。第二步,填充图像的位置,需要为您想要绘制的每个图像执行。

I should say that my suggestions will not give the optimal implementation but it will get you drawing more than 1 image. To be optimal you should consider doing:

我应该说我的建议不会给出最佳实现,但它会让你绘制超过1个图像。为了达到最佳,你应该考虑做:

  • Implement texture atlasing (pack all your images onto a single texture).
  • 实现纹理图集(将所有图像打包到单个纹理上)。

  • Only upload your texture and position coordinates once.
  • 仅上传纹理和位置坐标一次。

  • Use a better vertex and fragment shader to pick which image is being drawn (texture coordinate offset), where it is being drawn (position offset) and how large it should be (width & height scaling)
  • 使用更好的顶点和片段着色器来选择正在绘制的图像(纹理坐标偏移),绘制的位置(位置偏移)以及它应该有多大(宽度和高度缩放)