javascript - 为什么绑定(bind)缓冲区的顺序在简单的 WebGL 程序中似乎很重要?

标签 javascript webgl

抱歉,如果这真的很愚蠢。我遇到了最奇怪的 WebGL 问题。我编写了一个非常基本的 WebGL 程序来渲染 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>

这会显示一个空白的白色屏幕。我希望它能在鼠标位置显示纹理。控制台或任何东西都没有错误。然后我玩了一些,发现了一些我无法解释的奇怪现象。我将其添加到我的片段着色器中:

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 函数中):

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

现在我的红盒子已经不存在了!所以这向我表明,出于某种原因,我首先在这个简单的着色器上设置的任何缓冲区都不起作用,但我看不出任何原因。我已经尝试了各种不同的方法,并尝试将事情改组等等,到目前为止还没有运气。我也很困惑为什么顺序很重要。

有人知道吗?

最佳答案

在您的设置函数中,您使用的是 getUniformLocation要查询属性位置,但是属性不是制服,需要使用 getAttribLocation 查询它们的位置相反。

在属性上调用 getUniformLocation 返回 null 然后在 enableVertexAttribArray 中转换为 0 >vertexAttribPointer 调用,因此您将顶点属性绑定(bind)到同一位置。

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

关于javascript - 为什么绑定(bind)缓冲区的顺序在简单的 WebGL 程序中似乎很重要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44737355/

相关文章:

javascript - 绑定(bind)到表单时的主干模型状态

Javascript:在函数内调用函数时 window.location.href 不重定向

javascript - 在 Rhino eval 中包含一个 JavaScript 文件

opengl-es - OpenGL ES(WebGL)渲染许多小对象

javascript - 运行 webGL 的最低系统要求?

javascript - Shadertoy 的音频着色器如何工作?

javascript - 如何在 THREE.js 中生成粒子系统

javascript - WebGL 在页面上崩溃时的事件?

javascript - 遵循 Ember.js 2.4 "Getting Started"指南时,我无法构建应用程序

javascript - 获取 regex .replace() 出现的次数