当前位置: 首页 > news >正文

自由学习记录(73)

着色器入门6:前向渲染与延迟渲染_哔哩哔哩_bilibili

URP管线下,运行时是否可以动态切换?

🟡 可以,但不是“自动切换”,你要操作 URP 的渲染管线设置。

方法 1(推荐):

  • 准备两个 URP Renderer(一个设为 Forward,一个为 Deferred)

  • 在相机中切换渲染器:

【TA进阶】Unity中的延迟渲染技术_哔哩哔哩_bilibili

2023版本的unity的新的forward+,处理了deffered render下的半透明物体的问题

要么就是自定义处理

Avatar —— Unity 中绑定骨骼系统到动画控制器(Animator)用的抽象对象
是模型的“人物骨架描述”,不是查看骨骼的工具。

Strip Bones 是干什么的?

你截图中的 ✔ Strip Bones 表示:

Unity 在导入时会剥离掉未被权重影响的骨骼(优化用)

  • 如果你想完整看到所有骨骼,建议先取消勾选 Strip Bones 再 Apply。

  • 否则没用的骨骼会被优化掉,看不见。

https://docs.unity3d.com/6000.1/Documentation/Manual/FBXImporter-Rig.html

Generic 模式就是完全使用 FBX 模型里自带的骨骼结构,不做任何人形重定向

  • 不做“humanoid avatar mapping”

  • FBX 里怎么绑的骨,就怎么执行动画

  • 所以适合非人类结构的角色,比如:怪物、机械、MMD 模型、定制人物

Unity Shader 105 - 多光源 和 Shader Pass_哔哩哔哩_bilibili

Unity多Pass的 动态合批 规则_哔哩哔哩_bilibili

关于光照的什么都没写进shader(即没有用任何光源变量、函数、宏),Unity 就不会处理任何光照。 

决定光照是否“有效”的关键不是光源存在与否,而是 Shader 是否“使用”了光照信息。

如果你只保留了这个 Shader 的 ForwardBase Pass(不写 ForwardAdd Pass),而场景中存在 4 个点光源且都设置为了 Important,那么会发生什么?


Unity 如何处理这些光源?

Pass 名称是否会被 Unity 调用渲染行为
ForwardBase✅ 会调用(你写了)只处理主光源(最亮的那个点光源)
ForwardAdd❌ 不会调用(你没写)其他 3 个光源无处计算 → 被跳过

如果你写了 Add Pass,但屏蔽了 #pragma

补充一点:

即使你写了 Add Pass,但没有 #pragma multi_compile_fwdadd,Unity 也不会启用那些光照变体。一定要两者都写:

Tags { "LightMode" = "ForwardAdd" }

#pragma multi_compile_fwdadd

否则 Unity 编译器不会认为你支持 Add Pass。

建议小结

你想要的行为需要的设置
只要一个光源、没有多 Pass✅ 只写 ForwardBase 即可,场景中最多保留一个主光源
要多个逐像素点光源生效✳ 写 ForwardAdd Pass + 设置 #pragma multi_compile_fwdadd
想忽略点光源影响✅ 设置它们为 Not Important,并移除所有光照路径

如果我不写 Add Pass,那能不能在 Base Pass 中“直接处理所有 4 个点光源”?

情况一:这些光源被 Unity 判定为“逐顶点光”

例如:

  • RenderMode = Not Important

  • 或 RenderMode = Auto 且光源离模型远、光照弱

那么:

👉 Unity 会把这些光源数据打包为顶点光数据,通过 内置的 unity_LightColor, unity_4LightPosX0, 等数组传入,你可以在 vertex shader 里循环计算多个光源贡献,然后在 fragment shader 里插值输出。

这种就是你说的 “Base Pass 里把多个光源都加进来算”

示例代码片段(Unity 内置支持 4 个点光)

// 顶点光结构(Unity 内部默认 4 个)
uniform float4 unity_LightColor[4];
uniform float4 unity_LightPosition[4];// vertex shader 中:
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;fixed3 accumulated = 0;for (int i = 0; i < 4; ++i) {float3 lightDir = normalize(unity_LightPosition[i].xyz - worldPos);float NdotL = max(0, dot(worldNormal, lightDir));accumulated += unity_LightColor[i].rgb * NdotL;
}

❌ 情况二:光源是 Important → Unity 判定为逐像素光

如果 4 个点光源都设置成 Important,Unity 会:

  • 选一个最亮的塞进 ForwardBase;

  • 剩下的 必须要有 ForwardAdd Pass 才能处理

  • ❗它不会自动把剩下的合并进 ForwardBase。

所以:

❌ 你不能在 Base Pass 里计算所有 Important 光源的影响(Unity 不会传它们的数据给你)。

可以在frag里面遍历吗

功能层面:完全可行!(完全不可行,草,说假的)

VERTEXLIGHT_ON 宏作用范围有限

  • 这个宏 只在顶点着色器阶段有效,在 fragment 中是 始终未定义 的 Unity Answers+8Unity Answers+8Unity Answers+8。

  • 如果你用 Shade4PointLights(...),它 必须写在 vertex shadervert)里,然后把结果传递给 fragment。

把光的作用放到vert函数里面就好了

Shader "Custom/VertexMultiPointSinglePass" {Properties {_Diffuse ("Diffuse", Color) = (1,1,1,1)_Specular ("Specular", Color) = (1,1,1,1)_Gloss ("Gloss", Range(8,256)) = 20}SubShader {Tags { "RenderType"="Opaque" }LOD 200Pass {Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma target 3.0// 启用 ForwardBase 变体 + 顶点光支持#pragma multi_compile_fwdbase _ VERTEXLIGHT_ON#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"fixed4 _Diffuse;fixed4 _Specular;float _Gloss;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;fixed3 vertLight : TEXCOORD2;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;#ifdef VERTEXLIGHT_ON// 累加最多 4 盏顶点光o.vertLight = Shade4PointLights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb,unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0,o.worldPos, o.worldNormal);#elseo.vertLight = 0;#endifreturn o;}fixed4 frag(v2f i) : SV_Target {fixed3 N = normalize(i.worldNormal);// 基础环境 + 顶点光fixed3 col = UNITY_LIGHTMODEL_AMBIENT.xyz + i.vertLight;// 主方向光(逐像素处理)fixed3 L = normalize(_WorldSpaceLightPos0.xyz);fixed3 diff = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(N, L));fixed3 V = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);fixed3 H = normalize(L + V);fixed3 spec = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(N, H)), _Gloss);col += diff + spec;return fixed4(col, 1);}ENDCG}}FallBack "Specular"
}

Fragment shader 能读到 Unity 传的光源数组(如 unity_LightPosition[i]unity_LightColor[i]),你可以编写如下逻辑:

fixed3 accum = UNITY_LIGHTMODEL_AMBIENT.rgb;
for (int i = 0; i < 4; ++i) {
    float3 L = unity_LightPosition[i].xyz - worldPos;
    float nDotL = max(0, dot(normalize(i.worldNormal), normalize(L)));
    accum += unity_LightColor[i].rgb * nDotL;
}
return fixed4(accum, 1);

只要光源被识别为顶点光或 SH,它们的数据就存在,任何位置都能遍历。

性能与编译影响要注意

  1. 循环和分支可能不被 GPU 友好
    GPU 通常会对光照循环进行“展开”(unroll),Unity 限定了最大 4 个顶点光,避免过度展开带性能问题。Reddit

  2. 顶点 vs 片元处理成本差异

    • 顶点着色器 里做计算只会执行一次/顶点,会将结果插值至片元,性能更高。

    • 片元着色器 里做执行次数是按照像素级别做循环,成本显著更高。kylehalladay.com+6Unity Documentation+6Unity Discussions+6Game Development Stack Exchange

建议做法

场景使用建议
光源 ≤ 4 个,想保简单frag 做循环,完成功能但性能次之
性能敏感(移动端、高填充率)将光照累加放至 vert,只跑一次 ➜ 储存到 TEXCOORD,最终插值到 frag

没有出现四个点光源的作用

思路是正确的,但目前代码中存在几个关键问题导致顶点光(unity_LightPosition / unity_LightColor)无法正常发挥作用:
这个也不用管,可以直接去掉,但是保险起见加上也没有关系

Unity 不会自动启用顶点光宏 VERTEXLIGHT_ON

你在 Base Pass 中使用了:

#pragma multi_compile_fwdbase

但是,这不会自动生成支持顶点光的变体。Unity 必须明确知道你支持“顶点光模式”,即你需要:

#pragma multi_compile_fwdbase _ VERTEXLIGHT_ON

这样 Unity 才会传递 unity_LightPosition[i]unity_LightColor[i] 等数据,并使用 VERTEXLIGHT_ON 进行编译分支切换。否则,数组永远为空。

float3 Shade4PointLights(
  unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
  unity_LightColor[0].rgb, ...,
  unity_4LightAtten0,
  worldPos, worldNormal)
必须在 vert 中使用,把计算结果打包到传给 fragment 的 v2f(如 LIGHTING_COORDS 或 TEXCOORD)。

xxxx

这样用点光,就显示是一个pass了,没有additional pass

https://github.com/candycat1992/Unity_Shaders_Book?tab=readme-ov-file

属性里显示的是当前坐标系下的坐标?显示是世界坐标?gpt说是始终世界坐标,但是添加了约束的笔一直没有变化,但是却一直跟着笔在移动

【Blender】骨骼子级约束在动画中的相关应用(连接与断开)_哔哩哔哩_bilibili

baked成动画,才可以在unity里跟着动

Blender子级约束(Child of)真的弄明白了吗?——1_哔哩哔哩_bilibili

“Child Of” 约束,这是一个非常强大的父子关系模拟器(模拟而已,实际上不是)

它允许你让一个物体“临时成为另一个物体的子物体”,但同时可以灵活控制哪些变换(位置/旋转/缩放)被继承

Vertex Group

  • (可选)仅当你的对象是**网格(Mesh)**时生效。

  • 如果指定了一个顶点组,约束只对那个区域部分生效(局部影响)

  • 对于笔或球这种刚体物体来说,一般不填。

Transform Axes: Location, Rotation, Scale

这些决定你的物体会继承目标物体哪些变换:

选项作用
Location X/Y/Z继承 Cube 的平移移动
Rotation X/Y/Z继承 Cube 的旋转(旋转会影响方向)
Scale X/Y/Z继承 Cube 的缩放(如果 Cube 放大,子物体也会变大)

如果你只想让物体“跟着移动”,可以只勾选 Location。
如果只想方向对齐,不移动位置,可以只勾 Rotation。

Influence(影响力)

  • 范围:0.0 ~ 1.0,默认是 1。

  • 控制这个约束的作用强度:

    • 1.0:完全跟随父物体;

    • 0.0:完全忽略该约束;

    • 中间值:渐进式过渡(适合动画混合、约束切换)

会看见两个骨架的顶点组

传统 Shadow Mapping和SSShadow有什么区别?

传统 Shadow Mapping(基于光源视角)屏幕空间阴影(SSShadow、SSAO风格) 之间在 渲染阶段、坐标空间、数据来源、实时性与性能代价 等多方面的核心区别。

传统阴影映射是从光源视角生成深度图,再投影判断阴影;
而屏幕空间阴影是直接在相机最终图像上,用深度或法线等 GBuffer 信息“后处理”生成阴影效果。

维度传统 Shadow Mapping屏幕空间阴影 (Screen Space Shadows)
原理光源视角下生成 Depth Map,再在主相机中采样投影判断基于相机渲染结果的深度图/法线图,后处理生成阴影
坐标空间世界空间 ➜ 光源空间 ➜ ShadowMap 贴图空间完全是屏幕空间(camera-space)
需要额外 Pass 吗✅ 是,光源需要渲染 ShadowCaster Pass❌ 否,在 camera pass 后处理阶段进行
精度问题有:Shadow Acne / Peter Panning / Bias 问题无传统投影偏差,但有深度模糊误差
可以支持哪些光源?点光、聚光、方向光等皆可通常只支持方向光,模拟局部阴影较弱
是否支持物体遮挡(Occlusion)✅ 准确遮挡❌ 通常依赖近似,不支持遮挡后物体
对透明物体支持❌ 差,需要特殊处理❌ 屏幕空间也难以支持透明体阴影
性能消耗成本在于额外光源Pass + 深度贴图采样成本在于屏幕像素遍历 + GBuffer采样
阴影柔化PCF / VSM / ESM 等硬件技术屏幕空间模糊或基于深度差值卷积
典型用途通用阴影系统,适用于所有真实光照主用于 Directional Light 的模糊阴影增强
代表技术Unity/Unreal 内置 Shadow MappingUnity URP/HDRP 中的 "Screen Space Shadows"、SSAO、Raytraced Shadows

🎬 图像感受上举例:

类型显示效果
传统 Shadow Map一束光照打下来,投影后落到场景中,边缘清晰或软化(PCF等)
屏幕空间阴影相机看到的物体边缘“附着”一层柔化的影子,随视角而改变,阴影只能出现在当前屏幕可见物体上
  • 传统 Shadow Mapping 是真实物理空间模拟 ➜ 优点是遮挡真实、兼容性高;缺点是容易出错,需要多个 Pass;

  • 屏幕空间阴影是视觉欺骗的后处理 ➜ 优点是快速灵活、节省 Pass;缺点是不可穿透、不支持远处和透明对象、贴在图像上。

Screen space shadows

https://shahriyarshahrabi.medium.com/custom-shadow-mapping-in-unity-c42a81e1bbf8

Unity 中的 “Light Cookie”

在 Unity 中,“Cookie” 不是浏览器 cookie,而是 一种纹理遮罩(mask),用于控制灯光形状效果:

  • 本质:一张灰度或透明纹理(只取 Alpha 通道)

  • 作用:改变光源投射的形状或强度

  • 效果:模拟树荫、窗户光斑、Caustics 光斑等复杂光形状YouTube+11Unity Manual+11Medium+11Medium+3Medium+3YouTube+3

你可以在 Light 组件中设置 Cookie 属性,将纹理拖入即可,Unity 会把光线限制在纹理遮罩区域之内,形状随纹理透明度变化。对于点光与聚光灯可使用球面映射贴图(Cubemap),方向光也可平铺或单次投射Medium。

开启 Cookie 对性能影响很小,但能极大提升灯光氛围感。

Unity 官方明确状态(从文档来看):

使用 VertexLit shader 的物体 无法接收阴影,但仍能 投射阴影。Unity Documentation+11Unity Documentation+11Unity Discussions+11

即便是在 Unity 2022 的版本中(虽然文档基于早期版本,但 VertexLit 是 Unity 最原始的像素光照模型,Unity 并未改变其本质行为),其不支持从主视角接收阴影仍然成立。

社区反馈情况:

  • 多年社区讨论中均指出:即便自定义 Shader 使用 FallBack "VertexLit",也不会在主摄像机中显示阴影,只有投射阴影可见。Unity DiscussionsUnity Discussions

  • 最新 URP 或 Built‑in RP 实践中,同样通过社区测试反复得出:VertexLit 无法接收阴影。

目前没有官方说明或社区反馈表明 Unity 更改了 VertexLit shader 的行为,它仍是最简化版本:

  • 不支持 SHADOW_RECEIVER(不参与阴影比较)

  • 即便你自定义写 ShadowCaster pass,fallback 本身仍不会接收阴影。

cull是显示正反面,而Cast shadow的two side和cull是分开的

ablend 这样一般不投射阴影,或者force to apply shadow

而a cut ,这样的做法是自己给一个property,然后以此为阈值去切除a低的

(但这种使用方法应该是可以扩展的,得到某些特殊的投影效果,比如让投影有那种线条的分割感,用某个遮罩通道单独做一个,得出特殊的投影效果)

即使你没显式写 TRANSFER_SHADOWUNITY_LIGHT_ATTENUATION,而 Unity 2020 版本的编译器可能自动插入 shadow receiver variant,让你的 VertexLit shader 实际具备阴影接收能力。

但这种机制并非文档推荐,也可能在 Unity 2022+ 或 URP 中失效,所以可视为“隐藏行为”。

📌 官方行为说明

Unity Manual 表示:

Fallback "Name" 会将目标 Shader 的所有 SubShader 插入到当前 Shader 后面” Reddit+14Huihoo+14Unity Documentation+14。

换言之,使用 Fallback "VertexLit",Unity 会自动加入系统的 Legacy VertexLit Shader 的所有 SubShader 和 Pass,相当于额外创建了它的 ShadowCaster 和可能的 Receiver Pass。

🧪 社区实证补充

社区开发者指出:

FallBack Off 会导致 Surface Shader 无法接收阴影,因为它不包含 VertexLit 的 shadow 接收逻辑” Unity Discussions+5Unity Discussions+5Unity Discussions+5。

另一份资源也明确说明:

“VertexLit shader 包含用来进行 Shadow Caster 和 Receiver 的 Pass” Unity Discussions+15Unity Discussions+15Unity Discussions+15。

Unity 默认将物体按照 Render Queue 分组渲染,顺序如下:

  • Geometry(不透明):Default = 2000

  • AlphaTest(Alpha 贴图测试):约 2450

  • Transparent(半透明/Alpha Blend):约 3000

  • Overlay(特效叠加层):约 4000
    Unity Discussions+10Unity Documentation+10Unity Discussions+10Game Development Stack Exchange+13Unity Documentation+13Unity Documentation+13

也就是说,所有 Transparent 排序的物体会在不透明之后渲染,你代码中设置 Queue="Transparent" 已经指定为透明队列。

阶段描述
不透明后绘制透明✅ 默认顺序
半透明写入深度?❌ 不写(ZWrite Off)
半透明遮挡半透明?✅ 若深度测试失败被丢弃颜色,不会写深度
半透明不会因顺序问题全体舍弃✅ 渲染依然会继续

ZTest(Depth Test,深度测试)

  • 控制当前片元是否应该被绘制

  • 根据当前片元的深度与已有的深度缓冲值比较:

    • 默认是 Less,意味着只有当当前片元的深度小于(更靠近摄像机)已有深度时,才通过测试并绘制。

  • 常见值包括:Less, LEqual, Greater, Always, Disabled 等。 

  • 具体作用如 ZTest Always 绕过深度测试、总是绘制;ZTest Less 则为常规遮挡测试。

🔹 ZWrite(Depth Write,深度写入)

  • 控制当前通过测试的片元是否写入深度缓冲

  • 开启(On) 会将当前片元深度写入深度缓存,影响后续绘制;

  • 关闭(Off) 则不会写入,也不会阻挡后续片元。

  • 通常在屏幕空间排序和透明渲染时关闭 (ZWrite Off),避免透明物体遮挡自身或其他内容

使用场景对比

场景描述建议设置效果
不透明物体ZTest Less + ZWrite On正常遮挡,同步写入深度缓冲
半透明物体 (Blend)ZTest Less + ZWrite Off如果后台不透明遮挡则跳过,且不写深度
始终可见特效ZTest Always + ZWrite Off总是画在最前方,不影响深度缓冲

o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
表示:

uv.xy 是主纹理 _MainTex 的 UV 坐标,进行 tiling + offset;

uv.zw 是法线贴图 _BumpMap 的 UV 坐标,单独做了一套变换;

此时 o.uv 中就包含两套 UV 信息,因此 float4 是合理且必要的

Unity 渲染队列(Render Queue)详解

按照 Unity 官方文档,内置渲染管线中存在五大默认队列组合:

队列名数值用途说明
Background1000背景预设物体(Skybox背后背景)
Geometry2000默认不透明物体(Opaque)
AlphaTest2450只读 Alpha 测试(如裁剪地形)
Transparent3000半透明渲染(Alpha Blend)
Overlay4000UI和特效类渲染(UI层)

ComputeGrabScreenPos 的功能

  • 它是 UnityCG.cginc 提供的屏幕空间坐标辅助函数,输入是顶点的剪裁空间位置(clip space);

  • 返回一个 float4,其中 xyzw 可以直接用于 tex2Dproj(_GrabTexture, ... )_GrabScreenColor() 等 Sampling 操作;

  • 自动处理平台差异、坐标系反转等问题,确保 GrabPass 纹理采样正确采到物体背后的画面。

ComputeGrabScreenPos(o.pos) 会将当前顶点的 clip-space 坐标转换为可用于访问 GrabPass 屏幕纹理的 UV 坐标(scrPos),以便后续 fragment shader 正确采样背景画面。

xxx

为什么需要先转换成这种坐标系才可以使用贴图?我的模型已经了uv,现在又有了图,直接用不行吗

原因是 —— 采样的贴图类型不同,本质坐标系也不同。

贴图用途对应坐标坐标来源
模型贴图(如 BaseMap、NormalMap)模型 UV(v.texcoord.xy模型网格自带的 UV 坐标
屏幕贴图(如 GrabPass、屏幕遮罩)屏幕空间 UV(ComputeGrabScreenPos()Clip Space ➜ NDC ➜ Screen UV
后处理贴图(如 SSAO、Bloom)屏幕空间 UVCamera 渲染出来的屏幕图像

模型贴图(使用模型 UV)

fixed4 tex = tex2D(_MainTex, i.uv);

  • i.uv 是模型提供的 TEXCOORD0

  • 对应 BaseColor, Normal, Roughness 等;

  • 不受相机或屏幕影响,只与模型本身有关。

屏幕贴图(如玻璃、水面后面的画面)

 

o.scrPos = ComputeGrabScreenPos(o.pos);

...

half4 screenColor = tex2Dproj(_GrabTexture, i.scrPos);

  • 这里 _GrabTexture 是当前屏幕内容;

  • 我们并不是采样模型上的纹理,而是采样“模型后面原本应该看到的画面”;

  • 所以,必须转为屏幕空间坐标clip space ➜ NDC ➜ screen UV)去采样。

grabpass是整个屏幕的一张图,还是说这个材质上 的物体的覆盖范围的一张图

answer一针见血:

GrabPass 抓的是整个屏幕的一张图(即当前所有已渲染内容,不是当前物体本身的部分)。

所以需要;

所以需要这个模型在屏幕空间下的各个像素的位置ComputeGrabScreenPos(o.pos),

才可以进一步的只在这个模型范围之内做手脚,修改各种看到的效果

为什么要乘上 i.scrPos.z

  • i.scrPos.z从相机到该像素的深度(NDC 空间)

  • 越远的像素 → 折射偏移越大 → 更真实的折射透视感

根据深度加权地扰动屏幕坐标,实现 视差折射感(parallax refraction)。

除以 w 正是将 齐次裁剪空间坐标 (clip space) 转换为 屏幕空间 UV 坐标 的标准步骤

o.pos = UnityObjectToClipPos(v.vertex);
这是顶点经过变换后处于裁剪空间 (Clip Space) 的坐标:
o.pos = float4(x, y, z, w)
它表示该顶点相对于当前摄像机的位置,还未被除以 w不是真正的屏幕位置

【教程】技术美术入门:渲染管线概述_哔哩哔哩_bilibili

w 分量的来源

  • 最初,模型顶点在 Object Space(模型局部空间)以齐次坐标形式表示,通常是 (x, y, z, 1)

假设一个模型顶点在 Object Space 下的坐标:

v_model = (x=1, y=2, z=3, w=1)

其中 w=1齐次坐标的默认值,表示这是一个位置向量


🧭 1. 模型 → 世界 → 视图空间(Camera/View Space)

先通过模型矩阵(Model)和视图矩阵(View)变换:

v_view = ViewMatrix * ModelMatrix * v_model

  • 假设变换后得到(示例):

v_view = (vx=5, vy=4, vz=−10, vw=1)

vw 通常保持为 1,因为只是线性/平移变换


🧮 2. 视图空间 → Clip Space(乘以投影矩阵)

接着,把上面得到的 v_view 乘以 Projection 矩阵 P(Perspective Projection):

v_clip = P * v_view = (x_c, y_c, z_c, w_c)

一个典型的透视投影矩阵会把 -z 放到 w 分量中,因此:

w_c = -v_view.z = 10

(这是一种简化演示,具体值由 near/far plane 设定决定)
最终可能得到:

v_clip = (x_c=2.0, y_c=1.6, z_c= (some value), w_c=10)

clip空间里的xy可以说就是对齐屏幕了,z只是被压缩了,但是里面的前后关系依然清晰可见

(如何压缩的,取决于view space里的时候,相机的near和far两个面是怎么样的数值)

clip里,此时的w则是保存了原本的-z(更加直观的对比出前后的关系)

然后通过都除以w进入ndc

而这个-z在之后转换成屏幕坐标后(就变成三维了,)

  • Clip Space 的 XY 坐标在剔除后接近屏幕对齐,而 Z 被压缩,但仍保留“远近关系”

  • 这种压缩方式由 near/far 平面 决定;

  • Clip Space 的 w 分量则基本是来自 camera/view space 的 -z,也就是摄像机空间深度;

  • 最后经过 除以 w(也就是透视除法),进入 NDC,再映射到屏幕空间。

----总之,不用管,里面都是乱七八糟的设计,好在哪也和你没关系,这个w爱怎么样怎么样

clip space记住是压缩成了长方体了,

裁剪空间的定义要求所有坐标都在 [-w, w] 范围内。

P矩阵的设计目标正是让 view space 中的几何符合这个标准,而将 z 的远近信息藏进 w 中,以供透视除法恢复远近关系。

ScreenPos = ViewportTransform(NDC)
这时的 ScreenPos = (x, y, z) 是:

x, y:屏幕上的像素坐标(以像素为单位)或 UV 坐标(单位化的 [0,1] 区域)

z:深度值(通常 ∈ [0, 1]),来自 NDC 的 z 值映射

阶段xy 所在范围是否已是 [0,1] UV是否需要除以 w
ComputeScreenPos 输出的 .xy[-w, +w] → [0, w]❌ 不是✅ 必须
除以 .w 之后Clamped to [0,1]✅ 是
NDC(真实 UV)[0,1]✅ 是已完成

NDC (Normalized Device Coordinates)

  • 这是在 裁剪空间进行透视除法之后得到的坐标空间。

  • 坐标值通常属于:

    • x, y:范围是 [-1, +1](OpenGL),或已转换成 [0,1] 的 UV 空间(Unity 默认映射到 UV)。

    • z:范围是 [0,1][-1,1],具体取决于 API/D3D/OpenGL 等。

  • 只有当顶点经过 clipPos.xyz / clipPos.w 后,才进入 NDC(标准化设备坐标)。

ComputeScreenPos(clipPos) 的功能(Unity 内部)

  • 它接受的是裁剪空间坐标 float4 clipPos,其 .xy 分量的是还处在 “齐次空间”—— 即仍然属于 [-w, +w] 区间。

  • ComputeScreenPos 会对 xy((-w, +w) → (0, +w)) 的线性映射:

    "transform input from clip coordinate vertex position [-w, w] into [0, w]" Unity Discussions+1Unity User Manual+1GitHub+11Unity Discussions+11cyanilux.com+11Game Development Stack Exchange+5cyanilux.com+5Stack Overflow+5Unity Documentation+2Unity Documentation+2Unity User Manual+2

  • 然后通过 透视除法(片段里用 xy / w)才能真正归一化到 [0,1] 范围 Unity DiscussionsUnity Discussions。

http://www.dtcms.com/a/298738.html

相关文章:

  • 地铁逃生
  • 注意力机制的使用说明01
  • RNN模型数学推导过程(笔记)
  • 散列表(哈希表)
  • SQL基础⑮ | 触发器
  • 亚德诺半导体AD8539ARZ-REEL7 超低功耗轨到轨运算放大器,自动归零技术,专为可穿戴设备设计!
  • Python 程序设计讲义(20):选择结构程序设计——双分支结构的简化表示(三元运算符)
  • 【linux】Haproxy七层代理
  • 电子基石:硬件工程师的器件手册 (八) - 栅极驱动IC:功率器件的神经中枢
  • 【自动化运维神器Ansible】Ansible常用模块之Copy模块详解
  • 程序代码篇---卡尔曼滤波与PID的组合应用
  • 2.Linux 网络配置
  • 【PyTorch】图像多分类项目部署
  • python基础:request模块简介与安装、基本使用,如何发送get请求响应数据,response属性与请求头
  • centOS7 yum安装新版本的cmake,cmake3以上怎么安装,一篇文章说明白
  • Java并发编程第十篇(ThreadPoolExecutor线程池组件分析)
  • 无印 v1.6 视频解析去水印工具,支持多个平台
  • Android悬浮窗导致其它应用黑屏问题解决办法
  • RocketMQ 5.3.0 ARM64 架构安装部署指南
  • J2EE模式---数据访问对象模式
  • C语言案例《猜拳游戏》
  • VSCode 报错 Error: listen EACCES: permission denied 0.0.0.0:2288
  • Java 笔记 interface
  • C#入门实战:数字计算与条件判断
  • Web攻防-业务逻辑篇密码找回重定向目标响应包检验流程跳过回显泄露验证枚举
  • 【PyTorch】图像多分类项目
  • 一些常见的网络攻击方式
  • CY5-OVA科研方向,星戈瑞荧光
  • Pytest tmp_path 实战指南:测试中的临时目录管理
  • C语言————原码 补码 反码 (日渐清晰版)