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

Вопросы и ответы

В проекте OpenSceneGraph у меня есть коробка с цилиндром внутри. Я провожу 2 плоскости отсечения через модель коробки и цилиндра, каждая плоскость направлена в противоположную сторону. В результате получается срез через коробку. Я размещаю камеру на некотором расстоянии и направляю ее на срез, чтобы увидеть сечение модели.

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

Я пробовал настраивать толщину и цвет линий, но это не решает мою проблему.

Другие вещи, которые я пробовал:

  1. Добавление освещения под разными углами
  2. Удаление обратного отсечения
  3. Включение режима каркасной модели для краев
  4. Использование Scribe
  5. Использование эффекта osgFX::Outline

Каркасная модель и Scribe каждое предлагают небольшое улучшение видимости краев, но это не очень хорошо (несколько пикселей). Эффект osgFX Outline также создает очень тонкие края, и все это немного усиливается освещением. Но ничего не дает простую, четкую, хорошо определенную коробку и цилиндр.

Еще одна идея заключалась в том, чтобы заполнить цилиндр туманом, что имело бы эффект и “заполненное” чем-то, что можно было бы увидеть при отсечении. Однако это не сработало для меня (возможно, я неправильно реализовал туман). Мне также понадобились бы два разных цвета / плотности тумана, чтобы различать коробку и цилиндр, и я не уверен, что это сработает.

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

#include <osg/Shape>
#include <osg/ShapeDrawable>
#include <osg/Geode>
#include <osg/Material>
#include <osg/LineWidth>
#include <osgViewer/CompositeViewer>
#include <osg/PositionAttitudeTransform>
#include <osg/ClipNode>
#include <osg/ClipPlane>
#include <osgGA/TrackballManipulator>

const float BOX_LENGTH      = 240.0;
const float BOX_HEIGHT      =  20.0;
const float BOX_WIDTH       =  16.0;
const float CYL_RADIUS      =   0.5;
const float CAMERA_Y        = -1.0 * BOX_LENGTH/2;  // Где начинать камеру
const float CAMERA_TO_CLIP  = 100.0;                 // Расстояние от камеры до плоскости отсечения
const float SLICE_THICKNESS =  0.1;                  // Толщина нашего среза
const int WIDTH =  600;  // Размеры изображения
const int HEIGHT = 600;
const osg::Vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f);

int main(int argc, char** argv)
{
  // Создать коробку
  osg::ref_ptr<osg::Box> box = new osg::Box(osg::Vec3(0, 0, 0), BOX_WIDTH, BOX_LENGTH, BOX_HEIGHT);
  osg::ref_ptr<osg::ShapeDrawable> boxDrawable = new osg::ShapeDrawable(box);
  osg::ref_ptr<osg::Geode> boxGeode = new osg::Geode();
  boxGeode->addDrawable(boxDrawable);

  // Создать цилиндр
  osg::ref_ptr<osg::Cylinder> cylinder = new osg::Cylinder(osg::Vec3(0, 0, 0), CYL_RADIUS, BOX_LENGTH);
  osg::ref_ptr<osg::ShapeDrawable> cylinderDrawable = new osg::ShapeDrawable(cylinder);
  osg::ref_ptr<osg::Geode> cylinderGeode = new osg::Geode();
  cylinderGeode->addDrawable(cylinderDrawable);

  // ...и установить толщину линии и цвет
  osg::ref_ptr<osg::StateSet> stateSet = cylinderGeode->getOrCreateStateSet();
  osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth(3.0f);  // Регулируем толщину линии
  stateSet->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
  osg::ref_ptr<osg::Material> material = new osg::Material;
  material->setDiffuse(osg::Material::FRONT_AND_BACK, WHITE);  
  stateSet->setAttributeAndModes(material, osg::StateAttribute::ON);

  // Поместить цилиндр в коробку
  osg::ref_ptr<osg::PositionAttitudeTransform> cylinderTransform = new osg::PositionAttitudeTransform();
  cylinderTransform->setPosition(osg::Vec3(0, 0, 0));
  cylinderTransform->setAttitude(osg::Quat(osg::DegreesToRadians(90.0f), osg::Vec3(1, 0, 0))); // Повернуть на 90 градусов по оси x
  cylinderTransform->addChild(cylinderGeode);

  osg::ref_ptr<osg::Group> model = new osg::Group();
  model->addChild(boxGeode);
  model->addChild(cylinderTransform);

  // Настроить вид и просмотрщик
  osg::ref_ptr<osgViewer::View> view = new osgViewer::View;
  view->setUpViewInWindow( 0, 0, WIDTH, HEIGHT );
  view->getCamera()->setViewport(new osg::Viewport(0, 0, WIDTH, HEIGHT));
  osgViewer::CompositeViewer viewer;
  viewer.addView(view);

  // Настроить манипулятор, чтобы мы могли перемещать камеру
  osg::ref_ptr<osgGA::CameraManipulator> cameraManip(new osgGA::TrackballManipulator());
  view->setCameraManipulator(cameraManip.get());

  // Установить положение камеры
  float camera_pos_y = CAMERA_Y;
  float center_pt_y = camera_pos_y + CAMERA_TO_CLIP;
  osg::Vec3d eye(0.0, camera_pos_y, 0.0);
  osg::Vec3d center(0, center_pt_y, 0);
  osg::Vec3d up(0, 0, 1);
  cameraManip->setHomePosition(eye, center, up);
  cameraManip->home(0.0);

  // Создать плоскость отсечения через середину модели в плоскости x-z
  float y_side1 =  1.0;      // Отсечь все слева  (оставить все справа)
  float y_side2 = -1.0;      // Отсечь все справа (оставить все слева)
  float y_offset1 = -1.0 * center_pt_y;
  float y_offset2 =        center_pt_y + SLICE_THICKNESS;
  osg::ref_ptr<osg::ClipPlane> clipPlane1 = new osg::ClipPlane(0, 0.0, y_side1, 0.0, y_offset1);
  osg::ref_ptr<osg::ClipPlane> clipPlane2 = new osg::ClipPlane(1, 0.0, y_side2, 0.0, y_offset2);

  // Присоединить плоскости отсечения
  osg::ref_ptr<osg::ClipNode> clipNode = new osg::ClipNode();
  clipNode->addClipPlane(clipPlane1);
  clipNode->addClipPlane(clipPlane2);
  clipNode->addChild(model);

  // Корневой узел
  osg::ref_ptr<osg::Group> root(new osg::Group);
  root->addChild(clipNode);

  // Убедитесь, что плоскость отсечения включена
  root->getOrCreateStateSet()->setMode(GL_CLIP_PLANE0, osg::StateAttribute::ON);
  root->getOrCreateStateSet()->setMode(GL_CLIP_PLANE1, osg::StateAttribute::ON);

  view->setSceneData(root);
  viewer.run();

  return 0;
}

Ответ

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

Шаг 1: Использование шейдеров для обрисовки краев

Один из самых эффективных способов выделить края — это использование фрагментного шейдера, который будет рисовать обводку (контур) моделей в области сечения. Это позволит вам создать яркие контуры как для коробки, так и для цилиндра.

// Пример шейдера для обводки
const char* outlineVertexShader = R"(
#version 330 core
layout(location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
)";

const char* outlineFragmentShader = R"(
#version 330 core
out vec4 FragColor;
void main()
{
    FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Красный цвет для обводки
}
)";

Шаг 2: Добавление эффекта тумана (Fog)

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

osg::ref_ptr<osg::Fog> fog = new osg::Fog();
fog->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 0.5f)); // Цвет тумана (синий)
fog->setDensity(0.1f);
fog->setMode(osg::Fog::EXP);
root->getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::ON);

Шаг 3: Улучшение системы освещения

Вы можете добавить несколько источников света, чтобы создать более яркое освещение, которое подчеркнет геометрические формы вашей модели.

osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource();
osg::ref_ptr<osg::Light> light = new osg::Light();
light->setLightNum(0);
light->setPosition(osg::Vec4(0.0f, 5.0f, 5.0f, 1.0f));
light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
lightSource->setLight(light);
root->addChild(lightSource);

Шаг 4: Регулирование параметров состояния

Проверьте, правильно ли активированы состояния для ваших геометрий. Убедитесь, что отключено обратное отсечение (back culling), чтобы все стороны ваших моделей были видны.

stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);

Шаг 5: Настройка камеры

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

osg::Vec3d eye(0.0, camera_pos_y + offset, 0.0);

Реализация

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

Заключение

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

Если вам нужны дальнейшие разъяснения или примеры кода, пожалуйста, дайте знать!

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

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