Directx11教程三十九之DefferedShading(延迟渲染)

这节教程是关于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

 

 

 

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值