深度缓冲区

为了实现渲染管线判定物体的空间层级关系,由于系统无法仅凭色彩数据判断是否覆盖已有像素,深度缓冲(Depth Buffer)作为辅助缓冲区,又称之为Z缓存(z-buffer)

这个用于作为第二存储位图,尺寸和帧缓冲区完全一致,但存储的是物体像素到摄像机的空间距离

深度缓冲采用非线性映射存储0-1区间的数值,通过为近景物体分配更多数据位来实现高精度渲染

在渲染物体时会绘制物体的深度值然后与深度缓冲区中的值进行比对这个过程就是深度测试(depth test),当绘制物体的深度值小于或等于缓冲区值时,新物体就会覆盖原有的物体,若绘制的是不透明物体还会更新缓冲区中的值,透明物体一般情况下不会写入深度数据

然而这些不需要了解,因为这些比对操作由shadergraph自动完成

image-20260522192910972

这里我们需要关注这里面的这两个东西—–depth write(深度写入)和depth test(深度测试)

深度写入功能决定在深度测试通过时,是否更新深度缓冲区数值,默认是auto模式,该模式会自动处理不透明物体并跳过透明物体

而深度测试功能就是自定义新像素写入时的深度比较方式,系统默认采用L Equal模式也就是当前深度 ≤ 缓冲区深度

一、Depth Write(深度写入)— 3 个选项

控制是否将当前像素的深度值写入 Depth Buffer(深度缓冲区 / Z-Buffer)

选项 说明 典型应用
Auto(自动) 根据渲染队列(Render Queue)自动决定 默认设置,不透明物体开启,透明物体关闭
Force Enabled(强制开启) 强制写入深度,无视队列类型 特殊透明物体需要参与深度测试、粒子软粒子
Force Disabled(强制关闭) 强制不写入深度 纯透明物体、UI、特效、后处理

二、Depth Test(深度测试)— 8 个比较模式

控制当前像素的深度值如何与 Depth Buffer 中已有的深度值 进行比较,决定是否通过测试、是否渲染该像素

模式 数学含义 效果 典型应用
Less 当前深度 < 缓冲区深度 比已有物体更近才渲染 标准不透明物体(默认)
Greater 当前深度 > 缓冲区深度 比已有物体更远才渲染 特殊效果,如”只显示被遮挡部分”
LEqual(默认) 当前深度 ≤ 缓冲区深度 更近或相等时渲染 透明物体、避免 Z-Fighting
GEqual 当前深度 ≥ 缓冲区深度 更远或相等时渲染 天空盒、远处背景
Equal 当前深度 = 缓冲区深度 仅相等时渲染 特殊遮罩、Stencil 配合
NotEqual 当前深度 ≠ 缓冲区深度 不相等时渲染 排除特定深度区域
Always 永远通过 无视深度,总是渲染 UI、全屏特效、Debug 显示
Never 永远不通过 永远丢弃,不渲染 占位、调试、特殊剔除

当然也可以启用材质覆盖 Allow material override这样就可以在inspector窗口里对单个材质进行设置

image-20260522194748559

而unity会在绘制完所有不透明物体后将整个深度纹理状态复制到相机深度纹理这样着色器就可以访问这个纹理了,不过这个深度纹理仅对透明着色器有意义,对于不透明着色器读取深度纹理时这个纹理会显示为灰色,因为深度纹理不记录透明物体的深度信息

在URP中使用深度纹理

如果要在URP中使用深度纹理则需要启用这些文件里的Depth Texture(深度纹理)

image-20260522200227723

然后我们新建一个无光照的shadergraph

将surface type改为transparent 也就是透明,然后添加两个颜色节点——ForegroundColor(前景色)和BackgroundColor

image-20260523110126447

然后我们添加SceneDepth节点,这个默认会使用当前渲染像素的屏幕坐标来采样深度纹理,无需手动输入任何参数

image-20260523110325461

Unity Shader Graph 的 Scene Depth 节点提供三种 Sampling(采样模式),用于以不同方式解释深度缓冲区的值:

采样模式 说明
Linear 01 返回线性深度值,范围 0 到 1。0 对应相机的近裁剪面,1 对应远裁剪面。
Raw 返回深度缓冲区的原始非线性深度值,范围同样为 0 到 1,但分布不是线性的(通常在近处精度高,远处精度低)。
Eye 将深度值转换为** Eye(视图/相机)空间距离**,单位为,表示从相机到该像素对应场景点的实际距离。

在 Scene Depth 节点的上下文中,”线性关系”指的是 深度值与相机到物体的实际距离(Z 值)之间是否成正比

线性和非线性的区别

线性 = 深度值翻倍,实际距离也翻倍(成正比)
非线性 = 深度值翻倍,实际距离不是翻倍(不成正比)

模式 数学关系 直观理解
Raw 非线性(1/z 关系) 近处精度极高,远处精度极低。同样的深度值变化,在近处代表的实际距离变化很小,在远处代表的实际距离变化很大。
Linear 01 线性(归一化到 0-1) 深度值与实际距离成正比,但被压缩到 0(近裁剪面)到 1(远裁剪面)之间。0.5 正好对应远近裁剪面中间的距离。
Eye 线性(世界单位) 直接输出实际距离(米),0.5 就是 0.5 米,100 就是 100 米,没有任何归一化。

而做物体轮廓描边效果可以使用Linear01

image-20260523111230635

然后我们添加Lerp节点

Lerp是线性插值(Linear Interpolation)的简称,指的是在两个数值之间做平滑过渡

左侧的A 和B两个输入端口分别接收要混合的数值,T输入则控制混合权重,决定结果偏向A还是B,取值范围0-1

例如T=0.25时输出结果就是75%的A加上25%的B

image-20260523112323787

我们将前景色ForegroundColor设置为绿色,BackgroundColor后景色设置为白色

此时的材质显示的是白色

image-20260523125309976

然后我们让视角被这个白色方块遮挡这三个物体

image-20260523125448422

我们可以看到①和②号显示为前景色,而③号为白色,说明深度纹理有效

image-20260523131127007

调整透明度会影响绿色的明暗度,除了这个③号,这里有一个关键的决定性因素——surface type

image-20260523131821930

若设置为Opaque就表明这个是不透明材质,若设置为Transparent就是透明材质

image-20260523132305621

我们的③号就是用的Transparency的材质

不透明材质的物品全部显示为前景色,例如我们将背景色设置为黄色就可以更明显的看出

image-20260523133132264

但是我们若将背景物体扩大就很难分辨出来这几个方块的存在

image-20260523133613445

尤其是在游戏窗口完全是一片绿色

image-20260523133742353

此时我们可以调整主摄像机的Clipping Planes

image-20260523133823071

在 Unity 中,Clipping Planes(裁剪平面) 的两个值是:

名称 含义
Near 近裁剪面 相机能看到的最近距离。小于这个距离的物体不会被渲染
Far 远裁剪面 相机能看到的最远距离。大于这个距离的物体不会被渲染

如果我们将far改为25就可以看的更清楚了

image-20260523134142885

顶点着色器

网格是由一个个顶点构成的,这些顶点就是网格各个角落的点,点与点形成了面,每一帧画面Unity都要读取顶点数据,计算这些顶点在屏幕上应该显示的具体位置,然后这些顶点最终呈现在屏幕上(感觉和老电视的示波器差不多),这个过程是通过一系列坐标变换实现的

image-20260523140205230

首先是从抽象的网格数据开始,这时顶点位置是相对于网格自身的原点来定义的,这个坐标系称之为对象空间(Object Space)

根据物体的变化位置顶点被转换到世界空间,这时他们的位置是相对于场景的世界原点,然后根据摄像机的位置和参数设置,顶点会被转换到裁剪空间(clip space),此时的物体的坐标就是相对于屏幕的坐标(说人话就是跟皮影戏一样,要想看到皮影戏就必须确保屏幕上为皮影的图形分配位置)

image-20260523142321289

然而不需要了解这种东西,shadergraph会帮我们自动运算处理(虽然要是写shader的话需要手搓)

完成顶点着色器处理后网格数据会进行光栅化转化也就是把这些图形转换为一个一个的像素而片段着色器会对所有像素进行着色处理并最终呈现物体图像

image-20260523143013205

回到Unity创建无光照shadergraph

image-20260523143642395

之前我们都是直接用,这里讲讲这里的两个默认节点

对于Vertex(顶点阶段)

这是 Shader 的顶点着色器部分,负责处理模型的每个顶点:

输出端口 含义 默认空间
Position 顶点在 3D 空间中的坐标 Object Space(模型本地空间)
Normal 顶点法线方向,用于光照计算 Object Space
Tangent 顶点切线方向,用于法线贴图等 Object Space

Object Space(对象/模型空间):以模型自身原点为基准的坐标系。比如一个球体,球心就是 (0,0,0),不管这个球在世界中放在哪里


对于Fragment(片元/像素阶段)

这是 Shader 的片元着色器部分,负责计算最终屏幕上每个像素的颜色。默认只暴露了一个:

输入端口 含义
Base Color 材质的基础颜色/漫反射颜色

Shader Graph 自动帮你做了:

  1. 顶点位置变换:把 Object Space 的 Position 自动转换到 Clip Space(裁剪空间),这样模型才能正确显示在屏幕上
  2. 法线和切线:保留在 Object Space,供后续光照计算使用(如果你需要 World Space 的法线,要自己加 Transform 节点转换)

创建波浪效果

image-20260523214422911

我们需要两个浮点数节点–wavespeed(波速,控制每个顶点上下移动的距离)还有wavestrength(波强度,控制顶点移动的距离

我们需要使用顶点的世界位置作为偏移值,此时需要position位置节点来实现

image-20260523215234524

在 Unity Shader Graph 的 Position 节点中,Space(空间)下拉菜单有 5 种设置

选项 中文含义 说明
Object 对象/模型空间 以模型自身原点为基准,模型移动时坐标不变
World 世界空间 以场景世界原点为基准,模型移动时坐标变化
View 视图/观察空间 以摄像机为基准,Z轴指向摄像机正前方
Absolute World 绝对世界空间 类似 World,但不受 GPU Instancing 等变换影响(用于大规模地形等)
Tangential 切线空间 以顶点切线、副切线、法线构建的局部坐标系,常用于法线贴图

这里我们采用world就可以实现波浪网格在世界坐标系的无缝衔接

image-20260523215722984

现在添加计时功能,使用time时间节点并乘以波浪速度参数,我们把wavespeed放到multiply的B点,这里是用来设置波浪滚动方向的步骤

image-20260523220842337

在 Unity Shader Graph 中,Position 节点Split 节点分离后的四个输出是:

输出 含义
R X 坐标(红色通道)
G Y 坐标(绿色通道)
B Z 坐标(蓝色通道)
A W 分量(齐次坐标,通常为 1)

然后用split分离节点分离position坐标的各个分量,如果只把x坐标分量叠加到时间参数上,波浪就会产生水平移动效果,如果使用z坐标分量波浪会往另一个方向水平移动也就是垂直之前的方向

这里若要实现对角线移动效果.需要将x和z坐标分量通过add节点相加,然后加上刚才相乘的值

Unity Shader Graph 中的 Sine 节点 是一个基础的数学三角函数节点,用于生成正弦波形。以下是它的核心要点:

基本功能

  • 输入:接受一个浮点值(通常理解为弧度 radians
  • 输出:返回该弧度的正弦值,范围在 [-1, 1] 之间
  • 数学公式output = sin(input)

常见用途

场景 说明
周期性动画 让颜色、位置、UV 等随时间波动(如闪烁、呼吸效果)
波形生成 创建波浪形位移、水面波纹
遮罩/过渡 利用 [-1, 1] 的范围做软边缘或渐变控制
配合 Time 节点 Time → Sine 是最经典的自动动画组合
image-20260528172806668

然后把计算结果输入正弦函数节点得到y轴偏移量,也就是这样

image-20260528172655774

	<style>.uyekfclfcjmv{zoom: 33%;}</style>{% asset_img uyekfclfcjmv image-20260528173008616.png '"""image-20260528173008616"' %}

然后再用波浪强度wavestrength参数调节偏移量大小,然后将sine输出值和wavestrength相乘

然后新建一个Vector3节点,保持x和z分量为0,在y轴设置偏移值就得到了偏移向量

并添加一个position节点,选择object 对象空间坐标系,并用add节点加上我们刚才的y轴的偏移量,然后将结果连接到位置图形的输出端口

考虑到节点比较多移动起来比较困难,我们可以给节点分组,先用左键选中要分组的节点然后右键点击

image-20260528180829070

然后取个名字就实现分组了

image-20260528180927809

image-20260528174244354

然后我们像之前那样添加基础色color节点和纹理texture节点来控制纹理和颜色

image-20260528175134336

image-20260528175509952

这么看可能不是很明显,我将这个plane对象旋转90度就可以看的更清了

image-20260528175714069

实现更精细的波浪

我们旋转前三角形的边缘是与波浪运动方向保持一致,所以看起来不是太明显,不过仍然是有瑕疵的

为避免这种情况可以使用高精度网格模型,就是那种好几千个面的模型,这个相当于是将面切成大量的三角形使运动的时候的棱角感不是太明显,但是太吃配置了,如果场景里有大量这样的模型会消耗大量的GPU资源

还有一种方法是采用曲面细分技术,但是URP没有曲面细分,只有HDRP才有

因为这个是另一种渲染管,所以我们简单提一下吧

我们需要在这个面板找到HDRP特有的Tesseilation曲面细分功能

Tessellation(曲面细分)在 HDRP(High Definition Render Pipeline)中是一种几何层面的细节增强技术,它允许在 GPU 上动态细分三角形网格,从而在近距离观察时呈现更平滑、更精细的表面,而无需增加原始模型的多边形数量

image-20260528181451440

启用这个

image-20260528181604233

此时Vertex会新增加两个接口

Tessellation Factor(细分因子)

作用:控制细分程度——每个原始三角形被切成多少个小三角形

效果
1 不细分,保持原始网格
2~5 轻度细分,适合中距离
10+ 重度细分,极近距离特写
0 理论上不渲染(实际应避免)

Tessellation Displacement(细分置换)

作用:控制细分后顶点的位移量——基于高度图把顶点沿着法线方向”推出去”或”拉进来”

输入 说明
高度图(Height Map) 最常用,灰度图,白色凸起、黑色凹陷
位移强度 通常先对高度图做 Remap/Scale,再接入
无输入 只做平滑细分(Phong Tessellation),不位移

然后我们将最开始的这个位置节点设置从世界坐标(world)切换为绝对世界坐标(absolute world),这个是专门用来生成地形什么的用的

image-20260528182406531

然后我们删除掉这个add和position节点,直接用y轴偏移量连接Tessellation Displacement细分置换

image-20260528182615094

此时的Tessellation Factor 曲面因子值默认为1,也就是正常情况,当我们增大x的值就会通过增加顶点来细分网格

我们可以增加这个x的数值使网格的面数增加,细分系数最高可达硬件上限64