1. SkyBox的构建过程
OgreSceneManager.cpp
/** Enables / disables a 'sky box' i.e. a 6-sided box at constant
distance from the camera representing the sky.
@remarks
You could create a sky box yourself using the standard mesh and
entity methods, but this creates a plane which the camera can
never get closer or further away from - it moves with the camera.
(NB you could create this effect by creating a world box which
was attached to the same SceneNode as the Camera too, but this
would only apply to a single camera whereas this skybox applies
to any camera using this scene manager).
@par
The material you use for the skybox can either contain layers
which are single textures, or they can be cubic textures, i.e.
made up of 6 images, one for each plane of the cube. See the
TextureUnitState class for more information.
@param
enable True to enable the skybox, false to disable it
@param
materialName The name of the material the box will use
@param
distance Distance in world coorinates from the camera to
each plane of the box. The default is normally OK.
@param
drawFirst If true, the box is drawn before all other
geometry in the scene, without updating the depth buffer.
This is the safest rendering method since all other objects
will always appear in front of the sky. However this is not
the most efficient way if most of the sky is often occluded
by other objects. If this is the case, you can set this
parameter to false meaning it draws <em>after</em> all other
geometry which can be an optimisation - however you must
ensure that the distance value is large enough that no
objects will 'poke through' the sky box when it is rendered.
@param
orientation Optional parameter to specify the orientation
of the box. By default the 'top' of the box is deemed to be
in the +y direction, and the 'front' at the -z direction.
You can use this parameter to rotate the sky if you want.
@param groupName
The name of the resource group to which to assign the plane mesh.
*/
void SceneManager::setSkyBox(
bool enable,
const String& materialName,
Real distance,
bool drawFirst,
const Quaternion& orientation,
const String& groupName)
{
_setSkyBox(enable, materialName, distance,
static_cast<uint8>(drawFirst?RENDER_QUEUE_SKIES_EARLY: RENDER_QUEUE_SKIES_LATE),
orientation, groupName);
}
SkyBox实际实现代码:
//-----------------------------------------------------------------------
void SceneManager::_setSkyBox(
bool enable,
const String& materialName,
Real distance,
uint8 renderQueue,
const Quaternion& orientation,
const String& groupName)
{
// 是否启用SkyBox
if (enable)
{
MaterialPtr m = MaterialManager::getSingleton().getByName(materialName, groupName);
if (m.isNull())
{
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
"Sky box material '" + materialName + "' not found.",
"SceneManager::setSkyBox");
}
// Ensure loaded
m->load();
if (!m->getBestTechnique() ||
!m->getBestTechnique()->getNumPasses())
{
// 材质没有Technique或是获取Pass个数为零,使用默认设置的材质
LogManager::getSingleton().logMessage(
"Warning, skybox material " + materialName + " is not supported, defaulting.");
m = MaterialManager::getSingleton().getDefaultSettings();
}
// 确定纹理单元的类型是普通纹理还是3D纹理
bool t3d = false;
Pass* pass = m->getBestTechnique()->getPass(0);
if (pass->getNumTextureUnitStates() > 0 && pass->getTextureUnitState(0)->is3D())
t3d = true;
// 设置SkyBox的渲染顺序
mSkyBoxRenderQueue = renderQueue;
// Create node
if (!mSkyBoxNode)
{
// 创建SkyBox挂载的SceneNode节点
// SkyBoxNode不挂载在场景树的任何节点上,而是在SceneManager::_queueSkiesForRendering(Camera *cam),
// 设置SkyBoxNode的坐标为Camera的坐标,保证Camera与SkyBoxNode的坐标一致
mSkyBoxNode = createSceneNode("SkyBoxNode");
}
// Create object
if (!mSkyBoxObj)
{
// 创建SkyBox对象,设置不投影,将对象挂载到SkyBox上
mSkyBoxObj = OGRE_NEW ManualObject("SkyBox");
mSkyBoxObj->setCastShadows(false);
mSkyBoxNode->attachObject(mSkyBoxObj);
}
else
{
// 若已存在SkyBox对象,清除原对象的内容
if (!mSkyBoxObj->isAttached())
{
mSkyBoxNode->attachObject(mSkyBoxObj);
}
mSkyBoxObj->clear();
}
// 设置SkyBox的渲染队列组
mSkyBoxObj->setRenderQueueGroup(mSkyBoxRenderQueue);
if (t3d)
{
// 纹理单元是否为3D图片?
mSkyBoxObj->begin(materialName);
}
MaterialManager& matMgr = MaterialManager::getSingleton();
// Set up the box (6 planes)
for (uint16 i = 0; i < 6; ++i)
{
Plane plane;
String meshName;
Vector3 middle;
Vector3 up, right;
switch(i)
{
case BP_FRONT:
middle = Vector3(0, 0, -distance);
up = Vector3::UNIT_Y * distance;
right = Vector3::UNIT_X * distance;
break;
case BP_BACK:
middle = Vector3(0, 0, distance);
up = Vector3::UNIT_Y * distance;
right = Vector3::NEGATIVE_UNIT_X * distance;
break;
case BP_LEFT:
middle = Vector3(-distance, 0, 0);
up = Vector3::UNIT_Y * distance;
right = Vector3::NEGATIVE_UNIT_Z * distance;
break;
case BP_RIGHT:
middle = Vector3(distance, 0, 0);
up = Vector3::UNIT_Y * distance;
right = Vector3::UNIT_Z * distance;
break;
case BP_UP:
middle = Vector3(0, distance, 0);
up = Vector3::UNIT_Z * distance;
right = Vector3::UNIT_X * distance;
break;
case BP_DOWN:
middle = Vector3(0, -distance, 0);
up = Vector3::NEGATIVE_UNIT_Z * distance;
right = Vector3::UNIT_X * distance;
break;
}
// Modify by orientation
middle = orientation * middle;
up = orientation * up;
right = orientation * right;
if (t3d)
{
// 3D cubic texture
// Note UVs mirrored front/back
// I could save a few vertices here by sharing the corners
// since 3D coords will function correctly but it's really not worth
// making the code more complicated for the sake of 16 verts
// top left
Vector3 pos;
pos = middle + up - right;
mSkyBoxObj->position(pos);
mSkyBoxObj->textureCoord(pos.normalisedCopy() * Vector3(1,1,-1));
// bottom left
pos = middle - up - right;
mSkyBoxObj->position(pos);
mSkyBoxObj->textureCoord(pos.normalisedCopy() * Vector3(1,1,-1));
// bottom right
pos = middle - up + right;
mSkyBoxObj->position(pos);
mSkyBoxObj->textureCoord(pos.normalisedCopy() * Vector3(1,1,-1));
// top right
pos = middle + up + right;
mSkyBoxObj->position(pos);
mSkyBoxObj->textureCoord(pos.normalisedCopy() * Vector3(1,1,-1));
uint16 base = i * 4;
// 根据六个面上的点构建每个面,并获取纹理UV坐标,构建各个面
mSkyBoxObj->quad(base, base+1, base+2, base+3);
}
else // !t3d
{
// If we're using 6 separate images, have to create 6 materials, one for each frame
// Used to use combined material but now we're using queue we can't split to change frame
// This doesn't use much memory because textures aren't duplicated
String matName = mName + "SkyBoxPlane" + StringConverter::toString(i);
MaterialPtr boxMat = matMgr.getByName(matName, groupName);
if (boxMat.isNull())
{
// Create new by clone
boxMat = m->clone(matName);
boxMat->load();
}
else
{
// Copy over existing
m->copyDetailsTo(boxMat);
boxMat->load();
}
// Make sure the material doesn't update the depth buffer
// 天空总是处于其它物体的背后,不需要重写、检查深度值,SkyBox的颜色总是被覆盖的
boxMat->setDepthWriteEnabled(false);
// Set active frame
Material::TechniqueIterator ti = boxMat->getSupportedTechniqueIterator();
while (ti.hasMoreElements())
{
Technique* tech = ti.getNext();
if (tech->getPass(0)->getNumTextureUnitStates() > 0)
{
TextureUnitState* t = tech->getPass(0)->getTextureUnitState(0);
// Also clamp texture, don't wrap (otherwise edges can get filtered)
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
/** Changes the active frame in an animated or multi-image texture.
@remarks
An animated texture (or a cubic texture where the images are not combined for 3D use) is made up of
a number of frames. This method sets the active frame.
@note
Applies to both fixed-function and programmable pipeline.
*/
t->setCurrentFrame(i);
}
}
// section per material
mSkyBoxObj->begin(matName, RenderOperation::OT_TRIANGLE_LIST, groupName);
// top left
mSkyBoxObj->position(middle + up - right);
mSkyBoxObj->textureCoord(0,0);
// bottom left
mSkyBoxObj->position(middle - up - right);
mSkyBoxObj->textureCoord(0,1);
// bottom right
mSkyBoxObj->position(middle - up + right);
mSkyBoxObj->textureCoord(1,1);
// top right
mSkyBoxObj->position(middle + up + right);
mSkyBoxObj->textureCoord(1,0);
mSkyBoxObj->quad(0, 1, 2, 3);
mSkyBoxObj->end();
}
} // for each plane
if (t3d)
{
mSkyBoxObj->end();
}
}
mSkyBoxEnabled = enable;
mSkyBoxGenParameters.skyBoxDistance = distance;
}
SkyBox渲染:
也是在OgreSceneManager::_renderScene()中的_renderVisibleObjects()中进行
OgreSceneManager::_renderScene -----> OgreSceneManager::_renderVisibleObjects() -----> OgreSceneManager::renderVisibleObjectsDefaultSequence() ----->
OgreSceneManager::_renderQueueGroupObjects() -----> OgreSceneManager::renderBasicQueueGroupObjects() /* No shadows, ordinary pass */ -----> OgreSceneManager::renderObjects(pPriorityGrp->getSolidsBasic(), 0m, true, true); -----> OgreQueuedRenderableCollection::acceptVisitor() -----> OgreQueuedRenderableCollection::acceptVisitorGrouped() -----> OgreSceneManager::SceneMgrQueuedRenderableVisitor::visit(Ogre::Renderable *r) ----->
OgreSceneManager::renderSingleObject() -----> D3D9RenderSystem::_render(const RenderOperation &op);