刀光刀气空间波动效果

空间波动(DistortionMesh)效果实现

Github源码传送门————>SlashDistortion

Bilibili教程传送门————>小祥带你实现刀气空间波动效果

实机

整体思路

现实世界中我们看到的热浪(烧开水,火箭尾焰等)导致的空间波动,物理原理主要是温度升高引起空气折射率的变化,也就是光在空气中传播路径被弯曲了,导致我们透过热浪看后面的物体会出现波动感。游戏中常见的刀光导致的空间波动也是类似的效果,当然我们显然不用和现实一样考虑空气折射率以及光的传播,这时候就该游戏开发糊弄学发力了,实际上我们只需要对渲染结束后的全屏游戏画面进行扰动,也就是偏移采样就能达到类似的效果。所以第一步我们需要生成一张全屏贴图以供采样,这张贴图需要同时包含透明物体和非透明物体,但不包含我们的刀光mesh,所以需要创建自定义Pass控制渲染顺序并进行渲染,设置为全局贴图后续在shader中进行采样。第二步借助扰动贴图偏移screenUV,使用偏移后的坐标对渲染好的全屏贴图进行采样然后绘制刀光mesh即可。

SlashDistortionRendererFeature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private CopyColorRenderPass copyColorRenderPass;
private SlashDistortionRenderPass slashDistortionRenderPass;

public override void Create()
{
copyColorRenderPass = new CopyColorRenderPass();
copyColorRenderPass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;

slashDistortionRenderPass = new SlashDistortionRenderPass();
slashDistortionRenderPass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
}

public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(copyColorRenderPass);
renderer.EnqueuePass(slashDistortionRenderPass);
}
  • CopyColorRenderPass这个pass用来渲染包含所有内容的全屏贴图,所以渲染顺序为设置为在渲染透明物体之后
  • SlashDistortionRenderPass这个pass用来渲染刀光mesh,添加到RenderPasses需要放在CopyColorRenderPass之后

CopyColorRenderPass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class CopyColorRenderPass : ScriptableRenderPass
{
private Material blitMaterial;

private class PassData
{
public TextureHandle source;
}

public CopyColorRenderPass()
{
if (GraphicsSettings.TryGetRenderPipelineSettings<UniversalRenderPipelineRuntimeShaders>(out var settings))
{
blitMaterial = CoreUtils.CreateEngineMaterial(settings.coreBlitPS);
}
}

public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();

RenderTextureDescriptor desc = cameraData.cameraTargetDescriptor;
desc.depthStencilFormat = GraphicsFormat.None;
desc.msaaSamples = 1;
desc.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
TextureHandle tempColor = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, "_CameraColorTexture", true);

using (var builder = renderGraph.AddRasterRenderPass("CopyColor", out PassData passData))
{
passData.source = resourceData.activeColorTexture;

builder.AllowPassCulling(false);
builder.SetRenderAttachment(tempColor, 0);
builder.UseTexture(passData.source);

builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), blitMaterial, 0);
});

builder.SetGlobalTextureAfterPass(tempColor, Shader.PropertyToID("_CameraColorTexture"));
}
}
}
  • 创建一张贴图Blit当前相机画面(activeColorTexture)到这张贴图
  • 数据写到贴图之后通过SetGlobalTextureAfterPass设置为全局贴图供shader使用

SlashDistortionRenderPass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class SlashDistortionRenderPass : ScriptableRenderPass
{
private class PassData
{
public RendererListHandle rendererListHandle;
}

public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Slash Distortion", out var passData))
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
SortingCriteria sortFlags = cameraData.defaultOpaqueSortFlags;
RenderQueueRange renderQueueRange = RenderQueueRange.transparent;
FilteringSettings filterSettings = new FilteringSettings(renderQueueRange, ~0);

ShaderTagId shaderTagId = new ShaderTagId("SlashDistortion");

DrawingSettings drawSettings = RenderingUtils.CreateDrawingSettings(shaderTagId, renderingData, cameraData, lightData, sortFlags);

var rendererListParameters = new RendererListParams(renderingData.cullResults, drawSettings, filterSettings);

passData.rendererListHandle = renderGraph.CreateRendererList(rendererListParameters);

UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
builder.UseRendererList(passData.rendererListHandle);
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
builder.SetRenderAttachmentDepth(resourceData.activeDepthTexture, AccessFlags.Write);

builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
context.cmd.DrawRendererList(data.rendererListHandle);
});
}
}
}
  • 创建渲染队列,渲染所有ShaderTagId为SlashDistortion的物体

SlashDistortion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
half4 Frag (Varyings input) : SV_Target
{
float mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, input.uv).a;
mask = pow(mask, 2.0);

float2 scrollUV = input.uv + _ScrollSpeed.xy * _Time.y;
float2 distortion = SAMPLE_TEXTURE2D(_DistortionTex, sampler_DistortionTex, scrollUV);
distortion = (distortion * 2 - 1) * _DistortionStrength * mask;

float2 screenUV = GetNormalizedScreenSpaceUV(input.positionCS);
float2 distortedUV = screenUV + distortion;

float aberration = smoothstep(_AberrationThreshold, 1.0, length(distortion)) * _AberrationStrength * mask;
float2 aberrationOffset = distortion * aberration;

float r = SAMPLE_TEXTURE2D(_CameraColorTexture, sampler_CameraColorTexture, distortedUV + aberrationOffset).r;
float g = SAMPLE_TEXTURE2D(_CameraColorTexture, sampler_CameraColorTexture, distortedUV).g;
float b = SAMPLE_TEXTURE2D(_CameraColorTexture, sampler_CameraColorTexture, distortedUV - aberrationOffset).b;

return half4(r, g, b, mask);
}
  • _MaskTex提供透明度值同时作为扰动强度参与扰动计算使得边缘扰动平滑
  • _DistortionTex扰动贴图提供偏移量
  • scrollUV是给定一个流动速度(_ScrollSpeed)让扰动贴图在UV空间中随时间移动,实现噪声效果流动
  • GetNormalizedScreenSpaceUV方法获取当前像素对应屏幕UV,然后加上扰动值对屏幕贴图进行采样
  • aberrationOffset在偏移方向再添加一些色偏效果,偏移越多色偏效果越强,分别采样偏移后的RGB通道数值组合成最终像素色值

完结撒花~

pid:133498741


刀光刀气空间波动效果
https://baifabaiquan.cn/2025/08/15/SlashDistortion/
作者
白发败犬
发布于
2025年8月15日
许可协议