英语水平有限,欢迎大家批评指正
本文并没有将原文全部翻译,只是将其中的一些知识点翻译总结了一下,想要查看详细讲解的话,可以到原文处看一下,附上英文原文地址:http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Intermediate+Tutorial+7&structure=Tutorials
Introduction
本教程中我们将学习渲染到纹理的基本知识,该技术对很多特效特别是与着色器一起制作的特效是很重要的。渲染到纹理RTT的思想很简单,你只需要把场景的渲染输出数据发送给一个纹理,而不是把它发送给你的渲染窗口。然后这个纹理就可以像一张普通的纹理一样被硬件驱动使用了。
Render to texture
Creating the render textures
首先创建一个纹理:
Ogre::TexturePtr rtt_texture = Ogre::TextureManager::getSingleton().createManual("RttTex", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWindow->getWidth(), mWindow->getHeight(), 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
(mWindow是你的Ogre::RenderWindow的指针)
createManual()的第一个参数是纹理的名字,通常命名为RttTex。第二个参数指定了资源组,第三个参数为纹理类型,第四、五个参数为纹理的宽和高。你还需要传递mip maps的个数以及纹理的格式和一个使用标记(usage flag)。有很多不同的纹理格式,最简单的是PF_R8G8B8,它可以创建一个24为的RBG纹理。如果你需要一个alpha通道,那么应该用PF_R8G8B8A8格式。现在我们需要获取纹理的渲染目标,以设置一些参数并且稍后加一个RenderTargetListener。
Ogre::RenderTexture *renderTexture = rtt_texture->getBuffer()->getRenderTarget();
renderTexture->addViewport(mCamera);
renderTexture->getViewport(0)->setClearEveryFrame(true);
renderTexture->getViewport(0)->setBackgroundColour(Ogre::ColourValue::Black);
renderTexture->getViewport(0)->setOverlaysEnabled(false);
得到渲染纹理后,我们需要给他添加一个视口。该视口将被渲染纹理所填充。每帧视口都要clear自己,设背景色为黑色并使所有overlays不可用。
Write texture to file
现在我们要把我们创建的渲染纹理(RenderTexture)的内容保存到一个文件中,RenderTextures继承自RenderTarget,并有一个函数能将渲染纹理(RenderTexture)的内容保存到文件中(我们也可以用RenderWindwos来做)。但在保存内容到文件之前,你得更新你的渲染纹理(RenderTexture)。你可以手动的通过update()函数来更新渲染纹理(RenderTexture)或让程序通过调用setAutoUpdata()函数自动更新渲染纹理(RenderTexture)。
// Either this way
renderTexture->setAutoUpdated(true);
// or this way
renderTexture->update();
// Now save the contents
renderTexture->writeContentsToFile("start.png");
运行程序后,你会在你的exe程序所在的文件夹中找到一个a.png文件。
Implementing a mini screen
General
现在我们要在程序窗口的右下角添加一个小窗口,为此我们添加一个以我们的渲染纹理(RenderTexture)作为纹理的2D矩形(Rectangle2D)到场景中。我们场景将被显示两次:一次渲染到渲染唱k,另一次作为我们的2D矩形(Rectangle2D)的纹理。
用这个方法还可以做一个TV屏幕(或CCTV摄像机等),你可以通过放置一个摄像机,创建一个渲染纹理(RenderTexture)并显示到TV屏幕上,来显示场景的其他部分。
Setting up the rectangle
创建一个Ogre::Rectangle2D:
Ogre::Rectangle2D *mMiniScreen = new Ogre::Rectangle2D(true);
mMiniScreen->setCorners(0.5f, -0.5f, 1.0f, -1.0f);
mMiniScreen->setBoundingBox(Ogre::AxisAlignedBox(-100000.0f* Ogre::Vector3::UNIT_SCALE, 100000.0f * Ogre::Vector3::UNIT_SCALE));
第一行设参数为true来生成纹理坐标,后面用于将纹理绘制到矩形上。第二行指定矩形的角,左为-1.0、右为+1.0、上为+1.0、下为-1.0。这样我们的矩形就正好在程序窗口的右下角。我们还给矩形一个很大的绑定盒以防止它被剔除,当我们没有面向他的场景节点时。你还可以用AxisAlignedBox::setInfinite(),而不用手动设置绑定盒的大小,但过去这会有点问题,所以手动设置绑定盒是最快的方法。
现在我们已经创建了矩形,然后需要把它绑定到一个场景节点上。
Ogre::SceneNode*miniScreenNode= mSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode");
miniScreenNode->attachObject(mMiniScreen);
现在运行你的程序,你会看到在程序窗口的右下角有一个白色的矩形。
Creating a material from scratch
下面要显示我们前面在矩形上创建的渲染纹理(RenderTexture),我们得为他创建一个材质。我们可以写一个材质脚本或直接在运行时的代码中来创建材质。
Ogre::MaterialPtr renderMaterial = Ogre::MaterialManager::getSingleton().create("RttMat", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::Technique* matTechnique = renderMaterial->createTechnique();
matTechnique->createPass();
renderMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
renderMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex");
第一行创建一个空的材质,后面两行添加一个technique和一个pass,第三行我们使光源不可用以防止我们小屏幕比纹理还要暗,最后一行我们通过传送我们前面创建的渲染纹理(RenderTexture)创建了一个新的TextureUnitState。
现在我们可以将这个材质用到我们的小屏幕上了:
mMiniScreen->setMaterial("RttMat");
运行程序你会看到一个不是你想要的效果:小屏幕自己有一个小屏幕。要解决这个问题,OGRE引进了RenderTargetListener。
Usage of Render TargetListener
General
很多情况下只有场景中的某些对象需要RTT,例如我们只需要一个包含了不带小屏幕的程序窗口输出数据的纹理,这个纹理是用于我们的小屏幕的。所有每次在窗口输出数据保存到纹理前,我们得把小屏幕隐藏起来。这也是RenderTargetListener插入(come in)的地方。该侦听器有两个重要的函数:preRenderTargetUnpdate()和postRenderTargetUpdate()。正如其名,第一个函数是在渲染纹理(RenderTexture)被填充时被该侦听器自动调用的(这里我们可以隐藏小窗口),第二个函数是在渲染纹理(RenderTexture)被填充完后被该侦听器自动调用的(这里我们再次显示小窗口)。
Implementing a RenderTargetListener
写一个RenderTargetListener很简单,首先让我们的程序的类继承RenderTargetListener让它成为自己的侦听器。然后我们只要在preRenderTargetUnpdate()和postRenderTargetUpdate()函数中隐藏和显示我们的小屏幕就行了。所有你的程序的类应该如下所示:
IntermediateTutorial7.h
class IntermediateTutorial7 : public BaseApplication, public Ogre::RenderTargetListener
{
public:
IntermediateTutorial7(void);
virtual ~IntermediateTutorial7(void);
protected:
virtual void createScene(void);
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
virtual void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
virtual void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
Ogre::MovablePlane* mPlane;
Ogre::Entity* mPlaneEnt;
Ogre::SceneNode* mPlaneNode;
//This should be taken out of the createScene member and brought here
Ogre::Rectangle2D* mMiniScreen;
};
IntermediateTutorial7.cpp
void IntermediateTutorial7::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
{
mMiniScreen->setVisible(false);
}
void IntermediateTutorial7::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
{
mMiniScreen->setVisible(true);
}
最后一步给渲染纹理(RenderTexture)添加侦听器,因为程序的类继承自RenderTargetListener,所有我们只把this指针作为参数在createScene函数的最后传给addListener函数:
renderTexture->addListener(this);
运行程序,现在就可以看到你的程序中有一个小屏幕了。
RTTs and shaders
Passing a RTT to a shader
因为RTTs和着色器(shader)经常一起使用,所有你需要知道如何把渲染纹理(RenderTexture)传给着色器(shader)。最简单的例子就是,运行时你不需要为着色器修改纹理。如果不需要,你只需要告诉材质脚本运行时创建的纹理的名字,本例中是TttTex。所有你的texture_unit如下所示:
texture_unit
{
texture RttTex
}
如果运行时着色器要使用的纹理要改变,添加如下代码:
Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().getByName("Sepia"); material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("OtherRttTex");
第一行获取一个指向材质的指针,第二行将纹理改成所想要的名字。现在你可以用这两种方法之一在材质脚本中设置正确的纹理名,然后你可以用下面的代码在cg着色器中获取纹理了:
uniform sampler2D SceneSampler : register(s0)
编译运行你的程序!