shader_type spatial; // 全身 shader:body_mesh 只有一个材质 mat_body —— 身体底色照常取它的 BaseColor。 // 脸不再靠 UV 矩形,而是「贴在身体正面上的一块贴花」:用**模型本地坐标**的 (x, y) 定位, // 再用「法线朝前的程度」当遮罩。 // 这样它和 UV 孤岛怎么排完全无关 —— 表情可以放在身体正面任意位置,也不会再越界糊到肚子上。 // // 贴图 alpha 只代表「墨线覆盖率」,绝不写进空间 ALPHA —— body_mesh 的面片必须保持不透明。 // ================= 调表情就改这一段 ================= // 中心点是**模型本地坐标**(和 mesh 顶点同一个空间,模型整体大约 x -3~3、y -3~3)。 // 想往下挪就减小 .y,往左挪就减小 .x。 uniform vec2 face_center = vec2(0.0, 0.6); // 表情大小:.x 是左右宽度,.y 是上下高度 uniform vec2 face_size = vec2(4.0, 4.0); // 只在身体正面出现:朝**模型本地 +Z**(尾巴在 -Z 那一侧,所以正面是 +Z)的那面才画。 // 调大就只留最正面的一小块,调小会往侧面包过去。 uniform float face_front_edge : hint_range(0.0, 1.0) = 0.0; // 上下颠倒了就改成 false uniform bool face_flip_y = true; // ==================================================== uniform sampler2D body_tex : source_color, filter_linear, repeat_disable, hint_default_white; uniform vec4 body_tint : source_color = vec4(1.0, 1.0, 1.0, 1.0); uniform float surface_roughness : hint_range(0.0, 1.0) = 0.65; uniform float surface_metallic : hint_range(0.0, 1.0) = 0.0; // 表情底图:内置表情是保留原排布、已去底的透明图集;本地表情是整张透明 PNG。 uniform sampler2D base_tex : source_color, filter_linear, repeat_disable, hint_default_transparent; uniform vec2 base_uv_min = vec2(0.0, 0.0); uniform vec2 base_uv_size = vec2(1.0, 1.0); // 玩家笔画层(透明底) uniform sampler2D paint_tex : source_color, filter_linear, repeat_disable, hint_default_transparent; // 贴花要用「模型本地坐标」,而 fragment 里拿不到,所以从顶点阶段传过来 varying vec3 object_position; varying vec3 object_normal; void vertex() { object_position = VERTEX; object_normal = NORMAL; } void fragment() { // 全身底色都来自 mat_body 的 BaseColor,表情只是盖在脸上 vec3 albedo = texture(body_tex, UV).rgb * body_tint.rgb; vec2 face_uv = (object_position.xy - (face_center - face_size * 0.5)) / face_size; if (face_flip_y) { face_uv.y = 1.0 - face_uv.y; } // 用 step 而不是 if:采样留在统一控制流里,免得 mipmap 的导数在分支里未定义 float inside = step(0.0, face_uv.x) * step(face_uv.x, 1.0) * step(0.0, face_uv.y) * step(face_uv.y, 1.0); // 背面的点 (x, y) 和正面一样,也会落进矩形,所以要再按「朝不朝前」筛一遍。 // 模型正面是**本地 +Z**(尾巴在 -Z 那一侧),写成 -normal.z 表情就跑到背上去了。 float front = smoothstep(face_front_edge, face_front_edge + 0.3, normalize(object_normal).z); float mask = inside * front; vec4 base = texture(base_tex, base_uv_min + face_uv * base_uv_size); vec4 paint = texture(paint_tex, face_uv); // 透明处保持身体底色 —— 所以脸和周围不可能出现底色差 albedo = mix(albedo, base.rgb, base.a * mask); albedo = mix(albedo, paint.rgb, paint.a * mask); ALBEDO = albedo; ROUGHNESS = surface_roughness; METALLIC = surface_metallic; }