Ogre RTSS组件解析
我们为什么要用RTSS.
Ogre如计算物体位置,纹理,光照都有固定API如(glMatrixFrustumEXT, glLoadmatrix, glTexture, glLight ),使用这些API渲染称之为固定渲染管线,而最新的如D3D11,OpenGL3.0+都在淘汰固定渲染管线功能了,对应的都全力支持可编程管线,用shader实现各个方面的功能.二者详细的区别大家可以google.而Ogre之前默认采用固定渲染管线,对应的D3D9,而Ogre2.0+版本对应的DirectX是D3D11,这个版本已经完全放弃了固定渲染管线的实现,OpenGL因为扩展集的存在,OpenGL3+也还是可以使用固定渲染管线,但是官方的API对应的固定渲染API已经越来越少了,而在移动设备上使用的OpenGL ES2.0同D3D11一样完全不支持固定渲染管线,所以使用固定渲染管线已经不是一个明智的选择.
主要实现如下功能:
1.模拟固定管线功能,对于D3D10+,OpenGL ES2.0就能不写着色器代码而使用对应的固定管线功能(顶点转换,模型颜色,灯光,纹理,Alpha混合,雾等).
2.可定制组合各个功能,定义好相关的功能,可以根据当前条件,选择是否需要相应功能.
3.易维护,自成生成管理代替每个材质管理材料和程序.
RTSS类介绍.
模拟着色器程序
简单来说,下面这些类演示如何模拟着色器程序,我们编写一个程序,程序由入口函数和调用函数构成,其中每个函数在前面包含参数,内部包含针对参数和局部变量的各个操作.
ProgramSet:包含二个模拟着色器程序,一个顶点,一个片断.还包含这二个Program生成对应的GpuProgram,其中GpuProgram就是我们对应的着色器资源(请看前文Ogre GpuProgram分析),插入到Pass中使用.
Program:在这我们称模拟着色器程序,能转化成GpuProgram,在这主要封装头文件名与函数列表与参数列表,注意这里的参数是在下面的Function外部,针对这整个Program相当于全局函数,同时这些参数要求GPU自动封装的(自动根据场景填值),请看下面的UniformParameter.
Function:模拟一个函数,包含输入,输出,局部变量,内部包含针对这些参数的原子操作,如赋值,加减乘除,请看下面FunctionInvocation.
Parameter:模拟参数,主要有Semantic, Content二个主要类型参数, Semantic指明参数语义,如顶点,法线点,非Unknown的Semantic参数一般用做入口函数的参数, 反之Unknown的参数一般用做局部变量.而Content指定语义里的具体内部,如顶点分别有坐标空间,世界空间,视图空间,投影空间.最后GpuConstantType上文链接有说过,指明当前参数float,float2,mat4等类型的.
UniformParameter: Parameter的子类,包装的由GPU自动管理的参数,如MVP矩阵,一些场景参数如雾,灯光等,用户也可以自定义,但需自己在相应位置更新(具体请看下面SubRenderState),需要注意的是,纹理参数也是用这个包装,会与别的自动管理参数有些区别,后面例说.这类参数直接由Program持有,不由Function持有,后面会绑定到对应的GpuProgram的GpuProgramParameters上.
ConstParameter: Parameter的泛型子类,一般会做声明局部变量并初始化值.
FunctionAtom:模拟函数内部的每个原子函数,指明每个原子函数的顺序(分组,组顺序,组内部顺序).抽象类.
FunctionInvocation: FunctionAtom的子类,具体用来指明每个子函数,小时小到时前所面所说的赋值,加减乘除,点乘,叉乘. 大时大到一些算法如点光,方向光,平行光算法,或者是骨骼动画算法.
Operand: FunctionInvocation是每子操作,操作的是参数,Operand就是包装的参数,用于FunctionInvocation中,指定参数是传入,传出,传入传出,以及参数的使用位,如使用xy,xz,xyz,xyzw等.
整个过程差不多如下,在RTSS中,定义了一些常用的FunctionInvocation,我们要做的是生成Program,根据函数目的先想出要用的UniformParameter(场景参数或模型参数),添加头文件,然后生成入口Function,生成要用的Parameter(这里一般是顶点参数),把对应的Parameter包装成Operand给相应的FunctionInvocation使用,最后Function根据顺序排序,生成GpuProgram.
我们来看下如下代码,加深理解,首先是RTSS文件中,我所说的一些常用的FunctionInvocation的定义.
void FFP_Transform(in float3x3 m,
in float3 v,
out float3 vOut)
{
vOut = mul(m, v);
}
//-----------------------------------------------------------------------------
void FFP_Transform(in float4x4 m,
in float4 v,
out float4 vOut)
{
vOut = mul(m, v);
}
//-----------------------------------------------------------------------------
#ifndef OPENGL_ES_2 //-----------------------------------------------------------------------------
void FFP_Transform(in float3x4 m,
in float4 v,
out float3 vOut)
{
vOut = mul(m, v);
} //-----------------------------------------------------------------------------
void FFP_Transform(in float3x4 m,
in float3 v,
out float3 vOut)
{
vOut = mul((float3x3)m, v);
}
//-----------------------------------------------------------------------------
#endif
//-----------------------------------------------------------------------------
void FFP_Transform(in float4x4 m,
in float3 v,
out float3 vOut)
{
vOut = mul((float3x3)m, v);
}
FunctionInvocation
这里RTSS中一段代码,用于MVP转换,从局部坐标到投影坐标.
bool FFPTransform::createCpuSubPrograms(ProgramSet* programSet)
{
Program* vsProgram = programSet->getCpuVertexProgram();
Function* vsEntry = vsProgram->getEntryPointFunction(); // Resolve World View Projection Matrix.
UniformParameterPtr wvpMatrix = vsProgram->resolveAutoParameterInt(GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX, ); // Resolve input position parameter.
ParameterPtr positionIn = vsEntry->resolveInputParameter(Parameter::SPS_POSITION, , Parameter::SPC_POSITION_OBJECT_SPACE, GCT_FLOAT4); // Resolve output position parameter.
ParameterPtr positionOut = vsEntry->resolveOutputParameter(Parameter::SPS_POSITION, , Parameter::SPC_POSITION_PROJECTIVE_SPACE, GCT_FLOAT4); if (!(wvpMatrix.get()) || !(positionIn.get()) || !(positionOut.get()))
{
OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
"Not all parameters could be constructed for the sub-render state.",
"FFPTransform::createCpuSubPrograms" );
} // Add dependency.
vsProgram->addDependency(FFP_LIB_TRANSFORM); FunctionInvocation* transformFunc = OGRE_NEW FunctionInvocation(FFP_FUNC_TRANSFORM, FFP_VS_TRANSFORM, ); transformFunc->pushOperand(wvpMatrix, Operand::OPS_IN);
transformFunc->pushOperand(positionIn, Operand::OPS_IN);
transformFunc->pushOperand(positionOut, Operand::OPS_OUT); vsEntry->addAtomInstance(transformFunc); return true;
}
FFPTransform
根据这段代码里的参数与函数名,结合我们上面所说,差不多都能理解上面的代码,其中不管是Progrma还是Function,得到参数相应函数前缀都是resolve,简单来说,就是查找相关参数,有则返回,无则添加后返回. Progrma里的addDependency用于添加下面的FunctionInvocation所对应的头文件,其中FFP_LIB_TRANSFORM就是对应的字符串FFPLib_Transform,也就是上面的Cg文件名.而下面的FunctionInvocation中的第一个初始化参数FFP_FUNC_TRANSFORM对应的字符串FFP_Transform,我们可以看到,就是上面的对应函数名,而调用pushOperand把对应参数包装成Operand添加.
渲染状态
用户使用这里的类来控制材质的渲染状态,定制组合渲染.上面定义了RTSS中模拟着色器程序的组成,这里的类则是根据要实现的效果填充模拟着色器程序.
SubRenderState:我们前面所说,固定管线的常用功能有顶点转换,模型颜色,灯光,纹理,Alpha混合,雾等,这里每个固定管线的功能对应着一个SubRenderState,当然我们也可以自定义我们的SubRenderState实现某些特效.他的一些相关字段与方法:
getType():指明当前SubRenderState的类型,如"FFP_Transform","FFP_Fog",如果自己定义的,不要和这些同名.
getExecutionOrder():指明在RenderState中的顺序,一般的顺序为transorm,color,lighting….(自行查看FFPShaderStage),自定义的需要正确设定这个值.
createCpuSubPrograms():当前SubRenderState 需要对ProgramSet的更新,包含参数,头文件,原子函数,可以对照前面的FFPTransform代码来看,这是最简单的createCpuSubPrograms代码了.
updateGpuProgramsParams():有些SubRenderState在渲染Renderable时,需要根据当前Renderable, Pass, 场景参数集合,灯光做相应处理,主要更新参数,如Program中用户自定义而场景自动管理的UniformParameter,当前SubRenderState自定义的参数,或是针对传入的参数的修改.
preAddToRenderState():在添加前RenderState前,一般针对SGPass中的dstPass的修改,一般是针对自定义的纹理.
resolveParameters():当前SubRenderState 需要对ProgramSet的参数获取或添加.
resolveDependencies():当前SubRenderState所需要的头文件.
addFunctionInvocations():当前SubRenderState需要添加的原子函数.
RenderState: SubRenderState的集合,记录各个灯光的个数.这个类中的东东虽然不多,但是在RTSS中承上取下,各个类都多多少少和这个类直接联系.
TargetRenderState: RenderState的子类, RenderState只记录了各个SubRenderState,而这个类还会生成ProgramSet,相应ProgramSet的顶点,片断模拟Program,以及相应模拟Program的入口函数,并把SubRenderState集合更新对应的ProgramSet.(请看上面SubRenderState函数createCpuSubPrograms).
FFPRenderStateBuilder:方便创建常用固定管线的几类SubRenderState到TargetRenderState.
RTSS材质
RTSS本质就是针对原来Material自动生成一个新Technique,新Technique中生成新的Pass.新Pass包含RTSS自动生成的着色器资源.
SGScheme:包含方案名,方案下的SGTechnique集合.方案下的RenderState.
SGMaterial:原材质名,材质下针对各方案的生成的SGTechnique. SGScheme与 SGMaterial并没包含关系.
SGTechnique:如上所说,主要包含一个原Technique,一个RTSS生成的Technique,方案名,是那个材质,SGPass列表,Pass对应的RenderState列表.其中createSGPasses()根据原Technique与RTSS生成的Technique分别得到同一索引下的Pass,构成SGPass,如果当前有自定义的RenderState,赋给相应的SGPass.
SGPass:和SGTechnique一样,包含一个原Pass,一个RTSS生成的Pass.
buildTargetRenderState():生成TargetRenderState,使用FFPRenderStateBuilder针对TargetRenderState创建固定管线的相应SubRenderState,并合并SGPass的自定义RenderState.
acquirePrograms():调用下面的ProgramManager,把TargetRenderState生成的着色器资源添加到RTSS生成的Pass里.
notifyRenderSingleObject():调用TargetRenderState的SubRenderState集合.针对每个SubRenderState调用updateGpuProgramsParams()[此方法前面有说明].
生成着色器
用于着色质生成.前面只说的模拟着色器如何生成,那么模拟着色器Program如何生成对应的GpuProgram,我们来看如下这几个类.
ProgramManager:这个类管理所有模拟着色器程序,生成的GpuProgram,以及下面的二个类.主要作用把ProgramSet中的模拟着色器转化成GpuProgram.并关联到对应的Pass上.
createGpuProgram():使用ProgramWriter(看下面说明)把Program转化成着色器代码,然后根据代码生成HighLevelGpuProgram,设置相应的入口函数,设置profiles,加载.
createGpuPrograms():根据选择的着色器语言,生成正确的ProgramWriter和ProgramProcessor,然后分别调用createGpuProgram生成顶点和片断着色器程序.
acquirePrograms():先调用createGpuPrograms()生成顶点和片断着色器,然后关联到Pass中,最后绑定模拟着色器程序中的UniformParameter到对应GpuProgram中的GpuProgramParameters.这样针对GpuProgram中的UniformParameter改动实际是改动到了GpuProgramParameters中.
ProgramWriter:把模拟着色器程序Program转化成着色器代码,主要把Program的各个部分,头文件,UniformParameter列表,入口函数,原子函数转化成正确的着色器文本.因不同的着色器语言不同的写法,所以子类对应有Cg,glsl,glsles,hlsl.
ProgramProcessor:重新排列Program顶点和片断入口函数的参数.
其实最主要的操作应该是ProgramWriter的几个子类,这几个子类才是把Program转化成GpuProgram的关键,其中代码大家可以仔细看看.他和RenderState还有模拟着色器程序的类图大致如下:
RTSS运行流程
上面我们主要讲解了RTSS中的大部分类,那么这些类是如何开始运行,如何开头,如何更新了,在这先讲一个非常重要,但是前面没有提到的类ShaderGenerator.这个类整合了前面所说的类,并把这个类按照特定顺序执行.先简单说明这个类如下几个方法.
Initialize():初始化上面所说的ProgramManager,FFPRenderStateBuilder,RTSS文件解析节点(从这里来看,应该先初始RTSS,再调用资源的初始化,否则RTSS的文件节点是解析不到的),创建一个默认的SGScheme.
addSceneManager():添加一个场景,二个监听事件,这二个监听事件很重要,下面细说.
createShaderBasedTechnique():根据material创建或返回已经创建的SGMaterial, SGTechnique, SGScheme.并把相应的SGTechnique添加到对应的SGMaterial与SGScheme中.
validateScheme():对应SGScheme开始验证,针对SGScheme的SGTechnique集合验证,验证过程就是生成SGPass,SGPass调用acquirePrograms完成着色器代码创建并关联到对应的Pass上.这个函数一般由Ogre自动调用.
invalidateScheme():如果我们修改了SGScheme里的RenderState,就需要告诉RTSS我已经被修改过,请重新验证.
notifyRenderSingleObject():在渲染模型前,给出模型,Pass,Ogre场景与模型参数,灯光供用户来更新参数,具体请看SubRenderState中的updateGpuProgramsParams().
我们使用ShaderGenerator时,一般先初始化,然后调用addSceneManager添加场景,在添加场景中会添加如下二个监听类.
一是SGSceneManagerListener添加模型以渲染通道前,得到当前场景与视窗.主要二个方法preFindVisibleObject与postFindVisibleObjects.
二是SGRenderObjectListener在渲染当前Renderable前(提供对应Pass,Ogre自动管理参数集,灯光).主要包含notifyRenderSingleObject这个方法.
这二个事件就是RTSS和Ogre核心交互的基本,这二个事件发生的时机请看上文中渲染目标解析,简单说下,Ogre先把模型添加到渲染通道,这个是MovableObject负责,RTSS中的SGSceneManagerListener的preFindVisibleObject 在这个发生之前,而postFindVisibleObjects在这个这后.然后Ogre开始渲染在渲染通道里的模型,这个是Renderable负责,而RTSS中第二个事件中的notifyRenderSingleObject发生前面所说postFindVisibleObjects后,而在Ogre开始渲染模型前.
那么SGSceneManagerListener的preFindVisibleObject监听到时,注意到前面所说的发生位置,这个时候还得到的信息不多,大多是场景管理里的一些参数设置以及对应viewport的设置,RTSS得到viewport后开始验证当前Viewport的材质方案,然后在RTSS中已有的材质方案(SGScheme)中查找,如果没找到,Viewport原来该做啥继续做啥,如果找到了,则找到对应的材质方案(SGScheme)中的开始验证.
首先我们需要知道,SGScheme中的SGTechnique集合如何来的,主要有如下二个方法,在初始化资源文件时,查找到material中的pass包含有rtshader_system节点,那这个材质文件会根据方案名添加对应的方案中.还有就是我们程序员调用方法createShaderBasedTechnique方法,上一种到内部也是调用这个方法.
接着上面的SGScheme开始验证, 针对SGScheme中的SGTechnique集合中的每个SGTechnique调用buildTargetRenderState(),这个方法首先生成SGPass集合,然后调用SGPass中的buildTargetRenderState(), acquirePrograms(),这二个方法请看前面说明. acquirePrograms这个方法完成后,RTSS生成的着色器代码已经附加到原材质中的新Technique中的新Pass中了.最后关闭SGTechnique中的一个状态,说明已经生成过了.
如果有用户自定义的SubRenderState,添加到方案中的RenderState后,我们一般要调用invalidateScheme告诉RTSS需要重新验证,这样在下次渲染时, preFindVisibleObject监听到时,就会检测到方案需要重新验证,这样就生成新的着色器代码.
因为有些参数是自定义的,我们需要在渲染模型前获取某些信息来更新,通过监听SGRenderObjectListener中的notifyRenderSingleObject,我们就可以在相应的SubRenderState里updateGpuProgramsParams 完善参数信息.
RTSS的整个分析差不多就到这里,如果有什么不对的地方,欢迎大家指出.
Ogre RTSS组件解析的更多相关文章
- .NetCore中的日志(1)日志组件解析
.NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...
- Ext 常用组件解析
Ext 常用组件解析 Panel 定义&常用属性 //1.使用initComponent Ext.define('MySecurity.view.resource.ResourcePanel' ...
- Ionic 常用组件解析
Ionic 常用组件解析 $ionicModal(弹出窗口): //创建一个窗口 //此处注意目录的起始位置为app $ionicModal.fromTemplateUrl('app/security ...
- React Native组件(三)Text组件解析
相关文章 React Native探索系列 React Native组件系列 前言 此前介绍了最基本的View组件,接下来就是最常用的Text组件,对于Text组件的一些常用属性,这篇文章会给出简单的 ...
- React Native组件解析(二)之Text
React Native组件解析(二)之Text 1. 概述 Text组件对应于iOS的UILabel,Android的TextView,用来显示文本.但是Text组件的内部使用的并不是flexbox ...
- SpringMVC组件解析
SpringMVC组件解析 1. 前端控制器:DispatcherServlet 用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由 ...
- Sprign-mvc系列之Spring快速入门 什么是sprign-mvc spring-mvc的作用及其基本使用+组件解析+注解解析
Spring-mvc 什么是SpringMvc SpringMvc是一种基于java的实现Mvc设计模式的请求驱动类型的轻量级web框架,属于SpringFrameWork的后续产品,已经融合在Spr ...
- Axiom3D:Ogre地形组件代码解析
大致流程. 这里简单介绍下,Axiom中采用的Ogre的地形组件的一些概念与如何生成地形. 先说下大致流程,然后大家再往下看.(只说如何生成地形与LOD,除高度纹理图外别的纹理暂时不管.) 1.生成T ...
- 跨平台的.NET邮件协议MailKit组件解析
发起的.NET Core开源组织号召,进展的速度是我自己也没有想到的,很多园友都积极参与(虽然有些人诚心砸场子,要是以我以前的宝脾气,这会应该被我打住院了吧,不过幸好是少数,做一件事总有人说好,也有人 ...
随机推荐
- [na]二层sw数据交换
1,同vlan下,两台pc配置了GW,arp请求过程. Pc1 ping pc0的时候,触发pc1的arp请求,发给GW后,GW继续发给pc0(同一个vlan),pc0收到后给pc1回复.Pc1发出i ...
- 3-6-汉诺塔(Hanoi Tower)问题-栈和队列-第3章-《数据结构》课本源码-严蔚敏吴伟民版
课本源码部分 第3章 栈和队列 - 汉诺塔(Hanoi Tower)问题 ——<数据结构>-严蔚敏.吴伟民版 源码使用说明 链接☛☛☛ <数据结构-C语言版> ...
- MySQL开发索引创建规范
1. [强制]业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引. 说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的:另外,即使在应用层做了非 ...
- ASP.NET Core2.0 环境下MVC模式的支付宝PC网站支付接口-沙箱环境开发测试
1.新建.NET Core web项目 2.Controllers-Models-Views 分三个大部分 3.下载安装最新sdk 官方的SDK以及Demo都还是.NET Framework的,根据官 ...
- Oracle XQuery 过滤XML查询SQL
Oralce 支持SQL XQuery查询 一个简单示例: SELECT XMLQuery('for $i in /Videogame return $i/Type' passing by value ...
- iOS开发:代码通用性以及其规范 第二篇(猜想iOS中实现TableView内部设计思路(附代码),以类似的思想实现一个通用的进度条)
在iOS开发中,经常是要用到UITableView的,我曾经思考过这样一个问题,为什么任何种类的model放到TableView和所需的cell里面,都可以正常显示?而我自己写的很多view却只是能放 ...
- Android线程的创建与销毁
摘要: 在Android开发中经常会使用到线程,一想到线程,很多同学就立即使用new Thread(){...}.start()这样的方式.这样如果在一个Activity中多次调用上面的代码,那么将创 ...
- div模态显示内容
业务需要,上传的图片,本地显示大图: 模态代码: <div onclick="hidebigimg()" class = "bg-model" style ...
- js生成验证码并且验证
<html> <head> <title>验证码</title> <style type="text/css"> #co ...
- python matplotlib 画图
import numpy as np import matplotlib.pyplot as plt from pylab import * numpy 常用来组织源数据: 使用 plot 函数直接绘 ...