1
This commit is contained in:
@@ -1,29 +1,51 @@
|
||||
shader_type spatial;
|
||||
// 面部 = 两层贴图的合成:底图(预设脸)+ 笔画层(玩家自绘),后者带 alpha 盖在前面。
|
||||
// 两层都用「面部孤岛重映射后的 UV」采样,所以一张方形小图就能正好铺满整张脸。
|
||||
// 全身 shader:body_mesh 现在只有一个材质 mat_body,所以身体底色照常取它的 BaseColor,
|
||||
// 脸部**不再单独换材质**,而是在同一个 shader 里,只在指定 UV 矩形内把表情和手绘叠上去。
|
||||
// 这样脸和头部走的是同一套材质、同一套光照,不会再出现独立面片的色块边界。
|
||||
//
|
||||
// 为什么不是直接把贴图换掉:画布只装笔画、不装底图,橡皮擦掉笔画后露出来的是预设脸,
|
||||
// 而不是把脸擦成黑洞。这也是它能一边切预设一边保留手绘笔画的原因。
|
||||
// 贴图 alpha 只表示「墨线覆盖率」,绝不写进空间 ALPHA —— body_mesh 的面片必须保持不透明。
|
||||
|
||||
// 面部 UV 孤岛(实测自 mouse.glb 的 body_mesh surface 1 "face"):
|
||||
// u 0.142~0.356,v 0.038~0.238。v 往下外扩到 0.031 是为了取成正方形,避免拉伸。
|
||||
uniform vec2 face_uv_min = vec2(0.142, 0.031);
|
||||
uniform float face_uv_size : hint_range(0.01, 1.0) = 0.214;
|
||||
|
||||
// 预设脸。没接上时给张白图,至少不会一片黑
|
||||
uniform sampler2D base_tex : source_color, filter_linear, repeat_disable,
|
||||
uniform sampler2D body_tex : source_color, filter_linear, repeat_disable,
|
||||
hint_default_white;
|
||||
// 玩家笔画层(一个透明底的 SubViewport)。没接上时给张全透明图,等于没画
|
||||
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;
|
||||
|
||||
// 脸部在 body_mesh 的 UV 上占的矩形。**模型重导 / 改 UV 之后必须复核这里**:
|
||||
// 它要整块落在头部那块 UV 孤岛里面,否则表情会画到别的部位上。
|
||||
uniform vec2 face_uv_min = vec2(0.695, 0.682);
|
||||
uniform vec2 face_uv_size = vec2(0.301, 0.314);
|
||||
// 头部 UV 的 v 是「越往上越大」,而贴图第 0 行在上边,所以默认要把 v 翻过来,
|
||||
// 否则表情上下颠倒。
|
||||
uniform bool face_uv_flip_y = true;
|
||||
|
||||
// 表情底图:内置表情是保留原排布、已去底的透明图集;本地表情是整张透明 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;
|
||||
|
||||
void fragment() {
|
||||
vec2 uv = (UV - face_uv_min) / face_uv_size;
|
||||
vec4 base = texture(base_tex, uv);
|
||||
vec4 paint = texture(paint_tex, uv);
|
||||
// 全身底色都来自 mat_body 的 BaseColor,表情只是盖在脸上
|
||||
vec3 albedo = texture(body_tex, UV).rgb * body_tint.rgb;
|
||||
|
||||
ALBEDO = mix(base.rgb, paint.rgb, paint.a);
|
||||
// 顶掉 GLB 里 lambert1 那套手感,不然脸会比身体亮一块
|
||||
ROUGHNESS = 1.0;
|
||||
METALLIC = 0.0;
|
||||
vec2 face_uv = (UV - face_uv_min) / face_uv_size;
|
||||
if (face_uv_flip_y) {
|
||||
face_uv.y = 1.0 - face_uv.y;
|
||||
}
|
||||
// 用 step 而不是 if:采样留在统一控制流里,免得 mipmap 的导数在分支里未定义
|
||||
float mask = step(0.0, face_uv.x) * step(face_uv.x, 1.0)
|
||||
* step(0.0, face_uv.y) * step(face_uv.y, 1.0);
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user