title: unity-shader-reverse analysis of game rendering effects
categories: Unity3d-Shader
tags: [unity, shader, glsl, reverse]
date: 2022-07-18 18:59:06
comments: false
mathjax: true
toc: true
Reverse analysis of game rendering effect
Prequel
It’s all for copying other people’s effects, using software such as snapdragon profiler to capture a certain frame rendered by other people’s games, and restore it to the shader code of the corresponding engine through glsl reverse engineering.
There must be a comparison when restoring, for example, to restore a game made by unity, you can first write some shaders, pack it and use snapdragon to see what kind of glsl the function of the unity shader finally renders when rendering, and then face the one that needs to be reversed The game pushes back to the shader of unity.
Reverse of various functions
normalize normalization
// 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
size comparison
// 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
variable
intensive reading
uniform mediump float -> half (medium intensive)
input Output
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;
};
output
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;
Example 01 - Bird Soul
Room playing card diagram
Test shader git address:[email protected]:yangxuan0261/tdmj_shader.git
The main thing to look at is the processing of the following parts
- To the light/backlight part
- stroke
- shadow
1. First find out the rendering of the card body
Directly modify the fs rgb value to a certain value, it is easy to find the rendering batch of the card body
You can see that the rendering of the card body is separated from the stroke
2. Stroke
Enlarge the body of the card, remove the front side when the shader processes it, and only render the back side. Due to the depth relationship, it is blocked by the normal card body, and the stroke effect comes out.
And all the enlarged models of the cards can also be processed in one batch.