1、简介:
在渲染场景中的所有objects,而不包含光照。而且这些objects并不是被渲染到back buffer中,而是使用multiple render targets(MRT)渲染到一些off-screen buffers中(离线的bufffers)。在这些render targets中存储了场景的geometry数据,包括object的position,normal,color(一般是从一个diffuse color texture中采样得到),specular power和specular intensity。前面已经讲过render targets其实就是2D textures,但是在这里这些 geometry buffers(也称为G-buffer) 并不是存储color值,而是存储影响objects的lighting passs数据用于计算场景中所有pixels的最终color值。
对于场景中的每一种光源,都是从geometry buffers中采样数据,并计算被该光源影响的每一个pixel的直接和间接光照值。最后使用场景中所有光源产生的影响组合成最终的图像数据,并映射到一个full-screen quad中。
由于光照是在screen space中进行计算,渲染每一帧的计算成本主要由光源的数量以及受光源影响的pixels数量决定。Geometry数据是每一帧渲染一次,并且只对真正受到光源影响的geometry才会执行高成本的光照运算。除了具有高性能的优点之外,deferred shading还能简单一个渲染引擎的架构设计,因为使用这种方法场景中的所有objects可以共用一个shader。经常切换shaders会产生较大的性能开销,因此使用forward rendering方法的渲染引擎通常会批量处理使用相同materials的objects。如果不使用deferred shading系统,就需要一个更复杂的引擎架构。
缺点:
第一:使用deferred shading只能共用一种materials。
第二:geometry buffers需要占用大量的内存。
第三:deferred shading无法处理transparency(透明),透明效果通常由一个排序步骤 单独处理(from back to front,也就是距离camera的距离由远到近),并对透明的objects使用forward rendering pass。
第四:把geometry和lighting阶段分开计算,就无法使用硬件的anti-aliasing功能,因此需要使用一个额外的post-processing technique处理边缘的光滑度。
2、步骤:
1. 创建G-Buffer:
compositor DeferredShading/GBuffer
{
technique
{
// temporary textures
texture mrt_output target_width target_height PF_FLOAT16_RGBA PF_FLOAT16_RGBA chain_scope
target mrt_output
{
input none
pass clear
{
}
shadows off
material_scheme GBuffer
// everything but the lights and their meshes
// could do this with something like a visibility mask too
pass render_scene
{
//These values are synchronized with the code
first_render_queue 10
last_render_queue 79
}
}
}
}
上面创建G-Buffer合成器注意事项:
- mrt_output 创建是两个纹理(MRT)
- mrt_output 访问范围同一合成器链的后续合成器可用(类型:chain_scope)
- pass中只能将渲染队列是10-79中的对象信息渲染到纹理上。并且为每个对象手动创建一个将信息写到G-Buffer中的技术方案
- 这个合成器没有target_output pass因为G-Buffer是用来存储计算光照信息而不是最终渲染的纹理
- 两个纹理(RenderTexture)存储信息:
第一个纹理RGB:漫反射光颜色(DiffuseColour) 。 A:强度值(Specularity)
第二个纹理RGB:相机空间下法线 。A:深度值
2. 给满足渲染G-Buffer的对象生成技术方案:
(1)、重写Ogre::MaterialManager::Listener 为GBufferSchemeHandler对象因为每次遍历renderable设置技术方案时Ogre::MaterialManager会查找当前活动材质名(这里我们设置名GBuffer),如果没有就执行这句GBufferSchemeHandler::handleSchemeNotFound() ,在这个方法里将我们为每个对象单独创建的ToGBuffer技术方案设置为当前活动的方案,如果是透明对象则设置名“NoGBuffer”(这个对象不渲染到GBuffer中后期处理).
GBufferSchemeHandler::inspectPass() 这个方法是用来检测该对象是否透明、漫反射颜色是否是白色、纹理是否有法线纹理等
(2)、GBufferMaterialGenerator 类对象是给每个对象创建生成GBuffer信息的技术方案,因为每个对象的存储信息可能不一样所以我们要单独为每个对象动态创建技术方案。
例如某个对象创建的shader:
//顶点着色器
Ogre: void ToGBufferVP(
float4 iPosition : POSITION,
float3 iNormal : NORMAL,
float2 iUV0 : TEXCOORD0,
out float4 oPosition : POSITION,
out float3 oViewPos : TEXCOORD0,
out float3 oNormal : TEXCOORD1,
out float2 oUV0 : TEXCOORD2,
uniform float4x4 cWorldViewProj,
uniform float4x4 cWorldView
)
{
oPosition = mul(cWorldViewProj, iPosition);
oNormal = mul(cWorldView, float4(iNormal,0)).xyz;
oViewPos = mul(cWorldView, iPosition).xyz;
oUV0 = iUV0;
}
//片元着色器
Ogre: void ToGBufferFP(
float3 iViewPos : TEXCOORD0,
float3 iNormal : TEXCOORD1,
float2 iUV0 : TEXCOORD2,
out float4 oColor0 : COLOR0,
out float4 oColor1 : COLOR1,
uniform sampler sTex0 : register(s0),
uniform float4 cDiffuseColour,
uniform float cFarDistance,
uniform float cSpecularity
)
{
oColor0.rgb = tex2D(sTex0, iUV0).rgb;
oColor0.rgb *= cDiffuseColour.rgb;
oColor0.a = cSpecularity;
oColor1.rgb = normalize(iNormal);
oColor1.a = length(iViewPos) / cFarDistance;
}
Technique* GBufferSchemeHandler::handleSchemeNotFound(unsigned short schemeIndex,
const String& schemeName, Material* originalMaterial, unsigned short lodIndex,
const Renderable* rend)
{
Ogre::MaterialManager& matMgr = Ogre::MaterialManager::getSingleton();
String curSchemeName = matMgr.getActiveScheme();
matMgr.setActiveScheme(MaterialManager::DEFAULT_SCHEME_NAME);
Technique* originalTechnique = originalMaterial->getBestTechnique(lodIndex, rend);
matMgr.setActiveScheme(curSchemeName);
Technique* gBufferTech = originalMaterial->createTechnique();
gBufferTech->removeAllPasses();
gBufferTech->setSchemeName(schemeName);
#ifdef OGRE_BUILD_COMPONENT_RTSHADERSYSTEM
RTShader::ShaderGenerator& rtShaderGen = RTShader::ShaderGenerator::getSingleton();
rtShaderGen.createShaderBasedTechnique(*originalMaterial, originalTechnique->getSchemeName(), "NoGBuffer");
#else
Technique* noGBufferTech = originalMaterial->createTechnique();
noGBufferTech->removeAllPasses();
noGBufferTech->setSchemeName("NoGBuffer");
#endif
for (unsigned short i=0; i<originalTechnique->getNumPasses(); i++)
{
Pass* originalPass = originalTechnique->getPass(i);
PassProperties props = inspectPass(originalPass, lodIndex, rend);
if (!props.isDeferred)
{
#ifdef OGRE_BUILD_COMPONENT_RTSHADERSYSTEM
rtShaderGen.validateMaterial("NoGBuffer", originalMaterial->getName(), originalMaterial->getGroup());
#else
//Just copy the technique so it gets rendered regularly
Pass* clonePass = noGBufferTech->createPass();
*clonePass = *originalPass;
#endif
continue;
}
Pass* newPass = gBufferTech->createPass();
MaterialGenerator::Perm perm = getPermutation(props);
const Ogre::MaterialPtr& templateMat = mMaterialGenerator.getMaterial(perm);
//We assume that the GBuffer technique contains only one pass. But its true.
*newPass = *(templateMat->getTechnique(0)->getPass(0));
fillPass(newPass, originalPass, props);
}
return gBufferTech;
}
3. 计算光照显示结果:
compositor DeferredShading/ShowLit
{
technique
{
//Reference the main Gbuffer texture
texture_ref mrt_output DeferredShading/GBuffer mrt_output
target_output
{
input none
//We will dispatch the shadow texture rendering ourselves
shadows off
pass clear
{
}
// render skies and other pre-gbuffer objects
pass render_scene
{
first_render_queue 1
last_render_queue 9
}
//Render the lights and their meshes
pass render_custom DeferredLight
{
input 0 mrt_output 0
input 1 mrt_output 1
}
//Render the objects that skipped rendering into the gbuffer
pass render_scene
{
material_scheme NoGBuffer
first_render_queue 10
last_render_queue 79
}
//Render the post-GBuffer render queue objects
pass render_scene
{
//This value is synchronized with the code
first_render_queue 80
}
}
}
}
(1)、这个合成器有target_output所以这个合成器输出的是我们最终的结果。前面两个pass先清空当前场景然后渲染队列1-9的对象(天空盒等)
(2)、第三个pass 类型是render_custom 意思就是我们可以执行自己定义的Composition Pass。 分别定义了DeferredLightRenderOperation 和DeferredLightCompositionPass 对象 。输入GBuffer中的两个纹理。
class DeferredLightCompositionPass : public Ogre::CustomCompositionPass
{
public:
/** 创建custom RenderSystemOperation*/
virtual Ogre::CompositorInstance::RenderSystemOperation* createOperation(
Ogre::CompositorInstance* instance, const Ogre::CompositionPass* pass)
{
return OGRE_NEW DeferredLightRenderOperation(instance, pass);
}
protected:
virtual ~DeferredLightCompositionPass() {}
};
class DeferredLightRenderOperation : public Ogre::CompositorInstance::RenderSystemOperation
{
public:
//生成灯光的材质文件
DeferredLightRenderOperation(Ogre::CompositorInstance* instance, const Ogre::CompositionPass* pass);
/** @copydoc CompositorInstance::RenderSystemOperation::execute */
virtual void execute(Ogre::SceneManager *sm, Ogre::RenderSystem *rs);
virtual ~DeferredLightRenderOperation();
private:
/** Create a new deferred light */
DLight *createDLight(Ogre::Light* light);
//The texture names of the GBuffer components
Ogre::String mTexName0;
Ogre::String mTexName1;
//The material generator for the light geometry
MaterialGenerator* mLightMaterialGenerator;
//The map of deferred light geometries already constructed
typedef std::map<Ogre::Light*, DLight*> LightsMap;
LightsMap mLights;
//The ambient light used to render the scene
AmbientLight* mAmbientLight;
//The viewport that we are rendering to
Ogre::Viewport* mViewport;
};
DeferredLightRenderOperation ::execute 方法中先设置AmbientLight的常量farCorner值和渲染环境光,然后遍历每个灯创建DLight对象,更新阴影纹理和调用方法SceneManager::_injectRenderWithPass渲染其光照。
(3)、渲染透明对象和大于80渲染队列组中的对象