Unity ShaderGraph角膜折射节点:实现真实眼睛渲染的核心技术 📅 2026/8/9 1:48:15 1. 项目概述为什么我们需要一个专门的角膜折射节点在实时渲染领域眼睛的渲染一直是一个“圣杯”级别的挑战。它看起来简单一个球体加个纹理但想让它看起来真实、有生命感却需要处理极其复杂的光学现象。我们平时看到的眼睛其晶莹剔透、深邃有神的感觉很大程度上来自于光线在角膜也就是眼球最外层的透明部分和内部介质主要是房水之间发生的折射。这种折射会扭曲我们看到的虹膜和瞳孔让它们看起来不是简单地贴在眼球表面而是“陷”在眼球内部的。在Unity的ShaderGraph中Cornea Refraction Node角膜折射节点就是为了精确模拟这一物理现象而生的专用工具。它不是一个通用的折射节点而是专门为模拟人眼或类似生物眼睛的角膜折射而设计的。简单来说它回答了一个核心问题“当一束光或者说玩家的视线以某个角度看向眼球表面时它最终会‘看到’虹膜平面上的哪个点”这个节点在角色渲染尤其是高精度数字人、电影级CG角色或者追求极致真实感的游戏角色中至关重要。没有它眼睛看起来就像个塑料球有了它眼睛才能拥有那种“灵魂之窗”的生动感。我处理过不少项目角色其他部分都做得非常精致但眼睛一渲染就“死气沉沉”问题往往就出在忽略了或错误处理了角膜折射。2. 核心原理拆解从斯涅尔定律到对象空间计算要理解这个节点我们不能停留在“它会扭曲一下”的层面得深入其背后的物理和数学。2.1 物理基础斯涅尔定律与眼睛模型角膜折射遵循斯涅尔定律n₁ * sinθ₁ n₂ * sinθ₂。其中n是介质的折射率θ是光线与法线的夹角。对于眼睛n₁ (外部介质)通常是空气折射率约为1.0。n₂ (内部介质)角膜和房水折射率约为1.333到1.38。Unity节点默认使用1.333这是水的折射率也是一个常用的近似值。角膜表面不是一个完美的球面但通常我们用一个球面或椭球面来近似其表面法线就是Cornea Normal OS输入。当视线View Ray从空气射向角膜表面时会发生折射改变方向然后这个折射后的光线会继续在眼内传播直到击中虹膜平面Iris Plane。这个平面你可以理解为虹膜所在的位置它平行于角膜的切平面但向眼球内部偏移了一段距离这个距离就是Iris Plane Offset输入。2.2 节点的工作流程与空间概念这个节点的核心计算全部发生在对象空间Object Space。这是理解其输入输出的关键。对象空间是以3D模型自身为原点的坐标系不随模型移动、旋转、缩放而改变相对于模型本身。这样做的好处是计算稳定、独立于物体的世界变换。节点的计算流程可以概括为以下几步输入准备获取当前着色片元在对象空间中的位置Position OS和视线方向View Direction OS。同时提供该位置处角膜的法线方向Cornea Normal OS和折射率Cornea IOR。折射计算在对象空间中根据斯涅尔定律使用输入的视线方向、角膜法线和折射率计算出光线进入眼球后的折射方向。平面求交沿着这个折射方向计算它与虹膜平面的交点。虹膜平面由角膜表面的一个参考点通常就是Position OS在平面上的投影点和角膜法线定义并沿着法线负方向指向眼球内部偏移Iris Plane Offset的距离。输出结果计算出这个交点在对象空间中的坐标作为RefractedPositionOS输出。所以这个节点的输出本质上是一个UV坐标的“查找位置”。我们不会直接用这个坐标去显示颜色而是用它来采样虹膜纹理。原本我们直接用模型UV或屏幕位置采样虹膜现在则用这个经过折射扭曲后的位置去采样从而在视觉上产生虹膜被角膜扭曲的效果。注意View Direction OS的获取是关键。对于常规渲染它通常来自View Direction节点并转换为对象空间通过Transform节点。在光线追踪Ray Tracing或某些后期效果中它可能来自上一帧或上一反弹的计算结果。确保这个方向是从摄像机指向当前片元的方向并且在对象空间下表示。3. 节点接口深度解析与参数设置官方文档给出了接口但每个参数背后的“潜规则”和设置技巧才是实战中的干货。3.1 输入端口详解Position OS (Vector3)是什么当前正在着色的片元可以粗略理解为像素对应的模型表面点在对象空间中的位置。怎么来通常直接连接Position节点的Object输出。这是最标准、最不容易出错的做法。为什么这是所有计算的空间基准。折射的起点就在这里。View Direction OS (Vector3)是什么从摄像机或上一光线反弹点指向当前Position OS点的方向向量在对象空间中表示。这个向量需要是归一化的长度为1。怎么来这是最容易出错的地方。标准流程是使用View Direction节点获取世界空间的视线方向。使用Transform节点将方向从World空间转换到Object空间。核心技巧务必检查方向是否正确。一个简单的调试方法将View Direction OS直接作为颜色输出例如连接到Base Color。当你旋转摄像机时模型颜色应该平滑变化。如果颜色乱跳或不对说明空间转换有问题。Cornea Normal OS (Vector3)是什么角膜表面在Position OS点处的法线方向在对象空间中表示。同样需要是归一化的。怎么来对于简单的球体模型可以直接使用Normal Vector节点的Object输出。但对于更精确的人眼模型角膜区域可能是一个独立的、略微凸起的几何体这个法线应该来自高精度建模时生成的法线贴图或顶点法线。直接使用模型法线通常是个不错的起点。重要性法线的精度直接决定了折射效果的准确性。一个平滑的球体法线会产生规则的扭曲而一个带有细微凹凸的法线例如来自法线贴图可以模拟角膜表面的微观不规则让折射光产生更复杂的焦散效果增加真实感。Cornea IOR (Float)是什么角膜的折射率。默认值1.333水的折射率。怎么设置1.333是一个安全的默认值。如果你想追求更高的物理精度可以设置为1.376人类角膜的典型值。稍微提高这个值比如到1.38会使折射效应更强虹膜看起来“陷”得更深眼睛显得更湿润。这是一个重要的艺术控制参数。Iris Plane Offset (Float)是什么虹膜平面相对于角膜表面的偏移距离。可以理解为虹膜在眼球内部的“深度”。怎么设置默认值0.02假设模型单位是米即2厘米。这个值需要与你的3D眼球模型的几何结构精确匹配。如果偏移太大折射光线可能无法与虹膜平面相交尤其是在视角很偏的时候导致采样出错通常表现为黑色或错误颜色。如果偏移太小折射效果会不明显。实操心得最佳实践是根据你的眼球模型来测量这个值。在3D建模软件中测量从角膜最前点到虹膜纹理所在球面的径向距离。将这个值以米为单位输入。对于标准人眼模型0.02~0.025是一个常见范围。3.2 输出端口与应用RefractedPositionOS (Vector3)是什么计算得到的、折射后视线与虹膜平面交点的对象空间坐标。怎么用这是用于采样纹理的坐标而不是最终颜色。通常的流程是获取这个位置在虹膜平面上的2D投影坐标这通常需要一些额外的计算取决于你的眼球UV是如何展开的。将这个2D坐标作为UV去采样你的虹膜纹理Albedo、虹膜法线贴图、虹膜高度贴图等。关键点这个坐标是对象空间的而纹理采样通常需要UV空间0-1范围。因此你需要根据眼球模型的UV映射方式将RefractedPositionOS转换到UV空间。对于中心在原点、轴向对齐的球体模型一种常见方法是使用这个位置的X和Z分量假设Y轴朝上经过一些缩放和偏移来模拟UV。4. 在ShaderGraph中的完整实现流程理论说再多不如直接上操作。下面是一个在URP通用渲染管线的ShaderGraph中实现基础角膜折射效果的步骤。4.1 准备工作与图形搭建创建ShaderGraph在URP项目中创建一个新的Lit或UnlitShader Graph。Lit更复杂但能集成PBR光照Unlit更简单适合专注于折射效果本身。构建输入框架添加Position节点设置空间为Object输出连接到Cornea Refraction节点的Position OS。添加View Direction节点连接到一个Transform节点将From设为WorldTo设为ObjectType设为Direction。将Transform的输出连接到Cornea Refraction的View Direction OS。添加Normal Vector节点设置空间为Object输出连接到Cornea Refraction的Cornea Normal OS。添加两个Float属性分别命名为_CorneaIOR和_IrisOffset设置默认值为1.333和0.02并连接到对应输入口。添加核心节点在节点面板中搜索“Cornea Refraction”将其拖入图中并按上述步骤连接好。4.2 从折射坐标到纹理采样这是最具技巧性的部分。RefractedPositionOS是一个3D坐标我们需要把它映射到虹膜纹理的2D UV上。假设我们的眼球模型是一个UV展开为类似“行星仪投影”的球体即从侧面看UV的U轴环绕球体V轴从顶部到底部。一种常见且有效的映射方法是将对象空间坐标视为方向向量RefractedPositionOS本身是从眼球中心对象空间原点指向虹膜交点的向量吗不完全是。对于中心在原点的球体虹膜平面上的点其Y坐标接近一个固定值-IrisOffset。我们可以用这个向量的X和Z分量来构造UV。构造UV使用Split节点将RefractedPositionOS拆分为X, Y, Z。忽略Y分量或者用它来做一些深度混合。使用Combine节点将X和Z重新组合成一个Vector2。这个(X, Z)向量代表了交点在水平面上的投影方向。我们需要将其归一化并转换到0-1范围。使用Arctangent2节点Atan2计算角度angle atan2(Z, X)。这个角度范围是(-π, π]。将角度从(-π, π]映射到(0, 1)U (angle / (2 * PI)) 0.5。对于V坐标我们可以用一个固定的值如果虹膜是完美的圆环或者根据交点离中心的距离来模拟。更简单的方法是直接使用模型原始的V坐标因为折射主要影响水平方向的扭曲。采样纹理将计算好的UV连接到Sample Texture 2D节点的UV输入并将纹理属性分配给虹膜贴图。4.3 与眼球其他部分的混合眼睛不只有虹膜还有巩膜眼白、角膜高光等。完整的眼睛着色器需要处理多个部分区域遮罩你需要一个遮罩纹理或基于模型UV/位置的阈值来区分虹膜区域、瞳孔区域和巩膜区域。通常虹膜区域是一个圆盘。应用折射UV仅在虹膜区域可能包括瞳孔使用由Cornea Refraction计算出的扭曲UV去采样虹膜纹理。在巩膜区域则使用模型原始的UV或另一套UV。集成高光与凹凸用同样的扭曲UV去采样虹膜的法线贴图为虹膜添加凹凸细节。角膜的高光镜面反射通常不应该使用折射后的法线而应该使用角膜表面的原始法线Cornea Normal OS因为高光发生在角膜表面而不是虹膜内部。瞳孔处理瞳孔通常是虹膜纹理的一部分一个黑色圆孔。折射同样会作用于它使其形状发生扭曲这增强了真实感。有时需要根据光照动态缩放瞳孔这个缩放操作应该在折射扭曲之后进行即在扭曲后的UV空间中对瞳孔区域进行径向缩放。一个简化的混合节点逻辑伪代码如下float irisMask step(distance(i.uv, irisCenter), irisRadius); // 简单圆形遮罩 float2 irisUV calculateRefractedUV(i.positionOS, i.viewDirOS, i.normalOS); float2 scleraUV i.uv; // 眼白使用原UV float3 irisColor tex2D(_IrisTex, irisUV).rgb; float3 scleraColor tex2D(_ScleraTex, scleraUV).rgb; float3 finalColor lerp(scleraColor, irisColor, irisMask);5. 常见问题、调试技巧与性能优化在实际项目中使用这个节点你肯定会遇到各种坑。下面是我踩过的一些坑和总结的技巧。5.1 常见问题排查表问题现象可能原因排查与解决方案虹膜区域全黑或颜色异常1.RefractedPositionOS计算错误导致UV越界。2.Iris Plane Offset值过大折射光线未与平面相交。3. 视线方向View Direction OS未归一化或空间错误。1.调试输出将RefractedPositionOS的X/Y/Z分量分别作为颜色输出检查其值是否在合理范围非0非极大。2.减小Offset逐步减小Iris Plane Offset值例如从0.02减到0.005看是否出现图像。3.检查方向输出View Direction OS作为颜色旋转摄像机颜色应平滑渐变。确保使用了Direction类型的Transform。折射效果不明显或没有1.Cornea IOR值太接近1.0。2.Cornea Normal OS输入是平坦的或方向错误。3. 虹膜UV映射逻辑错误抵消了折射效果。1.增加IOR将IOR从1.333提高到1.38或1.4观察效果变化。2.检查法线输出Cornea Normal OS作为颜色模型表面应显示正确的法线方向通常正面为蓝色。3.旁路测试暂时用Position OS的XZ直接映射为UV看虹膜能否正确显示。然后再接入折射计算对比差异。边缘视角下出现 artifacts撕裂、闪烁1. 在视角与表面近乎平行时折射计算可能不稳定。2. UV映射在极点附近出现奇点例如使用atan2时所有经线汇聚。1.钳制与平滑对计算出的折射向量或最终UV进行钳制。可以引入一个基于视角与法线夹角的淡化因子在边缘处减弱折射效果。2.改进UV映射考虑使用三平面投影Tri-planar Projection或其他更健壮的方法来从3D坐标生成UV避免极点问题。性能开销大Cornea Refraction节点内部涉及复杂的向量运算和平面求交每个像素都会调用。1.限制使用区域确保仅在最需要的地方虹膜像素使用该节点。使用严格的遮罩提前丢弃非虹膜像素的计算。2.简化模型对于移动平台或远处角色可以考虑使用简化的折射近似比如用一张折射贴图Refraction Map或简单的视差偏移来代替实时计算。与屏幕空间折射效果冲突如果同时使用了屏幕空间折射SSR或折射探针可能导致双重折射或视觉错误。1.区分对象对角膜这类高精度对象禁用其屏幕空间折射效果通过材质或渲染器设置。2.层级管理确保角膜渲染在透明队列并处理好与后面虹膜层的渲染顺序。5.2 高级技巧与优化建议使用自定义函数Custom Function封装如果你发现节点图过于复杂可以将整个折射UV计算逻辑从输入到输出UV封装成一个HLSL代码的Custom Function节点。这样不仅图面整洁而且可能带来微小的性能提升因为减少了节点间的数据传递开销。结合视差映射Parallax Occlusion Mapping对于追求极致细节的眼睛可以在折射的基础上对虹膜应用视差映射。用折射后的UV去采样虹膜高度图然后进行视差偏移可以产生惊人的立体感让虹膜的纹理看起来真正“凹凸”在表面之下。注意计算顺序先折射再在折射后的“视线”上进行视差偏移。动态IOR与湿润度你可以将Cornea IOR与一个表示眼睛湿润度或情绪的参数联动。湿润的眼睛例如含泪时角膜表面更光滑折射率也可能有细微变化通常更高让眼睛看起来更“亮”。通过脚本控制这个参数可以增加角色的表现力。多级细节LOD策略在角色LOD系统中为低模眼睛准备一个简化着色器。这个简化版可以用一个基于顶点法线的简单扭曲顶点偏移来模拟折射效果或者完全去掉折射只使用一个经过预扭曲在纹理制作时处理的虹膜贴图。这能有效节省远处大量角色的渲染开销。角膜折射节点的使用标志着角色渲染从“看起来像”到“看起来真实”的关键一步。它需要的不仅仅是连接节点更需要对光学原理、空间变换和着色器编程有深入的理解。开始时可能会被各种坐标转换和UV映射搞晕但一旦打通这个流程你会发现它为角色注入的生动感是无可替代的。我个人的经验是先从一个完美的球体模型开始实现基础效果确保折射计算本身正确无误然后再将这套逻辑迁移到复杂的人眼模型上并处理区域混合、高光等细节这样能更高效地定位和解决问题。