You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js中为自制Minecraft模型添加皮肤纹理及外层?

给自制Minecraft模型添加皮肤纹理(React Three Fiber + Next.js)

核心思路:匹配Minecraft皮肤的UV映射

Minecraft皮肤是固定布局的PNG图片(通常64x64或256x64),每个身体部位对应图片上的特定区域。我们需要给每个box的面设置正确的UV坐标,再加载皮肤纹理替换纯色材质。

第一步:加载皮肤纹理

在Next.js中,推荐用@react-three/drei的useTexture钩子加载图片,它会自动处理SSR兼容问题。

第二步:为每个身体部位调整UV

默认的BoxGeometry UV是均匀分布的,需要手动修改每个面的UV,对应皮肤的对应区域。比如头部的正面对应皮肤的[8,8]到[16,16]区域,需要转换成0-1的UV坐标(除以64)。

修改后的完整代码

import { Canvas } from '@react-three/fiber';
import { OrbitControls, useTexture } from '@react-three/drei';
import { useEffect } from 'react';

function MinecraftModel() {
  // 加载皮肤纹理,替换成你的皮肤图片路径
  const skinTexture = useTexture('/skin.png');

  useEffect(() => {
    // 头部UV调整:对应皮肤的头部区域
    const headGeo = document.querySelector('mesh:nth-child(1) > boxGeometry');
    if (headGeo) {
      const uvAttribute = headGeo.attributes.uv;
      // 正面:皮肤[8,8]~[16,16] → UV (8/64,8/64)~(16/64,16/64)
      uvAttribute.setXY(0, 8/64, 8/64);
      uvAttribute.setXY(1, 16/64, 8/64);
      uvAttribute.setXY(2, 16/64, 16/64);
      uvAttribute.setXY(3, 8/64, 16/64);
      // 顶面:皮肤[8,0]~[16,8]
      uvAttribute.setXY(4, 8/64, 0/64);
      uvAttribute.setXY(5, 16/64, 0/64);
      uvAttribute.setXY(6, 16/64, 8/64);
      uvAttribute.setXY(7, 8/64, 8/64);
      // 底面:皮肤[24,0]~[32,8]
      uvAttribute.setXY(8, 24/64, 0/64);
      uvAttribute.setXY(9, 32/64, 0/64);
      uvAttribute.setXY(10, 32/64, 8/64);
      uvAttribute.setXY(11, 24/64, 8/64);
      // 左面:皮肤[0,8]~[8,16]
      uvAttribute.setXY(12, 0/64, 8/64);
      uvAttribute.setXY(13, 8/64, 8/64);
      uvAttribute.setXY(14, 8/64, 16/64);
      uvAttribute.setXY(15, 0/64, 16/64);
      // 右面:皮肤[16,8]~[24,16]
      uvAttribute.setXY(16, 16/64, 8/64);
      uvAttribute.setXY(17, 24/64, 8/64);
      uvAttribute.setXY(18, 24/64, 16/64);
      uvAttribute.setXY(19, 16/64, 16/64);
      // 背面:皮肤[32,8]~[40,16]
      uvAttribute.setXY(20, 32/64, 8/64);
      uvAttribute.setXY(21, 40/64, 8/64);
      uvAttribute.setXY(22, 40/64, 16/64);
      uvAttribute.setXY(23, 32/64, 16/64);
      uvAttribute.needsUpdate = true;
    }

    // 躯干UV调整:对应皮肤[20,20]~[28,32]区域
    const torsoGeo = document.querySelector('mesh:nth-child(2) > boxGeometry');
    if (torsoGeo) {
      const uvAttribute = torsoGeo.attributes.uv;
      // 正面
      uvAttribute.setXY(0, 20/64, 20/64);
      uvAttribute.setXY(1, 28/64, 20/64);
      uvAttribute.setXY(2, 28/64, 32/64);
      uvAttribute.setXY(3, 20/64, 32/64);
      // 背面
      uvAttribute.setXY(4, 32/64, 20/64);
      uvAttribute.setXY(5, 40/64, 20/64);
      uvAttribute.setXY(6, 40/64, 32/64);
      uvAttribute.setXY(7, 32/64, 32/64);
      // 左面
      uvAttribute.setXY(8, 16/64, 20/64);
      uvAttribute.setXY(9, 20/64, 20/64);
      uvAttribute.setXY(10, 20/64, 32/64);
      uvAttribute.setXY(11, 16/64, 32/64);
      // 右面
      uvAttribute.setXY(12, 28/64, 20/64);
      uvAttribute.setXY(13, 32/64, 20/64);
      uvAttribute.setXY(14, 32/64, 32/64);
      uvAttribute.setXY(15, 28/64, 32/64);
      // 顶面
      uvAttribute.setXY(16, 20/64, 16/64);
      uvAttribute.setXY(17, 28/64, 16/64);
      uvAttribute.setXY(18, 28/64, 20/64);
      uvAttribute.setXY(19, 20/64, 20/64);
      // 底面
      uvAttribute.setXY(20, 20/64, 32/64);
      uvAttribute.setXY(21, 28/64, 32/64);
      uvAttribute.setXY(22, 28/64, 36/64);
      uvAttribute.setXY(23, 20/64, 36/64);
      uvAttribute.needsUpdate = true;
    }

    // 右臂UV调整:对应皮肤[44,20]~[48,32]区域
    const rightArmGeo = document.querySelector('mesh:nth-child(3) > boxGeometry');
    if (rightArmGeo) {
      const uvAttribute = rightArmGeo.attributes.uv;
      // 正面
      uvAttribute.setXY(0, 44/64, 20/64);
      uvAttribute.setXY(1, 48/64, 20/64);
      uvAttribute.setXY(2, 48/64, 32/64);
      uvAttribute.setXY(3, 44/64, 32/64);
      // 其他面可参考Minecraft皮肤布局补充,这里省略
      uvAttribute.needsUpdate = true;
    }

    // 左臂、右腿、左腿的UV调整逻辑类似,可参考头部、躯干的方式补充
  }, [skinTexture]);

  return (
    <group position={[0, 1.5, 0]}>
      {/* 头部 */}
      <mesh>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial map={skinTexture} side={2} />
      </mesh>
      {/* 躯干 */}
      <mesh position={[0, -1.375, 0]}>
        <boxGeometry args={[0.5, 1.75, 1]} />
        <meshStandardMaterial map={skinTexture} side={2} />
      </mesh>
      {/* 右臂 */}
      <mesh position={[0, -1.375, 0.75]}>
        <boxGeometry args={[0.5, 1.75, 0.5]} />
        <meshStandardMaterial map={skinTexture} side={2} />
      </mesh>
      {/* 左臂 */}
      <mesh position={[0, -1.375, -0.75]}>
        <boxGeometry args={[0.5, 1.75, 0.5]} />
        <meshStandardMaterial map={skinTexture} side={2} />
      </mesh>
      {/* 右腿 */}
      <mesh position={[0, -3.125, 0.25]}>
        <boxGeometry args={[0.5, 1.75, 0.5]} />
        <meshStandardMaterial map={skinTexture} side={2} />
      </mesh>
      {/* 左腿 */}
      <mesh position={[0, -3.125, -0.25]}>
        <boxGeometry args={[0.5, 1.75, 0.5]} />
        <meshStandardMaterial map={skinTexture} side={2} />
      </mesh>
    </group>
  );
}

export default function SkinViewer() {
  return (
    <Canvas>
      <OrbitControls enableRotate={true} autoRotate={true} />
      <ambientLight intensity={1} />
      <directionalLight color="white" position={[0, 0, 5]} />
      <MinecraftModel />
    </Canvas>
  );
}

关于外层皮肤(第二层纹理)的实现

如果要添加外层(比如披风、皮肤的第二层细节),可以:

  • 给每个身体部位额外添加一层mesh,位置稍微偏移(比如0.01单位)
  • 加载同一张皮肤纹理,调整UV到皮肤的外层区域(比如64x64皮肤的右侧区域,或256x64的下半部分)
  • 给外层材质开启transparent: true,避免遮挡内层

注意事项

  • 皮肤图片需放在Next.js的public目录下,确保路径正确
  • 可参考Minecraft官方皮肤布局图,核对每个部位的UV坐标
  • 设置side={2}(即DoubleSide)确保模型正反两面都显示纹理

内容的提问来源于stack exchange,提问作者zzhuravleff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:06:01