Why does the order of binding buffers seem to matter in a simple WebGL program?
对不起,如果这真的很愚蠢。我遇到了最奇怪的 WebGL 问题。我编写了一个非常基本的 WebGL 程序来渲染一个sprite。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | <!DOCTYPE html> <html lang="en"> <head> <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> 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(); }); })(); </body> </html> |
这将显示一个空白的白色屏幕。我希望它会在鼠标位置显示纹理。控制台或任何东西都没有错误。然后我玩了一下,发现了一些我无法解释的奇怪现象。我将此添加到我的片段着色器中:
1 | if(v_texCoord==vec2(0.0,0.0)) gl_FragColor = vec4(1.0,0.0,0.0,1.0); |
事实上,图像上各处的纹理坐标显然是 0.0/0.0! WebGL 现在可以在 sprite 应该在的位置正确渲染一个红色框,与 sprite 的确切大小相同。所以然后我的注意力转向了我设置缓冲区以传递纹理坐标的部分,甚至发生了更奇怪的事情:如果你交换代码以便我首先设置位置缓冲区,就像这样(在 rendersprite 函数中):
1 2 3 4 5 6 7 | 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); |
现在我的红盒子已经不存在了!所以这向我表明,出于某种原因,我首先在这个简单的着色器上设置的任何缓冲区都不起作用,但我看不出任何原因。我尝试了各种不同的方式,并尝试过洗牌等等,到目前为止还没有运气。我也很困惑为什么顺序很重要。
有人知道吗?
在你的设置函数中,你使用
在属性上调用
1 2 3 4 | 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(); |