D3D渲染技术之混合案例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxw167/article/details/83007378

在本篇内容中,我们将介绍用于获得特定效果的一些混合因子组合, 在这些示例中,我们仅查看RGB混合, Alpha混合的处理方式类似。

无颜色写入

假设我们想要保持原始目标像素的原样,而不是覆盖它或将其与当前光栅化的源像素混合。例如,如果我们只想写入深度/模板缓冲区,而不是后台缓冲区。 为此,请将源像素混合因子设置为D3D12_BLEND_ZERO,将目标混合因子设置为D3D12_BLEND_ONE,将混合运算符设置为D3D12_BLEND_OP_ADD。 通过此设置,混合等式可简化为:
在这里插入图片描述
在这里给读者举一个例子,实现的方法是将D3D12_RENDER_TARGET_BLEND_DESC :: RenderTargetWriteMask成员设置为0,这样就不会写入任何颜色通道。
在这里插入图片描述
上图中,添加源和目标颜色, 添加颜色后,添加会创建更亮的图像。

增加/减少

假设我们要添加具有目标像素的源像素(见上图所示)。 要执行此操作,请将源混合因子设置为D3D12_BLEND_ONE,将目标混合因子设置为D3D12_BLEND_ONE,将混合运算符设置为D3D12_BLEND_OP_ADD。 通过此设置,混合等式可简化为:
在这里插入图片描述
我们可以使用上面的混合因子从目标像素中减去源像素,并用D3D12_BLEND_OP_SUBTRACT替换混合操作(见下图所示)。
在这里插入图片描述
从目标颜色中减去源颜色, 由于颜色被移除,减法会产生较暗的图像。

相乘

假设我们想要将源像素与其对应的目标像素相乘(见下图所示), 为此,我们将源混合因子设置为D3D12_BLEND_ZERO,将目标混合因子设置为D3D12_BLEND_SRC_COLOR,将混合运算符设置为D3D12_BLEND_OP_ADD。 通过此设置,混合等式可简化为:
在这里插入图片描述

在这里插入图片描述
将源颜色和目标颜色相乘。

透明度

源α分量被认为是控制源像素的不透明度的百分比(例如,0α表示0%不透明,0.4表示40%不透明,1.0表示100%不透明)。 不透明度和透明度之间的关系只是T = 1 - A,其中A是不透明度,T是透明度。 例如,如果某些东西是0.4不透明的,则它是1 - 0.4 = 0.6透明, 现在假设我们想要根据源像素的不透明度混合源像素和目标像素, 要执行此操作,请将源混合因子设置为D3D12_BLEND_SRC_ALPHA,将目标混合因子设置为D3D12_BLEND_INV_SRC_ALPHA,将混合运算符设置为D3D12_BLEND_OP_ADD。 通过此设置,混合等式可简化为:
在这里插入图片描述
例如,假设为as= 0.25,也就是说源像素仅为25%不透明, 然后当源像素和目标像素混合在一起时,我们期望最终颜色将是源像素的25%和目标像素的75%(源像素“后面”的像素)的组合,因为源像素是 75%透明。 上面的等式恰恰说明了这一点:
在这里插入图片描述
使用这种混合方法,我们可以绘制透明对象,如前面图中所示。 应该注意的是,使用这种混合方法,绘制对象的顺序很重要。 我们使用以下规则:
绘制不使用混合的对象, 接下来,按照与摄像机的距离对使用混合的对象进行排序, 最后,绘制以前后顺序使用混合的对象。
从后到前绘制顺序的原因是对象与空间背后的对象混合, 因为如果一个物体是透明的,我们可以通过它看到它背后的场景。 因此,必须将透明对象后面的所有像素都写入后缓冲区,以便我们可以将透明源像素与其后面场景的目标像素混合。
对于无颜色写入介绍的混合方法,绘制顺序无关紧要,因为它只是阻止源像素写入后缓冲区。 对于增加/减少和相乘讨论的混合方法,我们仍然首先绘制非混合对象,最后绘制混合对象; 这是因为我们希望在开始混合之前首先将所有非混合几何体放置在后缓冲区上。 但是,我们不需要对使用混合的对象进行排序, 这是因为操作是可交换的。 也就是说,如果从后缓冲区像素颜色B开始,然后对该像素执行n加法/减法/乘法混合,则顺序无关紧要:
在这里插入图片描述

混合和深度缓冲

当与加法/减法/乘法混合混合时,深度测试会出现问题,我们将仅使用加法混合来解释,但是相同的想法适用于减法/乘法混合。如果我们使用加法混合渲染一组S对象,那么想法是S中的对象不会相互模糊;相反,它们的颜色只是累积(见下图所示)。因此,我们不希望在S中对象之间进行深度测试;因为如果我们这样做,没有从后到前的绘制顺序,S中的一个对象会遮挡S中的另一个对象,从而导致像素片段由于深度测试而被拒绝,这意味着对象的像素颜色不会被累积到混合总和中。我们可以通过在S中渲染对象时禁用对深度缓冲区的写入来禁用S中对象之间的深度测试。由于禁用了深度写入,因此使用加法混合绘制的S中对象的深度将不会写入深度缓冲区;因此,由于深度测试,该对象不会遮挡后面S中任何后来绘制的对象。请注意,我们仅在S中绘制对象时禁用深度写入(使用附加混合绘制的对象集)。深度测试仍然启用,这样,非混合几何体(在混合几何体之前绘制)仍将模糊其后面的混合几何体。例如,如果在墙后面有一组加法混合的对象,则不会看到混合对象,因为实心墙遮挡了它们。如何禁用深度写入,一般地说,配置深度测试设置将在下面博客中介绍。
在这里插入图片描述
通过添加混合,在更多颗粒重叠并加在一起的源点附近强度更大, 随着颗粒扩散,强度减弱,因为较少的颗粒重叠并被加在一起。

Alpha 通道

在透明中的示例显示源alpha分量可用于RGB混合以控制透明度, 混合方程中使用的源颜色来自像素着色器。 我们将漫反射材质的alpha值作为像素着色器的alpha输出返回, 因此,漫反射贴图的alpha通道用于控制透明度。

float4 PS(VertexOut pin) : SV_Target
{
  float4 diffuseAlbedo = gDiffuseMap.Sample(
    gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
 
  …
  
  // Common convention to take alpha from diffuse albedo.
  litColor.a = diffuseAlbedo.a;
  return litColor;
}

通常可以在任何流行的图像编辑软件(如Adobe Photoshop)中添加Alpha通道,然后将图像保存为支持像DDS这样的Alpha通道的图像格式。

裁剪像素

有时我们希望不处理源像素, 这可以通过内在的HLSL裁剪(x)函数来完成, 此函数只能在像素着色器中调用,如果x <0,它将丢弃当前像素进一步处理。此函数对于渲染线栅纹理很有用,例如,如下图所示。 也就是说,渲染像素的像素是完全不透明或完全透明的。
在这里插入图片描述
与它的Alpha通道的铁丝网纹理, 具有黑色alpha值的像素将被裁剪功能不被绘制; 因此,只留下铁丝网。 实质上,alpha通道用于从纹理中屏蔽非栅栏像素。
在像素着色器中,我们抓取纹理的alpha分量, 如果它是一个接近0的小值,表示该像素是完全透明的,那么我们将该像素剪辑为进一步处理。

float4 PS(VertexOut pin) : SV_Target
{
  float4 diffuseAlbedo = gDiffuseMap.Sample(
    gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
   
#ifdef ALPHA_TEST
  // Discard pixel if texture alpha < 0.1. We do this test as soon 
  // as possible in the shader so that we can potentially exit the
  // shader early, thereby skipping the rest of the shader code.
  clip(diffuseAlbedo.a - 0.1f);
#endif
  …
 
  // Common convention to take alpha from diffuse albedo.
  litColor.a = diffuseAlbedo.a;
 
  return litColor;
}

观察我们只在定义了ALPHA_TEST时裁剪, 这是因为我们可能不想为某些渲染项调用裁剪,因此我们需要能够通过使用专门的着色器来打开/关闭它。 此外,使用alpha测试需要付出代价,因此我们只应在需要时使用它。
请注意,使用混合可以获得相同的结果,首先,不需要进行混合计算(可以禁用混合), 此外,可以通过像素着色器丢弃像素。
由于过滤,alpha通道可能会有点模糊,因此在裁剪像素时应留下一些缓冲空间。 例如,剪切像素的alpha值接近0,但不一定恰好为零。
下图显示了“Blend”演示的屏幕截图, 它使用透明混合渲染半透明水,并使用裁剪测试渲染线栅栏盒, 值得一提的另一个变化是,因为我们现在可以通过带有栅栏纹理的方框看到,我们想要禁用alpha测试对象的背面剔除:
在这里插入图片描述

D3D12_GRAPHICS_PIPELINE_STATE_DESC alphaTestedPsoDesc = opaquePsoDesc;
alphaTestedPsoDesc.PS = 
{ 
  reinterpret_cast<BYTE*>(mShaders["alphaTestedPS"]->GetBufferPointer()),
    mShaders["alphaTestedPS"]->GetBufferSize()
};
alphaTestedPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&alphaTestedPsoDesc, IID_PPV_ARGS(&mPSOs["alphaTested"])));

代码下载地址:链接:https://pan.baidu.com/s/1X0Vikf6qGYGPKU-Nwf-wYA
提取码:h79q

阅读更多

扫码向博主提问

海洋_

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • 3D引擎架构
  • 服务器架构
  • GPU渲染
  • 客户端架构
  • 引擎优化
去开通我的Chat快问
换一批

没有更多推荐了,返回首页