Why does the order of the binding buffers seem to matter in a simple WebGL program?

Sorry if this is really stupid. I have the weirdest WebGL problem. I wrote a really basic WebGL program to render a sprite.

<!DOCTYPE html>
<html lang="en">
<head><title></title>
<style>
html, body { height: 100%; margin: 0; padding: 0; overflow: hidden; }
canvas { position: absolute; top:0;left:0; cursor: none; }
</style></head><body>
<canvas id="webgltestingthing"></canvas>
<script>
var scene,gl,cursor,sh={};

var vertexShader="\
precision mediump float;\
attribute vec2 a_position;\
attribute vec2 a_texCoord;\
uniform vec2 u_resolution;\
varying vec2 v_texCoord;\
void main() {\
    gl_Position = vec4(a_position/u_resolution*2.0-1.0, 0, 1);\
    v_texCoord = a_texCoord;\
}";
var fragShader="\
precision mediump float;\
uniform sampler2D u_image;\
varying vec2 v_texCoord;\
void main() {\
    gl_FragColor = texture2D(u_image, v_texCoord);\
}";

function resizeScene() {
    document.getElementById("webgltestingthing").width = document.body.clientWidth;
    document.getElementById("webgltestingthing").height = document.body.clientHeight;
    if(gl) gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); }
mouseX=-1000; mouseY=-1000;
function trackMouse(e) { e = e || window.event; mouseX=event.pageX; mouseY=event.pageY; }
function trackMouseOut() { mouseX=-1000; mouseY=-1000; }
function createShader(gl, type, source) {
    var shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (success) { return shader; }
    console.log(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); }
function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    var success = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (success) { return {p: program, a: {}, u: {}}; }
    console.log(gl.getProgramInfoLog(program)); gl.deleteProgram(program); }
function createTexture(url,onload) {
    this.img=new Image(); this.onload=onload; this.loaded=false;
    this.img.src=url;
    this.w=0; this.h=0; this.texture=gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, this.texture);
    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.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    postload=function() {
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.img);
        this.loaded=true; this.onload(); }
    this.img.addEventListener("load",postload.bind(this));
    return this; }
function renderSprite(t,x,y) {
    if(!t.loaded) return;
    var x1=x,y1=scene.clientHeight-y,x2=x+t.img.width,y2=y1-t.img.height;
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, t.texture);
    gl.useProgram(sh.sprite.p);

    gl.bindBuffer(gl.ARRAY_BUFFER, sh.sprite.a.texCoordBuffer);
    gl.enableVertexAttribArray(sh.sprite.a.texCoord);
    gl.vertexAttribPointer(sh.sprite.a.texCoord, 2, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, sh.sprite.a.positionBuffer);
    gl.enableVertexAttribArray(sh.sprite.a.position);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW);
    gl.vertexAttribPointer(sh.sprite.a.position, 2, gl.FLOAT, false, 0, 0);

    gl.uniform2fv(sh.sprite.u.res, [scene.clientWidth, scene.clientHeight]);
    gl.uniform1i(sh.sprite.u.img, 0);

    gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function renderStuff() {
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    if(mouseX>0) renderSprite(cursor,mouseX,mouseY);
    requestAnimationFrame(renderStuff);
}
(function() {
    scene=document.getElementById("webgltestingthing"); resizeScene();
    window.addEventListener('resize', resizeScene, true);
    window.addEventListener('mousemove', trackMouse, true);
    scene.addEventListener('mouseout', trackMouseOut, true);
    gl=scene.getContext("experimental-webgl");

    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
    sh.f_sprite = createShader(gl, gl.FRAGMENT_SHADER, fragShader);
    sh.v_bypixels = createShader(gl, gl.VERTEX_SHADER, vertexShader);

    sh.sprite = createProgram(gl, sh.v_bypixels, sh.f_sprite);
    sh.sprite.u.res = gl.getUniformLocation(sh.sprite.p, "u_resolution");
    sh.sprite.u.img = gl.getUniformLocation(sh.sprite.p, 'u_image');
    sh.sprite.a.position = gl.getUniformLocation(sh.sprite.p, "a_position");
    sh.sprite.a.positionBuffer = gl.createBuffer();
    sh.sprite.a.texCoord = gl.getUniformLocation(sh.sprite.p, "a_texCoord");
    sh.sprite.a.texCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, sh.sprite.a.texCoordBuffer);
    gl.bufferData(gl.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]), gl.STATIC_DRAW);

    cursor=createTexture("graphics/cursor.png",function() { renderStuff(); });
})();
</script>
</body>
</html>

      

A blank white screen is displayed. I was hoping this would render the texture at the mouse position. There are no errors in the console. Then I played around a bit and noticed something strange that I could not explain. I added this to my fragment shader:

if(v_texCoord==vec2(0.0,0.0)) gl_FragColor = vec4(1.0,0.0,0.0,1.0);

      

Indeed, it turns out that the texture coordinate is everywhere in the image, apparently 0.0 / 0.0! WebGL now correctly displays a red border at the position where the sprite should be, with the exact size of the sprite. So my attention turned to the part where I set the buffer to pass in the texture coordinate, and something even weirder: if you change the code so that I first set the position buffer like this (in the rendersprite function):

    gl.bindBuffer(gl.ARRAY_BUFFER, sh.sprite.a.positionBuffer);
    gl.enableVertexAttribArray(sh.sprite.a.position);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW);
    gl.vertexAttribPointer(sh.sprite.a.position, 2, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, sh.sprite.a.texCoordBuffer);
    gl.enableVertexAttribArray(sh.sprite.a.texCoord);
    gl.vertexAttribPointer(sh.sprite.a.texCoord, 2, gl.FLOAT, false, 0, 0);

      

Now my red box is no longer there! So it indicates that for some reason whatever buffer I set first in this simple shader is not working, but I see no reason. I've tried all sorts of ways and tried to shuffle things and the like until no luck. I'm also confused as to why order matters at all.

Anyone have an idea?

+3


source to share


1 answer


In your customization function you use getUniformLocation

to query the locations of the attributes, the attributes are however not generic and their locations must be queried with getAttribLocation

.

The call getUniformLocation

in attributes returns null

, which is then dispatched to 0

in your calls enableVertexAttribArray

and vertexAttribPointer

so you bind the vertex attributes to the same location.



sh.sprite.a.position = gl.getAttribLocation(sh.sprite.p, "a_position");
sh.sprite.a.positionBuffer = gl.createBuffer();
sh.sprite.a.texCoord = gl.getAttribLocation(sh.sprite.p, "a_texCoord");
sh.sprite.a.texCoordBuffer = gl.createBuffer();

      

+1


source







All Articles