aboutsummaryrefslogtreecommitdiffhomepage
path: root/public
diff options
context:
space:
mode:
authorYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-10-04 00:21:06 +0900
committerYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-10-04 00:21:06 +0900
commitd871b2e093a70d5ee4e7c6c91f54f05c7422707d (patch)
treef617fa9dfedd990ae86d3e4bfab49eaecd965e5b /public
parentc81104129a9ba6e26aa33a00770f76a6aa97b2a9 (diff)
bluebey-studio: 髪の板の背中の筋(円筒UVの継ぎ目)を修正
円筒UVの継ぎ目をまたぐ三角形がテクスチャ全体を引き伸ばし、背中に縦の筋が出ていた。髪の板を三角形ごとに独立した頂点へ展開してから、継ぎ目をまたぐ三角形のUVを1周ぶん補正し、筋を消した。トリム後のモデルも取り込み。
Diffstat (limited to 'public')
-rw-r--r--public/bluebey-studio/assets/bluebey.glbbin3253400 -> 3109288 bytes
-rw-r--r--public/bluebey-studio/src/model.js33
2 files changed, 33 insertions, 0 deletions
diff --git a/public/bluebey-studio/assets/bluebey.glb b/public/bluebey-studio/assets/bluebey.glb
index 13131f5..0072864 100644
--- a/public/bluebey-studio/assets/bluebey.glb
+++ b/public/bluebey-studio/assets/bluebey.glb
Binary files differ
diff --git a/public/bluebey-studio/src/model.js b/public/bluebey-studio/src/model.js
index 68f1255..3925e85 100644
--- a/public/bluebey-studio/src/model.js
+++ b/public/bluebey-studio/src/model.js
@@ -134,6 +134,10 @@ export async function loadModel(source, { onProgress } = {}) {
const mouth = mouthPlate ?? mouthOriginal;
// The wrapping hair shell, if the model carries one (`hair-plate`).
const hairMesh = hairPlate;
+ // Its cylindrical UV has a seam at the back of the head. Triangles that cross
+ // it get one UV near 0 and the next near 1, so the whole texture is stretched
+ // across them - a visible streak down the back. Close the seam per triangle.
+ if (hairMesh) healCylinderSeam(hairMesh);
if (!eyeMesh) throw new Error('eye mesh not found in the GLB');
if (!mouth) throw new Error('mouth mesh not found in the GLB');
@@ -248,3 +252,32 @@ function inflateOverlay(mesh, centre, distance) {
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
}
+
+/**
+ * Make every triangle of a cylindrical UV island sit on one side of the seam.
+ *
+ * A cylinder unwrap cuts the surface open along one line (here the back of the
+ * head). A triangle that spans the cut has its corners at u ~ 1 and u ~ 0, so
+ * interpolating them drags the whole texture across the triangle - the streak
+ * seen down the back. Wrapping the low corners up by one turn keeps each
+ * triangle local; the texture's two edges are identical (the covering is drawn
+ * right across), so the join is invisible.
+ */
+function healCylinderSeam(mesh) {
+ const source = mesh?.geometry;
+ if (!source?.attributes?.uv) return;
+ // Give every triangle its own corners first. The seam's low and high sides
+ // share vertices, so editing a shared one would drag its neighbours too; a
+ // non-indexed copy isolates each triangle and the fix cannot leak.
+ const geometry = source.index ? source.toNonIndexed() : source;
+ const uv = geometry.attributes.uv;
+ for (let i = 0; i < uv.count; i += 3) {
+ const top = Math.max(uv.getX(i), uv.getX(i + 1), uv.getX(i + 2));
+ if (top - Math.min(uv.getX(i), uv.getX(i + 1), uv.getX(i + 2)) <= 0.5) continue;
+ for (let k = 0; k < 3; k += 1) {
+ if (uv.getX(i + k) < top - 0.5) uv.setX(i + k, uv.getX(i + k) + 1);
+ }
+ }
+ uv.needsUpdate = true;
+ mesh.geometry = geometry;
+}