Hey! I was hoping I could get some guidance on this. I have a shader graph outline effect (you can see it on the cliffs in the background here) that I’m translating to work for an instanced shader. I’m using URP and DrawMeshInstancedIndirect to do this, and I can confirm it ordinarily has worked fine for me for my grass shader (not pictured).
Problem is, I’ve never wanted the grass to be outlined, and now that I’m working on leaves, I do- but it seems like, despite the frame debugger’s depth texture clearly showing the leaf quads, they aren’t writing to depth somehow…?
As you can see, it’s highlighting the depth outlines behind the quads, which is a behavior I’m familiar with in my shader graph as happening when it’s applied to a transparent mesh. This tells me something weird is going on where it seems to be treating these leaf quads as transparent or something similar. I don’t really know what I’m doing in this area, so I’d appreciate any tips/help!
Here’s the shader! Please ignore any sloppiness, I’m in the process of fixing it up.
Shader "Verdict/Poisson Leaves"
{
Properties
{
_MainTex ("Leaves Texture", 2D) = "white" {}
_SwaySpeed ("Sway Speed", Range(0.0, 250.0)) = 1.0
_SwayDistance ("Sway Distance", Range(0.0, 0.5)) = 1.0
_WindSimplexScale ("Wind Simplex Scale", Range(0.0, 2.0)) = 1.0
_NoiseJitter ("Noise Jitter", Range(0.0, 10.0)) = 0.0
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _Cull("__cull", Float) = 2.0
[HideInInspector] _SrcBlend("__src", Float) = 1.0
[HideInInspector] _DstBlend("__dst", Float) = 0.0
[HideInInspector] _SrcBlendAlpha("__srcA", Float) = 1.0
[HideInInspector] _DstBlendAlpha("__dstA", Float) = 0.0
[HideInInspector] _ZWrite("__zw", Float) = 1.0
[HideInInspector] _BlendModePreserveSpecular("_BlendModePreserveSpecular", Float) = 1.0
[HideInInspector] _AlphaToMask("__alphaToMask", Float) = 0.0
[HideInInspector] _AddPrecomputedVelocity("_AddPrecomputedVelocity", Float) = 0.0
}
SubShader
{
// Universal Pipeline tag is required. If Universal render pipeline is not set in the graphics settings
// this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
// material work with both Universal Render Pipeline and Builtin Unity Pipeline
Tags
{
"RenderType" = "Opaque"
"Queue" = "AlphaTest"
"RenderPipeline" = "UniversalPipeline"
}
LOD 300
Pass
{
Name "ForwardLit"
Tags
{
"LightMode" = "UniversalForward"
}
Blend[_SrcBlend][_DstBlend], [_SrcBlendAlpha][_DstBlendAlpha]
ZWrite On
Cull[_Cull]
AlphaToMask[_AlphaToMask]
HLSLPROGRAM
#pragma target 2.0
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#include "../../../Resources/Random.cginc"
#include "../../../Resources/Simplex.compute"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
float GetLightIntensity(float3 color)
{
return max(color.r, max(color.g, color.b));
}
Light GetAdditionalLightForToon(int pixelLightIndex, float3 worldPosition)
{
// Convert the pixel light index to the light data index
int perObjectLightIndex = GetPerObjectLightIndex(pixelLightIndex);
// Call the URP additional light algorithm. This will not calculate shadows, since we don't pass a shadow mask value
Light light = GetAdditionalPerObjectLight(perObjectLightIndex, worldPosition);
// Manually set the shadow attenuation by calculating realtime shadows
light.shadowAttenuation = AdditionalLightRealtimeShadow(perObjectLightIndex, worldPosition);
return light;
}
struct AddtLightsOutput
{
float Diffuse;
float Specular;
float3 Color;
};
AddtLightsOutput AddAdditionalLights_float(float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView,
float MainDiffuse, float MainSpecular, float3 MainColor)
{
float mainIntensity = GetLightIntensity(MainColor);
AddtLightsOutput alo;
alo.Diffuse = MainDiffuse;
alo.Specular = MainSpecular;
alo.Color = MainColor;
int pixelLightCount = GetAdditionalLightsCount();
for (int i = 0; i < pixelLightCount; ++i)
{
Light light = GetAdditionalLight(i, WorldPosition, 1);
//half NdotL = 1;
// I don't use this, but multiply it in lightDiffuse if you do
half lightAtten = light.distanceAttenuation * light.shadowAttenuation * GetLightIntensity(light.color);
half lightDiffuse = lightAtten;
alo.Diffuse += round(lightDiffuse * 4) / 4;
alo.Color += round(light.color * lightDiffuse * 4) / 4;
}
return alo;
}
#pragma vertex vert
#pragma fragment frag
struct VertexData
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 grassColor : TEXCOORD1;
float simplexVal : TEXCOORD2;
float noiseVal : TEXCOORD3;
float3 lightColor : TEXCOORD4;
float4 screenPos : TEXCOORD7;
};
struct GrassData
{
float3 position;
float3 color;
float3 normal;
float3 transform;
float3 offset;
float2 wind;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
StructuredBuffer<GrassData> positionBuffer;
float3 _CameraRotation;
float4 _PlayerPosition;
float _WindSimplexScale, _Wind, _SwaySpeed, _SwayDistance;
float4 RotateAroundYInDegrees(float4 vertex, float degrees)
{
float alpha = degrees * 3.14 / 180.0;
float sina, cosa;
sincos(alpha, sina, cosa);
float2x2 m = float2x2(cosa, -sina, sina, cosa);
return float4(mul(m, vertex.xz), vertex.yw).xzyw;
}
float4 RotateAroundXInDegrees(float4 vertex, float degrees)
{
float alpha = degrees * 3.14 / 180.0;
float sina, cosa;
sincos(alpha, sina, cosa);
float2x2 m = float2x2(cosa, -sina, sina, cosa);
return float4(mul(m, vertex.yz), vertex.xw).zxyw;
}
float4 RotateAroundZInDegrees(float4 vertex, float degrees)
{
float alpha = degrees * 3.14 / 180.0;
float sina, cosa;
sincos(alpha, sina, cosa);
float2x2 m = float2x2(cosa, -sina, sina, cosa);
return float4(mul(m, vertex.xy), vertex.zw);
}
float3 RotateHueDegrees(float3 In, float Offset)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 P = lerp(float4(In.bg, K.wz), float4(In.gb, K.xy), step(In.b, In.g));
float4 Q = lerp(float4(P.xyw, In.r), float4(In.r, P.yzx), step(P.x, In.r));
float D = Q.x - min(Q.w, Q.y);
float E = 1e-10;
float3 hsv = float3(abs(Q.z + (Q.w - Q.y)/(6.0 * D + E)), D / (Q.x + E), Q.x);
float hue = hsv.x + Offset / 360;
hsv.x = (hue < 0)
? hue + 1
: (hue > 1)
? hue - 1
: hue;
float4 K2 = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 P2 = abs(frac(hsv.xxx + K2.xyz) * 6.0 - K2.www);
return hsv.z * lerp(K2.xxx, saturate(P2 - K2.xxx), hsv.y);
}
float4 BlendOverlay(float4 Base, float4 Blend, float Opacity)
{
float4 result1 = 1.0 - 2.0 * (1.0 - Base) * (1.0 - Blend);
float4 result2 = 2.0 * Base * Blend;
float4 zeroOrOne = step(Base, 0.5);
float4 result = result2 * zeroOrOne + (1 - zeroOrOne) * result1;
return lerp(Base, result, Opacity);
}
v2f vert(VertexData v, uint instanceID : SV_INSTANCEID)
{
v2f o;
float3 grassPosition = positionBuffer[instanceID].position;
float3 grassColor = positionBuffer[instanceID].color;
float3 grassNormal = positionBuffer[instanceID].normal;
float3 grassOffset = positionBuffer[instanceID].offset;
float3 transformPosition = positionBuffer[instanceID].transform;
float2 grassWind = positionBuffer[instanceID].wind - float2(5, 0);
float3 offsetPosition = grassPosition - grassOffset;
float idHash = randValue(abs(grassPosition.x * 1000 + grassPosition.z * 100000 + 2));
idHash = randValue(idHash * 100000);
float4 localPosition = RotateAroundZInDegrees(v.vertex, -_CameraRotation.x);
localPosition = RotateAroundYInDegrees(localPosition, 90.0f - _CameraRotation.y);
float windSimplex = snoise(grassPosition * _WindSimplexScale + 10) * snoise(grassPosition * _WindSimplexScale * 0.5 + 1000);
float speedMod = _SwaySpeed * max(_Wind / 50.0f, 0.5f);
float simplexTime = sin((_Time + windSimplex) * speedMod) / 2 + 0.5f;
grassPosition.x += simplexTime * v.uv.y * _SwayDistance * (_Wind / 75.0f);
float4 worldPosition = float4(grassPosition.xyz + localPosition, 1.0f);
/* Lighting BS */
float4 shadowCoord = TransformWorldToShadowCoord(transformPosition + grassPosition);
Light light = GetMainLight(shadowCoord);
float dt = dot(grassNormal, light.direction);
float atten = light.shadowAttenuation * light.distanceAttenuation * dt;
float shadow = step(0.5, atten);
shadow = max(shadow, 0.6);
o.vertex = TransformObjectToHClip(worldPosition);
/* Rim Lighting */
float satDtLight = saturate(dot(grassNormal, light.direction));
float lightPow = pow(satDtLight, 1);
float multSmooth = smoothstep(0.1f, 0.9f, lightPow) * 1;
float attenMult = max(0.6, multSmooth * shadow);
float3 attenMultColor = attenMult * light.color;
AddtLightsOutput alo = AddAdditionalLights_float(0, transformPosition + grassPosition, grassNormal, -GetViewForwardDir(), attenMult, 0, light.color);
o.lightColor = alo.Color * alo.Diffuse;
o.uv = v.uv;
o.noiseVal = idHash;
o.grassColor = grassColor;
o.screenPos = ComputeScreenPos(o.vertex);
return o;
}
float getEyeDepth(float2 uv)
{
float depthRaw = SAMPLE_TEXTURE2D(_CameraDepthTexture, sampler_CameraDepthTexture, uv);
return depthRaw;
}
void GetCrossSampleUVs_float(float2 UV, float2 TexelSize,
float OffsetMultiplier, out float2 UVOriginal, out float2 UVTopRight,
out float2 UVBottomLeft, out float2 UVTopLeft, out float2 UVBottomRight)
{
UVOriginal = UV;
UVTopRight = UV.xy + float2(0.0, TexelSize.y) * OffsetMultiplier;
UVBottomLeft = UV.xy - float2(0.0, TexelSize.y) * OffsetMultiplier;
UVTopLeft = UV.xy + float2(TexelSize.x * OffsetMultiplier, 0.0);
UVBottomRight = UV.xy - float2(TexelSize.x * OffsetMultiplier, 0.0);
}
float4 frag(v2f i) : SV_Target
{
float4 maskCol = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
//clip(-(0.5 - maskCol.a));
clip(0.0f);
float2 screenPos = i.screenPos.xy / i.screenPos.w;
float2 texelSize = float2(1.0f / _ScreenParams.x, 1.0f / _ScreenParams.y);
float2 uvOg;
float2 uvTr;
float2 uvBl;
float2 uvTl;
float2 uvBr;
GetCrossSampleUVs_float(screenPos, texelSize, 1, uvOg, uvTr, uvBl, uvTl, uvBr);
float depthOg = getEyeDepth(uvOg);
float depthTr = getEyeDepth(uvTr);
float depthBl = getEyeDepth(uvBl);
float depthTl = getEyeDepth(uvTl);
float depthBr = getEyeDepth(uvBr);
float diffTr = depthTr - depthOg;
float diffBl = depthBl - depthOg;
float diffTl = depthTl - depthOg;
float diffBr = depthBr - depthOg;
float cross = 2.0f;
float comb = sqrt(diffTr + diffBl + diffTl + diffBr) * cross;
float result = step(depthOg * 0.01f, comb);
return float4(result,result,result, 1);
}
ENDHLSL
}
}
FallBack "Hidden/Universal Render Pipeline/FallbackError"
}