Юнити с использованием LineRenderer для рисования линий на сфере

Вопрос или проблема

Я пытаюсь провести линии на глобусе, подобно научному программному обеспечению GPlates. В GPlates вы рисуете многоугольники на глобусе, щелкая по глобусу, чтобы разместить вершины.

Я обратился за помощью к ChatGPT, он предоставил первые два метода. Я написал последний сам. Поскольку я уже работал с UV-координатами текстуры, я попытался использовать их. Это действительно попытка на удачу. Я не знаю, какие векторы мне следует с интерполировать, чтобы заставить LinerRenderer нарисовать нужные мне линии.

using UnityEngine;

using System.Collections.Generic;

public class MousePosition : MonoBehaviour
{

    public LineRenderer lineRenderer;
    public float radius = 1.0f; // Радиус вашей сферы
    //public Vector2[] geoCoordinates; // Массив точек (широта, долгота)
    public int numSegments = 100; // Количество сегментов для гладкости

    private Vector3 prevHit;

    void DrawCurvedLineOnSphere(Vector3 startPoint, Vector3 endPoint)
    {
        List<Vector3> linePoints = new List<Vector3>();

        // Интерполяция вдоль большой окружности с использованием Slerp
        for (int j = 0; j <= numSegments; j++)
        {
            float t = (float)j / (float)numSegments;
            Vector3 interpolatedPoint = Vector3.Slerp(startPoint, endPoint, t);
            linePoints.Add(interpolatedPoint);
        }

        // Задайте точки LineRenderer
        lineRenderer.positionCount = linePoints.Count;
        lineRenderer.SetPositions(linePoints.ToArray());
    }

    Vector3 ConvertGeoToCartesian(float latitude, float longitude, float radius)
    {
        // Преобразование градусов в радианы
        float latRad = Mathf.Deg2Rad * latitude;
        float lonRad = Mathf.Deg2Rad * longitude;

        // Вычисление декартовых координат
        float x = radius * Mathf.Cos(latRad) * Mathf.Cos(lonRad);
        float y = radius * Mathf.Sin(latRad);
        float z = radius * Mathf.Cos(latRad) * Mathf.Sin(lonRad);

        return new Vector3(x, y, z);
    }

    void Update() {
        if (Input.GetMouseButton(0)) {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit)) {
                Renderer renderer = hit.collider.GetComponent<Renderer>();
                Material mat = renderer.material;
                Texture2D texture2D = mat.mainTexture as Texture2D;

                // Преобразование hit текстурных координат в пиксельные координаты
                Vector2 pixelUV = hit.textureCoord;
                pixelUV.x *= texture2D.width;
                pixelUV.y *= texture2D.height;
                Debug.Log("x: " + pixelUV.x + " y: " + pixelUV.y);

                // Проверьте, доступна ли текстура для чтения, прежде чем изменять пиксели
                if (texture2D.isReadable) {
                    // Установите цвет пикселя
                    texture2D.SetPixel((int)pixelUV.x, (int)pixelUV.y, Color.red);

                    // Примените изменения пикселей
                    texture2D.Apply();

                    // Лог успеха
                    Debug.Log("Цвет пикселя успешно установлен!");
                } else {
                    Debug.LogError("Текстура недоступна для чтения. Измените параметры импорта.");
                }

                if (prevHit != null) {
                    DrawCurvedLineOnSphere(prevHit, ConvertGeoToCartesian(pixelUV.x, pixelUV.y, radius));
                }
                prevHit = ConvertGeoToCartesian(pixelUV.x, pixelUV.y, radius);
            }
        }
    }
}

Я разместил сферу, которую сделал в Blender, в начале мира. Масштаб составляет 100 для x, y и z. Я думаю, что радиус 1.0 подходит для этого?

В настоящее время это создает одну линию, которая уходит в бесконечность, пересекающую сферу, и она рисует линию только при нажатой кнопке мыши и перетаскивании. Это происходит при использовании Alignment: Transform Z в моем LineRenderer. Моя цель — нарисовать линию всего лишь за 2 клика.

Chat GPT хочет использовать UV-координаты и преобразовать их в трехмерные декартовы координаты. Это имеет смысл, поскольку в конечном итоге я, вероятно, захочу хранить информацию в координатах долготы/широты, а не в мировом пространстве. В целом, я довольно запутан в том, какие координаты мне нужны.

Мой луч также, похоже, попадает на противоположную сторону сферы, я не уверен, что вызывает это. Я успешно установил цвет пикселя, но с другой стороны сферы.

Ближе всего к ответу на мой вопрос я нашел это Как нарисовать линии широты/долготы на поверхности сферы в Unity 3D?, но там маловато кода, и это не совсем то, что я хочу сделать.

Ответ или решение

Рисование линий на сфере в Unity с использованием LineRenderer

Вопрос о рисовании линий на сфере в Unity, что вы описали, действительно представляется интересной задачей. Рассмотрим ключевые моменты реализации с использованием компонента LineRenderer, а также исправления и оптимизации вашего кода.

1. Основные Концепции

Ваша цель заключается в том, чтобы рисовать линии по сфере, имитируя функциональность приложений, таких как GPlates. Основой для этого является идея использования координат длины и широты, а также конверсии их в 3D-пространство (координаты Cartesian).

2. Применение LineRenderer

Для начала вам необходимо убедиться, что у вашего объекта LineRenderer настроены правильные параметры. Убедитесь, что Alignment установлен на Transform Z. Это позволит правильно расположить линии, которые вы рисуете.

3. Корректное обновление координат и интерполяция

Ваш метод DrawCurvedLineOnSphere использует интерполяцию по сферической линейной интерполяции (Slerp). Убедитесь, что передаете правильные точки на сферу. Ваша текущая реализация использования Slerp может быть некорректной, так как она просто интерполирует между двумя векторами, не учитывая, что ваше представление на сфере должно соединять эти точки по кратчайшему пути.

Используйте следующий код, чтобы правильно интерполировать:

void DrawCurvedLineOnSphere(Vector3 startPoint, Vector3 endPoint)
{
    List<Vector3> linePoints = new List<Vector3>();

    // Нормализуйте векторы, чтобы они находились на поверхности сферы
    startPoint.Normalize();
    endPoint.Normalize();

    // Интерполяция вдоль большой окружности
    for (int j = 0; j <= numSegments; j++)
    {
        float t = (float)j / (float)numSegments;
        Vector3 interpolatedPoint = Vector3.Slerp(startPoint, endPoint, t);
        linePoints.Add(interpolatedPoint * radius); // Умножаем на радиус
    }

    // Установка точек в LineRenderer
    lineRenderer.positionCount = linePoints.Count;
    lineRenderer.SetPositions(linePoints.ToArray());
}

4. Корректная обработка координат

Обратите внимание на метод ConvertGeoToCartesian. Ваша реализация правильная, но, вместо использования pixelUV.x и pixelUV.y, вам следует использовать соответствующие географические координаты, которые вы хотите конвертировать.

Также, в вашем методе Update, убедитесь, что prevHit инициализируется или проверяется перед использованием:

if (prevHit != Vector3.zero) // Убедитесь, что это не начальное значение.
{
    DrawCurvedLineOnSphere(prevHit, ConvertGeoToCartesian(latitude, longitude, radius));
}

5. Исправление проблемы с Raycast

Если ваши лучи пересекают объект на противоположной стороне сферы, это может быть связано с настройками коллайдера или настройками камеры. Убедитесь, что у вас настроена правильная камера и что коллайдер на сфере соответствует ожидаемым размерам.

6. Оптимизация и Вывод

Ваш код, использующий LineRenderer для рисования линий на сфере, имеет хороший старт, но требует некоторых доработок, чтобы достичь нужного результата. Обратите внимание на правильную интерполяцию точек на поверхности сферы и точное использование координат для Slerp. Также не забудьте протестировать ваши изменения, чтобы убедиться, что индикаторы точек находятся в нужных местах.

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

Оцените материал
Добавить комментарий

Капча загружается...