白鹭引擎(Egret)扩展KTX处理器以支持ASTC纹理格式解析

新增 ASTC 扩展获取、写入 Capabilities.supportedCompressedTexture.astc,并把 ASTC 扩展加入 _supportedCompressedTextureInfo,这样引擎的压缩纹理上传链路就能识别 ASTC internalFormat。

javascript 复制代码
egret.web.js
WebGLRenderContext.prototype.getSupportedCompressedTexture = function () {
	var gl = this.context ? this.context : egret.sys.getContextWebGL(this.surface);
	this.pvrtc = gl.getExtension('WEBGL_compressed_texture_pvrtc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
	if (this.pvrtc) {
		this.pvrtc.name = 'WEBGL_compressed_texture_pvrtc';
	}
	//
	this.etc1 = gl.getExtension('WEBGL_compressed_texture_etc1') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc1');
	if (this.etc1) {
		this.etc1.name = 'WEBGL_compressed_texture_etc1';
	}
	//
	// ASTC (if supported by the current hardware/browser)
	this.astc = gl.getExtension('WEBGL_compressed_texture_astc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_astc');
	if (this.astc) {
		this.astc.name = 'WEBGL_compressed_texture_astc';
	}
	//
	if (egret.Capabilities._supportedCompressedTexture) {
		egret.Capabilities._supportedCompressedTexture = egret.Capabilities._supportedCompressedTexture || {};
		egret.Capabilities._supportedCompressedTexture.pvrtc = !!this.pvrtc;
		egret.Capabilities._supportedCompressedTexture.etc1 = !!this.etc1;
		egret.Capabilities._supportedCompressedTexture.astc = !!this.astc;
	}
	else {
		egret.Capabilities['supportedCompressedTexture'] = egret.Capabilities._supportedCompressedTexture || {};
		egret.Capabilities['supportedCompressedTexture'].pvrtc = !!this.pvrtc;
		egret.Capabilities['supportedCompressedTexture'].etc1 = !!this.etc1;
		egret.Capabilities['supportedCompressedTexture'].astc = !!this.astc;
	}
	//
	this._supportedCompressedTextureInfo = this._buildSupportedCompressedTextureInfo(/*this.context, compressedTextureExNames,*/ [this.etc1, this.pvrtc, this.astc]);
};
  1. 工具函数( xxx.png ↔ xxx@1.ktx 约定一致)
    sheetAtlasPathToKtxAt1Path(url):仅当 r.url 以 .png 结尾时,得到同路径 xxx@1.ktx。
    isAstcSupportedForSheet():先调用 WebGLRenderContext.getInstance().getSupportedCompressedTexture()(保证已探测扩展),再读 Capabilities 里的 astc。
  2. SheetProcessor.onLoadStart
    仍先加载图集 JSON,解析出大图资源 r(与原来一致,data.file 仍可指向 xxx.png)。
    若推得出 ktxUrl 且 ASTC 可用:用 host.load(rKtx, 'ktx') 加载 rKtx(url 为 xxx@1.ktx,type: 'ktx',name 为临时名避免和配置冲突)。
    成功后 host.remove(rKtx)(去掉 KTX 专用缓存项,避免双份),再 buildSheet(tex),内部仍 host.save(r, baseTexture),$resourceInfo 仍是原来的 r,unload 行为与以前一致。
    KTX 失败或不可用:退回原来的 host.load®(按 image 加载 PNG)。

处理非图集:增强 sheetAtlasPathToKtxAt1Path(支持 URL 带 ?/#),并在 ImageProcessor 中加入与 SheetProcessor 相同的 KTX 优先加载逻辑。

javascript 复制代码
assetsmanager.js

processor_1.getRelativePath = getRelativePath;
/**
 * 命名约定:xxx.png → xxx@1.ktx(图集大图 SheetProcessor、散图 ImageProcessor 共用)
 * 支持 url 带 ?query / #hash(只替换路径段的 .png)
 */
processor_1.sheetAtlasPathToKtxAt1Path = function (url) {
	if (!url || url.length < 5) {
		return null;
	}
	var qi = url.indexOf('?');
	var hi = url.indexOf('#');
	var end = url.length;
	if (qi >= 0) {
		end = Math.min(end, qi);
	}
	if (hi >= 0) {
		end = Math.min(end, hi);
	}
	var pathOnly = url.substring(0, end);
	var suffix = url.substring(end);
	if (pathOnly.length < 5) {
		return null;
	}
	var lower = pathOnly.toLowerCase();
	if (lower.slice(-4) !== '.png') {
		return null;
	}
	return pathOnly.slice(0, -4) + '@1.ktx' + suffix;
};
/**
 * 是否可用 ASTC(需先触发 WebGL 扩展探测)
 */
processor_1.isAstcSupportedForSheet = function () {
	try {
		if (egret['web'] && egret['web'].WebGLRenderContext && egret['web'].WebGLRenderContext.getInstance) {
			egret['web'].WebGLRenderContext.getInstance().getSupportedCompressedTexture();
		}
		var caps = egret.Capabilities;
		var s = caps && (caps._supportedCompressedTexture || caps['supportedCompressedTexture']);
		return !!(s && s.astc);
	}
	catch (e) {
		return false;
	}
};
processor_1.ImageProcessor = {
	onLoadStart: function (host, resource) {
		function loadImageViaImageLoader() {
			var loader = new egret.ImageLoader();
			loader.load(RES.getVirtualUrl(resource.root + resource.url));
			return promisify(loader, resource)
				.then(function (bitmapData) {
				var texture = new egret.Texture();
				texture._setBitmapData(bitmapData);
				var r = host.resourceConfig.getResource(resource.name);
				if (r && r.scale9grid) {
					var list = r.scale9grid.split(",");
					texture["scale9Grid"] = new egret.Rectangle(parseInt(list[0]), parseInt(list[1]), parseInt(list[2]), parseInt(list[3]));
				}
				return texture;
			});
		}
		var ktxUrl = processor_1.sheetAtlasPathToKtxAt1Path(resource.url);
		if (ktxUrl && processor_1.isAstcSupportedForSheet()) {
			var rKtx = { name: resource.name + "__egret_image_ktx", url: ktxUrl, type: 'ktx', root: resource.root };
			return host.load(rKtx, 'ktx').then(function (tex) {
				host.remove(rKtx);
				var r = host.resourceConfig.getResource(resource.name);
				if (r && r.scale9grid && tex) {
					var list = r.scale9grid.split(",");
					tex["scale9Grid"] = new egret.Rectangle(parseInt(list[0]), parseInt(list[1]), parseInt(list[2]), parseInt(list[3]));
				}
				return tex;
			}).catch(function () {
				return loadImageViaImageLoader();
			});
		}
		return loadImageViaImageLoader();
	},
	onRemoveStart: function (host, resource) {
		var texture = host.get(resource);
		texture.dispose();
	}
};

processor_1.SheetProcessor = {
	onLoadStart: function (host, resource) {
		return host.load(resource, "json").then(function (data) {
			var r = host.resourceConfig.getResource(RES.nameSelector(data.file));
			if (!r) {
				var imageName = getRelativePath(resource.url, data.file);
				r = { name: imageName, url: imageName, type: 'image', root: resource.root };
			}
			function buildSheet(baseTexture) {
				if (!baseTexture) {
					return null;
				}
				var frames = data.frames;
				var spriteSheet = new egret.SpriteSheet(baseTexture);
				spriteSheet["$resourceInfo"] = r;
				for (var subkey in frames) {
					var config = frames[subkey];
					var texture = spriteSheet.createTexture(subkey, config.x, config.y, config.w, config.h, config.offX, config.offY, config.sourceW, config.sourceH);
					if (config["scale9grid"]) {
						var str = config["scale9grid"];
						var list = str.split(",");
						texture["scale9Grid"] = new egret.Rectangle(parseInt(list[0]), parseInt(list[1]), parseInt(list[2]), parseInt(list[3]));
					}
				}
				host.save(r, baseTexture);
				return spriteSheet;
			}
			var ktxUrl = processor_1.sheetAtlasPathToKtxAt1Path(r.url);
			if (ktxUrl && processor_1.isAstcSupportedForSheet()) {
				var rKtx = { name: r.name + "__egret_sheet_ktx", url: ktxUrl, type: 'ktx', root: r.root };
				return host.load(rKtx, 'ktx').then(function (tex) {
					host.remove(rKtx);
					return buildSheet(tex);
				}).catch(function () {
					return host.load(r).then(function (bitmapData) {
						return buildSheet(bitmapData);
					}, function (e) {
						host.remove(r);
						throw e;
					});
				});
			}
			return host.load(r).then(function (bitmapData) {
				return buildSheet(bitmapData);
			}, function (e) {
				host.remove(r);
				throw e;
			});
		});
	},
	getData: function (host, resource, key, subkey) {
		var data = host.get(resource);
		if (data) {
			return data.getTexture(subkey);
		}
		else {
			return null;
		}
	},
	onRemoveStart: function (host, resource) {
		var sheet = host.get(resource);
		var r = sheet["$resourceInfo"];
		sheet.dispose();
		host.unload(r);
	}
};

解决Laya引擎导出的纹理压缩文件是 sRGB 的 ASTC KTX 格式颜色显示不对

Egret渲染管线仍是传统2D(非线性/非 Gamma-correct):屏幕期望直接显示 sRGB 值;一旦把 ASTC/KTX 按 sRGB 内部格式上传,GPU 会先做 sRGB→linear 解码,但最后不会再 linear→sRGB 编码输出,结果就会出现整体发暗/颜色怪异/对比不对。(因为使用的是Laya引擎打包出压缩纹理,使用的是sRGB 的 KTX,需要加载时把 sRGB internalFormat 映射成 linear)

javascript 复制代码
libs\modules\egret\egret.js

compressedData.glInternalFormat = this.glInternalFormat;
替换成
var internalFormat = this.glInternalFormat;
// Map ASTC sRGB internalFormat to linear to avoid color shift
// in engines/environments that don't use a full linear+sRGB output pipeline (e.g. mini-games).
// ASTC LDR linear: 0x93B0..0x93BD, ASTC LDR sRGB: 0x93D0..0x93DD
if (internalFormat >= 0x93D0 && internalFormat <= 0x93DD) {
   	internalFormat = internalFormat - 0x20;
}
compressedData.glInternalFormat = internalFormat;
相关推荐
老豆82 年前
【base64加密】js/ts的基础加密
javascript·html·base64·加密·cocos·laya·egret