Search code examples
shaderhlslogrepixel-shader

HLSL How can one pass data between shaders / read existing colour value?


I have 2 HLSL ps2.0 shaders. Simplified, they are:

Shader 1

  • Reads texture
  • Outputs colour value based on this texture

Shader 2

  • Problem: Need to read in the colour from Shader 1
  • Outputs the final colour which is a function of the input colour

(They need to be different shaders as I've reached the maximum vertex-shader outputs for 1 shader)


My problem is I cannot work out how Shader 2 can access the existing fragment/pixel colour.

Knowing how to do any of these things with HLSL would solve my problem;

  • Read existing pixel colour (I don't think this is possible)
  • Pass result of Shader 1 to Shader 2 as a float4
  • Render result of Shader 1 as a texture in memory, and have Shader 2 read that in

Solution

  • Compositor scripts seem to be only for fullscreen (or more accurately, full viewport) effects.

    Render-to-texture is the way to go. It's not necessarily accomplished with compositor scripts.

    This thread of mine on the Ogre forums goes into more detail;

       Ogre::Root r(...);
       Ogre::RenderWindow* window = r.createRenderWindow(...);
       //...
       Ogre::SceneManager* sm = r.createSceneManager(Ogre::ST_GENERIC, "sm");
       //...
    
       //Main scene camera
       Ogre::Camera* c = sm->createCamera("camera");
       {
           c->setNearClipDistance(5);
           Ogre::Viewport* v = window->addViewport(c);
           v->setBackgroundColour (Ogre::ColourValue(0, 0, 0));
           c->setAspectRatio (static_cast<double> (v->getActualWidth ()) / v->getActualHeight ());
       }
    
       //RTT
       Ogre::TexturePtr ptrTexture = Ogre::TextureManager::getSingleton().createManual(
           "RttTex",
           Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
           Ogre::TEX_TYPE_2D,
           512,//window->getWidth(),
           512,//window->getHeight(),
           0, //MIP_DEFAULT?
           Ogre::PF_R8G8B8,
           Ogre::TU_RENDERTARGET,
           0
       );
       Ogre::RenderTexture* renderTexture = ptrTexture->getBuffer()->getRenderTarget();
       renderTexture->setAutoUpdated(true);
    
       //Create material to use with rect
       {
           //You should replace this with the material you wish to render to texture
           //It can be defined in c++ (as this is) or in a material script
           Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create("material", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
           Ogre::Technique* tech = material->createTechnique();
           tech->createPass();
           material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
           material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
           material->getTechnique(0)->getPass(0)->createTextureUnitState("my_square_texture.dds");
       }
    
       //Create rect2D in yz plane to which we will draw our textures
       //Most likely you'll wish to reposition the node so it's offscreen
       const static float r_dimension = 1000.0;
       Ogre::SceneNode* rect_node = sm->getRootSceneNode()->createChildSceneNode("rect_node");
       {
           Ogre::ManualObject *rect = sm->createManualObject("rect");
           rect->begin("material", Ogre::RenderOperation::OT_TRIANGLE_FAN);
           rect->position(0, r_dimension, r_dimension);
           rect->textureCoord(0,0);
           rect->normal(0, 1, 0);
           rect->position(0, -r_dimension, r_dimension);
           rect->textureCoord(0,1);
           rect->normal(0, 1, 0);
           rect->position(0, -r_dimension, -r_dimension);
           rect->textureCoord(1,1);
           rect->normal(0, 1, 0);
           rect->position(0, r_dimension, -r_dimension);
           rect->textureCoord(1,0);
           rect->normal(0, 1, 0);
           rect->end();
           rect_node->attachObject(rect);
       }
    
       //Create camera, make it look at this rect2D
       Ogre::Camera* rtt_cam = sm->createCamera("rtt_cam");
    
       //Use same FOV as main camera
       Ogre::Radian fov_y = c->getFOVy();
       rtt_cam->setFOVy(fov_y);
    
       //Position the camera such that the texture fills the viewpoint
       {
           //Angle from normal (ie, "vector origin->camera") to to top of tecture is FOV/2
           //Distance origin to top of texture is r_dimension
           double cam_to_rect_distance = r_dimension/tan((fov_y.valueRadians())/2);
           rtt_cam->setPosition(cam_to_rect_distance, 0, 0);
           rtt_cam->lookAt(rect_node->getPosition());
       }
    
       //Debug using main window
       //window->addViewport(rtt_cam);
    
       //Write to RTT
       Ogre::Viewport* v = renderTexture->addViewport(rtt_cam);
    
       v->setClearEveryFrame(true); //You may wish to set this to false and render only when your material updates/changes
       v->setBackgroundColour(Ogre::ColourValue::Blue); //Debug colour. If we see blue border in RTT our cam position is wrong.
       v->setOverlaysEnabled(false); //We don't want overlays to show up on the RTT
    
       //TEMP Create debug screen (lifted from Ogre Tutorial 7)
       //Draws the result of RTT onscreen picture-in-picture
       {
           Ogre::Rectangle2D *miniScreen = new Ogre::Rectangle2D(true);
           miniScreen->setCorners(0.5f, -0.5f, 1.0f, -1.0f);
           //miniScreen->setBoundingBox(Ogre::AxisAlignedBox(-100000.0f * Ogre::Vector3::UNIT_SCALE, 100000.0f * Ogre::Vector3::UNIT_SCALE));
           Ogre::SceneNode* miniScreenNode = sm->getRootSceneNode()->createChildSceneNode("MiniScreenNode");
           miniScreenNode->attachObject(miniScreen);
    
           //Create material to read result of Rtt, purely for debug purposes
           Ogre::MaterialPtr screenMaterial = Ogre::MaterialManager::getSingleton().create("ScreenMatt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
           Ogre::Technique* screenTechnique = screenMaterial->createTechnique();
           screenTechnique->createPass();
           screenMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
           screenMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex");
    
           miniScreen->setMaterial("ScreenMatt");
    
           //TODO ideally we'd have render target listeners call setVisible(false) on pre update and  setVisible(true) post update,
           //so we don't get the infinite line picture-in-picture-in-picture in the preview window.
       }
    
       //Now you can bind your shader's material script to the rtt
    {
        Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().getByName("your_material_name");
        Ogre::Technique *technique = material->getTechnique(0);
        Ogre::Pass *pass = technique->getPass(0);
        Ogre::TextureUnitState *tunit = pass->getTextureUnitState("your_materials_tunit_name");
        tunit->setTextureName("Rtt");
    }
    
       //...
    
       while (! window->isClosed ()) {
           //...
           r.renderOneFrame();
       }