В проекте OpenSceneGraph у меня есть коробка с цилиндром внутри. Я провожу 2 плоскости отсечения через модель коробки и цилиндра, каждая плоскость направлена в противоположную сторону. В результате получается срез через коробку. Я размещаю камеру на некотором расстоянии и направляю ее на срез, чтобы увидеть сечение модели.
Когда камера направлена перпендикулярно сечению, края как коробки, так и цилиндра не видны. Мне нужно наклонить камеру под некоторым углом, чтобы края стали видимыми. В конечном итоге я хочу перемещать камеру вдоль длины коробки и делать последовательные снимки под этим фронтальным углом, чтобы визуализировать сечения на различных участках, поэтому мне нужно, чтобы края как коробки, так и цилиндра четко выделялись.
Я пробовал настраивать толщину и цвет линий, но это не решает мою проблему.
Другие вещи, которые я пробовал:
- Добавление освещения под разными углами
- Удаление обратного отсечения
- Включение режима каркасной модели для краев
- Использование Scribe
- Использование эффекта 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-буферов) для улучшения отображения моделей с различными плотностями и текстурами в полупрозрачных областях.
Если вам нужны дальнейшие разъяснения или примеры кода, пожалуйста, дайте знать!