这节教程是关于DeferredShading(延迟渲染)的,程序的结构如下:
看这个教程最好先掌握两样技术:(1) 2D Rendering D3D11教程七之2D渲染
(2)RenderToTexture(渲染到纹理技术)D3D11教程十四之RenderToTexture(RTT技术)
一,延迟渲染技术的简介和原理。
先这里有一篇关于延迟渲染技术大概的介绍,链接:延迟渲染技术
这里我们先不介绍延迟渲染是什么,我先抛出一个问题,想象在一个3D场景中有大量的物体和光源,情况非常复杂,但是无论情况多么复杂,最终渲染到屏幕的一个位置上的像素仅仅有一个,也就是说我们对场景的各种各样的3D物体的部分三角面进行的光照是没意义的,因为对应的那些像素未能渲染到屏幕上,这样就引出了延迟渲染技术,我们仅仅对最终渲染到到屏幕的像素进行光照处理就行了,这就是延迟渲染的本质所在。延迟渲染技术有助于我们渲染那些光照很复杂的场景。
二,延迟渲染技术的使用。
上面说到,延迟渲染就是我们仅仅对最终渲染到屏幕的像素进行光照处理,那么也许你已经想到了使用RTT(渲染到纹理技术).
我们本节教程是渲染一个漫反射光下的立方体,算法分为两大步:
第一步,首先正常渲染场景,利用RTT技术获取两张纹理,一张纹理存储着立方体最终渲染到屏幕的基础纹理的颜色值,另外一张纹理存储着屏幕相应像素的在世界空间的法向量。注意第一步的PixelShader输出的并非color,而是两个变量color和normal。
看看我们的RTT类,我们的RTT类这次有两个ID3D11RenderTargetView和ID3D11ShaderResourceView
DefferredBufferClass.h
#pragma once
#ifndef _DEFFERRED_BUFFER_CLASS_H
#define _DEFFERRED_BUFFER_CLASS_H
#include<Windows.h>
#include<D3D11.h>
#include"Macro.h"
const int BUFFER_COUNT = 2;
class DefferredBufferClass
{
private:
ID3D11Texture2D* mRenderTargetTextureArray[BUFFER_COUNT];
ID3D11RenderTargetView* mRenderTargetViewArray[BUFFER_COUNT];
ID3D11ShaderResourceView* mShaderResourceViewArray[BUFFER_COUNT];
ID3D11Texture2D* mDepthStencilBuffer;
ID3D11DepthStencilView* mDepthStencilView;
D3D11_VIEWPORT md3dViewport;
int TextureWidth, TextureHeight;
public:
DefferredBufferClass();
DefferredBufferClass(const DefferredBufferClass&other);
~DefferredBufferClass();
bool Initialize(ID3D11Device* d3dDevice,int TextureWidth,int TexureHeight,float ScreenDepth,float ScreenNear);
void ShutDown();
void SetRenderTarget(ID3D11DeviceContext* deviceContext);
void ClearRenderTarget(ID3D11DeviceContext* deviceContext,float red, float green, float blue, float alpha);
ID3D11ShaderResourceView* GetShaderResourceView(int index);
};
#endif // !_RENDER_3D_MODEL_TO_TEXTURE_H
DefferredBufferClass.CPP
#include"DefferredBufferClass.h"
DefferredBufferClass::DefferredBufferClass()
{
for (int i = 0; i < BUFFER_COUNT; ++i)
{
mRenderTargetTextureArray[i] = NULL;
mRenderTargetViewArray[i] = NULL;
mShaderResourceViewArray[i] = NULL;
}
mDepthStencilBuffer = NULL;
mDepthStencilView = NULL;
}
DefferredBufferClass::DefferredBufferClass(const DefferredBufferClass&other)
{
}
DefferredBufferClass::~DefferredBufferClass()
{
}
bool DefferredBufferClass::Initialize(ID3D11Device* d3dDevice, int TextureWidth, int TexureHeight, float ScreenDepth, float ScreenNear)
{
//第一,填充2D纹理形容结构体,并创建2D渲染目标纹理
D3D11_TEXTURE2D_DESC textureDesc;
ZeroMemory(&textureDesc, sizeof(textureDesc));
textureDesc.Width = TextureWidth;
textureDesc.Height = TexureHeight;
textureDesc.MipLevels = 1;
textureDesc.ArraySize = 1;
textureDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; //纹理像素为12个字节
textureDesc.SampleDesc.Count = 1;
textureDesc.Usage = D3D11_USAGE_DEFAULT;
textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
textureDesc.CPUAccessFlags = 0;
textureDesc.MiscFlags = 0;
for (int i = 0; i < BUFFER_COUNT; ++i)
{
HR(d3dDevice->CreateTexture2D(&textureDesc, NULL, &mRenderTargetTextureArray[i]));
}
//第二,填充渲染目标视图形容体,并进行创建目标渲染视图
D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
renderTargetViewDesc.Format = textureDesc.Format;
renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
renderTargetViewDesc.Texture2D.MipSlice = 0;
for (int i = 0; i < BUFFER_COUNT; ++i)
{
HR(d3dDevice->CreateRenderTargetView(mRenderTargetTextureArray[i], &renderTargetViewDesc, &mRenderTargetViewArray[i]));
}
//第三,填充着色器资源视图形容体,并进行创建着色器资源视图
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
shaderResourceViewDesc.Format = textureDesc.Format;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
for (int i = 0; i < BUFFER_COUNT; ++i)
{
HR(d3dDevice->CreateShaderResourceView(mRenderTargetTextureArray[i], &shaderResourceViewDesc, &mShaderResourceViewArray[i]));
}
//第四,填充2DTexture深度缓存(模板缓存)形容结构体,创建深度缓存(模板缓存)
D3D11_TEXTURE2D_DESC depthStencilDesc;
ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
depthStencilDesc.Width = TextureWidth;
depthStencilDesc.Height = TexureHeight;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
HR(d3dDevice->CreateTexture2D(&depthStencilDesc,//要创建的纹理的形容
0,
&mDepthStencilBuffer)); //指向深度缓存的指针
//第五,填充深度缓存视图形容结构体,创建深度缓存(模板缓存)视图
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;
HR(d3dDevice->CreateDepthStencilView(
mDepthStencilBuffer, //我们基于这个深度缓存/漏字板缓存创建一个视图
&depthStencilViewDesc,
&mDepthStencilView));//指向深度缓存/漏字板视图的指针
//第六,设置渲染的视口
md3dViewport.Width = static_cast<float>(TextureWidth);
md3dViewport.Height = static_cast<float>(TexureHeight);
md3dViewport.MinDepth = 0.0f;
md3dViewport.MaxDepth = 1.0f;
md3dViewport.TopLeftX = 0.0f;
md3dViewport.TopLeftY = 0.0f;
return true;
}
void DefferredBufferClass::ShutDown()
{
for (int i = 0; i < BUFFER_COUNT; ++i)
{
ReleaseCOM(mShaderResourceViewArray[i]);
ReleaseCOM(mRenderTargetTextureArray[i]);
ReleaseCOM(mRenderTargetViewArray[i]);
}
ReleaseCOM(mDepthStencilBuffer);
ReleaseCOM(mDepthStencilView);
}
//让此时所有图形渲染到这个目前渲染的位置
void DefferredBufferClass::SetRenderTarget(ID3D11DeviceContext* deviceContext)
{
//绑定渲染目标视图和深度模板视图到输出渲染管线,此时渲染输出到两张纹理中
deviceContext->OMSetRenderTargets(BUFFER_COUNT, mRenderTargetViewArray, mDepthStencilView);
//设置相应的视口
deviceContext->RSSetViewports(1, &md3dViewport);
}
void DefferredBufferClass::ClearRenderTarget(ID3D11DeviceContext* deviceContext,float red, float green, float blue, float alpha)
{
//设置清除缓存为的颜色
float color[4];
color[0] = red;
color[1] = green;
color[2] = blue;
color[3] = alpha;
//清除背后缓存
for (int i = 0; i < BUFFER_COUNT; ++i)
{
deviceContext->ClearRenderTargetView(mRenderTargetViewArray[i], color);
}
//清除深度缓存和模板缓存
deviceContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
}
// 将“被渲染模型到纹理的纹理”作为ShaderResourceView资源返回,这个资源将会跟其它的ShaderResourceView资源一样被送入Shader里计算.
ID3D11ShaderResourceView* DefferredBufferClass::GetShaderResourceView(int index)
{
return mShaderResourceViewArray[index];
}
延迟渲染Shader代码:
DefferredShader.fx
Texture2D ShaderTexture:register(t0); //纹理资源
SamplerState SampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
matrix WorldInvTranspose;
};
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
float3 Normal:NORMAL;
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float2 Tex:TEXCOORD0;
float3 W_Normal:NORMAL; //世界空间的法线
};
struct PixelOut
{
float4 color:SV_Target0;
float4 normal:SV_Target1;
};
VertexOut VS(VertexIn ina)
{
VertexOut outa;
//将坐标变换到齐次裁剪空间
outa.Pos = mul(float4(ina.Pos,1.0f), World);
outa.Pos = mul(outa.Pos, View);
outa.Pos = mul(outa.Pos, Proj);
//将法线变换到世界空间
outa.W_Normal = mul(ina.Normal, (float3x3)WorldInvTranspose); //此事世界逆转置矩阵的第四行本来就没啥用
outa.W_Normal = normalize(outa.W_Normal);
outa.Tex= ina.Tex;
return outa;
}
/*延迟渲染的PixelShader输出的为屏幕上的未经处理的渲染到屏幕的像素和像素对应的法线*/
PixelOut PS(VertexOut outa) : SV_Target
{
PixelOut pout;
//第一,获取像素的采样颜色
pout.color = ShaderTexture.Sample(SampleType, outa.Tex);
//第二,获取像素的法线量
pout.normal = float4(outa.W_Normal, 1.0f);
return pout;
}
从上面代码我们注意到PixelShader的输出已经改变了,Pixelout结构体里的两个变量 color和normal 绑定签名分别为SV_Target0和SV_Target1,也就是color输出到DefferBufferClass类的mShaderResourceViewArray[0],normal输出到DefferBufferClass类的mShaderResourceViewArray[1].
第二步,利用2DRendering技术 进行传统的渲染(forward shading)。
来看看我们第二步正常渲染的Shader代码:
LightShader.fx
Texture2D ColorTexture:register(t0); //颜色纹理
Texture2D NormalTexture:register(t1); //法向量纹理
SamplerState SampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
};
cbuffer CBLight:register(b1)
{
float4 DiffuseColor;
float3 LightDirection;
float pad;
}
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float2 Tex:TEXCOORD0;
};
VertexOut VS(VertexIn ina)
{
VertexOut outa;
matrix a = { {1.0f,0.0f,0.0f,0.0f},{ 0.0f,1.0f,0.0f,0.0f },{ 0.0f,0.0f,1.0f,0.0f },{0.0f,0.0f,0.0f,1.0f }};
//将坐标变换到相应的齐次裁剪空间
//outa.Pos = mul(float4(ina.Pos,1.0f), World);
//outa.Pos = mul(, a);
outa.Pos = mul(float4(ina.Pos, 1.0f), View);
outa.Pos = mul(outa.Pos, Proj);
//获取纹理坐标
outa.Tex= ina.Tex;
return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
float4 TexColor; //采集的纹理颜色
float4 Normal;
float LightFactor; //灯光因子
float4 color = {0.0f,0.0f,0.0f,0.0f}; //最终输出的颜色
//第一,获取像素的采样颜色
TexColor = ColorTexture.Sample(SampleType, outa.Tex);
//第二,获取像素的采样法向量
Normal = NormalTexture.Sample(SampleType, outa.Tex);
//第三,求出灯光因子
float3 InvLightDir = -LightDirection;
LightFactor = saturate(dot(InvLightDir, Normal.xyz));
//第四,求出灯光照射颜色
if (LightFactor>0)
{
color += LightFactor*DiffuseColor; //saturate(float1*float4)
}
color = saturate(color);
//return TexColor;
//第五,用灯光颜色调节纹理颜色
color = color*TexColor;
return color;
}
最后我们的运行结果:
源码链接
3D渲染引擎Git源码
https://github.com/2047241149/SDEngine
简单版本:
http://download.csdn.net/detail/qq_29523119/9685028