今天我们来学习场景相交检测
现实中的物体如果发生相交就会在两者的接触面会有阴影,例如你的手机放在桌面上可以看到手机和桌面的黑色阴影接触线,这个在unity中同样也是可以实现的
当着色器运行时它只能直接访问当前正在渲染的像素信息包括其在世界空间中的位置,我们想将该位置与在这个物体后面渲染的对象的位置进行比较,如果两点之间的距离小于我们指定的阈值,就检测到了一个交点
unity仅在之后将深度缓冲区的状态保存到深度纹理中,会先渲染所有不透明物体,再渲染所有透明物体,而着色器无法检测到任意两个透明物体之间的交叉
之前我们讲深度纹理的时候,提到过这个游戏场景是怎么渲染到摄像机的,也就是裁剪空间

unity随后通过根据相机的参数将一些裁剪空间坐标转换为屏幕空间坐标


而这个向量第四个分量等于相机与被渲染顶点之间的距离
然后就是其次坐标的概念(我也没看懂这个东西,这玩意就是大一的时候没学明白的线性代数的知识)
其次坐标
我们添加一个scene depth节点来获取相机和物体之间的距离,并设置为eye模式来获取精确值,
Scene Depth 从深度纹理中读取当前屏幕位置记录的深度值,表示:
沿着当前像素对应的视线方向,场景中已经写入深度缓冲区的表面距离摄像机有多远
| 参数 | 全称含义 | 数值范围 | 核心特点 |
|---|---|---|---|
| Eye | Eye-space depth | 非线性,近处密集、远处稀疏 | 相机空间的真实深度值,非线性分布 |
| Linear01 | Linear 0-1 depth | [0, 1] | 线性映射到 [0,1],0=近裁剪面,1=远裁剪面 |
| Raw | Raw depth buffer value | [0, 1] 或平台相关 | 深度纹理的原始存储值,通常是反向Z的非线性值 |
然后添加screen position节点并设置为Raw模式,
Screen Position 返回当前着色器像素在屏幕上的位置,以多种坐标系形式输出,返回当前像素的屏幕空间位置 (x,y,z,w)
对于 Raw 模式下的 Screen Position,w 可以用于获得当前像素相对于摄像机的深度信息(Eye Depth)。
用于:
- 采样屏幕纹理(如 Scene Color、Scene Depth)
- 制作基于屏幕坐标的特效(扫描线、故障艺术、径向模糊中心点)
- 实现与分辨率无关的 2D 效果
| 模式 | 坐标系 | 返回值 | 范围 | 核心特点 |
|---|---|---|---|---|
| Default | 屏幕左下角原点 | 归一化 UV (x, y, z, w) | x,y: [0, 1] | 最常用,采样屏幕纹理的标准 UV |
| Raw | 裁剪空间 | 齐次坐标 (x, y, z, w) | 未归一化,含透视除数 w | 手动做透视除法,极少用 |
| Center | 屏幕中心原点 | 归一化坐标 (x, y, z, w) | x,y: [-1, 1] | 径向特效、对称遮罩 |
| Tiled | 平铺模式 | 自动平铺的 UV (x, y, z, w) | 重复平铺,范围无限制 | 网格、重复图案 |
| Pixel | 屏幕像素坐标 | 实际像素位置 (x, y, z, w) | x,y: [0, 屏幕宽高] | 像素级精确控制 |
然后用split节点分出第四个分量w
还记得之前的这个图吗

graph LR Camera["📷 Camera"] Sphere["🔵 当前正在绘制的物体"] Floor["⬜ 地面(已经渲染到Depth Buffer)"] Camera -->|"Current Depth = 8"| Sphere Camera -->|"Scene Depth = 10"| Floor Sphere -->|"Depth Difference = 10 - 8 = 2"| Result["Intersection Depth"]
SceneDepth = 后面的表面距离摄像机多远
ScreenPosition.w = 当前表面距离摄像机多远
不过刚才的那几个节点我们要重复使用,我们可以使用子图来实现,类似于一个函数,只暴露输入口和输出口
子图

我们选中这些节点然后将这几个合并为子图,放在项目的任意位置
如图我们已经创建成功

添加控制器
双击就可以进入子图

由于这个子图没有输入,但是必须输出,所以我们可以创建一个浮点输出类型取名为out

然后我们将模式设置transparent透明,然后将子图连接到基础色上

将其运用到小球上,可以注意到交界处有黑色阴影

此时说明已经可以检测交叉处状态了,但是我们需要当球体远离交叉处时阴影逐渐降低
我们添加One minus节点(这个我们之前用过)来实现远离时值的降低,但是由于远离的时候的值会变成负数,所以我们需要这个Saturate节点来将负数值设置为0,超过1的值设为1
然后我们需要控制这个渐变值 首先添加一个intersection power的float类型用来控制,然后将其变成默认值为1 范围0.01~25的slider滑块,0会完全遮挡,但是25的遮挡效果非常微弱
为了更方便的调整遮挡效果的明暗度,我们添加一个Occlusion strength值来控制明暗度,这样要控制明暗度就可以直接控制这个occlusion就可以了
然后用lerp 节点线性插值来控制颜色和强度,T槽导入颜色的强度,A槽为设置的基础色的颜色,B槽添加一个float节点设置为0代表黑色,这样就可以让颜色混合更自然

这里温习一下这几个节点
这里是**普通材质(左)和我们场景相交材质(右)**的区别,可以看到右侧的融合的更自然,而且很有原神这些二游那味

比如我们的小球现在只漏出来一点点

然后露出来很多

这样就很直观了



评论区