在 webGL 中用.jpg图像覆盖矩形

covering rectangle with .jpg image in webGL

本文关键字:覆盖 图像 jpg webGL 中用      更新时间:2023-09-26

我在webGL世界中乞求,我在将图像绘制成我制作的矩形时遇到了一些麻烦。

首先,我为矩形制作了形状:

var posLoc = gl.getAttribLocation(program, "pos");
gl.enableVertexAttribArray(posLoc);
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var vertices = [
 1.0,  2.0,  0.0, 
-1.0,  2.0,  0.0, 
-1.0, -2.0,  0.0, 
 1.0, -2.0,  0.0  
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 0, 0);

然后我创建了纹理,使其处于活动状态并制作了如下所示的 mitmap:

var texture = null;
var image = document.createElement("img");
image.crossOrigin = "anonymous";
image.src = "https://upload.wikimedia.org/wikipedia/commons/3/38/JPEG_example_JPG_RIP_010.jpg";
image.onload = function() {
    texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
    gl.generateMipmap(gl.TEXTURE_2D);
    ...
}

最后,我试图绘制所有东西都不成功:

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);

这是一个完整的代码

var gl = document.querySelector("canvas").getContext("experimental-webgl");
var str = document.querySelector("#vs").textContent;
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, str);
gl.compileShader(vs);
	
var str = document.querySelector("#fs").textContent;
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, str);
gl.compileShader(fs);
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram(program);
var samplerLoc = gl.getUniformLocation(program, "sampler");
gl.uniform1i(samplerLoc, 0);
gl.enable(gl.DEPTH_TEST);
gl.clearColor(0.8, 0.6, 0.2, 1);
var texture = null;
var image = document.createElement("img");
image.crossOrigin = "anonymous";
image.src = "https://upload.wikimedia.org/wikipedia/commons/3/38/JPEG_example_JPG_RIP_010.jpg";
image.onload = function() {
		var posLoc = gl.getAttribLocation(program, "pos");
    gl.enableVertexAttribArray(posLoc);
    var vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    var vertices = [
       1.0,  2.0,  0.0, 
      -1.0,  2.0,  0.0, 
      -1.0, -2.0,  0.0, 
       1.0, -2.0,  0.0  
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 0, 0);
    var indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    var indices = [0, 1, 2, 0, 1, 3, 1, 2, 3, 0, 2, 3];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
		
    texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
    gl.generateMipmap(gl.TEXTURE_2D);
    
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST);
    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);
}
<canvas></canvas>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 pos;
attribute vec2 texture;
varying vec2 varyingTexture;
void main(void) {
	gl_Position = vec4(pos, 1.0);
	varyingTexture = texture;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 varyingTexture;
uniform sampler2D sampler;	
    
void main(void) {
	gl_FragColor = texture2D(sampler, varyingTexture);
}
</script>

有什么想法吗?

查看控制台会发现您正在应用不兼容的纹理过滤:

texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'. Or the texture is Float or Half Float type with linear filtering while OES_float_linear or OES_half_float_linear extension is not enabled.

事实上,您的纹理尺寸不是 2 的幂 (NPOT),因此您必须使用不需要 mipmap 的纹理过滤器,也无法生成 mipmap:

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// Use linear filter for minification
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// removed generate mipmap call

接下来,您需要设置适当的包装模式,对于 NPOT 纹理,唯一兼容的包装模式是 CLAMP_TO_EDGE

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);

然后,您将看到矩形更改了其颜色,但顶点数据不包含任何纹理坐标,因此它们将在所有位置替换为0,从而导致仅应用最顶层像素的颜色。要解决此问题,您需要获取texture属性的位置并启用属性数组:

var texLoc = gl.getAttribLocation(program, "texture");
gl.enableVertexAttribArray(texLoc); // Could use 1 directly here

接下来是您的顶点数据,您没有转换顶点位置,因此您使用从 -1 到 +1 的规范化设备坐标 (NDC),但是您的坐标超过了这个范围。查看此链接以了解有关坐标空间的详细信息。创建屏幕对齐的四边形并添加缺少的纹理坐标,顶点数据如下所示:

var vertices = [// '/'/ texture coordinates
   1.0,  1.0,  0.0, 1,0,
  -1.0,  1.0,  0.0, 0,0,
  -1.0, -1.0,  0.0, 0,1,
   1.0, -1.0,  0.0, 1,1
];// /'/'/'/' Fixed positions to lie within NDC

此外,您还使用索引缓冲区定义了四个三角形,而您只需要两个来构建一个四边形:

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

最后相应地设置顶点属性指针:

// Note the last two arguments are defined in BYTES
gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 20, 0);
gl.vertexAttribPointer(texLoc, 2, gl.FLOAT, false, 20, 12);

结果:

var gl = document.querySelector("canvas").getContext("experimental-webgl");
var str = document.querySelector("#vs").textContent;
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, str);
gl.compileShader(vs);
	
var str = document.querySelector("#fs").textContent;
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, str);
gl.compileShader(fs);
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram(program);
var samplerLoc = gl.getUniformLocation(program, "sampler");
gl.uniform1i(samplerLoc, 0);
gl.clearColor(0.8, 0.6, 0.2, 1);
var texture = null;
var image = document.createElement("img");
image.crossOrigin = "anonymous";
image.src = "http://upload.wikimedia.org/wikipedia/commons/3/38/JPEG_example_JPG_RIP_010.jpg";
image.onload = function() {
    // Get and enable vertex attribute for texture, note I renamed "texture" to "tex" in the shader and here
    var posLoc = gl.getAttribLocation(program, "pos");
    var texLoc = gl.getAttribLocation(program, "tex");
    gl.enableVertexAttribArray(0);
    gl.enableVertexAttribArray(1);
    var vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    // Add texture coordinates, fix positions
    var vertices = [
       1.0,  1.0,  0.0, 1,0,
      -1.0,  1.0,  0.0, 0,0,
      -1.0, -1.0,  0.0, 0,1,
       1.0, -1.0,  0.0, 1,1
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 20, 0);
    gl.vertexAttribPointer(texLoc, 2, gl.FLOAT, false, 20, 12);
    var indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    // Remove excess indices
    var indices = [0, 1, 2, 3, 2, 0];
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
		
    texture = gl.createTexture();
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    // Use linear filter for minification
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    // Do not generate mip maps
    // Apply appropriate wrap mode
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST);
    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);
}
<canvas></canvas>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 pos;
attribute vec2 tex;
varying vec2 varyingTexture;
void main(void) {
	gl_Position = vec4(pos, 1.0);
	varyingTexture = tex;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 varyingTexture;
uniform sampler2D sampler;	
    
void main(void) {
	gl_FragColor = texture2D(sampler, varyingTexture);
}
</script>