gpt4 book ai didi

c++ - OpenSceneGraph 中的世界到屏幕空间坐标

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:42:51 25 4
gpt4 key购买 nike

所以我有一个继承自 osg::Geode 的类 Label,我在 OpenSceneGraph 的世界空间中绘制它。显示每一帧后,我想读取屏幕空间坐标每个标签,这样我就可以找出它们在屏幕空间中的重叠程度。为此,我创建了一个类 ScreenSpace 来计算这个(有趣的函数是 calc_screen_coords。)

我写了一个小的子程序来转储每个帧的一些额外信息,包括表示程序认为屏幕空间坐标是什么的 ScreenSpace 框:

Labels with ScreenSpace that matches the Label

现在上图中,好像没有问题了;但如果我将它旋转到另一侧(用我的鼠标),那么它看起来就完全不同了:

Labels with ScreenSpace that does not match the Label whatsoever

这就是我不明白的。

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

有更好的方法吗?我应该尝试使用广告牌吗?我该怎么做呢? (我试过了,但它对我完全不起作用——我一定是漏掉了什么……)

下面是计算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;
}

有任何指示或帮助吗?

最佳答案

我找到了一个适合我的解决方案,但也不令人满意,所以如果您有更好的解决方案,我会洗耳恭听。

基本上,我从标签中获取不同的点,我知道在某些点上,我通过结合这个来计算屏幕空间。对于左侧和右侧,我取常规边界框的边界,对于顶部和底部,我用边界框的中心和标签的位置。

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);
}

关于c++ - OpenSceneGraph 中的世界到屏幕空间坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25999868/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com