移动端人物渲染

移动端人物渲染

四月 14, 2022

在这里插入图片描述

模型与贴图分析

模型

导入武器时属于泛型。

psk导入模型时3dmax效果比blender好,blender的模型不会平滑,选用3dmax加载模型。

贴图分析

Albedo 贴图

包含金属非金属部分的基本颜色

combmap 贴图

R通道是roughness 粗糙度

G通道是金属度

B,如果皮肤通道为红色部分地方为蓝色就是皮肤部分

法线贴图

见之前文章

光照框架

直接光照反射

lambert

1
half half_lambert = (diffuse_term+1.0)*0.5;

SSS 散光

请添加图片描述

1
2
3
4
5
half skin_area  = 1- comb_map.b;//获取颜色部分
half2 uv_lut = float2(min(1, diffuse_term*atten +_LUTOffset),_CurveOffset);//将颜色分布在光照边缘
half3 lut_color = tex2D(_SSSTrick,uv_lut);
half3 sss_diffuse = lut_color *base_col*_LightColor0.xyz* half_lambert;//散射部分光照
half3 diffuse = lerp(comm_diffuse,sss_diffuse,skin_area);//区分皮肤和非皮肤部分

直接光镜面反射

Blin-Phong

利用V+L代替reflect(-L,N)

KK各项异性

光照射头发、不锈钢锅底,光碟会出现这样的现象。头发可以去除间接光的漫反射。

简单原理版

在这里插入图片描述

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
half3 half_dir =normalize( light_dir+view_dir);//得到半角向量
half2 uv_shift = i.texcoord * _ShiftNoise_ST.xy+_ShiftNoise_ST.zw;//控制发丝密度
half shiftnoise = tex2D(_ShiftNoise,uv_shift).r;
shiftnoise =(shiftnoise*2.0-1.0)*_NoiseIntensity;//从-1 - 1 映射到 0 - 1
half3 b_offset = normal_dir*(_Shiftoffset+shiftnoise);//法线方向偏移(上下移动)

binormal_dir = normalize(binormal_dir+ b_offset);
half TdotN=dot(binormal_dir,half_dir);
half sinTH = sqrt(1-TdotN*TdotN);
fixed3 specular_color = pow(max(0,sinTH),_Shininess)//将公式
*_LightColor0.xyz;

ShiftNoises

_ShiftNoise

头发部分体现,调了半天没调好,开摆。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
half roughness = 0.2;
half3 half_dir =normalize( light_dir+view_dir);
half2 uv_Anoise = i.texcoord*_AnoiseMap_ST.xy+_AnoiseMap_ST.zw;
half3 aniso_noise = tex2D(_AnoiseMap,uv_Anoise);

half NdotH = dot(normal_dir,half_dir);
half TdotH = dot(tangent_dir,half_dir);

half NdotV = max(0, dot(normal_dir,view_dir));
half aniso_atten = saturate(sqrt(max(0,half_lambert/NdotV)))*atten;
//spec1
half3 specular_color1 = _SpecularColor1 + base_col;
half3 aniso_offset1 = normal_dir *(aniso_noise*_SpecNoise1+_Shiftoffset1);
half3 binormal_dir1 = normalize(binormal_dir + aniso_offset1);
half BdotH1 = dot(binormal_dir1,half_dir)/ _SpecShininess1;
half3 specular_term1 = exp(-(TdotH*TdotH+BdotH1*BdotH1)/(1.0+NdotH));
half3 specular1 = specular_term1*aniso_atten*specular_color1*_LightColor0.xyz;

half3 specular_color2 = _SpecularColor2 + base_col;
half3 aniso_offset2 = normal_dir *(aniso_noise*_SpecNoise2+_Shiftoffset2);
half3 binormal_dir2 = normalize(binormal_dir + aniso_offset2);
half BdotH2 = dot(binormal_dir2,half_dir)/ _SpecShininess2;
half3 specular_term2 = exp(-(TdotH*TdotH+BdotH2*BdotH2)/(1.0+NdotH));
half3 specular2 = specular_term2*aniso_atten*specular_color2*_LightColor0.xyz;
half3 specular = specular1+specular2;
col.xyz+=specular;

ACES_Tonemapping

1
2
3
4
5
6
7
8
9
10
float3 ACES_Tonemapping(float3 x)
{
float a = 2.51f;
float b = 0.03f;
float c = 2.43f;
float d = 0.59f;
float e = 0.14f;
float3 encode_color = saturate((x*(a*x + b)) / (x*(c*x + d) + e));
return encode_color;
};

宏开关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Properties
[Toggle(_D_ok)] _DiffuseCheck("_DiffuseCheck",Float) = 1.0


//Pass
#pragma shader_feature _D_ok;


#ifdef _D_ok
one
#else
two
#endif

或者把

1
#pragma shader_feature _D_ok;

换成

1
#pragma multi_compile _D_ok;

人物代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
Shader "Human"
{
Properties
{
_Diffuse("Diffuse", 2D) = "white" {}
_Normal("Normal", 2D) = "bump" {}
_NormalIntensity("Normal Intensity", Float) = 1.0
_Combmap("Combmap", 2D) = "black" {}
_SSSTrick("SSSTrick", 2D) = "black" {}
_CurveOffset("Curve Offset", Range(-1,1)) = 1.0
_LUTOffset("LUT Offset", Range(-1,1)) = 1.0
_SpecShininess("Spec Shininess", Float) = 1.0

_EnvMap("EnvMap", Cube) = "black" {}
_Expose("Expose", Float) = 1.0

[HideInInspector]custom_SHAr("Custom SHAr", Vector) = (0, 0, 0, 0)
[HideInInspector]custom_SHAg("Custom SHAg", Vector) = (0, 0, 0, 0)
[HideInInspector]custom_SHAb("Custom SHAb", Vector) = (0, 0, 0, 0)
[HideInInspector]custom_SHBr("Custom SHBr", Vector) = (0, 0, 0, 0)
[HideInInspector]custom_SHBg("Custom SHBg", Vector) = (0, 0, 0, 0)
[HideInInspector]custom_SHBb("Custom SHBb", Vector) = (0, 0, 0, 0)
[HideInInspector]custom_SHC("Custom SHC", Vector) = (0, 0, 0, 1)

}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "AutoLight.cginc"
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};
struct v2f
{
float2 texcoord : TEXCOORD0;

float4 pos : SV_POSITION;
float3 normal_world:TEXCOORD1;
float3 binormal_world:TEXCOORD2;
float3 tangent_world:TEXCOORD3;
float3 pos_world:TEXCOORD4;
LIGHTING_COORDS(5,6)
};
sampler2D _Diffuse;
float4 _Diffuse_ST;
sampler2D _Normal;
float _NormalIntensity;
float _SpecShininess;
float _Expose;
float4 _LightColor0;
sampler2D _Combmap;
sampler2D _SSSTrick;

samplerCUBE _EnvMap;
float4 _EnvMap_HDR;
float _CurveOffset;
float _LUTOffset;

float4 custom_SHAr;
float4 custom_SHAg;
float4 custom_SHAb;
float4 custom_SHBr;
float4 custom_SHBg;
float4 custom_SHBb;
float4 custom_SHC;
float3 SH(half3 normal_dir)
{
float4 normalForSH = float4(normal_dir, 1.0);
//SHEvalLinearL0L1
half3 x;
x.r = dot(custom_SHAr, normalForSH);
x.g = dot(custom_SHAg, normalForSH);
x.b = dot(custom_SHAb, normalForSH);
//SHEvalLinearL2
half3 x1, x2;
// 4 of the quadratic (L2) polynomials
half4 vB = normalForSH.xyzz * normalForSH.yzzx;
x1.r = dot(custom_SHBr, vB);
x1.g = dot(custom_SHBg, vB);
x1.b = dot(custom_SHBb, vB);
// Final (5th) quadratic (L2) polynomial
half vC = normalForSH.x*normalForSH.x - normalForSH.y*normalForSH.y;
x2 = custom_SHC.rgb * vC;
float3 sh = max(float3(0.0, 0.0, 0.0), (x + x1 + x2));
sh = pow(sh, 1.0 / 2.2);
return sh;
}
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoord =v.uv;
o.normal_world = normalize(mul(float4(v.normal,0.0),unity_WorldToObject)).xyz;
o.tangent_world = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)));
o.pos_world = normalize(mul(unity_ObjectToWorld,float4(v.vertex.xyz,0.0)));
o.binormal_world =normalize(cross(o.normal_world,o.tangent_world)*v.tangent.w);
TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
half atten = LIGHT_ATTENUATION (i);
fixed4 col = fixed4(0,0,0,1);
fixed3 albedo = tex2D(_Diffuse, i.texcoord).xyz;
fixed3 comb_map = tex2D(_Combmap, i.texcoord).xyz;

half metal = comb_map.g;
fixed3 base_col =albedo * (1-metal);
fixed3 spec_color = lerp(0.04,albedo,metal);

half3 normal_dir = normalize(i.normal_world);
half3 pos_world = normalize(i.pos_world);
half3 tangent_dir = normalize(i.tangent_world)*_NormalIntensity;
half3 binormal_dir = normalize(i.binormal_world)*_NormalIntensity;
half4 normalmap= tex2D(_Normal,i.texcoord);
half3 normal_data = UnpackNormal( normalmap);
float3x3 TBN = float3x3(tangent_dir,binormal_dir,normal_dir);
normal_dir = normalize(mul(normal_data,TBN));

half3 view_dir = normalize(_WorldSpaceCameraPos.xyz-pos_world);
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);

//直接光漫反射

half diffuse_term = max(dot(normal_dir,light_dir),0);
half3 comm_diffuse = diffuse_term*base_col*atten*_LightColor0.xyz;
half half_lambert = (diffuse_term+1.0)*0.5;
half skin_area = 1- comb_map.b;
half2 uv_lut = float2(min(1, diffuse_term*atten +_LUTOffset),_CurveOffset);
half3 lut_color = tex2D(_SSSTrick,uv_lut);
half3 sss_diffuse = lut_color *base_col*_LightColor0.xyz* half_lambert;
half3 diffuse = lerp(comm_diffuse,sss_diffuse,skin_area);
col.xyz+=diffuse;
//直接光镜面反射
half3 half_dir =normalize( light_dir+view_dir);
half roughness = comb_map.r;
half smoothness = 1.0-roughness;
half shininess = lerp(1.0,_SpecShininess,smoothness);
half reflect_term = pow(max(0,dot(normal_dir,half_dir)),shininess);
half3 specular = reflect_term * spec_color*atten*_LightColor0.xyz;
col.xyz+=specular;
//间接光照

half3 env_diffuse = SH(normal_dir)*base_col*half_lambert;
// sample the texture
col.xyz+=env_diffuse;

//间接镜面反射
roughness=roughness*(1.7-0.7*roughness);
half mip_level = roughness*6.0;
half4 Cube_col=texCUBElod(_EnvMap,float4(half_dir,mip_level));
half3 EnvHDR_col=DecodeHDR(Cube_col,_EnvMap_HDR);
half3 env_specular = EnvHDR_col *spec_color*half_lambert*_Expose;
col.xyz+=env_specular;
col.xyz*=0.8;
return col;
// return fixed4(diffuse,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}

头发代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
Shader "Hair"
{
Properties
{
_Diffuse("Diffuse", 2D) = "white" {}
_DiffuseColor("Diffuse Color", Color) = (0,0,0,1)
_Normal("Normal", 2D) = "bump" {}
_NormalIntensity("Normal Intensity", Float) = 1.0

_AnoiseMap("AnoiseMap", 2D) = "gray" {}
_SpecularColor1("Specular Color1", Color) = (0,0,0,1)
_SpecShininess1("Spec Shininess1", Float) = 1.0
_Shiftoffset1 ("Shift offset1", Range(-10,10)) = 1.0
_SpecNoise1 ("Spec Noise1", Float) = 1.0

_SpecularColor2("Specular Color2", Color) = (0,0,0,1)
_SpecShininess2("Spec Shininess2", Float) = 1.0
_Shiftoffset2 ("Shift offset2", Range(-10,10)) = 1.0
_SpecNoise2 ("Spec Noise2", Float) = 1.0

_EnvMap("EnvMap", Cube) = "black" {}
_Expose("Expose", Float) = 1.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "AutoLight.cginc"
#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};

struct v2f
{
float2 texcoord : TEXCOORD0;

float4 pos : SV_POSITION;
float3 normal_world:TEXCOORD1;
float3 binormal_world:TEXCOORD2;
float3 tangent_world:TEXCOORD3;
float3 pos_world:TEXCOORD4;
LIGHTING_COORDS(5,6)

};

sampler2D _Diffuse;
float4 _Diffuse_ST;
sampler2D _Normal;
float _NormalIntensity;
float _SpecShininess;
float _Expose;
float4 _LightColor0;
float4 _DiffuseColor;

samplerCUBE _EnvMap;
float4 _EnvMap_HDR;
float _CurveOffset;
float _LUTOffset;

sampler2D _AnoiseMap;
float4 _AnoiseMap_ST;
float4 _SpecularColor1;
float _SpecShininess1;
float _Shiftoffset1 ;
float _SpecNoise1 ;

float4 _SpecularColor2;
float _SpecShininess2;
float _Shiftoffset2 ;
float _SpecNoise2 ;

v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoord =v.uv;
o.normal_world = normalize(mul(float4(v.normal,0.0),unity_WorldToObject)).xyz;
o.tangent_world = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)));
o.pos_world = normalize(mul(unity_ObjectToWorld,float4(v.vertex.xyz,0.0)));
o.binormal_world =normalize(cross(o.normal_world,o.tangent_world)*v.tangent.w);

//o.texcoord = TRANSFORM_TEX(v.uv, _MainTex);
TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
}

fixed4 frag (v2f i) : SV_Target
{


half atten = LIGHT_ATTENUATION (i);
fixed4 col = fixed4(0,0,0,1);
fixed3 albedo = tex2D(_Diffuse, i.texcoord).xyz*_DiffuseColor;

fixed3 base_col =albedo ;
fixed3 spec_color =albedo ;

half3 normal_dir = normalize(i.normal_world);
half3 pos_world = normalize(i.pos_world);
half3 tangent_dir = normalize(i.tangent_world)*_NormalIntensity;
half3 binormal_dir = normalize(i.binormal_world)*_NormalIntensity;
half4 normalmap= tex2D(_Normal,i.texcoord);
half3 normal_data = UnpackNormal( normalmap);
float3x3 TBN = float3x3(tangent_dir,binormal_dir,normal_dir);
normal_dir = normalize(mul(normal_data,TBN));

half3 view_dir = normalize(_WorldSpaceCameraPos.xyz-pos_world);
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);

//直接光漫反射

half diffuse_term = max(dot(normal_dir,light_dir),0);
half half_lambert = (diffuse_term+1.0)*0.5;
half3 comm_diffuse = diffuse_term*base_col*atten*_LightColor0.xyz;
half3 diffuse = comm_diffuse;

col.xyz+=diffuse;
//直接光镜面反射
half roughness = 0.2;
half3 half_dir =normalize( light_dir+view_dir);
half2 uv_Anoise = i.texcoord*_AnoiseMap_ST.xy+_AnoiseMap_ST.zw;
half3 aniso_noise = tex2D(_AnoiseMap,uv_Anoise);

half NdotH = dot(normal_dir,half_dir);
half TdotH = dot(tangent_dir,half_dir);

half NdotV = max(0, dot(normal_dir,view_dir));
half aniso_atten = saturate(sqrt(max(0,half_lambert/NdotV)))*atten;
//spec1
half3 specular_color1 = _SpecularColor1 + base_col;
half3 aniso_offset1 = normal_dir *(aniso_noise*_SpecNoise1+_Shiftoffset1);
half3 binormal_dir1 = normalize(binormal_dir + aniso_offset1);
half BdotH1 = dot(binormal_dir1,half_dir)/ _SpecShininess1;
half3 specular_term1 = exp(-(TdotH*TdotH+BdotH1*BdotH1)/(1.0+NdotH));
half3 specular1 = specular_term1*aniso_atten*specular_color1*_LightColor0.xyz;

half3 specular_color2 = _SpecularColor2 + base_col;
half3 aniso_offset2 = normal_dir *(aniso_noise*_SpecNoise2+_Shiftoffset2);
half3 binormal_dir2 = normalize(binormal_dir + aniso_offset2);
half BdotH2 = dot(binormal_dir2,half_dir)/ _SpecShininess2;
half3 specular_term2 = exp(-(TdotH*TdotH+BdotH2*BdotH2)/(1.0+NdotH));
half3 specular2 = specular_term2*aniso_atten*specular_color2*_LightColor0.xyz;
half3 specular = specular1+specular2;
col.xyz+=specular;


//间接镜面反射
roughness=roughness*(1.7-0.7*roughness);
half mip_level = roughness*6.0;
half4 Cube_col=texCUBElod(_EnvMap,float4(half_dir,mip_level));
half3 EnvHDR_col=DecodeHDR(Cube_col,_EnvMap_HDR);
half3 env_specular = EnvHDR_col *spec_color*half_lambert*_Expose;
col.xyz+=env_specular;
//return col;
return fixed4(specular,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}