[DirectX 10.1+] 一种定制化的alpha-to-coverage实现
2015-11-22 18:50
781 查看
alpha-to-coverage是我个人非常钟爱的一种渲染技术,因为它可以用比较低的编程成本以及GPU成本实现类似顺序无关的透明(OIT)渲染。
最典型的使用alpha-to-coverage的例子就是《最终幻想13》三部曲了。
在PS3/Xbox 360上,由于只有MSAA 2x,它的颗粒感是很明显的,但PC版上,由于有了MSAA 8x,因此有了明显的效果。
具体的实现方式可以从DirectX SDK的例子 TransparencyAA就可以了解一二。(顺便说一下,这个例子本来是为了推广TransparencyAA的,但这种渲染技巧对毛发或者叶子边缘的蓬松感的表现没有什么好处,因为TransparencyAA本质上还是一种alpha test的改进形式。)
不过缺省的alpha-to-coverage是一种完全没有随机性的过程,因此它会在alpha值变化的地方产生类似地形图那样的条纹:
![](https://img-blog.csdn.net/20151129193302169)
为了解决这个问题,humus.name网站的大牛(点击打开链接)提出了使用伪随机函数在pixel shader里改写SV_Coverage,从而改进alpha-to-coverage效果的尝试。但大牛没有留下参考用的shader代码。
因此我就做了点小尝试,去实现大牛提出的效果。实验结果截图和参考shader会贴在后面。
![](https://img-blog.csdn.net/20151129193317892)
效果比第一张图的要好多了。这个是MSAA 4x下的截图,所以颗粒感还是有一些,但只要不放大了看还是很可以接受的。
Shader实现如下:
最典型的使用alpha-to-coverage的例子就是《最终幻想13》三部曲了。
在PS3/Xbox 360上,由于只有MSAA 2x,它的颗粒感是很明显的,但PC版上,由于有了MSAA 8x,因此有了明显的效果。
具体的实现方式可以从DirectX SDK的例子 TransparencyAA就可以了解一二。(顺便说一下,这个例子本来是为了推广TransparencyAA的,但这种渲染技巧对毛发或者叶子边缘的蓬松感的表现没有什么好处,因为TransparencyAA本质上还是一种alpha test的改进形式。)
不过缺省的alpha-to-coverage是一种完全没有随机性的过程,因此它会在alpha值变化的地方产生类似地形图那样的条纹:
为了解决这个问题,humus.name网站的大牛(点击打开链接)提出了使用伪随机函数在pixel shader里改写SV_Coverage,从而改进alpha-to-coverage效果的尝试。但大牛没有留下参考用的shader代码。
因此我就做了点小尝试,去实现大牛提出的效果。实验结果截图和参考shader会贴在后面。
效果比第一张图的要好多了。这个是MSAA 4x下的截图,所以颗粒感还是有一些,但只要不放大了看还是很可以接受的。
Shader实现如下:
// 根据屏幕坐标得到一个伪随机值。由于屏幕坐标是固定的,因此伪随机值不会导致闪烁 float rand_1_05(in float2 uv) { float2 noise = (frac(sin(dot(uv, float2(12.9898, 78.233)*2.0)) * 43758.5453)); return abs(noise.x + noise.y) * 0.5; } struct PsOutput { float4 v4Color : SV_Target; uint uCoverageMask : SV_Coverage; }; PsOutput PsCustomAlphaToCoverage(PsInput I) { PsOutput output; // Lookup alpha texture float4 v4AlphaSample = g_AlphaTexture.Sample( g_AlphaTextureSampler, I.v2TexCoord ); // Modulate by diffuse lighting value output.v4Color = v4AlphaSample * I.v4Diffuse; // 如果是普通的alpha to coverage,在这里就可以返回,也不需要输出SV_Coverage。 // 另外记得定制的alpha to coverage不能打开Blend state里的AlphaToCoverageEnabled选项。 // 根据alpha算出可以写多少个MSAA sample float alpha = clamp(output.v4Color.a * MSAA_SAMPLES, 0.0f, MSAA_SAMPLES); // 如果alpha值太低,则直接不输出任何MSAA sample if (alpha < 0.2f) output.uCoverageMask = 0; // 如果alpha值太高,则输出全部MSAA sample else if (alpha > MSAA_SAMPLES - 0.2f) { output.uCoverageMask = (uint(1) << MSAA_SAMPLES) - 1; } else { // 其余情况则根据alpha的范围决定。 // base和base+1为需要二择一的两组coverage mask。fraction为选择base+1的概率。 float fraction = frac(alpha); uint base = floor(alpha); float randValue = rand_1_05(I.v4Pos.xy); // 根据概率决定该屏幕像素的coverage mask选择的是base还是base+1。 if (randValue < fraction) base++; output.uCoverageMask = (uint(1) << base) - 1; } return output; }
相关文章推荐
- C++基于Directx MMX实现的图像灰度转换代码
- 操作系统进程描述
- 为什么DirectX做的游戏,操作感总是不如Windows原生程序?
- CPU显卡内存与3DMAX渲染的关系
- DirectX--昨天、今天、明天
- Win7版IE10下载包中暗藏了DirectX 11.1
- 2点关于ipx
- OpenGL与DirectX 比较
- DirectX学习第二篇:构建框架
- DirectX Exporter for 3ds Max
- (转)DirectX下 Viewing Frustum 的详细实现
- 绘制六面体
- 【DirectX 9.0c入门教程】之一 开发环境搭建:安装vs2008 sp1和DirectX SDK
- 绘制位图 -chap4
- DirectX渲染状态设置
- 【DirectX游戏编程】游戏中摄像机类的实现(上篇:如何获得观察坐标系转换矩阵)
- openGL\DirectX游戏开发电子书下载
- 如何学好游戏3D引擎编程
- DirectX Media Objects
- Directx 9.0/9.0c 在vs2010 里面的环境配置