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; // 模型空间顶点坐标
float3 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】阴影原理及应用

【Unity3D】阴影原理及应用的更多相关文章

  1. Unity3d 发动机原理详细介绍

    Unity3d 发动机原理详细介绍 www.MyException.Cn   发布于:2013-10-08 16:32:36   浏览:46次 0     Unity3d 引擎原理详细介绍 体系结构 ...

  2. DEM山体阴影原理以及算法具体解释

    山体阴影原理以及算法具体解释 山体阴影基本原理: 山体阴影是假想一个光源在某个方向和某个太阳高度的模拟下.用过临近像元的计算来生成一副0-255的灰度图. 一.山体阴影的主要參数: 1.  太阳光线的 ...

  3. Unity3d跨平台原理

    知乎的一个提问:unity3d跨平台原理 一些资料: IL IL是.NET框架中中间语言(Intermediate Language)的缩写.使用.NET框架提供的编译器可以直接将源程序编译为.exe ...

  4. 3D模板阴影原理

    3D模板阴影原理 1:先从3dsMax中导出一个简单的场景,一个园环,球,平面. 2:园环直接面向光源,园环对球体来说是一个光线的阻挡物,园环在它上面形成阴影,同时,园环和球体对平面来说是光线的阻挡物 ...

  5. Unity3d 引擎原理详细介绍

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

  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的跨平台原理核心在于对指令集CIL(通用中间语言)的应用. 机理 首先需要知道,Unity中的Mono是基于 通用语言架构(Common Language Infrastructure, ...

  9. CSS3实现图形曲线阴形和翘边阴影

    首先,来看看完成之后的效果图: 实现原理 ①曲线阴影实现: 多个阴影重叠,就是正常阴影+曲线阴影 正常情况下,有个矩形有正常的阴影,作为主投影,这时候再定义一个有一定弧度圆角的圆角矩形,然后放在正常矩 ...

  10. 聊聊 Material Design 里,阴影的那些事儿!

    当你的设计师要求你在某个 View 上增加阴影效果,那你只需要认真阅读本文,阴影的问题就不再是问题. 一.前言 设计师的世界,与常人不同,有时候想要扁平化的风格,有时候又想要拟物化的风格.而在 Mat ...

随机推荐

  1. 凡是有但是-varchar和nvarchar的初步学习之一

    凡是有但是-varchar和nvarchar的初步学习之一 背景 高应用开发, 在涉及到国内国外的问题时,重要的事情有两个: 时区转换, 字符集转换. 时区转换虽然是很难理清楚, 各种规范不统一的事情 ...

  2. [转帖]Nginx - 根据IP分配不同的访问后端

    https://www.cnblogs.com/hukey/p/11868017.html 1. 需求分析 为了在线上环境提供测试分支,规定将某IP转发到测试程序地址.如果是 ngx 直接对外,采用 ...

  3. [转帖]perf学习-linux自带性能分析工具

    目前在做性能分析的事情,之前没怎么接触perf,找了几篇文章梳理了一下,按照问题的形式记录在这里. 方便自己查看.   什么是perf? linux性能调优工具,32内核以上自带的工具,软件性能分析. ...

  4. 银河麒麟(Ubuntu)无法上网问题的解决方法

    最近部门借了几台银河麒麟的服务器. 因为有特殊用途, 不允许连接互联网,所以没办法只能搭建一个小的局域网进行处理. 但是发现在搭建过程中遇到了一些坑, 之前协助同事解决odoo问题时也遇到过, 当时本 ...

  5. 多模态 GPT-V 出世!36 种场景分析 ChatGPT Vision 能力,LMM 将全面替代大语言模型?

    LMM将会全面替代大语言模型?人工智能新里程碑GPT-V美国预先公测,医疗领域/OCR实践+166页GPT-V试用报告首发解读 ChatGPT Vision,亦被广泛称为GPT-V或GPT-4V,代表 ...

  6. 原生js判断某个区域的滚动条滚动到了底部

    原生js判断某个区域的滚动条滚动到了底部### 讲解==> 关系公式:element.scrollHeight - element.scrollTop === element.clientHei ...

  7. python安装与python、pip的环境变量配置

    进入官网 在你常用的搜索引擎中输入 python官网 然后进入. 可直接点击本链接 python官网进入: 也可在浏览器地址栏输入www.python.org回车进入官网. 下载 将鼠标放到菜单栏中的 ...

  8. C# 使用RabbitMQ消息队列

    参考文章 https://www.cnblogs.com/kiba/p/11703073.html和https://www.cnblogs.com/longlongogo/p/6489574.html ...

  9. VUE3子表格嵌套分页查询互相干扰的问题解决

    VUE3在表格中嵌套子表格子表格的分页查询互相干扰的问题解决 简单嵌套 如果不需要做子表格的分页查询,那么可以直接在主表格中嵌套子表格,有两种方式:一种是主表格加载的同时加载子表格数据,另一种是点击展 ...

  10. “I/O多路复用”和“异步I/O”的前世今生

    曾经的VIP服务在网络的初期,网民很少,服务器完全无压力,那时的技术也没有现在先进,通常用一个线程来全程跟踪处理一个请求.因为这样最简单.其实代码实现大家都知道,就是服务器上有个ServerSocke ...