【问题标题】:How to get 3D world space position from Render Texture如何从渲染纹理获取 3D 世界空间位置
【发布时间】:2024-01-17 12:47:01
【问题描述】:

我正在使用Render Texture 制作侧面地图,它工作正常,我已经完成了这些步骤。

  1. 渲染场景的一个主摄像头。
  2. NGUI 相机正在渲染 GUI。
  3. 该 GUI 包含使用 GUI 相机在纹理上渲染场景的渲染纹理(小地图)。

但是现在我想进一步改进地图并让它变得难以处理。我想要渲染纹理点击点的世界空间位置。 有什么方法可以让我从渲染纹理的特定点击点获取世界空间位置?

如果我单击渲染纹理中的某个位置,我如何才能在 3D 世界空间中获得该点。

例如:我在渲染纹理上单击了一个汽车对象。现在,如何在 3D 世界空间中突出显示或获取它。 如何将 2D 渲染纹理点击位置转换为世界空间位置!

【问题讨论】:

  • 指针交互不会发生在单个纹理上,它们是来自相机的光线投射撞击对撞机的结果。如果您使用 Unity5,您的游戏对象将需要一个碰撞器,您可以在相机上使用 EventSystem a PhysicsRaycasterIPointClickHander 接口
  • 我猜 raycast 使用物理并且相机上没有应用物理
  • 光线投射是物理系统的一部分,是的,但它们是从相机投射的。我建议您阅读文档:)。您需要在具有渲染纹理平面的游戏对象上使用对撞机,然后使用光线投射。一个快速的谷歌搜索给出了一些关于 Unity3d 答案的例子,看看 this threadthis
  • 将碰撞器附加到您的渲染纹理游戏对象。向它投射光线并将生命值存储在纹理空间中。接下来找到纹理使用的相机并使用先前存储的命中点,从对应于纹理坐标的视口拍摄第二条光线。任何被第二射线击中的东西都应该是正确的对象。 (未经测试)
  • 怎么样?或者它是如何关闭的。我的意思是它到底在哪里失败了?您是否尝试过使用Debug.DrawRay(start, direction, color, duration) 绘制调试光线(如果不使用可以设置持续时间的重载,光线只会出现在一个太短而无法看到的帧中)?我不在家里的自动取款机,所以我不能自己再试几个小时。

标签: c# unity3d camera


【解决方案1】:

这假设了一些事情。

首先我使用 EventSystems 来获得鼠标点击。这不是必需的,它可以很容易地改变(虽然由你^^)。要正确设置它,您的场景中需要一个EventSystem(如果您有一个 UI,您可能已经有一个)。

其次,您需要将PhysicsRaycaster 组件附加到您的主摄像头(玩家可以看到的摄像头)。

最后,在包含渲染纹理的渲染器的 GameObject 上(我使用了一个简单的四边形),您应用以下脚本并分配相应的相机。

using UnityEngine;
using UnityEngine.EventSystems;

//i chose box collider because its cheap
[RequireComponent(typeof(BoxCollider))]
public class RenderTextureRaycaster : MonoBehaviour, IPointerDownHandler {

    //assign in inspector
    public Camera portalExit;

    BoxCollider portal;
    Vector3 portalExitSize;

    void Start() {
        portal = GetComponent<BoxCollider>();
        //this is the target camera resolution, idk if there is another way to get it.
        portalExitSize = new Vector3(portalExit.targetTexture.width, portalExit.targetTexture.height, 0);
    }

    public void OnPointerDown(PointerEventData eventData) {
        //the click in world space
        Vector3 worldClick = eventData.pointerCurrentRaycast.worldPosition;
        //transformed into local space
        Vector3 localClick = transform.InverseTransformPoint(worldClick);
        //since the origin of the collider is in its center, we need to offset it by half its size to get it realtive to bottom left
        Vector3 textureClick = localClick + portal.size / 2;
        //now we scale it up by the actual texture size which equals to the "camera resoution"
        Vector3 rayOriginInCameraSpace = Vector3.Scale(textureClick, portalExitSize);

        //with this knowledge we can creata a ray.
        Ray portaledRay = portalExit.ScreenPointToRay(rayOriginInCameraSpace );
        RaycastHit raycastHit;

        //and cast it.
        if (Physics.Raycast(portaledRay, out raycastHit)) {
            Debug.DrawLine(portaledRay.origin, raycastHit.point, Color.blue, 4);
        }
        else {
            Debug.DrawRay(portaledRay.origin, portaledRay.direction * 100, Color.red, 4);
        }
    }
}

编辑:上面可能有点冗长,如果你喜欢一个衬里,你可以很容易地减少它,它只是为了展示它是如何工作的。也请仅将其视为概念证明,尚未经过彻底测试

【讨论】:

  • 请提及所有步骤,让我修改一下,我有一个用作小地图的渲染纹理的图像。从这个小地图(2D)我想得到世界空间位置。如果我在某个位置单击图像,那么我会在场景中获得位置。
  • 希望此澄清对您有所帮助!
  • 你这个场景完全要求改变我的等级制度
  • 好吧,对不起,我不知道关于 NGUI 什么的。你为什么不使用统一用户界面,因为 4.6(我认为)它非常棒。但它基本上应该遵循相同的逻辑。一旦你知道你在纹理中相对于它的左下点的位置,你就可以推导出相机屏幕上的位置。渲染纹理的相机与它渲染到的渲染纹理具有相同的分辨率。
  • @MohammadFaizanKhan 检查您的场景中是否有 EventSystem 以及附加到正确游戏对象(具有渲染纹理的四边形)的脚本。你的玩家相机也需要一个 PhysicsRaycaster。这是我用来尝试的设置imgur.com/7kUWGYZ