前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我与SM不得不说的故事(一)

我与SM不得不说的故事(一)

作者头像
逍遥剑客
发布2018-05-23 15:35:34
28.1K0
发布2018-05-23 15:35:34
举报

话说当年学shader的目的, 就是为了想实现一个当时天真地以为很NB的算法TSM, 一年多来朝着这个目标努力, 到最后却发现, 结果并没有想像中的美好-_-

最开始实现的是Projective Shadow, 也就是投影纹理的方法. 先把需要投影的物体从光源视角以单一颜色画到一张RTT上, 然后用投影矩阵生成纹理坐标投影到地表上去跟纹理进行混合. 为什么用它呢? 主要是它对硬件没什么要求, 能支持RTT就OK了. 那么, 接下来, 有这么几个问题要解决:

l 锯齿: 没办法, 谁叫RTT那么小呢, 再大也比地形要小

l 阴影范围: RTT就那么大, 投影完了只是对应地上一小块区域, 区域外的没影子了

l 阴影遮挡错误: 这个是投影阴影的硬伤, 它没法判断谁在前谁在后. 比如人站在桥上, 那么不管桥墩, 桥面还是桥底下, 统统投上了影子. 你要是站在屋里看到屋顶上有个影子, 这不是见鬼了么...

l 自阴影就不用想了, 会把自己的正反面全变成影子的颜色

那么, 这些问题, 一个一个来. 找啊找啊找朋友, 哦, 不对, 是找例子. 找到了NV SDK里的PSM例子, 挺典型的, 大场景+平行光+四种算法, 很不错. 下面看看这几个问题怎么一个个地干掉:

l 锯齿: 其实PSM系的算法(包括LiPSM, TSM), 都是把View/Project矩阵拿来猥亵一番, 让阴影在RTT占的面积尽可能地大. 变换完后, 离你近的阴影在RTT上比离你远的要大. 所以, 这几种算法的核心是怎么生成那个变换矩阵, shader那边跟传统的SM没什么差别. 所以, 那矩阵生成代码直接挖出来就能用@_@

l 阴影范围: 这个是两个因素决定的: 一是投影的物体所占的范围, 二是接收阴影的物体所占的范围. 跟据双方的包围盒加吧加吧就出一个范围, 再根据这个范围来对投影矩阵进行优化, 不浪费一块地儿~ 事实证明, 这方法在不考虑接收阴影的物体时, 比什么SM算法都有效, 不过也会产生很诡异的问题. 比如, 你自己一个人时, 阴影精度高乎想像. 而人或NPC一多, 又变成原来那个样子了. 而且, 随着NPC的走动, 阴影的锯齿也在动, 抖啊抖得像是羊癫疯

后俩错误, 没有Depth Buffer是无药可救了, 正好在NV这个例子里注意到了它用的是自家的Hardware Shadow Mapping, 然后我就义无反顾地叛变了…

用HSM(但愿这么叫没有跟某SM算法冲突了), 有啥好处呢?

1. 写DepthBuffer时完全可以把ColorWrite关掉, 理论上比同时写ColorBuffer+DepthBuffer要快2~4倍. 而且实现完了发现它可以跟ProjectiveShadowMapping(我又发明了个名字@#%!@)共用同样的shader, 所以呢, 最后的实现结果会比它速度要快, 白来的性能不要白不要, 更何况遮挡错误也解决了.

2. 根据NV官方文档的说法, 用HSM时开启Linear过滤会免费给你进行2x2的PCF模糊, 能够减轻阴影边缘的柔和度. 不过我当时用错了, 后面提到PCF时再详细说

坏处也有, 这是NV自己的标准! 但我用的ATi3600咋也能跑NV的这个DEMO呢? ATi官方只提到了它自己的那个什么Fetch4技术, 同样是DepthTexture, 比NV的难用多了. 反正这卡跑着没问题, 当时就把ATi的那种实现直接无视了, 不支持就换成投影阴影好了. 后来在某年的显卡驱动新闻中知道, ATi在HD2400之后的显卡也提供了对NV的DepthStencilTexture的支持. 另外, N卡把DST当普通纹理时只能看到一片白, 而A卡确是红黑相间, 可以把DST画出来DEBUG阴影时用. 看看人家ATi, 比HSM的娘家都好.

下面说说自阴影的问题…用SM做自阴影简单是对心理素质的一种磨练, 承受能力差的还是放弃吧-_-在与光线几乎平行的面上的阴影交界处, 那是惨不忍睹啊….就算是地形这种平面, 要是做自阴影, 还有可能出现一大片的”斑马纹”~ 为什么会这样呢? 主要有两个原因:

1. DepthBuffer精度不够. 其实一般D24S8就够了, 但是我同时又结合了TSM, 问题就来了. TSM变换完后Z的范围都集中在一个很小的范围内, 官方是建议把变换前的Z写进buffer的. 但是呢, 对于HSM, DepthBuffer里写什么值不是我们能控制的(这个过程是跟固定管线混合的, 免去了shader的切换), 要想写上自定义的Z值, 需要自定义的VertexShader和浮点格式的RTT, 这个性能消耗就又上去了, 忽略. 还有就是ZNear和ZFar的控制, 这个结合上面说过的包围盒裁剪就可以做得很好. 还有就是需要调节两个bias值来减轻这种现象, 但是调多了也会产生走样, 甚用~

2. 再就是几何问题了. 对于与光线近乎平行的面, 可能一大串的地方(从抽象机角度看)只对应深度纹理上一个在阴影里的像素, 那么这一大串像素就是黑的了(在阴影内). 正好旁边RTT上的另一个像素在阴影外, 那么它对应的那一大串像素就成白了的(阴影外) . 于是乎阴影交界处就产生了参差不齐的花纹, 从另一个角度看, 还挺有艺术感的. 这种情况, 调bias值一般解决不了. 所以, 大多数游戏都不做自阴影的, 可怜地鸡肋…

对于1的问题, 调调bias, 弄弄Frustum, 调节一下CullMode估计就能解决的很好. 对于2, 我只知道一种方法: 模糊!

比如用SM来生成地形的LightMap, 有自阴影的话很难看, 但是做一下Blur之后, 就会发现, 原来效果这么完美. 阴影边界自然而然地有了过渡, 不在再生硬了, 走样问题也被模糊掉了, 一举两得.

那么实时阴影也是一个道理, 通常会采用PCF. 但是PCF的本意没有错, 错的是它又带来更严重的斑马纹~关于它的解决方法, 还没学习到. 暂且不说. 还有一种比较流行的SM : VSM, 是基于方差的, 看demo效果是不错, 就是效率让人受不了, 而且也没法利用DST这个好东西了. 所以还是先看看怎么来解决PCF的缺点来得实用.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2009年12月03日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档