目的: 显示 立方体映射的特色(cube mapping), 还可以设置物体镜面映射出周围的景色
主要有两个类 CubeMapApplication 和 CubeMapListener
* 该例中对象的周围景象反射是通过设置对象的主材质material实现的, 在脚本中设置了env_map cubic_reflect和cubic_texture属性
该对象的子mesh则是复制原始mesh中的子mesh, 材质也是复制其子mesh的材质
* 该例可以学习如何克隆一个mesh, 并复制其顶点数据, 并克隆其子材质, 该例可以学习 硬件顶点缓存的一些知识
* 该例演示了 如何修改Mesh所带的顶点数据, 给这些顶点位置增加噪值, 并更新顶点的法线数据。 实现很特殊的效果
* 利用天空盒Skybox, 可以直接通过材质中的纹理单元cubic_texture属性来设置六个面。 就可以作出六张照片形成的虚拟空间效果。
* 对象的立体纹理需要开启UVW坐标, 而天空盒的立体纹理不需要开始UVW坐标, 开启该坐标在立体纹理中有用。
1. 类 CubeMapApplication --- 主类, 驱动类
构造函数: 无
函数createScene --- 构造场景
判断显卡是否支持 Cube Mapping.
环境光 0.5, 0.5, 0.5
设置天空盒 SKYBOX_MATERIAL "Examples/SceneSkyBox2"
创建光 MainLight
光的位置 20,80,50
创建一个场景节点
OverlayManager 通过名称“Example/CubeMappingOverlay”得到 一个Overlay
显示它
函数createFrameListener
2. 类 CubeMapListener
构造函数:
调用基类的构造函数
得到场景管理器和对象节点
通过MaterialManager得到材质 Examples/SceneCubeMap2 material 这个材质使用了combinedUVW格式的立方纹理
readConfig
加载 media.cfg
将该配置文件所有的Mesh和CubeMesh放入字符串容器中 getMultiSetting
setObject
设置要用的Mesh索引
得到mesh名称
prepareEntity
更新Overlay元素
setMaterialBlending
设置材质的混合
setCubeMap
设置立体纹理, 更新对象的纹理反射以及天空盒
setNoiseOn
在Overlay上提示开始噪值
updateInfoDisplacement
在Overlay上提示替换方式
updateInfoDensity
在Overlay上提示噪值度
updateInfoTimeDensity
在Overlay上提示时间噪值度
函数 prepareEntity---参数为纹理名称
判断是否有对象实体
有--调用clearEntity
移除材质容器的所有元素 clonedMaterials
对象节点移除所有对象的挂接
场景管理器销毁该环境映射实体 mSceneMgr->destroyEntity(ENTITY_NAME);
MeshManager 移除 clonedMesh
MeshManager 根据 mesh名称得到originalMesh——getByName
如果 originalMesh 为空
调用 MeshManager 的 load 方法 加载
如果还为空, 则抛出异常
函数prepareClonedMesh(复制mesh,顶点共享数据复, 材质名称等等)
创建一个立体映射实体 objectEntity
设置其材质, 这里的材质在构造函数中定义好了 MATERIAL_NAME, 为立体材质
// 下面一段代码的目的是, 根据得到的克隆mesh, 将其子mesh的材质克隆出一个材质, 并让创建的实体使用该克隆材质。
得到其第一个通道 pass
循环克隆的子mesh数
得到克隆的子mesh
得到对应的子实体
subMesh调用 isMatInitialised,返回为真的话
得到子mesh的材质名称 matName
根据matName 得到该材质 subMat
如该材质不为空
该材质重新加载
复制该材质 cloned
得到 cloned 的第一个通道 clonedPass
如果 meshName 等于 knot.mesh
循环该通道的纹理单元数
每个纹理单元调用 setScrollAnimation 设置为 1.0, 0
循环该通道的纹理单元数
将原通道的纹理单元复制到该克隆的纹理单元 getTextureUnitState createTextureUnitState
并调用setColourOperationEx设置为当前的操作
对应子实体设置为该子材质名称
将该克隆材质压入容器中
对象节点绑定该对象实体
是否开启 noiseOn
是的话调用 updateNoise()
函数 prepareClonedMesh
// 我们根据原始的mesh创建新的mesh, 但更改了 HBU 标识
创建手工纹理 clonedMesh, 名称为 MESH_NAME
调用 _setBounds 设置成 原Mesh的Bounds
调用 _setBoundingSphereRadius
调用 _prepareVertexData 设置克隆材质的顶点共享数据clonedMesh->sharedVertexData(参数为原材质的共享顶点数据orgVD)
循环原始mesh的子mesh数
克隆的mesh也创建一个子mesh newSM
原始子Mesh 调用isMatInitialised, 如果为真
newSM的名字同原子mesh
复制 useSharedVertices
调用 _prepareVertexData 复制顶点数据
复制 indexData->indexBuffer, indexData->indexStart, indexData->indexCount
函数 _prepareVertexData
// 设置克隆材质的顶点共享数据(参数为原材质的共享顶点数据orgVD)
// 将orgVD顶点的位置和法线数据复制到新的硬件缓存中, 其他的顶点数据不变
// 用顶点缓存的索引号实现绑定, 再将索引号放入顶点声明中
参数是否为0 ,为0 则直接返回0
// 当该Demo开始写的时候重组每个元素的顶点缓存,由带有一定扭曲的顶点程序替换, 用软件实现, 可以适应旧的显卡
调用原始顶点数据的顶点声明的clone方法, 得到新的顶点声明 newDecl
newDecl 调用 getElements方法得到元素列表
VertexDeclaration::VertexElementList::const_iterator di;
遍历元素
newDecl 调用 modifyElement 修改元素, 和原始数据一样, 效果等于用源顶点数据的类型和类别来初始化newDecl
orgVD调用reorganiseBuffers重组 newDecl的缓存
创建新的VertexData对象 newVD
其vertexCount和vertexStart同orgVD
得到newVD的声明newVDecl和缓存绑定newVBind
得到orgVD的顶点声明的元素列表 orgVD->vertexDeclaration->getElements() ;
遍历orgVD元素列表
根据每个元素的getSemantic方法得到ves, 为该顶点的类别, 是法线, 还是。。。
用方法getSource 得到 顶点缓存索引 source
得到原始顶点数据该顶点的缓存指针 orgBuf orgVD->vertexBufferBinding->getBuffer( source );
根据ves设置dynamic, 法线,位置顶点为真, 其余基本为假
判断dynamic
是
根据原始数据的顶点大小和顶点数目创建新的硬件缓存 newBuf
而后newBuf复制原始的数据
设置newVBind设置source索引号到newBuf
否
设置newVBind设置source索引号到原来的缓存
newVDecl增加元素, 根据source绑定的硬件缓存索引, 添加进newVDecl
返回newVD
updateNoise
循环克隆mesh的子mesh数
分别得到克隆mesh和源mesh的子mesh subMesh, orgSubMesh
如果 subMesh使用共享顶点, 根据共享顶点数据得到法线的硬件缓存地址
如果 sharedNormals = 0 即第一次共享时
则其等于 _normalsGetCleared
得到子Mesh的顶点共享数据的法线元素
得到法线的硬件顶点缓存
锁定该段缓存, 并将这段数据重置为0
_updateVertexDataNoiseAndNormals
给mesh的所有顶点加上一个噪值, 并设置法线
如果不使用共享顶点, 根据子Mesh的顶点数据得到法线的硬件缓存地址
_normalsGetCleared 清理法线数据
_updateVertexDataNoiseAndNormals 更新
_normalsSaveNormalized 单元化并保存硬件缓存中的法线数据
如果共享法线
_normalsSaveNormalized 单元化并保存硬件缓存中的法线数据
函数 _updateVertexDataNoiseAndNormals
参数: 克隆mesh的顶点共享数据, 原始mesh的顶点共享数据, 子mesh的索引数据(indexData), 锁定的法线硬件缓存
在克隆mesh的顶点共享数据中找到位置数据的元素dstVEPos--findElementBySemantic(VES_POSITION)
在顶点共享数据的缓存绑定中找到位置数据个索引得到缓存指针dstHVBPos getBuffer(dstVEPos->getSource())
同理, 得到原始mesh的orgVEPos 和缓存orgHVBPos
分别锁定目标和源缓存得到指针 dstDataPos, orgDataPos
循环-- 次数为 源 顶点数
将原始数据的顶点位置加上一个随机产生的噪值使得图形发生一种变形
源 解除锁定
// 开始计算各处的法线
根据子mesh的索引数据得到 硬件缓存的索引 indexHB
根据indexHB, 锁定缓存, 得到指针 vertexIndices
计算mesh的面数numFaces为 顶点索引数/3 indexData->indexCount / 3
循环面数 vertexIndices + 3
vertexIndices[0], vertexIndices[1], vertexIndices[2] 为顶点索引
在dstDataPos里面根据索引得到三个顶点的位置
求出法线
在normals法线缓存中根据索引设置法线向量
解除indexHB的锁定
解除目标缓存的锁定
函数 _normalsSaveNormalized
功能: 单元化硬件缓存中 法线的数据
参数: 子mesh的顶点数据, 法线的缓存地址 normals
根据顶点数据的顶点声明找到 法线的元素 normVE
根据绑定绑定缓存用normVE的索引找到硬件顶点缓存
循环顶点数 normals + 3
单元化 normals[0], normals[1], normals[2]
解除法线缓存的锁定
函数 setMaterialBlending
设置材质的混合方式
调用 prepareEntity 重新构造实体
更新 Overlay信息
函数 setCubeMap
// 用于改变映射的环境图片
得到映射的图片名称
循环立体纹理的每个面
getFrameTextureName 得到纹理名称
通过纹理管理器的 getByName 得到纹理指针
纹理管理移除该纹理
该纹理单元设置立体纹理 setCubicTextureName
得到SKYBOX_MATERIAL的材质mat2
循环该材质的立体纹理
移除旧的纹理
设置立体纹理
场景管理器设置天空盒 SKYBOX_MATERIAL
prepareEntity 重构实体
函数 frameRenderingQueued
调用基类的帧更新函数
设置tm
如果开始噪值功能, 则调用updateNoise
旋转对象
函数 processUnbufferedKeyInput
// 处理键盘输入
其中 ADJUST_RANGE 宏 和 SWITCH_VALUE 宏很值得学习
#define ADJUST_RANGE(_value,_keyPlus,_keyMinus,_minVal,_maxVal,_change,_macro) {/
if (mKeyboard->isKeyDown(_keyPlus)) /
{ _value+=_change ; if (_value>=_maxVal) _value = _maxVal ; _macro ; } ; /
if (mKeyboard->isKeyDown(_keyMinus)) /
{ _value-=_change; if (_value<=_minVal) _value = _minVal ; _macro ; } ; /
}
#define SWITCH_VALUE(_key,_timeDelay, _macro) { /
if (mKeyboard->isKeyDown(_key) && timeoutDelay==0) { /
timeoutDelay = _timeDelay ; _macro ;} }
主要有两个类 CubeMapApplication 和 CubeMapListener
* 该例中对象的周围景象反射是通过设置对象的主材质material实现的, 在脚本中设置了env_map cubic_reflect和cubic_texture属性
该对象的子mesh则是复制原始mesh中的子mesh, 材质也是复制其子mesh的材质
* 该例可以学习如何克隆一个mesh, 并复制其顶点数据, 并克隆其子材质, 该例可以学习 硬件顶点缓存的一些知识
* 该例演示了 如何修改Mesh所带的顶点数据, 给这些顶点位置增加噪值, 并更新顶点的法线数据。 实现很特殊的效果
* 利用天空盒Skybox, 可以直接通过材质中的纹理单元cubic_texture属性来设置六个面。 就可以作出六张照片形成的虚拟空间效果。
* 对象的立体纹理需要开启UVW坐标, 而天空盒的立体纹理不需要开始UVW坐标, 开启该坐标在立体纹理中有用。
1. 类 CubeMapApplication --- 主类, 驱动类
构造函数: 无
函数createScene --- 构造场景
判断显卡是否支持 Cube Mapping.
环境光 0.5, 0.5, 0.5
设置天空盒 SKYBOX_MATERIAL "Examples/SceneSkyBox2"
创建光 MainLight
光的位置 20,80,50
创建一个场景节点
OverlayManager 通过名称“Example/CubeMappingOverlay”得到 一个Overlay
显示它
函数createFrameListener
2. 类 CubeMapListener
构造函数:
调用基类的构造函数
得到场景管理器和对象节点
通过MaterialManager得到材质 Examples/SceneCubeMap2 material 这个材质使用了combinedUVW格式的立方纹理
readConfig
加载 media.cfg
将该配置文件所有的Mesh和CubeMesh放入字符串容器中 getMultiSetting
setObject
设置要用的Mesh索引
得到mesh名称
prepareEntity
更新Overlay元素
setMaterialBlending
设置材质的混合
setCubeMap
设置立体纹理, 更新对象的纹理反射以及天空盒
setNoiseOn
在Overlay上提示开始噪值
updateInfoDisplacement
在Overlay上提示替换方式
updateInfoDensity
在Overlay上提示噪值度
updateInfoTimeDensity
在Overlay上提示时间噪值度
函数 prepareEntity---参数为纹理名称
判断是否有对象实体
有--调用clearEntity
移除材质容器的所有元素 clonedMaterials
对象节点移除所有对象的挂接
场景管理器销毁该环境映射实体 mSceneMgr->destroyEntity(ENTITY_NAME);
MeshManager 移除 clonedMesh
MeshManager 根据 mesh名称得到originalMesh——getByName
如果 originalMesh 为空
调用 MeshManager 的 load 方法 加载
如果还为空, 则抛出异常
函数prepareClonedMesh(复制mesh,顶点共享数据复, 材质名称等等)
创建一个立体映射实体 objectEntity
设置其材质, 这里的材质在构造函数中定义好了 MATERIAL_NAME, 为立体材质
// 下面一段代码的目的是, 根据得到的克隆mesh, 将其子mesh的材质克隆出一个材质, 并让创建的实体使用该克隆材质。
得到其第一个通道 pass
循环克隆的子mesh数
得到克隆的子mesh
得到对应的子实体
subMesh调用 isMatInitialised,返回为真的话
得到子mesh的材质名称 matName
根据matName 得到该材质 subMat
如该材质不为空
该材质重新加载
复制该材质 cloned
得到 cloned 的第一个通道 clonedPass
如果 meshName 等于 knot.mesh
循环该通道的纹理单元数
每个纹理单元调用 setScrollAnimation 设置为 1.0, 0
循环该通道的纹理单元数
将原通道的纹理单元复制到该克隆的纹理单元 getTextureUnitState createTextureUnitState
并调用setColourOperationEx设置为当前的操作
对应子实体设置为该子材质名称
将该克隆材质压入容器中
对象节点绑定该对象实体
是否开启 noiseOn
是的话调用 updateNoise()
函数 prepareClonedMesh
// 我们根据原始的mesh创建新的mesh, 但更改了 HBU 标识
创建手工纹理 clonedMesh, 名称为 MESH_NAME
调用 _setBounds 设置成 原Mesh的Bounds
调用 _setBoundingSphereRadius
调用 _prepareVertexData 设置克隆材质的顶点共享数据clonedMesh->sharedVertexData(参数为原材质的共享顶点数据orgVD)
循环原始mesh的子mesh数
克隆的mesh也创建一个子mesh newSM
原始子Mesh 调用isMatInitialised, 如果为真
newSM的名字同原子mesh
复制 useSharedVertices
调用 _prepareVertexData 复制顶点数据
复制 indexData->indexBuffer, indexData->indexStart, indexData->indexCount
函数 _prepareVertexData
// 设置克隆材质的顶点共享数据(参数为原材质的共享顶点数据orgVD)
// 将orgVD顶点的位置和法线数据复制到新的硬件缓存中, 其他的顶点数据不变
// 用顶点缓存的索引号实现绑定, 再将索引号放入顶点声明中
参数是否为0 ,为0 则直接返回0
// 当该Demo开始写的时候重组每个元素的顶点缓存,由带有一定扭曲的顶点程序替换, 用软件实现, 可以适应旧的显卡
调用原始顶点数据的顶点声明的clone方法, 得到新的顶点声明 newDecl
newDecl 调用 getElements方法得到元素列表
VertexDeclaration::VertexElementList::const_iterator di;
遍历元素
newDecl 调用 modifyElement 修改元素, 和原始数据一样, 效果等于用源顶点数据的类型和类别来初始化newDecl
orgVD调用reorganiseBuffers重组 newDecl的缓存
创建新的VertexData对象 newVD
其vertexCount和vertexStart同orgVD
得到newVD的声明newVDecl和缓存绑定newVBind
得到orgVD的顶点声明的元素列表 orgVD->vertexDeclaration->getElements() ;
遍历orgVD元素列表
根据每个元素的getSemantic方法得到ves, 为该顶点的类别, 是法线, 还是。。。
用方法getSource 得到 顶点缓存索引 source
得到原始顶点数据该顶点的缓存指针 orgBuf orgVD->vertexBufferBinding->getBuffer( source );
根据ves设置dynamic, 法线,位置顶点为真, 其余基本为假
判断dynamic
是
根据原始数据的顶点大小和顶点数目创建新的硬件缓存 newBuf
而后newBuf复制原始的数据
设置newVBind设置source索引号到newBuf
否
设置newVBind设置source索引号到原来的缓存
newVDecl增加元素, 根据source绑定的硬件缓存索引, 添加进newVDecl
返回newVD
updateNoise
循环克隆mesh的子mesh数
分别得到克隆mesh和源mesh的子mesh subMesh, orgSubMesh
如果 subMesh使用共享顶点, 根据共享顶点数据得到法线的硬件缓存地址
如果 sharedNormals = 0 即第一次共享时
则其等于 _normalsGetCleared
得到子Mesh的顶点共享数据的法线元素
得到法线的硬件顶点缓存
锁定该段缓存, 并将这段数据重置为0
_updateVertexDataNoiseAndNormals
给mesh的所有顶点加上一个噪值, 并设置法线
如果不使用共享顶点, 根据子Mesh的顶点数据得到法线的硬件缓存地址
_normalsGetCleared 清理法线数据
_updateVertexDataNoiseAndNormals 更新
_normalsSaveNormalized 单元化并保存硬件缓存中的法线数据
如果共享法线
_normalsSaveNormalized 单元化并保存硬件缓存中的法线数据
函数 _updateVertexDataNoiseAndNormals
参数: 克隆mesh的顶点共享数据, 原始mesh的顶点共享数据, 子mesh的索引数据(indexData), 锁定的法线硬件缓存
在克隆mesh的顶点共享数据中找到位置数据的元素dstVEPos--findElementBySemantic(VES_POSITION)
在顶点共享数据的缓存绑定中找到位置数据个索引得到缓存指针dstHVBPos getBuffer(dstVEPos->getSource())
同理, 得到原始mesh的orgVEPos 和缓存orgHVBPos
分别锁定目标和源缓存得到指针 dstDataPos, orgDataPos
循环-- 次数为 源 顶点数
将原始数据的顶点位置加上一个随机产生的噪值使得图形发生一种变形
源 解除锁定
// 开始计算各处的法线
根据子mesh的索引数据得到 硬件缓存的索引 indexHB
根据indexHB, 锁定缓存, 得到指针 vertexIndices
计算mesh的面数numFaces为 顶点索引数/3 indexData->indexCount / 3
循环面数 vertexIndices + 3
vertexIndices[0], vertexIndices[1], vertexIndices[2] 为顶点索引
在dstDataPos里面根据索引得到三个顶点的位置
求出法线
在normals法线缓存中根据索引设置法线向量
解除indexHB的锁定
解除目标缓存的锁定
函数 _normalsSaveNormalized
功能: 单元化硬件缓存中 法线的数据
参数: 子mesh的顶点数据, 法线的缓存地址 normals
根据顶点数据的顶点声明找到 法线的元素 normVE
根据绑定绑定缓存用normVE的索引找到硬件顶点缓存
循环顶点数 normals + 3
单元化 normals[0], normals[1], normals[2]
解除法线缓存的锁定
函数 setMaterialBlending
设置材质的混合方式
调用 prepareEntity 重新构造实体
更新 Overlay信息
函数 setCubeMap
// 用于改变映射的环境图片
得到映射的图片名称
循环立体纹理的每个面
getFrameTextureName 得到纹理名称
通过纹理管理器的 getByName 得到纹理指针
纹理管理移除该纹理
该纹理单元设置立体纹理 setCubicTextureName
得到SKYBOX_MATERIAL的材质mat2
循环该材质的立体纹理
移除旧的纹理
设置立体纹理
场景管理器设置天空盒 SKYBOX_MATERIAL
prepareEntity 重构实体
函数 frameRenderingQueued
调用基类的帧更新函数
设置tm
如果开始噪值功能, 则调用updateNoise
旋转对象
函数 processUnbufferedKeyInput
// 处理键盘输入
其中 ADJUST_RANGE 宏 和 SWITCH_VALUE 宏很值得学习
#define ADJUST_RANGE(_value,_keyPlus,_keyMinus,_minVal,_maxVal,_change,_macro) {/
if (mKeyboard->isKeyDown(_keyPlus)) /
{ _value+=_change ; if (_value>=_maxVal) _value = _maxVal ; _macro ; } ; /
if (mKeyboard->isKeyDown(_keyMinus)) /
{ _value-=_change; if (_value<=_minVal) _value = _minVal ; _macro ; } ; /
}
#define SWITCH_VALUE(_key,_timeDelay, _macro) { /
if (mKeyboard->isKeyDown(_key) && timeoutDelay==0) { /
timeoutDelay = _timeDelay ; _macro ;} }