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

如何通过控制点实现Unity中3D吉他模型的位姿与缩放匹配

Unity 3D吉他模型匹配标记点的实现方案

一、数据接收与解析

  • 先统一Python发送的标记点数据格式,推荐用JSON传递指板矩形的四个角点屏幕坐标,或者包含中心位置、宽高、旋转角度的结构化数据。
  • 在Unity中创建C#脚本,挂载到吉他模型或空物体上,用你之前通信的方式(UDP/TCP/UnityWebRequest)接收数据。以UDP为例:
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class MarkerReceiver : MonoBehaviour
{
    UdpClient udpClient;
    IPEndPoint remoteEndPoint;

    void Start()
    {
        udpClient = new UdpClient(8888); // 对应Python的发送端口
        remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
    }

    void Update()
    {
        if (udpClient.Available > 0)
        {
            byte[] data = udpClient.Receive(ref remoteEndPoint);
            string json = Encoding.UTF8.GetString(data);
            MarkerData markerData = JsonUtility.FromJson<MarkerData>(json);
            AdjustGuitarTransform(markerData);
        }
    }

    [System.Serializable]
    public class MarkerData
    {
        public Vector2[] cornerPoints; // 指板四个角的屏幕像素坐标
    }
}

二、屏幕坐标转世界坐标

Python提取的是视频帧像素坐标,需要转换成Unity世界坐标:

  • 先转成视口坐标(范围0-1),再转世界坐标:
Vector3 ScreenToWorld(Vector2 screenPos, Camera mainCam, float depth)
{
    // depth是吉他在相机前的固定距离,可根据场景调整
    Vector3 viewportPos = mainCam.ScreenToViewportPoint(new Vector3(screenPos.x, screenPos.y, depth));
    return mainCam.ViewportToWorldPoint(viewportPos);
}

三、模型变换参数调整

1. 缩放适配

通过四个角点计算屏幕上的指板宽度,再转成世界空间距离,和模型原始尺寸做比例:

float GetScaleFactor(Vector2[] corners, Camera mainCam, float originalWidth)
{
    // 取指板长边的屏幕距离
    float screenWidth = Mathf.Max(
        Vector2.Distance(corners[0], corners[1]),
        Vector2.Distance(corners[1], corners[2])
    );
    // 转世界空间距离
    Vector3 worldPointA = ScreenToWorld(corners[0], mainCam, 5f);
    Vector3 worldPointB = ScreenToWorld(corners[1], mainCam, 5f);
    float worldWidth = Vector3.Distance(worldPointA, worldPointB);
    // 计算缩放比例
    return worldWidth / originalWidth;
}

设置模型缩放:transform.localScale = new Vector3(scale, scale, scale)(保持等比例)

2. 旋转对齐

根据指板的方向向量计算旋转角度:

Quaternion GetRotation(Vector2[] corners, Camera mainCam)
{
    // 计算指板长度方向的向量
    Vector2 screenDir = corners[1] - corners[0];
    Vector3 worldPointA = ScreenToWorld(corners[0], mainCam, 5f);
    Vector3 worldPointB = ScreenToWorld(corners[1], mainCam, 5f);
    Vector3 worldDir = (worldPointB - worldPointA).normalized;
    // 让吉他模型的长度方向对齐该向量(根据模型自身轴向调整)
    return Quaternion.FromToRotation(Vector3.right, worldDir);
}

3. 位置定位

取四个角点的中心作为模型位置:

Vector3 GetCenterPosition(Vector2[] corners, Camera mainCam)
{
    Vector2 screenCenter = (corners[0] + corners[1] + corners[2] + corners[3]) / 4f;
    return ScreenToWorld(screenCenter, mainCam, 5f);
}

设置模型位置:transform.position = centerPos

四、平滑过渡优化

直接硬切换参数会卡顿,用插值实现平滑过渡:

void AdjustGuitarTransform(MarkerData data)
{
    Camera mainCam = Camera.main;
    Vector3 targetPos = GetCenterPosition(data.cornerPoints, mainCam);
    float targetScale = GetScaleFactor(data.cornerPoints, mainCam, 1f); // 1f是模型原始宽度
    Quaternion targetRot = GetRotation(data.cornerPoints, mainCam);

    // 平滑过渡速度可调整
    transform.position = Vector3.Lerp(transform.position, targetPos, Time.deltaTime * 5f);
    transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(targetScale, targetScale, targetScale), Time.deltaTime * 5f);
    transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, Time.deltaTime * 5f);
}

五、调试技巧

  • 绘制Gizmos查看标记点位置:
MarkerData lastMarkerData;
void OnDrawGizmos()
{
    if (lastMarkerData != null)
    {
        Camera mainCam = Camera.main;
        foreach (var pos in lastMarkerData.cornerPoints)
        {
            Vector3 worldPos = ScreenToWorld(pos, mainCam, 5f);
            Gizmos.color = Color.red;
            Gizmos.DrawSphere(worldPos, 0.1f);
        }
    }
}
  • 先打印解析后的标记点数据,确认Unity接收的数值和Python发送的一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:50:37