Is there a way we could speed up the billboards?

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Is there a way we could speed up the billboards?

Post by christianclavet »

Hi,

During my "experiments" on IRB, I've seen a huge FPS drop because I used a lot of billboards for the vegetation.

From what I've seen so far on the forum, one possible solution would be to "batch" them into a single node. (AKA Batching or mesh combiner)

Then thinking about this (it really theorical, I'm not asking to implement anything here):

Suppose that I would create a function on the scene manager that would create a BatchingGroup, let's say:

Code: Select all

batchGroup* theGroup = smgr->addBatchingGroup()
and then would "flag" the node to be batched like this:

Code: Select all

node->assignBatchingGroup(theGroup);
So from this what I would understand that the scene manager, would have to NOT send the mesh to the GPU but batch it in the batch node, and that batch node would then be sent to the GPU. Also since all of theses nodes are moving, the billboard scene node would have to update the "batchingGroup" like we do actually with the VBO Hinting, by using the "setDirty()" command. If it doesnt move and rotate, then it would not update the batching node.

What I would like to know, is that method would have merits? Would it really worth it with the billboard scene node? I am missing something?

The way it's done should keep compatibility with older code and the method (If I'm able to do it someday) could be applied to other nodes, but the "changes" could be done one node at a time.
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

yes we could, hardware billboards, I am doing a project on it at the minute
fmx

Post by fmx »

Batching is the way to go, but it would have the same kind of troubles and overheads as animated meshes.
The key problem will always be the fact that the vertices would need to be edited almost every frame to face the camera

Hardware instancing could be used for same-material billboards tho
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

Thanks!

So adding a kind of "LOD" based on the distance that would "refresh" the batchgroup based on a time factor(ms) could be a good idea then?

Supposing adding something like this:

Code: Select all

node->updateBatchingGroupLOD(s32 level, f32 distance, u32 time);
From what the user would have entered would only update the mesh based on the settings made with that command.

Exemple:

Code: Select all

node->updateBatchingGroupLOD(1,340.0f,100);
node->updateBatchingGroupLOD(2,640.0f,250);
node->updateBatchingGroupLOD(3,900.0f,500);
So the "first level" will update the billboards (if they move), everytime,
the "second" level will update the billboards if they move, their distance is more than 340 unit, and set them to refresh at each 100ms

For the "if they move", if you look at my first post, there could be a check if the node had moved since the last refresh, if not, do not update the mesh in the batchgroup, if it has moved then update it. But all those checks could slow down the process... hummm
Last edited by christianclavet on Thu Dec 09, 2010 5:46 pm, edited 1 time in total.
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

which is usually the case as batching ALSO has to have same material billboards :)

I said hardware billboards, not instancing :) it's a vertex shader that rotates billboards to face the camera so you dont need to alter the batch mesh unless you change billboard position

P.S. christian... dont change depending on time but how much the ANGLE has changed
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

Cool idea about doing the billboard with a vertex shader. That way the geometry would be all on the GPU. Using this, I think a new node type would be required (similar to the billboard but the shader does the rotation as you said)
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

fmx wrote:Batching is the way to go, but it would have the same kind of troubles and overheads as animated meshes.
The key problem will always be the fact that the vertices would need to be edited almost every frame to face the camera

Hardware instancing could be used for same-material billboards tho
Even without instancing, you could put the camera-facing code in a vertex shader to really speed things up.
Mel
Competition winner
Posts: 2293
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

Can't point sprites be used to make billboards?
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

fastest billboarding shader I just came up with

just pass 2*apectRatioInTermsOfX (i.e. 2*4.f/3.f)
and it works only if you have billboard centred at 0.0 and vertices lie on the XY plane
else put centre coord of the quad in normals (gl_Normal) for each vertex, you wont need the normal (each billboard will face vec3(0.0,0.0,1.0) in v space). W coordinate can be replaced with 1.f

Code: Select all

// simple vertex shader
uniform float aspect2;

void main()
{
	gl_Position    = vec4(gl_Vertex.xy*vec2(aspect2,2.0),(gl_ModelViewProjectionMatrix*vec4(0.0,0.0,gl_Vertex.z,gl_Vertex.w)).zw);
	gl_FrontColor  = gl_Color;
	gl_TexCoord[0] = gl_MultiTexCoord0;
}
d3jake
Posts: 198
Joined: Sat Mar 22, 2008 7:49 pm
Location: United States of America

Post by d3jake »

I'll certainly be keeping an eye on where you folks end up going with this. I can imagine a usage for this to help scale a game's graphics. That is to say, if you know that you'll be using a player weapon\effect that a particle emitter wouldn't work for, a facing quad could be painted with a texture and used. It would certainly be cheaper to render then even the simplest three dimensional mesh.
The Open Descent Foundation is always looking for programmers! http://www.odf-online.org
"I'll find out if what I deleted was vital here shortly..." -d3jake
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

Right now I am creating two things for this... Shader based batched HW billboards (allowing total HW/VBO storage) and batched (CPU rotated) billboards, the second approach will have Christian's proposed LoD refreshing will occur based on firstly the size the billboard has on screen (fast approach but can fail where many particles are stuck together) and the change in angle between camera and billboard. I could also skip out indices of billboards which are too small to be bothered with/culled. I could also introduce dynamic octtrees which could be used for billboards which stay static (or mostly static, particle systems+octtree=bad idea), I would support different culling modes (EAC_enumerations) and moving/scaling the billboards independently of each other and feeding out empty scene nodes for animators. Culled billboards wouldn't be rotated... :) around 10 different combinations of culling will be available...

HW buffers would be a totally different story. First off, all is stored on HW. Culling could take place but doesn't have to, the particles would move around VIRTUALLY. The virtual position and scale would be passed into VS as uniforms ( advantage 6 floats instead of 16 for a MTrix, allowing 512 registers/instances), this allows to draw all instances without changing tform states. No CPU per vertex modification except for billboard addition or removal.
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

Right I drafted the thing out... ofcourse the implementation is missing

Code: Select all

#ifndef __I_EMPTY_BILLBOARD_SCENE_NODE_H_INCLUDED__
#define __I_EMPTY_BILLBOARD_SCENE_NODE_H_INCLUDED__

#include "IBillboardSceneNode.h"

namespace irr
{
namespace scene
{
class IEmptyBillboardSceneNode;
}
}

#include "IBatchedBillboardSceneNode.h"

namespace irr
{
namespace scene
{


class IEmptyBillboardSceneNode : public ISceneNode
{
    core::dimension2d<f32> Size;
    core::aabbox3df bbox;
    video::SColor TC,BC;
    bool IsInBatchVisible;
    IBatchedBillboardSceneNode* BatchParent;

public:

	//! Constructor
	IEmptyBillboardSceneNode(IBatchedBillboardSceneNode* batchParent, IBillboardSceneNode* node);

    //! Destructor
    virtual ~IEmptyBillboardSceneNode();

	//! Sets the size of the billboard.
	virtual void setSize(const core::dimension2d<f32>& size);

	//! Returns the size of the billboard.
	virtual const core::dimension2d<f32>& getSize() {return Size;}

	//! Set the color of all vertices of the billboard
	/** \param overallColor: the color to set */
	virtual void setColor(const video::SColor & overallColor);

	//! Set the color of the top and bottom vertices of the billboard
	/** \param topColor: the color to set the top vertices
	\param bottomColor: the color to set the bottom vertices */
	virtual void setColor(const video::SColor & topColor, const video::SColor & bottomColor);

	//! Gets the color of the top and bottom vertices of the billboard
	/** \param topColor: stores the color of the top vertices
	\param bottomColor: stores the color of the bottom vertices */
	virtual void getColor(video::SColor & topColor, video::SColor & bottomColor) const {
	    topColor = TC;
	    bottomColor = BC;
	}

    //! This method is called just before the rendering process of the whole scene.
    /** Nodes may register themselves in the render pipeline during this call,
    precalculate the geometry which should be renderered, and prevent their
    children from being able to register themselves if they are clipped by simply
    not calling their OnRegisterSceneNode method.
    If you are implementing your own scene node, you should overwrite this method
    with an implementation code looking like this:
    \code
    if (IsVisible)
        SceneManager->registerNodeForRendering(this);

    ISceneNode::OnRegisterSceneNode();
    \endcode
    */
    virtual void OnRegisterSceneNode();

    //! OnAnimate() is called just before rendering the whole scene.
    /** Nodes may calculate or store animations here, and may do other useful things,
    depending on what they are. Also, OnAnimate() should be called for all
    child scene nodes here. This method will be called once per frame, independent
    of whether the scene node is visible or not.
    \param timeMs Current time in milliseconds. */
    virtual void OnAnimate(u32 timeMs);

    //! Renders the node.
    virtual void render() {}
    virtual const core::aabbox3d<f32>& getBoundingBox() const {return bbox;}

    virtual bool isInBatchVisible() const {return IsInBatchVisible;}

    //! Returns whether the node should be visible (if all of its parents are visible).
    /** This is only an option set by the user, but has nothing to
    do with geometry culling
    \return The requested visibility of the node, true means
    visible (if all parents are also visible). */
    virtual bool isVisible() const {return false;}

    //! Check whether the node is truly visible, taking into accounts its parents' visibility
    /** \return true if the node and all its parents are visible,
    false if this or any parent node is invisible. */
    virtual bool isTrulyVisible() const {return false;}

    //! Sets if the node should be visible or not.
    /** All children of this node won't be visible either, when set
    to false. Invisible nodes are not valid candidates for selection by
    collision manager bounding box methods.
    \param isVisible If the node shall be visible. */
    virtual void setVisible(bool isVisible) {IsInBatchVisible = isVisible;}


    //! Removes this scene node from the scene
    /** If no other grab exists for this node, it will be deleted.
    */
    virtual void remove();

    //! Returns the material based on the zero based index i.
    /** To get the amount of materials used by this scene node, use
    getMaterialCount(). This function is needed for inserting the
    node into the scene hierarchy at an optimal position for
    minimizing renderstate changes, but can also be used to
    directly modify the material of a scene node.
    \param num Zero based index. The maximal value is getMaterialCount() - 1.
    \return The material at that index. */
    virtual video::SMaterial& getMaterial(u32 num);

    //! Get amount of materials used by this scene node.
    /** \return Current amount of materials of this scene node. */
    virtual u32 getMaterialCount() const;

    //! Sets all material flags at once to a new value.
    /** Useful, for example, if you want the whole mesh to be
    affected by light.
    \param flag Which flag of all materials to be set.
    \param newvalue New value of that flag. */
    void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
    {
        //Can't set, because the batch would be all wrong
    }

    //! Sets the texture of the specified layer in all materials of this scene node to the new texture.
    /** \param textureLayer Layer of texture to be set. Must be a
    value smaller than MATERIAL_MAX_TEXTURES.
    \param texture New texture to be used. */
    void setMaterialTexture(u32 textureLayer, video::ITexture* texture)
    {
        //Can't set, because the batch would be all wrong
    }

    //! Sets the material type of all materials in this scene node to a new material type.
    /** \param newType New type of material to be set. */
    void setMaterialType(video::E_MATERIAL_TYPE newType)
    {
        //Can't set, because the batch would be all wrong
    }

    //! Sets the relative scale of the scene node.
    /** \param scale New scale of the node, relative to its parent. */
    virtual void setScale(const core::vector3df& scale);

    //! Sets the rotation of the node relative to its parent.
    /** This only modifies the relative rotation of the node.
    \param rotation New rotation of the node in degrees. */
    virtual void setRotation(const core::vector3df& rotation);

    //! Sets the position of the node relative to its parent.
    /** Note that the position is relative to the parent.
    \param newpos New relative position of the scene node. */
    virtual void setPosition(const core::vector3df& newpos);

    //! Updates the absolute position based on the relative and the parents position
    /** Note: This does not recursively update the parents absolute positions, so if you have a deeper
        hierarchy you might want to update the parents first.*/
    virtual void updateAbsolutePosition();

    //! Retrieve the scene manager for this node.
    /** \return The node's scene manager. */
    virtual ISceneManager* getSceneManager(void) const;
};

} // end namespace scene
} // end namespace irr


#endif

Code: Select all

#ifndef __I_BATCHED_BILLBOARD_SCENE_NODE_H_INCLUDED__
#define __I_BATCHED_BILLBOARD_SCENE_NODE_H_INCLUDED__

#include "IBillboardSceneNode.h"

namespace irr
{
namespace scene
{
class IBatchedBillboardSceneNode;
}
}

#include "IEmptyBillboardSceneNode.h"

namespace irr
{
namespace scene
{


class IBatchedBillboardSceneNode : public ISceneNode
{
public:

	//! Constructor
	IBatchedBillboardSceneNode(ISceneNode* parent, ISceneManager* mgr,
        IBillboardSceneNode** nodes=0, u32 count=0, core::array<IEmptyBillboardSceneNode*> *impostorsOut=0,
        s32 id=-1, const core::vector3df& position = core::vector3df(0,0,0))
		: ISceneNode(parent, mgr, id, position) {}


	virtual IEmptyBillboardSceneNode* addBillboard(IBillboardSceneNode* node) =0;

	virtual void removeBillboard(IEmptyBillboardSceneNode* node) = 0;
	virtual void removeBillboard(IEmptyBillboardSceneNode* node,bool noDrop) = 0;
	virtual void removeBillboard(IBillboardSceneNode* node) = 0;

	virtual void updateBillboardSize(const IEmptyBillboardSceneNode* const node, const core::dimension2d<f32>& size) const = 0;
	virtual void updateBillboardColor(IEmptyBillboardSceneNode* node, const video::SColor & overallColor) = 0;
	virtual void updateBillboardColor(IEmptyBillboardSceneNode* node, const video::SColor & topColor, const video::SColor & bottomColor) = 0;
    virtual void updateBillboardScale(IEmptyBillboardSceneNode* node,const core::vector3df& scale) = 0;
    virtual void updateBillboardRotation(IEmptyBillboardSceneNode* node,const core::vector3df& rotation) = 0;
    virtual void updateBillboardPosition(IEmptyBillboardSceneNode* node,const core::vector3df& newpos) = 0;
    virtual void updateBillboardAbsoluteTransformation(IEmptyBillboardSceneNode* node,core::matrix4 AbsoluteTransformation) = 0;
    virtual video::SMaterial& getMaterial(u32 num) = 0;
    virtual u32 getMaterialCount() const = 0;

	virtual u32 getCount() = 0;
};

}
}

#endif

Code: Select all

#ifndef __C_BATCHED_BILLBOARD_SCENE_NODE_H_INCLUDED__
#define __C_BATCHED_BILLBOARD_SCENE_NODE_H_INCLUDED__

#include "IBatchedBillboardSceneNode.h"
#include "SMeshBuffer.h"

namespace irr
{
namespace scene
{


class CBatchedBillboardSceneNode : public IBatchedBillboardSceneNode
{
    core::array<IEmptyBillboardSceneNode*> impostors;
    core::array<IBillboardSceneNode*> billboards;
    SMeshBuffer buffer;

    core::aabbox3df bbox;
    core::array<video::SMaterial> billMaterial;
public:
    CBatchedBillboardSceneNode(ISceneNode* parent, ISceneManager* mgr,
        IBillboardSceneNode** nodes=0, u32 count=0, core::array<IEmptyBillboardSceneNode*> *impostorsOut=0,  s32 id=-1,
		const core::vector3df& position = core::vector3df(0,0,0));
    ~CBatchedBillboardSceneNode();

	virtual IEmptyBillboardSceneNode* addBillboard(IBillboardSceneNode* node);

	virtual void removeBillboard(IEmptyBillboardSceneNode* node);
	virtual void removeBillboard(IEmptyBillboardSceneNode* node,bool noDrop);
	virtual void removeBillboard(IBillboardSceneNode* node);

	virtual void updateBillboardSize(const IEmptyBillboardSceneNode* const node, const core::dimension2d<f32>& size) const;
	virtual void updateBillboardColor(IEmptyBillboardSceneNode* node, const video::SColor & overallColor);
	virtual void updateBillboardColor(IEmptyBillboardSceneNode* node, const video::SColor & topColor, const video::SColor & bottomColor);
    virtual void updateBillboardScale(IEmptyBillboardSceneNode* node,const core::vector3df& scale);
    virtual void updateBillboardRotation(IEmptyBillboardSceneNode* node,const core::vector3df& rotation);
    virtual void updateBillboardPosition(IEmptyBillboardSceneNode* node,const core::vector3df& newpos);
    virtual void updateBillboardAbsoluteTransformation(IEmptyBillboardSceneNode* node,core::matrix4 AbsoluteTransformation);
    virtual video::SMaterial& getMaterial(u32 num);
    virtual u32 getMaterialCount() const;

	virtual u32 getCount();

	virtual void render();
	virtual const core::aabbox3d<float>& getBoundingBox() const {return bbox;}
	virtual void OnRegisterSceneNode();
};

}
}

#endif
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

I think I found a bug in the Irrlicht Billboard... You can't set scale on it...

++UPDATE++

http://www.mediafire.com/?iot18wd63qnw35t

It is now ready... I did Batched (CPU) Billboards and a grid of 32*32*32 billboards experienced a 100% speed increase (from 25fps to 53). It is without the LoD yet and the dynamic movement functions are not implemented yet with the culling++OcTrees, so I think we can expect another 100% (or 3300% increase when culled). There is a bug, I set the culling off but the node dissapears off the screen anyway, something is causing triangles to flicker and the indices give you backfaces (but backface culling should be OFF anyway).

I dont see why I shouldn't get over 500fps with just a 16k mesh in the hardware mode...
devsh
Competition winner
Posts: 2049
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Post by devsh »

Right I have implemented LoD by angle, stopped using SMeshBuffer (which is really slow and buggy), i fixed the backface issues. NOW I GET A WHOPPING 200fps when camera is static (no operations on vertices if camera angle doesn't change by much) and ~170 when moving around constantly.

Hmm... wait a second.. isnt that something like 700% speed increase?
Kalango
Posts: 157
Joined: Thu Apr 26, 2007 12:46 am

Post by Kalango »

Seems to be good stuff man!
I would realy like to see a benchmark on this.
waiting for the final release...!
Post Reply