Put a text onto a game-object BUT as if it was painted

前端 未结 3 1681
耶瑟儿~
耶瑟儿~ 2020-12-30 16:53

I was looking for this answer on the web for some time. But apparently my search phrases were wrong or it is really that hard. But I doubt it.

Imagine, I have any 3

相关标签:
3条回答
  • 2020-12-30 17:42

    You can do that with a UI Text game object and the 3D Object.

    The steps are as follows:

    • In your empty scene, create a 3D Cube.

    • Create a UI Text object.

    • Drag the Canvas to become a child of the cube.

    • Set the Canvas to World Space render mode, remove the Canvas Scaler component and set Width = Height = 1 and all Pos = 0.

    • Set the text Width = Height = 100, all Scale = 0.01 and Pos Z = -0.5.

    0 讨论(0)
  • 2020-12-30 17:52

    I made a few changes that help.

    1. Your code: name = name + "_textDecal"; is probably meant to be innerObject.name = name + "_textDecal";
    2. in this same area, you need to add innerObject.transform.localPosition = Vector3.zero; and innerObject.transform.localRotaton = Quaternion.identity; otherwise the text doesn't seem to follow the object if it is moved or rotated

    But the real problem I find is this (on Unity 2020.1.f1): the texture of the original mesh is only properly shown within the Unity editor -- standalone builds show a black object (or whatever the camera background color is).

    Is the issue one I see explained somewhat vaguely in RenderTexture docs (that I cannot immediately locate again) that hinted that I needed to include a Texture (Material?) in my Resources folder that has similar shaders to the ones used in my RenderTexture?

    0 讨论(0)
  • 2020-12-30 17:55

    Not that simple but here is how you can achieve something similar. This requires your 3D models to be correctly UV mapped so you can simply apply a flat texture to it. The setup may sound complex but it is really cool ;)

    1. Create a RenderTexture: In the Asset do right mouse clickCreateRenderTexture and call it however you like e.g. TextTexture

      • To have a better resolution later increase it's size to e.g. 2048*2048 depedning on your needs of course.

    2. Create a new Material

      • using the just created RenderTexture as Albedo
      • and set the RenderingMode to Fade (in order to later have it's background transparent)

    3. Create a new Layer and call it e.g. TEXT

    4. In you normal main Camera under Culling Mask exclude the just created TEXT layer. So it will not render our Text content

    5. Add a new Camera to your scene (it will only render the text) and call it e.g. TextCamera

      and make the following settings:

      • Remove its AudioListener component
      • Clear FlagsSolid Color
      • Background → Color actually doesn't matter but make sure to set the Alpha level to 0!
      • Culling Mask → Nothing except the created TEXT layer
      • Target Texture → The created RenderTexture

      Now you already have a Material with a dynamically changeable Texture with transparent background and whatever content you like. So lets make it e.g. a UI.Text

    6. To your scene (I did it simply as child of the TextCamera so I can simply move it out of sight in the SceneView while working on other stuff) add a Text (including the Canvas etc - Unity usually adds it automatically)

      • Make all GameObjects (Canvas and Text) have the Layer TEXT so they will not be rendered by the normal Camera but only by the TextCamera.

      • Make sure the Canvas uses RenderMode = WorldSpace (it won't work with overlay canvas)!
      • Place the Canvas about e.g. 3 units in front of the TextCamera (or wherever you like so the Text is visible in the texture later)

      • To have better Text resolution I would also on the Text
      • on the RectTransform set width = 1000, height = 1000, Scale = 0.001, 0.001, 0.001
      • In the Text component set Font Size = 300
      • Just to be sure disable the Raycast Target option

    And now you can simply apply the created material to your 3D Object and hit play and should see that it gets completely transparent except having the text on it.

    So in order to use it as overlay for a 3D object you could e.g. simply duplicate the original object, call one e.g. Inner the other one Outer and make Inner a child of Outer. Now on the Outer you set our Text material. This works since a material using Fade as render mode is rendered on a different render chain which is rendered on top of the default one.

    → Tadaaa 3D object with Text applied to its surface and can even dynamically change the text and its properties like color etc


    The whole thing dynamic (Except creating Layers)

    Since you asked: Yes you can make this all in a script ... except creating new Layers! This is not possible on runtime!

    So you have to know all the Layers you will use beforehand then you can do something like

    [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;
        }
    }
    

    Basically reproducing all the steps from before. Since we can't create or edit Layers on runtime you have to know them beforehand and enter it as LayerToUse.

    I created every thing as child of the original object so it is easy to control and change it also later on runtime.

    0 讨论(0)
提交回复
热议问题