草庐IT

【图形学】31 Unity 的光源衰减和阴影

纸境止境 2023-04-10 原文

来源:《UNITY SHADER入门精要》

文章目录

1、用于光照的衰减纹理

  如我们之前所用到的一样,我们在 Unity 内部使用了一张名为 _LightTesture0 的纹理来采样获得衰减值,这样就避免了复杂的数学计算。
  为了通过 _LightTesture0 纹理采样来获得衰减值,首先,我们必须将光源从 世界空间 变换到自己的 光源空间,为此,我们需要通过 LightMatrix0 变换矩阵得到:

float3 lightCoord = mul(_LightMatrix0, float4(i.worldPosition, 1)).xyz;

  然后我们可以使用这个坐标的模的平方对衰减纹理进行采样,得到衰减值:

fixed atten = (tex2D(_LightTexture0,  dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;

  我们使用了光源空间中顶点距离的平方,通过 dot 函数来得到来对纹理进行采样。之所以没有使用距离是因为这种方法可以避免开方操作。然后,我们使用了 UNITY_ATTEN_CHANNEL 来得到衰减纹理中衰减值所在的分量,以得到最终的衰减值。

2、阴影的产生

①传统的阴影映射纹理

  在实时渲染中,我们最常使用的是一种名为Shadow Map的技术。这种技术理解起来非常简单,它会首先把摄像机的位置放在与光源重合的位置上,那么场景中该光源的阴影区域就是那些摄像机看不到的地方。而Unity就是使用的这种技术。这一过程是在光源空间完成的。
  Unity 采用一种名为 Shadow Map 的技术。它会把摄像机的位置放在与光源重合的位置上,那么场景中该光源的阴影区域就是摄像机看不到的位置。

  在前向渲染中,如果场景中的平行光开启了阴影,Unity 就会为该光源计算它的 阴影映射纹理(shadowmap)。这张阴影映射纹理本质上也是一张深度图,它记录了从该光源位置出发、能看到的场景中距离它最近的表面位置(深度信息)。
  Unity 中,我们这个 Pass 的 Tags 设置为 ShadowCaster 的Pass。这个 Pass 渲染的结果不会给到 帧缓存,而会给到 阴影映射纹理(或深度纹理,因为本来就是通过深度值信息来判断谁会被打亮,谁会形成阴影)。

②屏幕空间的阴影映射技术(Screenspace Shadow Map)

  这个技术原本是 延迟渲染 中产生阴影的方法,现在可以作用于 前向渲染。所使用的平台显卡需要支持 MRT 才能使用。
  当使用了屏幕空间的阴影映射技术时,Unity首先会通过调用 LightMode 为 ShadowCaster 的 Pass 来得到可投射阴影的光源的阴影映射纹理以及摄像机的深度纹理。然后,根据光源的阴影映射纹理和摄像机的深度纹理来得到屏幕空间的阴影图。如果摄像机的深度图中记录的表面深度大于转换到阴影映射纹理中的深度值,就说明该表面虽然是可见的,但是却处于该光源的阴影中。通过这样的方式,阴影图就包含了屏幕空间中所有有阴影的区域。如果我们想要一个物体接收来自其他物体的阴影,只需要在Shader中对阴影图进行采样。由于阴影图是屏幕空间下的,因此,我们首先需要把表面坐标从 模型空间 变换到 屏幕空间 中,然后使用这个坐标对阴影图进行采样即可。

3、Unity产生阴影

①如何开启Unity中的阴影生成

  Unity 中要产生阴影,需要开启 cast Shadows 和 Receive Shadows 两个选项。

②Shader代码中调用阴影

  在我们之前写的产生光照的代码中,我们没有写有关阴影的代码,但是它依然能产生出阴影,这是为何?因为我们在最后添加了 Fallback "Specular" 它自动回调了内置的 Specular ,而 Specular 中又回调了 VertexLit ,其中就有 阴影产生的代码了。
  虽然我们可以自己书写一个 ShadowCaster 的 Pass。但由于这个 Pass 的功能通常可以在多个 UnityShader 中调用的,所以,直接 Fallback 是个更加方便的用法。

  或者我们直接**把 Fallback 设置为 VertexLit **就能直接在Shader中产生阴影了,当然,Fallback 中的 Specular 和 Diffuse 都有调用 VertexLit 了。

③双面阴影

  由于默认的,尽管 cast Shadow 已经被开启了,但是对于一个平面,只有正面对着光源的才会产生阴影,背面不会。若要产生,则将 cast Shadow 的选项设置为 Two Sided。

4、Unity接收阴影

  在我们普通的 前向渲染光照Shader 中,做出这样的修改:
(1)包含新的内置文件:#include "AutoLight.cginc"。
(2)在顶点着色器的输出结构体 v2f 中添加内置宏 SHADOW_COORDS。声明一个对阴影纹理坐标的采样。

struct v2f{
	float4 pos : SV_POSITION;
	float4 worldNormal : TEXCOORD0;
	float3 worldPos : TEXCOORD1;
	SHADOW_COORDS(2)
};

(3)定点着色器返回之前添加另一个内置宏 TRANSFER_SHADOW

v2f vert(a2v v){
	v2f o;
	...
	TRANSFER_SHADOW(o);
	
	return o;
}

(4)片元着色器中依然使用内置宏来处理,采用内置宏 SHADOW_ATTENUTATION

fixed shadow = SHADOW_ATTENUATION(i);

(5)最后在返回值的时候,把变量 shadow 和漫反射以及高光反射的颜色相乘。

  最后注意我们只在

  我们完全可以看到,这里所有的阴影的代码都是通过内置宏来实现的,我们可以在 AutoLight.cginc 中找到它们的声明(内容非常长,而且也根据平台的不同而做了许多相关的优化)。
  需要注意的是,由于这些宏中会使用上下文变量来进行相关计算,例如 TRANSFER SHADOW 会使用 v.vertexa.pos 来计算坐标,因此为了能够让这些宏正确工作,我们需要保证自定义的变量名和这些宏中使用的变量名相匹配。我们需要保证:v2f 结构体中的顶点坐标变量名必须是 vertex ,顶点着色器的输出结构体 v2f 必须命名为 v,且 v2f 中的顶点位置变量必须命名为 pos。

5、更新 Addtional Pass 中的阴影部分

  因为上面的代码我们只更新了 Base Pass 的部分,而 Base Pass 只调用了一次。所以这次我们将新的代码添加在 Addtional Pass 中。
  前面的步骤都和 Base Pass 一样的,唯一不一样的事情是最后的片元着色器 计算衰减的那一部分:

fixed frag(v2f i) : SV_Target{
	...
	UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
	return fixed4(ambient + (diffues + specualr) * atten, 1.0);
}

  UNITY_LIGHT_ATTENUATION 是 Unity 内置的用于计算光照衰减和阴影的宏,我们可以在内置的 AutoLight.cginc 里找到它的相关声明。它接受3个参数,它会将光照衰减和阴影值相乘后的结果存储到第一个参数中。注意到,我们并没有在代码中声明第一个参数 atten,这是因为 UNITY_LIGHT_ATTENUATION 会帮我们声明这个变量。它的第二个参数是结构体 v2f,这个参数会传递给9.4.2节中使用的SHADOW ATTENUATION,用来计算阴影值。而第三个参数是世界空间的坐标,正如我们在93节中看到的一样,这个参数会用于计算光源空间下的坐标,再对光照衰减纹理采样来得到光照衰减。我们强烈建议读者查阅 AutoLight.cginc 中 UNITY_LIGHT_ATTENUATION 的声明,读者可以发现,Unity 针对不同光源类型、是否启用cookie等不同情况声明了多个版本的 UNITY_LIGHT_ATTENUATION。这些不同版本的声明是保证我们可以通过这样一个简单的代码来得到正确结果的关键。

6、帧调试器查看阴影绘制

7、透明物体产生阴影

①透明度测试的物体产生阴影

  之前我们产生阴影,填写 Fallback "VertexLit",调用内置的宏定义就完事了。为了让透明度测试的物体得到正确的阴影效果,我们只需要在 UntiyShader中更改一行代码,Fallback "Transparent/Cutout/VetexLit"。我们可以在内置文件中找到该Unity Shader的代码,它的ShadowCaster Pass也计算了透明度测试,因此会把裁剪后的物体深度信息写入深度图和阴影映射纹理中。但需要注意的是,由于 Transparent//Cutout/VertexLit 中计算透明度测试时,使用了名为 _Cutoff 的属性来进行透明度测试,因此,这要求我们的Shader中也必须提供名为 _Cutoff 的属性。否则,同样无法得到正确的阴影结果。
  同时,我们也也应该在 Cast Shadows 属性中设置为 Two Sided,以获取正确的面别对光源的阴影。

②透明度混合的物体产生阴影

  与透明度测试的物体相比,想要为使用透明度混合的物体添加阴影是一件比较复杂的事情。事实上,所有内置的透明度混合的 Unity Shader,如 Transparent/VertexLit 等,都没有包含阴影投射的Pass。
  这意味着,这些半透明物体不会参与深度图和阴影映射纹理的计算,也就是说,它们不会向其他物体投射阴影,同样它们也不会接收来自其他物体的阴影。
  书中提到的 dirty trick 是通过调用不透明物体的 Fallback "VertexLit" 来为混合透明的物体生成阴影。

有关【图形学】31 Unity 的光源衰减和阴影的更多相关文章

  1. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  3. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  4. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  5. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  6. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  7. ruby - 图形与 Prawn - 2

    寻找一个gem为prawn添加gtraphing功能,我找到了thisone但它似乎有点过时了。有没有更活跃的gem? 最佳答案 直接在Prawn内部绘图没有什么非常活跃的,但是Gruff是一个高度可配置的活跃gem,可以让你制作各种图表。事实上,prawn-graph基本上是gruff的包装器!我的建议是使用gruff生成所需的图表和图形,然后将它们作为图像嵌入到Prawn文档中。所以代码看起来像这样:g=Gruff::Line.new(400)g.title="TransparentBackground"g.theme={:co

  8. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

  9. 三分钟集成 TapTap 防沉迷 SDK(Unity 版) - 2

    三分钟集成Tap防沉迷SDK(Unity版)一、SDK介绍基于国家对上线所有游戏必须增加防沉迷功能的政策下,TapTap推出防沉迷SDK,供游戏开发者进行接入;允许未成年用户在周五、六、日以及法定节假日晚上8:00-9:00进行游戏,防沉谜时间段进入游戏会弹窗进行提示!开发环境要求:Unity2019.4或更高版本iOS10或更高版本Android5.0(APIlevel21)或更高版本🔗Unity集成Demo参考链接🔗UnityTapSDK功能体验APK下载链接二、集成前准备1.创建应用进入开发者后台,按照提示开始创建应用;2.开通服务在使用TDS实名认证和防沉迷服务之前,需要在上面创建的应

  10. 【Unity大气散射】GAMES104:3A中如何实现大气散射 - 2

    写在前面前两天学习并整理的大气散射基础知识:【Unity大气渲染】关于单次大气散射的理论知识,收获了很多,但不得不承认的是,这其实已经是最早的、90年代的非常古老的方法了,后来也出现了一些优化性的计算思路和方法。因此,我打算先不急着跟各种教程在Unity中实现大气散射,而是再花时间来看看最近的游戏是如何去实现大气渲染的:06.游戏中地形大气和云的渲染(下)|GAMES104-现代游戏引擎:从入门到实践接下来就跟着GAMES104讲地形大气和云渲染的部分学习并做简单的记录,涉及到之前没提到的Mie散射也只选择直接截图PPT的方式记录啦!毕竟对于做作品来说,之后实现出来才是重要的~当然,May佬的

随机推荐