Нужна помощь в понимании того, как работать с 2D / 3D символами

#c #opengl #graphics #data-visualization

#c #opengl #графика #данные-визуализация

Вопрос:

Вот фрагмент кода, который я хотел бы, чтобы мне помогли понять

 for (i = 0; i < samplesX; i  )
        for (j = 0; j < samplesY; j  )
        {
            newI = DIM * i / samplesX;
            newJ = DIM * j / samplesY;
            idx = (round(newJ) * DIM)   round(newI);
            if (color_dir == 1 amp;amp; draw_vecs == 1) {
                direction_to_color(vx[idx], vy[idx], color_dir);
            }
            if (color_dir == 1 amp;amp; draw_vecs == 2) {
                direction_to_color(fx[idx], fy[idx], color_dir);
            }
            else if (color_dir == 2) {
                scalar = rho[idx];
                set_colormap(scalar, min, max, clampLow, clampHigh);
            }
            else if (color_dir == 3) {
                scalar = sqrt(vx[idx] * vx[idx]   vy[idx] * vy[idx]);
                set_colormap(scalar, min, max, clampLow, clampHigh);
            }
            else if (color_dir == 4) {
                scalar = sqrt(fx[idx] * fx[idx]   fy[idx] * fy[idx]);
                set_colormap(scalar, min, max, clampLow, clampHigh);
            }
            /*if (draw_vecs == 1) {
                glVertex2f(wn   (fftw_real)newI * wn, hn   (fftw_real)newJ * hn);
                glVertex2f((wn   (fftw_real)newI * wn)   vec_scale * vx[idx], (hn   (fftw_real)newJ * hn)   vec_scale * vy[idx]);
            }
            else if (draw_vecs == 2) {
                glVertex2f(wn   (fftw_real)newI * wn, hn   (fftw_real)newJ * hn);
                glVertex2f((wn   (fftw_real)newI * wn)   vec_scale * fx[idx], (hn   (fftw_real)newJ * hn)   vec_scale * fy[idx]);
            }*/
            if (draw_vecs == 1) {
                glVertex2f(wn   (fftw_real)i * wn, hn   (fftw_real)j * hn);
                glVertex2f((wn   (fftw_real)i * wn)   vec_scale * vx[idx], (hn   (fftw_real)j * hn)   vec_scale * vy[idx]);
            }
            else if (draw_vecs == 2) {
                glVertex2f(wn   (fftw_real)i * wn, hn   (fftw_real)j * hn);
                glVertex2f((wn   (fftw_real)i * wn)   vec_scale * fx[idx], (hn   (fftw_real)j * hn)   vec_scale * fy[idx]);
            }
        }
    glEnd();
}
  

Насколько я понимаю, в настоящее время это позволяет отображать эти двумерные линии / стрелки (ежики), которые визуализируют силу / скорость в 2D, как видно на рисунке ниже.

-

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

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

  • трехмерные конусы
  • трехмерные эллипсоиды

Если я чего-то здесь не понимаю, пожалуйста, дайте мне знать!

Некоторые из переменных, включенных в приведенный выше фрагмент:

 const int DIM = 50;             //size of simulation grid
int   color_dir = 0;            //use direction color-coding or not
float scalar;
int newI, newJ;
float temp;
float vec_scale = 1000;         //scaling of hedgehogs
int   draw_vecs = 1;            //draw the vector field or not
  

Ответ №1:

Фрагмент кода, который у вас там есть, мог бы быть написан проще (также требуется некоторое понимание того, что означают некоторые переменные и функции).

Давайте разберем это.

Первые две строки просты для понимания, это стандартная строка для перебора 2D-массива

 for (i = 0; i < samplesX; i  )
        for (j = 0; j < samplesY; j  )
  

i и j запускают индексы, которые будут повторяться по каждому кортежу дискретных координат в (i,j) ∈ [i, samplesX) × [j, samplesY) . Следующие две строки переназначают 2D-индексы в новый диапазон значений, в частности [i, samplesX)×[j, samplesY) → [0, DIM)×[0, DIM) . Недостающая часть информации заключается в том, какой тип является DIM of. Это означало бы, что это должен быть какой-то тип с плавающей запятой.

      newI = DIM * i / samplesX;
     newJ = DIM * j / samplesY;
  

Следующая строка подвержена ошибкам. Он преобразует newI и newJ в работающий одномерный индекс для одномерного массива, который адресуется i и j .

Почему это проблематично? Потому что при преобразовании в формат DIM информация о пробелах могла быть потеряна. Такого рода потеря информации может привести к ошибкам безопасности (!), На самом деле, Skia, библиотека рендеринга, используемая Google Chrome, Android и другими проектами, недавно имела именно такую ошибку; статью стоит прочитать:https://googleprojectzero.blogspot.com/2019/02/the-curious-case-of-convexity-confusion.html

Правильный способ реализовать это — сделать DIM целым числом и выполнить над ним арифметику с фиксированной запятой, в конечном итоге усекая дробные цифры. Но я отвлекся. Следующий блок, по сути, выполняет поиск по таблице поиска для бедных людей. vx``vy и fx``fy представляют собой несколько сглаженных 2D-массивов, доступ к которым осуществляется через одномерный индекс, и direction_to_color сопоставляются либо со значением, предположительно, либо с вызовом glColor ; то же самое, вероятно, относится и к set_colormap . Это неправильное использование OpenGL.

Все переназначение из i и j в DIM , а затем поисковые запросы — это просто плохая реализация поиска текстуры. В OpenGL уже есть текстуры. Просто загрузите как массив координат текстуры и включите текстурирование.

Наконец, для каждого корешка выполняется два вызова glVertex , один с точкой наблюдения, которая находится в центрах сетки (wn, hn) , в смещенном местоположении (wn, hn) (i, j) .

Мой вердикт по этому коду: Полный мусор!Все это можно было бы сделать гораздо элегантнее, даже в 1994 году с OpenGL-1.0, для которого, похоже, был написан код. Если вы хотите реализовать свой собственный график векторного поля, не используйте это в качестве отправной точки.

В наши дни у нас есть программируемые графические процессоры с шейдерами. Все, что там можно сделать, — это несколько строк кода шейдера.

Комментарии:

1. Спасибо за ввод и объяснение. Этот код был предоставлен с намерением расширить его, поэтому, особенно в моем случае, я изо всех сил стараюсь ссылаться на исходный код при попытке реализовать новые функции. Теперь я предполагаю, что для повторения 3D-варианта вышеупомянутого фрагмента кода нам придется перебирать 3D-массив? Или есть более простое / правильное решение для реализации 3D-глифов?

2. @K.Saudi: Это назначение класса? Если это так, пожалуйтесь в свою школу, что вас учат сильно устаревшим знаниям, которые сегодня неактуальны за пределами поддержки устаревших кодовых баз. В любом случае, если они запрашивают 3D-корешки, хотят ли они, чтобы это было в 3D-сетке, или корешки должны быть направлены в 3D, но все еще находиться на 2D-сетке. Технически это не имеет большого значения, потому что 2D равно 3D с экстентом 3-го измерения, равным 1. Что касается того, как это сделать: для каждого …x и …y вы бы ввели другой …z и добавили и третий индекс. После i и j идет k , так что это было бы подходящее имя.