来源:《UNITY SHADER入门精要》

文章目录

  • 1、用于光照的衰减纹理
  • 2、阴影的产生
    • ①传统的阴影映射纹理
    • ②屏幕空间的阴影映射技术(Screenspace Shadow Map)
  • 3、Unity产生阴影
    • ①如何开启Unity中的阴影生成
    • ②Shader代码中调用阴影
    • ③双面阴影
  • 4、Unity接收阴影
  • 5、更新 Addtional Pass 中的阴影部分
  • 6、帧调试器查看阴影绘制
  • 7、透明物体产生阴影
    • ①透明度测试的物体产生阴影
    • ②透明度混合的物体产生阴影

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. unity Build-in 光源和光照模型

    1.Unity光源模型 1.1 两种渲染方式 前向渲染:每个物体都有可能被重复渲染,即基于物体的光源渲染:超出灯光设置数量被当作顶点光(6*6) 延迟渲染:基于光源的物体渲染:以灯光为单位,先计算灯光 ...

  2. Unity 3D光源-Point Light点光源详解/灯泡、模拟灯光效果教程

    Unity4大光源之点光源 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人! (拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享 ...

  3. UnityShader---光照(衰减和阴影))(内置渲染管线)---10

    光照衰减: // Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight'Shader "Unlit/15" ...

  4. 【Unity】Planar Shadows平面阴影的实现

    Plannar Shadows,即平面阴影,是一个适用于平坦地形的假阴影技术.要求阴影的Receiver为平面,Occluder不与其他物体穿插. 实现效果 1.定向光源Planar Shadows ...

  5. 计算机图形学笔记十一:Ray Tracing1(阴影,光线追踪基本原理)

    阴影,光线追踪基本原理 1.阴影贴图(Shadow Mapping) 1.1产生步骤 1.2存在问题 2.光线追踪( Ray Tracing) 2.1为什么需要光线追踪? 2.2基础光线追踪算法(Ba ...

  6. Unity Shader - Planar Shadow - 平面阴影

    文章目录 整体运行效果 思路 Shader 问题 Z-Fighting,解决:按法线方向偏移一丢丢 绘制 Alpha 混合重叠,解决:使用 stencil buffer 来规避 为何出现这个问题 解决 ...

  7. Unity 3D光源-Spot Light聚光灯用法详解、模拟手电筒、台灯等线性教程

    Unity4大光源之聚光灯 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人! (拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享 ...

  8. Unity使用RenderTexture实现实时阴影绘制

    前言 一般实时阴影主要还在出现在角色.怪物的脚底为了然场景表现的更加逼真,实现起来主要会用到这三种方式:脚底放个阴影图片跟着主角动,通过摄像机的RenderRexture绘制显示,直接使用Unity的 ...

  9. unity 手电筒光源SpotLight

    现实生活中, 夜晚可能会使用手电筒来照明, 在Unity中, 可以通过 Create , Light ,SpotLight 来创建一个 手电筒光源. 这时,可以调节此光源的 位置和角度, 还有光线能照 ...

最新文章

  1. 注射“基因剪刀”治疗罕见遗传病,患者28天内无明显不良反应,诺奖得主很激动...
  2. codevs1688 求逆序对(权值线段树)
  3. 自然语言处理中的语言模型预训练方法
  4. 蓝桥杯 日志统计 尺取
  5. matlab盒子分形维数_分形维数--matlab
  6. Windows Server 2012学习
  7. markdown这么好用的东西我才知道。。。多么不折腾的我。。。
  8. ArcGIS三种方式打断相交线------拓扑法
  9. bash脚本编程之十 函数
  10. java mysql字符串拼接_Oracle与MySQL字符串拼接
  11. 消息中间件MQ与RabbitMQ
  12. [计算机基础]浮点数在计算机中的表示
  13. 如何在Kindle上阅读漫画书和漫画
  14. unity设置中文版
  15. HTML5 移动页面自适应手机屏幕四种方法(禁止html5手机端双击页面放大的问题)
  16. 微信缓存dat怎么转图片_PC微信dat如何转图片?方式方法
  17. 热修复——Tinker的集成与使用
  18. MySQL的Logo为 标志_MySQL 的Logo为[     ]标志,海豚代表了速度、动力、精确等MySQL所拥有的特性。_国际贸易基础知识答案_学小易找答案...
  19. cmwap和cmnet接入点的区别
  20. 计算机中腾讯QQ程序的安装路径,更改应用商店内应用的安装位置

热门文章

  1. 浅谈世界上最值钱的东西
  2. linux虚拟机中dns安装,linux虚拟机dns服务器配置步骤(linux服务器配置dns步骤)
  3. Qt使用QProcess进程间双向通信(linux和win系统)
  4. 嵌入式实验报告总结一
  5. PT软件完成中小型企业网络主网
  6. server2008网卡驱动包_从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造...
  7. php 正则匹配11位数字,php正则匹配数字
  8. ceph-mds文件系统操作指南
  9. Linux身份验证策略
  10. 实体店如何借助抖音去推广,实体店如何玩转抖音