因此,我有一个Label类,它继承自osg::Geode,该类是我在OpenSceneGraph中绘制的世界中绘制的。显示每帧后,我想读取的屏幕空间坐标
每个标签,因此我可以找出它们在屏幕空间中有多少重叠。为此,我创建了一个ScreenSpace类,应该对其进行计算(有趣的功能是calc_screen_coords。)

我编写了一个小的子例程,该例程向每个帧转储了一些额外的信息,包括ScreenSpace框,该框代表程序认为屏幕空间的坐标是:

现在,在上面的图片中,似乎没有问题。但是,如果我将其旋转到另一侧(用鼠标),则外观会大不相同:

那是我不明白的。

我的屏幕空间计算错误吗?
还是我从Drawable中得到了错误的BoundingBox?
还是与我给setAutoRotateToScreen(true)对象的osgText::Text指令有关?

有一个更好的方法吗?我应该尝试使用广告牌吗?但是我该怎么办呢? (我尝试过,但对我完全不起作用-我一定想念一些东西...)

这是用于计算Label的屏幕空间坐标的源代码:

struct Pixel {
    // elided methods...

    int x;
    int y;
}

// Forward declarations:
pair<Pixel, Pixel> calc_screen_coords(const osg::BoundingBox& box, const osg::Camera* cam);
void rearange(Pixel& left, Pixel& right);

class ScreenSpace {
public:
    ScreenSpace(const Label* label, const osg::Camera* cam)
    {
        BoundingBox box = label->getDrawable(0)->computeBound();
        tie(bottom_left_, upper_right_) = calc_screen_coords(box, cam);
        rearrange(bottom_left_, upper_right_);
    }

    // elided methods...

private:
    Pixel bottom_left_;
    Pixel upper_right_;
}

pair<Pixel, Pixel> calc_screen_coords(const osg::BoundingBox& box, const osg::Camera* cam)
{
    Vec4d vec (box.xMin(), box.yMin(), box.zMin(), 1.0);
    Vec4d veq (box.xMax(), box.yMax(), box.zMax(), 1.0);

    Matrixd transmat
        = cam->getViewMatrix()
        * cam->getProjectionMatrix()
        * cam->getViewport()->computeWindowMatrix();

    vec = vec * transmat;
    vec = vec / vec.w();

    veq = veq * transmat;
    veq = veq / veq.w();

    return make_pair(
        Pixel(static_cast<int>(vec.x()), static_cast<int>(vec.y())),
        Pixel(static_cast<int>(veq.x()), static_cast<int>(veq.y()))
    );
}

inline void swap(int& v, int& w)
{
    int temp = v;
    v = w;
    w = temp;
}

inline void rearrange(Pixel& left, Pixel& right)
{
    if (left.x > right.x) {
        swap(left.x, right.x);
    }
    if (left.y > right.y) {
        swap(left.y, right.y);
    }
}

这是Label的构造(我尝试过删节):
// Forward declaration:
Geometry* createLeader(straph::Point pos, double height, Color color);

class Label : public osg::Geode {
public:
    Label(font, fontSize, text, color, position, height, margin, bgcolor, leaderColor)
    {
        osgText::Text* txt = new osgText::Text;
        txt->setFont(font);
        txt->setColor(color.vec4());
        txt->setCharacterSize(fontSize);
        txt->setText(text);

        // Set display properties and height
        txt->setAlignment(osgText::TextBase::CENTER_BOTTOM);
        txt->setAutoRotateToScreen(true);
        txt->setPosition(toVec3(position, height));

        // Create bounding box and leader
        typedef osgText::TextBase::DrawModeMask DMM;
        unsigned drawMode = DMM::TEXT | DMM::BOUNDINGBOX;
        drawMode |= DMM::FILLEDBOUNDINGBOX;
        txt->setBoundingBoxColor(bgcolor.vec4());
        txt->setBoundingBoxMargin(margin);
        txt->setDrawMode(drawMode);
        this->addDrawable(txt);

        Geometry* leader = createLeader(position, height, leaderColor);
        this->addDrawable(leader);
    }

    // elided methods and data members...
}

Geometry* createLeader(straph::Point pos, double height, Color color)
{
    Geometry* leader = new Geometry();
    Vec3Array* array = new Vec3Array();
    array->push_back(Vec3(pos.x, pos.y, height));
    array->push_back(Vec3(pos.x, pos.y, 0.0f));
    Vec4Array* colors = new Vec4Array(1);
    (*colors)[0] = color.vec4();
    leader->setColorArray(colors);
    leader->setColorBinding(Geometry::BIND_OVERALL);
    leader->setVertexArray(array);
    leader->addPrimitiveSet(new DrawArrays(PrimitiveSet::LINES, 0, 2));
    LineWidth* lineWidth = new osg::LineWidth();
    lineWidth->setWidth(2.0f);
    leader->getOrCreateStateSet()->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
    return leader;
}

有任何指示或帮助吗?

最佳答案

我找到了一个对我有用的解决方案,但也令人不满意,因此,如果您有更好的解决方案,我将不知所措。

基本上,我会从Label得出的某些观点得出不同的观点,
然后结合起来算出屏幕空间对于左侧和右侧,我
常规边界框的边界,对于顶部和底部,我使用
边界框的中心和标签的位置。

ScreenSpace::ScreenSpace(const Label* label, const osg::Camera* cam)
{
    const Matrixd transmat
        = cam->getViewMatrix()
        * cam->getProjectionMatrix()
        * cam->getViewport()->computeWindowMatrix();

    auto topixel = [&](Vec3 v) -> Pixel {
        Vec4 vec(v.x(), v.y(), v.z(), 1.0);
        vec = vec * transmat;
        vec = vec / vec.w();
        return Pixel(static_cast<int>(vec.x()), static_cast<int>(vec.y()));
    };

    // Get left right coordinates
    vector<int> xs; xs.reserve(8);
    vector<int> ys; ys.reserve(8);
    BoundingBox box = label->getDrawable(0)->computeBound();
    for (int i=0; i < 8; i++) {
        Pixel p = topixel(box.corner(i));
        xs.push_back(p.x);
        ys.push_back(p.y);
    };
    int xmin = *min_element(xs.begin(), xs.end());
    int xmax = *max_element(xs.begin(), xs.end());

    // Get up-down coordinates
    int ymin = topixel(dynamic_cast<const osgText::Text*>(label->getDrawable(0))->getPosition()).y;
    int center = topixel(box.center()).y;
    int ymax = center + (center - ymin);

    bottom_left_ = Pixel(xmin, ymin);
    upper_right_ = Pixel(xmax, ymax);
    z_ = distance_from_camera(label, cam);
}

09-28 08:45