可能你很想在桌面上实现一个3D物体渲染到桌面上,但又希望背景是透明的。这样就实现了像Office助手这样很酷的机器人交互。
OGRE是开源的3D图形引擎,使用这个引擎的亲一定很多,所以就谈谈利用它来如果实现。
实现步骤:
1.创建一个带WS_EX_LAYERED属性风格的窗口;
2.创建一个纹理,将场景内容渲染到这个纹理上;(纹理像素格式设为:ARGB,视口的背景色,设置为透明)
3.读取纹理的像素数据,生成32位4通道的BITMAP位图;
4.将位图利用UpdateLayeredWindow函数,更新窗口;
3~4步,放在渲染循环中。
我利用ParticleUniverse和Direct3D9两个插件,做了个粒子的demo如下:
可以发现渲染的帧率很低,性能瓶颈主要在从纹理取得数据生成位图,有空了再优化下。UpdateLayeredWindow本身是很快的。
下面直接贴代码了:(性能瓶颈的地方飘红)
#ifndef __VISION_APP_h_
#define __VISION_APP_h_
#include <OgreCamera.h>
#include <OgreEntity.h>
#include <OgreLogManager.h>
#include <OgreRoot.h>
#include <OgreViewport.h>
#include <OgreSceneManager.h>
#include <OgreRenderWindow.h>
#include <OgreConfigFile.h>
#include <OgreTexture.h>
#include <OISEvents.h>
#include <OISInputManager.h>
#include <OISKeyboard.h>
#include <OISMouse.h>
#include <SdkTrays.h>
#include <SdkCameraMan.h>
class App : public Ogre::FrameListener,
public Ogre::WindowEventListener, public OIS::KeyListener,
public OIS::MouseListener, OgreBites::SdkTrayListener
{
public:
App(void);
virtual ~App(void);
virtual void go(void);
protected:
virtual bool setup();
virtual bool configure(void);
virtual void chooseSceneManager(void);
virtual void createCamera(void);
virtual void createFrameListener(void);
virtual void createScene(void);
virtual void destroyScene(void);
virtual void createViewports(void);
virtual void setupResources(void);
virtual void createResourceListener(void);
virtual void loadResources(void);
// Ogre::FrameListener
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
// OIS::KeyListener
virtual bool keyPressed( const OIS::KeyEvent &arg );
virtual bool keyReleased( const OIS::KeyEvent &arg );
// OIS::MouseListener
virtual bool mouseMoved( const OIS::MouseEvent &arg );
virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id );
virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id );
// Ogre::WindowEventListener
//Adjust mouse clipping area
virtual void windowResized(Ogre::RenderWindow* rw);
//Unattach OIS before window shutdown (very important under Linux)
virtual void windowClosed(Ogre::RenderWindow* rw);
void UpdateWindow(Ogre::PixelBox& pixelbox);
Ogre::Root *mRoot;
Ogre::Camera* mCamera;
Ogre::SceneManager* mSceneMgr;
Ogre::RenderWindow* mWindow;
Ogre::String mResourcesCfg;
Ogre::String mPluginsCfg;
// OgreBites
OgreBites::SdkTrayManager* mTrayMgr;
OgreBites::SdkCameraMan* mCameraMan; // basic camera controller
OgreBites::ParamsPanel* mDetailsPanel; // sample details panel
bool mCursorWasVisible; // was cursor visible before dialog appeared
bool mShutDown;
//OIS Input devices
OIS::InputManager* mInputManager;
OIS::Mouse* mMouse;
OIS::Keyboard* mKeyboard;
Ogre::TexturePtr texture;
};
#endif // #ifndef __VISION_APP_h_
#include <Windows.h>
#include<GdiPlus.h>
#include<Shlwapi.h>
#include<atlbase.h>
#include<ObjIdl.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <ParticleUniverseSystemManager.h>
#include "App.h"
using namespace Ogre;
App::App(void)
: mRoot(0),
mCamera(0),
mSceneMgr(0),
mWindow(0),
mResourcesCfg(Ogre::StringUtil::BLANK),
mPluginsCfg(Ogre::StringUtil::BLANK),
mTrayMgr(0),
mCameraMan(0),
mDetailsPanel(0),
mCursorWasVisible(false),
mShutDown(false),
mInputManager(0),
mMouse(0),
mKeyboard(0) {
}
App::~App(void) {
if (mTrayMgr) delete mTrayMgr;
if (mCameraMan) delete mCameraMan;
//Remove ourself as a Window listener
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
windowClosed(mWindow);
delete mRoot;
}
bool App::configure(void) {
if(mRoot->restoreConfig()) {
// If returned true, user clicked OK so initialise
// Here we choose to let the system create a default rendering window by passing 'true'
mWindow = mRoot->initialise(true, "ParticleUniverse Render Window");
// Let's add a nice window icon
HWND hwnd;
mWindow->getCustomAttribute("WINDOW", (void*)&hwnd);
DWORD style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED);
return true;
} else {
return false;
}
}
void App::createScene(void){
ParticleUniverse::ParticleSystemManager*pManager =
ParticleUniverse::ParticleSystemManager::getSingletonPtr();
ParticleUniverse::ParticleSystem*pSys = pManager->createParticleSystem("mysys",
"example_014", mSceneMgr);
mSceneMgr->getRootSceneNode()->attachObject(pSys);
pSys->start();
}
void App::chooseSceneManager(void) {
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
}
void App::createCamera(void) {
// Create the camera
mCamera = mSceneMgr->createCamera("PlayerCam");
// Position it at 500 in Z direction
mCamera->setPosition(Ogre::Vector3(0,0,20));
// Look back along -Z
mCamera->lookAt(Ogre::Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
mCameraMan = new OgreBites::SdkCameraMan(
mCamera); // create a default camera controller
}
void App::createFrameListener(void) {
Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
OIS::ParamList pl;
size_t windowHnd = 0;
std::ostringstream windowHndStr;
mWindow->getCustomAttribute("WINDOW", &windowHnd);
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
mInputManager = OIS::InputManager::createInputSystem( pl );
mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(
OIS::OISKeyboard, true ));
mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject(
OIS::OISMouse, true ));
mMouse->setEventCallback(this);
mKeyboard->setEventCallback(this);
//Set initial mouse clipping size
windowResized(mWindow);
//Register as a Window listener
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
mTrayMgr = new OgreBites::SdkTrayManager("InterfaceName", mWindow, mMouse,
this);
mTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
mTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
mTrayMgr->hideCursor();
// create a params panel for displaying sample details
Ogre::StringVector items;
items.push_back("cam.pX");
items.push_back("cam.pY");
items.push_back("cam.pZ");
items.push_back("");
items.push_back("cam.oW");
items.push_back("cam.oX");
items.push_back("cam.oY");
items.push_back("cam.oZ");
items.push_back("");
items.push_back("Filtering");
items.push_back("Poly Mode");
mDetailsPanel = mTrayMgr->createParamsPanel(OgreBites::TL_NONE, "DetailsPanel",
200, items);
mDetailsPanel->setParamValue(9, "Bilinear");
mDetailsPanel->setParamValue(10, "Solid");
mDetailsPanel->hide();
mRoot->addFrameListener(this);
}
void App::destroyScene(void) {
}
void App::createViewports(void) {
texture = TextureManager::getSingleton().createManual( "RttTex",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D,
mWindow->getWidth(), mWindow->getHeight(), 0, PF_A8R8G8B8, TU_RENDERTARGET );
RenderTarget *rttTex = texture->getBuffer()->getRenderTarget();
Viewport *vp = rttTex->addViewport(mCamera);
vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
// Create one viewport, entire window
//Ogre::Viewport* vp = mWindow->addViewport(mCamera);
//vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(
Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
}
void App::setupResources(void) {
// Load resource paths from config file
Ogre::ConfigFile cf;
cf.load(mResourcesCfg);
// Go through all sections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::String secName, typeName, archName;
while (seci.hasMoreElements()) {
secName = seci.peekNextKey();
Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
Ogre::ConfigFile::SettingsMultiMap::iterator i;
for (i = settings->begin(); i != settings->end(); ++i) {
typeName = i->first;
archName = i->second;
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
archName, typeName, secName);
}
}
}
void App::createResourceListener(void) {
}
void App::loadResources(void) {
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
using namespace Ogre;
void App::UpdateWindow(Ogre::PixelBox& pixelbox) {
HWND hwnd;
mWindow->getCustomAttribute("WINDOW", (void*)&hwnd);
int width = pixelbox.getWidth();
int height = pixelbox.getHeight();
HDC winhdc = ::GetDC(hwnd);
HDC memhdc = CreateCompatibleDC(winhdc);
RECT rcWindow;
GetWindowRect(hwnd, &rcWindow);
BITMAPINFOHEADER header = { 0 };
header.biSize = sizeof(BITMAPINFOHEADER);
header.biWidth = width;
header.biHeight = -height;
header.biPlanes = 1;
header.biBitCount = 32;
header.biCompression = BI_RGB;
PVOID pvBits = NULL;
HBITMAP bmp = ::CreateDIBSection(NULL, (PBITMAPINFO)&header,
DIB_RGB_COLORS, &pvBits, NULL, 0);
HGDIOBJ oldbmp = ::SelectObject( memhdc, bmp);
UINT* data = static_cast<UINT*>(pvBits);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Ogre::ColourValue color = pixelbox.getColourAt(x, y, 0);
*data++ = color.getAsARGB();
}
}
BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
POINT srcpos = { 0, 0};
POINT ptWinPos = { rcWindow.left, rcWindow.top };
SIZE sizeWindow = {width, height};
::UpdateLayeredWindow(hwnd, winhdc, &ptWinPos, &sizeWindow, memhdc, &srcpos, 0,
&blend, ULW_ALPHA);
::SelectObject(memhdc, oldbmp);
::DeleteObject(bmp);
::DeleteDC(memhdc);
::ReleaseDC(hwnd, winhdc);
}
void App::go(void) {
mResourcesCfg = "resources.cfg";
mPluginsCfg = "plugins.cfg";
if (!setup())
return;
//mRoot->startRendering();
mRoot->getRenderSystem()->_initRenderTargets();
// Clear event times
mRoot->clearEventTimes();
Ogre::PixelBox* pixel = NULL;
while(true) {
//Pump messages in all registered RenderWindow windows
Ogre::WindowEventUtilities::messagePump();
if (!mRoot->renderOneFrame())
break;
Ogre::HardwarePixelBufferSharedPtr handle = texture->getBuffer();
if(!pixel){
int width = handle->getWidth();
int height= handle->getHeight();
char* buffer = new char[handle->getSizeInBytes()];
pixel = new Ogre::PixelBox(handle->getWidth(),
handle->getHeight(),
handle->getDepth(),
handle->getFormat(),
buffer);
}
handle->blitToMemory(*pixel);
UpdateWindow(*pixel);
}
// clean up
destroyScene();
}
bool App::setup(void) {
mRoot = new Ogre::Root(mPluginsCfg);
setupResources();
bool carryOn = configure();
if (!carryOn) return false;
chooseSceneManager();
createCamera();
createViewports();
// Set default mipmap level (NB some APIs ignore this)
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
// Create any resource listeners (for loading screens)
createResourceListener();
// Load resources
loadResources();
// Create the scene
createScene();
//不想看到帧率面板,可把这里注掉
createFrameListener();
return true;
};
bool App::frameRenderingQueued(const Ogre::FrameEvent& evt) {
if(mWindow->isClosed())
return false;
if(mShutDown)
return false;
//Need to capture/update each device
mKeyboard->capture();
mMouse->capture();
mTrayMgr->frameRenderingQueued(evt);
if (!mTrayMgr->isDialogVisible()) {
mCameraMan->frameRenderingQueued(
evt); // if dialog isn't up, then update the camera
if (mDetailsPanel->isVisible()) { // if details panel is visible, then update its contents
mDetailsPanel->setParamValue(0,
Ogre::StringConverter::toString(mCamera->getDerivedPosition().x));
mDetailsPanel->setParamValue(1,
Ogre::StringConverter::toString(mCamera->getDerivedPosition().y));
mDetailsPanel->setParamValue(2,
Ogre::StringConverter::toString(mCamera->getDerivedPosition().z));
mDetailsPanel->setParamValue(4,
Ogre::StringConverter::toString(mCamera->getDerivedOrientation().w));
mDetailsPanel->setParamValue(5,
Ogre::StringConverter::toString(mCamera->getDerivedOrientation().x));
mDetailsPanel->setParamValue(6,
Ogre::StringConverter::toString(mCamera->getDerivedOrientation().y));
mDetailsPanel->setParamValue(7,
Ogre::StringConverter::toString(mCamera->getDerivedOrientation().z));
}
}
return true;
}
bool App::keyPressed( const OIS::KeyEvent &arg ) {
if (mTrayMgr->isDialogVisible()) return
true; // don't process any more keys if dialog is up
if (arg.key == OIS::KC_F) { // toggle visibility of advanced frame stats
mTrayMgr->toggleAdvancedFrameStats();
} else if (arg.key ==
OIS::KC_G) { // toggle visibility of even rarer debugging details
if (mDetailsPanel->getTrayLocation() == OgreBites::TL_NONE) {
mTrayMgr->moveWidgetToTray(mDetailsPanel, OgreBites::TL_TOPRIGHT, 0);
mDetailsPanel->show();
} else {
mTrayMgr->removeWidgetFromTray(mDetailsPanel);
mDetailsPanel->hide();
}
} else if (arg.key == OIS::KC_T) { // cycle polygon rendering mode
Ogre::String newVal;
Ogre::TextureFilterOptions tfo;
unsigned int aniso;
switch (mDetailsPanel->getParamValue(9).asUTF8()[0]) {
case 'B':
newVal = "Trilinear";
tfo = Ogre::TFO_TRILINEAR;
aniso = 1;
break;
case 'T':
newVal = "Anisotropic";
tfo = Ogre::TFO_ANISOTROPIC;
aniso = 8;
break;
case 'A':
newVal = "None";
tfo = Ogre::TFO_NONE;
aniso = 1;
break;
default:
newVal = "Bilinear";
tfo = Ogre::TFO_BILINEAR;
aniso = 1;
}
Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(aniso);
mDetailsPanel->setParamValue(9, newVal);
} else if (arg.key == OIS::KC_R) { // cycle polygon rendering mode
Ogre::String newVal;
Ogre::PolygonMode pm;
switch (mCamera->getPolygonMode()) {
case Ogre::PM_SOLID:
newVal = "Wireframe";
pm = Ogre::PM_WIREFRAME;
break;
case Ogre::PM_WIREFRAME:
newVal = "Points";
pm = Ogre::PM_POINTS;
break;
default:
newVal = "Solid";
pm = Ogre::PM_SOLID;
}
mCamera->setPolygonMode(pm);
mDetailsPanel->setParamValue(10, newVal);
} else if(arg.key == OIS::KC_F5) { // refresh all textures
Ogre::TextureManager::getSingleton().reloadAll();
} else if (arg.key == OIS::KC_SYSRQ) { // take a screenshot
mWindow->writeContentsToTimestampedFile("screenshot", ".jpg");
} else if (arg.key == OIS::KC_ESCAPE) {
mShutDown = true;
}
mCameraMan->injectKeyDown(arg);
return true;
}
bool App::keyReleased( const OIS::KeyEvent &arg ) {
mCameraMan->injectKeyUp(arg);
return true;
}
bool App::mouseMoved( const OIS::MouseEvent &arg ) {
if (mTrayMgr->injectMouseMove(arg)) return true;
mCameraMan->injectMouseMove(arg);
return true;
}
bool App::mousePressed( const OIS::MouseEvent &arg,
OIS::MouseButtonID id ) {
if (mTrayMgr->injectMouseDown(arg, id)) return true;
mCameraMan->injectMouseDown(arg, id);
return true;
}
bool App::mouseReleased( const OIS::MouseEvent &arg,
OIS::MouseButtonID id ) {
if (mTrayMgr->injectMouseUp(arg, id)) return true;
mCameraMan->injectMouseUp(arg, id);
return true;
}
//Adjust mouse clipping area
void App::windowResized(Ogre::RenderWindow* rw) {
unsigned int width, height, depth;
int left, top;
rw->getMetrics(width, height, depth, left, top);
const OIS::MouseState &ms = mMouse->getMouseState();
ms.width = width;
ms.height = height;
}
//Unattach OIS before window shutdown (very important under Linux)
void App::windowClosed(Ogre::RenderWindow* rw) {
//Only close for window that created OIS (the main window in these demos)
if( rw == mWindow ) {
if( mInputManager ) {
mInputManager->destroyInputObject( mMouse );
mInputManager->destroyInputObject( mKeyboard );
OIS::InputManager::destroyInputSystem(mInputManager);
mInputManager = 0;
}
}
}
#include <windows.h>
#include "App.h"
using namespace Ogre;
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR cmdLine, INT) {
App app;
app.go();
return 0;
}