#Hey all! I'm currently trying to use a
1 messages · Page 1 of 1 (latest)
public class LightMaskBlurPass : ScriptableRenderPass // Initial Pass that will sample Tilemap Layer as Texture, and Light Texture and returned the masked value.
{
private List<ShaderTagId> shaderTagsList = new List<ShaderTagId>();
private FilteringSettings filteringSettings;
private ProfilingSampler _profilingSampler;
public RTHandle RT_LightMask;
public RTHandle RT_BlurredLight;
private LightSettings settings;
public LightMaskBlurPass(LightSettings settings, string name) // PASS "CONSTRUCTOR"
{
this.settings = settings;
filteringSettings = new FilteringSettings(RenderQueueRange.all, settings.layerMask);
Debug.Log(LayerMask.LayerToName(settings.layerMask));
shaderTagsList.Add(new ShaderTagId("Universal2D"));
//shaderTagsList.Add(new ShaderTagId("SRPDefaultUnlit"));
shaderTagsList.Add(new ShaderTagId("UniversalForward"));
_profilingSampler = new ProfilingSampler(name);
}
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
var colorDesc = renderingData.cameraData.cameraTargetDescriptor;
colorDesc.depthBufferBits = 0;
colorDesc.width /= settings.downsample;
colorDesc.height /= settings.downsample;
var colorDesc2 = renderingData.cameraData.cameraTargetDescriptor;
colorDesc2.depthBufferBits = 0;
RenderingUtils.ReAllocateIfNeeded(ref RT_LightMask, colorDesc, name: "RT_LightMask");
RenderingUtils.ReAllocateIfNeeded(ref RT_BlurredLight, colorDesc, name: "RT_BlurredLight");
ConfigureTarget(RT_LightMask);
ConfigureClear(ClearFlag.Color, Color.black);
}
// Draw the mask of our "Ground" tag to our "RT_GroundMask".
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, _profilingSampler))
{
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
SortingCriteria sortingCriteria = SortingCriteria.CommonTransparent;
DrawingSettings drawingSettings = CreateDrawingSettings(shaderTagsList, ref renderingData, sortingCriteria);
// draw output of lightMaskMAT to rtMaskedLightTexture ?
Blit(cmd, RT_LightMask, RT_LightMask, settings.lightMaskMAT, 0);
settings.GaussianBlurShaderMAT.SetTexture("_MainTex", RT_LightMask);
Blit(cmd, RT_LightMask, RT_BlurredLight, settings.GaussianBlurShaderMAT, 0);
cmd.SetGlobalTexture("RT_LightMask_GLOBAL", RT_LightMask);
cmd.SetGlobalTexture("RT_BlurredLight_GLOBAL", RT_BlurredLight);
}
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
public override void OnCameraCleanup(CommandBuffer cmd) { }
}
#endregion
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(P_GroundMask);
//renderer.EnqueuePass(P_LightMask);
renderer.EnqueuePass(P_LightMaskBlur);
}
First thing I'd guess is the vertex program used by the shader might not suitable for the Blit() call used by the renderer feature, which uses the Blitter API internally to draw a triangle.
That triangle only becomes a "fullscreen triangle" with a particular vertex shader, like the Vert one in the Blit.hlsl include file
Pass should also use ZWrite Off ZTest Always Blend Off Cull Off to avoid any other issues
Ahh, I see! I'm still learning how to actually write/understand HLSL shaders, is there anything in particular I should be changing in the shader itself to allow it to work with a Blit call?
Shader "GaussianBlur"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Spread("Standard Deviation (Spread)", Float) = 0
_GridSize("Grid Size", Integer) = 1
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#define E 2.71828f
sampler2D _MainTex;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_TexelSize;
uint _GridSize;
float _Spread;
CBUFFER_END
float gaussian(int x)
{
float sigmaSqu = _Spread * _Spread;
return (1 / sqrt(TWO_PI * sigmaSqu)) * pow(E, -(x * x) / (2 * sigmaSqu));
}
struct appdata
{
float4 positionOS : Position;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 positionCS : SV_Position;
float2 uv : TEXCOORD0;
};
v2f vert(appdata v)
{
v2f o;
o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
o.uv = v.uv;
return o;
}
ENDHLSL
Pass
{
Name "Horizontal"
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag_horizontal
float4 frag_horizontal(v2f i) : SV_Target
{
float3 col = float3(0.0f, 0.0f, 0.0f);
float gridSum = 0.0f;
int upper = ((_GridSize - 1) / 2);
int lower = -upper;
for (int x = lower; x <= upper; ++x)
{
float gauss = gaussian(x);
gridSum += gauss;
float2 uv = i.uv + float2(_MainTex_TexelSize.x * x, 0.0f);
col += gauss * tex2D(_MainTex, uv).xyz;
}
col /= gridSum;
return float4(col, 1.0f);
}
ENDHLSL
}
Pass
{
Name "Vertical"
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag_horizontal
float4 frag_horizontal(v2f i) : SV_Target
{
float3 col = float3(0.0f, 0.0f, 0.0f);
float gridSum = 0.0f;
int upper = ((_GridSize - 1) / 2);
int lower = -upper;
for (int y = lower; y <= upper; ++y)
{
float gauss = gaussian(y);
gridSum += gauss;
float2 uv = i.uv + float2(_MainTex_TexelSize.y * y, 0.0f);
col += gauss * tex2D(_MainTex, uv).xyz;
}
col /= gridSum;
return float4(col, 1.0f);
}
ENDHLSL
}
}
}
Mostly just that vertex shader in the include file I mentioned. It avoids using the typical World->Clip space transformations (via model, view and projection matrices), instead outputting specific vertex postions based on the vertex ID
Okay sure, I'll look into it!
Also for this, I am a little confused on how to implement this to the pass directly. Should I be adding that to the shader directly in the tags section? Or is there a way to add that to the renderer pass in some way?
Not under Tags, but those commands go inside the Pass{ } but outside the HLSLPROGRAM
e.g.
Pass {
ZWrite Off ZTest Always Blend Off Cull Off
Name "Horizontal"
HLSLPROGRAM
and similarly for the Vertical
You might also be able to put it inside SubShader and it'll automatically use that for all passes, not sure though.
Alright, perfect I'll give that a try!
Nothing really visibly happened as likely expected, but I still am yet to get this to "work" as a fullscreen shader to work with a blit, is there something I need to do directly with the shader code itself to render to a fullscreen texture to be compatible with the blit? Or is there some way to create a "Fullscreen" .shader file in some way?
Is there a way to use a different Vertex program in some way? I unfortunately am not familliar enough with anything more than the very basics of HLSL/ShaderToy code atm so please bare with me haha
Your current shader uses #pragma vertex vert, with vert() defined in the HLSLINCLUDE block, which includes that code in all passes.
As that shader uses the input positionOS and TransformObjectToHClip it's not suitable for fullscreen drawing when using the Blitter API (which Blit() in feature uses)
You could add
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
After the other Core.hlsl include line inside the HLSLPROGRAM, remove the current appdata, v2f and vert function and use #pragma vertex Vert in the passes instead. That Vert is defined in that Blit.hlsl include file.
May also need some other changes, like anywhere there is v2f you'd now use Varyings as that's what the include file has named that struct instead
i.uv would also be i.texcoord
Okay perfect! I'll try all of that, I really appreciate it!
Would the fragment portion stay the same? I assume that is a part of Core opposed to the Blit hlsl?
#pragma vertex Vert
#pragma fragment frag_horizontal
The fragment functions are defined in the pass just below that, but yes they'd stay the same (except for the other minor changes I mentioned above) since they handle the blurring you want
it worked!
It's blurring properly as expected I believe! Thank you so so so much for your help. That was driving me crazy the past few days haha! You really are a shader legend!
I think I understand the way HLSL shaders work a little bit more now, I think troubleshooting in the future is likely to be much easier too!
Might not be super important, but if you want to understand what that Vert function is doing this is an answer I've given elsewhere :
The renderer feature Blit() function just draws a triangle, it doesn't say where (which is why just using positionOS input doesn't draw anything), only that 3 vertices need to be drawn.
It's the shader that uses the vertex ID to determine the positions of those vertices.
You need to use GetFullScreenTriangleVertexPosition(input.vertexID) resulting in vertices at (-1, -1), (3, -1) and (-1, 3) (clipspace xy)
I see! So what is the basic "Core" UV shader trying to do? Is it wrapping the vertexes along the mesh that a "Blit"/Fullscreen shader wouldn't necessarily have/support?
Oh nevermind, the UV that the fullscreen shader doesn't have I assume.
Which gives the basic shader vertex data which is required to draw the fragments using it?
UVs are probably important too but yeah the cmd.DrawProcedural call that the Blitter API ends up using doesn't pass any vertex data
Thats why the Vert program uses the vertexID (0, 1 and 2) to calculate the clipspace positions and uv/texcoord
The UV is only for mapping the texture though. It didn't even get that far in this case, as the previous vertex shaders didn't end up producing any fragments - as it probably tried to draw the triangle with inputs of (0,0,0), (0,0,0) and (0,0,0)
Ahh I see, that makes a lot of sense!
And the result was only black, because the feature used ConfigureClear(ClearFlag.Color, Color.black);
Appreciate the explanation, I am really trying to learn and understand shaders as a whole currently. understanding how to differenciate a fullscreen shader is very very helpful as I typically (in shader graph) would just use it when nothing else was working haha.
I thought this too, but I had tried changing it to different colours and it would always show black, not sure why though!
That is what particularly confused me, but I unfortunately didn't get any response on why that may have been so I assume likely it was a case of needing to restart Unity or something.
Ah okay, also not sure why then
Oh probably because that ConfigureClear would be clearing what is set by ConfigureTarget, which is RT_LightMask in this case, not the Blurred one
Yeah it really was throwing me off, I had initially assumed it was something to do with vertex not actually drawing to my material at all, thus not allowing for anything to be drawn, but since nothing was clearing I was thinking I was maybe creating the RTHandle wrong.
Probably defaults to black as that's just what the target is initalised as
Ah, that makes sense yeah!
Yeah that would make sense
Would that mean the "blank" space on my LightMask should have been blue for example opposed to black?
Because I think I had checked that too but I can't remember for sure haha.
If you changed the ConfigureClear to blue then yes that's likely
That makes a lot of sense, as someone who isn't super familliar with shaders I often miss a lot of the very basic details thinking it's a bigger issue haha, but I suppose that's a big part of learning in itself!
I do think shader graph does give you at-least a decent grasp as to how they work to some degree and I think understanding the basics of that does get me in a decent headspace for it. I'll definitely try to continue learning and trying to understand/write my own HLSL shaders, they seem pretty fun to learn.
I had stumbled across these:
https://www.ronja-tutorials.com/post/
not too long ago, and they definitely gave a super simple picture as to what an HLSL shader actually is!
Hey, sorry to reopen this but I just had a tiny question related to the new package replacing some of the Core package definitions, while I am doubtful it is related unfortunately my Gaussian blur isn't seeming to draw a vertical pass, and I've tried a few things so I am just wondering if that would be correlated at all? The horizontal is working perfectly fine, but it's clearly not drawing a vertical pass unfortunately. I was just wondering if you'd have any obvious reason as to why that could be, if not that's totally fine! Just trying to cover all my bases haha!
Pass
{
ZWrite Off ZTest Always Blend Off Cull Off
Name "Horizontal"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag_horizontal
float4 frag_horizontal(Varyings i) : SV_Target
{
float3 col = float3(0.0f, 0.0f, 0.0f);
float gridSum = 0.0f;
int upper = ((_GridSize - 1) / 2);
int lower = -upper;
for (int x = lower; x <= upper; ++x)
{
float gauss = gaussian(x);
gridSum += gauss;
float2 uv = i.texcoord + float2(_MainTex_TexelSize.x * x, 0.0f);
col += gauss * tex2D(_MainTex, uv).xyz;
}
col /= gridSum;
return float4(col, 1.0f);
}
ENDHLSL
}
Pass
{
ZWrite Off ZTest Always Blend Off Cull Off
Name "Vertical"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag_vertical
float4 frag_vertical(Varyings i) : SV_Target
{
float3 col = float3(0.0f, 0.0f, 0.0f);
float gridSum = 0.0f;
int upper = ((_GridSize - 1) / 2);
int lower = -upper;
for (int y = lower; y <= upper; ++y)
{
float gauss = gaussian(y);
gridSum += gauss;
float2 uv = i.texcoord + float2(0.0f, _MainTex_TexelSize.y * y);
col += gauss * tex2D(_MainTex, uv).xyz;
}
col /= gridSum;
return float4(col, 1.0f);
}
ENDHLSL
}
my initial thought was that my vertical pass was actually incorrectly adjusting the X axis in the
float2 uv = i.texcoord + float2(0.0f, _MainTex_TexelSize.y * y);
line however, even after fixing it to alter the Vertical axis it still is only horizontally blurring unfortunately!
atm I think the feature is only using pass 0, while it should be calling each pass, something like
Blit(cmd, RT_LightMask, RT_Temp, settings.GaussianBlurShaderMAT, 0);
Blit(cmd, RT_Temp, RT_BlurredLight, settings.GaussianBlurShaderMAT, 1);
Ohh, I see! So you need to call each pass individually using the material?
That would make a ton of sense haha, appreciate your blisteringly fast response!
Basically yeah. I think if -1 was used it would render all passes, but incorrectly - as the result of the first pass needs to be the input to the second so needs to be done in this way
Ahh I see! I really appreciate it. One more thing I also recently noticed, and has kind of stumped me I am currently drawing my tilemap to a Texture to then be used as a mask, but since I am also altering the material the tilemap uses, it is actually drawing the shaded result of the tilemap, rather than just the base texture. Would it be better to draw the "shadow" to a full screen volume or something similar instead?
While it should just be the grayish area and the black, however unfortunately the "light" is showing through as it's a part of the material.
Are you using a lit material as the override? Maybe it should be an unlit one
It's unlit for the Tilemap's material, however, it doesn't reallly seem to be an issue since it's only the texture that's being affected rather than the shape as far as I can tell.
One thing I am still noticing is a pretty constant flashing on and off of my material, I assume it's from reallocating the texture every frame, but I am unsure of what the root cause is.
it seems to happen mostly when something actively happens eg: (moving mouse, scrolling, moving camera etc).
to be fair it actually seems to only really happen in the scene view so I assume it's just because it reduces the frequency of draw calls?
Maybe. I would add something like
if (renderingData.cameraData.isPreviewCamera) return;
to the start of AddRenderPasses method though
It'll make sure the feature isn't trying to run for thumbnail/inspector previews which can sometimes bug things out