自由学习记录(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 组合主要保证"能量守恒 + 统计意义上的真实"

相关推荐
小猪佩奇TONY2 小时前
OpenCL 学习(5)---- OpenCL 内核和内核参数
学习
01二进制代码漫游日记2 小时前
自定义类型:联合和枚举(一)
c语言·开发语言·学习·算法
非凡ghost3 小时前
小X分身APP(手机分身类工具)
android·windows·学习·智能手机·软件需求
weixin_458872613 小时前
东华复试OJ每日3题打卡·复盘82~84
学习
昌兵鼠鼠4 小时前
LeetCode Hot100 哈希
学习·算法·leetcode·哈希算法
旖旎夜光4 小时前
哈希(14)(下)
数据结构·c++·学习
2401_848009726 小时前
Redis零基础入门学习
数据库·redis·学习
如果你想拥有什么先让自己配得上拥有6 小时前
全等三角形的判定条件思考
学习·总结
zenpluck7 小时前
RTAB-Map学习记录(1)--论文阅读
c++·论文阅读·学习·机器人