第十章 混合
我们在此帧画面时前后绘制了地形和木板箱,使这两种材质的像素数据都位于后台缓冲区中。接着再运用混合技术将水面绘制到后台缓冲区,令水的像素数据与地形以及板条箱这两种像素数据在后台缓冲区内相混合,构成可以透过水看到地形与板条箱的效果。
学习目标:
- 理解混合技术的工作原理,并且在Direct3D中运用此技术。
- 学习Direct3D所支持的不同混合模式
- 探究如何用alpha分量来调节图元的透明度。
- 学会仅通过调用hlsl中的clip函数来阻止向后台缓冲区中绘制像素。
混合方程
我们最终呈现的颜色为:
。其中
时源混合因子,
(目标混合因子)
针对颜色向量而定义的分量式乘法
表示二元运算符,也就是最终如何对源和目标的式子进行混合。
混合运算
下列枚举项成员将用作混合方程中的二元运算符
enum D3D12_BLEND_OP
{
D3D12_BLEND_OP_ADD = 1,
D3D12_BLEND_OP_SUBTRACT = 2,
D3D12_BLEND_OP_REV_SUBTRACT = 3,
D3D12_BLEND_OP_MIN = 4,
D3D12_BLEND_OP_MAX = 5
} D3D12_BLEND_OP;
求取最小值或最大值的运算中会忽略混合因子
这些运算符也同样适用于alpha混合运算。而且,我们还能同时为RGB和Alpha这两种运算分别指定不同的运算符。例如,可以像下面一样使两个RGB项相加,却令两个alpha项相减:
Direct3D最近加了一项新特性,通过逻辑运算符对源颜色和目标颜色进行混合,用以取代上述传统的混合方程。这些逻辑运算符如下:
这里要注意,不能同时使用传统混合方程与逻辑运算符这两种混合手段,两者只能择其一。另外需要指出的是,为了使用逻辑运算符混合技术,就一定要选择它所支持的渲染目标格式——这一格式应当为UINT(无符号整数)的有关类型,否则我们会收到类似于下面的错误提示信息:
混合因子
通过为源混合因子与目标混合因子分别设置不同的混合运算符,就可以实现各式各样的混合效果。
enum D3D12_BLEND{D3D12_BLEND_ZERO = 1,D3D12_BLEND_ONE = 2,D3D12_BLEND_SRC_COLOR = 3,D3D12_BLEND_INV_SRC_COLOR = 4,D3D12_BLEND_SRC_ALPHA = 5,D3D12_BLEND_INV_SRC_ALPHA = 6,D3D12_BLEND_DEST_ALPHA = 7,D3D12_BLEND_INV_DEST_ALPHA = 8,D3D12_BLEND_DEST_COLOR = 9,D3D12_BLEND_INV_DEST_COLOR = 10,D3D12_BLEND_SRC_ALPHA_SAT = 11,D3D12_BLEND_BLEND_FACTOR = 14,D3D12_BLEND_INV_BLEND_FACTOR = 15,D3D12_BLEND_SRC1_COLOR = 16,D3D12_BLEND_INV_SRC1_COLOR = 17,D3D12_BLEND_SRC1_ALPHA = 18,D3D12_BLEND_INV_SRC1_ALPHA = 19,D3D12_BLEND_ALPHA_FACTOR = 20,D3D12_BLEND_INV_ALPHA_FACTOR = 21} D3D12_BLEND;
其中F是颜色的混合F是alpha分量的混合
当为D3D12_BLEND_ALPHA_FACTOR 和 D3D12_BLEND_INV_ALPHA_FACTOR值的时候,需要通过OMSetBlendFactor来设置对应的混合因子,其默认为(1, 1, 1, 1)的混合因子
混合状态
讨论完了混合运算符和混合因子后,那么怎样用Direct3D来设置这些数值呢?就像其他的Direct3D状态一样,混合状态亦是PSO的一部分。到目前为止,我们一直使用的都是默认的混合状态,即并没有启用混合技术,我们可以看到下面默认的方式:
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);explicit CD3DX12_BLEND_DESC( CD3DX12_DEFAULT ) noexcept{AlphaToCoverageEnable = FALSE;IndependentBlendEnable = FALSE;const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc ={FALSE,FALSE,D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,D3D12_LOGIC_OP_NOOP,D3D12_COLOR_WRITE_ENABLE_ALL,};for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)RenderTarget[ i ] = defaultRenderTargetBlendDesc;}
为了配置非默认状态,我们必须填写D3D12_BLEND_DESC结构体。该结构体定义如下:
typedef struct D3D12_BLEND_DESC{BOOL AlphaToCoverageEnable;BOOL IndependentBlendEnable;D3D12_RENDER_TARGET_BLEND_DESC RenderTarget[ 8 ];} D3D12_BLEND_DESC;
- AlphaToCoverageEnable:指定为true,则启用alpha-to-coverage功能,这是一种在渲染叶片或门等纹理时极其有用的一种多重采样技术。若指定为false,则禁用alpha-to-coverage功能。另外,要使用此技术还需开启多重采样(即创建后台缓冲区与深度缓冲区时要启用多重采样)。
- IndependentBlendEnable:Direct3D最多可同时支持8个渲染目标。若此标志设置为true,即表明可以向每一个渲染目标执行不同的混合操作(不同的混合因子、不同的混合运算以及设置不同的混合禁用或开启状态等)。如果将此标志设为false,则意味着所有的渲染目标均使用D3D12_BLEND_DESC::RenderTarget数组中第一个元素所描述的方式进行混合。多渲染目标技术常用于高级算法,而现在我们只假设每次仅向一个渲染目标进行绘制。
- 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;BOOL LogicOpEnable;D3D12_BLEND SrcBlend;D3D12_BLEND DestBlend;D3D12_BLEND_OP BlendOp;D3D12_BLEND SrcBlendAlpha;D3D12_BLEND DestBlendAlpha;D3D12_BLEND_OP BlendOpAlpha;D3D12_LOGIC_OP LogicOp;UINT8 RenderTargetWriteMask;} D3D12_RENDER_TARGET_BLEND_DESC;
- BlendEnable:指定为true,则启用常规混合功能;指定为false,则禁用常规混合功能。注意,不能将BlendEnable与LogicOpEnable同时设置为true,只能从常规混合与逻辑运算符混合两种方式中选择一种。
- LogicOpEnable:指定为true,则启用逻辑混合运算,反之则反。注意,不能将BlendEnable和LogicOpEnable同时设置为true,只能从常规混合与逻辑运算混合中选择一种。
- SrcBlend:枚举类型D3D12_BLEND中的成员之一,用于指定RGB混合中的源混合因子
- DestBlend:枚举类型D3D12_BLEND中的成员之一,用于指定RGB混合中的源混合因子
- BlendOp:枚举类型D3D12_BLEND_OP中的成员之一,用于指定RGB混合运算符
- SrcBlendAlpha:枚举类型D3D12_BLEND中的一个成员,指定了alpha混合中的源混合因子
- DestBlendAlpha:枚举类型D3D12_BLEND中的一个成员,指定了alpha混合中的源混合因子
- BlendOpAlpha:枚举类型D3D12_BLEND_OP中的一个成员,指定了alpha混合运算符
- LogicOp:枚举类型D3D12_LOGIC_OP中的成员之一,指定了源颜色与目标颜色在混合时所用的逻辑运算符。
- RenderTargetWriteMask:下列标志中一种或多种的组合
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;
这些标志控制着混合后的数据可被写入后台缓冲区中的哪些颜色通道。例如指定D3D12_COLOR_WRITE_ENABLE_ALPHA可以禁止向RGB通道的写操作,而仅写入alpha通道有关数据。对于一些高级技术而言,这种灵活性是及其实用的。当混合功能被禁止时,从像素着色器返回的颜色数据将按没有设置上述写掩码来进行处理(即不对目标像素执行任何操作,也就是这个设置没用了)
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;psoDesc.BlendState.RenderTarget[0] = transparencyBlendDesc;
如同其它的PSO一般,我们应当在应用程序的初始化期间创建它们,接着再根据需求以m_CommandList->SetPipelineState方法在不同的状态之间进行切换
混合示例
- 如果不需要针对颜色进行写操作,只想写入深度/缓冲区,就把源像素混合因子设置为D3D12_BLEND_ZERO,将目标混合因子配置为ONE,然后操作时add。或者直接将RenderTargetWriteMask设置为0来禁止颜色的写入
- 加法和减法的操作
- 乘法。如果希望源像素与其它对应的目标像素相乘,那么应设源混合因子为D3D12_BLEND_ZERO、目标混合因子为D3D12_BLEND_SRC_COLOR,再将混合运算符置为D3D12_BLEND_OP_ADD,也就是直接拿源颜色当因子了
- 透明混合。其中A为不透明度,
为透明度。当我们设置了下面的配置,D3D12_BLEND_SRC_ALPHA、目标混合因子为D3D12_BLEND_INV_SRC_ALPHA,并将混合运算符设置为D3D12_BLEND_OP_ADD。有了这些配置,混合方程就变为了:
这里就需要考虑透明物体的绘制问题,首先我们应该先把无需混合处理的物体绘制了,然后再根据混合物体和相机之间的距离对它们进行排序。最后,按由远及近的顺序通过混合的方式来绘制这些物体。
依照由后向前的顺序进行绘制的原因是,每个物体都会与其后的所有物体执行混合运算。对于一个透明的物体而言,我们应当可以透过它看到其背后的场景。因此就需要将透明物体的所有对应像素都预先写入后台缓冲区内,随后再将此透明物体的源像素与其后场景的目标像素进行混合。
(10.5.1也就是禁止颜色的写入)后面加法和乘法和因子基本无关,所以无视顺序
混合和深度缓冲区
在使用加法/减法/乘法运算进行混合的时候,会涉及到深度测试。比如加法,如果要用加法混合来渲染一个物体集合S,并希望S中的物体不会相互遮挡,这就意味着我们只需将这些物体的颜色数据简单地累加即可。
为此我们不愿再s中的物体之间进行深度测试。若开启深度测试,却并没有按从后至前的顺序进行绘制,那么,当s中的两个物体存在遮挡关系,经过深度测试后,靠后的像素片段便会被丢弃,这意味着该物体的像素颜色将不会被累加至混合求和的结果中。在渲染s中的物体时,我们可以通过禁止向深度缓冲区的写操作来禁用S中的物体之间的深度测试。由于深度写入操作已被禁止,S中的物体在进行加法混合时,便不会将深度信息写入深度缓冲区,因此,S中的物体便不会因深度测试而直接覆盖其后的物体。注意,我们只是在绘制s(要用加法混合的方式来绘制的一个物体集合)中的物体时禁用了深度写入操作,但深度值的读取与深度检测仍然开启。这样对于位于深度靠前的非混合几何体,仍然会遮挡其后的混合几何体。
alpha通道
裁剪像素
有时候,我们希望彻底禁止某个源像素参与后续的处理。这可以通过HLSL的内置函数clip(x)来实现。此函数仅供像素着色器调用,若x < 0,则当前这一像素将从后面的处理阶段中丢弃。用这个函数来处理铁丝网的绘制很合适。
这里我们就可以在像素着色器中通过alpha去提前的判断是否丢弃这个片段
#ifdef ALPHA_TESTclip(diffuseAlbedo.a - 0.1f);
#endif
这里我没有设置混合,也就是这里它不是混合的问题,他是直接的根据clip去丢弃像素片段了
注意这里有个问题,可以看到背面是看不到的,我们需要针对alpha_test的物体设定对应的pso也就是禁止背面剔除
psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
ComPtr<ID3D12PipelineState> alphaTestPSO;
ThrowIfFailed(m_Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(alphaTestPSO.GetAddressOf())));
m_PSOs["alphaTest"] = std::move(alphaTestPSO);
在renderItems的时候切换pso,注意记得切换回去
for (size_t i = 0; i < ritems.size(); i++){if (i == 1) //box的位置{cmdList->SetPipelineState(m_PSOs["alphaTest"].Get());}auto ri = ritems[i];D3D12_VERTEX_BUFFER_VIEW vertexBufferView = ri->Geo->VertexBufferView();D3D12_INDEX_BUFFER_VIEW indexBufferView = ri->Geo->IndexBufferView();cmdList->IASetVertexBuffers(0, 1, &vertexBufferView);cmdList->IASetIndexBuffer(&indexBufferView);cmdList->IASetPrimitiveTopology(ri->PrimitiveType);CD3DX12_GPU_DESCRIPTOR_HANDLE tex(m_SRVDescriptorHeap->GetGPUDescriptorHandleForHeapStart());tex.Offset(ri->mat->DiffuseSrvHeapIndex, m_CbvSrvUavDescriptorSize);cmdList->SetGraphicsRootDescriptorTable(0, tex);ID3D12Resource* objResource = (ID3D12Resource*)*m_CurrentFrameResource->ObjectCB.get();ID3D12Resource* matResource = (ID3D12Resource*)*m_CurrentFrameResource->MaterialCB.get();D3D12_GPU_VIRTUAL_ADDRESS objGPU = objResource->GetGPUVirtualAddress() + ri->ObjCBIndex * d3dUtil::CalcConstBufferByteSize(sizeof(ObjectConsts));D3D12_GPU_VIRTUAL_ADDRESS matGPU = matResource->GetGPUVirtualAddress() + ri->mat->MatCBIndex * d3dUtil::CalcConstBufferByteSize(sizeof(MaterialConstants));cmdList->SetGraphicsRootConstantBufferView(1, objGPU);cmdList->SetGraphicsRootConstantBufferView(2, matGPU);cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);cmdList->SetPipelineState(m_PSOs["material"].Get());}
雾
雾的用处很多,例如浓雾可以掩饰远处景物在渲染上的失真。
以及防止发生物体突然出现的情况,比如一个物体一开始的时候位于视锥体远平面的后侧,但由于相机移动,使之突然出现在视锥体的范围之内,让它可见了,雾就可以掩盖突然出现这一现象。
实现雾化效果的流程如下:
首先指明雾的颜色、由摄像机到雾气的最近距离以及雾的分散范围(即从雾到摄像机的最近距离至雾能完全覆盖物体的这段范围),接下来再将网络三角形上点的颜色置为原色与雾色的加权平均值:
参数s的范围为,由一个以摄像机位置与被雾覆盖物体表面点之间的距离作为参数的函数来确定。随着该表面点与观察点之间距离的增加,它会被雾气遮挡得愈加朦胧。
其中dist(p, E)为表面点p与摄像机位置E之间的距离。而函数saturate会将其参数限制在区间[0, 1]内:
cbuffer cbPass : register(b2)
{float4x4 gView;float4x4 gInvView;float4x4 gProj;float4x4 gInvProj;float4x4 gViewProj;float4x4 gInvViewProj;float3 gEyePosW;float gcbPerObjectPad1;float2 gRenderTargetSize;float2 gInvRenderTargetSize;float gNearZ;float gFarZ;float gTotalTime;float gDeltaTime;float4 gAmbientLight;//允许应用程序在每一帧都能改变雾效参数//例如,我们可能只在一天中的特定时间才使用雾效float4 gFogColor;float gFogStart;float gFogRange;float2 cbPerPassPad2;Light gLights[MaxLights];
};
这里我们加入cbPerPassPad2来填充padding,保持4字节的对齐
float4 PS(VertexOut pin) : SV_Target
{float4 diffuseAlbedo = gDiffuseMap.Sample(gsamLinearWrap, pin.TexC) * gDiffuseAlbedo;
#ifdef ALPHA_TESTclip(diffuseAlbedo.a - 0.1f);
#endifpin.NormalW = normalize(pin.NormalW);float disToEye = length(gEyePosW - pin.PosW);float3 toEyeW = normalize(gEyePosW - pin.PosW);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 FOGfloat fogAmount = saturate((disToEye - gFogStart) / gFogRange);litColor = lerp(litColor, gFogColor, fogAmount);
#endiflitColor.a = diffuseAlbedo.a;return litColor;}
有些场景可能并不会用到雾效,因此我们这里设置为静态开关的可选项,若要使用,则需在编译着色器时定义FOG宏。我们通过向CompileShader函数提供下列D3D_SHADER_MACRO结构体来开启雾效
D3D_SHADER_MACRO
是一个用于定义着色器宏的结构体数组,必须以{nullptr, nullptr}
作为结束标志。这是因为D3DCompileFromFile
函数会遍历该数组,直到遇到两个指针都为nullptr
的元素才停止解析。
const D3D_SHADER_MACRO defines[] = {{"FOG", "1"},{"ALPHA_TEST", "1"},{nullptr, nullptr}};m_VSShader = d3dUtil::CompileShader(L"source/shader/Texture.usf", nullptr, "VS", "vs_5_0");m_PSShader = d3dUtil::CompileShader(L"source/shader/Texture.usf", defines, "PS", "ps_5_0");
然而其实这一章节没有用到相关的复杂的混合模式,都是默认的one zero add模式,其中铁丝网也是clip。不过其方式实际上就是先渲染不透明的物体,然后切换pso关闭深度缓冲区的写入,然后渲染半透明物体
我们把山体的纹理和海的纹理补上
//grassstd::unique_ptr<Texture> mountainTex = std::make_unique<Texture>();mountainTex->Name = "grass";mountainTex->FileName = L"Textures/grass.dds";mountainTex->AlphaMode = DirectX::DDS_ALPHA_MODE_OPAQUE;mountainTex->is_cube = false;DirectX::LoadDDSTextureFromFile(m_Device.Get(), mountainTex->FileName.c_str(),mountainTex->Resource.GetAddressOf(), mountainTex->Data,mountainTex->SubResourceData, 0, &mountainTex->AlphaMode, &mountainTex->is_cube);CD3DX12_HEAP_PROPERTIES grassHeap(D3D12_HEAP_TYPE_UPLOAD);UINT64 grassSize = GetRequiredIntermediateSize(mountainTex->Resource.Get(), 0, mountainTex->SubResourceData.size());CD3DX12_RESOURCE_DESC grassDesc = CD3DX12_RESOURCE_DESC::Buffer(grassSize);ThrowIfFailed(m_Device->CreateCommittedResource(&grassHeap,D3D12_HEAP_FLAG_NONE, &grassDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,IID_PPV_ARGS(mountainTex->UploadHeap.GetAddressOf())));CD3DX12_RESOURCE_BARRIER grasspB = CD3DX12_RESOURCE_BARRIER::Transition(mountainTex->Resource.Get(),D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);m_CommandList->ResourceBarrier(1, &grasspB);UpdateSubresources(m_CommandList.Get(), mountainTex->Resource.Get(), mountainTex->UploadHeap.Get(),0, 0, mountainTex->SubResourceData.size(), mountainTex->SubResourceData.data());grasspB = CD3DX12_RESOURCE_BARRIER::Transition(mountainTex->Resource.Get(),D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON);m_CommandList->ResourceBarrier(1, &grasspB);m_Textures[mountainTex->Name] = std::move(mountainTex);
海面半透明
最后我们按照上面的思路将海水设置为半透明
seaTex->Name = "sea";
seaTex->FileName = L"Textures/water1.dds";
seaTex->AlphaMode = DirectX::DDS_ALPHA_MODE_STRAIGHT;
seaTex->is_cube = false;
std::vector<RenderItem*> m_Transparent;
这里我们设置pipeline,关闭深度写入,设置新的混合状态
D3D12_BLEND_DESC blendStateDesc{};
blendStateDesc.AlphaToCoverageEnable = false;
blendStateDesc.IndependentBlendEnable = false;
D3D12_RENDER_TARGET_BLEND_DESC rendeTargetBlendDesc{};
rendeTargetBlendDesc.BlendEnable = true;
rendeTargetBlendDesc.LogicOpEnable = false;
rendeTargetBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
rendeTargetBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
rendeTargetBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
rendeTargetBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
rendeTargetBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
rendeTargetBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
rendeTargetBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
rendeTargetBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
blendStateDesc.RenderTarget[0] = rendeTargetBlendDesc;psoDesc.BlendState = blendStateDesc;
psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
ComPtr<ID3D12PipelineState> transparentPSO;
ThrowIfFailed(m_Device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(transparentPSO.GetAddressOf())));
m_PSOs["transparent"] = std::move(transparentPSO);
然后我们这里设定一个新的renderItem的数组去存储半透明物体的渲染项,我们先绘制不透明物体然后绘制半透明物体
m_Opaques.clear();
m_Transparent.clear();
for (int i = 0; i < m_RenderItems.size(); ++i){if (m_RenderItems[i]->Geo->Name == "sea"){m_Transparent.push_back(m_RenderItems[i].get());continue;}m_Opaques.push_back(m_RenderItems[i].get());}
DrawRenderItems(m_CommandList.Get(), m_Opaques, m_Transparent);
然后就是绘制的地方
void MaterialApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems,
const std::vector<RenderItem*>& transparent)
{for (size_t i = 0; i < ritems.size(); i++){if (ritems[i]->Geo->Name == "box") //box的位置{cmdList->SetPipelineState(m_PSOs["alphaTest"].Get());}else{cmdList->SetPipelineState(m_PSOs["material"].Get());}auto ri = ritems[i];D3D12_VERTEX_BUFFER_VIEW vertexBufferView = ri->Geo->VertexBufferView();D3D12_INDEX_BUFFER_VIEW indexBufferView = ri->Geo->IndexBufferView();cmdList->IASetVertexBuffers(0, 1, &vertexBufferView);cmdList->IASetIndexBuffer(&indexBufferView);cmdList->IASetPrimitiveTopology(ri->PrimitiveType);CD3DX12_GPU_DESCRIPTOR_HANDLE tex(m_SRVDescriptorHeap->GetGPUDescriptorHandleForHeapStart());tex.Offset(ri->mat->DiffuseSrvHeapIndex, m_CbvSrvUavDescriptorSize);cmdList->SetGraphicsRootDescriptorTable(0, tex);ID3D12Resource* objResource = (ID3D12Resource*)*m_CurrentFrameResource->ObjectCB.get();ID3D12Resource* matResource = (ID3D12Resource*)*m_CurrentFrameResource->MaterialCB.get();D3D12_GPU_VIRTUAL_ADDRESS objGPU = objResource->GetGPUVirtualAddress() + ri->ObjCBIndex * d3dUtil::CalcConstBufferByteSize(sizeof(ObjectConsts));D3D12_GPU_VIRTUAL_ADDRESS matGPU = matResource->GetGPUVirtualAddress() + ri->mat->MatCBIndex * d3dUtil::CalcConstBufferByteSize(sizeof(MaterialConstants));cmdList->SetGraphicsRootConstantBufferView(1, objGPU);cmdList->SetGraphicsRootConstantBufferView(2, matGPU);cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);}for (int i = 0; i <transparent.size(); ++i){auto ri = transparent[i];cmdList->SetPipelineState(m_PSOs["transparent"].Get());D3D12_VERTEX_BUFFER_VIEW vbv = ri->Geo->VertexBufferView();D3D12_INDEX_BUFFER_VIEW ibv = ri->Geo->IndexBufferView();cmdList->IASetVertexBuffers(0, 1, &vbv);cmdList->IASetIndexBuffer(&ibv);cmdList->IASetPrimitiveTopology(ri->PrimitiveType);CD3DX12_GPU_DESCRIPTOR_HANDLE matHandle(m_SRVDescriptorHeap->GetGPUDescriptorHandleForHeapStart());matHandle.Offset(ri->mat->DiffuseSrvHeapIndex, m_CbvSrvUavDescriptorSize);cmdList->SetGraphicsRootDescriptorTable(0, matHandle);//constID3D12Resource* objR = (ID3D12Resource*)*m_CurrentFrameResource->ObjectCB.get();ID3D12Resource* matR = (ID3D12Resource*)*m_CurrentFrameResource->MaterialCB.get();D3D12_GPU_VIRTUAL_ADDRESS objGPU = objR->GetGPUVirtualAddress() + ri->ObjCBIndex * d3dUtil::CalcConstBufferByteSize(sizeof(ObjectConsts));D3D12_GPU_VIRTUAL_ADDRESS matGPU = matR->GetGPUVirtualAddress() + ri->mat->MatCBIndex * d3dUtil::CalcConstBufferByteSize(sizeof(MaterialConstants));cmdList->SetGraphicsRootConstantBufferView(1, objGPU);cmdList->SetGraphicsRootConstantBufferView(2, matGPU);cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation,0);}
}
注意这里水的材质alpha的地方必须是小于1.0
water->DiffuseAlbedo = DirectX::XMFLOAT4(0.0f, 0.2f, 0.6f, 0.6f);
效果非常的不错