Поместите текст на игровой объект, НО, как если бы он был нарисован - PullRequest
0 голосов
/ 20 сентября 2019

Я долго искал этот ответ в Интернете.Но, видимо, мои поисковые фразы были неверны или это действительно так сложно.Но я сомневаюсь в этом.

Представь, у меня есть какое-то 3d-тело.Сфера, цилиндр или куб.

Я хочу поместить текст на этот объект.Как будто это была наклейка или нарисовано на этом объекте.Это означает, что он будет следовать за кривыми или краями объектов, если их обернуть вокруг них.Мне удалось создать несколько текстов, которые ВСЕГДА спереди или сзади моего объекта.Но это не то, что я хочу.

Самое близкое к тому, что я хочу, выглядит так:

enter image description here

Я не могу поверить, что мне нужно для этого кодировать.Это должно быть достигнуто через ребенка / родителей, холст и текст, ТОЛЬКО с использованием инспектора и компонентов.

Это так?

1 Ответ

9 голосов
/ 20 сентября 2019

Не все так просто, но вот как вы можете достичь чего-то подобного. Для этого требуется, чтобы ваши 3D-модели были правильно отображены в УФ-свете, чтобы вы могли просто применить к ним плоскую текстуру. Настройка может показаться сложной, но она действительно крутая;)

  1. Создайте RenderTexture: в Asset do щелкните правой кнопкой мыши CreateRenderTexture и назовите его так, как вам нравится, например TextTexture

    enter image description here

    • Чтобы иметь более высокое разрешение, увеличьте его size, например, 2048*2048, исходя из ваших потребностей.

    enter image description here

  2. Создать новый материал

    enter image description here

    • используя только что созданный RenderTexture как Albedo
    • и установите для RenderingMode значение Fade (для того, чтобы позже сделать его фон прозрачным)

    enter image description here

  3. Создайте новый Слой и назовите его, например, TEXT

    enter image description hereenter image description here

  4. У вас обычный основной Camera под Culling Mask исключить тон только что создал TEXT слой.Таким образом, он не будет отображать наш текстовый контент

    enter image description here

  5. Добавить новый Camera к вашей сцене (он будет только отображатьтекст) и назовите его, например, TextCamera

    enter image description here

    и выполните следующие настройки:

    enter image description here

    • Удалите AudioListener компонент
    • Clear FlagsSolid Color
    • Background → Цвет на самом деле не имеет значения, но обязательно установитеальфа-уровень до 0!
    • Culling Mask → Ничего, кроме созданного TEXT слоя
    • Target Texture → Созданного RenderTexture

    Теперь у вас уже есть Материал с динамически изменяемой текстурой с прозрачным фоном и любым другим содержимым, которое вам нравится.Итак, давайте сделаем это, например, UI.Text

  6. Для вашей сцены (я сделал это просто как потомок TextCamera, чтобы я мог просто переместить его из поля зрения в SceneView во время работыдобавьте Text (включая Canvas и т. д. - Unity обычно добавляет его автоматически)

    enter image description here

    • Сделать все GameObjects (Canvas)и текст) имеют слой TEXT, поэтому они не будут отображаться обычным Camera, а только TextCamera.

    enter image description here enter image description here

    • Убедитесь, что Canvas использует RenderMode = WorldSpace (он не будет работать с наложенным холстом)!
    • Поместите Canvas около например 3 единиц перед TextCamera (или где угодно, чтобы текст был виден в текстуре позже)

    enter image description here

    • Чтобы получить лучшее разрешение текста, я бы также включил Text
    • в RectTransform set width = 1000, height = 1000, Scale = 0.001, 0.001, 0.001
    • В компоненте Textустановить Font Size = 300
    • Просто чтобы отключить Raycast Target опция

    enter image description here

И теперь вы можете просто применить созданный материал к своему 3D-объекту и нажать кнопку воспроизведения идолжен видеть, что он становится полностью прозрачным, за исключением текста на нем.

enter image description here

Таким образом, чтобы использовать его в качестве наложения для трехмерного объекта, вы можете, например,просто скопируйте исходный объект, назовите один, например, Inner, другой Outer и сделайте Inner дочерним элементом Outer.Теперь на Outer вы устанавливаете наш текстовый материал.Это работает, поскольку материал, использующий Fade в качестве режима рендеринга, отображается в другой цепочке рендеринга, которая отображается поверх стандартной.

→ Tadaaa 3D-объект с текстом, нанесенным на его поверхность, и может даже динамически изменять текст и его свойства, такие как цвет и т. Д.

enter image description here


Все это динамично (за исключением создания слоев)

Так как вы спросили: Да, вы можете сделать все это в сценарии ... за исключением создания новых слоев!Это невозможно во время выполнения!

Таким образом, вы должны знать все слои, которые вы будете использовать заранее, тогда вы можете сделать что-то вроде

[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class TextOnObjectManager : MonoBehaviour
{
    // reference via Inspector if possible
    [SerializeField] private Camera mainCamera;
    [SerializeField] private string LayerToUse;

    private void Awake()
    {
        // 0. make the clone of this and make it a child
        var innerObject = new GameObject(name + "_original", typeof(MeshRenderer)).AddComponent<MeshFilter>();
        innerObject.transform.SetParent(transform);
        // copy over the mesh
        innerObject.mesh = GetComponent<MeshFilter>().mesh;
        name = name + "_textDecal";

        // 1. Create and configure the RenderTexture
        var renderTexture = new RenderTexture(2048, 2048, 24) { name = name + "_RenderTexture" };

        // 2. Create material
        var textMaterial = new Material(Shader.Find("Standard"));

        // assign the new renderTexture as Albedo
        textMaterial.SetTexture("_MainTex", renderTexture);

        // set RenderMode to Fade
        textMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
        textMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
        textMaterial.SetInt("_ZWrite", 0);
        textMaterial.DisableKeyword("_ALPHATEST_ON");
        textMaterial.EnableKeyword("_ALPHABLEND_ON");
        textMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
        textMaterial.renderQueue = 3000;

        // 3. WE CAN'T CREATE A NEW LAYER AT RUNTIME SO CONFIGURE THEM BEFOREHAND AND USE LayerToUse

        // 4. exclude the Layer in the normal camera
        if (!mainCamera) mainCamera = Camera.main;
        mainCamera.cullingMask &= ~(1 << LayerMask.NameToLayer(LayerToUse));

        // 5. Add new Camera as child of this object
        var camera = new GameObject("TextCamera").AddComponent<Camera>();
        camera.transform.SetParent(transform, false);
        camera.backgroundColor = new Color(0, 0, 0, 0);
        camera.clearFlags = CameraClearFlags.Color;
        camera.cullingMask = 1 << LayerMask.NameToLayer(LayerToUse);

        // make it render to the renderTexture
        camera.targetTexture = renderTexture;
        camera.forceIntoRenderTexture = true;

        // 6. add the UI to your scene as child of the camera
        var Canvas = new GameObject("Canvas", typeof(RectTransform)).AddComponent<Canvas>();
        Canvas.transform.SetParent(camera.transform, false);
        Canvas.gameObject.AddComponent<CanvasScaler>();
        Canvas.renderMode = RenderMode.WorldSpace;
        var canvasRectTransform = Canvas.GetComponent<RectTransform>();
        canvasRectTransform.anchoredPosition3D = new Vector3(0, 0, 3);
        canvasRectTransform.sizeDelta = Vector2.one;

        var text = new GameObject("Text", typeof(RectTransform)).AddComponent<Text>();
        text.transform.SetParent(Canvas.transform, false);
        var textRectTransform = text.GetComponent<RectTransform>();
        textRectTransform.localScale = Vector3.one * 0.001f;
        textRectTransform.sizeDelta = new Vector2(2000, 1000);

        text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
        text.fontStyle = FontStyle.Bold;
        text.alignment = TextAnchor.MiddleCenter;
        text.color = Color.red;
        text.fontSize = 300;
        text.horizontalOverflow = HorizontalWrapMode.Wrap;
        text.verticalOverflow = VerticalWrapMode.Overflow;

        Canvas.gameObject.layer = LayerMask.NameToLayer(LayerToUse);
        text.gameObject.layer = LayerMask.NameToLayer(LayerToUse);

        text.text = "This is a dynamically generated example!";

        // 7. finally assign the material to the child object and hope everything works ;)
        innerObject.GetComponent<MeshRenderer>().material = textMaterial;
    }
}

По существу воспроизводя все шаги, сделанные ранее.Поскольку мы не можем создавать или редактировать слои во время выполнения, вы должны знать их заранее и ввести их как LayerToUse.

Я создал каждую вещь как дочерний элемент исходного объекта, поэтому его легко контролировать и изменятьтакже позже во время выполнения.

enter image description here

...