1. 程式人生 > >Introduction to 3D Game Programming with DirectX 12 學習筆記之 --- 第十章:混合

Introduction to 3D Game Programming with DirectX 12 學習筆記之 --- 第十章:混合

程式碼工程地址:

學習目標

  1. 理解混合的工作原理和如何在D3D中使用它;
  2. 學習D3D支援的不同的混合模式;
  3. 學習Alpha元件是如何控制透明基元的;
  4. 學習如何通過HLSL的clip函式防止畫素被繪製到後置緩衝中。

1 混合方程

CsrcC_{src}為從畫素著色器輸出的ij個畫素的顏色,令CdstC_{dst}當前後置緩衝中第ij個畫素的顏色。如果沒有混合,CsrcC_{src}將重寫CdstC_{dst}的顏色;但是增加混合後,它們將會按照下面的公式計算出新的顏色: 在這裡插入圖片描述 FsrcF_{src}FdstF_{dst}可以是第三節中的任意值。 上面的混合公式只用以計算RGB,Alpha元件使用的是一個分開的相似工程: 在這裡插入圖片描述

兩個方程本質上是一樣的,但是有時混合因子和計算方式是不一樣的,所以將RGB和Alpha分開可以讓我們將它們分開處理,這樣就可以達到更多的效果。

混合中Alpha元件的使用頻率要比RGB少很多,主要是因為我們不關心後置緩衝中的alpha值。後置緩衝中的alpha值只有在你的演算法真正需要它的時候我們才會考慮它。

2 混合運算

混合運算可能是下面列舉中的一種: 在這裡插入圖片描述 在min/max運算中,混合因子會被無視。

這些運算也可以被應用於Alpha元件的計算,你也可以為RGB和Alpha設定不同的運算公式,比如: 在這裡插入圖片描述 最近D3D新加一個特性,可以為混合使用邏輯運算子,可以使用的邏輯運算如下:

typedef enum D3D12_LOGIC_OP
{
	D3D12_LOGIC_OP_CLEAR = 0,
	D3D12_LOGIC_OP_SET = ( D3D12_LOGIC_OP_CLEAR + 1 ) ,
	D3D12_LOGIC_OP_COPY = ( D3D12_LOGIC_OP_SET + 1 ) ,
	D3D12_LOGIC_OP_COPY_INVERTED = ( D3D12_LOGIC_OP_COPY + 1 ) ,
	D3D12_LOGIC_OP_NOOP = ( D3D12_LOGIC_OP_COPY_INVERTED + 1 ) ,
	D3D12_LOGIC_OP_INVERT = ( D3D12_LOGIC_OP_NOOP + 1 ) ,
	D3D12_LOGIC_OP_AND = ( D3D12_LOGIC_OP_INVERT + 1 ) ,
	D3D12_LOGIC_OP_NAND = ( D3D12_LOGIC_OP_AND + 1 ) ,
	D3D12_LOGIC_OP_OR = ( D3D12_LOGIC_OP_NAND + 1 ) ,
	D3D12_LOGIC_OP_NOR = ( D3D12_LOGIC_OP_OR + 1 ) ,
	D3D12_LOGIC_OP_XOR = ( D3D12_LOGIC_OP_NOR + 1 ) ,
	D3D12_LOGIC_OP_EQUIV = ( D3D12_LOGIC_OP_XOR + 1 ) ,
	D3D12_LOGIC_OP_AND_REVERSE = ( D3D12_LOGIC_OP_EQUIV + 1 ) ,
	D3D12_LOGIC_OP_AND_INVERTED = ( D3D12_LOGIC_OP_AND_REVERSE + 1 ) ,
	D3D12_LOGIC_OP_OR_REVERSE = ( D3D12_LOGIC_OP_AND_INVERTED + 1 ) ,
	D3D12_LOGIC_OP_OR_INVERTED = ( D3D12_LOGIC_OP_OR_REVERSE + 1 )
} D3D12_LOGIC_OP;

你不能同時使用傳統的混合方程和邏輯運算;你只能選擇一種。另外選擇的邏輯運算必須是之前這隻的渲染目標格式所支援的-----格式必須是UINT的,否則會有下面的報錯:

D3D12 ERROR:
ID3D12Device::CreateGraphicsPipelineState: The
render target format at slot 0 is format
(R8G8B8A8_UNORM). This format does not support
logic ops. The Pixel Shader output signature
indicates this output could be written, and the
Blend State indicates logic op is enabled for this
slot. [ STATE_CREATION ERROR #678:
CREATEGRAPHICSPIPELINESTATE_OM_RENDER_TARGET_DOES_NOT_SUPPORT_D3D12 WARNING:
ID3D12Device::CreateGraphicsPipelineState: Pixel
Shader output ‘SV_Target0’ has type that is NOT
unsigned int, while the corresponding Output Merger
RenderTarget slot [0] has logic op enabled. This
happens to be well defined: the raw bits output
from the shader will simply be interpreted as UINT
bits in the blender without any data conversion.
This warning is to check that the application
developer really intended to rely on this behavior.
[ STATE_CREATION WARNING #677:
CREATEGRAPHICSPIPELINESTATE_PS_OUTPUT_TYPE_MISMATCH]

3 混合因子

下面列出一些基本混合因子的描述,還有一些高階的混合因子,可以檢視SDK文件中的D3D12_BLEND列舉描述:

D3D12_BLEND_ZERO: F = (0, 0, 0) and F = 0
D3D12_BLEND_ONE: F = (1, 1, 1) and F = 1
D3D12_BLEND_SRC_COLOR: F = (rs, gs, bs)
D3D12_BLEND_INV_SRC_COLOR: Fsrc = (1 − rs, 1 − gs, 1 − bs)
D3D12_BLEND_SRC_ALPHA: F = (as, as, as) and F = as
D3D12_BLEND_INV_SRC_ALPHA: F = (1 − as, 1 − as, 1 − as) and F = (1 − as)
D3D12_BLEND_DEST_ALPHA: F = (ad, ad, ad) and F = ad
D3D12_BLEND_INV_DEST_ALPHA: F = (1 − ad, 1 − ad, 1 − ad) and F = (1 − ad)
D3D12_BLEND_DEST_COLOR: F = (rd, gd, bd)
D3D12_BLEND_INV_DEST_COLOR: F = (1 − rd, 1 − gd, 1 − bd)
D3D12_BLEND_SRC_ALPHA_SAT: F = (a′s, a′s, a′s) and F = a′s 
// where a′s = clamp(as, 0, 1)

混合因子(比如D3D12_BLEND_BLEND_FACTOR)是作為第二個引數在ID3D12GraphicsCommandList::OMSetBlendFactor函式中設定。該值在你修改它之前是固定的。 上面所有的因子適用於RGB,對於Alpha元件,所有以_COLOR結尾的都不能使用。

我們可以通過下面的方式:

void ID3D12GraphicsCommandList::OMSetBlendFactor( const FLOAT BlendFactor[ 4 ]);

傳遞一個預設因子(1, 1, 1, 1)。

4 混合狀態

混合狀態是PSO的一部分,目前為止,我們都是使用預設混合狀態(禁用混合):

D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
… 
opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);

為了定義一個非預設的混合狀態,我們需要填充D3D12_BLEND_DESC結構:

typedef struct D3D12_BLEND_DESC {
	BOOL AlphaToCoverageEnable; // Default: False
	BOOL IndependentBlendEnable; // Default: False
	D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[8];
	} D3D11_BLEND_DESC;
  1. AlphaToCoverageEnable:指定為true,啟用alpha-to-coverage,是一個多重紋理對映技術,在繪製植物葉子等紋理的時候非常有用。它需要多重紋理對映是啟用的。
  2. IndependentBlendEnable:D3D支援同時繪製8個渲染目標,所以設定為true是,可以支援對每個渲染目標使用不同的混合模式。如果設定為false,代表所有渲染目標只能使用D3D12_BLEND_DESC::RenderTarget陣列中第一個混合模式來渲染。
  3. RenderTarget:8個D3D12_RENDER_TARGET_BLEND_DESC元素的陣列,表示i個元素是第i個同時渲染目標的混合模式。如果IndependentBlendEnable設定為false,那麼所有渲染目標使用RenderTarget[0]來混合。

D3D12_RENDER_TARGET_BLEND_DESC結構定義如下:

typedef struct D3D12_RENDER_TARGET_BLEND_DESC
{
	BOOL BlendEnable; // Default: False
	BOOL LogicOpEnable; // Default: False
	D3D12_BLEND SrcBlend; // Default: D3D12_BLEND_ONE
	D3D12_BLEND DestBlend; // Default: D3D12_BLEND_ZERO
	D3D12_BLEND_OP BlendOp; // Default: D3D12_BLEND_OP_ADD
	D3D12_BLEND SrcBlendAlpha; // Default: D3D12_BLEND_ONE
	D3D12_BLEND DestBlendAlpha; // Default: D3D12_BLEND_ZERO
	D3D12_BLEND_OP BlendOpAlpha; // Default: D3D12_BLEND_OP_ADD
	D3D12_LOGIC_OP LogicOp; // Default: D3D12_LOGIC_OP_NOOP
	UINT8 RenderTargetWriteMask; // Default: D3D12_COLOR_WRITE_ENABLE_ALL
} D3D12_RENDER_TARGET_BLEND_DESC;
  1. BlendEnable:BlendEnable和LogicOpEnable不能同時設定為true;
  2. LogicOpEnable:BlendEnable和LogicOpEnable不能同時設定為true;
  3. SrcBlend:Src RGB因子;
  4. DestBlend:Dst RGB因子;
  5. BlendOp:D3D12_BLEND_OP列舉中的值,定義RGB混合公式;
  6. SrcBlendAlpha:Src Alpha因子;
  7. DestBlendAlpha:Dst Alpha 因子;
  8. BlendOpAlpha:D3D12_BLEND_OP列舉中的值,定義Alpha混合公式;
  9. LogicOp:D3D12_LOGIC_OP列舉中的值,定義邏輯運算的公式;
  10. RenderTargetWriteMask:下面一個或者多個標籤的混合:
typedef enum D3D12_COLOR_WRITE_ENABLE {
	D3D12_COLOR_WRITE_ENABLE_RED = 1,
	D3D12_COLOR_WRITE_ENABLE_GREEN = 2,
	D3D12_COLOR_WRITE_ENABLE_BLUE = 4,
	D3D12_COLOR_WRITE_ENABLE_ALPHA = 8,
	D3D12_COLOR_WRITE_ENABLE_ALL =
		( D3D12_COLOR_WRITE_ENABLE_RED |
		D3D12_COLOR_WRITE_ENABLE_GREEN |
		D3D12_COLOR_WRITE_ENABLE_BLUE |
		D3D12_COLOR_WRITE_ENABLE_ALPHA )
} D3D12_COLOR_WRITE_ENABLE;

這些標籤控制哪些通道在混合後會被寫入後置緩衝。 混合操作是需要更多的逐畫素計算,所以是消耗較多的效能,所以只有當需要使用的時候開啟它,使用完成後就關閉它。

下面的程式碼展示了建立和設定一個混合狀態:

// Start from non-blended PSO
D3D12_GRAPHICS_PIPELINE_STATE_DESC transparentPsoDesc = opaquePsoDesc;

D3D12_RENDER_TARGET_BLEND_DESC transparencyBlendDesc;
transparencyBlendDesc.BlendEnable = true;
transparencyBlendDesc.LogicOpEnable = false;
transparencyBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
transparencyBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
transparencyBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
transparencyBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
transparencyBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
transparencyBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
transparencyBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
transparencyBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;

transparentPsoDesc.BlendState.RenderTarget[0] = transparencyBlendDesc;

ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
	&transparentPsoDesc,
	IID_PPV_ARGS(&mPSOs["transparent"])));

對於其他PSO,你需要在程式初始化時建立它們,然後在需要使用的時候使用ID3D12GraphicsCommandList::SetPipelineState方法來切換它們。

5 例子

這節,我們看一些通過混合因子的組合達到的一些效果。我們只描述RGB,Alpha的思路類似。

5.1 不寫入顏色

如果我們要保持原來的目標顏色不改變,這個是比較有用的。比如我們只希望寫到深度/模板緩衝中,而不是後置緩衝。可以設定src因子D3D12_BLEND_ZERO,dst因子為D3D12_BLEND_ONE,然後混合運算為:D3D12_BLEND_OP_ADD: 在這裡插入圖片描述 還有一種其他的方法,設定D3D12_RENDER_TARGET_BLEND_DESC::RenderTargetWriteMask為0,那麼沒有顏色通道會被寫入:

5.2 相加/相減

假設我們要將源畫素和目標畫素相加,我們可以將因子設定為D3D12_BLEND_ONE和D3D12_BLEND_ONE,運算設定為D3D12_BLEND_OP_ADD: 在這裡插入圖片描述 在這裡插入圖片描述 如果要相減,就把運算修改為D3D12_BLEND_OP_SUBTRACT: 在這裡插入圖片描述

5.3 相乘

可以設定因子為:D3D12_BLEND_ZERO和D3D12_BLEND_SRC_COLOR,然後運算設定為D3D12_BLEND_OP_ADD: 在這裡插入圖片描述 在這裡插入圖片描述

5.4 透明

如果要通過源畫素的不透明度來混合,設定因子為:D3D12_BLEND_SRC_ALPHA和D3D12_BLEND_INV_SRC_ALPHA,然後運算設定為D3D12_BLEND_OP_ADD: 在這裡插入圖片描述 使用這種混合方法,我們就可以繪製透明物體了。但是這種方法,繪製的物體的順序就很重要,我們使用下面的規則: 先繪製不需要混合的物體,然後對需要混合運算的物體根據距離攝像機的位置來排序,最後對需要使用混合的物體從後往前繪製。 對於5.1的混合方法,物體繪製順序不重要,但是5.2和5.3繪製順序還是很重要,我們還是要先繪製沒有混合的物體。但是我們不需要對需要混合的物體排序,因為這些操作是滿足交換律的,所以我們做N個相加/相減/相乘運算後,即使順序不同,結果還是一致的。

5.5 混合和深度緩衝

當我們使用相加/相減/相乘進行混合時,有一個深度緩衝的問題。如果我們渲染一組需要混合的物體,我們假設它們是互相不阻擋的,因為如果它們互相阻擋(並且沒有進行從後往前的排序渲染),那麼被遮擋的畫素在深度測試的時候就不會寫入後置緩衝中,這樣就導致混合無法準確計算。所以我們希望這組需要混合的物體不進行深度測試。 我們可以通過禁用寫入深度緩衝的方法,在繪製這組需要混合的物體的時候禁用深度測試;但是對於不需要混合的物體,深度讀取和測試還是啟用的。

6 Alpha 通道

可以通過PS中返回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;
}

新增Alpha通道的影象可以通過各種軟體,比如PS,匯出支援Alpha通道的檔案格式,比如DDS。

7 裁剪畫素

HLSL內建的clip(x)可以裁剪掉畫素,讓它不再進行後續的計算。該函式只能在PS中呼叫,當x小於0時,就會裁剪掉當前畫素。它對下面這種型別的貼圖就很適用: 在這裡插入圖片描述 在PS中,我們取出Alpha值進行裁剪判定:

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會被進行模糊運算,所以他們可能不是直接等於0,而是接近0。

下圖是本章混合的Demo,水流是通過透明混合實現的,箱子貼條是通過裁剪實現的。還有一個問題,因為箱子的部分畫素被裁剪了,所以可以看到後面的面,那麼就需要禁用背面消除: 在這裡插入圖片描述

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"])));

8 霧

我們實現霧效果的策略:定義一個霧的顏色,霧開始距離,霧的範圍。那麼一個三角形點的顏色就有正常的顏色和霧的顏色的加權平均數來計算出來: 在這裡插入圖片描述 在這裡插入圖片描述 引數S是0到1的一個值,它是相機位置和表面頂點的距離的函式: 在這裡插入圖片描述 saturate會把範圍擷取到0到1: 在這裡插入圖片描述 下面的著色器程式碼展示瞭如何實現霧效果,我們在畫素階段計算出光照顏色後,計算和差值:

// Defaults for number of lights.
#ifndef NUM_DIR_LIGHTS
	#define NUM_DIR_LIGHTS 3
#endif

#ifndef NUM_POINT_LIGHTS
	#define NUM_POINT_LIGHTS 0
#endif

#ifndef NUM_SPOT_LIGHTS
	#define NUM_SPOT_LIGHTS 0
#endif

// Include structures and functions for lighting.
#include "LightingUtil.hlsl"

Texture2D gDiffuseMap : register(t0);
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);

// Constant data that varies per frame.
cbuffer cbPerObject : register(b0)
{
	float4x4 gWorld;
	float4x4 gTexTransform;
};

// Constant data that varies per material.
cbuffer cbPass : register(b1)
{
	float4x4 gView;
	float4x4 gInvView;
	float4x4 gProj;
	float4x4 gInvProj;
	float4x4 gViewProj;
	float4x4 gInvViewProj;
	float3 gEyePosW;
	float cbPerPassPad1;
	float2 gRenderTargetSize;
	float2 gInvRenderTargetSize;
	float gNearZ;
	float gFarZ;
	float gTotalTime;
	float gDeltaTime;
	float4 gAmbientLight;
	
	**// Allow application to change fog parameters once per frame.
	// For example, we may only use fog for certain times of day.
	float4 gFogColor;
	float gFogStart;
	float gFogRange;**
	
	float2 cbPerPassPad2;
	
	// Indices [0, NUM_DIR_LIGHTS) are directional lights;
	// indices [NUM_DIR_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHTS) are point lights;
	// indices [NUM_DIR_LIGHTS+NUM_POINT_LIGHTS,
	// NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)
	// are spot lights for a maximum of MaxLights per object.
	Light gLights[MaxLights];
};

cbuffer cbMaterial : register(b2)
{
	float4 gDiffuseAlbedo;
	float3 gFresnelR0;
	float gRoughness;
	float4x4 gMatTransform;
};

struct VertexIn
{
	float3 PosL : POSITION;
	float3 NormalL : NORMAL;
	float2 TexC : TEXCOORD;
};

struct VertexOut
{
	float4 PosH : SV_POSITION;
	float3 PosW : POSITION;
	float3 NormalW : NORMAL;
	float2 TexC : TEXCOORD;
};

VertexOut VS(VertexIn vin)
{
	VertexOut vout = (VertexOut)0.0f;
	
	// Transform to world space.
	float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
	vout.PosW = posW.xyz;
	
	// Assumes nonuniform scaling; otherwise, need to use inverse-transpose
	// of world matrix.
	vout.NormalW = mul(vin.NormalL, (float3x3)gWorld);
	
	// Transform to homogeneous clip space.
	vout.PosH = mul(posW, gViewProj);
	
	// Output vertex attributes for interpolation across triangle.
	float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform);
	vout.TexC = mul(texC, gMatTransform).xy;
	
	return vout;
}

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
	
	// Interpolating normal can unnormalize it, so renormalize it.
	pin.NormalW = normalize(pin.NormalW);
	
	// Vector from point being lit to eye.
	float3 toEyeW = gEyePosW - pin.PosW;
	float distToEye = length(toEyeW);
	toEyeW /= distToEye; // normalize
	
	// Light terms.
	float4 ambient = gAmbientLight*diffuseAlbedo;
	const float shininess = 1.0f - gRoughness;
	Material mat = { diffuseAlbedo, gFresnelR0, shininess };
	float3 shadowFactor = 1.0f;
	float4 directLight = ComputeLighting(gLights, mat, pin.PosW, pin.NormalW, toEyeW, shadowFactor);
	float4 litColor = ambient + directLight;
	
	**#ifdef FOG
		float fogAmount = saturate((distToEye - gFogStart) / gFogRange);
		litColor = lerp(litColor, gFogColor, fogAmount);
	#endif**
	
	// Common convention to take alpha from diffuse albedo.
	litColor.a = diffuseAlbedo.a;
	
	return litColor;
}

一些場景可能不需要霧,所以我們在編譯Shader的時候,可選擇的FOG巨集定義。在我們的Demo中,我們增加D3D_SHADER_MACRO到CompileShader函式:

const D3D_SHADER_MACRO defines[] =
{
	"FOG", "1",
	NULL, NULL
};

mShaders["opaquePS"] = d3dUtil::CompileShader(
	L"Shaders\\Default.hlsl", defines, "PS",
	"ps_5_0");

9 總結

  1. 混合技術是可以讓當前畫素和在後置緩衝中畫素以特定的方式進行混合的技術;
  2. 混合公式為(RGB和Alpha計算是分開的): 在這裡插入圖片描述
  3. F是混合因子,是D3D12_BLEND列舉中的型別,對於Alpha計算,以_COLOR結尾的不能使用;
  4. 源alpha資料來自於漫反射材質,在我們的框架中漫反射材質由紋理貼圖定義,Alpha值由Alpha通道定義;
  5. HLSL的Clip(x)函式可以剪下畫素,不進行後續的畫素著色器運算 ,該函式只能在畫素著色器中進行呼叫,當X小於0是生效,它可以用來優化畫素著色器;
  6. 使用霧化效果可以修改天氣效果和大氣層透視。隱藏遙遠的渲染物體和遮蔽突現的問題。在我們的線性霧化模型中,我們定義霧的顏色,開始位置和霧的範圍,頂點的顏色有正常的光效效果和霧的顏色進行加權平均值來計算: 在這裡插入圖片描述

10 練習題

1. 嘗試不同的混合因子: 在BuildPSOs()函式中修改下面程式碼即可:

D3D12_RENDER_TARGET_BLEND_DESC transparencyBlendDesc;
	transparencyBlendDesc.BlendEnable = true;
	transparencyBlendDesc.LogicOpEnable = false;
	transparencyBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
	transparencyBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
	transparencyBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
	transparencyBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
	transparencyBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
	transparencyBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
	transparencyBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
	transparencyBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;

2. 修改Demo為先繪製水,解釋結果: 修改Draw函式中的程式碼順序即可:

// 繪製水流
mCommandList->SetPipelineState(mPSOs["transparent"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Transparent]);

// 繪製山脈
mCommandList->SetPipelineState(mPSOs["opaque"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);

// 繪製盒子
mCommandList->SetPipelineState(mPSOs["alphaTested"].Get());
DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::AlphaTested]);

在這裡插入圖片描述 沒有產生透明效果,是因為先繪製的水流,而後置緩衝中還沒有畫素,導致無法進行正確的混合。

5. 修改Demo混合渲染狀態中,不寫入紅色和綠色通道: 修改BuildPSOs函式中程式碼即可:

transparencyBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_BLUE;

在這裡插入圖片描述