WebGL中绘制多个2D图像

4
我一直在学习html5rocks上的教程,成功地使用WebGL制作了一个JavaScript程序,在画布上显示了一张图片。下面是我所写的代码。
然而,问题是似乎没有人能向你展示如何在WebGL中绘制多个对象。我以前没有直接使用过WebGL,因此对我来说不太直观。
要如何修改此代码以绘制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);
}

我的目标是开发一个(非常基础的)2D像素游戏,但不使用库(除了可能是glMatrix.js)。

[编辑] 我的渲染函数之前出现了错误,已经修复。

1个回答

5
以下代码执行以下操作:
  1. 编译顶点和片段着色器
  2. 将它们链接到一个着色器程序中
  3. 创建一个顶点缓冲区来保存纹理坐标并填充它(texCoordBuffer)
  4. 创建一个纹理(createTexture)
  5. 配置纹理的采样方式(texParameteri)
以上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);

其余的代码必须针对每个要绘制的图像执行,并且它执行以下操作:
  1. 将图像上传到纹理中(texImage2D)
  2. 创建一个顶点缓冲区来保存位置并将其填充(buffer)
  3. 调用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);
// 绘制 contextGL.drawArrays(contextGL.TRIANGLES, 0, 6);

你需要将步骤2拆分为不同的步骤。第一步,为位置创建顶点缓冲区,应只执行一次。第二步,填充图像位置,需要针对要绘制的每个图像执行。

我应该说,我的建议不会提供最佳实现,但它将使您能够绘制超过1个图像。要达到最佳效果,您应该考虑执行以下操作:

  • 实现纹理打包(将所有图像打包到单个纹理中)。
  • 仅上传一次纹理和位置坐标。
  • 使用更好的顶点和片段着色器来选择正在绘制哪个图像(纹理坐标偏移),在哪里绘制(位置偏移)以及应该有多大(宽度和高度缩放)。

感谢您的详细解释!但是,您说“您需要将步骤2拆分为单独的步骤。第一步,为位置创建顶点缓冲区,应该只执行一次”-您所说的“一次”是什么意思?我不是应该为每个图像执行整个步骤2吗? - Spectraljump
你想把 var buffer = contextGL.createBuffer(); 从循环中移出,其余部分保持不变。 - Cutch
另一种方法是在第二步创建一个单位矩形,并添加一些统一变量(如矩阵)来将该单位矩阵平移、缩放和旋转到您想要绘制图像的大小和位置。哪种方法更快或更好,更新每个图像的缓冲区还是为每个图像计算不同的矩阵,我不知道。 - gman
谢谢大家,我明白了。我会在这之后立即着手提高效率。 - Spectraljump

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接