cocos2dx 渲染DDS格式纹理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/iamlegendary/article/details/53884400

DDS简介:DDS文件格式要追述到S3(Silicon & Software Systems)公司提出的一种纹理压缩格式S3TC(S3 Texture Compression), 其目的是通过对纹理的压缩, 以达到节约系统带宽并提高效能的目的. S3TC就是通过压缩方式, 利用有限的纹理缓存空间来存储更多的纹理, 因为它支持6:1的压缩比例, 所以6M的纹理可以被压缩为1M存放在材质缓存中, 从而在节约了缓存的同时也提高了显示性能. 后来的DXTC和FXT1都是与S3TC类似的技术, 它们分别是微软和3dfx开发的纹理压缩标准, FXT1能提供比S3TC更高的压缩比, 达到8:1, 同时它也在3DFX新版本的Glide中得到支持. DXTC是1999年微软从S3公司取得S3TC的授权后更名而来的, 并在DirectX6中提供了支持, 即使用户的图形硬件不能支持S3TC, DirectX API会自动解码压缩后的纹理贴图. 压缩纹理贴图可以使用高品质的离线压缩器, 不会造成加载程序时有很多延时, 而DDS文件就可以使用DXTC方式压缩或是存储未压缩的像素格式.
简单的说 DDS纹理的特性是占用磁盘空间大,但其纹理压缩格式的特性,可以节约缓存使占用内存大大降低,一些3D建模游戏会运用DDS贴图,提高游戏技能,顺畅度也大幅提高。
我对于DDS纹理贴图的研究其实源于项目的需求。先来讲个故事:BOSS某天给我指派了一个任务,大概是你看别人家的游戏为什么如此顺畅,你来研究一下别人用了什么框架,什么技术然后需要用扒到的素材做出一款简化的demo。(求当是我心里的阴影面积)我当是还是蛮激动的,因为毕竟不用去做琐碎无聊的事,也可趁此机会也可以学习下其他的框架。
在chrome调试器中扒到得资源里的DDS格式文件,此类文件可以下载一个XnView软件工具直接打开查看,或者下载一个可以安装到Photoshop中的s3tc格式的插件。
cocos2dx如何加载DDS纹理:(这里我用的JS脚本)
1.Native加载
对于JSBinding来说,加载很方便。因为cocos底层有直接解压缩S3TC压缩格式的函数。可以直接采用用路径名建立精灵
如:var spr = new cc.sprite(“res/test.dds”);
与加载png的方式并无差异,将精灵加在显示的layer上,可以直接看到加载DDS纹理的图片。
2.H5加载
说来很奇怪,cocos html并没有提供可以直接加载DDS文件格式的函数。若是如Native那样加载,会出现“.dds文件类型不能识别的错误。”
网上可供参考的资料少之又少,通过对OpenGL渲染纹理的了解和对底层代码的分析,终于将DDS贴图渲染出来,显示在我的TestLayer上。
这里贴出代码:

_getSourceBuffer: function(){
        var self = this;
        var xhr = new XMLHttpRequest();
        xhr.addEventListener('load', function (ev) {
            console.log("xhr resp");
            if (xhr.status == 200) {
                self.parseDDS(xhr.response, self._uploadCallback, errorCallback, "");
            } else {
                errorCallback(xhr.statusText);
            }
        }, false);
        xhr.open('GET', "res/test.dds", true);
        xhr.responseType = 'arraybuffer';
        xhr.send(null);
    }

getSourceBuffer函数是采用XMLHttpRequest的方式读取出test.dds的ArrayBuffer,即二进制的原始缓存数据。

parseDDS: function(arrayBuffer, callback, errorCallback, desc) {
        console.log("parseDDS");
        if (!callback || !errorCallback) {
            return
        }
        var header = new Int32Array(arrayBuffer, 0, DDS_HEADER_LENGTH);
        if (header[DDS_HEADER_MAGIC] != DDS_MAGIC) {
            errorCallback("Invalid magic number in DDS header");
            return 0
        }
        if (!header[DDS_HEADER_PF_FLAGS] & DDPF_FOURCC) {
            errorCallback("Unsupported format, must contain a FourCC code");
            return 0
        }
        var fourCC = header[DDS_HEADER_PF_FOURCC];
        var internalFormat;
        switch (fourCC) {
            case FOURCC_DXT1:
                internalFormat = COMPRESSED_RGB_S3TC_DXT1_EXT;
                break;
            case FOURCC_DXT3:
                internalFormat = COMPRESSED_RGBA_S3TC_DXT3_EXT;
                break;
            case FOURCC_DXT5:
                internalFormat = COMPRESSED_RGBA_S3TC_DXT5_EXT;
                break;
            case FOURCC_ATC:
                internalFormat = COMPRESSED_RGB_ATC_WEBGL;
                break;
            case FOURCC_ATCA:
                internalFormat = COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL;
                break;
            case FOURCC_ATCI:
                internalFormat = COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL;
                break;
            default:
                errorCallback("Unsupported FourCC code: " + int32ToFourCC(fourCC));
                return
        }
        var levels = 1;
        if (header[DDS_HEADER_FLAGS] & DDSD_MIPMAPCOUNT) {
            levels = Math.max(1, header[DDS_HEADER_MIPMAPCOUNT])
        }
        var width = header[DDS_HEADER_WIDTH];
        var height = header[DDS_HEADER_HEIGHT];
        var dataOffset = header[DDS_HEADER_SIZE] + 4;
        var dxtData = new Uint8Array(arrayBuffer, dataOffset);

        var B0 = this.textureLevelSize(internalFormat, width, height)
        var A0 = new Uint8Array(dxtData.buffer,dxtData.byteOffset,B0);

        this.initWithData(A0,internalFormat,width,height,cc.size(width,height));
        if(callback) callback();
    },

parseDDS函数的功能是将上述得到的二进制缓存数组进行解析,获得人力的width,height,size和纹理数据块压缩格式等信息,再通过initWithData进行Texture2D的初始化:

initWithData: function (data, pixelFormat, pixelsWide, pixelsHigh, contentSize) {
        var self = this, tex2d = cc.Texture2D;
        var gl = cc._renderContext;
        var format = gl.RGBA, type = gl.UNSIGNED_BYTE;

        var bitsPerPixel = cc.Texture2D._B[pixelFormat];

        var bytesPerRow = pixelsWide * bitsPerPixel / 8;
        if (bytesPerRow % 8 === 0) {
            gl.pixelStorei(gl.UNPACK_ALIGNMENT, 8);
        } else if (bytesPerRow % 4 === 0) {
            gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
        } else if (bytesPerRow % 2 === 0) {
            gl.pixelStorei(gl.UNPACK_ALIGNMENT, 2);
        } else {
            gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
        }

        self._webTextureObj = gl.createTexture();
        cc.glBindTexture2D(self);

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

        var ext = (
        gl.getExtension("WEBGL_compressed_texture_s3tc") ||
        gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") ||
        gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc")
        );

        // Specify OpenGL texture image
        switch (pixelFormat) {
            case tex2d.PIXEL_FORMAT_RGBA8888:
                format = gl.RGBA;
                break;
            case tex2d.PIXEL_FORMAT_RGB888:
                format = gl.RGB;
                break;
            case tex2d.PIXEL_FORMAT_RGBA4444:
                type = gl.UNSIGNED_SHORT_4_4_4_4;
                break;
            case tex2d.PIXEL_FORMAT_RGB5A1:
                type = gl.UNSIGNED_SHORT_5_5_5_1;
                break;
            case tex2d.PIXEL_FORMAT_RGB565:
                type = gl.UNSIGNED_SHORT_5_6_5;
                break;
            case tex2d.PIXEL_FORMAT_AI88:
                format = gl.LUMINANCE_ALPHA;
                break;
            case tex2d.PIXEL_FORMAT_A8:
                format = gl.ALPHA;
                break;
            case tex2d.PIXEL_FORMAT_I8:
                format = gl.LUMINANCE;
                break;
            case ext.COMPRESSED_RGBA_S3TC_DXT5_EXT:
                format = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
                break;
            default:
                cc.assert(0, cc._LogInfos.Texture2D_initWithData);
        }
        if(format == ext.COMPRESSED_RGBA_S3TC_DXT5_EXT){
            //gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, pixelsWide, pixelsHigh, 0, format, type, data);
            gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, pixelsWide, pixelsHigh, 0, data);
        }else{
            gl.texImage2D(gl.TEXTURE_2D, 0, format, pixelsWide, pixelsHigh, 0, format, type, data);
        }


        self._contentSize.width = contentSize.width;
        self._contentSize.height = contentSize.height;
        self._pixelsWide = pixelsWide;
        self._pixelsHigh = pixelsHigh;
        self._pixelFormat = pixelFormat;
        self.maxS = contentSize.width / pixelsWide;
        self.maxT = contentSize.height / pixelsHigh;

        self._hasPremultipliedAlpha = false;
        self._hasMipmaps = false;
        self.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE);

        self._textureLoaded = true;

        return true;
    }

至此一个已将DDS格式纹理解析好的数据初始化了一个Texture2D的对象,初始化流程参考OPENGL基本渲染流程和cocos引擎底层对cc.Texture2D的渲染流程。

再用解析渲染后的Texture建立精灵,addChild在layer上即可显示与场景之中。

附:解析所需其他宏的定义

function fourCCToInt32(value) {
    return value.charCodeAt(0) +
        (value.charCodeAt(1) << 8) +
        (value.charCodeAt(2) << 16) +
        (value.charCodeAt(3) << 24);
}

var FOURCC_DXT1 = fourCCToInt32("DXT1");
var FOURCC_DXT3 = fourCCToInt32("DXT3");
var FOURCC_DXT5 = fourCCToInt32("DXT5");

var FOURCC_ATC = fourCCToInt32("ATC ");
var FOURCC_ATCA = fourCCToInt32("ATCA");
var FOURCC_ATCI = fourCCToInt32("ATCI");

// DXT formats, from:
// http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
var COMPRESSED_RGB_S3TC_DXT1_EXT  = 0x83F0;
var COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
var COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
var COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;

// ATC formats, from:
// http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/
var COMPRESSED_RGB_ATC_WEBGL                     = 0x8C92;
var COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL     = 0x8C93;
var COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE;

var DDS_MAGIC = 0x20534444;
var DDSD_MIPMAPCOUNT = 0x20000;
var DDPF_FOURCC = 0x4;

var DDS_HEADER_LENGTH = 31; // The header length in 32 bit ints.

// Offsets into the header array.
var DDS_HEADER_MAGIC = 0;

var DDS_HEADER_SIZE = 1;
var DDS_HEADER_FLAGS = 2;
var DDS_HEADER_HEIGHT = 3;
var DDS_HEADER_WIDTH = 4;

var DDS_HEADER_MIPMAPCOUNT = 7;

var DDS_HEADER_PF_FLAGS = 20;
var DDS_HEADER_PF_FOURCC = 21;

猜你喜欢

转载自blog.csdn.net/iamlegendary/article/details/53884400
今日推荐