unity-shader-游戏渲染效果逆向分析


title: unity-shader-游戏渲染效果逆向分析
categories: Unity3d-Shader
tags: [unity, shader, glsl, 逆向]
date: 2022-07-18 18:59:06
comments: false
mathjax: true
toc: true

游戏渲染效果逆向分析


前篇

都是为了抄别人的效果, 使用 snapdragon profiler 等软件将别人游戏渲染某帧时截取出来, 通过 glsl 逆向还原成对应引擎的 shader 代码.

还原时得有个对比, 比如还原的是 unity 做的游戏, 可以先随便写点 shader, 打包后用 snapdragon 看看 unity shader 的函数最终在渲染时的是怎么样的 glsl, 然后对着要逆向的 游戏 反推回 unity 的 shader.


各种函数的逆向

normalize 归一化

// normalize(_WorldSpaceLightPos0.xyz)

u_xlat0.x = dot(_WorldSpaceLightPos0.xyz, _WorldSpaceLightPos0.xyz);
u_xlat0.x = inversesqrt(u_xlat0.x);
u_xlat0.xyz = u_xlat0.xxx * _WorldSpaceLightPos0.xyz;

UnityObjectToWorldNormal

// UnityObjectToWorldNormal(v.normal)

u_xlat1.x = dot(in_NORMAL0.xyz, unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_WorldToObjectArray[0].xyz);
u_xlat1.y = dot(in_NORMAL0.xyz, unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_WorldToObjectArray[1].xyz);
u_xlat1.z = dot(in_NORMAL0.xyz, unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_WorldToObjectArray[2].xyz);
u_xlat0.x = dot(u_xlat1.xyz, u_xlat1.xyz);
u_xlat0.x = inversesqrt(u_xlat0.x);
u_xlat0.xyz = u_xlat0.xxx * u_xlat1.xyz;

UnityObjectToClipPos

// UnityObjectToClipPos(v.vertex);

u_xlat1 = in_POSITION0.yyyy * unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_ObjectToWorldArray[1];
u_xlat1 = unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_ObjectToWorldArray[0] * in_POSITION0.xxxx + u_xlat1;
u_xlat1 = unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_ObjectToWorldArray[2] * in_POSITION0.zzzz + u_xlat1;
u_xlat1 = u_xlat1 + unity_Builtins0Array[u_xlati0 / 8].hlslcc_mtx4x4unity_ObjectToWorldArray[3];
u_xlat2 = u_xlat1.yyyy * hlslcc_mtx4x4unity_MatrixVP[1];
u_xlat2 = hlslcc_mtx4x4unity_MatrixVP[0] * u_xlat1.xxxx + u_xlat2;
u_xlat2 = hlslcc_mtx4x4unity_MatrixVP[2] * u_xlat1.zzzz + u_xlat2;
u_xlat1 = hlslcc_mtx4x4unity_MatrixVP[3] * u_xlat1.wwww + u_xlat2;
gl_Position = u_xlat1;

saturate

// saturate
#ifdef UNITY_ADRENO_ES3
    u_xlat0.x = min(max(u_xlat0.x, 0.0), 1.0);
#else
    u_xlat0.x = clamp(u_xlat0.x, 0.0, 1.0);
#endif

大小比较

// bool a = b < c.x;
#ifdef UNITY_ADRENO_ES3
    u_xlatb1 = !!(u_xlat16_14<u_xlat16_0.x);
#else
    u_xlatb1 = u_xlat16_14<u_xlat16_0.x;
#endif

变量

精读

uniform mediump float -> half (中等精读)


输入输出

in mediump vec4 in_POSITION0;
in mediump vec3 in_NORMAL0;
in mediump vec2 in_TEXCOORD0;

// 对应 unity
struct appdata_t {
    
    
    half4 vertex : POSITION;
    half3 normal : NORMAL;
    half2 texcoord : TEXCOORD0;
};

输出

out mediump vec2 vs_TEXCOORD0;
out mediump vec3 vs_TEXCOORD1;

// 对应 unity
struct v2f {
    
    
    half4 vertex : SV_POSITION; // unity 必要的属性
    half2 texcoord : TEXCOORD0;
    half3 tex02 : TEXCOORD1;
};

uniform

uniform 	vec4 _WorldSpaceLightPos0; // unity 内置的世界空间下的 灯光位置

uniform 	mediump vec4 _Tint;
uniform 	mediump vec4 _ColorLight;
uniform 	mediump vec4 _ColorUnlight;
uniform 	mediump float _SplitThreshold;

// 对应 unity
_Tint("Tint", Color) = (1,1,1,1)
_ColorLight("ColorLight", Color) = (1,1,0,1)
_ColorUnlight("ColorUnlight", Color) = (0,0,0,1)
_SplitThreshold("SplitThreshold", float) = 1
            
half4 _Tint;
half4 _ColorLight;
half4 _ColorUnlight;
half _SplitThreshold;

实例01 - 雀魂

房间打牌示意图

image-20220721163816269

测试 shader git 地址: [email protected]:yangxuan0261/tdmj_shader.git

主要看的是一下几部分的处理

  1. 向光/背光 部分
  2. 描边
  3. 阴影

1. 先找出牌牌体的渲染

直接修改 fs rgb 值为某个值, 就很容易找到牌体的渲染批次

asd

可以看到 牌体的渲染 和 描边分开的


2. 描边

asd

将牌体放大, shader 处理时剔除掉正面, 只渲染背面, 由于深度关系, 被正常牌体遮挡住, 描边效果就出来了.

而且所有这种牌体放大的模型也可以一个批次处理完成.

image-20220721165110995


3. 阴影

sdf


猜你喜欢

转载自blog.csdn.net/yangxuan0261/article/details/125946318