空间波动(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通道数值组合成最终像素色值
完结撒花~