Rendering to texture , procedural texture generation

Problems building or running the engine, queries about how to use features etc.
User avatar
paul424
Gnome
Posts: 377
Joined: Thu May 24, 2012 7:16 pm
x 18

Rendering to texture , procedural texture generation

Post by paul424 »

Ogre Version: : 13 6 5
Operating System: :Linux Tumbleweed:
Render System: :OpenGL3+

Hello, what I try is the manual, procedural resource creation. I want a texture be generated from shader programs , to do that I created ORTHO camera, and mesh quad which is supposed to do :

Code: Select all

"        // Create the render texture
        perlinTexture = Ogre::TextureManager::getSingleton().createManual(
            "PerlinNoiseTexture",
            Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
            Ogre::TEX_TYPE_2D,
            4096, 4096,
            0,
            Ogre::PF_R8G8B8A8,
            Ogre::TU_RENDERTARGET
            );

    // Attach a camera and viewport to render the texture
    Ogre::RenderTexture* renderTexture = perlinTexture->getBuffer()->getRenderTarget();
    Ogre::Camera* camera = sceneManager->createCamera("PerlinNoiseCam");
    camera->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
    camera->setOrthoWindow(4096, 4096);
    getRenderWindow()->addViewport(camera);
    
    Ogre::Viewport* viewport = renderTexture->addViewport(camera);
    viewport->setClearEveryFrame(true);
    viewport->setBackgroundColour(Ogre::ColourValue::Blue);
    viewport->setOverlaysEnabled(false);
    
    // Create a quad
    Ogre::Plane plane(Ogre::Vector3::UNIT_Z, 0);
    Ogre::MeshManager::getSingleton().createPlane(
        "Plane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        plane, 4096, 4096, 1, 1, true, 1, 1, 1, Ogre::Vector3::UNIT_Y);

    Ogre::Entity* quadEntity = sceneManager->createEntity("Plane");
    quadEntity->setMaterialName("Cloud", "General");
    Ogre::SceneNode* quadNode = sceneManager->getRootSceneNode()->createChildSceneNode();
    quadNode->attachObject(quadEntity);

    quadNode->setPosition(0, 0, -1000);


    sceneManager->setAmbientLight(Ogre::ColourValue(.5, .5, .5));
    Ogre::LogManager::getSingleton().logMessage("Setup complete.");"

The main question here is why I cannot use the perlinTexture as a base for my material with a new Entity ?
It's not the fault of assigning the material with texture. cause the program works perfectly well for custom texture ( for example a genreated red one ... )
I have even tried to copy over the contents to a new texture -- freshTexture without success , it always crashes somewhere on rendering within the SceneManager code. Maybe the culprit is I try to use two OgreBites applications ? Please have a look at whole code here :

Code: Select all

#include <Ogre.h>
#include <OgreShaderGenerator.h>
#include <OgreApplicationContext.h>
#include <iostream>
Ogre::TexturePtr perlinTexture;

void setupTileMesh(Ogre::SceneManager* sceneMgr, const Ogre::String& meshName, Ogre::TexturePtr myTexture)
{
    // Bind the PerlinNoiseTexture to the CloudMaterial programmatically
    Ogre::MaterialPtr cloudMaterial = Ogre::MaterialManager::getSingleton().getByName("Fog");
    if (!cloudMaterial.isNull())
    {
        Ogre::Pass* pass = cloudMaterial->getTechnique(0)->getPass(0);
        Ogre::TextureUnitState* texState = pass->getTextureUnitState(0);

    if (texState != nullptr && !myTexture.isNull())
    {
        texState->setTexture(myTexture);
    }
    else
    {
        std::cerr << "Error: TextureUnitState or myTexture is null." << std::endl;
        return;
    }

    // Assuming the meshName points to a pre-loaded mesh
    Ogre::Entity* tileEntity = sceneMgr->createEntity(meshName);
    Ogre::SceneNode* tileNode = sceneMgr->getRootSceneNode()->createChildSceneNode();

    // Check and apply materials to submeshes
    if (tileEntity->getNumSubEntities() > 1)
    {
        // Apply DirtMaterial to the first submesh
        tileEntity->getSubEntity(0)->setMaterialName("Dirt");

        // Apply CloudMaterial to the second submesh
        tileEntity->getSubEntity(1)->setMaterial(cloudMaterial);


        // Apply CloudMaterial to the third submesh
        tileEntity->getSubEntity(2)->setMaterial(cloudMaterial); 
        
        
        // Debugging output to check submesh count
        std::cout << "Submesh count: " << tileEntity->getNumSubEntities() << std::endl;
    }
    else
    {
        std::cerr << "Error: Not enough submeshes in the entity." << std::endl;
        return;
    }

    tileNode->attachObject(tileEntity);
}
else
{
    std::cerr << "Error: Cloud material 'Fog' is null." << std::endl;
}
}


// Function to save PerlinNoiseTexture to an image file
void savePerlinNoiseTexture(Ogre::TexturePtr texture, const std::string& filename) {
    Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer();
    Ogre::Image image;

pixelBuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();

image.loadDynamicImage(static_cast<Ogre::uchar*>(pixelBox.data), texture->getWidth(), texture->getHeight(),
                       1, texture->getFormat(), true);

image.save(filename);

pixelBuffer->unlock();
}

class MyApplication : public OgreBites::ApplicationContext, public OgreBites::InputListener {
public:
    MyApplication() : OgreBites::ApplicationContext("MyApplication") {}

void setup() {
    OgreBites::ApplicationContext::setup();
    addInputListener(this);

    Ogre::Root* root = getRoot();
    Ogre::SceneManager* sceneManager = root->createSceneManager("OctreeSceneManager", "SceneManager");

    // Load resources
    Ogre::ConfigFile cf;
    cf.load("resources.cfg");

    Ogre::String name, locType;
    Ogre::ConfigFile::SectionIterator secIt = cf.getSectionIterator();
    while (secIt.hasMoreElements()) {
        Ogre::ConfigFile::SettingsMultiMap* settings = secIt.getNext();
        for (auto& pair : *settings) {
            locType = pair.first;
            name = pair.second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(name, locType);
        }
    }

    // Create the render texture
    perlinTexture = Ogre::TextureManager::getSingleton().createManual(
        "PerlinNoiseTexture",
        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        Ogre::TEX_TYPE_2D,
        4096, 4096,
        0,
        Ogre::PF_R8G8B8A8,
        Ogre::TU_RENDERTARGET
        );

    // Attach a camera and viewport to render the texture
    Ogre::RenderTexture* renderTexture = perlinTexture->getBuffer()->getRenderTarget();
    Ogre::Camera* camera = sceneManager->createCamera("PerlinNoiseCam");
    camera->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
    camera->setOrthoWindow(4096, 4096);
    getRenderWindow()->addViewport(camera);
    
    Ogre::Viewport* viewport = renderTexture->addViewport(camera);
    viewport->setClearEveryFrame(true);
    viewport->setBackgroundColour(Ogre::ColourValue::Blue);
    viewport->setOverlaysEnabled(false);
    
    // Create a quad
    Ogre::Plane plane(Ogre::Vector3::UNIT_Z, 0);
    Ogre::MeshManager::getSingleton().createPlane(
        "Plane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        plane, 4096, 4096, 1, 1, true, 1, 1, 1, Ogre::Vector3::UNIT_Y);

    Ogre::Entity* quadEntity = sceneManager->createEntity("Plane");
    quadEntity->setMaterialName("Cloud", "General");
    Ogre::SceneNode* quadNode = sceneManager->getRootSceneNode()->createChildSceneNode();
    quadNode->attachObject(quadEntity);

    quadNode->setPosition(0, 0, -1000);


    sceneManager->setAmbientLight(Ogre::ColourValue(.5, .5, .5));
    Ogre::LogManager::getSingleton().logMessage("Setup complete.");
    
}
};


class MyApplication2 : public OgreBites::ApplicationContext, public OgreBites::InputListener {
public:
    MyApplication2() : OgreBites::ApplicationContext("MyApplication2") {}

void setup()
{
    // Call the base class setup
    OgreBites::ApplicationContext::setup();

    // Load resources
    Ogre::ConfigFile cf;
    cf.load("resources.cfg");

    Ogre::String name, locType;
    Ogre::ConfigFile::SectionIterator secIt = cf.getSectionIterator();
    while (secIt.hasMoreElements()) {
        Ogre::ConfigFile::SettingsMultiMap* settings = secIt.getNext();
        for (auto& pair : *settings) {
            locType = pair.first;
            name = pair.second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(name, locType);
        }
    }

    
    // Register as an input listener
    addInputListener(this);

    // Get the root, render window, and scene manager
    Ogre::Root* root = getRoot();
    Ogre::RenderWindow* window = getRenderWindow();
    Ogre::SceneManager* scnMgr = root->createSceneManager("OctreeSceneManager","SceneManager2");


    // Set up camera
    Ogre::Camera* cam = scnMgr->createCamera("MainCamera");
    cam->setAutoAspectRatio(true);
    cam->setNearClipDistance(5);

    // Create a viewport
    Ogre::Viewport* vp = window->addViewport(cam);
    vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0));
    cam->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));

    // Set up a scene
    Ogre::SceneNode* rootNode = scnMgr->getRootSceneNode();
    Ogre::SceneNode* camNode = rootNode->createChildSceneNode();
    camNode->setPosition(Ogre::Vector3(5,5,5));
    camNode->attachObject(cam);
    camNode->lookAt(Ogre::Vector3(0,0,0), Ogre::Node::TS_WORLD);



    scnMgr->setAmbientLight(Ogre::ColourValue(.5, .5, .5));
    Ogre::Light* light = scnMgr->createLight("MainLight");
    Ogre::SceneNode* lightNode = rootNode->createChildSceneNode();
    lightNode->setPosition(2, 8, 5);
    lightNode->attachObject(light);

    
    

}
};
Ogre::TexturePtr createRedTexture(Ogre::String textureName, int width, int height)
{
    // Create a manual texture
    Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(
        textureName,
        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        Ogre::TEX_TYPE_2D,
        width, height,
        0,
        Ogre::PF_R8G8B8A8,
        Ogre::TU_DEFAULT
        );

if (texture.isNull())
{
    std::cerr << "Error: Texture creation failed." << std::endl;
    return Ogre::TexturePtr();
}

// Lock the texture buffer to fill it with red color
Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer();
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();

uint8_t* pDest = static_cast<uint8_t*>(pixelBox.data);

// Fill the texture with red color
for (size_t y = 0; y < height; ++y)
{
    for (size_t x = 0; x < width; ++x)
    {
        size_t pixelIndex = (y * width + x) * 4;
        pDest[pixelIndex] = 255;     // Red
        pDest[pixelIndex + 1] = 0;   // Green
        pDest[pixelIndex + 2] = 0;   // Blue
        pDest[pixelIndex + 3] = 255; // Alpha
    }
}

// Unlock the buffer to apply changes
pixelBuffer->unlock();

return texture;
}



int main(int argc, char** argv) {

Ogre::TexturePtr freshTexture;
// Prepare a PixelBox for copying the content
    Ogre::PixelBox srcPixelBox(
        Ogre::Box(0, 0, 4096, 4096),
        Ogre::PF_R8G8B8A8,
        new uint8_t[4096 * 4096 * 4] // Allocate memory for the pixel data
        );

// Cleanup the allocated memory for the PixelBoxes

try
{
    MyApplication app;
    app.initApp();

    // Render multiple frames to ensure the texture is correctly updated
    app.getRoot()->startRendering();

// Create a fresh texture to copy the content
        freshTexture = Ogre::TextureManager::getSingleton().createManual(
            "FreshPerlinTexture",
            Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
            Ogre::TEX_TYPE_2D,
            4096, 4096,
            0,
            Ogre::PF_R8G8B8A8,
            Ogre::TU_DEFAULT
            );


// Copy the content from perlinTexture to the source PixelBox
        perlinTexture->getBuffer()->blitToMemory(srcPixelBox);

// Copy the content from the source PixelBox to the destination PixelBox
        freshTexture->getBuffer()->blitFromMemory(srcPixelBox);
        delete[] static_cast<uint8_t*>(srcPixelBox.data);

    app.closeApp();
}
catch (const Ogre::Exception& e) {
    std::cerr << "An exception has occurred: " << e.getFullDescription().c_str() << std::endl;
    return 1;
}


MyApplication2 app2;
app2.initApp();
Ogre::TexturePtr redTexture = createRedTexture("myRedTexture", 4096, 4096);
setupTileMesh(app2.getRoot()->getSceneManager("SceneManager2"), "FogOfWar.mesh", freshTexture);
app2.getRoot()->startRendering();
app2.closeApp();


return 0;
}

User avatar
paul424
Gnome
Posts: 377
Joined: Thu May 24, 2012 7:16 pm
x 18

Re: Rendering to texture , procedural texture generation

Post by paul424 »

For some reason I should have used Ogre::RenderTarget::update() to obtain a texture , instead of Ogre::Root::startRendering() or renderOneFrame() !

update()
virtual void Ogre::RenderTarget::update ( bool swapBuffers = true )
virtual

Tells the target to update it's contents.

Remarks
If OGRE is not running in an automatic rendering loop (started using Root::startRendering), the user of the library is responsible for asking each render target to refresh. This is the method used to do this. It automatically re-renders the contents of the target using whatever cameras have been pointed at it (using Camera::setRenderTarget).

Code: Select all

This allows OGRE to be used in multi-windowed utilities and for contents to be refreshed only when required, rather than constantly as with the automatic rendering loop. 

Parameters
swapBuffers For targets that support double-buffering, if set to true, the target will immediately swap it's buffers after update. Otherwise, the buffers are not swapped, and you have to call swapBuffers yourself sometime later. You might want to do this on some rendersystems which pause for queued rendering commands to complete before accepting swap buffers calls - so you could do other CPU tasks whilst the queued commands complete. Or, you might do this if you want custom control over your windows, such as for externally created windows.

Reimplemented in Ogre::D3D9MultiRenderTarget, and Ogre::D3D9RenderTexture.