[Solved] HardwareOcclusionQuery -- Query in RenderSingleObj

Problems building or running the engine, queries about how to use features etc.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

[Solved] HardwareOcclusionQuery -- Query in RenderSingleObj

Post by tgraupmann »

I have a question about how to implement a particular feature.

I would like to render an Ogre::Billboard.

However, I would like to adjust the alpha value of this Ogre::Billboard based on how much the Ogre::Billboard is occluded. I'd also like to do a fluid transition.

The idea is the Ogre::Billboard would fade out as an Ogre::Entity mesh appears in front of the Ogre::Billboard.

Any suggestions how to do this?

I have one idea. If the material has depth_test and depth_check on, maybe I could use the Ogre sort order value to make an educated guess how much the object is occluded?

Say if the sort order is 0-25 then alpha 100%-75%.
If the sort order is 25-50 then alpha 75%-50%.
If the sort order is 50-75 then alpha 50%-25%.
If the sort order is 75-100 then alpha 25%-0%.

I'm willing to try any better ideas.

Apparently, the worst idea is to use the depth buffer.
Last edited by tgraupmann on Wed Dec 06, 2006 5:26 pm, edited 5 times in total.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 66
Contact:

Post by sinbad »

You'd need to use HardwareOcclusionQuery. Bear in mind that you probably want to lag behind a frame or two in pulling the results of the query since they are pipelined elements, and pulling them too early will stall the pipeline.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Found another post for somebody else also working on a lens flare:
http://www.ogre3d.org/phpBB2/viewtopic. ... a872313ca8
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

How do you go about setting a rendering callback for begin and end on a Ogre::Billboard or Ogre::BillboardSet?

And then I could use HardwareOcclusionQuery:
http://www.ogre3d.org/docs/api/html/cla ... Query.html

Make a call to HardwareOcclusionQuery::beginOcclusionQuery in the begin render callback.

And make a call to HardwareOcclusionQuery::endOcclusionQuery in the end render callback.

HardwareOcclusionQuery::getLastQuerysPixelcount should tell me how many pixels were shown and I'll be able to base the alpha on that.

So my only question is how do you set up a rendering callback for a single Ogre::Billboard?

Must have something to do with Ogre::RenderQueueListener on the Ogre::SceneManager.:
http://www.ogre3d.org/docs/api/html/cla ... anagera173
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Ahh this was easy. RenderQueueListener is a delegate. So I created a class that inherits from Ogre::RenderQueueListener and implements (renderQueueStarted & renderQueueEnded).

And then within that class you do a:

Code: Select all

Ogre::SceneNodeManager::addRenderQueueListener(this);
The next step is:

Code: Select all

m_HardwareOcclusionQuery = new D3D9HardwareOcclusionQuery(pD3DDevice);
But where do we get the IDirect3DDevice9?
http://www.ogre3d.org/phpBB2/viewtopic. ... t3ddevice9
sinbad wrote:You'll have to use D3D9RenderWindow::getCustomAttribute("D3DDEVICE", &lpD3DDev), because this is a D3D-specific property and therefore not accessible through the normal interfaces.
So my nice problem is I need to figure out how to access D3D9RenderWindow.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Ok to get D3D9RenderWindow I just casted the regular window into a D3D9RenderWindow.

Code: Select all

	LPDIRECT3DDEVICE9 lpD3DDev;;
	((D3D9RenderWindow*)window)->getCustomAttribute("D3DDEVICE", &lpD3DDev);
And then used the LPDIRECT3DDEVICE9 with D3D9HardwareOcclusionQuery.

Code: Select all

	m_HardwareOcclusionQuery = new D3D9HardwareOcclusionQuery(lpD3DDev);
	assert (m_HardwareOcclusionQuery);
Now I'm getting a link error. Even though I've added RenderSystem_Direct3D9.lib.

Code: Select all

error LNK2001: unresolved external symbol "public: __thiscall Ogre::D3D9HardwareOcclusionQuery::D3D9HardwareOcclusionQuery(struct IDirect3DDevice9 *)" (??0D3D9HardwareOcclusionQuery@Ogre@@QAE@PAUIDirect3DDevice9@@@Z)
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Ah that was completely unnecessary. All I needed to do was:

Code: Select all

	m_HardwareOcclusionQuery = Root::getSingleton().getRenderSystem()->createHardwareOcclusionQuery();
	assert (m_HardwareOcclusionQuery);
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Ok now I am seeing the pixel counts get populated:

Code: Select all

StarElement: renderQueueStarted [0] [] [0]
StarElement: renderQueueEnded [0] [] [0]
StarElement: renderQueueStarted [5] [] [0]
StarElement: renderQueueEnded [5] [] [0]
StarElement: renderQueueStarted [50] [] [0]
StarElement: renderQueueEnded [50] [] [0]
StarElement: renderQueueStarted [95] [] [0]
StarElement: renderQueueEnded [95] [] [0]
StarElement: renderQueueStarted [100] [] [0]
StarElement: renderQueueEnded [100] [] [0]
StarElement: Pixel Count: [2084383]

Code: Select all

void StarElement::renderQueueStarted(uint8 queueGroupId, const std::string &invocation, bool &skipThisInvocation)
Is it possible to set the queueGroupId on an Ogre::BillboardSet, so that I can test just that set?
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Is it possible to change the queueGroupId for a BillboardSet or a Billboard or a SceneNode?
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

I found an example where the queueGroupId was set at the SceneManager level:
http://www.ogre3d.org/phpBB2/viewtopic. ... euegroupid

Is it possible to set at the SceneNode or BillboardSet or Billboard level?
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Ahhhhh:

m_BillboardSet->setRenderQueueGroup(33);
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

I don't think HardwareOcclusionQuery works like I pictured.

I set the render queue group.

Code: Select all

	static unsigned int s_RenderQueueGroup = 11;
	m_RenderQueueGroup = s_RenderQueueGroup++;
	m_BillboardSet->setRenderQueueGroup(m_RenderQueueGroup);
I enabled the listener:

Code: Select all

GetSceneManager()->addRenderQueueListener(this);
When the BillboardSet that I'm trying to test is not on the screen, the Pixel Count fluctates up to 1000k. When I'm looking directly at the Billboard set, the Pixel count is 200k. If a mesh goes between the BillboardSet and the camera, I see no effect.

Code: Select all

void StarElement::renderQueueStarted(uint8 queueGroupId, const std::string &invocation, bool &skipThisInvocation)
{
	//
	// SELECT OUR BILLBOARDSET
	//
	if (queueGroupId != m_RenderQueueGroup)
	{
		return;
	}

	if (m_HardwareOcclusionQuery)
	{
		m_HardwareOcclusionQuery->beginOcclusionQuery();
	}
}

Code: Select all

void StarElement::renderQueueEnded(uint8 queueGroupId, const std::string &invocation, bool &repeatThisInvocation)
{
	//
	// SELECT OUR BILLBOARDSET
	//
	if (queueGroupId != m_RenderQueueGroup)
	{
		return;
	}

	if (m_HardwareOcclusionQuery)
	{
		m_HardwareOcclusionQuery->endOcclusionQuery();

		m_LastPixelCount = m_PixelCount;
		m_HardwareOcclusionQuery->pullOcclusionQuery(&m_PixelCount);
	}
}
Am I not doing something correctly?
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Is it possible to pause the HardwareOcclusionQuery for queueGroupIds that I'm not interested in?

If the queueGroupId isn't desired. In Started I would pause the query and in End I would unpause the query.

With the code snippets above I should only be capturing the queueGroupId but it doesn't seem like it...

Is there an Ogre handy way to get an queueGroupId that isn't already in use?

What are the ranges of acceptable queueGroupIds? (because depending on which one I use the Billboard will disappear or show behind other Billboards)

Will choosing a different queueGroupId affect the rendering order?
Last edited by tgraupmann on Tue Nov 28, 2006 7:35 pm, edited 1 time in total.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

sinbad wrote:You'd need to use HardwareOcclusionQuery. Bear in mind that you probably want to lag behind a frame or two in pulling the results of the query since they are pipelined elements, and pulling them too early will stall the pipeline.
Am I stalling the pipeline and getting bogus results? I'm doing a HardwareOcclusionQuery on several objects, so if I wait a few frames, how will I know which object that I'm getting a Pixel Count for?
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Here is another idea. What if I do the occlusion testing all from renderQueueStarted. First beginOcclusionQuery, render just the queueGroupId, endOcclusionQuery, and then collect the pixel count.

Code: Select all

void StarElement::renderQueueStarted(uint8 queueGroupId, const std::string &invocation, bool &skipThisInvocation)
{
	//
	// SELECT OUR BILLBOARDSET
	//
	if (queueGroupId != m_RenderQueueGroup)
	{
		return;
	}

	if (m_HardwareOcclusionQuery)
	{
		m_HardwareOcclusionQuery->beginOcclusionQuery();
		GetSceneManager()->_renderQueueGroupObjects(GetSceneManager()->getRenderQueue()->getQueueGroup(queueGroupId), Ogre::QueuedRenderableCollection::OrganisationMode::OM_PASS_GROUP);
		m_HardwareOcclusionQuery->endOcclusionQuery();

		///
		/// ALREADY RENDERED
		///
		skipThisInvocation = true;

		///
		/// GET THE PIXEL COUNT
		///
		m_LastPixelCount = m_PixelCount;
		m_HardwareOcclusionQuery->pullOcclusionQuery(&m_PixelCount);
	}

Code: Select all

void StarElement::renderQueueEnded(uint8 queueGroupId, const std::string &invocation, bool &repeatThisInvocation)
{
}
Unfortunately, it seems the Pixel count is still off. For a billboard, the number shouldn't be higher than texture->width times texture->Height. But I'm getting Pixel Counts even when the Billboard is not on the screen.

I know the BillboardSet is the only thing in the queueGroupId.
Last edited by tgraupmann on Tue Nov 28, 2006 11:49 pm, edited 2 times in total.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

I'm seeing one other odd behavior.

The renderQueueStarted for the BillboardSet queueGroupId doesn't start getting called until the Billboard comes into view. Which is expected.

But if the Billboard leaves view, renderQueueStarted for the BillboardSet queueGroupId keeps getting called. I think I'd expect that the callback should not occur when the BillboardSet is out of view.
User avatar
xavier
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 9481
Joined: Fri Feb 18, 2005 2:03 am
Location: Dublin, CA, US
x 22

Post by xavier »

The docs explain this -- the billboard set will continue rendering for a while after it leaves the view. You can set the amount of time the set will continue to update -- see the API docs for more.

And for the love of God, please stop spamming every topic in the forums since the dawn of Man that remotely mentions HOQ. I'm waiting for you to dredge something up that was written about the time of Ogre 0.10...
Do you need help? What have you tried?

Image

Angels can fly because they take themselves lightly.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

What you have archives!!! Where are the archives!? ;p
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

xavier wrote:The docs explain this -- the billboard set will continue rendering for a while after it leaves the view. You can set the amount of time the set will continue to update -- see the API docs for more.
I didn't run across that. The Ogre.CHM is my constant companion. Good feature. I don't see a function on the BillboardSet that can do this. What class/function will allow me to do this?
User avatar
xavier
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 9481
Joined: Fri Feb 18, 2005 2:03 am
Location: Dublin, CA, US
x 22

Post by xavier »

My bad -- I was thinking ParticleSystem:

http://www.ogre3d.org/docs/api/html/cla ... eSystema37

Like I said in PM, make sure you are calling _updateBounds() when you are done setting up your billboard set, and make sure that the bbox for the set doesn't intersect the frustum (even if no actual bboards are visible).
Do you need help? What have you tried?

Image

Angels can fly because they take themselves lightly.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Maybe I can express my problem in Pictures:

Pixel Count: 7405513
Image

Pixel Count: 6227905
Image

Pixel Count: 12368469 (not even visible)
Image

The Pixel Counts don't seem correct to me...

And all this just to find out how many pixels are visible on the White Square.

I might just have to count them in a shader...
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

xavier wrote:Like I said in PM, make sure you are calling _updateBounds() when you are done setting up your billboard set
Tried that on both the SceneNode and the BillboardSet each time the SceneNode is moved. No Cigar. I'll try the other idea...
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

xavier wrote:and make sure that the bbox for the set doesn't intersect the frustum (even if no actual bboards are visible).
I can get the frustum to clip the BillboardSet by doing a:

Code: Select all

m_BillboardSet->setDefaultDimensions(0, 0);
This causes the Billboard to clip if any corner is occluded. The oddest thing. If the Billboard gets clipped shouldn't the HOQ report 0 Pixels?

Unfortunately, this doesn't appear to be the case.

When I can see the Billboard is being clipped, the Pixel count is: 5719714.

I think something deep down in Ogre is broken...

Would it help if I give you a small demo app that illustrates the problem? Or is the source code above enough?
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

tgraupmann wrote:Would it help if I give you a small demo app that illustrates the problem? Or is the source code above enough?
I wonder if my problem is specific to BillboardSets. Because I was able to get this to work for Plane Entities.

I modified the Samples\Demo_TextureFX\TextureFX.h example source code to do Hardware Occlusion Testing. Here is the source code. It works!

Code: Select all

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2005 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/**
    \file 
        TextureFX.h
    \brief
        Shows OGRE's ability to handle different types of texture effects.
*/


#include "ExampleApplication.h"

class TextureEffectsApplication : public ExampleApplication, RenderQueueListener
{
public:
	TextureEffectsApplication() :
		mScalingPlane_PixelCount	(0), // Hold the pixel count for
		mScrollingKnot_PixelCount	(0), // Hardware Occlusion Testing
		mWateryPlane_PixelCount		(0)  // "						 "
	{
	}

	// Implements RenderQueueListener, For Hardware Occlusion Testing
	void renderQueueStarted(uint8 queueGroupId, const std::string &invocation, bool &skipThisInvocation)
	{
		switch (queueGroupId)
		{
		case mScalingPlane_RenderQueueGroup:
			break;

		case mScrollingKnot_RenderQueueGroup:
			break;

		case mWateryPlane_RenderQueueGroup:
			break;

		default:
			// not interested
			return;
		};

		// CREATE HardwareOcclusionQuery
		Ogre::HardwareOcclusionQuery* hardwareOcclusionQuery = Root::getSingleton().getRenderSystem()->createHardwareOcclusionQuery();
		assert (hardwareOcclusionQuery);

		// Start the HardwareOcclusionQuery
		hardwareOcclusionQuery->beginOcclusionQuery();

		// Render the Item
		mSceneMgr->_renderQueueGroupObjects(mSceneMgr->getRenderQueue()->getQueueGroup(queueGroupId), Ogre::QueuedRenderableCollection::OrganisationMode::OM_PASS_GROUP);
		
		// Stop the HardwareOcclusionQuery
		hardwareOcclusionQuery->endOcclusionQuery();

		// ALREADY RENDERED
		skipThisInvocation = true;

		switch (queueGroupId)
		{
		case mScalingPlane_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mScalingPlane_PixelCount);
			break;

		case mScrollingKnot_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mScrollingKnot_PixelCount);
			break;

		case mWateryPlane_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mWateryPlane_PixelCount);
			break;
		};

		// DELETE HardwareOcclusionQuery
		Root::getSingleton().getRenderSystem()->destroyHardwareOcclusionQuery(hardwareOcclusionQuery);
		hardwareOcclusionQuery = 0;

		// SET THE DEBUG TEXT TO DISPLAY THE PIXEL COUNTS
		char buffer[250] = {0};
		sprintf(buffer,
			"ScalingPlane: [%d], WateryPlane [%d], ScrollingKnot [%d]",
			mScalingPlane_PixelCount, mWateryPlane_PixelCount, mScrollingKnot_PixelCount);
		mWindow->setDebugText(buffer);
	}

	// Implements RenderQueueListener, For Hardware Occlusion Testing
	void renderQueueEnded(uint8 queueGroupId, const std::string &invocation, bool &repeatThisInvocation)
	{
	}

protected:

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mScalingPlane_RenderQueueGroup = 1;

	// Pixel Count
	unsigned int mScalingPlane_PixelCount;

    void createScalingPlane()
    {
        // Set up a material for the plane

        // Create a prefab plane
        Entity *planeEnt = mSceneMgr->createEntity("Plane", SceneManager::PT_PLANE);
        // Give the plane a texture
        planeEnt->setMaterialName("Examples/TextureEffect1");

		// For Hardware Occlusion Testing
		planeEnt->setRenderQueueGroup(mScalingPlane_RenderQueueGroup);

        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(-250,-40,-100));

        node->attachObject(planeEnt);
    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mScrollingKnot_RenderQueueGroup = 2;

	// Pixel Count
	unsigned int mScrollingKnot_PixelCount;

    void createScrollingKnot()
    {
        Entity *ent = mSceneMgr->createEntity("knot", "knot.mesh");


        ent->setMaterialName("Examples/TextureEffect2");

		// For Hardware Occlusion Testing
		ent->setRenderQueueGroup(mScrollingKnot_RenderQueueGroup);

        // Add entity to the root scene node
        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(200,50,150));

        node->attachObject(ent);

    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mWateryPlane_RenderQueueGroup = 3;

	// Pixel Count
	unsigned int mWateryPlane_PixelCount;

    void createWateryPlane()
    {
        // Create a prefab plane
        Entity *planeEnt = mSceneMgr->createEntity("WaterPlane", SceneManager::PT_PLANE);

		// For Hardware Occlusion Testing
		planeEnt->setRenderQueueGroup(mWateryPlane_RenderQueueGroup);

        // Give the plane a texture
        planeEnt->setMaterialName("Examples/TextureEffect3");

        mSceneMgr->getRootSceneNode()->attachObject(planeEnt);
    }

    // Just override the mandatory create scene method
    void createScene(void)
    {

        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

        // Create a point light
        Light* l = mSceneMgr->createLight("MainLight");
        // Accept default settings: point light, white diffuse, just set position
        // NB I could attach the light to a SceneNode if I wanted it to move automatically with
        //  other objects, but I don't
        l->setPosition(20,80,50);

		// Added for Hardware Occlusion Testing
		mSceneMgr->addRenderQueueListener(this);

        createScalingPlane();
        createScrollingKnot();
        createWateryPlane();


        // Set up a material for the skydome
        MaterialPtr skyMat = MaterialManager::getSingleton().create("SkyMat",
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
        // Perform no dynamic lighting on the sky
        skyMat->setLightingEnabled(false);
        // Use a cloudy sky
        TextureUnitState* t = skyMat->getTechnique(0)->getPass(0)->createTextureUnitState("clouds.jpg");
        // Scroll the clouds
        t->setScrollAnimation(0.15, 0);

        // System will automatically set no depth write

        // Create a skydome
        mSceneMgr->setSkyDome(true, "SkyMat", -5, 2);
    }

};
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am
Contact:

Post by tgraupmann »

Image
I put the pixel counts in the debug text (see above).
Post Reply