在场景中绘制多个不透明物体时很简单,哪个物体离得近,看到的就是哪个物体。但如果加入一个透明的物体,像玻璃,如何渲染就有些麻烦。拿一块红色的玻璃挡住眼睛,看到的物体都偏红,换成蓝色的玻璃,物体都偏蓝。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;   // CsrcD3D11_BLEND DestBlend;  // CdstD3D11_BLEND_OP BlendOp; // 颜色混合方式D3D11_BLEND SrcBlendAlpha;  // FsrcD3D11_BLEND DestBlendAlpha; // FdstD3D11_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

Windows 8 Directx 开发学习笔记(十二)利用混合实现浮在水面的木箱相关推荐

  1. Windows 8 Directx 开发学习笔记(二)建立模型及初始化设备

    上一篇中介绍的DirectxApp类给整个应用搭建了一个框架,而这篇文章涉及的CubeRenderer类则是负责填充框架,呈现实际内容:一个旋转的彩色立方体.CubeRenderer类中的方法通过名称 ...

  2. Polyworks脚本开发学习笔记(十二)-输出和读取文本文件

    Polyworks脚本开发学习笔记(十二)-输出和读取文本文件 Polyworks作为一个测量工具,将测量的数据方便的导出到文本文件则是一项必须的功能.在DATA_FILE这个命令下提供了很多子命令用 ...

  3. Windows 8 Directx 开发学习笔记(十一)地形纹理贴图

    前一篇实现木箱贴图时,木箱的六个面都正好用一整张纹理图,即六个面的纹理坐标均在[0,1]内.然而在为比较大的模型贴图时,像山峰河谷模型,如果只用一张纹理图,那么每个三角形只得到几个纹理元素,无法为提供 ...

  4. Windows 8 Directx 开发学习笔记(十四)使用几何着色器实现三角形细分

    几何着色器是从DirectX 10才引入的着色器,是一个可选阶段,位于顶点着色器和像素着色器阶段之间.顶点着色器以顶点作为输入数据,而几何着色器以完整的图元作为输入数据,像点.直线.三角形等.之所以引 ...

  5. Windows 8 Directx开发学习笔记(一)应用基本框架

    Windows 8系统10月25日就要正式发布,其应用可与Windows Phone 8应用兼容,所以打算转到Windows 8系列的开发.之前虽然开发过应用,但对游戏开发更感兴趣,随意开始学习Met ...

  6. Windows 8 DirectX 开发学习笔记(十六)使用Terragen生成自然环境贴图

    DirectX 游戏编程入门中提到一个Terragen软件可以生成环境贴图,所以登陆它的官方网站看了下.没想到Terragen生成的图片和照片一样,效果非常好,很多电影里有用到,所以下载免费版尝试一下 ...

  7. Windows 8 DirectX 开发学习笔记(十五)使用Billboard实现树木贴图

    要使用DirectX来获得三维效果,一般首先要生成一个三维模型,然后计算它在可视空间中的投影.这样得到的二维图像十分真实,但是计算量也很大.在大规模场景渲染中,随着模型精度的提高,这样的处理方式十分消 ...

  8. Windows 8 Directx 开发学习笔记(十)纹理贴图实现旋转的木箱

    纹理贴图映射(texturemapping)是可以显著提高场景细节和真实感的一种技术,基本原理是将图像数据映射到3D三角形表面(之前的文章提到过,三维模型其实是由很多个三角形拼接而成).当使用纹理资源 ...

  9. Windows 8 Directx 开发学习笔记(十三)利用模板实现木箱镜像

    假设墙上有一面镜子,镜子前面有个木箱.如果观察角度合适,整个木箱镜像都会在镜子里,计算起来还比较简单:而变换个角度,木箱的镜像可能只有一部分在镜子里,这时单纯依靠计算来实现就很麻烦.DirectX提供 ...

最新文章

  1. 对python里的装饰器
  2. CentOS部署NetCore - 2. 安装NetCore SDK On CentOS
  3. php篮球比赛,篮球数据API接口 - 【篮球比赛动画直播变化数据】API调用示例代码...
  4. 2.30mongodb创建集合,数据管理2.31PHP的mongdb扩展
  5. php.ini settimelimit,PHP-set_time_limit()和ini_set('max_execution_time',...)之间的区别...
  6. 多线程和单线程 打印数字到100000 的速度对比
  7. go | vscode远程调试linux服务器开发
  8. 余额宝收益冻结是什么意思?
  9. 是否允许此网站打开你计算机上的程序
  10. 世界主要国家的网络管理体制
  11. Linux内核安全包括哪些内容,Linux内核安全更新怎么样 解决了哪些问题
  12. 中国移动盘古搜索开创全新服务模式
  13. 阿里巴巴服务器泡进“水里”液冷服务器技术
  14. IOI2022 D2T1 数字电路(计数概率/组合数学+线段树区间翻转)
  15. DAZ3D导入模型设置材质
  16. 英特尔下一任CEO预测:五大热门人选出炉
  17. 正则表达式-正则表达式的元字符
  18. 2017淘宝有好货报名要求
  19. 三农数据(1996-2020)四:农、畜牧业产品产量、生产性固定资产、农场数、耕地面积等
  20. NSArray使用小结

热门文章

  1. 形考任务一在mysql创建数据库及表_数据库运维 形考任务1 实验1 MySQL数据库服务器配置...
  2. C# 子类实例化基类 基类使用不了子类的方法_C#中的类、方法和属性
  3. 软件测试工程师,需要达到什么水平才能顺利拿到 20k+ 无压力?
  4. 无法在此设备上查看受保护内容_细说丨你想要的Excel保护与加密都在这里
  5. 瑞芯微和全志哪个好_瑞芯微和全志科技基本面信息简要对比和整理
  6. 如何在NVIDIA(英伟达)官网下载老版本Toolkit-SDK---例如下载CUDA Toolkit 8.0
  7. codeblock 安装debug调试
  8. python中文分词统计_python 实现中文分词统计
  9. iphone模拟器_应用日报 | Xcode 现身 5.4 英寸 iPhone 模拟器,和平精英上线特斯拉皮肤...
  10. linux硬盘检测工具,linux 系统下磁盘检测工具e2fsck的使用