自由学习记录(96)
毛茸茸,摇动的草地,反射探针,体积雾,手机端材质
NDC(归一化设备坐标)的差异
差异出现在 投影变换之后:
API / 平台 | NDC X | NDC Y | NDC Z 范围 |
---|---|---|---|
OpenGL | -1→1 | -1→1 | -1→1 |
DirectX / Vulkan | -1→1 | -1→1 | 0→1 |
-
OpenGL:近平面投影后 Z = -1,远平面 Z = +1。
-
DirectX/Vulkan/Metal:近平面投影后 Z = 0,远平面 Z = 1。
3. Unity 的处理
Unity 会屏蔽这些差异:
-
在 Shader 宏里通过
UNITY_UV_STARTS_AT_TOP
、平台宏和_ProjectionParams
做适配。 -
所以写 Unity Shader 时你只需要记住:
-
相机空间 forward = –Z。
-
投影后 Z 的范围 Unity 会替你处理(除非你在做跨平台深度贴图手动解码)。
-
Unity 的坐标系:一路左手制
-
世界空间:
Unity 使用左手坐标系,X 轴指右,Y 轴指上,Z 轴指前(远离摄像机方向)。Unity Documentationtecharthub.com -
相机空间(View Space):
本质也是左手坐标系。只是相机被设定在原点,并且朝向是 –Z(摄像机前方),但 X 和 Y 方向的定义保持不变。Game Development Stack ExchangeUnity Discussions
总结:两个空间的坐标系是一致的,差异只在于转换方式和空间的定义点不同。
https://stackoverflow.com/questions/4124041/is-opengl-coordinate-system-left-handed-or-right-handed
The F0 range for most common dielectrics will be from 0.02-0.05 (linear values). For conductors, the F0 range will be 0.5-1.0. The reflectivity of a surface is therefore determined by the refractive index as shown in the equation below (Lagarde 2011).F0范围对于大多数常见电介质将从0.02-0.05(线性值)。对于导体,F0范围将是0.5-1.0。因此,表面的反射率由下述方程中的折射率决定
Refracted light is absorbed, and the color tint of metals comes from the reflected light, so in our maps, we don’t give metals a diffuse color.
-
在 金属贴图 (metallic map) 里:
-
黑色 = 非金属
-
白色 = 金属
-
-
在 漫反射贴图 (diffuse map) 里:
-
金属区域 = 黑色(因为金属几乎没有漫反射)
-
非金属区域 = 有颜色(木头、塑料的颜色)。
-
-
金属一旦生锈,表面就变成了“非金属物质”(比如氧化铁)。
-
所以在贴图里,锈掉的部分应该当 非金属 (dielectric) 来处理,F0 = 0.04。
-
渲染时就会显示那种暗红色的锈迹。
https://www.adobe.com/learn/substance-3d-designer/web/the-pbr-guide-part-1?learnIn=1&locale=en
我正在尝试理解OpenGL坐标系统。然而,一些教程说默认坐标系统是左手系(参见http://www.c-sharpcorner.com/UploadFile/jeradus/OpenGLBasics11172005014307AM/OpenGLBasics.aspx)而另一些教程说它是右手系(参见http://www.falloutsoftware.com/tutorials/gl/gl0.htm)。哪个是正确的?我明白我们可以通过镜像将一个转换为另一个,但我想知道默认坐标。
-
OpenGL 并没有硬性规定你一定要用左手系还是右手系来组织你的物体。
-
你在 CPU 端定义的顶点数据,可以是任何坐标系。最终都会被变换成裁剪空间。
为什么 OpenGL 不硬性规定世界/对象坐标系?
因为 OpenGL 是一个“状态机 + 矩阵流水线”,它的职责是:
👉 把你提供的顶点,经过一系列矩阵变换,最后映射到 裁剪空间 / NDC。
-
你在 CPU 端组织顶点的方式(左手/右手)是完全自由的。
-
只要你提供相应的 Model / View / Projection 矩阵,就能把数据变换到 OpenGL 规定的 裁剪空间 (Clip Space)。
-
所以 OpenGL 不需要知道“你原来是左手还是右手”,它只要求最后进入裁剪阶段的数据符合它的规则。
什么是由我来提供,具体举几个实际中的情况
好问题 👍,我前面说“由你来提供”,意思是:OpenGL 本身不自动生成这些变换矩阵,而是由开发者在程序里传入(作为 Uniform 或常量缓冲区),再由顶点着色器使用。
我给你几个具体实际中的情况,帮你感受“你需要提供”的意思:
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(3.0f, 0.0f, -5.0f)); // 平移到场景某处
model = glm::rotate(model, glm::radians(45.0f), glm::vec3(0.0, 1.0, 0.0)); // 绕 Y 旋转
model = glm::scale(model, glm::vec3(2.0f)); // 放大 2 倍
这就是你 提供给着色器 的 model 矩阵。
OpenGL 不知道相机在哪,所以你要自己算这个矩阵。
这里就是通过世界坐标下的相机坐标,通过投影到相机空间的三个基向量方向,确定每个点在位置上要往哪个方向移动多少的值,
先旋转再平移,
-
OpenGL 的本质:它只是一个 图形状态机 + 渲染流水线,负责把你提交的顶点、纹理、状态数据,经过矩阵运算、光栅化,最后变成像素。
-
它内部没有“场景”或“相机”的概念,更不会记住“世界里有个摄像机在 (0,0,3),看向 (0,0,0)”这种信息。
什么叫“状态机”?
状态机 (State Machine) = 系统内部保存一组“状态”,之后所有操作都会受到这些状态的影响。
-
你设置一次状态,它就会一直保持,直到你显式修改。
-
你再发的绘制命令,都会在这个“当前状态”下执行。
OpenGL 就是一个 图形状态机:
-
它内部维护很多“状态”,比如:
-
当前使用的着色器程序 (
glUseProgram
) -
当前绑定的纹理 (
glBindTexture
) -
当前的顶点数组对象 VAO (
glBindVertexArray
) -
深度测试开关 (
glEnable(GL_DEPTH_TEST)
) -
混合模式、剔除模式、缓冲区绑定……
-
一旦你设置了这些,OpenGL 就会记住。
然后当你调用:glDrawArrays(GL_TRIANGLES, 0, 36);
它就会在 当前所有状态的组合下,去执行渲染。
-
状态机 = OpenGL 内部保存的开关/配置/绑定资源。
-
渲染流水线 = 当你发出绘制命令时,GPU 按照固定流程(顶点处理 → 裁剪 → 光栅化 → 片段处理 → 混合)处理数据。
👉 所以说 OpenGL 是“图形状态机 + 渲染流水线”:
-
状态机部分决定了“怎么处理”(开了哪些功能,绑定了哪些资源)。
-
流水线部分决定了“按什么顺序处理数据”。
渲染状态机指的是渲染过程中那些“一旦设置就持续生效”的状态选项,比如:
-
开启/关闭某些功能:深度测试、混合、剔除、MSAA、动态分辨率等。
-
资源绑定:当前使用的着色器、材质、纹理、渲染目标、后处理效果等。
-
渲染阶段控制:哪些 Pass 会执行、顺序如何、何时做阴影或透明物体渲染。
-
灯光/阴影配置、剔除规则、光照类型、多摄像头设置等。
https://stackoverflow.com/questions/33407209/why-does-the-camera-face-the-negative-end-of-the-z-axis-by-default
1. 在 Object / World / Eye(视图)空间上,OpenGL 默认是右手坐标系。
如 Stack Overflow 所说:
"OpenGL is right handed in object space and world space."
Stack Overflow
2. 而到了裁剪空间(Clip Space)与 NDC(Normalized Device Coordinates),坐标系会被**“翻转”**,也就是变成 左手坐标系:
-
这是因为 OpenGL 的标准投影矩阵(如 glOrtho/glFrustum)对 Z 轴做了 -1 倍缩放,使得 +Z 轴变向屏幕内,变成左手感知方向 Stack Overflow。
-
结果是:虽然前面阶段是右手系,但输出到屏幕时已经变成左手系了。
3. Redit 上也有开发者解释:
“当抛弃固定管线后,进入裁剪空间(Clip Space)的坐标系是左手的... +Z 方向指向屏幕内 → 是左手系统。”
4. 现代观点也认同这一点:
文章标题直接说明**“OpenGL is NOT right-handed”**。
它指出:现代 OpenGL 只认识 NDC 坐标,而它本身是左手系。
GingerBill
对比表格 (OpenGL vs DirectX)
渲染阶段 / 坐标空间 | OpenGL 默认 | DirectX 通常方式 |
---|---|---|
Object / World / View 空间 | 右手坐标系 | 也可以是右手或左手,自由选择 |
Clip Space / NDC | 默认左手:+Z 朝屏幕内 | 常见为右手:+Z 朝屏幕外 |
🟡 方法 2:直接画一个坐标轴 Gizmo
写一个简单的 Mesh(或者在 Unity 里用 Gizmos.DrawLine
):
-
画一条红色线条: (0,0,0) → (1,0,0) (+X)
-
画一条绿色线条: (0,0,0) → (0,1,0) (+Y)
-
画一条蓝色线条: (0,0,0) → (0,0,1) (+Z)
然后在场景里观察:
-
如果蓝轴朝向屏幕外(远离你),那是右手系。
-
如果蓝轴朝向屏幕内(指向你),那是左手系。
Unity 的 Scene 视图里默认就有这种小坐标轴,你可以验证一下。
🔵 方法 3:NDC 检查
你可以直接把一个点 (0,0,1)
送进 gl_Position
:
gl_Position = vec4(0,0,1,1);
-
如果这个点通过投影后在屏幕 前面可见,说明 +Z 朝向相机(左手)。
-
如果不可见或被裁掉,说明 +Z 是远离相机的(右手)。
https://www.reddit.com/r/GraphicsProgramming/comments/1amkqk6/left_vs_right_handed_systems_which_part_of_the/?utm_source=chatgpt.com
Unity 的 Shader(无论是 ShaderLab
、HLSL 还是 Shader Graph)最终都会被编译成对应平台的着色器代码,比如 OpenGL 的 GLSL 或 DirectX 的 HLSL。
这意味着你在 Unity 里用的 Shader,底层实际上是运行在那些 API 上的渲染流水线中。
软件 vs 硬件的渲染“管线”层级区别
-
硬件层:如 GPU 和图形 API(OpenGL、Direct3D),定义顶点着色器、光栅化、片段着色器等低级阶段。The Khronos GroupWikipedia
-
引擎层(软件层):如 Unity,引擎定义的是更高层次的“渲染管线”:如何处理材质、光照、摄像机等。它们最终会调用底层管线执行。Game Development Stack Exchange
Unity 特有的“可编程渲染管线”(Scriptable Render Pipeline,SRP)系统就是一种更面向内容、可定制的高层封装。Unity Documentation张卫的博客
https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@7.1/manual/universalrp-builtin-feature-comparison.html
平台兼容性第一,这样的出发角度的确一般不会去用hdrp了
而且builtin没有想象的那么老要被舍弃,依然有优秀的兼容性的
_ProjectionParams
是 Unity 内置的一个 Shader uniform,由引擎在渲染时自动传入,主要提供相机投影相关的 4 个参数。它的定义(在 UnityCG.cginc)
float4 _ProjectionParams;
四个分量含义如下:
-
x = 1.0 或 -1.0
-
区分深度缓冲的 y 方向是否翻转。
-
+1 表示正常的透视投影,-1 通常表示渲染到纹理时做了垂直翻转。
-
用来处理平台差异(如 D3D vs OpenGL)和摄像机渲染到 RT 时的坐标反转问题。
-
-
y = near plane (相机近裁剪面距离)
-
z = far plane (相机远裁剪面距离)
-
w = 1.0 + 1.0 / far plane
-
用在深度相关的计算里(比如线性化深度)。
-
便于从深度缓冲值恢复到视空间深度。
-
https://gamedev.stackexchange.com/questions/183048/differences-between-unity-ue-shaders-and-opengl-shaders?newreg=681ea5a01949431f8d8f3050dfb1690f
_ProjectionParams.x
所指的“渲染到纹理时”的“那个纹理”是指 Unity 渲染过程中使用的 Render Texture(渲染纹理)。它在以下情景中至关重要:
-
Unity 会将场景渲染到一个 Render Texture,尤其是在做后处理特效(Image Effects)、深度纹理生成或自定义渲染流程时,这种纹理是 camera 渲染的“中间结果” Unity Documentationdocs.unity.cn。
-
不同平台(如 Direct3D vs OpenGL)在渲染到 Render Texture 时,Y 方向可能被 Unity “内部翻转”,以保持主屏幕渲染的一致性 Unity Documentation+1。
你看到的 UnityObjectToClipPos()
、LinearEyeDepth()
不是 GLSL/HLSL 本身的内建函数,而是 Unity 的预处理宏/辅助函数。
-
它们写在 Unity 自带的 CGInclude 文件里(比如
UnityCG.cginc
、HLSLSupport.cginc
)。 -
当你写 Shader 的时候,Unity 的 shader compiler 会根据目标平台(OpenGL、DirectX、Metal、Vulkan…)把这些宏展开成对应的底层代码。
innerSpotAngle
是 Unity 中专用于 Spot Light(聚光灯) 的一个属性,表示灯光射锥中 完全发光区域的角度。它属于 Scriptable Render Pipeline(如 URP/HDRP)时有效,不适用于 Built-in 渲染管线。Unity DocumentationUnity Learn
用途说明
-
innerSpotAngle
定义了从聚光灯中心向外,灯光保持最大亮度的范围。 -
超出这个角度但仍在
outerSpotAngle
内部时,光强会 逐渐衰减,形成柔和过渡的边缘效果(penumbra)。Unity LearnPerso ESIEE
举例:若设置 inner = 30°
,outer = 60°
:
-
≤ 30° 范围内:亮度最大。
-
30° ~ 60°:光强线性衰减。
-
60°:无光照。
Culling Per Tile | No | No | Yes |
Culling Per Layer | Yes Based on GameObject layers | Yes Based on dedicated Light layers | Yes Based on dedicated Light layers |
Multiple directional lights | Yes | Yes Supports shadowing for one directional light at a time. | Yes Supports shadowing for one directional light at a time. To simulate more, use Spot light with a Box shape. |
Number of real-time lights per object | Unlimited | In Deferred: unlimited. In Forward: 4 for GLES2, 8 for all other graphics APIs. | Unlimited |
Built-in 管线设计年代久远,Spot 光只提供单一角度(outer);没有分离的内圈/外圈控制逻辑。功能受限但兼容性强。
Culling Per Object / Per Tile / Per Layer
Culling(剔除) = 决定某个物体要不要被某盏灯光影响。
-
Per Object
每个物体单独决定是否被某个灯光影响。
👉 常见于传统管线(Built-in),比较直接,但性能一般。 -
Per Tile
屏幕被划分成小方块(tile,例如 16x16 像素),再决定这些 tile 中的像素是否要算某个灯光。
👉 这是 tiled/clustered lighting,URP/HDRP 里都有,适合大量灯光场景。 -
Per Layer
通过 Unity 的 Light Layers(光照层),只让特定层的物体被特定灯光影响。
👉 例如:UI 层的物体不被场景灯光照亮。
☀️ 2. Multiple directional lights
是否支持场景中同时存在多个方向光(sun-like light)。
-
Built-in 支持多个,但性能差。
-
URP/HDRP 默认只允许 1 个方向光投射阴影,其他的只算光照不算阴影。
💡 3. Number of real-time lights per object
一件物体最多能被多少个 实时灯光(非烘焙)影响。
-
Built-in:无限制(但性能可能爆炸)。
-
URP:Forward 模式下有上限(如 GLES2 上限 4,其它 API 上限 8),Deferred 模式几乎无限。
-
HDRP:无限制。
📷 4. Number of real-time lights per Camera
每个相机一次渲染时,能同时考虑多少盏实时灯光。
-
Built-in:理论上无限(但拖慢性能)。
-
URP:有限制,比如移动端最多 32,其它平台 256。
-
HDRP:基于 tile/cluster,Deferred 模式下可以做到每 tile 63 盏灯,Forward 模式下每 cluster 63。
URP Forward+ (Tiled vs Clustered) - #5 by Kabinet13 - Unity Engine - Unity Discussions
URP 为什么说 “Single pass only”
这指的是 URP 渲染架构与 Built-in 模式的本质差异:
-
Built-in 渲染管线使用 Multiple Passes。比如 Forward 渲染时,主光照用一个 Pass,其他光源累加再用多个 Pass(ForwardBase + ForwardAdd)RedditUnity Forums。
-
URP(Universal Render Pipeline)设计为 Single-pass 渲染:它不允许在一个 Shader 中定义多个 Pass,取而代之的是用一个线性流程(multiple stages)优化渲染,兼容更广平台,性能更高RedditUnity Forums。
那么,我可以写多个 Pass 吗?
-
在纯 ShaderLab 里写多个 Pass 是可行的,但 URP 渲染流程不能执行它们;Unity 会报错或忽略它们Reddit。
-
如果你想模拟多个渲染阶段效果,可以用 URP 的 Render Features / Scriptable Render Passes 插件功能,把不同渲染逻辑拆成多个阶段(stage),它们在 URP 管线中按顺序执行,效果类似但仍属于一个总流(render graph)Unity LearnUnity Documentation。
什么是 Light Cookies?
-
在剧院和电影灯光中,
cookie
(或称为 cucoloris、gobo)指在灯光前放置的图案板,让光线投射出特定的图案形状,比如窗户、树叶的阴影等 Wikipedia。 -
在 Unity 中,Light Cookie 是一个 贴在 Light 上的遮罩贴图,可以改变光照的形状和强度,用于模拟花纹光斑、窗格阴影、海面反射等效果,并且实现效率高、运行时开销低 Unity DocumentationWayline。
在 Unity 各渲染管线中的支持情况
渲染管线 | 是否支持 Light Cookies | 特性说明 |
---|---|---|
Built-in Render Pipeline | 支持,仅使用 Alpha 通道形状遮罩 | 仅影响光影形状,不支持颜色遮罩 Unity Documentation |
URP (Universal Render Pipeline) | 不支持(正在研究但未实现)---新版支持了 | 当前版本里无法使用 Light Cookies Unity Documentation |
HDRP (High Definition Render Pipeline) | 支持彩色 cookies(RGB + Alpha) | 支持颜色和形状遮罩,效果更丰富 Unity Documentation |
怎么在 Built-in 中使用 Light Cookie
-
制作贴图:用灰度图(或带透明图)代表遮罩,常为 Alpha 通道或 “从灰度产生 Alpha”。
-
设置 Texture:在 Unity Inspector 中,将图片的 Texture Type 设为 “Cookie”,并启用 Alpha From Grayscale(如果使用灰度图) Unity Documentation+1。
-
应用到灯光:拖动这个贴图到灯光组件的 Cookie 字段即可。
-
调整效果:聚光灯(Spot Light)会在光束范围内投影一次,定向光(Directional Light)可选择重复铺设效果,并可调整 Cookie Size 控制大小 Unity DocumentationWayline。
https://www.reddit.com/r/Unity3D/comments/13qsafw/does_urpsrp_officially_support_multipass_shaders/?utm_source=chatgpt.com
URP 是怎么处理多光照的?
1. Single-pass + 循环处理
-
URP 支持 一个 Pass 内处理多光源。Shader 会在这个 Pass 中使用 光源循环(light loop) 的方式,依次累积多个光源的贡献 Unity Documentation。
-
这个逻辑适用于传统 Forward 渲染路径,也支持 Forward+ 渲染路径 Unity Documentation。
2. 每个物体最多处理有限数量光源
-
URP 在每个物体上默认只处理 1 个主光(Main Light) + 最多 8 个附加光源(Additional Lights),且这些都是每像素计算(可选改为 per-vertex)Unity DocumentationUnity Discussions。
-
因此,它不会为每个光使用一个 Pass,而是在同一个 Pass 中用循环处理多个光源计算。
3. 为何不是多 Pass?
-
多 Pass 渲染(如 Built-in 中常见的 ForwardBase + ForwardAdd)会为每个光源额外生成一个渲染调用,效率较低。
-
URP 的单 Pass 循环结构提高性能,并减少渲染开销,实现更高效率 Unity Documentation+1。
小结对比表
渲染管线类型 | 多光照处理方式 |
---|---|
Built-in | 使用多个 Pass(如 ForwardBase + 多个 ForwardAdd) |
URP | 在一个 Pass 内用循环累积多个光源贡献 |
用一句用户社区语来解释:
URP “就是一次过处理所有光照,不按每个光拆 Pass” 的设计 Unity DocumentationUnity Discussions。
IES Lights(即 IES 灯光)源自真实灯具厂商提供的 IES 光度曲线(IES Profiles),用于模拟真实世界光源的发光分布。它们在 Unity(尤其 HDRP)中作为一种更加物理精准的渲染工具可直接使用。以下是核心信息:
Light Anchor 是什么?
**Light Anchor(灯光锚点)**是 HDRP 中专门提供的组件,用来让灯光绕目标点(锚点)相对主摄像机做动态定位或旋转,非常适合创建电影镜头般的灯光布置。它能让多个光源环绕主体位置,以实现更为灵活的打光效果。
Unity DocumentationUnity
使用方法:
-
选中场景中的一个 Light GameObject。
-
在 Inspector 点击 Add Component → Rendering → Light Anchor 添加组件。
-
组件会默认以当前挂载对象的位置作为 Anchor(锚点)。
-
设置属性即可控制灯光相对于 Anchor 和主摄像机的行为,包括:
-
Orbit(环绕):左右旋转角度
-
Elevation(仰角):上下角度
-
Roll:控制 Cookie 或 IES 灯光图样的旋转角
-
Distance:灯光距离锚点的距离
-
Up Direction:设置 Y 轴参考是相机视角还是世界方向
-
Anchor Position Override:让锚点跟随场景中某个 GameObject
-
Common presets:快速应用常见的影视打光角度预设
Unity DocumentationUnity User Manual
Light Anchor 是 HDRP 内部实现的一部分,目前并没有单独公开的源码可以直接查看或修改。它由 HDRP 包内部的渲染工具所控制,属于 Unity 的闭源管线功能模块。如果你深入调试或改写其行为,可能需要自己实现类似 Shader + 脚本驱动逻辑。
https://docs.unity3d.com/6000.2/Documentation/Manual/shadow-cascades.html
当方向性光源产生的实时阴影靠近相机时,阴影会呈现出像素化的效果。 a problem called perspective aliasing,
在 Scene 视图中可切换“Shadow Cascades”Draw Mode 检查每级阴影范围与切分是否合理Unity User Manual。
Unity 中 how to use?
Built-in 渲染管线
-
前往
Project Settings → Quality
,为每个 Quality Level 设置当中支持 0 / 2 / 4 级联阴影。 -
Unity 会自动配置阴影贴图分段的范围和比例Unity DocumentationUnity User Manual。
URP(Universal Render Pipeline)
-
在
Universal Render Pipeline Asset
中配置阴影级联数量与分段比例,比 Buit-in 更集中与统一Unity Documentation。
HDRP(High Definition Render Pipeline)
-
在 HDRP 的 Volume Settings 中(如 Shadow Settings),让方向光支持级联阴影。HDRP 默认支持多级联,而 Shadow Map Atlas 处理更高级,且支持动态缩放、影图复用等优化Unity Documentation。
-
Stable Fit 投影:Unity 会尽可能让阴影贴图在摄像机旋转过程中保持“静止”,避免可见的阴影抖动或“游走”现象(shadow swimming)。
“Stable Fit renders lower resolution shadows but they don’t wobble with camera movements.” Unity DocumentationStack Overflow
-
Close Fit 投影:它会让阴影贴图“紧贴”摄像机视野,从而利用更高分辨率渲染更清晰的阴影,但代价是当摄像机移动或旋转时,阴影贴图可能会“jitter”或略微抖动。
“Close Fit renders higher resolution shadows but they can sometimes wobble slightly if the camera moves.” Unity DocumentationUnity DiscussionsStack Overflow
-
如果你在场景中看到阴影边缘在相机移动时抖动或跳动,表明你的 Shadow Projection 设置不合适,这时可以尝试切换到 Stable Fit,改善视觉稳定性。
“Try Changing Shadow projection → Close Fit That works for me !!”
“But yes that pixel light solution did not work for me... after changing [...] from Stable Fit to Close Fit, I got a better result.” RedditStack Overflow -
如果你对阴影的清晰度要求更高,并愿意接受轻微抖动,可以选择 Close Fit,优先考虑画面质量。
Unity 中如何设置
这些选项可以在 Project Settings → Quality Settings → Shadows → Shadow Projection 中找到(仅存在于 Built-in 渲染管线中):
-
选择 Stable Fit 或 Close Fit,然后根据需要调节 Shadow Distance、Cascade 数量等其他设置。 Unity Documentation
Screen-space shadows 是一种在 屏幕空间(Screen Space) 中计算阴影的技术,它通过 深度贴图和屏幕空间光线追踪/体素化 等方式,生成阴影信息,而不是依赖传统的 Shadow Maps:
-
Unity 官方文档中称它为 Screen Space Shadows Renderer Feature,适用于 URP 的 Forward 渲染。它使用额外的 Render Texture 来在屏幕空间计算主光源对不透明物体的阴影,并在渲染前进行绘制,节省多次读取 Shadow Cascade 贴图的资源开销。Unity Documentation
-
在 URP 16.0+ 的新版文档里进一步说明:它用一个 单一的屏幕空间阴影贴图 来替代多个 Shadow Cascade 贴图,在 Forward 渲染中能提升性能,但会消耗更多内存,还需要 Depth Pre-Pass 支持。Unity Documentation
-
社区讨论中也有人提到:这种 Screen-space 阴影可以为那些传统 Shadow Mapping(阴影贴图)没有涵盖到的区域补充阴影细节,但可能带来性能负担。Reddit
“Shadow Screen Space Pass” 在渲染流程中是哪里?
在实际的渲染流程里,它体现为一个新的 Render Pass,专门负责:
-
在 DrawOpaqueObjects pass 之前,进行深度预通道(Depth Pre-pass),构建场景的深度贴图。
-
根据屏幕空间中不透明物体的深度信息,计算阴影遮蔽。
-
输出一个屏幕空间阴影贴图。
-
在后续实际渲染主光照时,使用该贴图来决定像素是否受阴影影响。
这个过程就是我们说的 "Shadow Screen Space Pass"。
将其与 Shadow Lighting Pass 区别对比
功能 | Shadow Lighting Pass | Shadow Screen Space Pass |
---|---|---|
渲染阶段 | 从光源视角绘制 Shadow Map | 在屏幕空间中基于深度贴图计算阴影信息 |
依赖机制 | 依赖多个深度贴图(Shadow Cascades) | 依赖额外的屏幕空间贴图(Depth + Shadow Texture) |
性能特点 | Shadow Map 切换和多次数读性能开销 | 减少 Cascade 读取次数,但增加内存开销 |
应用范围 | 用于所有可产生阴影的物体(包括透明) | 通常只用于不透明物体,透明仍使用 Shadow Map |
如何在 Unity URP 中使用?
-
打开 Unity 编辑器,进入对应的 URP Renderer Asset 设置。
-
点击“A d d Renderer Feature”,添加 Screen Space Shadows。
-
这样就会自动启用前 Depth Pre-pass,并在 DrawOpaqueObjects 阶段前计算并绘制屏幕空间阴影。
-
可以在 Frame Debugger 中查看 ScreenSpaceShadows Pass 与 MainLightShadow Pass 的区别,观察场景中哪些区域使用了哪一种方式。Unity Documentation
什么是 Shadow Bias?
Shadow Bias 是在进行比较判断(是否在阴影中)时,在深度上加入一个微小偏移值,以 避免由于数值精度和插值误差引起的自阴影问题。当一个像素距离几乎和 shadow map 中记录的深度相等时,渲染可能误判为被遮挡,从而出现 shadow acne。这时,通过添加 bias 可以确保这些“边界像素”正确被判为“可见”,而不出现伪影。 Unity DocumentationScratchapixel
Unity 中的 Bias 设置项
在 Unity 中,每个光源组件中有两个用来控制阴影偏移的参数:
-
Bias(深度偏差)(Depth Bias):在 shadow map 生成过程中,让渲染的几何体向光源方向微调(偏移),避免随着采样出现 self-shadow 问题。
-
Normal Bias(法线偏差):沿法线对几何体进行内缩,减少斜面或边缘的 shadow acne,但可能导致阴影靠内且变窄。
-
Unity URP 也支持这两个参数,可从 Pipeline Settings 或 Light 面板中自定义设置。Unity Documentation
Unity 中,每个光源组件中有两个用来控制阴影偏移的参数:
-
Bias(深度偏差)(Depth Bias):在 shadow map 生成过程中,让渲染的几何体向光源方向微调(偏移),避免随着采样出现 self-shadow 问题。
-
Normal Bias(法线偏差):沿法线对几何体进行内缩,减少斜面或边缘的 shadow acne,但可能导致阴影靠内且变窄。
-
Unity URP 也支持这两个参数,可从 Pipeline Settings 或 Light 面板中自定义设置。Unity Documentation
使用时的平衡取舍
调整类型 | 效果 | 可能副作用 |
---|---|---|
增加 Bias(深度偏差) | 尽量减少 Shade Acne | 可能导致阴影与物体分离 (“peter-panning”) |
增加 Normal Bias(法线偏差) | 减少斜面的自阴影伪影 | 阴影轮廓变窄,可能出现 light bleeding |
理想情况下,两者应尽量小,但又要足够防止伪影。这需要对不同场景进行视觉比对与调整。通常 Unity 的默认值适用于大多数场景。Unity DocumentationScratchapixelCatlike CodingMr F
社区调优经验
从 Unreal Engine 的官方文档也可看到类似设置:
“An overview of the Lighting example ... Shadow Bias … help objects feel more grounded along a surface. Decreasing value can lead to self-shadowing artifacts.”
Epic Games Developers
社区建议也提到:
“Try adjusting bias values and cascade settings to fix shadow artifacts.”
Epic Developer Community Forums
PCF(Percentage Closer Filtering)是什么?
PCF 是一种用于缓解 shadow map 划痕(锯齿阴影)问题的技术:
-
原理:对每个像素,不只取一个深度比较结果,而是在 shadow map 周围采样多个位置,执行多次深度比较,然后将结果平均,这样能得到一个带有柔和边缘的阴影效果。
-
术语由来:因为它计算的“阴影程度”是多次“是否靠近光源(closer)”判断结果的平均百分比。这就是为什么叫“Percentage Closer Filtering”。
NVIDIA DeveloperWikipedia
一个通俗的例子就是,将硬阴影比作黑白边界,而 PCF 就把边界抹成灰色,形成软化过渡。
PCF 在现实代码里怎么做?
来自 OpenGL 教程的一个简单实现逻辑:
float CalcShadowFactor(vec4 LightSpacePos) {// 计算投影 UV 和深度值...for (offset in 3×3 kernel) {Factor += texture(shadowMap, offset); // 深度比较结果为 0 或 1}return Factor / 9.0; // 得到阴影占比
}
这里 texture() 是 shadow map 的 sampler2DShadow,返回的是比较结果(非传统纹理值)
ogldev.org
PCF 不只是在计算“模糊”,而是滤 Boolean 值
正如某位开发者在专业社区里解释的那样:
“PCF 处理的是布尔比较结果,而不是先滤深度再比较。”
GPU 硬件通常支持 2×2 的 PCF 核合并,并使用双线性插值来再滤一次。
Unity 中的 PCF 实现方式(HDRP 举例)
在 Unity HDRP 中,就提供了多个阴影 滤波质量级别:
-
Low(低):点光/聚光灯使用 PCF 3×3(4 次采样);方向光使用 PCF Tent 5×5(9 次采样)
-
Medium(中):使用 PCF 5×5(9 次采样)
-
High(高):使用更高级的 PCSS(Percentage Closer Soft Shadows)
Unity Documentation+1
PCSS 是建立在 PCF 之上的软阴影版本,具有动态模糊尺寸的特点,依赖光源与阻挡物距离。
Unity DocumentationNVIDIA Developer Downloads
为什么 PCF 必不可少?
-
抗锯齿能力出众:能显著缓解硬阴影边缘锯齿问题
-
易于实现,性能可控:你可以根据设备状况选择 kernel 大小
-
Unity Shader Graph、URP 等都能支持 PCF 设置
Unity Discussions
项目 | 内容说明 |
---|---|
核心原理 | 多次采样 shadow map,平均比较结果得软阴影 |
算法重点 | 滤的是布尔比较结果,不是深度值本身 |
Unity 实现 | HDRP 提供 Low/Medium/High,不同质量对应不同采样核 |
高级版本 | PCSS(软阴影)基于 PCF 增加动态模糊范围控制 |
优劣比较 | 优点:抑制锯齿;缺点:采样次数越多,性能消耗越高 |
Unity Built-in 渲染管线和**URP(通用管线)**对光线追踪效果(如 Ambient Occlusion、Reflections 等)基本都标注为“不支持”──这是事实。Unity 将这些功能主要集中在 **HDRP(高清渲染管线)**中,并且依赖 DXR(DirectX Raytracing)的前提条件才能开启。Unity DiscussionsUnity DocumentationUnity
那我自己写的 Shader 说是光线追踪,但没用 HDRP,这算什么?
这其实是一种让人容易混淆的说法。你遇到的是一种类似“光线追踪效果 simulation”的自定义 Shader 或算法,例如:
-
利用 ray marching 在片元 Shader 中模拟追踪光线;
-
或在 Compute Shader 里自己实现射线投射逻辑,比如处理反射、阴影等。
从技术上讲,这属于“自己的射线追踪逻辑”,并不是利用硬件 DXR 和 Unity HDRP 提供的“真正”的 Ray Tracing 功能。这种方式 虽然可以做出类似效果,但不被官方标为“Ray-traced Reflections”等功能,因为这些标记特指 HDRP + DXR 的硬件加速实现。Unity Discussions
Book of the Dead: Quixel, wind, scene building, and content optimization tricks
赛道与挑战项目
1) Shading / 材质表现
项目:风雨湿润一体化材质(地面+金属+布料三材质)
-
要点:屏幕空间雨滴扰动、法线/粗糙度随雨强度实时变化、积水区间接反射融合。
-
验收:RTX 4060 笔电 1080p 60 FPS;额外 Draw 不超过 +2;显存 < 300 MB;雨强从 0→1 过渡无跳变与拉花。
2) Render Feature / RenderGraph
项目:URP 延迟贴花(Decal)与法线混合
-
要点:在 URP 自写贴花 Pass,支持 BaseColor/Normal/Roughness 投射,法线正确切换切线空间。
-
验收:100 个贴花不掉帧;与阴影、后处理兼容;边界无锯齿/无双影;材质球界面可视化参数面板。
3) GPU 仿真 / Compute
项目:10 万体 Boids + 障碍回避 + 蒙皮跟随
-
要点:ComputeBuffer 驱动,DrawMeshInstancedIndirect 渲染,小队编队与目标点切换,支持 1 个蒙皮角色作为吸引体。
-
验收:10 万体 60 FPS;GC 0 次;Dispatch/Frame ≤ 3;转向/回避无穿模;提供 Nsight/RenderDoc 抓帧截图与参数表。
4) 体积效果 / 雾光
项目:体素化体积雾(Froxels)+ 阴影
-
要点:视锥体体素网格,单向光体积阴影积分,体积蓝噪点时间降噪。
-
验收:体积阴影稳定无闪烁;降噪后残噪 < 5%(以差值帧统计);近景光柱清晰、远景渐隐自然。
5) 工具链 / TA Pipeline
项目:纹理通道打包与探针可视化工具
-
要点:一键把多贴图通道压进 RGBA,自动生成导入规则;场景内可视化 Light Probe/Reflection Probe 影响区域。
-
验收:美术拖拽源贴图→自动生成目标材质可用包;误配率 0;探针热区/死区一目了然;有 EditorWindow 与日志。
统一交付格式(每个项目都这样)
-
Repo 结构:
Assets/Runtime
,Assets/Editor
,Docs/
,内含 README、Profiling 报告、已录制演示视频。 -
指标表:FPS、CPU/GPU 时间、批次、显存、带宽、过绘。
-
技术笔记:架构图 + 关键公式/伪代码 + 踩坑与对策。
-
测试场景:小中大三档负载,截图与参数保存。
游戏内部常见的内存管理策略
游戏在内存压力下做的更多是资源卸载或画质降低,而非压缩内存。常用做法包括:
-
On-demand 卸载资源
在收到系统的内存告警(例如 Android 的 onTrimMemory),游戏会释放缓存、非当前使用贴图等资源。
Android Developers -
内存预分配机制
开发者常通过 Arena 分配器、对象池等方法预先申请内存并反复使用,避免频繁申请导致碎片或崩溃。
jenniferchukwu.com -
动态调整图像资源
在资源加载和运行时,根据硬件性能动态调节纹理压缩、分辨率等,以减轻内存压力。
DEV Community
Unity官方下载_Unity新版_从Unity Hub下载安装 | Unity中国官网
区域信息与 Unity 账户无关
-
单纯创建 Unity ID(Unity 帐号)时,你只需填写电子邮件和密码,并不会被系统要求选择或确认所在地区。系统也不会依据你的网络位置自动将账号划分至某个区域。Unity
-
Unity 的后台组织结构由你本人创建或加入的 Unity Organizations(组织) 决定,而非基于 IP 或国家划分。你可以随时管理或切换你的组织,与地理位置无关。Unity
https://www.youtube.com/watch?v=KvVL-LIq28A
“There was an error during the Project Binding process. Please make sure you are online and logged in: Failed to get the service authentication token. Try again or contact Unity support.”
这是 Unity 在尝试将项目连接到 Cloud Diagnostics 服务时,未能获取有效服务验证令牌而导致的问题。
结论:Hub 走了代理不代表 Editor 内的 Unity AI 请求也走了。AI 走的是 Editor 的网络栈;若 Editor 没经代理,服务端按出口 IP判定到中国区,就会提示 “not available in your region”。Unity 明确写了代理要分别让各组件识别,且 AI 在中国不可用。Unity Documentation+1Unity Discussions
你现在该做的最小改动
把启动脚本再加两行
UNITY_PROXYSERVER
是 Editor 使用 libcurl 时的专用变量;很多人只设 HTTP(S)_PROXY,导致 Editor 不跟随。Unity Discussions+2Unity Discussions+2
https://discussions.unity.com/t/offline-usage-of-unity-getting-harder/763430?utm_source=chatgpt.com
// Handle the final result after all attempts have been exhausted or a definitive failure was encountered.if (result != null){if (result.Result.Error.AiResponseError == AiResultErrorEnum.UnavailableForLegalReasons)Account.settings.RegionAvailable = false;if (result.Result.Error.AiResponseError == AiResultErrorEnum.ApiNoLongerSupported)Account.settings.PackagesSupported = false;var errorMessage = result.Result.Error.AiResponseError == AiResultErrorEnum.RateLimitExceeded // typically means wrong url (staging vs prod)? $"Account information returned {result.Result.Error.AiResponseError} on Url '{selectedEnvironment}'. Is the Url correct?\nTrace Id {result.SdkTraceId} => {result.W3CTraceId}": $"Error after {retryAttempt + 1} attempt(s): {result.Result.Error.AiResponseError} - {result.Result.Error.Errors.FirstOrDefault()} -- Result type: {typeof(TResponse).Name} -- Url: {selectedEnvironment} -- Trace Id {result.SdkTraceId} => {result.W3CTraceId}";if (!string.IsNullOrEmpty(UnityConnectProvider.organizationKey) && errorMessage != s_LastLoggedError){Debug.Log(errorMessage);s_LastLoggedError = errorMessage;}}
UnavailableForLegalReasons
是服务端直接拒绝(等价于 451/政策限制)。请求已发到 generators-beta.ai.unity.com,所以链路通。要么 Editor 仍用到受限出口 IP,要么账号/组织被政策标记。Unity Documentation
-
VRAM 是专用的显卡内存
它使用高带宽的 GDDR/HBM 类型 RAM,紧邻 GPU,读写速度极快、延迟低。用途包括保存纹理、帧缓冲、着色器常量等。
Super UserWikipedia -
系统RAM(DDR)是通用内存
用于操作系统、程序、CPU 运算,但带宽与延迟均逊于 VRAM。系统 RAM 和 VRAM 在物理与逻辑上是隔离的。
Super UserWikipedia
当 VRAM 不足,系统怎么做?
-
溢出到系统 RAM
当 GPU 的 VRAM 不够时,驱动会尝试让 GPU 从系统 RAM 获取数据,尤其是没有及时释放的纹理或缓冲区,或者有重用需求时。
但这需要通过 PCI-Express 总线,速度远慢于 VRAM,延迟与带宽大幅下降。
Super UseriRender Cloud Rendering ServiceDiskMFRStack Overflowtherealmjp.github.io -
社区经验反馈
Reddit 用户指出,即使发生“spillover”,显卡性能会急剧下降,帧率可能从几十 FPS 跳到个位数。“It’s like saying yes you can run Windows and game on a 15 year old HDD... You just shouldn’t.”
Reddit -
现代集成 GPU(UMA 架构)例外
集成 GPU(iGPU)与 CPU 共享同一物理内存。GPU 使用系统 RAM 的一部分作为其显存区域,这不算“溢出”,而是架构本身的设计。
Reddittherealmjp.github.io
特别说明:新硬件/驱动支持手动调整共享显存
-
Intel 推出了 Shared GPU Memory Override(Arc iGPU)
允许用户将高达 87% 的系统 RAM 分配给 iGPU 用作显存。适用于 AI 或高 VRAM 需求任务,但可能影响 CPU 性能。
Tom's HardwareTechRadar -
AMD 有类似的 Variable Graphics Memory(VGM)
可通过 Adrenalin 软件,让笔记本将多达 75% 的系统 RAM 当作 VRAM 使用,提高游戏表现。
The Verge
类型 | 存储位置 | 使用方式 |
---|---|---|
独立显卡(普通情况) | GPU 专用 VRAM | 高速专用内存,不与 CPU 共享 |
当 VRAM 不够 | 系统 RAM 作为溢出 | 通过 PCIe 缓慢访问,性能大幅下降 |
集成 GPU(UMA) | 系统 RAM(共享) | 架构设计为共享,无效区分显存和 RAM |
新驱动功能 | 可手动分配内存池 | Intel/AMD 提供工具扩展系统 RAM 用作 GPU 内存区域 |
3.顶部页签_哔哩哔哩_bilibili
还有红绿色盲的选项
4.CPU Usage 上_哔哩哔哩_bilibili
分析单个函数被调用的情况,和单个函数主动调别的模块的情况
5.CPU Usage 下_哔哩哔哩_bilibili
树形的显示timeline,这样更直观的直接看到那个函数占用时长很长
Frame Rate 与 精度间的关系
情况 | 帧率影响 | 建议做法 |
---|---|---|
Raycast 每帧调用(Update 内) | 部分帧缺失导致错判。但检测逻辑保持准确性 | 优化性能,避免卡顿,必要时合并多次调用 |
物理碰撞(固定步调) | 使用 FixedUpdate,帧率波动不影响 | 推荐物理模拟使用 FixedUpdate |
快速移动穿透问题(tunneling) | 高速度情况下可能跳过碰撞 | 启用 CCD(Continuous Collision Detection) 或用 ray 越距离检测 Godot Forumdigitalrune.github.io |
实战建议
-
用 FixedUpdate 而不是每帧调用射线。
-
若目标速度极快,考虑 CCD 或在移动路径上用短距离 raycast 避免漏判。
-
避免帧率低导致逻辑执行不稳定。必要时限制最低 FPS 或使用时间累积模拟。
是否使用固定时间步(fixed timestep)。Unity 默认物理在 FixedUpdate 中以固定间隔运行,与帧率无关,因此碰撞逻辑保持一致。
“The frame-rate won't affect physics if you've got it set to simulate during the fixed-update (default).” Unity Discussions
什么是垂直同步,什么是三重缓冲。垂直同步到底要不要开,垂直同步和三重缓冲到底有什么用。
https://zhuanlan.zhihu.com/p/41848908
显示器都有一个自己的刷新频率,60Hz的显示器一秒钟就是刷新60张画面,144Hz显示器就是一秒钟刷新144张画面。这当中,每一张画面我们都称之为一帧。
显示器的刷新率是固定的,比如60Hz显示器那么他就是固定每隔1/60秒刷新一帧。
显示屏的刷新率是固定的,也就是说你看上去屏幕即使没有更新画面,但是他底层还是在刷新的,而推动刷新的帧率是不固定的
基本机制区分
-
刷新率(Refresh Rate):显示器固定以每秒 X 次(如 60 Hz、144 Hz)重绘屏幕,即使画面内容未变,背后仍在周期性发起刷新。WikipediaSamsung Business Insights
-
帧率(Frame Rate):由 GPU/CPU 决定、随负载浮动,表示每秒生成了多少帧内容。IntelAVADirect
结论:显示器持续刷新,帧率则反映你系统实际有多少画面可供显示。
常见误区与实际效果
-
若帧率低于刷新频率,显示器会在没有新帧输入时重复上一个画面,导致暂停或卡顿感,但硬件仍按固定频率刷新。
-
若帧率超过刷新率,显示器仅显示其能力范围内的帧,未呈现的帧会被“丢弃”或重复显示。RedditAVADirectSteam Community
伏笔:可变刷新率(VRR)
-
有些现代屏幕支持 可变刷新率(VRR),能根据系统帧率自动调整刷新频率,实现同步,从而消除卡顿与撕裂。常见技术包括 G-Sync、FreeSync、Adaptive-Sync。WikipediaViewSonic
https://zhuanlan.zhihu.com/p/41848908
所以虽然很多人对于游戏画面流畅的定义是60FPS,但是你要清楚的就是,在你不能保证显卡输出的帧和显示器刷新率完美契合,那么显卡输出60FPS实际上是不流畅的,因为你的显示器一直会发生错帧现象,你可能就会看到50帧的画面,所以这就是为什么,保证游戏画面流畅的帧数需要高于60而不是等于60。
Freesync的驱动部件由厂家自己或者找外包完成,同面板不同产品性能品质可以差几十条街,极端例子FS2735 VS 马云DIY。
而不同面板的性能又层次不齐,就导致各种黑屏,闪屏,莫名其妙的延迟等现象,而G-sync的显示器,从研发,设计,量产都是由NV全程参与协调的,同时还需要往显示器内部植入专用芯片,也就是G-sync实际就是一个高门槛,这个门槛就决定了你的体验肯定比层次不齐的Freesync强,但是如果使用的是顶尖的Freesync面板,那其体验一点都不比G-sync差,所以评论区有个比较专业的老哥就说了,其实G-sync就是买个放心,保证这个技术肯定能带给你良好的体验,而Freesync就有点抽奖的意思了,运气好,这个面板完美适配Freesync,运气不好,体验很差,这个功能干脆就不开。