DirectX11编程11 Blend混合

环境:VS2017  语言:C++

 

总起:

这一章主要对应红龙书的第九章。

 

工程地址:https://github.com/anguangzhihen/Dx11。主要以Chapter 9_1 Blend Demo作为讲解的基础。

 

本次的文章主要讲解三个知识点:Blend混合、Clip剪裁、Fog雾的实现。

 

首先我们不看Dx11中的Blend,我从Unity的常用的混合公式开始说起。

 

为什么要混合呢?在不透明的世界中混合是没有必要,但是一旦涉及到透明的物体玻璃、水瓶等,就要考虑到当前物体和背景物体是如何进行混合处理的。

 

最常用的公式:Blend SrcAlpha OneMinusSrcAlpha。

 

意思就是当前物体颜色乘以物体Alpha值,而背景的颜色乘以1减去物体Alpha值,再将两者相加就获得了混合后的结果。

 

还有一种常用的是在特效上使用的颜色叠加效果公式:Blend SrcAlpha One。

 

我们来看一下本章能完成的效果:

 

Blend混合:

上述效果图中水和山就是使用Alpha混合。

   

关键代码:

// 创建混合状态
D3D11_BLEND_DESC transparentDesc = { 0 };
transparentDesc.AlphaToCoverageEnable = false;
transparentDesc.IndependentBlendEnable = false;
transparentDesc.RenderTarget[0].BlendEnable = true;
transparentDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
transparentDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
transparentDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
transparentDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
transparentDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
transparentDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
transparentDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
HR(device->CreateBlendState(&transparentDesc, &TransparentBS));

// DrawScene中进行混合
float blendFactor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
md3dImmediateContext->OMSetBlendState(RenderStates::TransparentBS, blendFactor, 0xffffffff);
md3dImmediateContext->DrawIndexed(3 * mWaves.TriangleCount(), 0, 0);
md3dImmediateContext->OMSetBlendState(0, blendFactor, 0xffffffff);

 

我们可以看到类似Unity中的混合公式,没错一模一样。

 

D3D11_BLEND_DESC:

    1.AlphaToCoverageEnable,在开启Multi-sample多重采样时,在透明和不透明之间达到抗锯齿的效果;

    2.IndependentBlendEnable,Dx11支持8张RenderTarget的渲染,如果该值为true,则对于每张RenderTarget的渲染单独制定其描述,否则所有RenderTarget都使用第一个描述。

    3.RenderTarget,指定描述,即混合公式(唯一要注意的是RenderTargetWriteMask参数可以指定输出颜色)。

 

ID3D11DeviceContext::OMSetBlendState:

1.pBlendState,设置想要的混合状态;

2.BlendFactor,在混合公式中使用到了D3D11_BLEND_BLEND_FACTOR或D3D11_BLEND_INV_BLEND_FACTOR时作为参考值;

3.SampleMask,强制指定需要的多重采样(如果将第5个bit位改为0,则第5个采样会被禁用,当然此时你必须要有5个以上的采样才行,只有4或以下是没有效果的)。

 

Clip裁剪:

我们看到效果图中的箱子是中空的,这种效果在片元着色器中使用到了clip技术,判断舍弃Alpha值在0.1以下的像素点。

 

代码:

// clip透明的部分
bool gAlphaClip = true;
if (gAlphaClip)
{
	clip(texColor.a - 0.1f);
}

 

如果clip中的表达式小于0,则会强制舍弃该像素点,并且直接跳过之后代码的执行,所以clip的判断越早执行Shader运行的效率越高。

 

Fog雾:

这种雾的实现非常简单,是一种类似屏幕的后处理效果,实际场景中是没有具体的雾的。

 

按照距离,如果物体离相机越远,则该物体混合的雾颜色越多。

 

具体的计算公式:foggedColor = (1-s)·litColor+s·fogColor。

其中s的计算:s=saturate((dist(p,E)-fogStart)/fogRange)。(saturate相当于clamp(0,1),dist(p,E)代表相机和像素的距离)

 

让我们来看看具体的代码:

// 雾
bool gFogEnabled = true;
if (gFogEnabled)
{
	float fogLerp = saturate((distToEye - gFogPos.x) / gFogPos.y);
	litColor = lerp(litColor, gFogColor, fogLerp);
}