Render Flow of Divinity II (part 2 shadow map)

作者:clayman 仅供个人学习使用,转载请保留作者以及原文链接,勿用于任何商业用途。

上一篇文章介绍了Divinity2渲染流程的pre pass阶段,最后稍稍漏了一部分,这里补上。Pre-pass中ps 76行以后的代码是alpha test实现,这里asm代码逻辑看起来稍微有些奇怪,逻辑其实并不复杂,通过改变c0变量(AlphaTestFunction)的值,可以选择裁剪alpha值大于,小于或者等于AlphaTestRef的像素,比普通alpha test稍微高级一点点的版本:)    好了,接下来让我们看shadow map生成。

Shadow map生成在pre-pass之后从EID 14924开始,点击0x226D8C80处的纹理,可以看到shadow map如下图所示:

   这是一张包含了4张shadow map的纹理,2048*2048,R16G16F格式,显然这是cascaded/parallel-split shadow maps。但如果你仔细看贴图中的内容,这并不是标准的ppsm,而是以视点为中心与观察方向无关的csm,类似于real-time 3rd中介绍Hellgate:London所用的方法: generate a fixed set of shadow map at differernt resolutions , nested around the viewer.

这里可以找到该算法的详细信息以及demo。由于与视线无关,这种方法的好处是不必每帧都重新渲染所有层次的shadow map,可以把计算分散到n帧进行,比如每帧总是渲染最靠近观察者的左上角L0级shadow map,n%3==0帧刷新L1级,n%3==1帧刷新L2,n%3==2刷新L3级shadow map,牺牲一定的质量换取性能。实际上游戏设置就有每帧刷新所有shadow map还是分步刷新的选项。Pix文件抓数据时用了最高设置,所以后面可以看到4张shadow map都刷新了。注意,因为是渲染到texture atlases,需要为不同的shadow map设置不同的viewport。下面是生成shadow map所用的vertex shader:

 1 // Generated by Microsoft (R) D3DX9 Shader Compiler
 2 // Parameters:
 3 //   float4x4 kLightViewProj;
 4 //   float4x4 kWorld;
 5 // Registers:
 6 //   Name           Reg   Size
 7 //   -------------- ----- ----
 8 //   kWorld         c0       4
 9 //   kLightViewProj c4       4
10 //
11 // Default values:
12 //   kWorld
13 //     c0   = { 0, 0, 0, 0 };
14 //     c1   = { 0, 0, 0, 0 };
15 //     c2   = { 0, 0, 0, 0 };
16 //     c3   = { 0, 0, 0, 0 };
17 //
18 //   kLightViewProj
19 //     c4   = { 0, 0, 0, 0 };
20 //     c5   = { 0, 0, 0, 0 };
21 //     c6   = { 0, 0, 0, 0 };
22 //     c7   = { 0, 0, 0, 0 };
23 //
24
25 vs_1_1
26 def c8, 0, 0, 0, 0
27 dcl_position v0
28
29 dp4 r0.x, v0, c0       //worldPos = mul(inputPos,kWorld)
30 dp4 r0.y, v0, c1
31 dp4 r0.z, v0, c2
32 dp4 r0.w, v0, c3
33
34 dp4 oPos.x, r0, c4     //lightViewProjPos = mul(worldPos,lightViewProj);
35 dp4 oPos.y, r0, c5     //outPos.xy = lightViewProjPos.xy;
36 dp4 r1.x, r0, c6
37 dp4 r0.y, r0, c7
38
39 slt r0.z, r1.x, c8.x   //sign = (lightViewProjPos.z < 0)? 1 : 0;
40 mad r0.x, r0.z, -r1.x, r1.x   // temp = sign * (-lightViewProjPos.z) + lightViewProjPos.z;
41
42 mov oPos.zw, r0.xyxy   //outPos.zw = float2(temp ,lightViewProjPos.w);
43 mov oT0.xy, r0        //outTexcoord.xy = xxxx

代码非常简单,唯一需要注意的是39,40行对z值为负数的情况作了处理(当物体位于camera之后)。另外,slt/mad也是最常见的用branchless代码实现branch的方法,翻译为普通代码相当于:
if(lightViewProjPos.z < 0)
   lightViewProjPos.z = 0;

  接下来是pixel shader代码:

1  ps_2_0
2     def c0, 0, 0, 0, 0
3     dcl t0.xy
4
5     rcp r0.w, t0.y           r0.w = 1 / lightViewProjPos.w
6     mul r0.x, r0.w, t0.x     r0.x = r0.w * t0.x;
7     mul r0.y, r0.x, r0.x     r0.y = r0.x * r0.x;
8     mov r0.zw, c0.x          r0.zw = 00;
9     mov oC0, r0

  PS输出post-perspective距离,特别注意G通道中输出了距离的平方,所以这还是一张variance shadow map!EID24883完成了4张shadow map的渲染。 Divinity2只有静态物体产生阴影:(, 唯一比较特别的是树叶,树叶总是在每张shadow map最后渲染,如果采用跨帧渲染渲染,树叶阴影总是静态的,如果是全刷选项,树叶则是动态阴影。感兴趣可以查看24883处DP所用的shader,这里不再仔细分析。接下来EID24884 – 24928对shadow map做了blur,vs代码非常简单,只看第一个blur pass的ps代码:

 1 // Generated by Microsoft (R) D3DX9 Shader Compiler
 2 // Parameters:
 3 //   float g_SunSMTexelSize;
 4 // Registers:
 5 //   Name             Reg   Size
 6 //   ---------------- ----- ----
 7 //   g_SunSMTexelSize c0       1
 8
 9 // Default values:
10 //g_SunSMTexelSize
11 //c0   = { 0.000488281, 0, 0, 0 };
12
13 preshader
14 mul c0.x, c0.x, (-2)
15 add c1.x, c0.x, c0.x
16
17 // approximately 2 instructions used
18 // Generated by Microsoft (R) D3DX9 Shader Compiler
19 // Parameters:
20 //   float g_SunSMTexelSize;
21 //   sampler2D shadowMapSampler;
22
23
24 // Registers:
25 //   Name             Reg   Size
26 //   ---------------- ----- ----
27 //   g_SunSMTexelSize c2       1
28 //   shadowMapSampler s0       1
29 // Default values:
30 //   g_SunSMTexelSize
31      c2   = { 0.000488281, 0, 0, 0 };
32
33 ps_2_0
34 def c3, 0, 0.0625, 0, 0
35 def c4, 0.0625, 0.25, 0.375, 0.25
36 def c5, 0, 1, 0, 0
37 dcl t0.xy
38 dcl_2d s0
39
40 mov r0.x, c3.x               r0.x = c3.x
41 mov r0.y, c0.x               r0.y = g_SunSMTexelSize * -2
42 add r0.xy, r0, t0            r0.xy += texcoord
43
44 mov r1.x, c3.x
45 mov r1.y, -c2.x
46 add r1.xy, r1, t0            r1.xy = texcoord + float2(0,g_SunSMTexelSize *2);
47
48 mov r2.x, c3.x
49 mov r2.y, c2.x
50 add r2.xy, r2, t0            r2.xy = texcoord + float2(0,g_SunSMTexelSize);
51
52 mov r3.x, c3.x
53 mov r3.y, c1.x
54 add r3.xy, r3, t0            r3.xy = texcoord + float2(0,g_SunSMTexelSize * 4);
55
56 texld r0, r0, s0
57 texld r1, r1, s0
58 texld r2, r2, s0
59 texld r4, t0, s0
60 texld r3, r3, s0
61
62 mov r0.y, r1.x
63 mov r0.w, r2.x
64 mov r0.z, r4.x               r0 = float4(sample0.x,sample1.x,sample2.x,sample4.x);
65
66
67 mul r1, r0, r0               r1 = mul(r0,r0); //r0^2
68 dp4 r0.w, r0, c4             weightSum = dot(r0,c4);
69 dp4 r1.w, r1, c4             weightSum2 = dot(r1,c4);
70 mul r2.w, r3.x, r3.x         r2.w = r3 * r3
71 mad r0.x, r3.x, c3.y, r0.w   r0.x = r3.x * 0.0626 + weightSum
72 mad r0.y, r2.w, c3.y, r1.w   r0.y = r2.w * 0.0625 + weightSum2
73 mov r0.z, c5.x
74 mov r0.w, c5.y
75 mov oC0, r0                  return float4(r0.x,r0.y,0,1);
76
77 // approximately 29 instruction slots used (5 texture, 24 arithmetic)

  标准的水平模糊,做了5个采样点 -2,0,1,2,4。有意思的是采样只用了普通的深度,然后做了一次平方,而没有直接读取shadow map中之前输出的深度平方值。这样的话,其实shadow map生成还有优化余地,直接使用f16的纹理,或者f32的纹理也行。最后一段代码看起来稍微有些复杂,其实只是加上了第5个参数的权重而已。

从EID24948开始,又专为主角渲染了一张shadow map,纹理大小1024*1024,R32F格式,代码和之前的类似,只不过vs多了骨骼动画的计算.这里不详细讨论。

下一次,我将介绍最复杂也是最有意思的light pass阶段。最后,foward渲染阶段时,再着重分析各种不同物体(terrain,model,sky...)的渲染。

--------------------------本人才学疏漏,如有错误欢迎指证,未完待续--------------------------------

转载于:https://www.cnblogs.com/clayman/archive/2013/01/13/2859040.html

Render Flow of Divinity II (part 2 shadow map)相关推荐

  1. [工作积累] shadow map问题汇总

    1.基本问题和相关 Common Techniques to Improve Shadow Depth Maps: https://msdn.microsoft.com/en-us/library/w ...

  2. 阴影映射(Shadow Map)的研究(一)

    阴影映射(Shadow Map)的研究(一) 这段时间在搭好自己的框架后,就开始马不停蹄地研究阴影映射的内容了,说起阴影映射,倒不如说shadow map更容易被专业人士所接受.shadow map是 ...

  3. 联级阴影贴图CSM(Cascaded shadow map)原理与实现

    联级阴影贴图CSM(Cascaded shadow map)原理与实现 CSM是利用分层的ShadowMap技术,实现大场景的阴影算法.示意图如下图: 我们通过给眼视锥分片,为每个分片生成一个相同分辨 ...

  4. 所有的shadow map的名称

    本贴内容转自http://hyzgame.blogspot.com/2009/04/sm.html,一位网友列出的他搜到的所有的shadow map的名称,挺好,方便大家查找,复制到这里.现在TSM似 ...

  5. Shadow Map阴影贴图技术之探 【转】

    这两天勉勉强强把一个shadowmap的demo做出来了.参考资料多,苦头可不少.Shadow Map技术是目前与Shadow Volume技术并行的传统阴影渲染技术,而且在游戏领域可谓占很大优势.本 ...

  6. Shadow Map 原理和改进 【转】

    http://blog.csdn.net/ronintao/article/details/51649664 参考 1.Common Techniques to Improve Shadow Dept ...

  7. Shadow Map在DirectX9.0 SDK Sample 的实现方法

    很宝贵的资料,总结的相当不错.目前原创作者未知,如有知情者肯请告知,感激不尽~   一.前言 这个教程主要面对DirectX9.0的初学者,文中代码说明部分以DirectX9.0c SDK(Augus ...

  8. Cascaded Shadow Map(CSM)中的一些问题

    Cascaded Shadow Map(CSM)是目前引擎中主流的阴影技术,效率与效果均不错.它与传统的单张Shadow Map的区别主要在于将视锥体进行了层次的分解,每一层单独计算相关的SM,这样在 ...

  9. Vulkan Cascade Shadow Map的故事

    你只需做你自己,做你想做的事,不要沦为人海中的沧海一粟. --<沉默的多数派> 序 最近几周,看了看知乎上关于阴影的文章,虽然我记得大二的时候好像也看过那本<Unity Shader ...

最新文章

  1. 在编写存储过程时使用 Set NoCount On
  2. 在一个大项目中,我选择了另一种JDK实现AdoptOpenJDK
  3. mqtt实例 php_php--mqtt实现推送
  4. Java进阶:多线程Lock管理多个Condition的实践
  5. java克鲁斯卡尔算法_Java语言基于无向有权图实现克鲁斯卡尔算法代码示例
  6. Web Service实现分布式服务的基本原理
  7. RobHess的SIFT源码分析:imgfeatures.h和imgfeatures.c文件
  8. 关于WEB集群中文件服务器的讨论
  9. Python(一)缺点
  10. Ubuntu18.04取消VIM自动备份文件
  11. About MS Reporting Service
  12. 小白都能了解的聚类算法之一(Kmeans与GMM)
  13. ubuntu 22.04安装微信QQ阿里旺旺等
  14. 如何获取目标期刊的参考文献格式模板?
  15. linux分段加载程序_Linux的分段机制
  16. 凸集函数之基本属性和示例
  17. 作为一个平面设计师,该如何转变平面设计思维
  18. 【PS/AI】10款逼真的喷泉背景免费矢量设计素材
  19. 菜鸟炒美股(一) (转)
  20. 2016年微信app支付开发填坑篇

热门文章

  1. IT各大公司名字由来
  2. 曲面等值线上任意一点的曲率公式
  3. 11、形参和实参的概念
  4. 微信H5中动态设置标题(vue-wechat-title)
  5. 腾讯地图之地图选点组件隐藏返回当前位置按钮
  6. archlinux下我的软件列表
  7. centos 永久关闭THP
  8. Windows 11的Windows Hello的指纹解锁用不了程序报错解决方法。
  9. LibreTranslate
  10. 使用JavaMail群发邮件