This commit is contained in:
2026-10-03 05:40:26 +08:00
parent faf799837f
commit 7bcbcc69ea
7 changed files with 96 additions and 44 deletions
+20 -14
View File
@@ -1,9 +1,23 @@
shader_type spatial;
// 全身 shader:body_mesh 现在只有一个材质 mat_body,所以身体底色照常取它的 BaseColor,
// 脸部**不再单独换材质**,而是在同一个 shader 里,只在指定 UV 矩形内把表情和手绘叠上去。
// 这样脸和头部走的是同一套材质、同一套光照,不会再出现独立面片的色块边界。
// 全身 shader:body_mesh 只有一个材质 mat_body —— 身体底色照常取它的 BaseColor,
// 脸部不再换材质,只在这一个 shader 里把表情和手绘叠到指定 UV 矩形上。
// 脸和周围走的是同一套材质、同一套光照,所以不会再出现独立面片的色块边界。
//
// 贴图 alpha 只表示「墨线覆盖率」,绝不写进空间 ALPHA —— body_mesh 的面片必须保持不透明。
// 贴图 alpha 只代表「墨线覆盖率」,绝不写进空间 ALPHA —— body_mesh 的面片必须保持不透明。
// ================= 调表情就改这一段 =================
// 头部那一段 UV 孤岛的实测范围:u 0.695~0.996(左右),v 0.682~0.996(上下,v 越大越靠头顶)。
// 下面三个值就是「表情画在哪 / 多大 / 正不正」,FaceRig 不会覆盖它们,改完存盘即可。
//
// 表情中心点:**想往下挪就减小 .y**,往左挪就减小 .x
uniform vec2 face_center = vec2(0.8455, 0.639);
// 表情大小:.x 是左右宽度,.y 是上下高度(想只盖住半张脸就把 .y 改小)
uniform vec2 face_size = vec2(0.301, 0.314);
// 上下颠倒了就改成 false(头部 UV 的 v 朝上、贴图第 0 行在上边,所以默认要翻)
uniform bool face_flip_y = true;
// ====================================================
//
// 中心 ± 半个大小必须整个落在上面那段 UV 孤岛里,否则表情会糊到肚子 / 背上去。
uniform sampler2D body_tex : source_color, filter_linear, repeat_disable,
hint_default_white;
@@ -11,14 +25,6 @@ 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;
@@ -32,8 +38,8 @@ void fragment() {
// 全身底色都来自 mat_body 的 BaseColor,表情只是盖在脸上
vec3 albedo = texture(body_tex, UV).rgb * body_tint.rgb;
vec2 face_uv = (UV - face_uv_min) / face_uv_size;
if (face_uv_flip_y) {
vec2 face_uv = (UV - (face_center - face_size * 0.5)) / face_size;
if (face_flip_y) {
face_uv.y = 1.0 - face_uv.y;
}
// 用 step 而不是 if:采样留在统一控制流里,免得 mipmap 的导数在分支里未定义
+5 -12
View File
@@ -30,17 +30,13 @@ const FACE_SYNC_INITIAL_DELAY := 1.0
@export_group("画布")
@export var canvas_size: int = 512
## 与两个原始贴图的背景/头部底色完全相同:RGB(137,179,255)。
## 与模型身体贴图的底色一致(FaceRig 启动时会从 mat_body 的 BaseColor 左上角自动读出来)。
## 只给画布和缩略图垫底用,不参与 3D 着色。
@export var skin_color: Color = Color(0.537255, 0.701961, 1.0, 1.0)
@export_group("脸部 UV")
## 脸部矩形在 body_mesh 的 UV 上占的范围(实测自当前模型:头部孤岛是 u 0.695~0.996 / v 0.682~0.996)。
## 它必须整块落在头部那块 UV 孤岛里;模型重导后 UV 一变,表情就会画歪或跑到别的部位上。
@export var face_uv_min: Vector2 = Vector2(0.695, 0.682)
## 脸部矩形的 UV 边长
@export var face_uv_size: Vector2 = Vector2(0.301, 0.314)
## 头部 UV 的 v 越往上越大,而贴图第 0 行在上边 —— 不翻就在脸上上下颠倒
@export var face_uv_flip_y: bool = true
## 表情画在哪 / 多大 / 正不正,全部写在 face_paint.gdshader 顶部的「调表情就改这一段」里
## (face_center / face_size / face_flip_y)。**FaceRig 故意不传这三个 uniform**,
## 免得脚本里的值把 shader 里调好的值盖掉。
var mesh: MeshInstance3D
var material: ShaderMaterial
@@ -88,9 +84,6 @@ func _ready() -> void:
material.set_shader_parameter("body_tint", body_material.albedo_color)
material.set_shader_parameter("surface_roughness", body_material.roughness)
material.set_shader_parameter("surface_metallic", body_material.metallic)
material.set_shader_parameter("face_uv_min", face_uv_min)
material.set_shader_parameter("face_uv_size", face_uv_size)
material.set_shader_parameter("face_uv_flip_y", face_uv_flip_y)
material.set_shader_parameter("paint_tex", painter.texture)
mesh.set_surface_override_material(surface_index, material)