带你读《2022技术人的百宝黑皮书》——内存优化: 纹理压缩技术(2)https://developer.aliyun.com/article/1340979?groupCode=taobaotech
KTX文件格式
KTX(Khronos texture)是一种通用的纹理压缩存储格式,OpenGL(ES)、Vulkan等均支持,KTX文件中包含了纹理加载所需的所有参数及数据,比如format 、type、宽高等等,更多信息见wiki。
如下是一个ktx文件的内容:
基于这个格式,可以实现KTXLoader,用于解析KTX资源,生成纹理(通常游戏引擎会自带)。如下所示,读取KTX文件到ArrayBuffer然后解析拿到元信息:
// KhronosTextureContainer constructor(arrayBuffer, facesExpected, baseOffset = 0) { this.arrayBuffer = arrayBuffer; this.baseOffset = baseOffset; // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is: // '´', 'K', 'T', 'X', ' ', '1', '1', 'ª', '\r', '\n', '\x1A', '\n' // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A const identifier = new Uint8Array(this.arrayBuffer, this.baseOffset, 12); if (identifier[0] !== 0xAB || identifier[1] !== 0x4B || identifier[2] !== 0x54 || identifier[3] !== 0x58 || identifier[4] !== 0x20 || identifier[5] !== 0x31 || identifier[6] !== 0x31 || identifier[7] !== 0xBB || identifier[8] !== 0x0D || identifier[9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) { return; } // load the reset of the header in native 32 bit uint const dataSize = Uint32Array.BYTES_PER_ELEMENT; const headerDataView = new DataView(this.arrayBuffer, this.baseOffset + 12, 13 * dataSize); const endianness = headerDataView.getUint32(0, true); const littleEndian = endianness === 0x04030201; this.glType = headerDataView.getUint32(1 * dataSize, littleEndian); // must be 0 for compressed textures this.glTypeSize = headerDataView.getUint32(2 * dataSize, littleEndian); // must be 1 for compressed textures this.glFormat = headerDataView.getUint32(3 * dataSize, littleEndian); // must be 0 for compressed textures this.glInternalFormat = headerDataView.getUint32(4 * dataSize, littleEndian); // the value of arg passed to gl.compressedTexImage2D(,,x,,,,) this.glBaseInternalFormat = headerDataView.getUint32(5 * dataSize, littleEndian); // specify GL_RG- B,space after the header for meta-data this.pixelWidth = headerDataView.getUint32(6 * dataSize, littleEndian); // level 0 value of arg passed to gl.compressedTexImage2D(,,,x,,,) this.pixelHeight = headerDataView.getUint32(7 * dataSize, littleEndian); // level 0 value of arg passed to gl.compressedTexImage2D(,,,,x,,) this.pixelDepth = headerDataView.getUint32(8 * dataSize, littleEndian); // level 0 value of arg passed to gl.compressedTexImage3D(,,,,,x,,) this.numberOfArrayElements = headerDataView.getUint32(9 * dataSize, littleEndian); // used for texture arrays this.numberOfFaces = headerDataView.getUint32(10 * dataSize, littleEndian); // used for cubemap textures, should either be 1 or 6 this.numberOfMipmapLevels = headerDataView.getUint32(11 * dataSize, littleEndian); // number of levels; disregard possibility of 0 for compressed textures this.bytesOfKeyValueData = headerDataView.getUint32(12 * dataSize, littleEndian); // the amount of space after the header for meta-data ... }
带你读《2022技术人的百宝黑皮书》——内存优化: 纹理压缩技术(4)https://developer.aliyun.com/article/1340977?groupCode=taobaotech