Cesium原理篇:6 Renderer模塊(2: Texture)


       Texture也是WebGL中重要的概念,使用起來也很簡單。但有句話叫大道至簡,如果真的想要用好紋理,里面的水其實也是很深的。下面我們來一探究竟。

       下面是WebGL中創建一個紋理的最簡過程:

var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
// 創建紋理句柄
var 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_WRAP_S, gl.CLAMP_TO_EDGE);
// 釋放
gl.bindTexture(gl.TEXTURE_2D, null);

       如果你覺得上面的這段代碼簡單易懂,不妨在看看WebGL中提供的gl.glTexImage2D的重載方法:

void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ArrayBufferView? pixels);
void gl.texImage2D(target, level, internalformat, format, type, ImageData? pixels);
void gl.texImage2D(target, level, internalformat, format, type, HTMLImageElement? pixels);
void gl.texImage2D(target, level, internalformat, format, type, HTMLCanvasElement? pixels);
void gl.texImage2D(target, level, internalformat, format, type, HTMLVideoElement? pixels);

     一個再簡單的紋理調用,在實際中也會有變幻無窮的方式,而這就是實現功能和產品封裝上的區別,Cesium中提供了Texture類,整體上考慮了主要的使用場景,在代碼設計上簡化了學習成本,當然在編碼上也較為優雅,我們不妨看一下Cesium中創建紋理的偽代碼:

function Texture(options) {
    // 如下三個if判斷,用來查看是否是深度紋理、深度模版紋理或浮點紋理
    //  並判斷當前瀏覽器是否支持,數據類型是否滿足要求
    if (pixelFormat === PixelFormat.DEPTH_COMPONENT) {
    }

    if (pixelFormat === PixelFormat.DEPTH_STENCIL) {
    }

    if (pixelDatatype === PixelDatatype.FLOAT) {
    }

    var preMultiplyAlpha = options.preMultiplyAlpha || pixelFormat === PixelFormat.RGB || pixelFormat === PixelFormat.LUMINANCE;
    var flipY = defaultValue(options.flipY, true);

    var gl = context._gl;
    var textureTarget = gl.TEXTURE_2D;
    var texture = gl.createTexture();

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(textureTarget, texture);

    if (defined(source)) {
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha);
        // Y軸方向是否翻轉
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);

        if (defined(source.arrayBufferView)) {
            // 紋理數據是arraybuffer的形式下,調用此方法
            gl.texImage2D(textureTarget, 0, internalFormat, width, height, 0, pixelFormat, pixelDatatype, source.arrayBufferView);
        } else if (defined(source.framebuffer)) {
            // 紋理數據是紋理緩沖區中的數據時,調用此方法
            if (source.framebuffer !== context.defaultFramebuffer) {
                source.framebuffer._bind();
            }

            gl.copyTexImage2D(textureTarget, 0, internalFormat, source.xOffset, source.yOffset, width, height, 0);

            if (source.framebuffer !== context.defaultFramebuffer) {
                source.framebuffer._unBind();
            }
        } else {
            // 紋理數據是其他類型: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement
            gl.texImage2D(textureTarget, 0, internalFormat, pixelFormat, pixelDatatype, source);
        }
    } else {
        // 紋理數據為空
        gl.texImage2D(textureTarget, 0, internalFormat, width, height, 0, pixelFormat, pixelDatatype, null);
    }
    gl.bindTexture(textureTarget, null);
}

       Cesium.Texture支持紋理貼圖,還有深度和模版,以及浮點紋理等擴展性的用法,保證了Cesium可以支持深度值,模版等操作,滿足一些復雜情況下的需求,同時,通過Texture.fromFramebuffer方式,可以支持FBO作為一張紋理,實現離屏渲染的效果。因此,在紋理數據創建上,Cesium還是比較完整的。

       同時,Cesium.Sample類提供了數據的一些顯示風格設置,比如TextureWrap,Filter的設置,在Texture類中有一個sampler的屬性,用戶在賦值時自動設置:

sampler : {
    get : function() {
        return this._sampler;
    },
    set : function(sampler) {
        // ……

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(target, this._texture);
        gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, minificationFilter);
        gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magnificationFilter);
        gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS);
        gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT);
        if (defined(this._textureFilterAnisotropic)) {
            gl.texParameteri(target, this._textureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, sampler.maximumAnisotropy);
        }
        gl.bindTexture(target, null);

        this._sampler = sampler;
    }
},

       另外,為了解決紋理閃爍的情況,Cesium中提供了MipMap的設置方式:

Texture.prototype.generateMipmap = function(hint) {
    hint = defaultValue(hint, MipmapHint.DONT_CARE);

    var gl = this._context._gl;
    var target = this._textureTarget;

    gl.hint(gl.GENERATE_MIPMAP_HINT, hint);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(target, this._texture);
    gl.generateMipmap(target);
    gl.bindTexture(target, null);
};

       當然,這種方式比較方便,瀏覽器內部自己創建MipMap,相當於一個影像金字塔的過程,如果你出於效率和效果的優化,希望自己創建MipMap也是可以的,不過目前的Cesium.Texture還不支持這種情況。

       個人認為,目前Texture實現的中規中矩,基本支持了各種紋理情況,能夠滿足后面模版緩存,深度緩存等高級用法,並對這一部分做了一個很好的封裝,能夠滿足各類應用。但如果想要用好紋理,其實里面還有很多可以擴展的地方,比如支持壓縮紋理,這對於顯存的意義,特別是Cesium這種比較消耗顯存的應用(特別是移動端),還是很有意義的。對紋理壓縮技術感興趣的,可以讀一下這篇《為什么需要紋理壓縮》,當然效率高也是有代價了,比如效果和兼容性,另外,隨着對紋理創建的增加,個人認為增加一個紋理管理器TextureManager還是很有必要的,而且並不復雜。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM