Created
December 14, 2019 08:32
-
-
Save hadashiA/4e37d2f42dd376d50f57ebd6311121eb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Shader "Hidden/Heipu/Metaball" | |
{ | |
Properties | |
{ | |
_MainTex ("Main Tex", 2D) = "white" {} | |
_ColorRamp ("Color Ramp", 2D) = "white" {} | |
_Threshold ("Threshold", float) = 0.04 | |
_LineLength ("Line Length", float) = 0.5 | |
_Intensity ("Intensity", float) = 1 | |
} | |
SubShader | |
{ | |
// No culling or depth | |
Cull Off ZWrite Off ZTest Always | |
HLSLINCLUDE | |
// Required to compile gles 2.0 with standard srp library | |
#pragma prefer_hlslcc gles | |
#pragma exclude_renderers d3d11_9x | |
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl" | |
uniform TEXTURE2D(_MainTex); | |
uniform TEXTURE2D(_MetaballSource); | |
uniform SAMPLER(sampler_MainTex); | |
uniform float4 _MainTex_TexelSize; | |
struct Attributes | |
{ | |
float4 positionOS : POSITION; | |
float2 uv : TEXCOORD0; | |
}; | |
struct Varyings | |
{ | |
float2 uv : TEXCOORD0; | |
float4 positionCS : SV_POSITION; | |
}; | |
half3 Sample(float2 uv) | |
{ | |
return SAMPLE_TEXTURE2D(TEXTURE2D_ARGS(_MainTex, sampler_MainTex), uv); | |
} | |
half3 SampleBox(float2 uv, float delta) | |
{ | |
float4 offset = _MainTex_TexelSize.xyxy * float2(-delta, delta).xxyy; | |
half3 s = | |
Sample(uv + offset.xy) + Sample(uv + offset.zy) + | |
Sample(uv + offset.xw) + Sample(uv + offset.zw); | |
return s * 0.25f; | |
} | |
Varyings PassVertex(Attributes input) | |
{ | |
Varyings output = (Varyings)0; | |
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); | |
output.positionCS = vertexInput.positionCS; | |
output.uv = input.uv; | |
return output; | |
} | |
ENDHLSL | |
Pass | |
{ | |
Name "DownSampling" | |
HLSLPROGRAM | |
#pragma vertex PassVertex | |
#pragma fragment DownSamplingPassFragment | |
half4 DownSamplingPassFragment(Varyings input) : SV_Target | |
{ | |
return half4(SampleBox(input.uv, 1), 1); | |
} | |
ENDHLSL | |
} | |
Pass | |
{ | |
Name "UpSampling" | |
Blend [_SrcBlend] [_DstBlend] | |
HLSLPROGRAM | |
#pragma vertex PassVertex | |
#pragma fragment UpSamplingPassFragment | |
half4 UpSamplingPassFragment(Varyings input) : SV_Target | |
{ | |
return half4(SampleBox(input.uv, 0.5), 1); | |
} | |
ENDHLSL | |
} | |
Pass | |
{ | |
Name "ApplyBloom" | |
HLSLPROGRAM | |
#pragma vertex PassVertex | |
#pragma fragment BloomPassFragment | |
uniform float _Intensity; | |
half4 BloomPassFragment(Varyings input) : SV_Target | |
{ | |
half4 c = SAMPLE_TEXTURE2D(TEXTURE2D_ARGS(_MainTex, sampler_MainTex), input.uv); | |
c.rgb += _Intensity * SampleBox(input.uv, 0.5); | |
return c; | |
} | |
ENDHLSL | |
} | |
Pass | |
{ | |
Name "ApplyMetaball" | |
HLSLPROGRAM | |
#pragma vertex PassVertex | |
#pragma fragment BloomPassFragment | |
uniform TEXTURE2D(_ColorRamp); | |
uniform float _Threshold; | |
uniform float _LineLength; | |
half4 BloomPassFragment(Varyings input) : SV_Target | |
{ | |
half4 c = SAMPLE_TEXTURE2D(TEXTURE2D_ARGS(_MainTex, sampler_MainTex), input.uv); | |
c.rgb += SampleBox(input.uv, 0.5); | |
half d = c.r; | |
clip(d - _Threshold); | |
// TODO: | |
c = lerp(c, half4(1, 1, 1, 1), step(_Threshold, d)); | |
half4 ramp = SAMPLE_TEXTURE2D(TEXTURE2D_ARGS(_ColorRamp, sampler_MainTex), float2(d * 0.1 + _Time.y * 0.1, 0.1)); | |
// c = lerp(half4(1, 1, 1, 1), c, smoothstep(_Threshold - d, _Threshold - d + 0.02, 0)); | |
// c = lerp(ramp, c, smoothstep(_Threshold - d, _Threshold - d + 0.02, 0)); | |
// c = lerp(ramp, c, smoothstep(_Threshold - d, _Threshold - d + _LineLength, 0)); | |
c = smoothstep(_Threshold - d, _Threshold - d + _LineLength, 0); | |
return c; | |
} | |
ENDHLSL | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class MetaballPass : ScriptableRenderPass | |
{ | |
public RenderTargetIdentifier SourceIdentifier; | |
int BlurryIterations => temporatyTargetHandles.Length; | |
readonly string profilerTag; | |
readonly IEnumerable<IDrawable> targets; | |
readonly Material metaballMaterial; | |
readonly RenderTargetHandle metaballSourceHandle; | |
readonly RenderTargetHandle[] temporatyTargetHandles; | |
readonly int downSamplingPass; | |
readonly int upSamplingPass; | |
readonly int applyBloomPass; | |
readonly int applyMetaballPass; | |
readonly MaterialPropertyBlock metaballSourceProps = new MaterialPropertyBlock(); | |
public MetaballPass( | |
string profilerTag, | |
RenderPassEvent renderPassEvent, | |
IEnumerable<IDrawable> targets, | |
Material metaballMaterial, | |
int blurryIterations) | |
{ | |
this.profilerTag = profilerTag; | |
this.renderPassEvent = renderPassEvent; | |
this.targets = targets; | |
this.metaballMaterial = metaballMaterial; | |
metaballSourceHandle.Init("_MetaballSource"); | |
temporatyTargetHandles = new RenderTargetHandle[blurryIterations]; | |
for (var i = 0; i < blurryIterations; i++) | |
{ | |
temporatyTargetHandles[i].Init($"_MetaballTemp{i}"); | |
} | |
downSamplingPass = metaballMaterial.FindPass("DownSampling"); | |
upSamplingPass = metaballMaterial.FindPass("UpSampling"); | |
applyBloomPass = metaballMaterial.FindPass("ApplyBloom"); | |
applyMetaballPass = metaballMaterial.FindPass("ApplyMetaball"); | |
metaballSourceProps.SetInt("_StencilComp", (int)CompareFunction.Always); | |
} | |
// This method is called before executing the render pass. | |
// It can be used to configure render targets and their clear state. Also to create temporary render target textures. | |
// When empty this render pass will render to the active camera render target. | |
// You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>. | |
// The render pipeline will ensure target setup and clearing happens in an performance manner. | |
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) | |
{ | |
} | |
// Here you can implement the rendering logic. | |
// Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers | |
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html | |
// You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline. | |
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) | |
{ | |
if (targets.IsEmpty()) | |
return; | |
var cam = renderingData.cameraData.camera; | |
var cmd = CommandBufferPool.Get(profilerTag); | |
using (new ProfilingSample(cmd, profilerTag)) | |
{ | |
var targetDescriptor = renderingData.cameraData.cameraTargetDescriptor; | |
targetDescriptor.depthBufferBits = 0; | |
cmd.GetTemporaryRT(metaballSourceHandle.id, targetDescriptor, FilterMode.Bilinear); | |
cmd.SetRenderTarget(metaballSourceHandle.id); | |
cmd.ClearRenderTarget(true, true, Color.black, 1f); | |
foreach (var x in targets) | |
{ | |
var pass = x.Material.FindPass("MetaballSource"); | |
if (x.Transform is RectTransform rectTransform) | |
{ | |
var screenPos = RectTransformUtility.WorldToScreenPoint(cam, rectTransform.position); | |
RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, screenPos, cam, out var worldPoint); | |
var matrix = Matrix4x4.TRS(worldPoint, Quaternion.identity, rectTransform.lossyScale); | |
cmd.DrawMesh(x.Mesh, matrix, x.Material, 0, pass, metaballSourceProps); | |
} | |
else | |
{ | |
cmd.DrawMesh(x.Mesh, x.Transform.localToWorldMatrix, x.Material, 0, pass, metaballSourceProps); | |
} | |
} | |
// Blurring | |
// Down sampling | |
var currentSource = metaballSourceHandle; | |
var currentDestination = temporatyTargetHandles[0]; | |
targetDescriptor.width /= 2; | |
targetDescriptor.height /= 2; | |
cmd.GetTemporaryRT(currentDestination.id, targetDescriptor, FilterMode.Bilinear); | |
cmd.Blit(currentSource.id, currentDestination.id, metaballMaterial, downSamplingPass); | |
cmd.ReleaseTemporaryRT(currentSource.id); | |
for (var i = 1; i < BlurryIterations; i++) | |
{ | |
currentSource = currentDestination; | |
currentDestination = temporatyTargetHandles[i]; | |
targetDescriptor.width /= 2; | |
targetDescriptor.height /= 2; | |
cmd.GetTemporaryRT(currentDestination.id, targetDescriptor, FilterMode.Bilinear); | |
cmd.Blit(currentSource.id, currentDestination.id, metaballMaterial, downSamplingPass); | |
} | |
// Up sampling | |
for (var i = BlurryIterations - 2; i >= 0; i--) | |
{ | |
currentSource = currentDestination; | |
currentDestination = temporatyTargetHandles[i]; | |
cmd.Blit(currentSource.id, currentDestination.id, metaballMaterial, upSamplingPass); | |
cmd.ReleaseTemporaryRT(currentSource.id); | |
} | |
// cmd.SetGlobalTexture("_MetaballSource", currentDestination.Identifier()); | |
cmd.Blit(currentDestination.id, SourceIdentifier, metaballMaterial, applyMetaballPass); | |
} | |
context.ExecuteCommandBuffer(cmd); | |
CommandBufferPool.Release(cmd); | |
} | |
/// Cleanup any allocated resources that were created during the execution of this render pass. | |
public override void FrameCleanup(CommandBuffer cmd) | |
{ | |
cmd.ReleaseTemporaryRT(metaballSourceHandle.id); | |
foreach (var handle in temporatyTargetHandles) | |
{ | |
cmd.ReleaseTemporaryRT(handle.id); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class MetaballRendererFeature : ScriptableRendererFeature | |
{ | |
[System.Serializable] | |
public sealed class MetaballSettings | |
{ | |
public string ProfilerTag = "MetaballRenderFeature"; | |
public RenderPassEvent Event = RenderPassEvent.BeforeRenderingTransparents; | |
public Shader MetaballShader; | |
public BlendMode SrcBlend = BlendMode.SrcAlpha; | |
public BlendMode DstBlend = BlendMode.OneMinusSrcAlpha; | |
public Texture2D RampTexture; | |
[Range(1, 16)] | |
public int BlurryIterations = 1; | |
[Range(0.001f, 1f)] | |
public float Threshold = 0.04f; | |
[Range(0f, 1f)] | |
public float LineLength = 0.5f; | |
} | |
static readonly HashSet<IDrawable> Targets = new HashSet<IDrawable>(); | |
public static void AddTarget(IDrawable item) => Targets.Add(item); | |
public static void RemoveTarget(IDrawable item) => Targets.Remove(item); | |
[SerializeField] | |
MetaballSettings settings = new MetaballSettings(); | |
MetaballPass metaballPass; | |
public override void Create() | |
{ | |
var metaballMaterial = CoreUtils.CreateEngineMaterial(settings.MetaballShader); | |
metaballMaterial.SetTexture("_ColorRamp", settings.RampTexture); | |
metaballMaterial.SetFloat("_Threshold", settings.Threshold); | |
metaballMaterial.SetFloat("_LineLength", settings.LineLength); | |
metaballMaterial.SetInt("_SrcBlend", (int)settings.SrcBlend); | |
metaballMaterial.SetInt("_DstBlend", (int)settings.DstBlend); | |
metaballPass = new MetaballPass( | |
settings.ProfilerTag, | |
settings.Event, | |
Targets, | |
metaballMaterial, | |
settings.BlurryIterations); | |
} | |
// Here you can inject one or multiple render passes in the renderer. | |
// This method is called when setting up the renderer once per-camera. | |
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) | |
{ | |
metaballPass.SourceIdentifier = renderer.cameraColorTarget; | |
renderer.EnqueuePass(metaballPass); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment