自由学习记录(118)

"catalogue"(或美式拼写"catalog")常用于指代一个系统化、全面的物品或作品清单(如音乐目录、商品目录),强调完整性和分类结构,尤其在艺术、出版或商业语境中。

h = Hybrid / Highlight normal(低概率,但存在)

在部分引擎/项目中:

  • N = 原始几何法线

  • hN = 混合后用于高光的法线(Hybrid Normal)

例如:

float3 hN = normalize(lerp(N, normalMapN, hairNormalWeight));

hN 是一个"只给高光用"的法线结果,而不是直接的贴图法线。

一旦你把它当作 Normal Map 导入,它就不再是"一张 RGBA 数据贴图",而是"一张被 Unity 处理过的法线资源"。

  1. 选择法线编码约定
  • DirectX 风格(默认):

    • X → R

    • Y → G(可能翻转,取决于设置)

    • Z → 重建(不存)

  • OpenGL/Metal 等平台可能在后台做转换

  1. 丢弃或覆盖原始通道语义
  • Unity 认为:

    "这张贴图只描述一个向量,不是 4 通道任意数据。"

  • 因此:

    • B 通道通常不再保留原始值(常被当作常量或压缩占位)

    • A 通道通常被 Unity 内部占用或忽略

    • 在很多压缩格式下(BC5、ASTC Normal),根本没有 B/A 的存储概念

  1. 压缩方式被强制改变
  • PC/主机:

    • 常见是 BC5(RG 两通道)
  • 移动:

    • ASTC normal、ETC2 normal
  • 这些格式只存 RG 或等价信息

👉 所以:
你在 RGBA 里塞的第三、第四个维度的数据,在导入为 Normal Map 后,基本注定会丢。

代价:

  • 不能享受 BC5 normal 压缩的质量/效率

  • 你要自己处理平台差异

但优点:

  • RGBA 四通道语义完全由你掌控

Normal Map 的平台重解释(你前面已经踩到)

  • PC(DX11/12):

    • Normal Map → BC5

    • 只有 RG,Z 重建,BA 语义不存在

  • 移动(GLES / Vulkan):

    • ASTC / ETC2 normal

    • 通道重排、精度差异更大

如果你:

  • 在 PC 上"看起来还能用 B/A"

  • 到移动端突然坏掉

这是100% 合理现象

线性/非线性与采样精度差异(非常常见,但很隐蔽)

2️⃣ sRGB / Linear 的处理差异

  • Default + sRGB On:

    • R/G/B 会做 gamma→linear
  • Normal Map / Linear:

    • 不做 gamma
  • 不同平台对 sRGB sampling 的精度和路径不同

一个原本"只是 mask"的通道,如果被错误当成 sRGB 采样:

  • 在 PC 上可能"还能接受"

  • 在移动上会明显偏移阈值

这对:

  • HairArea

  • Ramp mask

  • 高光阈值

    极其致命。

头发为什么"必须"要法线信息

不论你用的是

  • Kajiya--Kay

  • Marschner

  • 或现代的 dual-lobe / TRT 近似

头发高光的形状和位置,几乎完全由"局部切线方向"决定

但在实时里,人物头发通常是:

  • card / ribbon / shell(平面几何)

  • 极少真实圆柱体

  • 顶点数被严格限制

结果就是:

几何法线 ≠ 真实头发丝方向

如果你只用 mesh normal:

  • 高光会沿着卡片法线走

  • 出现"塑料片""油漆刷"的效果

  • 各向异性方向完全错误

头发法线图的本质:不是"凹凸",而是"方向场"

提供微尺度法线分布(NDF)

  • 让 spec lobe 宽度更接近真实头发

  • 减少卡片级别的 banding

shader 通常不走标准 PBR 法线解释

绝大多数头发 shader:

  • 不用 GGX

  • 不用标准 N·L / N·V

  • 而是使用 strand-based BRDF

例如:

复制代码

T = normalize(projected tangent)

θ = angle(L, T)

在这种模型里:

  • 法线主要用于重建 T

  • 而不是参与几何遮挡意义上的 shading

所以你不会看到类似皮肤那样的凹凸阴影。

对"strand-based"头发来说,决定性变量不是传统 PBR 里的 N,而是局部的 T(strand direction)以及围绕 T 的截面坐标系(经常用 N/B 或 frame)。你说的"PBR 里 T/B 只是基底,不做处理"基本成立:在金属/塑料那类各向同性微表面里,TBN 主要是给 normal map 提供局部坐标;但在头发里,T 是 BRDF 的主轴,B/N 更多是为了把"围绕主轴的角度"和"离轴程度"表达出来。

Kajiya--Kay 本质上是基于 T(strand tangent)的模型,而不是基于 N。这一点在概念上和实现上都非常明确,只是很多"简化实现"把它伪装成了像 PBR 一样在用 N,容易造成误解。

Kajiya--Kay 把头发当作"无限长圆柱纤维",BRDF 的主轴是纤维轴 T,高光强度由光线与 T 的夹角决定,而 N/B 只是在工程实现中用来辅助计算。

注意这里完全没有 N

几何意义是:光线与圆柱轴线越"侧向",反射越强,这正是头发那条沿发丝方向拉长的高光。

为什么你在 shader 里仍然"看到 N / B"?

这是实现层面的妥协,而不是模型层面的依赖。

在实时实现中通常需要:

  • 限制高光只在"可见半空间"

  • 处理背面、阴影、能量裁剪

  • 在卡片几何上避免完全无方向的响应

于是常见写法是:

  • 用 N(通常是几何法线 Ng)做 N·L / N·V 的 gate

  • 或者用 B / N 构造一个稳定 frame,算 φ、算 wrap

但这些都是附加约束项,不是 Kajiya--Kay 的定义核心。

模型 主轴 T 的地位
标准 PBR(GGX) N T 只是给 normal map 提供坐标
各向异性金属 N T 控制 lobe 方向,但仍围绕 N
Kajiya--Kay 头发 T T 是 BRDF 的定义轴

"基于 T 轴"的意思不是"画面只看 T",而是:高光(以及很多时候的散射近似)用 T 来决定主方向。你最终仍然要输出一个像素颜色,所以必须把"每个像素的 T 是什么"喂给 shader,并且还要有一套稳定的局部坐标系去计算角度项(至少要能算出跟 T 的夹角、以及围绕 T 的方位关系)。这就是为什么你依然会看到 normal map / flow map / tangent map 之类的东西出现。

  1. 基于 T 的高光要怎么"显示出来"

    最简的 Kajiya--Kay 风格高光基本就是:

  • 先得到像素级 T(单位向量)

  • 计算光线与 T 的夹角:sinθ = length(cross(L, T))(等价于 sqrt(1-(L·T)^2))

  • spec = pow(saturate(sinθ), exponent)

  • 再乘一个可见性/门控项(比如 N·L / N·V 或者 wrap),避免"背面也亮/卡片透亮"的假象

这里的关键是:你不需要"真实圆柱法线",只要 T 场对,高光就会沿发丝方向拉长、位置也会像头发。

  1. 既然靠 T,为啥还需要 normal map?

    要分清你说的"normal map"是哪一种用途:

A. 传统凹凸用途(给 PBR 的 N)

头发通常不靠它做"凹凸阴影",否则确实容易"坑坑洼洼"。

B. 方向用途(给头发的 T / frame)

很多项目把"发流方向"编码在一张看起来像 normal map 的贴图里(或者直接就是 tangent/flow map),原因是:

  • 贴图采样、MIP、各向异性过滤都是硬件友好的

  • 你可以在像素级细化 T,让高光更像"很多细丝"而不是"一片卡片"

  • 在发束交界处过渡更平滑

常见做法之一是:顶点提供一个粗 T(沿卡片长轴),贴图提供一个"在卡片平面内的方向偏移",最终得到像素级 T。

也有人直接用 flow map(RG 存 2D 方向)作为像素 T,再归一化即可。

  1. mixV 是什么意思?

    "mixV"在头发卡片里非常常见,但不同代码库命名略有差异。最常见的含义是"把某个向量和 View 向量做混合",用途通常是以下两类(你基本可以用现象去对号入座):

A. 混合法线用来做门控/变亮(解决卡片过暗、背面怪异)

典型写法类似:

N_gate = normalize(lerp(N_geom, V, mixV))

然后用 N_gate 去算 N·L / N·V 或 Fresnel 门控。

效果:减少卡片在掠射角时的"黑片"、降低双面翻转带来的高光跳变。

B. 用 V 参与构造稳定的局部 frame(解决 B/N 不稳定)

当你只有 T 时,需要补齐 N/B。很多实现会用 V 作为参考向量:

N = normalize( V - T * dot(V, T) ) (把 V 正交化到垂直 T 的平面)

B = cross(T, N)

然后 mixV 控制你"更相信几何法线"还是"更相信视线构造的 frame":

frame_ref = normalize(lerp(N_geom, V, mixV)) 再去做上面的正交化。

效果:稳定方位角、减少 seam 和背面翻转导致的 φ 跳变。

如果你的代码里 mixV 改大后:

  • 头发侧面不那么黑、卡片感减弱:大概率是 A

  • 高光在卡片正反面更稳定、减少翻面跳变:大概率是 B 或 A+B 叠加

  1. 这种做法常见吗?

    很常见。尤其在:

  • hair cards(不是 strand geometry);

  • 需要双面渲染;

  • T 来自梳理/flow、而 mesh tangent 不可靠;

  • 想要"高光像丝、但不想增加几何"。

常见组合是:

  • 顶点 T(卡片长轴) + 贴图细化 T(flow/"normal-like")

  • diffuse 用 Ng(避免凹凸感),spec 用 strand frame(T 主导)

  • 再用 mixV 之类的技巧做门控/稳定性补丁

最常见情况:N = 表面法线,HN = Hair Normal / Strand Normal

在头发 shader 里很典型的一种结构是:

  • N:用于 diffuse 或 N·L / N·V 门控

  • HN:用于 specular 计算(尤其是 Kajiya / dual-lobe 那部分)

也就是说:

diffuse 用 N

spec 用 HN

这样做的好处:

  1. 避免"凹凸感"

    diffuse 用几何趋近平滑的 N,画面不会坑洼

  2. 高光更细腻

    spec 用 HN(通常来自 flow-like 信息),高光沿发流更精细

  3. 控制更自由

    可以单独调 HNormalStrength,不影响整体明暗结构

在头发卡片里,这种"双法线体系"非常常见。

HN = Head Normal / Half Normal / Highlight Normal

不同团队命名不同,但语义类似:

HN 常常代表:

  • Highlight Normal

  • Hair Normal

  • 或者某种"改造过的 normal"

典型用途包括:

  1. 只用于计算 T-frame

    用 HN 去重建一个更"接近真实发丝"的方向

    而 N 只是作为卡片基底

  2. 做 spec shift

    在 dual spec 模型中,常常需要一个 slightly shifted normal

    一套 normal 控制 primary lobe

    一套 normal 控制 secondary lobe

如果你看到 shader 里有两套 spec exponent 或 shift,那基本就是这个。

Blender 的 Incoming(入射/视线)向量Geometry 节点 中的一项参数,它输出一个指向摄像机或观察点的方向向量(在物体空间或世界空间中)。该节点通常用于基于视角的变化来控制材质效果,例如制作视线相关的特殊高光、边缘光或与视点相关的纹理遮罩。

核心功能与用途

  • 物理特性:此向量的方向由表面点指向相机。
  • 用途 :最常用于 Shader 编辑器中,通过计算视角与表面法线的关系(配合 Dot Product 节点)来实现类似 Fresnel(菲涅尔) 或边缘检测的效果。

"bump" 只是默认贴图类型,不会强制影响你拖进去的实际贴图。

dot(nTS.xy, nTS.xy) 就是 x*x + y*y,所以:

1 - dot(nTS.xy, nTS.xy) 等价于 1 - x*x - y*y。

Blender的Mix节点(选择Multiply模式时)中,

**Fac(Factor/因子)**用于控制基色(Color1/A)与混合色(Color2/B)相乘的程度。当Fac为0时,输出为原始值(A);为1时,输出为乘积结果(

𝐴×𝐵

)。Fac介于0-1之间时,则是原始值与乘积结果的线性插值。

  • Fac 0.0 (默认): 输出基色 (Color1),不发生相乘改变。

  • Fac 1.0: 完全应用Multiply模式,图像变暗(由于颜色值通常

    <1is less than 1
    <1 ,相乘变小)。

Blender的Mix(混合)节点中的Factor(系数,通常简写为Fac)控制两个输入(A和B)之间的混合比例。Fac值为0.0时完全显示输入A,为1.0时完全显示输入B,中间值则是两者的线性插值(如0.5为50%混合)。这不仅适用于颜色,也适用于纹理、数值和着色器混合。

  • "饿死"在这里不仅是生物学上的死亡,更是拉康意义上的**"第二次死亡"**:即在象征秩序中被彻底抛弃和抹除。

  • 这种对"彻底匮乏"的恐惧成为了原始驱动力,导致个体即便在物质极大丰富后,依然无法停止强迫性重复(Repetition Compulsion)

网络中的"抱团"与"寻找同伴"是一种纯粹的符号性行为, 更像是一种**"社会性昆虫的化学信息素追踪"。个体并不具备独立意图,它们只是在嗅到某种特定频率的符号(关键词、站队信号)时,自动触发了预设的生化反应。

深受"理解他人"这一伦理律令的束缚。在你的逻辑中,如果将某种声音定义为"垃圾",就意味着在语言层面上终结了对话的可能性,这违背了你对"开放系统"或"无限游戏"的学术信仰。你内心的"尊重"其实是对逻辑连贯性的尊重:你害怕一旦通过"贬低"来获得优越感,你的整套客观性分析工具就会失效。

"彻底匮乏"的恐惧

本质原因是执行域(evaluation domain)不同,而 Shader Editor 里"Geometry → Tangent"节点并不等于 Geometry Nodes 系统里的 Tangent 节点。

需要先澄清一个关键点:

在 Shader Editor 里看到的 "Geometry → Tangent" 节点,

仍然是一个 着色阶段节点(shading node)

它不是 Geometry Nodes 里的几何处理节点。

它只是名字上带有 "Geometry"。

Tangent 节点是一个 着色输入节点(Shading Input Node)

它输出的是当前着色点的切线方向向量,用于构建 TBN 或驱动各向异性方向。

它有两种模式:

• Radial

• UV Map(如果选择某个 UV)

并且可以指定 X/Y/Z 轴。

它依赖:

  • 当前材质

  • 当前 UV

  • 当前着色法线

  • MikkTSpace 切线构建规则

  • 插值后的 per-fragment 数据

这些数据只存在于 材质求值阶段(GPU fragment stage)

不是。

你截图里的 Radial 不是"分离 Tangent 的 XYZ 分量"的意思,它是 切线方向的生成模式

下面精确解释。

一、这个节点在做什么

Shader Tangent 节点本质是:

输出一个 切线方向向量(T vector)

它不是把某个已有 Tangent 拆分,而是根据模式生成一个切线方向。


二、Radial 的真实含义

Radial 的意思是:

生成一个"围绕某个轴的径向切线方向"。

更具体地说:

它构建的是一个绕指定轴的圆周方向向量。

例如:

如果选择 Z 轴,

Radial Tangent = 在 XY 平面内绕 Z 轴的圆周方向。

数学上可以理解为:

给定点的世界空间位置 P

投影到与轴垂直的平面

计算圆周方向(类似极坐标的 θ 方向)

它是一个"环绕方向向量",常用于:

  • 各向异性拉丝金属

  • 圆柱刷纹

  • 唱片纹理

  • 头发绕轴高光

和 UV 模式的区别

如果选择 UV Map 模式:

Tangent = UV 的 U 方向的切线(基于 MikkTSpace)

如果选择 Radial:

Tangent = 程序生成的圆周方向向量

它不依赖 UV。

Radial 模式生成的是绕某个轴的圆周切线方向(tangential direction),也就是极坐标里的 θ 方向。

它不是"随便给个顺时针方向",而是由右手坐标系严格定义的。


一、数学上它在做什么

假设选择 Z 轴。

对每个着色点 P:

  1. 取它相对原点的位置向量

  2. 投影到 XY 平面

  3. 构造圆周方向

本质上等价于:

T = normalize( Z_axis × P_projected )

这个叉乘决定方向。

因为 Blender 使用右手坐标系:

  • Z 轴向上

  • X 向右

  • Y 向前

叉乘顺序固定,所以方向是确定的。


二、顺时针还是逆时针?

取决于你从哪个方向看。

如果:

你从 +Z 方向往下看(俯视)

生成的方向是逆时针

如果从 −Z 看,会感觉是顺时针。

这不是"随机方向",而是右手法则决定的。

把"世界轴 XYZ"和"TBN 局部基"彻底分离开

在 Blender 的材质系统里,插值发生在"顶点属性 → 像素属性"这个固定流程中:顶点阶段输出一堆 attribute(position/normal/UV/tangent...),光栅化后按重心坐标插值到每个像素。这个插值方向不是你能用 Tangent 节点去改的;它永远是三角形内的重心插值(再加上派生/归一化等步骤)。

你截图这个 Tangent 节点(含 Radial/UV Map)控制的是"给 BRDF 一个方向向量 T",不是控制插值。

你一直在用"图形管线里 attribute 插值/顶点基底"的模型去套 Blender 的这个 Tangent 节点,但这个节点在材质里更多是"给各向异性/法线空间提供一个方向向量",而不是"控制插值"。把它压缩成两条规则就清楚了。

你把 Tangent 节点当成了"控制 attribute/插值的节点",但它更像是"在像素阶段取用/构造一个方向向量给 BSDF 用"。两者在管线位置上差一层。

  • Blender 的 Tangent 节点不是"attribute 写入器",而是"shader 读/算出来的方向提供器"。

  • UV 模式:走你熟悉的 attribute 插值路径。

  • Radial 模式:走 "T(P) 解析计算" 路径,X/Y/Z 是定义这个解析场的参考轴,不是插值轴。

"解析场"在这里你可以当成一句很朴素的话:不是从网格顶点里取一个 attribute 再插值,而是"在每个着色点用一个确定的公式直接算出一个向量"。它不需要存储在模型里,也不需要 UV。它就是一个函数。

所以所谓"解析向量场"就是:T = f(P),P 是当前着色点的位置(或它在某个空间里的坐标)。

TBN 是用来把"方向"表达成局部坐标系的结果,它是构造出来的输出。你要先有 T 和 N,才能有 TBN。

所以任何用来生成 T 的东西,都不可能"在 TBN 空间里"。

Radial 要做的事情是"绕某条轴旋转",数学上就是用 axis 和 position 做叉乘/投影。

这要求 axis 和 position 都在同一个坐标系里,否则结果没有意义。

在 Blender 材质系统里,这类节点通常是在"某个固定的几何空间"里定义的,实践上你可以把它理解为:默认跟对象一起动(更像对象空间),而不是锁死世界空间。

我们分两种情况写成 Unity HLSL 形式:

一、UV 模式的 Tangent(标准 TBN 管线)

这是最常见、最接近你管线认知的情况。

在 Unity 里,顶点结构通常包含:

三、两者对比成一句话

UV 模式:

T = interpolated(vertex_tangent)

Radial 模式:

T = normalize(cross(axis, projected_position))

两种都最终进入:

B = cross(N, T)

但来源完全不同。

你觉得"在 frag 里这样算很莫名其妙",这个直觉其实来自一个前提:

在你的经验里,Tangent 应该是 顶点 attribute → 插值 → frag 使用

而不是在 frag 里重新算。

但这里并不莫名其妙,只是用途不同。

我们拆开看。

为什么 UV Tangent 在 vertex 阶段算?

因为:

Tangent 来自 UV 偏导

它是一个 与三角形拓扑强相关的量

标准做法:

  • 在 mesh 级别计算顶点 tangent

  • 存进 vertex buffer

  • 插值到 frag

  • normalize

  • 构造 TBN

这是合理的,因为:

tangent 是 mesh intrinsic attribute。

Blender中的基础几何体模型(如Cube、UV Sphere等)在创建时自动自带默认的UV映射,但自行制作的复杂模型通常需要手动展开(Unwrap)以生成有效的UV贴图。自动生成的UV通常是基础的投影方式,无法满足精细的材质贴图需求。

在你的经验里,Tangent 应该是 顶点 attribute → 插值 → frag 使用

而不是在 frag 里重新算。

但这里并不莫名其妙,只是用途不同。

我们拆开看。


一、为什么 UV Tangent 在 vertex 阶段算?

因为:

Tangent 来自 UV 偏导

它是一个 与三角形拓扑强相关的量

标准做法:

  • 在 mesh 级别计算顶点 tangent

  • 存进 vertex buffer

  • 插值到 frag

  • normalize

  • 构造 TBN

这是合理的,因为:

tangent 是 mesh intrinsic attribute。

Radial 本质不是 mesh intrinsic attribute

Radial 是:

T = f(P)

它只是一个"方向场函数"。

它不依赖三角形拓扑

不依赖 UV

不依赖 vertex tangent

它只依赖位置 + 一个轴。

这和"从顶点 attribute 插值"是两种完全不同的语义。

posOS - axis * dot(posOS, axis);而且是"从向量里减去在某个轴方向上的分量",等价于把向量投影到垂直该轴的平面上

1️⃣ 两个单位向量点乘 ≠ 夹角本身

如果 a、b 是单位向量:

复制代码

dot(a, b) = cos(θ)

结果是夹角的余弦值,不是夹角 θ。

要得到角度:

复制代码

θ = acos(dot(a,b))

所以 dot 不是"得到角度",而是得到:

两个向量在同一方向上的重合程度

2️⃣ "dot 是把多位变成一位"这种理解不对

dot 不是"降维操作"。

dot 的几何意义是:

把一个向量在另一个方向上的投影长度提取出来

例如:

复制代码

dot(v, a)

等价于:

"v 在 a 方向上的分量大小"

它是一个标量。

这不是随便变成一位,

而是提取一个方向分量。

float3 c = a * b; // HLSL/GLSL 默认就是逐分量

(c.x, c.y, c.z) = (a.x*b.x, a.y*b.y, a.z*b.z)

用途:

颜色调制(albedo * light)

掩码

每通道缩放

它没有几何意义,不涉及方向关系。

经常用于:

  • 计算夹角

  • 计算光照(N·L)

  • 做投影

  • 取某方向分量

控股是指通过持有某一公司一定数量的股份,而对该公司进行控制的公司。控股公司按控股方式,分为纯粹控股公司和混合控股公司。纯粹控股公司不直接从事生产经营业务,只是凭借持有其他公司的股份,进行资本营运。混合控股公司除通过控股进行资本营运外,也从事一些生产经营业务。

控股公司(Holding Company)不一定是上市公司。控股是一种基于股权关系的控制形式,控股主体可以是个人、非上市公司,也可以是上市公司。控股公司旨在控制其他公司经营,

分时 / 日K / 周K / 月K

你现在选的是"分时",这就是当天分钟级走势。

如果切换:

  • 日K → 每根K线代表1天

  • 周K → 每根代表1周

  • 月K → 每根代表1个月

真正反映公司几年发展的是:

  1. 营业收入(3--5年)

  2. 净利润(是否持续盈利)

  3. 经营现金流

  4. 股本变化(是否频繁增发)

  5. 主营业务是否变化

  6. 控股股东是否变更

适合判断行业地位。

核心指标:

  1. 营业收入(Revenue)

    最直接的规模指标。

  2. 总资产

  3. 市值(Market Cap)

如果是实体经营能力比较,优先用"营业收入"。

如果是资本市场影响力,用"市值"。

但它们分别属于:

  • 逐分量乘:代数运算

  • dot:内积

  • cross:外积(仅 3D)

a * b

dot(a,b)

cross(a,b)

什么必须这样做?

因为:

如果不减去轴向分量,

在 3D 中你得到的就不是纯圆周方向,

而会掺杂轴向成分。

Radial 的定义是:

T ⟂ axis

T ⟂ radius

这正是:

叉乘的几何意义。

更压缩版本(你熟悉的线性代数表达)

给定单位轴 a:

复制代码

r = (I - a aᵀ) P

T = normalize(a × r)

其中:

I - a aᵀ = 投影到垂直 a 的平面矩阵。

这里的 r 可以理解为"半径向量(radius vector)",但更精确一点说:

r 是"从旋转轴到当前点的径向向量"。

它不是物体的半径长度,而是:

当前点相对于轴的"横向位移向量"

为什么叫半径向量?

因为在极坐标里:

  • r 是径向方向

  • θ 是圆周方向

而 radial Tangent 要的不是 r,而是:

T = a × r

也就是:

绕轴的圆周方向(θ 方向)


所以关系是:

r = 轴到点的径向向量

T = 轴 × r = 圆周切线方向

在 Radial 模式下,可以这样建模:

  • P:当前着色点的位置(在某个几何空间中)

  • 原点:该空间的原点

  • axis:该空间中的某个主轴(X / Y / Z)

然后:

r = P − axis * dot(P, axis)

这个 r 的几何意义就是:

从"该空间原点处的旋转轴"指向当前点的径向向量(去掉轴向分量后的)

如果这个空间是对象空间(常见情况),那就是:

  • 点是对象空间下的点

  • 原点是对象空间原点

  • 轴是对象空间的 X/Y/Z

那么 radial 的中心就是:

对象空间原点处那条轴线。

关键补一句:

如果你把物体整体平移(对象变换),

radial 的方向不会因为世界坐标改变而乱掉,

因为它是相对于对象空间定义的。

如果你移动对象但方向不变,

说明它不是锁世界空间。

你的场景光照是 HDR 环境或 Viewport shading 的间接影响,

并不是完全无光。

更重要的是:

面部比例和曲率变化足够自然,

哪怕没有高光模型,也能读出体积。

头发渲染中常用"两套法线"(通常指顶点法线与基于UV计算的副切线/切线法线)来模拟各向异性高光,常基于Kajiya-Kay模型或类似算法。具体实现为:一套法线保持片面原有形态,另一套基于头发UV走向(U轴为切线,V轴为副切线)计算得出,通过叉乘法线和光线产生柱状高光,使头发呈现出真实、圆柱体的细腻质感。

你要的是"可控的观感结构",而标准 BRDF 组合主要保证"能量守恒 + 统计意义上的真实"

相关推荐
西岸行者12 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意12 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码12 天前
嵌入式学习路线
学习
毛小茛12 天前
计算机系统概论——校验码
学习
babe小鑫12 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms12 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下12 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。12 天前
2026.2.25监控学习
学习
im_AMBER12 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J12 天前
从“Hello World“ 开始 C++
c语言·c++·学习