新增 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]);
};
- 工具函数( xxx.png ↔ xxx@1.ktx 约定一致)
sheetAtlasPathToKtxAt1Path(url):仅当 r.url 以 .png 结尾时,得到同路径 xxx@1.ktx。
isAstcSupportedForSheet():先调用 WebGLRenderContext.getInstance().getSupportedCompressedTexture()(保证已探测扩展),再读 Capabilities 里的 astc。 - 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;