Directx11教程四十五之MotionBlur(运用VelocityBuffer)下

程序结构:


MotionBlur实现过程:

总体分为四个pass:

(1)RenderGbufferPass,  得到colorRT,velocityBufferRT,DepthRT

Texture2D ShaderTexture:register(t0);  //纹理资源
SamplerState SampleType:register(s0);   //采样方式


static const float EPSILON1 = 0.01f;

static const float2 VONE = float2(1.0f, 1.0f);
static const float2 VHALF = float2(0.5f, 0.5f);

//VertexShader
cbuffer CBCurMatrix:register(b0)
{
	matrix curWorld;
	matrix curView;
	matrix Proj;
	matrix WorldInvTranspose;
};

cbuffer CBPreMatrix:register(b1)
{
	matrix preWorld;
	matrix preView;
};

cbuffer CBOther:register(b2)
{
	float halfExpore;		//0.5f
	float halfExporeXFrame;		//FPS的一半		
	float c_k;
	float pad;
}


struct VertexIn
{
	float3 Pos:POSITION;
	float3 Normal:NORMAL;
	float2 Tex:TEXCOORD;  //多重纹理可以用其它数字
};


struct VertexOut
{
	float4 Pos:SV_POSITION;
	float2 Tex:TEXCOORD1;
	float4 curClipSpacePos:TEXCOORD2;
	float4 preClipSpacePos:TEXCOORD3;
};


struct PixelOut
{
	float4 color:SV_Target0;
	float4 velocity:SV_Target1;
};


float2 writeBiasScale(float2 v)
{

	return (v + VONE) * VHALF;

}

VertexOut VS(VertexIn ina)
{
	VertexOut outa;

	//将坐标变换到齐次裁剪空间
	float4 curClipSpacePos = mul(float4(ina.Pos, 1.0f), curWorld);
	curClipSpacePos = mul(curClipSpacePos, curView);
	curClipSpacePos = mul(curClipSpacePos, Proj);
	outa.Pos = curClipSpacePos;

	//目前帧的齐次裁剪空间位置
	outa.curClipSpacePos = curClipSpacePos;

	//前一帧的齐次裁剪空间位置
	float4 preClipSpacePos = mul(float4(ina.Pos, 1.0f), preWorld);
	preClipSpacePos = mul(preClipSpacePos, preView);
	preClipSpacePos = mul(preClipSpacePos, Proj);
	outa.preClipSpacePos = preClipSpacePos;

	outa.Tex= ina.Tex;
	return outa;
}


/*延迟渲染的PixelShader输出的为屏幕上的未经处理的渲染到屏幕的像素和像素对应的法线*/
PixelOut PS(VertexOut outa) : SV_Target
{
	PixelOut pout;

	//第一,获取像素的采样颜色
    pout.color = ShaderTexture.Sample(SampleType, outa.Tex);

	float3 curNDCPos = outa.curClipSpacePos.xyz / outa.curClipSpacePos.w;
	float3 preNDCPos = outa.preClipSpacePos.xyz / outa.preClipSpacePos.w;


	//[-1,1] * FPS,也就是每帧在NDC空间的移动[-1,1]扩大FPS倍
	float2 vQX = (curNDCPos - preNDCPos).xy *	halfExporeXFrame;

	float fLenQX = length(vQX);

	
	float fWeight = max(0.5, min(fLenQX, c_k));

	fWeight /= (fLenQX + EPSILON1);

	//实现速度在一个范围内
	vQX *= fWeight;

	pout.velocity = float4(writeBiasScale(vQX),0.5f, 1.0f);

	return pout;
}

colorRT:



VelocityBufferRT:



(2)RenderTileMaxPass,得到tileMaxVelocityRT

Texture2D velocityRT:register(t0);  //纹理资源
SamplerState SamplePointClamp:register(s0);   //采样方式
cbuffer CBEveryFrame:register(b0)
{
	float c_k;
	float3 pad;
};
struct VertexIn
{
	float3 Pos:POSITION;
	float2 Tex:TEXCOORD;  //多重纹理可以用其它数字
};
struct VertexOut
{
	float4 Pos:SV_POSITION;
	float2 Tex:TEXCOORD0;
};
static const float4 GRAY = float4(0.5f, 0.5f, 0.5f, 1.0f);
static const float2 VONE = float2(1.0f, 1.0f);
static const float2 VTWO = float2(2.0f, 2.0f);
static const float2 VHALF = float2(0.5f, 0.5f);
float2 textureSize(Texture2D tex)
{
	uint width, height;
	tex.GetDimensions(width, height);
	return float2(width, height);
}

float2 readBiasScale(float2 v)
{
	return (v * VTWO) - VONE;
}

float2 writeBiasScale(float2 v)
{
	return (v + VONE) * VHALF;
}
VertexOut VS(VertexIn ina)
{
	VertexOut outa;
	outa.Pos = float4(ina.Pos, 1.0f);
	outa.Tex= ina.Tex;
	return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
	float4 color = GRAY;
	float2 texCoordBase = outa.Tex;
	float2 texCoordIncrement = float2(1.0f, 1.0f) / textureSize(velocityRT);
	float fMaxManitudeSquared = 0.0f;

	//实现一个像素存储的速度为临近像素存储速度的最大值
	for (int s = 0; s < c_k; ++s)
	{
		for (int t = 0; t < c_k; ++t)
		{
			float2 texCoords = texCoordBase + float2(s, t) * texCoordIncrement;
			float2 texLookup = velocityRT.SampleLevel(SamplePointClamp, texCoords, 0).xy;
			float2 velocity = readBiasScale(texLookup);
			float fMagitudeSpuard = dot(velocity, velocity);
			if (fMaxManitudeSquared < fMagitudeSpuard)
			{
				color.xy = writeBiasScale(velocity);
				fMaxManitudeSquared = fMagitudeSpuard;
			}
		}
	}
	return color;
}



(3)RenderNeigborMaxPass,得到NeighborMaxVelocityRT:

Texture2D velocityTileMaxRT:register(t0);  //纹理资源
SamplerState SamplePointClamp:register(s0);   //采样方式
static const float2 VONE = float2(1.0f, 1.0f);
static const float2 VTWO = float2(2.0f, 2.0f);
static const float2 VHALF = float2(0.5f, 0.5f);
struct VertexIn
{
	float3 Pos:POSITION;
	float2 Tex:TEXCOORD;  //多重纹理可以用其它数字
};
struct VertexOut
{
	float4 Pos:SV_POSITION;
	float2 Tex:TEXCOORD0;
};
float2 textureSize(Texture2D tex)
{
	uint width, height;
	tex.GetDimensions(width, height);
	return float2(width, height);
}
float2 readBiasScale(float2 v)
{
	return (v * VTWO) - VONE;
}
float2 writeBiasScale(float2 v)
{
	return (v + VONE) * VHALF;
}
VertexOut VS(VertexIn ina)
{
	VertexOut outa;
	outa.Pos = float4(ina.Pos, 1.0f);
	outa.Tex= ina.Tex;
	return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
	float4 color = 1;
	float2 texcoordBase = outa.Tex;
	float2 texCoordIncrement = float2(1.0f, 1.0f) / textureSize(velocityTileMaxRT);
	float fMaxMagnitideSpuared = 0.0f;
	for (int s = -1; s <= 1; ++s)
	{
		for (int t = -1; t <= 1; ++t)
		{
			float2 texCoords = texcoordBase + float2(s, t) * texCoordIncrement;
			float2 texLookup = velocityTileMaxRT.SampleLevel(SamplePointClamp, texCoords, 0).xy;
			float2 velocity = readBiasScale(texLookup);
			float fMagitudeSpuard = dot(velocity, velocity);
			if (fMaxMagnitideSpuared < fMagitudeSpuard)
			{
				float fDisplacement = abs(float(s)) + abs(float(t));
				float2 vOrientation = sign(float2(s, t)* velocity);
				float distance = vOrientation.x + vOrientation.y;
				if (abs(distance) == fDisplacement)
				{
					color.xy = writeBiasScale(velocity);
					fMaxMagnitideSpuared = fMagitudeSpuard;
				}	
			}
		}
	}
	return color;
}



(4)运用多张RT(colorRT,depthRT,VelocityRT,randomRT,neightborMaxRt)运用随机采样重构法,计算移动模糊的像素:

Texture2D colorRT:register(t0);
Texture2D velocityRT:register(t1);
Texture2D neighborMaxRT:register(t2);
Texture2D depthRT:register(t3);
Texture2D randomRT:register(t4);
static const float EPSILON1 = 0.01f;
static const float HALF_VELOCITY_CUTOFF = 0.25f;
static const float VARIANCE_THRESHOLD = 1.5f;
static const float WEIGHT_CORRECTION_FACTOR = 60.0f;
static const float SOFT_Z_EXTENT = 0.10f;
static const float CYLINDER_CORNER_1 = 0.95f;
static const float CYLINDER_CORNER_2 = 1.05f;
static const float2 VHALF = float2(0.5f, 0.5f);
static const float2 VONE = float2(1.0f, 1.0f);
static const float2 VTWO = float2(2.0f, 2.0f);
SamplerState SamPointWarp:register(s0);		//采样方式
SamplerState SamLinearClamp:register(s1);   //采样方式
SamplerState SamPointClamp:register(s2);   //采样方式
cbuffer CBEveryFrame:register(b0)
{
	float c_half_exposure;
	float c_k;
	float c_s;
	float c_max_sample_tab_distance;
};
struct VertexIn
{
	float3 Pos:POSITION;
	float2 Tex:TEXCOORD;  //多重纹理可以用其它数字
};
struct VertexOut
{
	float4 Pos:SV_POSITION;
	float2 Tex:TEXCOORD0;
};
float2 readBiasScale(float2 v)
{
	return (v * VTWO) - VONE;
};
float GetDepth(float2 uv)
{
	return -(depthRT.SampleLevel(SamPointClamp, uv, 0).r);
};
float psseudoRandom(float2 uv)
{
	return randomRT.SampleLevel(SamPointWarp, uv, 0).r - 0.5f;
};
float2 textureSize(Texture2D tex)
{
	uint width, height;
	tex.GetDimensions(width, height);
	return float2(width, height);
};
float softDepthCompare(float za, float zb)
{
	return clamp((1.0 - (za - zb) / SOFT_Z_EXTENT), 0.0, 1.0);
};
float cone(float magDiff, float magV)
{
	return 1.0f - abs(magDiff) / magV;
};
float cylinder(float magDiff, float magV)
{
	return 1.0 - smoothstep(CYLINDER_CORNER_1 * magV, CYLINDER_CORNER_2 * magV, abs(magDiff));
};
VertexOut VS(VertexIn ina)
{
	VertexOut outa;
	outa.Pos = float4(ina.Pos, 1.0f);
	outa.Tex = ina.Tex;
	return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
	float2 texSize = textureSize(colorRT);
	float2 texcoordBase = outa.Tex;
	float4 texColor = colorRT.SampleLevel(SamLinearClamp, texcoordBase, 0);
	//相邻最大速度变量
	float2 nVelocity = readBiasScale(neighborMaxRT.Sample(SamPointClamp, texcoordBase).xy);
	float nVelocityLen = length(nVelocity);
	float TempNeiVelocity = nVelocityLen * c_half_exposure;
	bool flagNVelocity = (TempNeiVelocity >= EPSILON1);
	TempNeiVelocity = clamp(TempNeiVelocity, 0.1f, c_k);
	//如果速度太小,则直接返回基本的颜色
	if (TempNeiVelocity < HALF_VELOCITY_CUTOFF)
	{
		return texColor;
	}
	//纠正halfVelocity
	if (flagNVelocity)
	{
		nVelocity *= (TempNeiVelocity / nVelocityLen);
		nVelocityLen = length(nVelocity);
	}
	//速度变量
	float2 velocity = readBiasScale(velocityRT.Sample(SamPointClamp, texcoordBase).xy);
	float velocityLen = length(velocity);
	float TempVelocity = velocityLen * c_half_exposure;
	bool flagVelocity = (TempVelocity >= EPSILON1);
	TempVelocity = clamp(TempVelocity, 0.1f, c_k);
	if (flagVelocity)
	{
		velocity *= (TempVelocity / velocityLen);
		velocityLen = length(velocity);
	}
	//在【-0.5f, 0.5f】之间的随机值
	float r = psseudoRandom(texcoordBase);
	//获取深度缓存值
	float depth = GetDepth(texcoordBase);

	//如果目前片元的速度太小,则使用NeighborMaxVelocity
	float2 CorrectedVelocity = (velocityLen < VARIANCE_THRESHOLD) ?
		normalize(nVelocity) : normalize(velocity);
	//权重值
	float weight = c_s / WEIGHT_CORRECTION_FACTOR / TempVelocity;
	//累积和
	float3 sum = texColor.xyz * weight;
	//相同片元的下标
	int selfIndex = (c_s - 1) / 2;
	float max_sample_tab_distance = c_max_sample_tab_distance / texSize.x;
	float2 half_texel = VHALF / texSize.x;

	//循环进行重构采样中
	for (int i = 0; i < c_s; ++i)
	{
		//跳过相同的片元
		if (i == selfIndex) { continue; }

		//T为目前片元和采样片元之间的距离
		//采样不是邻居的片元,而是更远的
		float lerpAmount = (float(i) + r + 1.0f) / (c_s + 1.0f);
		float T = lerp(-max_sample_tab_distance, max_sample_tab_distance, lerpAmount);
		//在纠正速度变量和相邻速度变量
		float2 Switch = (i & 1 == 1) ? CorrectedVelocity : nVelocity;
		//目前采样的位置
		float2 newSampleTexcoord = float2(texcoordBase + float2(Switch * T + half_texel));
		float2 newVelocity = readBiasScale(velocityRT.SampleLevel(SamPointClamp, newSampleTexcoord, 0).xy);
		float newVelocityLen = length(newVelocity);
		//纠正,clamp half_velocity
		float TempNewVelocity = newVelocityLen * c_half_exposure;
		bool flagNewVelocity = (TempNewVelocity > EPSILON1);
		TempNewVelocity = clamp(TempNewVelocity, 0.1f, c_k);
		if (flagNewVelocity)
		{
			newVelocity *= (TempNewVelocity / newVelocityLen);
			newVelocityLen = length(newVelocity);
		}
		//新采样点的深度
		float newDepth = GetDepth(newSampleTexcoord);
		float alphaY = softDepthCompare(depth, newDepth) * cone(T, TempNewVelocity)+
			softDepthCompare(newDepth, depth) * cone(T, TempVelocity)+ cylinder(T, TempNewVelocity) * cylinder(T, TempVelocity) * 2.0;
		weight += alphaY;
		sum += (alphaY *  colorRT.SampleLevel(SamLinearClamp, newSampleTexcoord, 0)).xyz;
	}
	return float4(sum / weight, 1.0f);
}




参考 资料:

【1】NVIDIA SDK 的 AdvancedMotionBlur Sample

http://docs.nvidia.com/gameworks/index.html#gameworkslibrary/graphicssamples/d3d_samples/motionblurd3dadvancedsample.htm

【2】McGuire M., Hennessy P., Bukowski M., Osman B.: A reconstruction filter for plausible motion blue. In I3D (2012), pp. 135-142.,非常推荐看这篇paper


源码链接:

https://download.csdn.net/download/qq_29523119/10308144

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值