首页 > 解决方案 > WebGL:使用单独的纹理渲染两个对象

问题描述

我目前正在开发一个 WebGL 中的简单项目,该项目需要渲染多个带有纹理的简单图像的 2D 对象。实际项目会生成随机数量的对象,通常在 5 到 9 个之间,在画布周围为它们设置顶点数据以将它们分开,并且应该渲染,但是它一次只会渲染一个(通常是最后一个,但我可以更改 gl.activeTexture 以显示数组中的其他对象)。我尝试在这里使用关于着色器中纹理数组的问题,但无济于事,所以我最终创建了一个非常简单的测试程序,它只是尝试加载两个对象和纹理,一个在画布的左侧,另一个在右侧。

从这里开始,我尝试将所有内容完全分开,甚至为每个对象提供自己的着色器、程序、缓冲区和所有内容,然后在为每个对象调用 gl.drawElements 之前为每个对象绑定绘图调用中的所有内容。这仍然没有向我显示正确的结果,只出现了第二个纹理,但是它确实让我发现了我认为正在发生的事情。通过注释掉第二个的绑定和绘制调用,第一个纹理出现,但是它出现在第二个纹理的位置,而不是它的顶点应该放置的位置。所以,我假设这个程序(和我的项目代码)中发生的事情是它实际上正在绘制两者,但由于某种原因,将最后绘制的顶点应用于所有顶点,从而堆叠它们并且只显示顶部(或最后一张)。

我还尝试了对以下代码的混杂调整,仅使用一个程序,使用相同的索引,纹理坐标,还有一些注释掉的行,试图以不同的顺序进行调用。任何被注释掉的东西并不意味着我一定认为它是错误的或正确的,只是从我在这一点上漫无目的地尝试的各种事情来看。

我曾经使用过 OpenGL,并且在绘制多个具有自己纹理的对象时几乎没有遇到任何问题,而且我知道 WebGL 在某些方面(包括纹理)的工作方式与 OpenGL 不同,但我看不出我在哪里创建了问题。我相信这是非常简单的事情,任何指导将不胜感激。

我为长长的代码块道歉,它几乎只是直接输入我认为需要的所有内容,而无需尝试走任何捷径。initShaders 调用来自我在教科书中使用的 WebGL js 文件,并不是我编写的,而 loadImage 调用只是简单地<img>从 html 代码中加载一个。据我所知,正确加载图像没有问题。我只包括了第一个顶点和片段着色器,因为其他两个除了 id 之外是相同的。

<script id="vertex-shader1" type="x-shader/x-vertex">
        attribute vec4 vPosition;
        attribute vec2 vTexCoord;

        varying vec2 fTexCoord;

        void main() {
            fTexCoord = vTexCoord;
            gl_Position = vPosition;
        }

    </script>

    <script id="fragment-shader1" type="x-shader/x-fragment">
        precision mediump float;

        varying vec2 fTexCoord;

        uniform sampler2D texture;

        void main() {
            gl_FragColor = texture2D(texture, fTexCoord);
        }
    </script>
"use-strict"

var gl;

var images = [];

var program1;
var program2;
var texture1;
var texture2;

var vBuff1;
var vBuff2;
var iBuff1;
var iBuff2;
var tBuff1;
var tBuff2;

var vPos1;
var vPos2;
var fTexCoord1;
var fTexCoord2;

var sampler1;
var sampler2;

var vertices1 = [
    vec4(-0.8, 0.1, 0.0, 1.0),
    vec4(-0.8, 0.3, 0.0, 1.0),
    vec4(-0.6, 0.3, 0.0, 1.0),
    vec4(-0.6, 0.1, 0.0, 1.0)
];

var vertices2 = [
    vec4(0.1, 0.1, 0.0, 1.0),
    vec4(0.1, 0.3, 0.0, 1.0),
    vec4(0.3, 0.3, 0.0, 1.0),
    vec4(0.3, 0.1, 0.0, 1.0)
];

var indices1 = [
    0, 1, 2,
    0, 2, 3
];

var indices2 = [
    0, 1, 2,
    0, 2, 3
];

var tcs1 = [
    vec2(0, 0),
    vec2(0, 1),
    vec2(1, 1),
    vec2(1, 0)
];

var tcs2 = [
    vec2(0, 0),
    vec2(0, 1),
    vec2(1, 1),
    vec2(1, 0)
];

window.onload = function init() {
    var canvas = document.getElementById("gl-canvas");
    gl = WebGLUtils.setupWebGL(canvas);
    if (!gl) { alert("WebGL isn't available"); }

    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);

    loadImages();

    program1 = initShaders(gl, "vertex-shader1", "fragment-shader1");
    gl.useProgram(program1);

    vBuff1 = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices1), gl.STATIC_DRAW);

    vPos1 = gl.getAttribLocation(program1, "vPosition");
    gl.vertexAttribPointer(vPos1, 4, gl.FLOAT, false, 0, 0);
    //gl.enableVertexAttribArray(vPos1);

    iBuff1 = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff1);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices1), gl.STATIC_DRAW);

    tBuff1 = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, tBuff1);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(tcs1), gl.STATIC_DRAW);

    fTexCoord1 = gl.getAttribLocation(program1, "vTexCoord");
    gl.vertexAttribPointer(fTexCoord1, 2, gl.FLOAT, false, 0, 0);
    //gl.enableVertexAttribArray(fTexCoord1);

    sampler1 = gl.getUniformLocation(program1, "texture");

    texture1 = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture1);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, images[0]);
    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.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

    gl.bindTexture(gl.TEXTURE_2D, null);

///////////////////////////////////////////////////////////////////////////////////////
/*
    program2 = initShaders(gl, "vertex-shader2", "fragment-shader2");
    gl.useProgram(program2);
*/
    vBuff2 = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuff2);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices2), gl.STATIC_DRAW);

    vPos2 = gl.getAttribLocation(program1, "vPosition");
    gl.vertexAttribPointer(vPos2, 4, gl.FLOAT, false, 0, 0);
    //gl.enableVertexAttribArray(vPos2);

    iBuff2 = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff2);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices2), gl.STATIC_DRAW);

    tBuff2 = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, tBuff2);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(tcs2), gl.STATIC_DRAW);

    fTexCoord2 = gl.getAttribLocation(program1, "vTexCoord");
    gl.vertexAttribPointer(fTexCoord2, 2, gl.FLOAT, false, 0, 0);
    //gl.enableVertexAttribArray(fTexCoord2);

    sampler2 = gl.getUniformLocation(program1, "texture");

    texture2 = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture2);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, images[1]);
    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.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

    gl.bindTexture(gl.TEXTURE_2D, null);

    render();
};

function render() {
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.useProgram(program1);

    gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
    gl.enableVertexAttribArray(vPos1);
    gl.enableVertexAttribArray(fTexCoord1);

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture1);

    gl.uniform1i(sampler1, 0);

//  gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
//  gl.enableVertexAttribArray(vPos1);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff1);
    gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_BYTE, 0);

    //gl.bindTexture(gl.TEXTURE_2D, null);

//  gl.useProgram(program2);

    gl.bindBuffer(gl.ARRAY_BUFFER,vBuff2);
    gl.enableVertexAttribArray(vPos2);
    gl.enableVertexAttribArray(fTexCoord2);

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture2);

    gl.uniform1i(sampler2, 0);

//  gl.bindBuffer(gl.ARRAY_BUFFER, vBuff2);
//  gl.enableVertexAttribArray(vPos2);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff2);

    gl.drawElements(gl.TRIANGLES, indices2.length, gl.UNSIGNED_BYTE, 0);

    requestAnimFrame(render);
}

标签: javascriptwebgltextures

解决方案


首先,AFAIK 你的代码不能工作。它调用一个函数loadImages,然后立即使用图像。图像在浏览器中异步加载,因此您需要在图像加载时进行回调,或者使用异步函数

这是您的代码工作。首先,我做了一个loadImage返回 Promise 的。然后我创建了一个异步函数loadImages,使用它来加载所有图像并等待它们加载。然后我创建了另一个异步函数,称为main首先等待loadImages然后调用init

第二个问题是 WebGL1 中的属性是全局状态。这意味着您需要在渲染时而不是在初始化时设置它们,以便调用在渲染时进行gl.enableVertexAttribArray并且gl.vertexAttribPointer需要在渲染时使用适当的值来渲染您正在渲染的特定事物。gl.vertexAttribPointer将当前ARRAY_BUFFER绑定复制到该属性。

您可能会发现这些教程很有帮助,尤其是关于属性此状态图的教程,它们可能会帮助您可视化 WebGL 内部发生的事情

"use-strict";

const vec2 = (...args) => [...args];
const vec4 = (...args) => [...args];
const flatten = a => new Float32Array(a.flat());
const WebGLUtils = {
  setupWebGL: (canvas) => { return canvas.getContext('webgl'); },
};
const initShaders = (gl, vs, fs) => twgl.createProgram(gl, [vs, fs]);
const requestAnimFrame = requestAnimationFrame;

var gl;

var images = [];

var program1;
var program2;
var texture1;
var texture2;

var vBuff1;
var vBuff2;
var iBuff1;
var iBuff2;
var tBuff1;
var tBuff2;

var vPos1;
var vPos2;
var fTexCoord1;
var fTexCoord2;

var sampler1;
var sampler2;

var vertices1 = [
	vec4(-0.8, 0.1, 0.0, 1.0),
	vec4(-0.8, 0.3, 0.0, 1.0),
	vec4(-0.6, 0.3, 0.0, 1.0),
	vec4(-0.6, 0.1, 0.0, 1.0)
];

var vertices2 = [
	vec4(0.1, 0.1, 0.0, 1.0),
	vec4(0.1, 0.3, 0.0, 1.0),
	vec4(0.3, 0.3, 0.0, 1.0),
	vec4(0.3, 0.1, 0.0, 1.0)
];

var indices1 = [
	0, 1, 2,
	0, 2, 3
];

var indices2 = [
	0, 1, 2,
	0, 2, 3
];

var tcs1 = [
	vec2(0, 0),
	vec2(0, 1),
	vec2(1, 1),
	vec2(1, 0)
];

var tcs2 = [
	vec2(0, 0),
	vec2(0, 1),
	vec2(1, 1),
	vec2(1, 0)
];

function init() {
	var canvas = document.getElementById("gl-canvas");
	gl = WebGLUtils.setupWebGL(canvas);
	if (!gl) { alert("WebGL isn't available"); }

	gl.viewport(0, 0, canvas.width, canvas.height);
	gl.clearColor(0.0, 0.0, 0.0, 1.0);

	program1 = initShaders(gl, "vertex-shader1", "fragment-shader1");
	gl.useProgram(program1);

	vBuff1 = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
	gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices1), gl.STATIC_DRAW);

	vPos1 = gl.getAttribLocation(program1, "vPosition");

	iBuff1 = gl.createBuffer();
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff1);
	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices1), gl.STATIC_DRAW);

	tBuff1 = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, tBuff1);
	gl.bufferData(gl.ARRAY_BUFFER, flatten(tcs1), gl.STATIC_DRAW);

	fTexCoord1 = gl.getAttribLocation(program1, "vTexCoord");

	sampler1 = gl.getUniformLocation(program1, "texture");

	texture1 = gl.createTexture();
	gl.bindTexture(gl.TEXTURE_2D, texture1);
	gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, images[0]);
	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.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

	gl.bindTexture(gl.TEXTURE_2D, null);

///////////////////////////////////////////////////////////////////////////////////////
/*
	program2 = initShaders(gl, "vertex-shader2", "fragment-shader2");
	gl.useProgram(program2);
*/
	vBuff2 = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, vBuff2);
	gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices2), gl.STATIC_DRAW);

	vPos2 = gl.getAttribLocation(program1, "vPosition");

	iBuff2 = gl.createBuffer();
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff2);
	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices2), gl.STATIC_DRAW);

	tBuff2 = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, tBuff2);
	gl.bufferData(gl.ARRAY_BUFFER, flatten(tcs2), gl.STATIC_DRAW);

	fTexCoord2 = gl.getAttribLocation(program1, "vTexCoord");
	gl.vertexAttribPointer(fTexCoord2, 2, gl.FLOAT, false, 0, 0);
	//gl.enableVertexAttribArray(fTexCoord2);

	sampler2 = gl.getUniformLocation(program1, "texture");

	texture2 = gl.createTexture();
	gl.bindTexture(gl.TEXTURE_2D, texture2);
	gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, images[1]);
	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.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

	gl.bindTexture(gl.TEXTURE_2D, null);

	render();
};

function render() {
	gl.clear(gl.COLOR_BUFFER_BIT);

	gl.useProgram(program1);

	gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
	gl.enableVertexAttribArray(vPos1);
	gl.vertexAttribPointer(vPos1, 4, gl.FLOAT, false, 0, 0);
  
	gl.bindBuffer(gl.ARRAY_BUFFER, tBuff1);
	gl.enableVertexAttribArray(fTexCoord1);
	gl.vertexAttribPointer(fTexCoord1, 2, gl.FLOAT, false, 0, 0);

	gl.activeTexture(gl.TEXTURE0);
	gl.bindTexture(gl.TEXTURE_2D, texture1);

	gl.uniform1i(sampler1, 0);

	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff1);
	gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_BYTE, 0);

	gl.bindBuffer(gl.ARRAY_BUFFER,vBuff2);
	gl.enableVertexAttribArray(vPos2);
	gl.vertexAttribPointer(vPos2, 4, gl.FLOAT, false, 0, 0);
  
	gl.activeTexture(gl.TEXTURE0);
	gl.bindTexture(gl.TEXTURE_2D, texture2);

	gl.uniform1i(sampler2, 0);

	gl.bindBuffer(gl.ARRAY_BUFFER,tBuff2);
	gl.enableVertexAttribArray(fTexCoord2);
	gl.vertexAttribPointer(fTexCoord2, 2, gl.FLOAT, false, 0, 0);

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff2);

	gl.drawElements(gl.TRIANGLES, indices2.length, gl.UNSIGNED_BYTE, 0);

	requestAnimFrame(render);
}

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.crossOrigin = 'anonymous';
    img.src = url;
  });
}

async function loadImages(imgs) {
  images = await Promise.all(imgs.map(loadImage));
}

async function main() {
  await loadImages([
    'https://webglfundamentals.org/webgl/resources/f-texture.png',
    'https://webglfundamentals.org/webgl/lessons/resources/noodles-01.jpg',
  ]);
  init();
}
main();
<script id="vertex-shader1" type="x-shader/x-vertex">
  attribute vec4 vPosition;
  attribute vec2 vTexCoord;

  varying vec2 fTexCoord;

  void main() {
    fTexCoord = vTexCoord;
    gl_Position = vPosition;
  }

</script>

<script id="fragment-shader1" type="x-shader/x-fragment">
  precision mediump float;

  varying vec2 fTexCoord;

  uniform sampler2D texture;

  void main() {
    gl_FragColor = texture2D(texture, fTexCoord);
  }
</script>
<canvas id="gl-canvas"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>


推荐阅读