-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【常见问题】Unity阴影和深度纹理的补充说明(重要) #49
Comments
今天知乎上有人问书里面提到的屏幕空间阴影技术是怎么实现的。这个书上在原理实现上讲得很简略,不过大家只要有耐心都可以在Unity里找到它的实现。这种屏幕空间阴影的实现是延迟渲染里面阴影的常见实现方法,网上也有一些文章介绍它们,例如这篇Tutorial - Deferred Rendering Shadow Mapping。 屏幕空间的阴影延迟渲染中的光照计算绝大部分都是在屏幕空间里进行的,同样也包括阴影。这种屏幕空间的阴影实现主要有这么几个步骤:
上面这个过程在9.4.3节大致提到过。这里再补充一些实现细节问题,其实这个过程就和三张纹理的生成有关系:摄像机的深度纹理,光源的ShadowMap,以及靠前两者得到的屏幕空间阴影纹理。下面主要还是针对Unity里面的实现大概解释一下,希望有兴趣的还是要自己去trace下Unity的各个文件看看它的实现。 摄像机的深度纹理和光源的ShadowMap这两张纹理是前期的准备工作。在Unity里在是前向渲染路径的情况下,这两张纹理主要都是靠有一个Shader中LightMode为ShadowCaster的Pass来完成的,这个实现细节在这个issue上面的答案中给了非常详细的解释。不再赘述。 上面那张ShadowMap有很多空白区域是因为开启了4层的Shadow Cascades,所以实际上渲染了四张ShadowMap。由于这个场景的FarPlane值比较大,而物体离摄像机都很近(距离在10以内),所以其他三张就啥也没渲染到。 屏幕空间阴影纹理这张纹理也是靠内置的一个Shader渲染得到的。从Unity 5.4的Frame Debugger里看到的这个Pass的名称是Hidden/Internal-ScreenSpaceShadows(在DefaultResourcesExtra/Internal-ScreenSpaceShadows.shader)。这个Pass在不同的Unity版本里是不一样的,比如在Unity 5.3里面就是Internal-PrePassCollectShadows(在DefaultResources/Internal-PrePassCollectShadows.shader),看的时候还是要全局搜索确定下。 这个Shader挺长的就不放了,主要思路就是从摄像机的深度纹理里采样得到该fragment的深度值,然后利用矩阵变换计算得到该点对应的世界空间的世界坐标(利用CameraToWorld矩阵),然后再变换到光源空间下的坐标(利用WorldToShadow矩阵),最后拿这个坐标对光源的ShadowMap采样计算阴影。 物体的阴影在前向渲染里面渲染每个物体的时候,会先计算fragment在屏幕空间的位置scrPos,然后再据此对屏幕空间阴影纹理采样即可。这个在Unity里面就是靠内置宏来完成的,比如SHADOW_COORDS、TRANSFER_SHADOW、UNITY_LIGHT_ATTENUATION那一套,这个也可以自己看到实现代码的。 |
屏幕后处理使用的深度和法线纹理在书里的第13章讲了一些使用摄像机深度和法线纹理的特效。这里需要纠正和补充一些关于什么时候以及什么条件下会渲染到这两张纹理(深度纹理以及深度+法线纹理)的内容。
|
Unity版本5.5.1 |
哇, 乐乐女神真是细心,刚看到13.1 节的时候感觉说的不清不楚,于是上issues 来看看,没想到就真的有补充说明 👍 |
你好!感谢你编写这本书让我入门了UnityShader! |
你好,对于所有的ForwardBass Pass来说,只要你想让它能够有正确的光照,都需要设置好LightMode,添加#pragma multi_compile_fwdbase。multi_compile_fwdbase会为这个shader自动生成相应的shader variants,这些variants会负责处理各种光照keywords下的光照计算,比如要不要接受阴影等。 |
你好女神,简单看了Unity ShadowMap的实现,有一点疑问,在前向渲染下屏幕空间阴影相对于传统比较是否真的有提升呢,直接使用传统的方式实现cascades是否比屏幕空间性能高呢,? 传统方式实现cascades: 那是否除非OverDraw非常多的时候(传统方式OverDraw计算阴影的代码开销更大一些)屏幕空间阴影才有优势呢,是否实际情况很难多到可以抵消屏幕空间1、2步的开销呢?还是Unity只是为了让前向和延迟的阴影方案保持保持一致性,也可能是我忽略了哪里。 |
在9.4节,书里提到Unity会在一些平台上使用屏幕空间的阴影映射技术,并在后面解释了如何让物体投射和接收阴影。这里有一些内容需要补充。
截止到Unity 5.4,当项目工程的目标平台是Mobile的时候,就不会使用屏幕空间的阴影映射技术,即使用原始的Shadows Map方法。在代码里,Unity会定义内置宏UNITY_NO_SCREENSPACE_SHADOWS来控制。而当项目工程的目标平台是支持屏幕空间阴影的话,例如PC, Mac & Linux Standalone平台(其他平台还没有验证过……有验证过的欢迎补充)时,会开启屏幕空间的阴影映射技术。正如书中P201下面所写的那样,由于宏UNITY_NO_SCREENSPACE_SHADOWS的定义与否,会生成不同的代码。读者可以通过帧调试器(Frame Debugger)来分辨当前是否使用了屏幕空间的阴影映射技术:
左边是PC平台,使用了屏幕空间的阴影映射技术,可以看到渲染事件明显较多,除了需要生成光源的阴影映射纹理,还增加了生成摄像机的深度纹理和生成屏幕空间阴影纹理的渲染事件。而右边是Mobile平台,使用的是传统的Shadow Map,只需要为光源生成阴影映射纹理。
P.S.:左边在生成光源的阴影映射纹理时也显然比右边多了很多渲染事件,这是因为PC平台上为了效果和性能使用了Shadow Cascades技术,在上图里面4个cascades,而Mobile平台上Shadow Cascades是禁用的。
使用的是屏幕空间的阴影映射技术还是传统的Shadow Map也会影响阴影效果。
屏幕空间的阴影映射技术
此时LightMode为ShadowCaster的Pass会同时影响阴影投射和阴影接收的效果。具体来说,不管是希望物体能够向其他物体投射阴影还是自己接收来自其他物体的阴影,都需要定义LightMode为ShadowCaster的Pass。
因为对于投射阴影来说,需要使用LightMode为ShadowCaster的Pass来生成该光源的阴影映射纹理,这样如果它距离光源更近的话就会记录到阴影映射纹理中;对于接收阴影来说,也需要使用这个Pass来生成屏幕空间的深度纹理,从而可以在计算屏幕空间的阴影纹理时,可以据此来判断该点是否在阴影中。简单来说,因为阴影纹理是一张在正常渲染物体前就生成好的,因此**无论是阴影投射还是阴影接收,都需要定义LightMode为ShadowCaster的Pass**。
需要注意的是,是否会渲染到生成深度纹理不仅仅和LightMode为ShadowCaster的Pass相关,也和渲染队列有关。正如Unity文档中所说:
因此,如果想要确保一个物体会被渲染到深度纹理中,我们需要保证它使用的shader:包含一个LightMode为ShadowCaster的Pass,并且它的渲染队列<= 2500。
综上,我们给出和屏幕空间的阴影相关的几个变量的使用规则:
我们给出在屏幕空间的阴影映射技术下,要实现各个效果所需要的变量设置:
对于Unity 5的Standard Shader的实现,如果我们把它的Rendering Mode设置成Transparent,它的阴影处理方式是和上表中的第三种,即不接收/挡住阴影,但投射阴影,是相同的。除此之外,为了实现半透明阴影的效果,Standard Shader会配合使用Dither的方法来伪造效果。具体可参见issue
传统的Shadow Map
由于不会提前渲染Depth Texture,此时LightMode为ShadowCaster的Pass仅会影响阴影投射的效果。对于需要接收阴影的物体来说,由于它们会在普通的Pass中进行空间转换和判断是否在光源阴影内的计算,而不是提前生成的信息,因此它不需要定义LightMode为ShadowCaster的Pass也可以正确接收到其他物体的阴影。
为了完整性,我们给出给出和Shadow Map阴影相关的几个变量的使用规则:
我们给出在Shadow Map阴影映射技术下,要实现各个效果所需要的变量设置:
这一点需要在书中9.4.2节第二部分”让物体接收阴影“中补充。
The text was updated successfully, but these errors were encountered: