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. 【FreeRTOS】堆内存管理

    动态内存分配及其与FreeRTOS的相关性 为了使FreeRTOS更易用,内核对象(如任务.队列.信号量.事件组)不在编译期静态分配,而是在运行时动态分配,FreeRTOS在内核对象创建时分配RAM, ...

  2. Qt5.9 UI设计(五)——将Tabwidget与treeWidget相互关联

    前言 前面一章介绍了ControlTabWidget ControlTreeWidget maintitlebar 三个子页面同时布局到 mainwindow 的方法,本章介绍如何将ControlTr ...

  3. 【面试题精讲】如何使用Stream的聚合功能

    有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 系列文章地址 求和(Sum): List<Integer ...

  4. [转帖]离线部署单机kubenetes-1.28.4

    系统版本: openEuler 22.03 (LTS-SP2) docker版本:24.0.7 kubenetes版本: 1.28.4 虚机IP: 192.168.177.138 基于 https:/ ...

  5. [转帖]Strong crypto defaults in RHEL 8 and deprecation of weak crypto algorithms

    https://access.redhat.com/articles/3642912   TABLE OF CONTENTS What policies are provided? Removed c ...

  6. Grafana 监控 PG数据库的操作过程

    Grafana 监控 PG数据库的操作过程 容器化运行 postgres-exporter 进行处理 1. 镜像运行 exporter docker run -p 9187:9187 -e DATA_ ...

  7. nginx 企业版与开源版本的区别

  8. 你应该知道的Hooks知识

    Hooks Hooks 是 React16.8 的新增特性,能够在不写 class 的情况下使用 state 以及其他特性. 动机 在组件之间复用状态逻辑很难 复杂组件变得难以理解 难以理解的 cla ...

  9. fiddler如何抓取https请求

    pc端browse 1.打开下载好的fiddler,点击tools选择options后进入https tab下,勾选Decrypt  HTTPS CONNECTS 和Ignore server cer ...

  10. 【计数,DP】CF1081G Mergesort Strikes Back

    Problem Link 现有一归并排序算法,但是算法很天才,设了个递归深度上限,如果递归深度到达 \(k\) 则立即返回.其它部分都和正常归并排序一样,递归中点是 \(\lfloor (l+r)/2 ...