1 阴影原理

光源照射到不透明物体上,会向该物体的后面投射阴影,如果阴影区域存在其他物体,这些物体不被光源照射的部分就需要渲染阴影。因此,我们可以将阴影的生成抽象出 2 个流程:物体投射阴影、物体接收阴影。

1.1 阴影相关开关

1)开启 Light 组件渲染阴影

  • No Shadows:不渲染阴影
  • Hard Shadows:硬阴影(阴影边缘较清晰)
  • Soft Shadows:软阴影(阴影边缘较模糊)

2)开启投射阴影 / 接收阴影

  • Cast Shadows:投射阴影,取值有 Off(关闭投射)、On(开启投射)、Two Sided(双面都可以投射阴影)、Shadows Only(只投射阴影,但物体不可见)
  • Receive Shadows:接收阴影

注意:投射阴影和接收阴影开关可以独立控制,不相互依赖,因此,一个物体可以只投射阴影而不接收阴影,也可以只接收阴影而不投射阴影。

1.2 物体投射阴影

1)阴影映射纹理

物体的 MeshRenderer 组件开启了 Cast Shadows 后,Unity 会把该物体加入到光源的阴影映射纹理(Shadow Map)的计算中。阴影映射纹理是一张深度图,它记录了从光源位置能看到的顶点的位置和深度,即将相机放在光源位置渲染的深度纹理。

2)屏幕空间的阴影映射纹理

Unity 5 中,Unity 使用了不同于这种传统的阴影采样技术,即屏幕空间的阴影映射技术(Screenspace Shadow Map)。其原理是:先生成相机的深度纹理和灯光的阴影映射纹理,对于场景中任意一点,以其深度纹理的 x、y 值和阴影映射纹理的 z 值作为该点的屏幕空间阴影映射纹理。注意:Unity 并没有在所有平台上都使用了这种技术并,需要显卡支持 MRT,有些移动平台就不支持该特性。

3)ShadowCaster Pass

正常的渲染流程会调用 LightMode 标签为 ForwardBase 和 Additional 的 Pass,而深度映射纹理是一张深度图,不需要渲染顶点颜色,为节省性能,Unity 设计了 LightMode 为 ShadowCaster 的 Pass 专门用于渲染阴影纹理。如果当前 Shader 文件中没有 LightMode 为 ShadowCaster 的 Pass,就去 Fallback 指定的 Shader 中继续寻找,如果仍没有找到,就继续去 Fallback 里寻找,直到找到了 ShadowCaster Pass 或没有 Fallback,如果最后找到了,Unity 会使用该 Pass 更新光源的阴影映射纹理。

4)渲染阴影映射纹理

对于阴影映射纹理的渲染,Unity 封装得较好,用户只需要在 Shader 的 Fallback 中指定 Diffuse、Specular 或 VertexLit 等,如下:

Fallback "Specular"

内置的 Specular 中没有 ShadowCaster Pass,其 Fallback 为 VertexLit(Shader 见:Unity Editor 安装目录下的 Editor\Data\built-in-shaders-xxx\Shaders\DefaultResourcesExtra\Normal-VertexLit.shader 文件,built-in-shaders),VertexLit 中有 ShadowCaster Pass,如下:

// Pass to render object as a shadow caster
Pass {Name "ShadowCaster"Tags { "LightMode" = "ShadowCaster" }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_shadowcaster#include "UnityCG.cginc"struct v2f {V2F_SHADOW_CASTER;};v2f vert( appdata_base v ){v2f o;TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)return o;}float4 frag( v2f i ) : SV_Target{SHADOW_CASTER_FRAGMENT(i)}ENDCG
}

1.3 物体接收阴影

对于模型的任意顶点,使用该点对应的阴影灰度值(可以从屏幕空间的阴影映射纹理中获取)乘以光照纹理,得到的就是该点最终要显示的纹理。阴影灰度值的计算如下:

struct v2f {float4 pos : SV_POSITION; // 裁剪空间顶点坐标float3 normal: Normal; // 世界空间顶点法线向量float3 worldPos : TEXCOORD0; // 世界空间顶点坐标SHADOW_COORDS(1) // 声明一个用于阴影纹理采样的uv坐标, 入参是下一个可用的插值寄存器的索引(此处下一个可用插值寄存器是TEXCOORD1, 其索引为1)
};v2f vert(a2v v) {v2f o;...TRANSFER_SHADOW(o); // 计算阴影纹理uv坐标return o;
}fixed4 frag(v2f i) : SV_Target {...// fixed shadow = SHADOW_ATTENUATION(i); // 计算阴影灰度// return fixed4(ambient + (diffuse + specular) * shadow, 1);UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); // 计算衰减因子和阴影灰度的乘积return fixed4(ambient + (diffuse + specular) * atten, 1);
}

说明:上述宏的定义在 AutoLight.cginc 文件中;这些宏中使用了上下文中部分变量进行相关计算(如:v2f 中的 pos),因此 pos 的名称不能改变。

2 阴影应用

Shadow.Shader

Shader "MyShader/Shadow" {Properties {_ModelColor ("Model Color", Color) = (1, 1, 1, 1) // 模型颜色_Specular ("Specular Color", Color) = (1, 1, 1, 1) // 镜面反射颜色_Gloss ("Gloss", Range(8.0, 256)) = 20 // 镜面反射光泽度}SubShader {Tags { "RenderType"="Opaque" }Pass { Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma multi_compile_fwdbase#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"fixed4 _ModelColor; // 模型颜色fixed4 _Specular; // 镜面反射颜色float _Gloss; // 镜面反射光泽度struct a2v {float4 vertex : POSITION; // 模型空间顶点坐标half3 normal: NORMAL; // 模型空间顶点法线向量};struct v2f {float4 pos : SV_POSITION; // 裁剪空间顶点坐标float3 normal : Normal; // 世界空间顶点法线向量float3 worldPos : TEXCOORD0; // 世界空间顶点坐标SHADOW_COORDS(1)};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)o.normal = UnityObjectToWorldNormal(v.normal); // 将模型空间法线向量变换到世界空间o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 将模型空间顶点坐标变换到世界空间TRANSFER_SHADOW(o);return o;}fixed4 frag(v2f i) : SV_Target {fixed3 normal = normalize(i.normal); // 世界空间法线向量fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界空间灯光向量fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 世界空间观察向量fixed3 halfDir = normalize(lightDir + viewDir); // 半向量fixed3 albedo = _ModelColor; // 模型自身颜色fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; // 环境光fixed3 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir)); // 漫反射光fixed3 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss); // 镜面反射光(Blinn Phong光照模型)// fixed shadow = SHADOW_ATTENUATION(i); // 计算阴影灰度// return fixed4(ambient + (diffuse + specular) * shadow, 1);UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); // 计算衰减因子和阴影灰度的乘积return fixed4(ambient + (diffuse + specular) * atten, 1);}ENDCG}}FallBack "Specular"
}

运行效果:

3 帧调试器查看阴影绘制过程

通过 Window → Analysis → Frame Debug 打开帧调试器,如下:

渲染对象主要有:Camera DepthTexture(相机深度纹理)、ShadowMap(阴影映射纹理)、Screenspace ShadowMap(屏幕空间阴影映射纹理)、TempBuffer(帧缓冲区)。

1)Camera DepthTexture

2)ShadowMap

3)Screenspace ShadowMap

4)TempBuffer

【Unity3D】阴影原理及应用相关推荐

  1. unity3d 工程原理_Unity3D重要知识点

    数据结构和算法很重要!图形学也很重要!大的游戏公司很看重个人基础,综合能力小公司看你实际工作能力,看你的Demo. 1.什么是渲染管道? 是指在显示器上为了显示出图像而经过的一系列必要操作. 渲染管道 ...

  2. Unity3D 阴影和深度纹理总结

    首先介绍一下,阴影的生成,阴影一般是在屏幕空间生成的,利用Unity3D引擎实现阴影的绘制,首先要明白其实现原理,Unity5的Demo已经为我们提供了代码实现,先介绍一下阴影的视线原理: 屏幕空间的 ...

  3. unity3d 工程原理_Unity3D——镜面反射原理及实现(一)

    1.前言 本文章旨在与大家一起探讨学习新知识,如有疏漏或者谬误,请大家不吝指出. PS:GAD平台导入word后,公式都转成图片了,而且像素很低,看起来模糊,我这边是手动截图替换掉原来的公式,当然,还 ...

  4. Unity3d 引擎原理详细介绍

    体系结构 为了更好地理解游戏的软件架构和对象模型,它获得更好的外观仅有一名Unity3D的游戏引擎和编辑器是非常有用的,它的主要原则. Unity3D 引擎 Unity3D的是一个屡获殊荣的工具,用于 ...

  5. unity3d 工程原理_Unity3D研究院之两个游戏工程资源同步问题(八十六)

    有的公司是不想让美术和策划有程序权限的,但是Unity在处理团队协作上不太理想,这就造成需要把美术工程的资源同步到程序工程里.目前我有两个方法,各有利弊. 1.由美术或者策划同步 Unity提供了.u ...

  6. Unity3d 引擎原理详细介绍、Unity3D引擎架构设计

    体系结构 为了更好地理解游戏的软件架构和对象模型,它获得更好的外观仅有一名Unity3D的游戏引擎和编辑器是非常有用的,它的主要原则. Unity3D 引擎 Unity3D的是一个屡获殊荣的工具,用于 ...

  7. Unity3d 引擎原理详细介绍、Unity3D引擎架构设计 - zhibolife

    时间 2014-03-24 11:18:00  博客园-所有随笔区原文  http://www.cnblogs.com/zhibolife/p/3620440.html 体系结构 为了更好地理解游戏的 ...

  8. Unity3d 引擎原理详细介绍、Unity3D引擎架构设计- zhibolife(转载分享)

    体系结构 为了更好地理解游戏的软件架构和对象模型,它获得更好的外观仅有一名Unity3D的游戏引擎和编辑器是非常有用的,它的主要原则. Unity3D 引擎 Unity3D的是一个屡获殊荣的工具,用于 ...

  9. Unity3D换装系统原理

    在unity中实现换装的方式主要有三种: 1.换材质或换贴图 2.显示或隐藏模型 3.合并网格 第二种主要用于换装备,但当涉及到"本质"上的换装时(例如猪脚是普通人,发动技能后左手 ...

最新文章

  1. 微信小程序如何使用iconfont阿里巴巴图标库?
  2. 进制转换(完成Python14作业的背景补充)
  3. Matlab(一) .jpg转.ppm
  4. java学习(33):巩固练习
  5. spirng cloud docker部署
  6. 12006.eeprom之M24C02
  7. (转)利用AIR的ServerSocket类让 AIR 做socket服务器
  8. 如何在 macOS Monterey 中更改光标的颜色样式?
  9. JS中的slice和splice
  10. 测试的目的、时期、分类
  11. iis6.0渗透实战笔记
  12. 52_LSTM及简介,RNN单元的内部结构,LSTM单元的内部结构,原理,遗忘门,输入门,输出门,LSTM变体GRU,LSTM变体FC-LSTM,Pytorch LSTM API介绍,案例(学习笔记)
  13. 将mov格式的视频转换mp4
  14. Busybox实践2:分析busybox文件链接原理并编程模拟实现自己的busybox文件
  15. 对MOSFET与IGBT详细的区别分析以及举例说明
  16. HtmlCss光标(插入符caret)透明隐藏光标 221106笔记
  17. 合天网安 第四周 | Check your source code
  18. 巡检报告实例-Python脚本生成
  19. 用于android的音乐可视化工具,Android音乐播放可视化
  20. 完美世界实习面试总结

热门文章

  1. 机器学习(1)——周志华
  2. 手把手教你如何通过Java给图片添加文字和图片水印
  3. Nature子刊 | 褚海燕组-土壤生物多样性与城市绿地生态系统功能(朱永官/韦革宏点评)...
  4. Cesium更换地球背景
  5. 去中心化存储项目终极指南 | Filecoin, Storj 和 PPIO 项目异同 (上)
  6. PG中XLOG日志结构
  7. 思维导图的优缺点与绘制思维导图方法
  8. 集线器Hub、交换机Switch 和 路由器Router 的区别
  9. 项目经理之项目经理应该做什么
  10. 【Swift 60秒】51 - Closures as parameters