Unity ShaderGraph实现伪室内透光效果:低开销打造风格化场景氛围
2026/7/9 21:13:28 网站建设 项目流程

1. 项目概述与核心思路

最近在做一个风格化场景的项目,美术同学想实现一种“伪室内”的效果,简单来说,就是让一个在户外的建筑模型,看起来像是内部有灯光透出来,营造出窗户后面有房间、有生活的感觉。这种效果在赛博朋克、低多边形或者一些需要快速搭建氛围的场景里特别有用,能极大地提升场景的叙事感和细节丰富度。如果用传统建模方式去真的搭建室内,工作量巨大,而且对性能也是不小的负担。所以,我们决定用ShaderGraph在Unity里通过着色器“骗”过玩家的眼睛。

这个“伪室内”效果的核心思路,其实就是利用一张或几张表现室内场景的贴图(我们称之为“室内贴图”或“窗户贴图”),通过着色器技术,将其“贴”到建筑模型的窗户、门洞等镂空部分,并配合光照、模糊、动态等处理,模拟出从内部透光的效果。它本质上是一种基于屏幕空间或模型UV空间的“贴花”与“光照模拟”技术的结合。我花了几天时间研究和调试,最终用ShaderGraph搭建了一套比较稳定且效果可控的方案,不仅实现了静态的室内透光,还能模拟简单的动态(比如灯光闪烁、电视屏幕变化),性能开销极低。下面我就把这套方案的完整设计思路、节点连接、参数调试以及踩过的坑,毫无保留地分享出来。

2. 效果核心原理与方案选型

在动手搭建ShaderGraph之前,我们必须先想清楚要“骗”过眼睛,需要解决哪几个关键问题。这决定了我们整个着色器的架构。

2.1 效果拆解:我们需要模拟什么?

一个看起来真实的“室内透光”效果,通常包含以下几个视觉要素:

  1. 发光源:窗户区域本身应该作为光源,向外发射光线。这意味着窗户部分的亮度要远高于周围墙壁,并且能照亮附近的环境(即便很微弱)。
  2. 室内内容:透过窗户,应该能看到室内的景象,比如墙壁、家具的剪影、发光的灯具等。这部分内容需要有合理的透视和细节。
  3. 光线衰减与氛围:光线从室内穿过玻璃到室外,会有衰减。靠近窗户边缘的地方可能更亮,中心区域则能看清更多室内细节。同时,玻璃本身可能带有颜色、污渍或反射。
  4. 动态元素:真实的室内灯光可能闪烁(如老式日光灯)、电视屏幕会变化、有人影掠过。加入简单的动态能极大提升真实感。

2.2 技术方案对比与选型

基于以上要素,有几种常见的技术路径:

  • 方案A:使用Emissive自发光材质这是最直接的方法,给窗户部分一个高强度的自发光材质。优点是简单、性能好。但缺点非常明显:它只是一个纯色或简单纹理的发光板,完全没有室内空间的纵深感和细节,看起来非常假,像一块广告牌。

  • 方案B:在模型后方放置一个带室内贴图的Quad在建筑模型每个窗户的后面,真正放置一个带有室内贴图的平面。通过调整平面的位置和缩放,可以模拟出不错的透视效果。这种方法效果最好,最灵活。但缺点同样突出:每个窗户都需要一个额外的Draw Call和面片,管理复杂,性能随窗户数量线性增长,不适合大规模使用。

  • 方案C:使用Shader进行UV空间映射(本项目方案)这是我们最终选择的方案。其核心思想是:将室内贴图“投射”到模型特定的UV区域(即窗户部分)

    1. 遮罩:我们需要一张遮罩贴图(Mask Texture),来精确界定模型的哪些部分是窗户(白色),哪些部分是墙壁(黑色)。这张图通常可以在建模软件中烘焙,或者用模型的第二套UV配合程序化噪声生成。
    2. 映射:为室内贴图设计一套独立的UV坐标。这套坐标需要解决一个问题:如何让一张平面的贴图,在窗户这个“洞”里看起来有透视感?这里我们采用类似“视差映射”的简化思想,或者更简单地,使用一个与窗户形状匹配的、经过透视变换的UV。
    3. 合成:将采样到的室内贴图颜色,与模型本身的基础颜色(墙壁颜色)进行混合。混合的系数就是遮罩贴图的强度。同时,室内贴图的亮度贡献到自发光通道,实现发光效果。

为什么最终选择方案C?因为它完美平衡了效果、性能和灵活性。一张精心绘制的室内贴图可以包含丰富的细节;通过Shader计算,一个材质球就能处理整个建筑模型上所有的窗户,Draw Call不变;通过参数调节,可以轻松控制亮度、颜色、动态效果;而且完全可以与Unity的全局光照系统(如Baked GI)结合,让这些“伪光源”真正参与到场景光照中。接下来,我们就进入ShaderGraph,一步步实现它。

3. ShaderGraph核心节点网络搭建

打开Unity,创建一个新的ShaderGraph(URP或Built-in管线均可,节点名称可能略有差异,但逻辑相通)。我以URP为例进行说明。我们的Graph最终会有几个主要的属性块:室内贴图、遮罩贴图、UV变换参数、发光参数等。

3.1 基础颜色与遮罩混合

首先,我们建立最基础的通道:将室内贴图通过遮罩,“贴”到模型上。

  1. 创建属性

    • BaseColor(Color):建筑墙壁的基础颜色。
    • InteriorTexture(Texture2D):那张表现室内场景的贴图。建议使用一张横向的、带有透视感的房间角落贴图,这样适用于大多数窗户。
    • MaskTexture(Texture2D):黑白遮罩贴图。白色区域代表窗户。
    • BaseMap(Texture2D):可选,墙壁的纹理贴图。
  2. UV处理

    • UV节点连接到Sample Texture 2D节点采样BaseMapMaskTexture。这里通常使用模型的主UV集(UV0)。
    • 对于InteriorTexture,我们不能直接使用模型UV,否则贴图会拉伸变形。我们需要一套独立的UV变换
      • 添加Tiling And Offset节点。创建属性Interior_Tiling(Vector2) 和Interior_Offset(Vector2)。
      • 将模型的UV节点连接到Tiling And Offset节点的UV输入,将Interior_TilingInterior_Offset属性连接到对应输入。这样,美术同学可以通过调整Tiling(缩放)和Offset(偏移),来让室内贴图的内容适配窗户的视野范围。例如,调整Offset的X值,可以模拟“看”向房间的不同角度。
  3. 采样与混合

    • 用处理后的UV去采样InteriorTexture
    • 使用Lerp(线性插值)节点进行混合。将BaseColor(或采样BaseMap的结果)连接到A端口,将采样到的InteriorTexture颜色连接到B端口。
    • 将采样MaskTexture红色通道(通常用来存储遮罩)连接到Lerp节点的T(插值系数)端口。这样,遮罩为0(黑色)的地方显示墙壁颜色,遮罩为1(白色)的地方显示室内贴图颜色。
    • 将这个Lerp的结果输出给主节点的Base Color

至此,一个静态的、基础的伪室内贴图效果就完成了。你可以通过调节遮罩贴图和室内贴图的UV,让室内景象出现在正确的窗户位置。

3.2 自发光与光照增强

静态贴图还不够,我们需要让它“发光”。

  1. 创建发光强度属性Emission_Intensity(Float), 可以设置一个较大的默认值,比如5或10。
  2. 计算发光颜色:将采样到的InteriorTexture颜色与Emission_Intensity相乘,得到一个高亮度的颜色。这里可以再做一层处理,比如将颜色先转换为灰度,再乘以一个Emission_Color属性,这样可以单独控制发光的色相和强度,更加灵活。
  3. 遮罩控制发光区域:将上一步得到的高亮度颜色,再次与MaskTexture的强度(可以是RGB通道的某种计算,如(R+G+B)/3,或者直接用R通道)相乘。确保只有窗户区域在发光。
  4. 输出:将最终计算得到的发光颜色,连接至主节点的Emission输入口。

重要提示:在URP中,要使自发光影响周围环境(屏幕空间光照、Bloom后处理),需要确保材质的Surface Type设置为Transparent(如果窗户部分是透明的)或Opaque但勾选了Add Precomputed Lighting,并且需要在后处理Volume中启用Bloom效果。发光的强度需要通过Emission_Intensity和Bloom的阈值配合调试。

3.3 模拟透视与深度感(进阶)

为了让平面的贴图看起来更有立体感,我们可以模拟一些简单的透视效果。

  1. 视差偏移(简易版)

    • 原理:根据视角方向,轻微偏移室内贴图的UV,模拟视线穿过窗户看到室内不同部分的效果。虽然不如真正的视差贴图精确,但开销极小,能增加生动性。
    • 实现:
      • 获取视角方向:使用View Direction节点,并转换为切线空间(Transform节点,World to Tangent)。
      • 我们只关心XY方向(即平行于窗户平面的偏移)。将切线空间的View Direction的X和Y分量提取出来。
      • 用这两个分量,分别乘以一个很小的系数属性Parallax_Strength(例如0.05),再乘以遮罩强度(确保只有窗户区域有偏移),得到UV偏移量。
      • 将这个偏移量加到InteriorTexture的UV上,再去做采样。
    • 效果:当摄像机移动时,室内的画面会随着视角有轻微的滑动,仿佛你真的在从不同角度窥视室内,立体感瞬间提升。
  2. 边缘光与衰减

    • 现象:真实窗户的透光,在窗户框边缘附近可能更亮(光线衍射),或者因为玻璃污渍导致中心与边缘亮度不一。
    • 实现:
      • 我们可以利用遮罩贴图本身。对MaskTexture采样结果进行一些处理。例如,使用Smoothstep节点:Smoothstep(0.2, 0.8, Mask.r)。这样可以将遮罩的0-1过渡区域进行平滑,并压缩中间亮部区域的范围,使得窗户边缘的过渡带产生一个亮度变化,可以用来调制发光强度。
      • 或者,单独采样一张用于控制亮度衰减的贴图(Ramp图),将其与遮罩结合,创造出中心亮、边缘暗,或边缘亮、中心暗等不同效果。

3.4 添加动态效果

静态的室内看起来还是像一张画。我们加入一些简单的动态。

  1. 灯光闪烁

    • 使用一个简单的三角函数(Sine Time节点)来模拟周期性闪烁。Sine Time本身输出的是基于时间变化的正弦波。
    • 创建一个属性Flicker_Speed用于控制速度,Flicker_Intensity用于控制闪烁强度范围。
    • 计算式:Flicker = 1 + (sin(Time * Flicker_Speed) * Flicker_Intensity)。这样Flicker会在1±Intensity之间波动。
    • 将计算出的Flicker值,乘到之前计算好的发光颜色上。注意,为了更真实,可以只让室内贴图中代表“光源”(如灯泡、台灯)的特定颜色通道(比如高亮白色部分)参与闪烁,这需要更精细的贴图规划。
  2. 电视屏幕噪波

    • 假设我们的室内贴图中有一块区域是电视屏幕。
    • 我们可以使用一个动态的噪声图。ShaderGraph提供了Gradient Noise节点。
    • Time节点与一个速度属性相乘,加到噪声UV的某个分量上,让噪声动起来。
    • 然后,将动态噪声与电视屏幕区域的颜色混合(例如Screen混合模式)。电视屏幕区域同样需要一张遮罩来界定(可以利用室内贴图的Alpha通道,或者第二张遮罩图)。
  3. 动态UV偏移(模拟人影)

    • 这是更高级的效果。原理是让室内贴图的一部分(比如代表人影的深色区域)随时间缓慢移动。
    • 我们可以准备两张室内贴图:一张基础图,一张带有人影叠加层的图。或者在一张贴图上,人影位于特定颜色通道。
    • 使用一个缓慢变化的Time信号去驱动Interior_Offset的微小变化,但只应用在代表人影的颜色信息上。这通常需要更复杂的节点网络,涉及颜色通道分离和条件混合。

4. 参数调节与美术资源制作要点

Shader搭建好了,效果的好坏七分靠Shader,三分靠美术资源。这里分享一些制作贴图和调节参数的心得。

4.1 遮罩贴图制作

遮罩贴图是效果的基石,必须精准。

  • 精度:尽量使用高精度的贴图(如1024x1024),确保窗户边缘清晰锐利,没有锯齿。模糊的遮罩边缘会导致发光区域扩散到墙上,显得很脏。
  • 绘制方式:在建模软件(如Blender, 3ds Max)中,将窗户部分的面单独分一个材质ID或顶点色,然后烘焙出一张纯黑白的遮罩贴图。这是最准确的方法。
  • 备用方案:如果模型没有分好,可以在Unity里利用模型的第二套UV(UV1),配合程序化噪声来生成。但可控性较差。

4.2 室内贴图设计

这张图决定了“室内有什么”。

  • 透视:强烈建议使用带有两点透视的室内场景照片或手绘素材。视角略微俯视或平视,这样贴在窗户上时,透视关系才显得自然。纯正面的视角会显得很平。
  • 明暗对比:贴图本身应有明确的明暗关系。光源处(灯具、窗户对面)最亮,角落、家具背面要暗。这样在乘以发光强度后,高亮部分会更突出,暗部也不会过曝,保留细节。
  • 色彩:色调要统一。暖光(钨丝灯)用橙黄色调,冷光(日光灯、电视)用蓝白色调。可以在Shader里用Emission_Color属性整体染色,但贴图本身的色调是基础。

4.3 Shader参数调试清单

把关键参数列出来,方便调试时参考:

参数名功能典型值/范围调试技巧
Interior_Tiling缩放室内贴图(1,1) 开始调节值>1会放大室内细节(看得更近),值<1会缩小(看得更远)。用于匹配窗户大小和预期的观看距离。
Interior_Offset偏移室内贴图(0,0) 开始调节微调X值可以“环顾”室内左右,微调Y值可以调整视线高低。这是调整构图的关键。
Emission_Intensity发光强度5 - 20白天场景调低,夜晚场景调高。需要与场景的后处理Bloom阈值配合调试。强度太高会导致Bloom过曝成一片白光。
Emission_Color发光颜色白色 (1,1,1,1)用于统一改变室内光线的色温。例如,设置为淡黄色(1,0.9,0.7,1)模拟暖光灯。
Parallax_Strength视差强度0.0 - 0.1从0开始慢慢增加,直到摄像机移动时有轻微但可察觉的滑动感即可。太大反而会穿帮。
Flicker_Speed闪烁速度1 - 10模拟日光灯故障可以快一些(8-10),模拟烛光可以慢一些(1-3)。
Flicker_Intensity闪烁强度0.0 - 0.3控制亮度波动的幅度。0.1-0.2的微闪比较自然,0.3以上会显得灯光接触不良。

调试时,建议在目标运行平台(如PC、主机)的最终画质下进行,因为移动平台和PC平台的Bloom、色调映射等后处理效果差异很大。

5. 性能优化与常见问题排查

这个效果虽然不复杂,但在大规模使用(如整个城市建筑)时,仍需注意性能。

5.1 性能优化点

  1. 纹理合并:如果有多栋建筑使用不同的室内贴图,尽量将多张小图合并成一张大图集(Texture Atlas)。这样可以将多次采样合并为一次,减少纹理采样指令。遮罩贴图也可以合并。
  2. Shader变体管理:如果你的ShaderGraph包含很多可选的动态效果(如闪烁、噪声),通过Shader Keywords来控制。不要在一个Shader里包含所有功能,然后靠参数设为0来关闭,这可能导致不必要的变体和性能开销。应该为“静态版”和“动态版”创建不同的材质实例,或使用Shader Variant分支。
  3. LOD支持:为远处建筑使用简化版本的Shader。例如,在远处可以完全关闭视差效果、动态效果,甚至降低室内贴图的分辨率(通过Mipmap)。
  4. 剔除优化:确保建筑模型使用了正确的遮挡剔除(Occlusion Culling)。因为我们的窗户是自发光,如果被其他建筑挡住,就不应该被渲染。

5.2 常见问题与解决方案

这里记录了几个我在测试中遇到的具体问题及解决方法。

问题现象可能原因解决方案
窗户发光边缘有锯齿或闪烁1. 遮罩贴图分辨率不足。
2. 抗锯齿(MSAA/TAA)与Alpha Test或Alpha Blend的边界处理问题。
1. 提高遮罩贴图精度,或开启其导入设置的Generate Mip Maps并使用三线性过滤。
2. 尝试将材质的Alpha Clip Threshold稍微调高(如从0.5调到0.6),或改用Alpha Blend模式并调试混合模式。
Bloom效果下窗户光晕过大,糊成一片Emission_Intensity值过高,超过了后处理Volume中Bloom效果的Threshold(阈值)和Intensity(强度)的合理范围。1.优先降低材质Emission_Intensity,使其在不开Bloom时看起来亮度合适。
2. 然后调整Bloom的Threshold,确保只有最亮的部分(如灯具中心)能触发Bloom。
3. 最后用Bloom的Intensity控制光晕大小。三者需协同调试。
从某些角度观看,室内贴图“穿帮”或消失1. 视差强度(Parallax_Strength)设置过大。
2. 室内贴图UV偏移(Interior_Offset)导致有效画面移出视野。
3. 模型法线方向错误,导致背面剔除。
1. 减小Parallax_Strength
2. 重置或调整Interior_Offset
3. 检查模型法线,或在Shader中将Cull Mode设置为Off(双面渲染),但会略微增加开销。
动态闪烁效果不自然,像机械开关使用了简单的Sine Time,波形过于规则完美。引入噪声进行调制。例如:Flicker = 1 + (sin(Time * Speed + Noise) * Intensity),其中Noise是一个缓慢变化的低频率噪声值。或者,使用Random Range节点配合时间取整,模拟不规则的闪烁间隔。
在移动平台上帧率下降明显1. 使用了高分辨率纹理且未压缩。
2. 过于复杂的动态计算(如每帧采样多次噪声)。
3. 没有使用合批(Batch)。
1. 为移动平台使用ASTC压缩格式的纹理,并适当降低分辨率。
2. 简化或关闭动态效果。考虑使用顶点动画或纹理动画替代每像素的复杂计算。
3. 确保建筑模型材质相同,且符合Unity的静态/动态合批条件。

最后,再分享一个调试小技巧:在Scene视图中,将渲染模式切换到OverdrawShaded Wireframe模式,可以直观地看到Shader的渲染复杂度和覆盖范围,对于优化性能非常有帮助。伪室内效果是一个典型的“视觉把戏”,它的成功在于对细节的把握。多观察现实世界中窗户透光的现象,比如傍晚时分楼宇的灯光,不断调整你的遮罩、贴图和Shader参数,直到它看起来“感觉对了”为止。这个方案已经在我们好几个项目中成功应用,从赛博朋克的霓虹公寓到中世纪小镇的烛光橱窗,只需要更换贴图和调整参数,就能快速产出令人信服的效果,性价比极高。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询