在场景中绘制多个不透明物体时很简单,哪个物体离得近,看到的就是哪个物体。但如果加入一个透明的物体,像玻璃,如何渲染就有些麻烦。拿一块红色的玻璃挡住眼睛,看到的物体都偏红,换成蓝色的玻璃,物体都偏蓝。DirectX中的“混合(Blending)”技术可以解决这个问题。混合技术其实也不难,但是通过不同运算方式和系数的组合,它能实现很多效果。它的基本原理就是混合方程:
其中乘号表示向量对应元素相乘,C和A分别表示颜色和Alpha的混合结果,其它参数可以自由指定。详细的介绍可以看DirectX 10游戏编程入门,虽然版本不同,但是原理没有区别。继续拿玻璃做例子,个人感觉混合技术就像一个调色板,只有两种颜料,分别是玻璃的颜色Csrc和它后面对应位置的物体颜色Cdst,而最终的颜色C是两种颜料按不同比例混合后的颜色,其中Fsrc控制玻璃的颜料比例,Fdst控制物体的颜料比例。Alpha通道的混合原理与颜色混合类似,不过少了两个分量。
因为Csrc和Cdst随物体不同而变化,所以实际使用混合时每个方程需要指定三个参数:混合方式、Fsrc和Fdst。在DirectX 11中,与混合相关的结构体定义如下:
typedef struct D3D11_BLEND_DESC
{
BOOL AlphaToCoverageEnable;
BOOLIndependentBlendEnable;
D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[ 8 ];
} D3D11_BLEND_DESC;
typedef struct D3D11_RENDER_TARGET_BLEND_DESC
{
BOOL BlendEnable;
D3D11_BLEND SrcBlend; // Csrc
D3D11_BLEND DestBlend; // Cdst
D3D11_BLEND_OP BlendOp; // 颜色混合方式
D3D11_BLEND SrcBlendAlpha; // Fsrc
D3D11_BLEND DestBlendAlpha; // Fdst
D3D11_BLEND_OP BlendOpAlpha; // Alpha混合方式
UINT8 RenderTargetWriteMask;
} D3D11_RENDER_TARGET_BLEND_DESC;
其他参数的说明可以参照MSDN
http://msdn.microsoft.com/ZH-CN/library/windows/desktop/ff476087(v=vs.85).aspx
了解混合技术的原理之后就要着手将混合效果添加到上一篇文章中已实现的模型中去,给水面添加透明的效果,使它更真实些。首先从修改顶点着色器代码开始。打算将Alpha纹理一整张铺在水面上,不需要复制。所以在顶点着色器的输出中添加一个texa成员,用于保存Alpha纹理坐标。
struct VertexShaderOutput
{
float4 posH : SV_POSITION;
float3 posW : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOOD;
float2 texa :TEXCOOD_ALPHA;
};
在main方法中,正常纹理坐标需要变换,但是Alpha纹理坐标不需要。
// 纹理坐标变换
output.tex = mul(float4(input.tex, 0.0f, 1.0f),texTransform).xy;
// Alpha纹理不进行变换
output.texa = input.tex;
然后就要修改像素着色器部分,像素着色器的输入自然与顶点着色器的输出对应。同时,像素着色器还要添加一个全局变量texAlpha存储Alpha纹理资源。
Texture2D texDiffuse : register(t0);
Texture2D texAlpha : register(t1);
struct PixelShaderInput
{
float4 posH : SV_POSITION;
float3 posW : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOOD;
float2 texa :TEXCOOD_ALPHA;
};
注意texDiffuse和texAlpha后面的register,它用来说明资源的存储位置。Alpha纹理的使用与普通纹理使用没有区别,不过这次需要用采样器采的是纹理的Alpha分量,用.a表示。
// 只有水模型会设置并使用Alpha纹理
// 绘制其他模型时不保证texAlpha一定有内容
// 不能实际应用
// 按原始透明度显示太透明所以乘4
finalColor.a = texAlpha.Sample(samplerLinear,input.texa).a * 4;
接下来就是修改C++代码。需要改动的地方只有Renderer.h和Renderer.cpp两个文件而已。这次载入的Alpha纹理资源是dds格式的,所以向项目中添加DDSTextureLoader.h和DDSTextureLoader.cpp文件,并在Renderer.h中包含DDSTextureLoader.h。此外,还要在Renderer类中添加两个成员,用来保存Alpha纹理视图和混合状态。
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_WaterAlphaSRV;
Microsoft::WRL::ComPtr<ID3D11BlendState> m_BlendState;
完成后就可以在CreateDeviceResources方法中的createTexTask部分添加Alpha纹理载入代码并初始化混合状态。
DX::ThrowIfFailed(
CreateDDSTextureFromFile(
m_d3dDevice.Get(),
L"Texture/water_alpha.dds",
&tex,
m_WaterAlphaSRV.GetAddressOf()
)
);
// 初始化混合状态
D3D11_BLEND_DESC blendDesc = {0};
blendDesc.RenderTarget[0].BlendEnable = TRUE;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; // Color_Fsrc
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // Color_Fdst
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; // Color_Operation
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; // Alpha_Fsrc
blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; // Alpha_Fdst
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; // Alpha_Operation
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
DX::ThrowIfFailed(
m_d3dDevice->CreateBlendState(
&blendDesc,
&m_BlendState
)
);
主要说明下颜色混合的设置。颜色的混合参数Fsrc和Fdst的说明可以从MSDN找到
http://msdn.microsoft.com/ZH-CN/library/windows/desktop/ff476086(v=vs.85).aspx
D3D11_BLEND_SRC_ALPHA
The blend factoris (As, As, As, As), that is alpha data (A) from a pixel shader. No pre-blendoperation.
D3D11_BLEND_INV_SRC_ALPHA
The blend factoris ( 1 - As, 1 - As, 1 - As, 1 - As), that is alpha data (A) from a pixelshader. The pre-blend operation inverts the data, generating 1 - A.
D3D11_BLEND_OP_ADD
Add source 1 andsource 2.
将其代入混合方程可得出实际的计算公式:
假设当前玻璃的透明度是0.2,那么最终的混合结果就是0.2份的玻璃颜色加上0.8份的物体颜色。创建好混合状态之后就能够使用了。不过这时要注意物体的绘制顺序,需要遵守下面的规则:
首先绘制不透明物体。然后,根据透明物体与摄像机之间的距离进行排序,按照从后向前的顺序绘制透明物体。因为混合时,透明物体要在不透明物体的前面,从而能够透过它看到后面的物体。如果这时不透明物体还没有画出来,那什么都看不到。所以,必须将透明物体后面的物体绘制出来后再进行混合。
这样,在Render方法中,要完成陆地渲染后再设置混合状态,并设置Alpha纹理,最后渲染水面,完成后清除混合状态。
// 设置水面纹理和Alpha纹理
m_d3dContext->PSSetShaderResources(
0, // 对应像素着色器中register(t0)
1,
m_WaterSRV.GetAddressOf()
);
m_d3dContext->PSSetShaderResources(
1, // 对应像素着色器中register(t1)
1,
m_WaterAlphaSRV.GetAddressOf()
);
// 设置混合状态
FLOAT blendFactors[4] = { 0, };
m_d3dContext->OMSetBlendState(m_BlendState.Get(),blendFactors, 0xffffffff);
……
// 清除混合模式状态
m_d3dContext->OMSetBlendState(0, blendFactors,0xffffffff);
实际运行效果如下图:
图1是使用Alpha纹理贴图的效果,图2是统一水面的Alpha为0.6。蓝圈中是两图类似的部分,红圈则是不同部分,不是特别明显。
还可以在这个模型中加入之前实现的木箱,细节不用多说,注意要在绘制水面之前渲染木箱。效果如下图:
本篇文章的源代码:Direct3DApp_HillWaveBlend
原文地址:http://blog.csdn.net/raymondcode/article/details/8456714
分享到:
相关推荐
改进山峰水面模型。使水面透明,并在水面上添加一个木箱。 VS2012 Pro + Win8 Pro编译运行
实现木箱的镜像效果。 VS2012 Pro + Win8 Pro编译运行
DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX ...
DirectX.pdf DirectX基础学习 DirectX开发练习
同样实现木箱的镜像效果,不过对代码进行了整理,去掉重复代码,更简洁一些。 VS2012 Pro + Win8 Pro编译运行
DirectX, 某人的学习笔记.DirectX, 某人的学习笔记.
DirectX 是开发三维场景的利器,也是目前大多三维游戏采用的开发平 台。国内外关于DirectX 开发的书籍不是非常多,而且大多数书籍都是针对 Visual C++语言来介绍的。在安装DirectX SDK 时可以选择安装例子和教程, ...
利用directx8 for D6 开发空战游戏模型directx8 for D6本站有!
使用纹理贴图实现一个旋转的木箱,并添加简单的漫反射光效果。 VS2012 Pro + Win8 Pro编译运行
在安装DirectX SDK时可以选择安装例子和教程,微软为开发者提供了许多较为实用的例子供大家学习。本文结合计算机三维图形学和DirectX的开发帮助,并参考国内外关于DirectX开发的书籍和网站资料等,主要介绍如何采用...
定义陆地和水面两种材质,建立山峰和水面模型。 模拟平行光、点光源、聚光灯同时照射的效果。 VS2012 Pro + Win8 Pro编译运行
微软的DirectX软件开发工具包(SDK)提供了一套优秀的应用程序编程接口(APIs),这个编程接口可以提供给你开发高质量、实时的应用程序所需要的各种资源。DirectX技术的出现将极大的有助于发展下一代多媒体应用程序...
DirectX开发文档,学习体会型的,希望对大家有所帮助。
采用Visual Studio 2015 Community开发,基于DirectX 9.0,代码简单易懂,功能比较全面,开发时参考了博客:http://www.cnblogs.com/graphics/p/2580832.html
在Delphi中利用DirectX 8 中DirectShow做VCD播放器
VC++ DirectX 游戏开发 物有所值,适合于初学者
DirectX 12 3D游戏开发实战
开发工具: Microsoft Visual Studio 2005 Microsoft DirectX 9.0 SDK Update
为山峰水面模型贴图,并包括平行光、聚光灯、点光三种光照效果。山峰模型有草地和岩石两种纹理,分别为grass.jpg、rock.jpg。 VS2012 Pro + Win8 Pro编译运行
DirectX 是开发三维场景的利器,也是目前大多三维游戏采用的开发平台。