WedX - журнал о программировании и компьютерных науках

Как визуализировать ленту, проецируемую из 3D-линии, всегда обращенную вперед

В настоящее время у нас есть (изогнутая) линия, состоящая из точек в трехмерном пространстве (в open Inventor, но решение этой проблемы, скорее всего, не зависит от конкретного продукта).

Мы хотим нарисовать поверхность, описывающую ленту, спроецированную ортогонально от этой линии, и которая всегда обращена к камере при ее вращении.

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

У нас есть:

  • Положение и поворот камеры

  • Положение каждой точки на линии

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

Правилен ли этот подход, и если да, то что не так с приведенным ниже кодом, который должен его описывать?

Если нет, то как нам достичь основной цели (лента всегда обращена к пользователю)?

SbVec3f getOuterPoint(SbVec3f p3, const float ribbon_width, float cosine, float sine)
{
    return SbVec3f(p3[0] + ribbon_width*cosine, p3[1] - ribbon_width*sine, p3[2]);
}

SbVec3f getOuterPoint(SbVec3f old_p3, SbVec3f other_point, const float ribbon_width)
{
    float oangle = atan2(old_p3[1] - other_point[1], old_p3[0] - other_point[0]);
    float ocosine = cosf(oangle);
    float osine = sinf(oangle);
    return getOuterPoint(old_p3, ribbon_width, ocosine, osine);
}

SbVec3f getOuterPoint(SbVec3f p0, SbVec3f p1, const float ribbon_width, SoCamera* camera)
{
    SbVec3f axis;
    float angle;
    SoSFRotation camera_rotation = camera->orientation;
    camera_rotation.getValue(axis, angle);
    //std::cout << axis[0] << " " << axis[1] << " " << axis[2] << ":" << angle << std::endl;

    const SbVec3f &camera_position = camera->position.getValue();

    SbVec3f camera_axis = axis;

    SbVec3f well_axis = p1 - p0;

    well_axis.normalize();
    camera_axis.normalize();

    float cam_constant[3] = { -p1[0], -p1[1], -p1[2] };
    float well_constant[3] = { -p1[0], -p1[1], -p1[2] };

    /*

    //float p1_constant = camera_axis[0] * p1[0] + camera_axis[1] * p1[1] + camera_axis[2] * p1[2]
    //  - (camera_axis[0] * camera_position[0] + camera_axis[1] * camera_position[1] + camera_axis[2] * camera_position[2]);

    //// X, Y, Z are unknown
    //float line_unknown = camera_axis[0] * X + camera_axis[1] * Y + camera_axis[2] * Z;

    // 
    //
    // camera_axis.x * (x - p1[0]) + camera_axis.y * (y - p1[1]) + camera_axis.z * (z - p1[2]) = 0      (1)
    // well_axis.x   * (x - p1[0]) + well_axis.y   * (y - p1[1]) + well_axis.z   * (z - p1[2]) = 0      (2)

    // let z become free variable t

    // camera_axis.x * (x - p1[0]) + camera_axis.y * (y - p1[1]) = - camera_axis.z * (t - p1[2]) 
    // well_axis.x   * (x - p1[0]) + well_axis.y   * (y - p1[1]) = - well_axis.z   * (t - p1[2]) 

    // camera_axis.x * (x - p1[0]) + camera_axis.y * (y - p1[1]) = - camera_axis.z * t - camera_axis.z * p1[2] 
    // well_axis.x * (x - p1[0]) + well_axis.y * (y - p1[1]) = - well_axis.z * t - well_axis.z * p1[2] 

    // camera_axis.x * x  + camera_axis.y * y  = - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1] (3)
    // well_axis.x * x  + well_axis.y * y  = - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1]               (4)

    (3) * well_axis.x:

    well_axis.x * camera_axis.x * x  + well_axis.x * camera_axis.y * y  = well_axis.x * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1])

    (4) * camera_axis.x
    camera_axis.x * well_axis.x * x  + camera_axis.x * well_axis.y * y  = camera_axis.x * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1])

    Subtracting
    well_axis.x * camera_axis.y * y - camera_axis.x * well_axis.y * y  = well_axis.x * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1]) - camera_axis.x * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1])

    (well_axis.x * camera_axis.y - camera_axis.x * well_axis.y) * y = well_axis.x * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1]) - camera_axis.x * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1])
    y = well_axis.x * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1]) - camera_axis.x * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1]) / (well_axis.x * camera_axis.y - camera_axis.x * well_axis.y)


    (3) * well_axis.y
    well_axis.y * camera_axis.x * x  + well_axis.y * camera_axis.y * y  = well_axis.y * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1])
    (4) * camera_axis.y
    camera_axis.y * well_axis.x * x  + camera_axis.y * well_axis.y * y  = camera_axis.y * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1])

    Subtracting
    x = well_axis.y * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1]) - camera_axis.y * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1]) / well_axis.y * camera_axis.x  - camera_axis.y * well_axis.x


    So:
    x = well_axis.y * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1]) - camera_axis.y * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1]) / (well_axis.y * camera_axis.x - camera_axis.y * well_axis.x)
    y = well_axis.x * ( - camera_axis.z * t - camera_axis.z * p1[2] + camera_axis.x  *p1[0] + camera_axis.y  * p1[1]) - camera_axis.x * ( - well_axis.z * t - well_axis.z * p1[2] + well_axis.x  *p1[0] + well_axis.y  * p1[1]) / (well_axis.x * camera_axis.y - camera_axis.x * well_axis.y)
    z = t


    x = ((well_axis.z * camera_axis.y - camera_axis.z * well_axis.y) * t
    - camera_axis.z * well_axis.y * p1[2]
    + camera_axis.x  * well_axis.y * p1[0]
    + well_axis.z * camera_axis.y * p1[2]
    - well_axis.x * camera_axis.y * p1[0] ) 
    / (well_axis.y * camera_axis.x - camera_axis.y * well_axis.x)

    y =  ( - camera_axis.z * well_axis.x * t - camera_axis.z * well_axis.x * p1[2] + camera_axis.x  * well_axis.x * p1[0] + camera_axis.y  * well_axis.x * p1[1] + well_axis.z * camera_axis.x * t + well_axis.z * camera_axis.x * p1[2] - well_axis.x  * camera_axis.x * p1[0] - well_axis.y * camera_axis.x * p1[1]) / (well_axis.x * camera_axis.y - camera_axis.x * well_axis.y)


    y =  ((well_axis.z * camera_axis.x - camera_axis.z * well_axis.x) * t 
    - camera_axis.z * well_axis.x * p1[2]
    + camera_axis.y  * well_axis.x * p1[1] 
    + well_axis.z * camera_axis.x * p1[2]
    - well_axis.y * camera_axis.x * p1[1])
    / (well_axis.x * camera_axis.y - camera_axis.x * well_axis.y)


    // Given these two equations, we now have a parameterised equation

    // (x,y,z) = (mt + a, nt + b, t) = (m, n, 1)t + (a + b + 0)
    // 
    // m = ((well_axis[2] * camera_axis[1] - camera_axis[2] * well_axis[1])) / (well_axis[1] * camera_axis[0] - camera_axis[1] * well_axis[0])
    // 
    // n = ((well_axis[2] * camera_axis[0] - camera_axis[2] * well_axis[0])) / (well_axis[0] * camera_axis[1] - camera_axis[0] * well_axis[1])
    // 
    // a = (- camera_axis[2] * well_axis[1] * p1[2] + camera_axis[0] * well_axis[1] * p1[0] + well_axis[2] * camera_axis[1] * p1[2] - well_axis[0] * camera_axis[1] * p1[0]) / (well_axis[1] * camera_axis[0] - camera_axis[1] * well_axis[0])
    // 
    // b = (- camera_axis[2] * well_axis[0] * p1[2] +camera_axis[1] * well_axis[0] * p1[1]  + well_axis[2] * camera_axis[0] * p1[2] - well_axis[1] * camera_axis[0] * p1[1]) / (well_axis[0] * camera_axis[1] - camera_axis[0] * well_axis[1])
*/

    float m = ((well_axis[2] * camera_axis[1] - camera_axis[2] * well_axis[1])) / (well_axis[1] * camera_axis[0] - camera_axis[1] * well_axis[0]);
    // 
    float n = ((well_axis[2] * camera_axis[0] - camera_axis[2] * well_axis[0])) / (well_axis[0] * camera_axis[1] - camera_axis[0] * well_axis[1]);
    // 
    float a = (-camera_axis[2] * well_axis[1] * p1[2] + camera_axis[0] * well_axis[1] * p1[0] + well_axis[2] * camera_axis[1] * p1[2] - well_axis[0] * camera_axis[1] * p1[0]) / (well_axis[1] * camera_axis[0] - camera_axis[1] * well_axis[0]);
    // 
    float b = (-camera_axis[2] * well_axis[0] * p1[2] + camera_axis[1] * well_axis[0] * p1[1] + well_axis[2] * camera_axis[0] * p1[2] - well_axis[1] * camera_axis[0] * p1[1]) / (well_axis[0] * camera_axis[1] - camera_axis[0] * well_axis[1]);

    float t = 2;

    return SbVec3f(m * t + a, n * t + b, t);
}

void setVertices(WellBore * pWell, SoVertexProperty * vertex_property, SoCamera* camera)
{
    int nPoints = pWell->nPoints;

    const float ribbon_width = 50.0f;

    int vertex_index = 0;
    int face_index = 0;

    int max_to_draw = nPoints;
    vertex_property->vertex.deleteValues(max_to_draw);

    SbVec3f on_well0x = pWell->points[1];
    SbVec3f in_space0x = getOuterPoint(pWell->points[0], on_well0x, ribbon_width, camera);

    for (int i = 0; i < max_to_draw - 1; ++i)
    {
        SbVec3f on_well0 = pWell->points[i];
        SbVec3f on_well1 = pWell->points[i + 1];

        SbVec3f in_space1 = getOuterPoint(on_well0, on_well1, ribbon_width, camera);

        vertex_property->vertex.set1Value(vertex_index + 0, in_space0x);
        vertex_property->vertex.set1Value(vertex_index + 1, on_well0x);
        vertex_property->vertex.set1Value(vertex_index + 2, on_well1);
        vertex_property->vertex.set1Value(vertex_index + 3, in_space0x);
        vertex_property->vertex.set1Value(vertex_index + 4, in_space1);

        vertex_index += 5;

        on_well0x = on_well1;
        in_space0x = in_space1;
    }
}

void cameraDebug(SoXtViewer * myViewer, WellBore* pWell)
{
    SoCamera* camera = myViewer->getCamera();

    SbVec3f camera_position = camera->position.getValue();
    //std::cout << camera_position[0] << " " << camera_position[1] << " " << camera_position[2] << std::endl;

    SbVec3f axis;
    float angle;
    SoSFRotation camera_rotation = camera->orientation;
    camera_rotation.getValue(axis, angle);
    //std::cout << axis[0] << " " << axis[1] << " " << axis[2] << ":" << angle << std::endl;

    SoNode* node = SoNode::getByName(SbName("points"));
    SbString str;
    SoVertexProperty* vertices = static_cast<SoVertexProperty*>(static_cast<SoVertexShape*>(node)->vertexProperty.getValue());
    //std::cout << vertices->vertex.getNum() << str << std::endl;

    setVertices(pWell, vertices, camera);
}

Ответы:


1

Узкоконкретный ответ на ваш вопрос относительно несложный. Подход, указанный в вашем вопросе, звучит правильно, но предоставленный вами код кажется слишком сложным... Вкратце: у вас есть один вектор, определяемый двумя последовательными точками на пути ленты, и второй вектор, определяемый направлением камеры. Нужный вам вектор — это просто векторное произведение этих двух векторов. Положение камеры не имеет значения, важно только направление. Используя Open Inventor, вы должны вычислить направление камеры, используя вектор направления по умолчанию и текущую ориентацию камеры следующим образом:

// Camera (reverse) direction vector
SbVec3f defVec(0,0,1), camVec;
const SbRotation& camRot = camera->orientation.getValue();
camRot.multVec( defVec, camVec );
camVec.normalize();

Если verts — это путь на ленте, то для каждого сегмента пути у вас есть две координаты, и вы можете вычислить две дополнительные координаты смещения, чтобы четыре координаты определяли прямоугольный многоугольник, обращенный к пользователю, например:

SbVec3f ribVec, orthoVec;
for (int i = 0; i < numSegs; ++i) {
  ribVec = verts[i+1] - verts[i];
  ribVec.normalize();
  orthoVec = camVec.cross(ribVec);
  orthoVec.normalize();
  verts2[i*4  ] = verts[i];  // i*4 because 4 verts per segment
  verts2[i*4+1] = verts[i+1];
  verts2[i*4+2] = verts[i+1] + ribbonWidth * orthoVec;
  verts2[i*4+3] = verts[i  ] + ribbonWidth * orthoVec;

Теперь вы можете работать над более сложной частью — как обработать «стыки» между этими полигонами, чтобы лента выглядела красиво...

10.06.2016
Новые материалы

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

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

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

Учебные заметки: создание моего первого пакета Node.js
Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


Для любых предложений по сайту: [email protected]