RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 809830
Accepted
Dmitrii
Dmitrii
Asked:2020-04-05 14:52:38 +0000 UTC2020-04-05 14:52:38 +0000 UTC 2020-04-05 14:52:38 +0000 UTC

为 1 个 Unity 对象拍照

  • 772

我想以 PNG 格式获得地形的准确图像,包括所有的树木和草。

Application.CaptureScreenshot

它不起作用,因为您首先需要将相机对准,对齐以获得方形图像,查看所有角落以使其不会出现在任何地方......通常,当您不止一次这样做时,我想自动化它。

我怎样才能做到这一点?例如,仅拍摄整个地形或平面。

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    selya
    2020-04-06T18:29:42Z2020-04-06T18:29:42Z

    一般来说,如果我理解正确,那么你需要一个神奇的按钮“在场景中截取所有地形”。

    好吧,让我们想想:

    • 我们将无法按照应有的方式设置透视相机,因为地形在不同的点可能有不同的高度,它不会变得很酷。所以我们使用正交相机。

    • 让事情变得更好,UnityEditor而不用抽搐UnityEngine。

    • 最好先渲染RenderTexture,然后保存到常规纹理和磁盘。

    • 然后不要忘记删除您身后的所有内容。

    考虑到所有先前的注释,我铆接了以下代码(代码被大量注释):

    using UnityEditor;
    using UnityEngine;
    using UnityEditor.SceneManagement;
    using System.IO;
    
    public class MenuTest : MonoBehaviour {    
    
        // Добавляем пункт меню
        [MenuItem("MyMenu/Capture Terrain ScreenShots")]
        static void CaptureTerrainScreenShots()
        {
            int textureHeight = 1024;
    
            // Получаем количество сцен
            int sceneCount = UnityEngine.SceneManagement.SceneManager.sceneCountInBuildSettings;
            for (int i = 0; i < sceneCount; i++)
            {
                // Получаем путь к сцене
                string scenePath = UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(i);
                // И ее имя
                string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath);
    
                // Открываем сцену
                EditorSceneManager.OpenScene(scenePath);
    
                // Получаем объект типа Terrain в сцене
                Terrain terrain = FindObjectOfType<Terrain>();
    
                if (terrain == null) continue;
    
                // Берем его размер
                Vector3 size = terrain.terrainData.size;
    
                // Создаем камеру, с которой будем снимать
                Camera camera = new GameObject("ScreenShotCamera", typeof(Camera)).GetComponent<Camera>();
    
                // Позиционируем ее над террейном
                camera.transform.position = terrain.transform.position + new Vector3(size.x / 2, size.y, size.z / 2);
                camera.transform.LookAt(new Vector3(size.x / 2, 0, size.z / 2));
                camera.orthographic = true;
                camera.orthographicSize = size.z / 2;
    
                // Создаем текстуру, в которую будем рендерить
                // (Предварительно сохранив текущую)
                RenderTexture currentRT = RenderTexture.active;
                // Создаем текстуру, пропорциональную размеру террейна
                RenderTexture rt = new RenderTexture(textureHeight, (int)(textureHeight * size.z / size.x), 24);
                // Или вот так - 1 юнит = 1 пиксель
                // RenderTexture rt = new RenderTexture(text(int)size.x, (int)size.z, 24);
    
    
                // Устанавливаем созданную текстуру как целевую
                RenderTexture.active = rt;
                camera.targetTexture = rt;
    
                // Принудительно вызываем рендер камеры
                camera.GetComponent<Camera>().Render();
    
                // Получаем обычную текстуру из RenderTexture'ы
                // Ее можно будет использовать в игре, или же
                // Сохранить, что мы и сделаем
                Texture2D image = new Texture2D(rt.width, rt.height);
                image.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
                image.Apply();
    
                // Пишем текстуру в .png файл
                byte[] bytes = image.EncodeToPNG();
                File.WriteAllBytes(Application.dataPath + "/TerrainScreenshots/" + sceneName + ".png", bytes);
    
                // Восстанавливаем рендер таргет
                RenderTexture.active = currentRT;
    
                // Чистим все (при необходимости)
                DestroyImmediate(image);
                DestroyImmediate(camera.gameObject);
                DestroyImmediate(rt);
            }
        }
    }
    

    当您将此代码保存到您的项目时,您将在顶部有一个菜单项MyMenu/Capture Terrain ScreenShots:

    在此处输入图像描述

    单击哪个后,您添加到构建设置 ( File/BuildSettings) 的所有场景将循环运行,并且出现在它们上的第一个地形将被相机捕获。

    该代码绝对有效,我在我的机器上对其进行了测试。不要忘记指定保存截图所需的路径(我提醒你,该路径上的所有文件夹都必须创建,否则会出现类似“部分路径不存在等等等等”的错误)。

    顺便说一句,代码中有一个不太明显的时刻。当我设置相机时,我设置了一些size:

    camera.orthographicSize = size.z / 2;
    

    这等于景观的深度。你问宽度在哪里?并且宽度是和你设置的贴图宽度成比例的camera.targetTexture,所以我特意只通过 来设置贴图大小textureHeight,而宽度已经算是比例了。

    是的。如果您需要截取未添加到构建设置中的场景的屏幕截图,那么您将不得不想出另一种获取场景的方法。例如,如果它们在同一个文件夹Resources/ЭтаСамаяПапка中,那么您可以使用Resources.LoadAll. 然而,这是一个完全不同的故事(我懒得写对文件系统的迭代,抱歉)。

    像这样的东西。我希望打勾)

    • 3
  2. vmchar
    2020-04-05T16:36:23Z2020-04-05T16:36:23Z

    为了对单个对象或一组对象“拍照”,您可以使用Render Texture,然后借助代码中的简单操作,您可以将生成的 Render Texute 保存为纹理并保存为一个PNG文件。

    例如,类似的技术被用于在游戏中创建迷你地图。说明创建小地图的过程的教程。

    关于自动化,在这里您必须自己完成,但由于您将通过单独的相机进行渲染,因此您可以根据需要对其进行自定义。这是使用渲染纹理的另一个示例。

    • 2
  3. nipercop
    2020-04-06T11:00:58Z2020-04-06T11:00:58Z

    你好!

    2)根据地形大小准确调整相机参数

    有一次我做了类似的事情,但仅限于GameObject's. 我在某个地方找到了一个例子,我记不太清了,但代码仍然存在。

    float CalculateOptimalDistanceToObject(Bounds bounds) {
        float maxSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
        float dist = 2 * maxSize / (2 * Mathf.Tan(.5f * _camera.fieldOfView * Mathf.Deg2Rad));
        return dist;
    }
    

    首先,您需要计算Bounds对象的边界,并使用它们来调整相机。以地形为代价,我不确定它是如何工作的,但原则上它们也是非洲的物体)

    用法:

    float dist = CalculateOptimalDistanceToObject(_terrain.terrainData.bounds);
    _camera.transform.position = _terrain.transform.position + Vector3.back * dist; 
    

    以防万一,如果您想为某些游戏对象自定义相机?这是计算对象边界的代码。

    Bounds CalculateBounds(GameObject go) {
        Bounds bounds = new Bounds(go.transform.position, Vector3.zero);
        Transform[] childs = go.GetComponentsInChildren<Transform>();
        for(int i = 0; i < childs.Length; i++) {
            //childs[i].gameObject.layer = _LayerPreview;
            Renderer r = childs[i].GetComponent<Renderer>();
            if(r != null) {
                bounds.Encapsulate(r.bounds);
            }
        }
        return bounds;
    }
    

    代码并不完美,您可能需要根据需要调整一些东西,但主要的是它可以满足您的需求。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5