ddf8196 / MaterialBinTool

MCBE RenderDragon着色器解包/打包/编译工具
MIT License
108 stars 12 forks source link

关于material.bin文件格式的一些猜测 #1

Closed cuixiang0130 closed 2 years ago

cuixiang0130 commented 2 years ago

Shader Code部分

public void readFrom(ByteBuf buf) {
            type = ByteBufUtil.readString(buf);
            platform = ByteBufUtil.readString(buf);
            //与enum中的序号对应
            typeId = buf.readByte();
            platformId = buf.readByte();

            short shaderInputCount = buf.readShortLE();
            shaderInputList = new ArrayList<>(shaderInputCount);
            for (int i = 0; i < shaderInputCount; ++i) {
                ShaderInput shaderInput = new ShaderInput();
                shaderInput.readFrom(buf);
                shaderInputList.add(shaderInput);
            }

            unknownBytes0 = ByteBufUtil.readBytes(buf, 8);
            //这里提取出来的是经过bgfx的shaderc编译后的着色器
            code = ByteBufUtil.readByteArray(buf);
   }

对应的枚举类

enum ShaderCodeType {
    Vertex,
    Fragment,
    Compute,
    Unknown
}
enum ShaderCodePlatform {
    Direct3D_SM40,
    Direct3D_SM50,
    Direct3D_SM60,
    Direct3D_SM65,
    Direct3D_XB1,
    Direct3D_XBX,
    GLSL_120,
    GLSL_430,
    ESSL_100,
    ESSL_300,
    ESSL_310,
    Metal,
    Vulkan,
    Nvn,
    Pssl,
}
cuixiang0130 commented 2 years ago

从Minecraft中提取出来的编译后着色器版本号是5,是2018年的时候,现在bgfx的shaderc代码已经改动了不少,大概可以参考一下这个https://github.com/bkaradzic/bgfx/commit/70c06f46c35587f8e32ffeab4954f0c9284acd23

cuixiang0130 commented 2 years ago

根据bgfx的源码,反编译bgfx的着色器过程大概是这样

            // 前3位是VSH/FSH/CSH表示着色器代码类型,对应Vertex、Fragment、Compute三种着色器,最后一位是版本号,Minecraft提取出来为5
            byte[] magicNumber = ByteBufUtil.readBytes(buf, 4);
            //根据着色器的输入输出的变量计算出hash值,不过运行时只校验Vertex与Fragment两个着色器hash值相同,确保Vertex的输出与Fragment输入相同,只要两个相同,随便填一个应该都可以
            int hashIn = buf.readIntLE();
            short uniformCount = buf.readShortLE();
            materialUniformList = new ArrayList<>(uniformCount);
            for (int i = 0; i < uniformCount; ++i) {
                MaterialUniform materialUniform = new MaterialUniform();
                materialUniform.readFrom(buf);
                materialUniformList.add(materialUniform);
            }

            code = ByteBufUtil.readByteArray(buf);
           // 永远为0
            buf.readByte();

MaterialUniform

public void readFrom(ByteBuf buf) {
        byte nameLength = buf.readByte();
        name = new String(ByteBufUtil.readBytes(buf, nameLength), StandardCharsets.UTF_8);
        type = buf.readByte();
        count = buf.readByte();
        regIndex = buf.readShortLE();
        regCount = buf.readShortLE();
}
cuixiang0130 commented 2 years ago

如果是DirectX的话,后面还存了attribute的信息,可以翻bgfx的shaderc_hlsl.cpp

cuixiang0130 commented 2 years ago

然后SamplerDefinition这里,1.18跟在1.17的格式上加了个int

public void readFrom(ByteBuf buf) {
        name = ByteBufUtil.readString(buf);
        //猜测应该是索引值?
        index = buf.readByte();
        unknownByte1 = buf.readByte();
        unknownByte2 = buf.readByte();
        unknownBool0 = buf.readBoolean();
        unknownByte3 = buf.readByte();
        unknownStr0 = ByteBufUtil.readString(buf);
        //1.18的文件这里多了个int,似乎一直是1
        unknownInt = buf.readIntLE();
        unknownBool1 = buf.readBoolean();
        if (unknownBool1) {
            unknownStr1 = ByteBufUtil.readString(buf);
        }

        unknownBool2 = buf.readBoolean();
        if (unknownBool2) {
            unknownStr2 = ByteBufUtil.readString(buf);
            unknownInt0 = buf.readIntLE();
        }
    }
cuixiang0130 commented 2 years ago

Pass类中这段代码也不太适合现在的情况,现在1.18 Windows版中的文件也是长度15的字符串bitset

            if (version >= 0x16) {
                v1_17_40_20_Android = buf.readIntLE() == 15;
                buf.readerIndex(buf.readerIndex() - 4);
            } else {
                v1_17_40_20_Android = false;
            }

            if (v1_17_40_20_Android) {
                bitSet = ByteBufUtil.readString(buf);
            } else {
                unknownByte0 = buf.readByte();
            }
cuixiang0130 commented 2 years ago

虽然Android提取出的代码没有编译成什么字节码,但估计可能还是得用bgfx的shaderc编译一下,之前尝试着修改的时候,调用一些内置函数Minecraft直接崩溃,我应该没写错语法(大概)。而且用bgfx的话,着色器可以write once,compile everywhere了

ddf8196 commented 2 years ago

感谢反馈

ddf8196 commented 2 years ago

Pass类中这段代码也不太适合现在的情况,现在1.18 Windows版中的文件也是长度15的字符串bitset

            if (version >= 0x16) {
                v1_17_40_20_Android = buf.readIntLE() == 15;
                buf.readerIndex(buf.readerIndex() - 4);
            } else {
                v1_17_40_20_Android = false;
            }

            if (v1_17_40_20_Android) {
                bitSet = ByteBufUtil.readString(buf);
            } else {
                unknownByte0 = buf.readByte();
            }

这个地方这么写是因为我写这个工具的那段时间win10版和安卓版的version同为0x16,同名的.material.bin文件这个位置之前的其他地方也相同,但win10版的这个位置只有一个字节,安卓版则是现在这样的字符串bitset,没法区分,为了兼容旧版本还是得留着(

cuixiang0130 commented 2 years ago

今天又试了一下,发现之前调用一些内置函数导致Minecraft崩溃好像真是我的问题(没切换输入法输了个中文符号();什么的,😅笑嘻了),所以修改着色器应该没什么这些限制