资源描述
实验六:凹凸纹理映射技术
一、实验目的
掌握凹凸纹理映射的原理,熟悉Ogre中纹理映射的使用方法。
二、实验仪器
pc、vs2005
三、实验原理及过程
1、网上检索凹凸纹理映射相关技术
凹凸纹理映射是一种纹理混合方法,它可以创建三维物体复杂的纹理外观表面。普通的纹理映射只能模拟比较平滑的三维物体表面,难以显示表面高低起伏、凹凸不平的效果。凹凸纹理映射能够通过一张表示物体表面凹凸程度的高度图(称为凹凸纹理),对另一张表示物体表面环境映射的纹理图的纹理坐标进行相应的干扰,经过干扰的纹理坐标将应用于环境映射,从而产生凹凸不平的显示效果。凹凸纹理映射通常由三张纹理映射图组成,第一张纹理图表示物体表面原始纹理颜色,第二张凹凸纹理图表示物体表面凹凸的高度起伏值,用来对下一张环境纹理图坐标进行干扰,第三张纹理图表示周围镜面反射或漫反射光照的环境光照映射图。让我们来看看一个粗糙的表面。
从远处看,你判断这个物体是粗糙的的唯一证据是在它表面上下的亮度有改变。你的大脑能够获得这些亮暗不一的图案信息,然后判断出它们是表面中有凹凸的部位。左边的一幅图就说明了这一点。你可以发现它是一个浮雕式的表面。一些矩型和字母被印入表面,但是它们摸上去就像是一个隐藏的监控器的玻璃。如果这个图像是在适当的位置上,那么它除了改变亮度,不需要再做任何其他的工作。那么你也许会问:我是怎么知道哪些点要亮,哪些点要暗呢?这不难。绝大多数人生活在这样一种环境下——这个环境的大多数光源来自上方(译者注:比如白天主要的光来自太阳,夜晚主要的光来自天花板上的日光灯)。所以向上倾的地方就会更亮,而向下倾的地方就会更暗。所以这种现象使你的眼睛看到一个物体上亮暗区域时,可以判断出它的凹凸情况。相对亮的块被判断是面向上的,相对暗的块被判断是面向下的。所以我只需要给物体上的线条简单得上色。如果你想要更多的证据,这里还有一幅几乎相同的图,不同于前的是它旋转了180度。所以它是前一幅图倒转的图像。那些先前看起来是凹进去的区域,现在看起来是凸出来的了。
凹凸映射(凹凸纹理)Bump Mapping
这个时候你的大脑并没有被完全欺骗,你脑中存留的视觉印象使你仍然有能力判断出这是前一幅图,只是它的光源变了,是从小往上照的你的大脑可能强迫性地判断出它是第一幅图。事实上,你只要始终盯着它,并且努力地想像着光是从右下方向照射的,你就会理解它是凹的(译者注:因为日常生活的习惯,你会很容易把这些图形判断成凸出的图形,但是因为有了上一幅对照图的印象,你可能才会特别注意到这些图块其实还是凹入的,只是判断方法不符合我们日常生活习惯,因为这时大多数光不是从上方照射,而是从下往上照射)。凹凸纹理映射技术可以用来模拟粗糙物体表面凹凸不平的细节,如:橘子、草莓、树皮等。凹凸纹理映射最早只能用于离线绘制系统,随着图形硬件的发展,他已经成为游戏引擎中不可缺少的部分。最早的凹凸纹理映射使用一个高度图和曲面参数(通常是纹理坐标)的偏导数计算扰动后的法向。这个偏导数表明了物体表面改变的尺度。理论上,凹凸映射中涉及的法向操作实在像素层次上的。
凹凸纹理映射的步骤如下:
(1)计算每个顶点处的T、B、N,并计算切平面坐标的矩阵。
(2)根据该矩阵将光源变换到切平面空间,变换后光源的x、y即顶点的相邻点。
(3)从凹凸纹理中取出顶点和相邻点处的高度,根据两者的法向量计算出顶点的新法向,再根据新法向量进行光照明计算。
2、利用Ogre实现凹凸纹理映射描述程序实现时的思路包括对每个调用的API进行详细说明
1)顶点渲染主函数:VS_OUTPUT VS(float4 Pos : POSITION, float2 Tex : TEXCOORD, float3 Normal : NORMAL, float3 Tangent : TANGENT){ ……}
2)像素渲染器主函数:PS_OUTPUT PS(float2 Tex: TEXCOORD0, float3 Light : TEXCOORD1, float3 View : TEXCOORD2, float3 Att : TEXCOORD3){ ……}
四、实验结果
五、实验心得
本次实验主要掌握凹凸纹理映射的原理,熟悉Ogre中纹理映射的使用方法;凹凸纹理映射是一种纹理混合方法,它可以实现三维物体复杂的纹理表面,普通的纹理映射只能模拟比较平滑的三维物体表面,而凹凸纹理映射经相应的技术可以产生凹凸不平的显示效果,凹凸纹理映射技术可以用来模拟粗糙物体表面凹凸不平的细节;凹凸映射和纹理映射非常相似。然而,纹理映射是把颜色加到多边形上,而凹凸映射是把
粗糙信息加到多边形上。这在多边形的视觉上会产生很吸引人的效果。我们只需要添加一点信息到本来需要使用大量多边形的物体上。需要注意的是这个物体是平的,但是它看起来却是粗糙不平的。让我们来看看左边的那个立方体。如果你很近地观察它时,你会发现它上面的很多细节。它看起来好像是由成千上万个多边形构成的,其实它只是由6个矩形构成。通过本次实验了解凹凸纹理映射的原理,学会在ogre中对三维物体使用纹理映射的方法,对ogre的了解更进一步,收获颇多。
六、 主要代码
Dot3Bump.h
#ifndef __Dot3Bump_H__
#define __Dot3Bump_H__
#include "SdkSample.h"
using namespace Ogre;
using namespace OgreBites;
class _OgreSampleClassExport Sample_Dot3Bump : public SdkSample
{
public:
Sample_Dot3Bump()
: mMoveLights (true)
{
mInfo["Title"] = "Bump Mapping";
mInfo["Description"] = "Shows how to use the dot product blending operation and normalization cube map "
"to achieve a bump mapping effect. Tangent space computations made through the guide of the tutorial "
"on bump mapping from http://users.ox.ac.uk/~univ1234 by paul.baker@univ.ox.ac.uk.";
mInfo["Thumbnail"] = "thumb_bump.png";
mInfo["Category"] = "Lighting";
mInfo["Help"] = "Left click and drag anywhere in the scene to look around. Let go again to show "
"cursor and access widgets. Use WASD keys to move.";
}
StringVector getRequiredPlugins()
{
StringVector names;
names.push_back("Cg Program Manager");
return names;
}
void testCapabilities(const RenderSystemCapabilities* caps)
{
if (!caps->hasCapability(RSC_VERTEX_PROGRAM) || !(caps->hasCapability(RSC_FRAGMENT_PROGRAM)))
{
OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your graphics card does not support vertex and fragment programs, "
"so you cannot run this sample. Sorry!", "Dot3BumpSample::testCapabilities");
}
if (!GpuProgramManager::getSingleton().isSyntaxSupported("arbfp1") &&
!GpuProgramManager::getSingleton().isSyntaxSupported("ps_2_0"))
{
OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support shader model 2, "
"so you cannot run this sample. Sorry!", "Dot3BumpSample::testCapabilities");
}
}
bool frameRenderingQueued(const FrameEvent& evt)
{
if (mMoveLights)
{
// rotate the light pivots
mLightPivot1->roll(Degree(evt.timeSinceLastFrame * 30));
mLightPivot2->roll(Degree(evt.timeSinceLastFrame * 10));
}
return SdkSample::frameRenderingQueued(evt); // don't forget the parent class updates!
}
void itemSelected(SelectMenu* menu)
{
if (menu == mMeshMenu)
{
// change to the selected entity
mObjectNode->detachAllObjects();
mObjectNode->attachObject(mSceneMgr->getEntity(mMeshMenu->getSelectedItem()));
// remember which material is currently selected
int index = std::max<int>(0, mMaterialMenu->getSelectionIndex());
// update the material menu's options
mMaterialMenu->setItems(mPossibilities[mMeshMenu->getSelectedItem()]);
mMaterialMenu->selectItem(index); // select the material with the saved index
}
else
{
// set the selected material for the active mesh
((Entity*)mObjectNode->getAttachedObject(0))->setMaterialName(menu->getSelectedItem());
}
}
void checkBoxToggled(CheckBox* box)
{
if (StringUtil::startsWith(box->getName(), "Light", false))
{
// get the light pivot that corresponds to this checkbox
SceneNode* pivot = box->getName() == "Light1" ? mLightPivot1 : mLightPivot2;
SceneNode::ObjectIterator it = pivot->getAttachedObjectIterator();
while (it.hasMoreElements()) // toggle visibility of light and billboard set
{
MovableObject* o = it.getNext();
o->setVisible(box->isChecked());
}
}
else if (box->getName() == "MoveLights")
{
mMoveLights = !mMoveLights;
}
}
protected:
void setupContent()
{
// create our main node to attach our entities to
mObjectNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
setupModels();
setupLights();
setupControls();
mCamera->setPosition(0, 0, 500);
setDragLook(true);
}
void loadResources()
{
#ifdef USE_RTSHADER_SYSTEM
ResourceGroupManager& rgm = ResourceGroupManager::getSingleton();
Ogre::StringVector groupVector = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
Ogre::StringVector::iterator itGroup = groupVector.begin();
Ogre::StringVector::iterator itGroupEnd = groupVector.end();
Ogre::String shaderCoreLibsPath;
for (; itGroup != itGroupEnd; ++itGroup)
{
Ogre::ResourceGroupManager::LocationList resLocationsList = Ogre::ResourceGroupManager::getSingleton().getResourceLocationList(*itGroup);
Ogre::ResourceGroupManager::LocationList::iterator it = resLocationsList.begin();
Ogre::ResourceGroupManager::LocationList::iterator itEnd = resLocationsList.end();
bool coreLibsFound = false;
// Find the location of the core shader libs
for (; it != itEnd; ++it)
{
if ((*it)->archive->getName().find("RTShaderLib") != Ogre::String::npos)
{
shaderCoreLibsPath = (*it)->archive->getName() + "/";
coreLibsFound = true;
break;
}
}
// Core libs path found in the current group.
if (coreLibsFound)
break;
}
// Create the resource group of the RT Shader System.
rgm.createResourceGroup("RTShaderSystemMaterialsGroup");
rgm.addResourceLocation(shaderCoreLibsPath + "materials", "FileSystem", "RTShaderSystemMaterialsGroup");
rgm.initialiseResourceGroup("RTShaderSystemMaterialsGroup");
rgm.loadResourceGroup("RTShaderSystemMaterialsGroup", true);
#endif
}
void unloadResources()
{
#ifdef USE_RTSHADER_SYSTEM
ResourceGroupManager& rgm = ResourceGroupManager::getSingleton();
// Destroy the resource group of the RT Shader System
rgm.destroyResourceGroup("RTShaderSystemMaterialsGroup");
#endif
}
void setupModels()
{
StringVector matNames;
matNames.push_back("Examples/BumpMapping/MultiLight");
matNames.push_back("Examples/BumpMapping/MultiLightSpecular");
matNames.push_back("Examples/OffsetMapping/Specular");
matNames.push_back("Examples/ShowUV");
matNames.push_back("Examples/ShowNormals");
matNames.push_back("Examples/ShowTangents");
#ifdef USE_RTSHADER_SYSTEM
matNames.push_back("RTSS/NormalMapping_SinglePass");
matNames.push_back("RTSS/NormalMapping_MultiPass");
#endif
mPossibilities["ogrehead.mesh"] = matNames;
mPossibilities["knot.mesh"] = matNames;
matNames.clear();
matNames.push_back("Examples/Athene/NormalMapped");
matNames.push_back("Examples/Athene/NormalMappedSpecular");
matNames.push_back("Examples/Athene/NormalMappedSpecular");
matNames.push_back("Examples/ShowUV");
matNames.push_back("Examples/ShowNormals");
matNames.push_back("Examples/ShowTangents");
#ifdef USE_RTSHADER_SYSTEM
matNames.push_back("RTSS/Athene/NormalMapping_SinglePass");
matNames.push_back("RTSS/Athene/NormalMapping_MultiPass");
#endif
mPossibilities["athene.mesh"] = matNames;
for (std::map<String, StringVector>::iterator it = mPossibilities.begin(); it != mPossibilities.end(); it++)
{
// load each mesh with non-default hardware buffer usage options
MeshPtr mesh = MeshManager::getSingleton().load(it->first, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
// build tangent vectors for our mesh
unsigned short src, dest;
if (!mesh->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
{
mesh->buildTangentVectors(VES_TANGENT, src, dest);
// this version cleans mirrored and rotated UVs but requires quality models
// mesh->buildTangentVectors(VES_TANGENT, src, dest, true, true);
}
// create an entity from the mesh and set the first available material
Entity* ent = mSceneMgr->createEntity(mesh->getName(), mesh->getName());
ent->setMaterialName(it->second.front());
}
}
void setupLights()
{
mSceneMgr->setAmbientLight(ColourValue::Black); // disable ambient lighting
// create pivot nodes
mLightPivot1 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mLightPivot2 = mSceneMgr->getRootSceneNode()->createChildSceneNode();
Light* l;
BillboardSet* bbs;
// create white light
l = mSceneMgr->createLight();
l->setPosition(200, 0, 0);
l->setDiffuseColour(1, 1, 1);
l->setSpecularColour(1, 1, 1);
// create white flare
bbs = mSceneMgr->createBillboardSet();
bbs->setMaterialName("Examples/Flare");
bbs->createBillboard(200, 0, 0)->setColour(ColourValue::White);
mLightPivot1->attachObject(l);
mLightPivot1->attachObject(bbs);
// create red light
l = mSceneMgr->createLight();
l->setPosition(40, 200, 50);
l->setDiffuseColour(1, 0, 0);
l->setSpecularColour(1, 0.8, 0.8);
// create white flare
bbs = mSceneMgr->createBillboardSet();
bbs->setMaterialName("Examples/Flare");
bbs->createBillboard(50, 200, 50)->setColour(ColourValue::Red);
mLightPivot2->attachObject(l);
mLightPivot2->attachObject(bbs);
}
void setupControls()
{
mTrayMgr->showCursor();
// make room for the controls
mTrayMgr->showLogo(TL_TOPRIGHT);
mTrayMgr->showFrameStats(TL_TOPRIGHT);
mTrayMgr->toggleAdvancedFrameStats();
// create a menu to choose the model displayed
mMeshMenu = mTrayMgr->createLongSelectMenu(TL_BOTTOM, "Mesh", "Mesh", 370, 290, 10);
for (std::map<String, StringVector>::iterator it = mPossibilities.begin(); it != mPossibilities.end(); it++)
mMeshMenu->addItem(it->first);
// create a menu to choose the material used by the model
mMaterialMenu = mTrayMgr->createLongSelectMenu(TL_BOTTOM, "Material", "Material", 370, 290, 10);
// create checkboxes to toggle lights
mTrayMgr->createCheckBox(TL_TOPLEFT, "Light1", "Light A")->setChecked(true, false);
mTrayMgr->createCheckBox(TL_TOPLEFT, "Light2", "Light B")->setChecked(true, false);
mTrayMgr->createCheckBox(TL_TOPLEFT, "MoveLights", "Move Lights")->setChecked(true, false);
// a friendly reminder
StringVector names;
names.push_back("Help");
mTrayMgr->createParamsPanel(TL_TOPLEFT, "Help", 100, names)->setParamValue(0, "H/F1");
mMeshMenu->selectItem(0); // select first mesh
}
void cleanupContent()
{
// clean up properly to avoid interfering with subsequent samples
for (std::map<String, StringVector>::iterator it = mPossibilities.begin(); it != mPossibilities.end(); it++)
MeshManager::getSingleton().unload(it->first);
mPossibilities.clear();
}
std::map<String, StringVector> mPossibilities;
SceneNode* mObjectNode;
SceneNode* mLightPivot1;
SceneNode* mLightPivot2;
bool mMoveLights;
SelectMenu* mMeshMenu;
SelectMenu* mMaterialMenu;
};
#endif
Dot3Bump.cpp
#include "SamplePlugin.h"
#include "Dot3Bump.h"
using namespace Ogre;
using namespace OgreBites;
#ifndef OGRE_STATIC_LIB
SamplePlugin* sp;
Sample* s;
extern "C" _OgreSampleExport void dllStartPlugin()
{
s = new Sample_Dot3Bump;
sp = OGRE_NEW SamplePlugin(s->getInfo()["Title"] + " Sample");
sp->addSample(s);
Root::getSingleton().installPlugin(sp);
}
extern "C" _OgreSampleExport void dllStopPlugin()
{
Root::getSingleton().uninstallPlugin(sp);
OGRE_DELETE sp;
delete s;
}
#endif
展开阅读全文