必须渲染缓冲区纹理尺寸是2的幂?

时间:2014-11-11 13:16:02

标签: webgl

我们用于WebGL渲染缓冲区存储的纹理是否需要具有二次幂的维度?

背景信息

我正在追逐客户在此设置中报告的FRAMEBUFFER_INCOMPLETE_ATTACHMENT:

Windows 7 Enterprise 32位 Firefox版本:33 视频卡:Intel Q45 / Q43 Express芯片组驱动程序版本8.13.10.2413

到目前为止,我不知道为什么会这样,所以猜测它可能与NPOT纹理有关。

这是我的渲染缓冲区实现,它还没有两个纹理:

SceneJS._webgl.RenderBuffer = function (cfg) {

    /**
     * True as soon as this buffer is allocated and ready to go
     * @type {boolean}
     */
    this.allocated = false;

    this.canvas = cfg.canvas;
    this.gl = cfg.canvas.gl;
    this.buf = null;
    this.bound = false;
};

/**
 * Called after WebGL context is restored.
 */
SceneJS._webgl.RenderBuffer.prototype.webglRestored = function (_gl) {
    this.gl = _gl;
    this.buf = null;
};

/**
 * Binds this buffer
 */
SceneJS._webgl.RenderBuffer.prototype.bind = function () {
    this._touch();
    if (this.bound) {
        return;
    }
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buf.framebuf);
    this.bound = true;
};


SceneJS._webgl.RenderBuffer.prototype._touch = function () {

    var width = this.canvas.canvas.width;
    var height = this.canvas.canvas.height;

    if (this.buf) { // Currently have a buffer
        if (this.buf.width == width && this.buf.height == height) { // Canvas size unchanged, buffer still good
            return;
        } else { // Buffer needs reallocation for new canvas size
            this.gl.deleteTexture(this.buf.texture);
            this.gl.deleteFramebuffer(this.buf.framebuf);
            this.gl.deleteRenderbuffer(this.buf.renderbuf);
        }
    }

    this.buf = {
        framebuf: this.gl.createFramebuffer(),
        renderbuf: this.gl.createRenderbuffer(),
        texture: this.gl.createTexture(),
        width: width,
        height: height
    };

    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buf.framebuf);
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.buf.texture);
    this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
    this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
    this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
    this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
   

    try {
        // Do it the way the spec requires
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null);
    } catch (exception) {
        // Workaround for what appears to be a Minefield bug.
        var textureStorage = new WebGLUnsignedByteArray(width * height * 3);
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, textureStorage);
    }

    this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.buf.renderbuf);
    this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT16, width, height);
    this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, this.buf.texture, 0);
    this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, this.buf.renderbuf);
    this.gl.bindTexture(this.gl.TEXTURE_2D, null);
    this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
    // Verify framebuffer is OK
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buf.framebuf);

    if (!this.gl.isFramebuffer(this.buf.framebuf)) {
        throw SceneJS_error.fatalError(SceneJS.errors.ERROR, "Invalid framebuffer");
    }

    var status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);

    switch (status) {

        case this.gl.FRAMEBUFFER_COMPLETE:
            break;

        case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
            throw SceneJS_error.fatalError(SceneJS.errors.ERROR, "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");

        case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
            throw SceneJS_error.fatalError(SceneJS.errors.ERROR, "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");

        case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
            throw SceneJS_error.fatalError(SceneJS.errors.ERROR, "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");

        case this.gl.FRAMEBUFFER_UNSUPPORTED:
            throw SceneJS_error.fatalError(SceneJS.errors.ERROR, "Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");

        default:
            throw SceneJS_error.fatalError(SceneJS.errors.ERROR, "Incomplete framebuffer: " + status);
    }

    this.bound = false;
};

/**
 * Clears this renderbuffer
 */
SceneJS._webgl.RenderBuffer.prototype.clear = function () {
    if (!this.bound) {
        throw "Render buffer not bound";
    }
    this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
    this.gl.disable(this.gl.BLEND);
};

/**
 * Reads buffer pixel at given coordinates
 */
SceneJS._webgl.RenderBuffer.prototype.read = function (pickX, pickY) {
    var x = pickX;
    var y = this.canvas.canvas.height - pickY;
    var pix = new Uint8Array(4);
    this.gl.readPixels(x, y, 1, 1, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pix);
    return pix;
};

/**
 * Unbinds this renderbuffer
 */
SceneJS._webgl.RenderBuffer.prototype.unbind = function () {
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
    this.bound = false;
};

/** Returns the texture
 */
SceneJS._webgl.RenderBuffer.prototype.getTexture = function () {
    var self = this;
    return {
        bind: function (unit) {
            if (self.buf && self.buf.texture) {
                self.gl.activeTexture(self.gl["TEXTURE" + unit]);
                self.gl.bindTexture(self.gl.TEXTURE_2D, self.buf.texture);
                return true;
            }
            return false;
        },
        unbind: function (unit) {
            if (self.buf && self.buf.texture) {
                self.gl.activeTexture(self.gl["TEXTURE" + unit]);
                self.gl.bindTexture(self.gl.TEXTURE_2D, null);
            }
        }
    };
};

/** Destroys this buffer
 */
SceneJS._webgl.RenderBuffer.prototype.destroy = function () {
    if (this.buf) {
        this.gl.deleteTexture(this.buf.texture);
        this.gl.deleteFramebuffer(this.buf.framebuf);
        this.gl.deleteRenderbuffer(this.buf.renderbuf);
        this.buf = null;
        this.bound = false;
    }
};

1 个答案:

答案 0 :(得分:0)

据我所知(我不使用WebGL),WebGL规范委托这些FBO相关调用的OpenGL ES 2.0规范。每个组件8位的RGBA不是ES 2.0中作为渲染目标支持的格式。许多设备都支持它(使用OES_rgb8_rgba8扩展名做广告),但它不是标准的一部分。

您用作COLOR_ATTACHMENT0的纹理是具有8位分量的RGBA:

this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0,
                   this.gl.RGBA, this.gl.UNSIGNED_BYTE, textureStorage);

尝试将其指定为RGB565,颜色可渲染:

this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0,
                   this.gl.RGB, this.gl.UNSIGNED_SHORT_5_6_5, textureStorage);

如果您需要纹理中的alpha分量,RGBA4444或RGB5_A1是您唯一的便携式选项:

this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0,
                   this.gl.RGBA, this.gl.UNSIGNED_SHORT_4_4_4_4, textureStorage);
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0,
                   this.gl.RGBA, this.gl.UNSIGNED_SHORT_5_5_5_1, textureStorage);

规范实际上看起来有些矛盾。在" WebGL和OpenGL ES 2.0之间的差异",它说:

  

以下帧缓冲对象附件的组合,当所有附件都是帧缓冲附件完成,非零且具有相同的宽度和高度时,必须导致帧缓冲完成帧缓冲:
  COLOR_ATTACHMENT0 = RGBA / UNSIGNED_BYTE纹理

乍看之下表明支持RGBA/UNSIGNED_BYTE。但是,条件是"当所有附件都是帧缓冲附件完成时,根据ES 2.0规范,具有此格式的附件附件完整。并且在WebGL规范中没有覆盖什么"附件完成"装置