OnNodePreRender()

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
LesPaul75
Posts: 35
Joined: Tue Jan 22, 2013 8:27 am

OnNodePreRender()

Post by LesPaul75 »

I'm experimenting with a custom light manager. I borrowed code from Example 20 and everything seemed fine. Then, I experimented with changing the color of the lights in the scene during OnNodePreRender(). Is that allowed? It doesn't seem to work correctly.

I've boiled the failing example down to the following. The scene has two lights, one red and one green (when they are initially created). Then, I alter their colors in OnNodePreRender() as follows:

Code: Select all

// Called before the specified scene node is rendered
virtual void OnNodePreRender(irr::scene::ISceneNode* node)
{
    // Only calculate light distances during "solid" render pass
    if (m_current_render_pass != irr::scene::ESNRP_SOLID)
    {
        return;
    }
 
    // The "m_sorted_lights" vector is sorted here, by distance ... [0] is now the closest and [1] is the farthest from the node
    // The sort is nothing fancy, just std::sort(), and I have verified that it is correct
 
    // Enable the closest light, make it yellow
    m_sorted_lights[0].m_node->setVisible(true);
    m_sorted_lights[0].m_node->getLightData().DiffuseColor.set(1.0f, 1.0f, 0.0f);
 
    // Disable the farthest light, make it cyan
    m_sorted_lights[1].m_node->setVisible(false);
    m_sorted_lights[1].m_node->getLightData().DiffuseColor.set(0.0f, 1.0f, 1.0f);
}
When this runs, I can see the light changing color between purple and yellow. I added some sanity-check code in OnNodePostRender() and I verify that the second light (m_sorted_lights[1]) is never enabled. So, all I can figure is that somehow it's not safe to access getLightData() during OnNodePreRender()?
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: OnNodePreRender()

Post by chronologicaldot »

Function drawAll() of SceneManager.cpp, beginning on line 1346, should answer that question.
Basically, the lights are drawn before the node is, so changing the light color does nothing.
However, if you set up a light manager, you can use the function ILightManager::OnRenderPassPreRender(timeMillis) to change the light color, but it won't apply to individual nodes that the light reaches unless you render each node in a new render pass, changing the light as you go, but seems tedious.
Elsewhere on the boards, the guys are talking about mods that allow for infinite lights and more control over lighting, so have a look around. I think it's under Project Announcements. This might be it: http://irrlicht.sourceforge.net/forum/v ... =6&t=37811
LesPaul75
Posts: 35
Joined: Tue Jan 22, 2013 8:27 am

Re: OnNodePreRender()

Post by LesPaul75 »

chronologicaldot wrote:Function drawAll() of SceneManager.cpp, beginning on line 1346, should answer that question.
Basically, the lights are drawn before the node is, so changing the light color does nothing.
Crud. I saw that code as I was snooping around and guessed it might be the problem -- but can you tell me, what does it mean to "render" a light? My understanding of the 3D pipeline is that light really only matters when an actual triangle is drawn, the lighting calculation happens, dot product, etc... So what is the render pass for the lights actually doing? If I'm looking in the right place, in CLightSceneNode::render(), it appears to just draw a bounding box and then call driver->addDynamicLight(LightData).
Elsewhere on the boards, the guys are talking about mods that allow for infinite lights and more control over lighting, so have a look around. I think it's under Project Announcements. This might be it: http://irrlicht.sourceforge.net/forum/v ... =6&t=37811
IrrRenderer looks interesting... Is that the best option for deferred rendering with Irrlicht? Any other options for supporting lots of lights?

Thanks
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: OnNodePreRender()

Post by chronologicaldot »

LesPaul75 wrote:
chronologicaldot wrote:Function drawAll() of SceneManager.cpp, beginning on line 1346, should answer that question.
Basically, the lights are drawn before the node is, so changing the light color does nothing.
Crud. I saw that code as I was snooping around and guessed it might be the problem -- but can you tell me, what does it mean to "render" a light? My understanding of the 3D pipeline is that light really only matters when an actual triangle is drawn, the lighting calculation happens, dot product, etc... So what is the render pass for the lights actually doing? If I'm looking in the right place, in CLightSceneNode::render(), it appears to just draw a bounding box and then call driver->addDynamicLight(LightData).
Pardon me, yes, that's right. (Should've double checked. My bad.) But rereading your post, I'm not sure what the error is.

I suppose I should mention: irrlicht sorts the lights before rendering, so it's possible your list is inaccurate, depending on how it's tied to the light data. See line 1428 of CSceneManager.cpp
irrlicht automatically turns lights on and off based on how close they are to the camera. I don't know if you knew that or if that's causing the problem. I haven't tried out your specific setup myself.
LesPaul75 wrote:IrrRenderer looks interesting... Is that the best option for deferred rendering with Irrlicht? Any other options for supporting lots of lights?
edit: This one supports 200+ lights:
http://irrlicht.sourceforge.net/forum/v ... =6&t=37811
LesPaul75
Posts: 35
Joined: Tue Jan 22, 2013 8:27 am

Re: OnNodePreRender()

Post by LesPaul75 »

I believe the sort in CSceneManager only happens if no custom light manager is used.
edit: This one supports 200+ lights:
viewtopic.php?f=6&t=37811
I'm checking out both Sudi's renderer and IrrRenderer now. I'm wondering if they support both OpenGL and DirectX, because I don't want to be locked to one API.

edit: Looks like they are both OpenGL only. I'd still be happy just knowing how to change light colors for each node. I'm pretty sure I could accomplish all the lighting I need if that worked.
LesPaul75
Posts: 35
Joined: Tue Jan 22, 2013 8:27 am

Re: OnNodePreRender()

Post by LesPaul75 »

Ok, I've "solved" this, but I'm putting "solved" in quotes because it's not a very good solution. The real solution would be to tinker with the CSceneManager code itself and slightly change the order in which lights are handled.

The problem is exactly as you stated -- the render() function of CLightSceneNode has already happened before OnNodePreRender(), which means that addDynamicLight() has already happened for all the lights. So if you start changing light colors, etc, you're really doing nothing for the current render pass.

To work around, instead of storing CLightSceneNode pointers and sorting them, I store actual SLight objects. Then, my OnNodePreRender() becomes this:

Code: Select all

    // Called before the specified scene node is rendered
    virtual void OnNodePreRender(irr::scene::ISceneNode* node)
    {
        // Only calculate light distances during "solid" render pass
        if (m_current_render_pass != irr::scene::ESNRP_SOLID)
        {
            return;
        }
     
        // The "m_sorted_lights" vector is sorted here, by distance ... [0] is now the closest and [1] is the farthest from the node
 
        // Remove all lights from the driver
        m_scene->getVideoDriver()->deleteAllDynamicLights();
 
        s32 index;
 
        // Enable the closest light, make it yellow
        m_sorted_lights[0].m_light_data.DiffuseColor.set(1.0f, 1.0f, 0.0f);
        index = m_scene->getVideoDriver()->addDynamicLight(m_sorted_lights[0].m_light_data);
        m_scene->getVideoDriver()->turnLightOn(index, true);
 
        // Disable the farthest light, make it cyan
        m_sorted_lights[0].m_light_data.DiffuseColor.set(0.0f, 1.0f, 1.0f);
        index = m_scene->getVideoDriver()->addDynamicLight(m_sorted_lights[0].m_light_data);
        m_scene->getVideoDriver()->turnLightOn(index, true);
    }
This works! I'm actually removing all the lights from the driver and then re-adding them in sorted order. There doesn't seem to be a huge performance hit with this approach.

In case anyone comes across this and wonders why anyone would want to do this... My initial idea was to just choose the eight lights closest to the current node and turn them on, while turning all other lights off. That way, I could have any number of lights in the scene, but each node would only be lit by the eight closest. When I tried this approach, I could see really severe "popping" of the lights. As one light moved just a little farther away than another, it would instantly turn off and the other light would turn on. The effect was very ugly and not realistic at all.

So, I thought, I could interpolate the color of the eighth closest light. As the ninth closest light got closer and closer, I would fade the color of the eighth light out, gradually, as the ninth light faded in. This failed because it's too late to change light colors. But now, I can modify light colors by dealing directly with the SLight data instead of the CLightSceneNode data.

Thanks.
Post Reply