因此,我有一個類Label
繼承我在OpenSceneGraph世界空間中繪製的osg::Geode
。在顯示每一幀之後,我想要讀取每個標籤的屏幕空間座標 ,以便我可以找出它們在屏幕空間中的重疊程度。爲此,我創建了一個類ScreenSpace
應計算該(有趣的功能是calc_screen_coords
。)世界到OpenSceneGraph中的空間座標
我寫了一個小的子程序轉儲一些額外的每幀信息,包括這代表着什麼程序認爲的屏幕空間盒屏幕空間座標是:
現在在上面的圖片,似乎是沒有問題的;但如果我把它旋轉到另一側(與我的鼠標),那麼它看起來完全不同:
而這正是我不明白。
我的世界屏幕空間計算錯了嗎? 或者我從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;
}
任何指針或幫助?
我也使用從世界到屏幕的轉換,反之亦然,我使用類似的代碼,就像'calc_screen_coords'。唯一的區別是我沒有用同類分量來劃分座標系,並且該分量總是被設置爲0.0。另外,我直接使用'Vec3d',不需要投射任何參數,也不要直接修改世界座標。在我的功能中,我返回屏幕座標。 – 2014-09-23 16:21:43
@ AdriC.S。我總是明白,你需要在最後除以同質分量來獲得實際的像素值 - 我在這裏錯了嗎?我希望看到你的功能。另外,當你說你直接使用'Vec3d'時,我假設你仍然只對x和y組件感興趣。 – cassava 2014-09-23 17:07:24
@cassava:在投影之後使用'w = 1'作爲輸入點,並用'w'除以正確的方法。有一件事情是錯誤的,至少在一般情況下,你只使用邊界框的2個點。實際上是在世界空間中的邊界框(包括那個自動旋轉)?如果是這樣,它是軸對齊嗎? – derhass 2014-09-23 18:23:44