拆包一游戏的时候发现大部分贴图都是灰度+1~2 张 256*1 像素的颜色查找表,用了名为 ZanLib/ClutBiliniar 的着色器,一点资料都没找到,想写一个 Python 脚本把它转换成彩色, ChatGPT 教了我一晚上没成功 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Game Engines
Unreal Engine
MyCryENGINE
edis0n0
V2EX    游戏开发

拆包一游戏的时候发现大部分贴图都是灰度+1~2 张 256*1 像素的颜色查找表,用了名为 ZanLib/ClutBiliniar 的着色器,一点资料没找到,想写一个 Python 脚本把它转换成彩色, ChatGPT 教了我一晚上没成功

  •  
  •   edis0n0 2022-12-24 02:45:21 +08:00 3140 次点击
    这是一个创建于 1021 天前的主题,其中的信息可能已经有所发展或是发生改变。

    着色器 ZanLib/ClutBiliniar 的其中一个 SubProgram:

    SubProgram "gles hw_tier00 " { Keywords { "_TRANSPARENT_OFF" "_DITHERALPHA_OFF" "_VERTEXCOLOR_OFF" "_BLENDFACTOR_OFF" } "#ifdef VERTEX #version 100 uniform vec4 hlslcc_mtx4x4unity_ObjectToWorld[4]; uniform vec4 hlslcc_mtx4x4unity_MatrixVP[4]; uniform vec4 _MainTex_ST; uniform vec4 _MainTex_TexelSize; attribute highp vec4 in_POSITION0; attribute highp vec4 in_TEXCOORD0; varying mediump vec2 vs_TEXCOORD0; varying mediump vec2 vs_TEXCOORD1; varying mediump vec2 vs_TEXCOORD2; varying mediump vec2 vs_TEXCOORD3; varying highp vec2 vs_TEXCOORD4; vec4 u_xlat0; vec4 u_xlat1; vec2 u_xlat4; void main() { u_xlat0 = in_POSITION0.yyyy * hlslcc_mtx4x4unity_ObjectToWorld[1]; u_xlat0 = hlslcc_mtx4x4unity_ObjectToWorld[0] * in_POSITION0.xxxx + u_xlat0; u_xlat0 = hlslcc_mtx4x4unity_ObjectToWorld[2] * in_POSITION0.zzzz + u_xlat0; u_xlat0 = u_xlat0 + hlslcc_mtx4x4unity_ObjectToWorld[3]; u_xlat1 = u_xlat0.yyyy * hlslcc_mtx4x4unity_MatrixVP[1]; u_xlat1 = hlslcc_mtx4x4unity_MatrixVP[0] * u_xlat0.xxxx + u_xlat1; u_xlat1 = hlslcc_mtx4x4unity_MatrixVP[2] * u_xlat0.zzzz + u_xlat1; gl_Position = hlslcc_mtx4x4unity_MatrixVP[3] * u_xlat0.wwww + u_xlat1; u_xlat4.x = 0.0; u_xlat4.y = _MainTex_TexelSize.y; u_xlat0.xy = in_TEXCOORD0.xy * _MainTex_ST.xy + _MainTex_ST.zw; u_xlat0.xy = (-_MainTex_TexelSize.xy) * vec2(0.5, 0.5) + u_xlat0.xy; vs_TEXCOORD1.xy = u_xlat4.xy + u_xlat0.xy; vs_TEXCOORD0.xy = u_xlat0.xy; u_xlat1.x = _MainTex_TexelSize.x; u_xlat1.y = 0.0; vs_TEXCOORD2.xy = u_xlat0.xy + u_xlat1.xy; vs_TEXCOORD3.xy = u_xlat0.xy + _MainTex_TexelSize.xy; vs_TEXCOORD4.xy = u_xlat0.xy * _MainTex_TexelSize.zw; return; } #endif #ifdef FRAGMENT #version 100 #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif precision highp int; uniform vec4 _CLUT_TexelSize; uniform lowp sampler2D _MainTex; uniform lowp sampler2D _CLUT; varying mediump vec2 vs_TEXCOORD0; varying mediump vec2 vs_TEXCOORD1; varying mediump vec2 vs_TEXCOORD2; varying mediump vec2 vs_TEXCOORD3; varying highp vec2 vs_TEXCOORD4; #define SV_Target0 gl_FragData[0] vec4 u_xlat0; lowp float u_xlat10_0; vec4 u_xlat1; mediump vec4 u_xlat16_1; lowp vec4 u_xlat10_1; vec4 u_xlat2; mediump vec4 u_xlat16_2; lowp vec4 u_xlat10_2; float u_xlat3; lowp vec4 u_xlat10_3; vec2 u_xlat8; vec2 u_xlat10; void main() { u_xlat10_0 = texture2D(_MainTex, vs_TEXCOORD3.xy).w; u_xlat0.x = u_xlat10_0 * _CLUT_TexelSize.z + (-_CLUT_TexelSize.x); u_xlat0.x = u_xlat0.x / _CLUT_TexelSize.z; u_xlat0.y = float(0.0); u_xlat8.y = float(0.0); u_xlat10_1 = texture2D(_CLUT, u_xlat0.xy); u_xlat10_0 = texture2D(_MainTex, vs_TEXCOORD1.xy).w; u_xlat0.x = u_xlat10_0 * _CLUT_TexelSize.z + (-_CLUT_TexelSize.x); u_xlat2.x = u_xlat0.x / _CLUT_TexelSize.z; u_xlat2.y = float(0.0); u_xlat10.y = float(0.0); u_xlat10_3 = texture2D(_CLUT, u_xlat2.xy); u_xlat16_1 = u_xlat10_1 + (-u_xlat10_3); u_xlat0.xy = fract(vs_TEXCOORD4.xy); u_xlat1 = u_xlat0.xxxx * u_xlat16_1 + u_xlat10_3; u_xlat10_2.x = texture2D(_MainTex, vs_TEXCOORD2.xy).w; u_xlat2.x = u_xlat10_2.x * _CLUT_TexelSize.z + (-_CLUT_TexelSize.x); u_xlat10.x = u_xlat2.x / _CLUT_TexelSize.z; u_xlat10_2 = texture2D(_CLUT, u_xlat10.xy); u_xlat10_3.x = texture2D(_MainTex, vs_TEXCOORD0.xy).w; u_xlat3 = u_xlat10_3.x * _CLUT_TexelSize.z + (-_CLUT_TexelSize.x); u_xlat8.x = u_xlat3 / _CLUT_TexelSize.z; u_xlat10_3 = texture2D(_CLUT, u_xlat8.xy); u_xlat16_2 = u_xlat10_2 + (-u_xlat10_3); u_xlat2 = u_xlat0.xxxx * u_xlat16_2 + u_xlat10_3; u_xlat1 = u_xlat1 + (-u_xlat2); u_xlat0 = u_xlat0.yyyy * u_xlat1 + u_xlat2; SV_Target0.w = ceil(u_xlat0.w); SV_Target0.xyz = u_xlat0.xyz; return; } #endif " } 

    图形学零基础,会写一点 Python 但完全没用过 NumPy ,有 V 友能帮帮忙么?

    附上 ChatGPT 的聊天记录:

    不知道正确率有多少,但能胡编出这么多看起来相关的内容也挺厉害了

    19 条回复    2022-12-26 13:37:46 +08:00
    edis0n0
        1
    edis0n0  
    OP
       2022-12-24 02:47:49 +08:00
    这是我照着 ChatGPT 的指点瞎写的: https://pastebin.com/QEshP61s
    HuPu
        2
    HuPu  
       2022-12-24 08:14:10 +08:00
    挺有意思 如何跟 chatgpt 说话是门学问啊 我感觉我就不太会
    beijiaoff
        3
    beijiaoff  
       2022-12-24 09:27:11 +08:00
    观望一下,感觉目前做不到吧
    musi
        4
    musi  
       2022-12-24 11:03:33 +08:00
    我觉得这你要是 google 你已经写完了
    edis0n0
        5
    edis0n0  
    OP
       2022-12-24 11:17:51 +08:00
    @musi #4 google 了两天,没有找到一点点资料,全是这个公司自己搞的
    ruanimal
        6
    ruanimal  
       2022-12-24 18:15:05 +08:00
    真的有人信 chatGPT 能教人写代码?
    edis0n0
        7
    edis0n0  
    OP
       2022-12-25 11:33:57 +08:00
    @ruanimal #6 还真别说,刚才换了个问法它真写出来能用的了
    edis0n0
        8
    edis0n0  
    OP
       2022-12-25 11:35:59 +08:00
    @HuPu
    @beijiaoff 主要的问题是 chatgpt 输入长度有限,给它太多信息的时候会有一大部分被它忘掉
    c0t
        9
    c0t  
       2022-12-25 13:06:40 +08:00 via Android   1
    贴出来的代码没什么难度啊...很字面意思,voxel 类的游戏全是这样的贴图
    c0t
        10
    c0t  
       2022-12-25 13:10:44 +08:00 via Android   1
    @c0t 不过你可能首先得知道比如 unity 里的 texelsize 这种语义里惯例存的是啥,不知道 chatgpt 有这种知识没有
    edis0n0
        11
    edis0n0  
    OP
       2022-12-25 13:26:17 +08:00
    @c0t #10 我 google 了一下 texelsize 存的应该是 (1/width, 1/height, width, height),所以 chatgpt 输出的有点错了

    它说的逻辑我简化了下貌似就是根据 index 图的 alpha 值去 clut 取对应点颜色,自己写了一遍:

    import numpy as np
    from skimage import io

    index_image = io.imread('index.png')
    clut_image = io.imread('clut.png')
    x_coord = index_image[:, :, 3]
    y_coord = np.zeros_like(x_coord)
    clut_colors = clut_image[y_coord.astype(int), x_coord.astype(int), :]
    io.imsave('output.png', clut_colors)

    貌似输出结果还很正常,不知道有没有漏了什么东西
    edis0n0
        12
    edis0n0  
    OP
       2022-12-25 13:32:48 +08:00
    @c0t #10 google 到的那贴子说的是 1/width 是从 unity 源码看到的,那-_CLUT_TexelSize.x 应该是一个接近 0 的值,因为我看后面是根据它的值去对应位置取数据,那应该是按 int 处理,小数部分没有意义,那我就直接按 0 处理了。不知道为什么原始的 GL 代码会这么长,总觉得我简化逻辑的时候搞错了什么,一直没看出来,请大佬再帮忙分析下。
    c0t
        13
    c0t  
       2022-12-25 18:15:32 +08:00   1
    @edis0n0
    因为采样了四个像素(大概这么理解就好)吧?不过不用模型 uv 的话,loop main texture 解码出来的结果可能没啥意义?可能是很多个模型公用了一张 main texture 。

    上一半 vertex shader 里输出到 frag shader 里的东西:

    // four pixel
    // 0 2
    // 1 3
    varying mediump vec2 vs_TEXCOORD0; // texture sample position
    varying mediump vec2 vs_TEXCOORD1; // texture sample position + (0,1/height), pixel below
    varying mediump vec2 vs_TEXCOORD2; // texture sample positin + (1/width,0), next pixel
    varying mediump vec2 vs_TEXCOORD3; // texture sample position + (1/width,1/height),
    varying highp vec2 vs_TEXCOORD4; // texture sample position in pixel
    c0t
        14
    c0t  
       2022-12-25 18:24:15 +08:00   1
    @edis0n0
    然后 frag shader 里,从四个 alpha 值得到四个像素颜色,然后做一次 bilinear interpolation ,大概就是这样
    c0t
        15
    c0t  
       2022-12-25 18:26:21 +08:00   1
    @c0t ClutBiliniar 这个名字就是 color lookup bilinear 的意思吧
    c0t
        16
    c0t  
       2022-12-25 18:56:33 +08:00   span class="small fade"> 1
    @edis0n0
    texture2D 是用 uv 坐标采样的,所以不是整数

    (-_CLUT_TexelSize.x); 这个不太好说,主要是看 main texture 里存的到底是什么?可能和生成图片方式有关系吧?不过好像确实有点奇怪:

    u_xlat10_0 = texture2D(_MainTex, vs_TEXCOORD1.xy).w;
    u_xlat0.x = u_xlat10_0 * _CLUT_TexelSize.z + (-_CLUT_TexelSize.x);
    u_xlat2.x = u_xlat0.x / _CLUT_TexelSize.z;
    u_xlat2.y = float(0.0);
    u_xlat10_3 = texture2D(_CLUT, u_xlat2.xy);

    没搞懂意义,u_xlat10_0 * _CLUT_TexelSize.z + (-_CLUT_TexelSize.x); 照理来说,这个式子两边都不是一个单位的啊,u_xlat10_0 * _CLUT_TexelSize.z 单位是 pixel ,(-_CLUT_TexelSize.x); 这个是 1/pixel ,最终除一个 _CLUT_TexelSize.z 之后,这个部分也几乎没作用了
    edis0n0
        17
    edis0n0  
    OP
       2022-12-25 19:53:38 +08:00
    @c0t 谢谢大佬指点。刚 google 了下插值算法一般用于放大图片,请问这个 bilinear interpolation 是还有放大图片的作用么? main texture 存的是一张只有 alpha 通道有数据的游戏背景图,vertex shader 中没看出还有放大的代码以及倍数一类的信息。如果倍数是 1.0 的话是不是相当于可以不用考虑?它和 opencv 的 cv2.resize()插值效果有差异么?
    c0t
        18
    c0t  
       2022-12-25 20:13:15 +08:00
    @edis0n0
    嗯嗯,背景图的话就是单纯的放大的时候,平滑像素用的了,不会有一块一块的情况。比例一样就无所谓了。你上面代码应该直接用就好。

    那这个功能就是为了节约存储空间,原本图形硬件上是自带插值的,分了两张贴图没法用了

    看了 cv2.resize() 的说明,默认就是线性插值,应该没有差异
    cherryas
        19
    cherryas  
       2022-12-26 13:37:46 +08:00
    小心 chatgpt 投毒.

    我问了个 vits 模型怎么写,给我编的有模有样的,怎么导包,方法名是什么.都像那回事.

    折腾了一会我发现 python 根本没 vits 这个包.
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     864 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 21:43 PVG 05:43 LAX 14:43 JFK 17:43
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86