Transferred from:
Http://www.cnblogs.com/cppguru/archive/2009/11/19/1605970.html
Brief analysis of the implementation principle of Ogre rendering Queue (1)
Rendering queue is an important concept in Ogre. All objects in the scenario are put into a specific rendering queue by Ogre before being drawn. The rendering queue mainly plays two roles: 1. ensure the correct order of drawing. For example, first draw the sky box, then draw the general object, and finally draw the interface. 2. Improve rendering efficiency. Ogre puts objects with the same pass together for rendering, so as to minimize the switching of rendering status. Generally, users usually set the rendering queue sequence number in entity. In fact, the workflow process of the entire rendering queue is far more complex than this, but end user intervention is basically not required. This article analyzes the details of this part without intervention.
The rendering queue is a key part of the Ogre rendering process and control the rendering efficiency. Therefore, you should thoroughly learn and understand this part. I think that through this process, we can not only learn the design ideas of excellent open-source libraries, but also understand the implementation principles to help us better use them. In addition, there are many key classes associated with the entire Ogre rendering process. Understanding the rendering queue is of great benefit to further understanding other functions.
In order to better understand this content, I plan to expand it in the following order, hoping to make it easy to understand.
1. Implementation of the rendering queue.
This section mainly analyzes the relationship and implementation details between various classes in the rendering queue in Ogre,
2. How to interact with the rendering queue in other parts of Ogre.
This section mainly analyzes who will operate the rendering queue when. This part helps to gain a deep understanding of how the various parts of the rendering queue collaborate to complete the entire work.
3. What can users do.
This section mainly analyzes the operational space left by the rendering queue.
Implementation of rendering queue
It is a simple relational diagram of the main classes corresponding to the rendering queue and serves as a reference diagram for reading subsequent content.
You need to know that the class that the rendering queue first comes into use is RenderQueue. At first glance, it is easy to misunderstand the name. In fact, RenderQueue is not a rendering queue but a manager, manages a group of objects named RenderQueueGroup. RenderQueue is a scenario Manager (SceneManager) object. A scenario manager has a RenderQueue object.
RenderQueueGroup indicates a rendering queue group with a fixed number. Obviously, this class is not the final rendering queue, but also a manager that manages a group of RenderPriorityGroup objects with priority. The fixed number of RenderQueueGroup is defined by the RenderQueueGroupID enumeration variable in Ogre, as follows:
Enum RenderQueueGroupID
{
RENDER_QUEUE_BACKGROUND = 0,
RENDER_QUEUE_SKIES_EARLY = 5,
RENDER_QUEUE_MAIN = 50,
RENDER_QUEUE_SKIES_LATE = 95,
Render_queue_overwriting = 100,
};
Each rendering queue group object has a value in the preceding enumeration variable to identify the processing priority of the rendering queue group. From the enumerated values, the smaller the number, the more draw first. For example, the sky queue group numbered 5 should be drawn before the interface numbered 100 overlay the queue group.
The RenderQueue class uses the fixed number of the rendering queue group as the index to save each RenderQueueGroup object. RenderQueue does not provide a method to create the RenderQueueGroup object. You only need to provide a number to obtain a queue group object. The corresponding object is automatically created based on the existence of the specified number, and the rendering queue group of each number is only one. The RenderQueue class uses a map with a fixed number as the key to save the rendering queue group objects. The advantage of this is that the rendering queue group objects can be quickly inserted and obtained. Because map is automatically sorted during insertion, rendering queue group objects can be retrieved from the map in sequence during painting, instead of processing the sorting separately.
RenderQueueGroup is easy to implement and provides something called a priority number. Each priority number corresponds to a RenderPriorityGroup object. In fact, this layer of priority is to provide more precise priority based on the above grouping. For example, in the sky box rendering object group numbered 1, you can divide several priorities. This priority is not required in most cases. You can use the default value.
Let's take a look at the RenderPriorityGroup object managed by RenderQueueGroup. In fact, this is not the final rendering queue, but also a manager, which manages six fixed types of rendering queues. The layer-by-layer structure is used to determine the position of the rendering queue. The rendering queue in which the rendering object is finally put is the responsibility of the RenderPriorityGroup object. The classification criteria are determined based on the Technique information associated with the rendering object. Generally, the rendering queue of the rendering object is determined based on whether to use transparent materials or whether to enable shadow. The specific algorithm is not described here.
The objects managed in the RenderPriorityGroup object are the QueuedRenderableCollection class, which is a real rendering queue and stores and sorts rendering objects. Currently, the rendering queue supports three sorting methods: ascending, descending, and pass. The sorting method must be determined when the rendering queue is empty. Because the elements inserted into the rendering queue depend on the sorting method, the sorting method cannot be modified when the set is not empty. The rendering queue class contains two sets used to save rendering objects, as shown below:
PassGroupRenderableMap mGrouped;
RenderablePassList mSortedDescending;
The first set stores the set sorted by pass. Any rendering object drawn using the same pass object is placed in this group.
The second set is the set that sorts rendering objects by the distance between the rendering object and the camera. In fact, this set is used for both ascending and descending order, and reverse access can be used to achieve reverse results.
From the source code, we can see that when inserting a rendering object, the rendering object is placed into the set described above according to the sorting method set by the current rendering queue. It should be noted that when an element is inserted, it is only stored in a group but not really sorted. The timing of sorting will be analyzed later. The rendering queue has the sort method to complete this sorting.
Now let's look back at how the entire rendering queue achieves the design goal of ensuring correct drawing order and improving drawing efficiency.
First, let's look at the draw sequence. The RenderQueueGroup object with a fixed number is the first step to ensure the order of drawing. It splits the background, objects, and interfaces to ensure that the rendering sequence is normal. In addition, the rendering queue QueuedRenderableCollection objects can be arranged in ascending or descending order of distance to ensure that objects with transparent attributes are drawn from the far and near order, and the second step to ensure the order of painting is completed.
Then we can see the rendering efficiency. The rendering queue QueuedRenderableCollection objects can be grouped by pass. Rendering objects drawn using the same pass during painting are continuously drawn, greatly reducing the switching of rendering status.
So far, we have analyzed the implementation principle of the rendering queue function. The system is divided into four layers to implement the entire rendering queue function. The first three layers play the role of the manager, and the last layer stores the rendering objects. The organizational structure is complicated. I think this complex software structure can be understood more easily only through the application context, so next we will analyze how other classes in Ogre interact with the rendering queue. The second part of this article will analyze the interaction part.
Brief analysis of the implementation principle of the Ogre rendering Queue (2)
In brief, the main Ogre rendering process uses a rendering queue in three steps: Clear, construct, and access. This process is repeated during the painting of each frame.
Clear rendering queue
Ogre clears the rendering queue before each frame is rendered. Familiar with the Ogre rendering process, you can easily see that the prepareRenderQueue () method is called in the SceneManager: _ renderScene () method. The implementation of this method is very simple, that is, clearing the current rendering queue and initializing some configuration parameters of the rendering queue. The clearing function of the rendering queue is RenderQueue: clear (). This function continues to call the clearing function of other internal objects.
Render queue Construction
The basic principle of rendering queue construction is to calculate all objects visible to the current frame from the scenario manager, and add these visible objects to the rendering queue in sequence. Based on the content described above, the rendering queue saves objects according to certain rules based on the input objects and their own configurations.
After the SceneManager: prepareRenderQueue () method is called, Ogre will continue to call the _ findVisibleObjects () method of the scenario manager. From the function name, we can see that this function is used to find visible objects. The following is the core code for its implementation:
GetRootSceneNode ()-> _ findVisibleObjects (cam, getRenderQueue (), visibleBounds, true, mDisplayNodes, onlyShadowCasters );
It is easy to see from the code that it extracts the root node from the scenario manager, and calls the _ findVisibleObjects () method of the root node to complete the search for visible objects and rendering queue filling function. This function is a recursive function that recursively calls all subnodes from the root node. We are most concerned with the second parameter of this function, which represents the rendering queue used by the current scenario manager. Passing the scenario manager to this function means that each node object is responsible for rendering queue filling. When this function returns, we can get the filled rendering queue. To gain a deeper understanding of the entire process, we can see in the _ findVisibleObjects method of the node that obtains all the MovableObject objects bound to the node, and call the _ updateRenderQueue method of these MovableObject objects. There are many objects inherited from MovableObject in Ogre. Here we will continue our analysis from the Entity object we are most familiar. Entity is an ovableObject object. Therefore, if the object bound to this node is an Entity object, the _ updateRenderQueue method called on the node is actually the _ updateRenderQueue method of the Entity object.
We know that the Entity object contains the SubEntity object, and only the SubEntity object is the object that can be displayed, so we can get all the SubEntity objects that can be displayed in the _ updateRenderQueue method of the Entity, and put it into the rendering queue passed in as a parameter. The Code is as follows:
SubEntityList: iterator I, iend;
Iend = displayEntity-> mSubEntityList. end ();
For (I = displayEntity-> mSubEntityList. begin (); I! = Iend; ++ I)
{
If (* I)-> isVisible ())
{
If (mRenderQueueIDSet)
Queue-> addRenderable (* I, mRenderQueueID );
Else
Queue-> addRenderable (* I );
}
}
Here we finally see the add operation on the rendering queue, calling the addRenderable function of RenderQueue. The addRenderable function has several overloaded versions. You can select an appropriate function as needed.
Now we know how a rendering object is updated to a rendering queue. Of course, the functions mentioned here are very complex. Here I will only introduce the important parts, and the rest of the content is not in the scope of the discussion.
Access to the rendering queue
After constructing the rendering queue of all visible objects in the current frame, the remaining step is to retrieve the rendering object we want to draw from the rendering queue. We return to the SceneManager: _ renderScene () method. Previously we talked about calling the SceneManager: _ findVisibleObjects () method to fill the rendering queue. After this method, we can see the _ renderVisibleObjects () method, which calls the rendervisibleobjectsdefasequsequence method. In the source code, there is also a method for rendering in the Custom order. This part will be analyzed in detail later. Ogre always strives for scalability as much as possible, so here it is no exception to providing users with a custom rendering order. Let's start with the simple default Rendering sequence.
Since the process of enabling shadow is complicated, we will only discuss the rendering process without shadow by default. SceneManager: rendervisibleobjectsdefasequsequence function implementation is very simple. The RenderQueueGroup is retrieved from RenderQueue in descending order of priority for processing. As mentioned above, RenderQueueGroup has performed another internal priority sorting, so the RenderPriorityGroup object is retrieved from RenderQueueGroup and processed in sequence according to the priority. We will discuss how to process a shadow without adding a shadow, so we only need to process the three render queues in the RenderPriorityGroup (as described earlier, the RenderPriorityGroup object contains six rendering Queues with different functions ). The sequence includes general objects, non-sorted transparent objects, and sorted transparent objects.
When the rendering object is inserted into the queue, no sorting is performed. Now it is time to sort objects. Each rendering queue calls the sort method to complete the sorting. After sorting, the correct and valid drawing order is obtained, and the visitor mode is used for the final rendering call. In the end, all rendering calls SceneManager :: the renderSingleObject function renders an object. The visitor mode increases the difficulty of understanding the code. I 'd like to ignore this part for the time being and write an article later.
The idea behind the scenes is quite simple, but the implementation is too deep, so it is difficult to see the original appearance. So it's hard to write clearly in the previous section. Let's take a look.
The following section describes what Ogre users can do.
In general, Ogre provides three levels of control over this function,
1. Configure the RenderQueue object.
2. RenderQueue object listener.
3. Customize the Rendering sequence.
RenderQueue object Configuration
A scenario manager has only one RenderQueue object, and the object does not change during rendering. Therefore, this object has several configurable parameters. The configuration time is of course before rendering. From the method of the RenderQueue object, we can see that a series of functions starting with set are responsible for setting these parameters. This method can only be used to configure the rendering queue in general and cannot interfere with internal functions during the rendering queue operation. Therefore, it is called the first level of control.
It is very easy to configure this object. You can call SceneManager: getRenderQueue to obtain the RenderQueue object pointer and access related methods through the object pointer.
RenderQueue object listener
The listener concept is very common in the Ogre system, and an interface for setting listeners is also provided in the rendering queue. The method of the supervisor interface is defined as follows:
Virtual bool renderableQueued (Renderable * rend, uint8 groupID, ushort priority, Technique ** ppTech, RenderQueue * pQueue) = 0;
You can implement the listener interface method. This method is called before the rendering object is put into the rendering queue. This method can be used to modify the Technique object for drawing this object, and the function can return false to prevent Ogre from placing this object into the rendering queue. The listener can call the RenderQueue: setRenderableListener method.
Custom Rendering sequence
Custom rendering order is of course the most flexible control method, and also a more complex method. This feature mainly includes two main classes: RenderQueueInvocationSequence and RenderQueueInvocation. The scenario Manager determines whether the RenderQueueInvocationSequence object has been set during rendering. If so, start rendering in the way defined by this object, and vice versa. The RenderQueueInvocationSequence object is related to the viewport. You can associate a custom rendering object on each viewport to control the rendering process of each viewport.
The RenderQueueInvocationSequence object is a simple container class used to save the instance of the RenderQueueInvocation object. The RenderQueueInvocation object is responsible for calling the rendering process of RenderQueueGroup.
The control principle of this structure is actually quite simple. The RenderQueueInvocation object is used to map the Rendering sequence. This object is stored in the RenderQueueInvocationSequence in order and accessed in order. At the same time, this object is associated with RenderQueueGroup to change the Rendering sequence.
For example, there are two RenderQueueInvocation objects. The first one is associated with the RenderQueueGroup object No. 50th, and the second one is associated with the first RenderQueueGroup object. RenderQueueInvocation is stored and accessed in sequence. Therefore, RenderQueueGroup object No. 1 is drawn on the fifth day, which changes the draw sequence. It should be clear that this mechanism only re-defines the Rendering sequence of RenderQueueGroup. to fully control the Rendering sequence, you must inherit from the RenderQueueInvocation class and reload the virtual functions of this class as needed.
Conclusion
In general, the rendering queue is still relatively complex. Here we can only go through a flow process, and many details need to be carefully reviewed, to make it clear that each part needs to be written into an article separately. For me, the process of writing this article is a learning process. Many parts that were not carefully studied have been reviewed again, I always think that reading high-quality source code is indeed an effective way to improve the capability.
Bytes ------------------------------------------------------------------------------------------------------------
Note:
In short, you can see the contact information of the organization of RenderTarget> ViewPort> RenderQueue> Camera> SceneMgr,
With the rendering of Ogre, you will find that you can gradually begin to optimize and modify it as needed, isn't it good?
I am so tired of living. I don't know how to live. I have experienced many things over the past few days, such as Snoop, money, and material. I still think that I am right. I have not found myself thinking that I am so simple, everything is idealistic. The last few days I thought it was a good life. It was just a mocking Of Me. I finally understood why someone wrote "I Am a man without tears". Maybe, it is time to accept these facts.
The greatest blow to my life is my passion. No matter how sincere I am, the other party has no intention. Is it just a joke in her opinion? I thought it was a time to die for her. I spent more than a dozen pounds and couldn't eat well. This was a good result, so I wouldn't expect anything anymore. I suddenly felt a lot better, and it was a time of pain, the breaking moment ,? I am dealing with the devil, right?