Beautify addon
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "EdgeFusion",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:15fc0a57446b3144c949da3e2b9737a9",
|
||||
"GUID:df380645f10b7bc4b97d4f5eb6303d95"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f279941a09ad6a84abae8535f4c1e3fe
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c387a9e47f88c84bbeed75d63aca589
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e096bf0357759042b9aa71338d9769b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd7e6a89ce450c249916df842590c29e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
Shader "Hidden/Kronnect/EdgeFusion/EdgeFusion"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
ZTest Always
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "EdgeFusion Blend"
|
||||
HLSLPROGRAM
|
||||
#pragma target 3.0
|
||||
#pragma vertex EFVertFullscreen
|
||||
#pragma fragment FragBlendEdges
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_INTRA_OBJECT EDGE_FUSION_CONCAVE_ONLY
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_ENABLE_JITTER
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_NOISE
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_MSAA_EDGE_FIX
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_ID_EXCLUSION
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_ANTI_FLICKER
|
||||
#include "EdgeFusionCommon.hlsl"
|
||||
#include "EdgeFusionBlendPass.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "EdgeFusion Debug"
|
||||
HLSLPROGRAM
|
||||
#pragma target 3.0
|
||||
#pragma vertex EFVertFullscreen
|
||||
#pragma fragment FragDebug
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_INTRA_OBJECT EDGE_FUSION_CONCAVE_ONLY
|
||||
#include "EdgeFusionCommon.hlsl"
|
||||
#include "EdgeFusionDebugPass.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "EdgeFusion Compare"
|
||||
HLSLPROGRAM
|
||||
#pragma target 3.0
|
||||
#pragma vertex EFVertFullscreen
|
||||
#pragma fragment FragCompare
|
||||
#include "EdgeFusionCommon.hlsl"
|
||||
#include "EdgeFusionComparePass.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f67763b8beefc748b2814c56b00cb54
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
#ifndef EDGE_FUSION_BLEND_LIB_INCLUDED
|
||||
#define EDGE_FUSION_BLEND_LIB_INCLUDED
|
||||
|
||||
static const float2 sunflower_points[32] = {
|
||||
float2(-0.08756255f, 0.08021447f),
|
||||
float2(0.01468209f, -0.16729483f),
|
||||
float2(0.12514433f, 0.16322862f),
|
||||
float2(-0.23386945f, -0.04136821f),
|
||||
float2(0.22404494f, -0.14251905f),
|
||||
float2(-0.07551290f, 0.28090421f),
|
||||
float2(-0.14480914f, -0.27882118f),
|
||||
float2(0.31549522f, 0.11521835f),
|
||||
float2(-0.32929810f, 0.13592947f),
|
||||
float2(0.15916285f, -0.34012176f),
|
||||
float2(0.11787271f, 0.37579677f),
|
||||
float2(-0.35591507f, -0.20626006f),
|
||||
float2(0.41817273f, -0.09193410f),
|
||||
float2(-0.25554255f, 0.36348298f),
|
||||
float2(-0.05910422f, -0.45610320f),
|
||||
float2(0.36320827f, 0.30611232f),
|
||||
float2(-0.48920069f, 0.02022998f),
|
||||
float2(0.35711789f, -0.35537999f),
|
||||
float2(-0.02390958f, 0.51706675f),
|
||||
float2(-0.34025894f, -0.40774392f),
|
||||
float2(0.53932101f, 0.07256488f),
|
||||
float2(-0.45720732f, 0.31811294f),
|
||||
float2(0.12499574f, -0.55561858f),
|
||||
float2(0.28923687f, 0.50475690f),
|
||||
float2(-0.56566134f, -0.18046139f),
|
||||
float2(0.54967531f, -0.25396393f),
|
||||
float2(-0.23821633f, 0.56920574f),
|
||||
float2(-0.21267188f, -0.59128201f),
|
||||
float2(0.56606831f, 0.29750964f),
|
||||
float2(-0.62893715f, 0.16578581f),
|
||||
float2(0.35758678f, -0.55612960f),
|
||||
float2(0.11377869f, 0.66204563f)
|
||||
};
|
||||
|
||||
inline float2 GetOffset(int index) {
|
||||
return sunflower_points[index];
|
||||
}
|
||||
|
||||
float _JitterFrame;
|
||||
|
||||
#define dot2(x) dot(x, x)
|
||||
|
||||
float3 HashToColor(float x) {
|
||||
float3 v = frac(sin(float3(x, x * 1.37, x * 2.17)) * 4378.5453);
|
||||
return saturate(v);
|
||||
}
|
||||
|
||||
float GetLuma(float3 rgb) {
|
||||
//const float3 lum = float3(0.2627, 0.6780, 0.0593); // Rec.2020 (HDR/wide gamut)
|
||||
//const float3 lum = float3(0.2126, 0.7152, 0.0722); // Rec709 (sRGB/HD)
|
||||
const float3 lum = float3(0.299, 0.587, 0.114); // Rec601 (SD/broadcast)
|
||||
return dot(rgb, lum);
|
||||
}
|
||||
|
||||
float3 ColorLerp(float3 a, float3 b, float t) {
|
||||
#if UNITY_COLORSPACE_GAMMA
|
||||
a = SRGBToLinear(a);
|
||||
b = SRGBToLinear(b);
|
||||
#endif
|
||||
float3 c = lerp(a, b, t);
|
||||
#if UNITY_COLORSPACE_GAMMA
|
||||
c = LinearToSRGB(c);
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
float FindEdgeNeighbour(float2 uvA, out float2 neighbourUV) {
|
||||
|
||||
neighbourUV = uvA;
|
||||
float4 oidA = SampleObjectData(uvA);
|
||||
if (abs(oidA.r) < 1e-6) return 0.0;
|
||||
|
||||
float4 oidEdge = oidA;
|
||||
float2 edgeUV = uvA;
|
||||
float linearDepthA = LinearEyeDepth(oidA.y, _ZBufferParams);
|
||||
float compensatedDefaultRadius = _DefaultRadiusWorld * (1.0 + _DistanceCompensation * linearDepthA);
|
||||
float maxRadiusWorldSqr = compensatedDefaultRadius * compensatedDefaultRadius;
|
||||
float bestDistSqr = maxRadiusWorldSqr;
|
||||
|
||||
#if EDGE_FUSION_ANTI_FLICKER
|
||||
float sumSoftW = 0.0;
|
||||
float2 accumEdgeUV = float2(0.0, 0.0);
|
||||
float3 accumEdgeWpos = float3(0.0, 0.0, 0.0);
|
||||
#endif
|
||||
|
||||
float3 wposA = GetWorldPos(uvA, oidA.y);
|
||||
float3 edgeWpos = wposA;
|
||||
#if EDGE_FUSION_INTRA_OBJECT || EDGE_FUSION_CONCAVE_ONLY
|
||||
float3 normalA = GetNormal(oidA);
|
||||
bool edgeCanUseSameIds = normalA.x < 100.0;
|
||||
#endif
|
||||
float uvRadiusBase = GetRadiusUV(oidA, linearDepthA);
|
||||
float radiusLimit = MAX_SCREEN_RADIUS;
|
||||
float uvRadiusEff = (radiusLimit * uvRadiusBase) / (uvRadiusBase + radiusLimit + 1e-6);
|
||||
float aspectRatio = _SrcColorTexture_TexelSize.y / _SrcColorTexture_TexelSize.x;
|
||||
float2 uvRadiusSearch = float2(uvRadiusBase, uvRadiusBase * aspectRatio);
|
||||
|
||||
#if EDGE_FUSION_ENABLE_JITTER
|
||||
float ca, sa;
|
||||
float2 pixel = uvA * _ScreenParams.xy;
|
||||
float jitter = InterleavedGradientNoise(pixel, _JitterFrame);
|
||||
float angle = jitter * 2.0 * 3.1415927;
|
||||
sincos(angle, sa, ca);
|
||||
float2 axisX = float2(ca, sa) * uvRadiusSearch;
|
||||
float2 axisY = float2(-sa, ca) * uvRadiusSearch;
|
||||
#endif
|
||||
|
||||
float edgeFactor = 0.0;
|
||||
|
||||
for (int d = 0; d < _SampleCount; d++) {
|
||||
float2 offset;
|
||||
float2 pdir = GetOffset(d);
|
||||
#if EDGE_FUSION_ENABLE_JITTER
|
||||
offset = axisX * pdir.x + axisY * pdir.y;
|
||||
#else
|
||||
offset = pdir * uvRadiusSearch;
|
||||
#endif
|
||||
|
||||
float2 uvB = uvA + offset;
|
||||
if (uvB.x < 0.0 || uvB.x > 1.0 || uvB.y < 0.0 || uvB.y > 1.0) continue;
|
||||
|
||||
float4 oidB = SampleObjectData(uvB);
|
||||
if (abs(oidB.r) < 1e-6) continue;
|
||||
|
||||
// Check if it's a different valid object
|
||||
bool isSameObject = SameObjectIds(oidB, oidA);
|
||||
|
||||
// If intra-object fusion is enabled, check for edges within the same object
|
||||
#if EDGE_FUSION_INTRA_OBJECT || EDGE_FUSION_CONCAVE_ONLY
|
||||
bool sameIds = isSameObject && edgeCanUseSameIds;
|
||||
if (sameIds) {
|
||||
float3 normalB = GetNormal(oidB);
|
||||
float normalDot = dot(normalA, normalB);
|
||||
isSameObject = normalDot >= NORMAL_THRESHOLD;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isSameObject) continue;
|
||||
|
||||
#if EDGE_FUSION_ID_EXCLUSION
|
||||
if (IsExcludedIdPair(oidA, oidB)) continue;
|
||||
#endif
|
||||
|
||||
// Binary refine between center (uv) and this outer point (uvB)
|
||||
float2 a = uvA, b = uvB;
|
||||
for (int k = 0; k < _BinarySearchSteps; k++) {
|
||||
float2 m = (a + b) * 0.5;
|
||||
float4 oidM = SampleObjectData(m);
|
||||
|
||||
// Check object ID sameness (inter-object edges)
|
||||
bool sameM = SameObjectIds(oidM, oidA);
|
||||
|
||||
// For intra-object edges, check normal/depth discontinuities
|
||||
#if EDGE_FUSION_INTRA_OBJECT || EDGE_FUSION_CONCAVE_ONLY
|
||||
if (sameIds && sameM) {
|
||||
float3 normalM = GetNormal(oidM);
|
||||
sameM = dot(normalA, normalM) >= NORMAL_THRESHOLD;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sameM) { a = m; } else { b = m; oidB = oidM; }
|
||||
}
|
||||
|
||||
float3 wposB = GetWorldPos(b, oidB.y);
|
||||
float distSqr = dot2(wposB - wposA);
|
||||
|
||||
if (distSqr >= maxRadiusWorldSqr) continue;
|
||||
|
||||
#if EDGE_FUSION_ANTI_FLICKER
|
||||
float softW = 1.0 / (distSqr + 1e-10);
|
||||
sumSoftW += softW;
|
||||
accumEdgeUV += b * softW;
|
||||
accumEdgeWpos += wposB * softW;
|
||||
#else
|
||||
if (distSqr >= bestDistSqr) continue;
|
||||
#endif
|
||||
|
||||
// Winner tracking drives oidEdge and the early-exit count. When anti-flicker is OFF, this is also where edgeUV/edgeWpos are set
|
||||
if (distSqr < bestDistSqr) {
|
||||
bestDistSqr = distSqr;
|
||||
oidEdge = oidB;
|
||||
#if !EDGE_FUSION_ANTI_FLICKER
|
||||
edgeUV = b;
|
||||
edgeWpos = wposB;
|
||||
#endif
|
||||
edgeFactor++;
|
||||
if (edgeFactor >= _EarlyExitHits) break;
|
||||
|
||||
#if EDGE_FUSION_INTRA_OBJECT || EDGE_FUSION_CONCAVE_ONLY
|
||||
edgeCanUseSameIds = sameIds;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (edgeFactor <= 0.0) return 0.0;
|
||||
|
||||
#if EDGE_FUSION_ANTI_FLICKER
|
||||
// Replace edgeUV/edgeWpos with the weighted average and recompute bestDistSqr from the resolved edgeWpos
|
||||
edgeUV = accumEdgeUV / sumSoftW;
|
||||
edgeWpos = accumEdgeWpos / sumSoftW;
|
||||
bestDistSqr = dot2(edgeWpos - wposA);
|
||||
#endif
|
||||
|
||||
#if EDGE_FUSION_NOISE
|
||||
float noise = SAMPLE_TEXTURE3D_LOD(_NoiseTex3D, sampler_LinearRepeat, wposA * NOISE_SCALE, 0).r;
|
||||
noise = smoothstep(0.5 - NOISE_CONTRAST, 0.5 + NOISE_CONTRAST, noise);
|
||||
noise *= NOISE_INTENSITY;
|
||||
edgeUV -= (uvA - edgeUV) * noise;
|
||||
#endif
|
||||
|
||||
// Compute neighbour/mirrored pixel UV
|
||||
float2 deltaUV = edgeUV - uvA;
|
||||
neighbourUV = edgeUV + deltaUV;
|
||||
if (neighbourUV.x < 0.0 || neighbourUV.x > 1.0 || neighbourUV.y < 0.0 || neighbourUV.y > 1.0) return 0.0;
|
||||
float4 neighbourData = SampleObjectData(neighbourUV);
|
||||
|
||||
// Is still different object?
|
||||
#if !EDGE_FUSION_INTRA_OBJECT && !EDGE_FUSION_CONCAVE_ONLY
|
||||
if (SameObjectIds(neighbourData, oidA)) return 0.0;
|
||||
#endif
|
||||
|
||||
// Is still the edge object?
|
||||
if (!SameObjectIds(neighbourData, oidEdge)) return 0.0;
|
||||
|
||||
// Check neighbour distance (real 3D, using the neighbour's own depth).
|
||||
float3 neighbourWpos = GetWorldPos(neighbourUV, neighbourData.y);
|
||||
float neighbourDistSqr = dot2(neighbourWpos - edgeWpos);
|
||||
|
||||
// Distance falloff
|
||||
float neighbourLinearDepth = LinearEyeDepth(neighbourData.y, _ZBufferParams);
|
||||
float neighbourUvRadiusBase = GetRadiusUV(neighbourData, neighbourLinearDepth);
|
||||
float neighbourUvRadiusEff = (radiusLimit * neighbourUvRadiusBase) / (neighbourUvRadiusBase + radiusLimit + 1e-6);
|
||||
float falloffRadius = min(uvRadiusEff, neighbourUvRadiusEff);
|
||||
float reductionFactorSS = length(deltaUV) / falloffRadius;
|
||||
float reductionFactorWS = sqrt( max(bestDistSqr, neighbourDistSqr)) / compensatedDefaultRadius;
|
||||
edgeFactor = saturate(1.0 - max(reductionFactorSS, reductionFactorWS));
|
||||
|
||||
// Concavity test
|
||||
#if EDGE_FUSION_CONCAVE_ONLY
|
||||
if (SameObjectIds(neighbourData, oidA)) {
|
||||
float3 normalNVS = GetNormal(neighbourData);
|
||||
float3 dVS = mul((float3x3)UNITY_MATRIX_V, neighbourWpos - wposA);
|
||||
float3 dnVS = normalNVS - normalA;
|
||||
float s = dot(dVS, dnVS);
|
||||
float nn = dot(dnVS, dnVS);
|
||||
const float epsilon = 0.0004;
|
||||
bool isConcave = (2.0 * s + epsilon * nn) < 0.0;
|
||||
if (!isConcave) return 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return edgeFactor;
|
||||
}
|
||||
|
||||
#endif // EDGE_FUSION_BLEND_LIB_INCLUDED
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c0dbc0469bfe9f4faa064a164230120
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef EDGE_FUSION_BLEND_PASS_INCLUDED
|
||||
#define EDGE_FUSION_BLEND_PASS_INCLUDED
|
||||
|
||||
#include "EdgeFusionBlendLibrary.hlsl"
|
||||
|
||||
float4 FragBlendEdges(EFVaryings i) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
||||
float2 uv = UnityStereoTransformScreenSpaceTex(i.texcoord);
|
||||
|
||||
float4 c0 = SampleColor(uv);
|
||||
|
||||
float2 uvSurfaceB;
|
||||
float edgeFactor = FindEdgeNeighbour(uv, uvSurfaceB);
|
||||
if (edgeFactor <= 0.0) return c0;
|
||||
|
||||
#if EDGE_FUSION_MSAA_EDGE_FIX
|
||||
float2 dir = normalize(uvSurfaceB - uv) * _SrcColorTexture_TexelSize.xy * MSAA_FIX_POWER;
|
||||
c0 = SampleColor(uv - dir);
|
||||
float4 surf0 = SampleColor(uvSurfaceB + dir);
|
||||
#else
|
||||
float4 surf0 = SampleColor(uvSurfaceB);
|
||||
#endif
|
||||
|
||||
float fallOff = 2;
|
||||
|
||||
// screen-edge "vignette" fade
|
||||
float2 distUV = min(uv, 1.0 - uv);
|
||||
float edgeFade = min(distUV.x, distUV.y);
|
||||
float2 distUVB = min(uvSurfaceB, 1.0 - uvSurfaceB);
|
||||
float edgeFadeB = min(distUVB.x, distUVB.y);
|
||||
edgeFade = min(edgeFade, edgeFadeB);
|
||||
edgeFade = 1 - smoothstep(0, 0.05, edgeFade);
|
||||
fallOff += edgeFade * 4.0;
|
||||
|
||||
// avoid brightening shadows
|
||||
float lumaBase = GetLuma(c0.rgb);
|
||||
float invLuma2 = rcp(lumaBase * lumaBase + 1e-6);
|
||||
fallOff += clamp( SHADOW_PROTECTION * invLuma2, 0, 16);
|
||||
|
||||
// apply falloff
|
||||
float blendAmount = pow(edgeFactor, fallOff);
|
||||
|
||||
// blend
|
||||
blendAmount *= GLOBAL_INTENSITY * 0.5;
|
||||
float3 blended = ColorLerp(c0.rgb, surf0.rgb, blendAmount);
|
||||
|
||||
return float4(blended, c0.a);
|
||||
|
||||
}
|
||||
|
||||
#endif // EDGE_FUSION_BLEND_PASS_INCLUDED
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b2c3aa5891859e42accbbf36354a1ae
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,190 @@
|
||||
#ifndef EDGE_FUSION_COMMON_INCLUDED
|
||||
#define EDGE_FUSION_COMMON_INCLUDED
|
||||
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
|
||||
|
||||
TEXTURE2D_X(_ObjectIDTexture);
|
||||
TEXTURE2D_X(_SrcColorTexture);
|
||||
TEXTURE3D(_NoiseTex3D);
|
||||
TEXTURE2D_X(_CustomGroupTexture);
|
||||
|
||||
// Packed float parameters
|
||||
float4 _BlendData1; // x: GlobalIntensity, y: MaxBlendDistance, z: NormalThreshold, w: NoiseContrast
|
||||
float4 _BlendData2; // x: MaxScreenRadius, y: ShadowProtection, z: NoiseIntensity, w: NoiseScale
|
||||
|
||||
// Macros for accessing packed parameters
|
||||
#define GLOBAL_INTENSITY _BlendData1.x
|
||||
#define MAX_BLEND_DISTANCE _BlendData1.y
|
||||
#define NORMAL_THRESHOLD _BlendData1.z
|
||||
#define NOISE_CONTRAST _BlendData1.w
|
||||
#define MAX_SCREEN_RADIUS _BlendData2.x
|
||||
#define SHADOW_PROTECTION _BlendData2.y
|
||||
#define NOISE_INTENSITY _BlendData2.z
|
||||
#define NOISE_SCALE _BlendData2.w
|
||||
|
||||
float _DefaultRadiusWorld;
|
||||
float _RadiusScale;
|
||||
float _DistanceCompensation;
|
||||
int _SampleCount;
|
||||
int _BinarySearchSteps;
|
||||
int _EarlyExitHits;
|
||||
int _DebugMode;
|
||||
float _DepthDebugMultiplier;
|
||||
float4 _SrcColorTexture_TexelSize;
|
||||
float4 _CameraDepthTexture_TexelSize;
|
||||
float _MSAAFixPower;
|
||||
|
||||
#define MSAA_FIX_POWER _MSAAFixPower
|
||||
|
||||
inline float4 SampleColor(float2 uv) {
|
||||
return SAMPLE_TEXTURE2D_X_LOD(_SrcColorTexture, sampler_LinearClamp, uv, 0);
|
||||
}
|
||||
|
||||
inline float4 SampleObjectData(float2 uv) {
|
||||
return SAMPLE_TEXTURE2D_X_LOD(_ObjectIDTexture, sampler_PointClamp, uv, 0);
|
||||
}
|
||||
|
||||
inline bool SameObjectIds(float4 objA, float4 objB) {
|
||||
return abs(objA.r - objB.r) < 0.8;
|
||||
}
|
||||
|
||||
// Packing helpers: pack object ID and normalized radius into the red channel
|
||||
#define EF_PACK_QUANT 0.01
|
||||
|
||||
inline float PackObjectIdRadius(float objectId, float radius) {
|
||||
#if defined(SHADER_API_MOBILE) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLES) || defined(SHADER_API_WEBGPU)
|
||||
uint id = asuint(objectId);
|
||||
id = ((id >> 16) ^ id) * 0x45d9f3b;
|
||||
id = ((id >> 16) ^ id) * 0x45d9f3b;
|
||||
id = (id >> 16) ^ id;
|
||||
float baseId = float(id % 32u);
|
||||
#else
|
||||
float baseId = floor(objectId / EF_PACK_QUANT);
|
||||
baseId = fmod(baseId, 4096.0);
|
||||
#endif
|
||||
return baseId + radius;
|
||||
}
|
||||
|
||||
inline float UnpackIdFromPacked(float packed) {
|
||||
#if defined(SHADER_API_MOBILE) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLES) || defined(SHADER_API_WEBGPU)
|
||||
return float(uint(floor(packed) + 0.5));
|
||||
#else
|
||||
return floor(packed) * EF_PACK_QUANT;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline float UnpackRadiusFromPacked(float packed) {
|
||||
return frac(packed);
|
||||
}
|
||||
|
||||
float ConvertWorldRadiusToUV(float radius, float linearDepth) {
|
||||
radius *= (1.0 + _DistanceCompensation * linearDepth);
|
||||
float denom = lerp(max(linearDepth, 1.0), 1.0, unity_OrthoParams.w);
|
||||
return 0.5 * radius * unity_CameraProjection._m11 / denom;
|
||||
}
|
||||
|
||||
float GetRadiusUV(float4 objectData, float linearDepth) {
|
||||
float radius = UnpackRadiusFromPacked(objectData.r) * _RadiusScale;
|
||||
return ConvertWorldRadiusToUV(radius, linearDepth);
|
||||
}
|
||||
|
||||
float3 GetWorldPos(float2 uv, float depth) {
|
||||
#if !UNITY_REVERSED_Z
|
||||
depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, depth);
|
||||
#endif
|
||||
|
||||
float3 viewPos = ComputeViewSpacePosition(uv, depth, UNITY_MATRIX_I_P);
|
||||
return mul(UNITY_MATRIX_I_V, float4(viewPos, 1.0)).xyz;
|
||||
}
|
||||
|
||||
float3 ReconstructNormal(float2 uv, float depthC) {
|
||||
// Use 3-tap reconstruction
|
||||
float2 texelSize = _CameraDepthTexture_TexelSize.xy;
|
||||
|
||||
// Sample depths at neighbor pixels
|
||||
float depthR = SAMPLE_TEXTURE2D_X_LOD(_CameraDepthTexture, sampler_PointClamp, uv + float2(texelSize.x, 0), 0).r;
|
||||
float depthU = SAMPLE_TEXTURE2D_X_LOD(_CameraDepthTexture, sampler_PointClamp, uv + float2(0, texelSize.y), 0).r;
|
||||
|
||||
// Convert to world positions
|
||||
float3 pC = GetWorldPos(uv, depthC);
|
||||
float3 pR = GetWorldPos(uv + float2(texelSize.x, 0), depthR);
|
||||
float3 pU = GetWorldPos(uv + float2(0, texelSize.y), depthU);
|
||||
|
||||
// Compute derivatives
|
||||
float3 dpdx = normalize(pR - pC);
|
||||
float3 dpdy = normalize(pU - pC);
|
||||
|
||||
// Cross product for normal
|
||||
float3 n = cross(dpdy, dpdx);
|
||||
return n;
|
||||
}
|
||||
|
||||
float3 ReconstructNormalWS(float3 worldPos) {
|
||||
float3 dpdx = ddx(worldPos);
|
||||
float3 dpdy = ddy(worldPos);
|
||||
float3 normalWS = cross(dpdy, dpdx);
|
||||
return dot(normalWS, normalWS) != 0 ? normalize(normalWS) : float3(0, 0, 1);
|
||||
}
|
||||
|
||||
inline float3 GetNormal(float4 objectData) {
|
||||
float2 nxy = objectData.zw;
|
||||
float nz = sqrt(saturate(1.0 - dot(nxy, nxy)));
|
||||
return float3(nxy, nz);
|
||||
}
|
||||
|
||||
float2 WorldToScreenPos(float3 wpos) {
|
||||
float4 clip = TransformWorldToHClip(wpos);
|
||||
float4 sp = ComputeScreenPos(clip);
|
||||
float2 uv = saturate(sp.xy / sp.w);
|
||||
return UnityStereoTransformScreenSpaceTex(uv);
|
||||
}
|
||||
|
||||
struct EFVaryings
|
||||
{
|
||||
float4 positionCS : SV_POSITION;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
};
|
||||
|
||||
struct EFFullscreenAttributes
|
||||
{
|
||||
uint vertexID : SV_VertexID;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
};
|
||||
|
||||
EFVaryings EFVertFullscreen(EFFullscreenAttributes input)
|
||||
{
|
||||
EFVaryings o;
|
||||
UNITY_SETUP_INSTANCE_ID(input);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
||||
o.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
|
||||
o.texcoord = GetFullScreenTriangleTexCoord(input.vertexID);
|
||||
return o;
|
||||
}
|
||||
|
||||
#if EDGE_FUSION_ID_EXCLUSION
|
||||
float _ExclusionMask[32];
|
||||
|
||||
inline uint GetExclusionSlot(float4 objData) {
|
||||
float id = UnpackIdFromPacked(objData.r);
|
||||
uint slot = uint(id + 0.5);
|
||||
return (slot >= 1u && slot <= 32u) ? slot : 0u;
|
||||
}
|
||||
|
||||
inline bool IsExcludedIdPair(float4 objA, float4 objB) {
|
||||
uint slotA = GetExclusionSlot(objA);
|
||||
uint slotB = GetExclusionSlot(objB);
|
||||
|
||||
if (slotA == 0u || slotB == 0u) return false;
|
||||
|
||||
uint idxA = slotA - 1u;
|
||||
uint idxB = slotB - 1u;
|
||||
|
||||
return (((uint)_ExclusionMask[idxA] & (1u << idxB)) |
|
||||
((uint)_ExclusionMask[idxB] & (1u << idxA))) != 0u;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // EDGE_FUSION_COMMON_INCLUDED
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90b602d422344ee44ab8925d0c8af36e
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#ifndef EDGE_FUSION_COMPARE_PASS_INCLUDED
|
||||
#define EDGE_FUSION_COMPARE_PASS_INCLUDED
|
||||
|
||||
TEXTURE2D_X(_CompareTex);
|
||||
float4 _CompareParams; // x: cos(angle), y: sin(angle), z: panning or flag, w: line width
|
||||
float4 _CompareLineColor; // rgba line color
|
||||
|
||||
float4 FragCompare(EFVaryings i) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
||||
float2 uv = UnityStereoTransformScreenSpaceTex(i.texcoord);
|
||||
|
||||
float2 dd = uv - 0.5.xx;
|
||||
float co = dot(_CompareParams.xy, dd);
|
||||
float dist = distance(_CompareParams.xy * co, dd);
|
||||
float aa = saturate((_CompareParams.w - dist) / abs(_SrcColorTexture_TexelSize.y));
|
||||
|
||||
float sameSide = (_CompareParams.z > -5);
|
||||
float2 pixelUV = lerp(uv, float2(uv.x + _CompareParams.z, uv.y), sameSide);
|
||||
float2 pixelNiceUV = lerp(uv, float2(uv.x - 0.5 + _CompareParams.z, uv.y), sameSide);
|
||||
float4 original = SAMPLE_TEXTURE2D_X(_SrcColorTexture, sampler_PointClamp, pixelUV);
|
||||
float4 effected = SAMPLE_TEXTURE2D_X(_CompareTex, sampler_PointClamp, pixelNiceUV);
|
||||
|
||||
float2 cp = float2(_CompareParams.y, -_CompareParams.x);
|
||||
float t = dot(dd, cp) > 0;
|
||||
float4 outCol = lerp(original, effected, t);
|
||||
outCol = lerp(outCol, _CompareLineColor, aa);
|
||||
return outCol;
|
||||
}
|
||||
|
||||
#endif // EDGE_FUSION_COMPARE_PASS_INCLUDED
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f1c46064363fae48801f64dffdebeca
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef EDGE_FUSION_DEBUG_PASS_INCLUDED
|
||||
#define EDGE_FUSION_DEBUG_PASS_INCLUDED
|
||||
|
||||
#include "EdgeFusionBlendLibrary.hlsl"
|
||||
|
||||
// Keep in sync with DebugMode enum in EdgeFusion.cs
|
||||
#define EF_DEBUG_OBJECT_IDS 1
|
||||
#define EF_DEBUG_EDGES 2
|
||||
#define EF_DEBUG_BLENDING 3
|
||||
#define EF_DEBUG_NORMALS 4
|
||||
#define EF_DEBUG_DEPTH 5
|
||||
#define EF_DEBUG_SPECIAL_GROUP 6
|
||||
|
||||
float4 FragDebug(EFVaryings i) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
||||
float2 uv = UnityStereoTransformScreenSpaceTex(i.texcoord);
|
||||
|
||||
float4 baseColor = SampleColor(uv);
|
||||
|
||||
float4 oidA = SampleObjectData(uv);
|
||||
float id = oidA.x;
|
||||
if (abs(id) <= 1e-6) return baseColor;
|
||||
float3 color = HashToColor(floor(id));
|
||||
|
||||
if (_DebugMode == EF_DEBUG_OBJECT_IDS) {
|
||||
return float4(color, 1.0);
|
||||
}
|
||||
|
||||
float linearDepth = LinearEyeDepth(oidA.y, _ZBufferParams);
|
||||
if (linearDepth > MAX_BLEND_DISTANCE) return baseColor;
|
||||
|
||||
if (_DebugMode == EF_DEBUG_DEPTH) {
|
||||
float depth01 = saturate(_DepthDebugMultiplier * linearDepth / MAX_BLEND_DISTANCE);
|
||||
return float4(saturate(depth01).xxx, 1.0);
|
||||
}
|
||||
|
||||
if (_DebugMode == EF_DEBUG_EDGES) {
|
||||
float2 nearestEdgeUV;
|
||||
float foundEdge = FindEdgeNeighbour(uv, nearestEdgeUV);
|
||||
if (foundEdge > 0) {
|
||||
float edgeDistance = length( (nearestEdgeUV - uv) * _ScreenParams.xy);
|
||||
if (edgeDistance < 2.0) {
|
||||
return float4(saturate(1.0 - color.rgb), 1.0);
|
||||
}
|
||||
}
|
||||
return float4(color, 1.0);
|
||||
}
|
||||
|
||||
if (_DebugMode == EF_DEBUG_NORMALS) {
|
||||
float3 normalA = GetNormal(oidA);
|
||||
return float4(normalA * 0.5 + 0.5, 1.0);
|
||||
}
|
||||
|
||||
if (_DebugMode == EF_DEBUG_BLENDING) {
|
||||
float2 uvNeighbour;
|
||||
float foundEdge = FindEdgeNeighbour(uv, uvNeighbour);
|
||||
float blendAmount = foundEdge * foundEdge;
|
||||
blendAmount *= (0.5 * GLOBAL_INTENSITY);
|
||||
color.rgb = blendAmount;
|
||||
return float4(color, 1.0);
|
||||
}
|
||||
|
||||
if (_DebugMode == EF_DEBUG_SPECIAL_GROUP) {
|
||||
bool isSpecial = UnpackIdFromPacked(oidA.r) == 32;
|
||||
return isSpecial ? float4(1, 0.6, 0.1, 1) : baseColor;
|
||||
}
|
||||
|
||||
return float4(color, 1.0);
|
||||
}
|
||||
|
||||
#endif // EDGE_FUSION_DEBUG_PASS_INCLUDED
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 931c347579516b14f93842c44318e58b
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
Shader "Hidden/Kronnect/EdgeFusion/EdgeFusionFill"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
ZTest Always
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "EdgeFusion Fill Object ID"
|
||||
HLSLPROGRAM
|
||||
#pragma target 3.0
|
||||
#pragma vertex EFVertFullscreen
|
||||
#pragma fragment FragFillObjectID
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_SPECIAL_GROUP
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_INTRA_OBJECT EDGE_FUSION_CONCAVE_ONLY
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_DEBUG_BLEND_NORMALS
|
||||
#include "EdgeFusionCommon.hlsl"
|
||||
#include "EdgeFusionFillPass.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab67280516f03c148bb44fbf806aacb1
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef EDGE_FUSION_FILL_PASS_INCLUDED
|
||||
#define EDGE_FUSION_FILL_PASS_INCLUDED
|
||||
|
||||
#include "EdgeFusionCommon.hlsl"
|
||||
|
||||
// Reserved object ID for special group (ID 32)
|
||||
#define CUSTOM_OBJECT_ID 32.0
|
||||
|
||||
// Terrain object ID passed from script (31.0 when blendWithWorld is true, 0.0 otherwise)
|
||||
float _TerrainObjectId;
|
||||
|
||||
float4 FragFillObjectID(EFVaryings i) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
||||
float2 uv = UnityStereoTransformScreenSpaceTex(i.texcoord);
|
||||
|
||||
float4 objectData = SampleObjectData(uv);
|
||||
|
||||
// Negative depth marks excluded pixels
|
||||
if (objectData.y < 0) {
|
||||
return float4(0,0,0,0);
|
||||
}
|
||||
|
||||
// If pixel already has an object ID, keep it unchanged
|
||||
if (abs(objectData.r) > 1e-6) {
|
||||
return objectData;
|
||||
}
|
||||
|
||||
// This pixel is empty (terrain/excluded geometry)
|
||||
float rawDepth = SampleSceneDepth(uv);
|
||||
float linearDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
|
||||
|
||||
// Only fill if within blend distance
|
||||
if (linearDepth >= MAX_BLEND_DISTANCE) {
|
||||
return objectData; // Keep empty
|
||||
}
|
||||
|
||||
float3 nVS;
|
||||
#if EDGE_FUSION_INTRA_OBJECT || EDGE_FUSION_CONCAVE_ONLY || EDGE_FUSION_DEBUG_BLEND_NORMALS
|
||||
float3 worldPos = GetWorldPos(uv, rawDepth);
|
||||
float3 nWS = ReconstructNormalWS(worldPos);
|
||||
nVS = mul((float3x3)UNITY_MATRIX_V, nWS);
|
||||
nVS = normalize(nVS);
|
||||
#else
|
||||
nVS = float3(0, 0, 1);
|
||||
#endif
|
||||
|
||||
#if EDGE_FUSION_SPECIAL_GROUP
|
||||
float3 marker = SAMPLE_TEXTURE2D_X_LOD(_CustomGroupTexture, sampler_PointClamp, uv, 0).rgb;
|
||||
bool isCustom = any(marker > 0.0001);
|
||||
float objectId = isCustom ? CUSTOM_OBJECT_ID : _TerrainObjectId;
|
||||
#else
|
||||
float objectId = _TerrainObjectId;
|
||||
#endif
|
||||
|
||||
// If objectId is 0, return empty (no blending for terrain when blendWithWorld is false)
|
||||
if (objectId < 0.5) {
|
||||
return objectData;
|
||||
}
|
||||
|
||||
float packedR = PackObjectIdRadius(objectId, _DefaultRadiusWorld / _RadiusScale);
|
||||
return float4(packedR, rawDepth, nVS.xy);
|
||||
}
|
||||
|
||||
#endif // EDGE_FUSION_FILL_PASS_INCLUDED
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4a984358a7f7a544832e9bbf7dacde9
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
Shader "Kronnect/EdgeFusion/ObjectID"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
|
||||
_EdgeFusionRadius("Edge Fusion Radius", Float) = 0
|
||||
_CustomObjectId("Custom Object ID", Float) = 0
|
||||
_DisallowIntraFusion("Disallow Intra Fusion", Float) = 0
|
||||
[HideInInspector] _ZWrite("ZWrite", Int) = 0
|
||||
[HideInInspector] _ZTest("ZTest", Int) = 3 // Equal = Make it work with cutout materials
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
Name "Edge Fusion Object ID"
|
||||
ZWrite [_ZWrite]
|
||||
ZTest [_ZTest]
|
||||
Cull [_Cull]
|
||||
|
||||
HLSLPROGRAM
|
||||
#if UNITY_PLATFORM_ANDROID || (UNITY_PLATFORM_WEBGL && !SHADER_API_WEBGPU) || UNITY_PLATFORM_UWP
|
||||
#pragma target 3.5
|
||||
#else
|
||||
#pragma target 4.5
|
||||
#endif
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile_instancing
|
||||
#pragma instancing_options nolightprobe nolightmap
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_MSAA_ON
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_INTRA_OBJECT EDGE_FUSION_CONCAVE_ONLY
|
||||
#pragma multi_compile_local_fragment _ EDGE_FUSION_DEBUG_BLEND_NORMALS
|
||||
#pragma multi_compile _ LOD_FADE_CROSSFADE
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityDOTSInstancing.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UniversalDOTSInstancing.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
|
||||
#include "EdgeFusionCommon.hlsl"
|
||||
|
||||
#if defined(LOD_FADE_CROSSFADE)
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LODCrossFade.hlsl"
|
||||
#endif
|
||||
|
||||
struct ObjIDAttributes
|
||||
{
|
||||
float4 positionOS : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float3 normalOS : NORMAL;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
};
|
||||
|
||||
struct ObjIDVaryings
|
||||
{
|
||||
float4 positionCS : SV_POSITION;
|
||||
nointerpolation float objectID : TEXCOORD0;
|
||||
float4 screenPos : TEXCOORD1;
|
||||
float viewDepth : TEXCOORD2;
|
||||
float3 worldPos : TEXCOORD3;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
};
|
||||
|
||||
TEXTURE2D(_BaseMap);
|
||||
SAMPLER(sampler_BaseMap);
|
||||
float _MaxBlendDistance;
|
||||
|
||||
CBUFFER_START(UnityPerMaterial)
|
||||
float _EdgeFusionRadius;
|
||||
float _CustomObjectId;
|
||||
float _DisallowIntraFusion;
|
||||
CBUFFER_END
|
||||
|
||||
#ifdef UNITY_DOTS_INSTANCING_ENABLED
|
||||
UNITY_DOTS_INSTANCING_START(ObjectIDMetadata)
|
||||
UNITY_DOTS_INSTANCED_PROP(float, _EdgeFusionRadius)
|
||||
UNITY_DOTS_INSTANCED_PROP(float, _CustomObjectId)
|
||||
UNITY_DOTS_INSTANCED_PROP(float, _DisallowIntraFusion)
|
||||
UNITY_DOTS_INSTANCING_END(ObjectIDMetadata)
|
||||
|
||||
#define _EdgeFusionRadius UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float, _EdgeFusionRadius)
|
||||
#define _CustomObjectId UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float, _CustomObjectId)
|
||||
#define _DisallowIntraFusion UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float, _DisallowIntraFusion)
|
||||
#endif
|
||||
|
||||
float GetIdFromObject() {
|
||||
float4x4 m = UNITY_MATRIX_M;
|
||||
float3 r0 = m[0].xyz;
|
||||
float3 r1 = m[1].xyz;
|
||||
float3 r2 = m[2].xyz;
|
||||
float3 pos = float3(m[0][3], m[1][3], m[2][3]);
|
||||
float objectID = dot(r0, float3(73.0, 157.0, 131.0))
|
||||
+ dot(r1, float3(269.0, 97.0, 211.0))
|
||||
+ dot(r2, float3(337.0, 41.0, 173.0))
|
||||
+ dot(pos, float3(521.0, 313.0, 463.0));
|
||||
return objectID;
|
||||
}
|
||||
|
||||
ObjIDVaryings vert(ObjIDAttributes input)
|
||||
{
|
||||
ObjIDVaryings output;
|
||||
UNITY_SETUP_INSTANCE_ID(input);
|
||||
UNITY_TRANSFER_INSTANCE_ID(input, output);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
|
||||
|
||||
float4 positionOS = input.positionOS;
|
||||
float3 normalOS = input.normalOS;
|
||||
|
||||
float3 positionWS = TransformObjectToWorld(positionOS.xyz);
|
||||
float3 positionVS = TransformWorldToView(positionWS);
|
||||
output.viewDepth = -positionVS.z;
|
||||
|
||||
output.positionCS = TransformObjectToHClip(positionOS.xyz);
|
||||
output.screenPos = ComputeScreenPos(output.positionCS);
|
||||
|
||||
float objectId = _CustomObjectId;
|
||||
if (objectId < 0.5) {
|
||||
objectId = GetIdFromObject();
|
||||
#if UNITY_ANY_INSTANCING_ENABLED
|
||||
objectId += unity_InstanceID;
|
||||
#endif
|
||||
objectId = fmod(abs(objectId), 4096.0);
|
||||
}
|
||||
|
||||
output.objectID = floor(objectId);
|
||||
|
||||
output.worldPos = positionWS;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 frag(ObjIDVaryings input) : SV_Target0
|
||||
{
|
||||
UNITY_SETUP_INSTANCE_ID(input);
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||||
|
||||
if (input.viewDepth > _MaxBlendDistance) return float4(0, 0, 0, 0);
|
||||
|
||||
#if defined(LOD_FADE_CROSSFADE)
|
||||
LODFadeCrossFade(input.positionCS);
|
||||
#endif
|
||||
|
||||
float objectId = input.objectID;
|
||||
float radius = _EdgeFusionRadius;
|
||||
|
||||
if (radius <= 0) {
|
||||
radius = _DefaultRadiusWorld;
|
||||
} else if (radius > 0.9 * _RadiusScale) {
|
||||
return float4(0, -1, 0, 0);
|
||||
}
|
||||
|
||||
float2 uv = input.screenPos.xy / input.screenPos.w;
|
||||
float sceneDepth = SampleSceneDepth(uv);
|
||||
|
||||
float rawDepth;
|
||||
#if EDGE_FUSION_MSAA_ON
|
||||
rawDepth = input.positionCS.z;
|
||||
#if UNITY_REVERSED_Z
|
||||
if (rawDepth < sceneDepth) return float4(0, 0, 0, 0);
|
||||
#else
|
||||
if (rawDepth > sceneDepth) return float4(0, 0, 0, 0);
|
||||
#endif
|
||||
#else
|
||||
rawDepth = sceneDepth;
|
||||
#endif
|
||||
|
||||
float linearDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
|
||||
float uvRadius = ConvertWorldRadiusToUV(radius, linearDepth);
|
||||
float screenRadiusPx = uvRadius * _ScreenParams.y;
|
||||
if (screenRadiusPx < 2.0) {
|
||||
return float4(0, -1, 0, 0);
|
||||
}
|
||||
|
||||
float packedR = PackObjectIdRadius(objectId, radius / _RadiusScale);
|
||||
|
||||
float3 nVS;
|
||||
#if EDGE_FUSION_INTRA_OBJECT || EDGE_FUSION_CONCAVE_ONLY || EDGE_FUSION_DEBUG_BLEND_NORMALS
|
||||
float3 nWS = ReconstructNormalWS(input.worldPos);
|
||||
nVS = mul((float3x3)UNITY_MATRIX_V, nWS);
|
||||
nVS = normalize(nVS);
|
||||
if (_DisallowIntraFusion > 0.5) {
|
||||
nVS.x = 999.0;
|
||||
}
|
||||
#else
|
||||
nVS = float3(0, 0, 1);
|
||||
#endif
|
||||
return float4(packedR, rawDepth, nVS.xy);
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d4153fbab3b0924c88c7b47c27b3916
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,137 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-2811601345551093397
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
version: 10
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: ObjectIdInstancing
|
||||
m_Shader: {fileID: 4800000, guid: 4da66f2095f944a4d83816603b7009f8, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 1
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AddPrecomputedVelocity: 0
|
||||
- _AlphaClip: 0
|
||||
- _AlphaToMask: 0
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EdgeFusionRadius: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 0
|
||||
- _Glossiness: 0
|
||||
- _GlossyReflections: 0
|
||||
- _Metallic: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.005
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.5
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _WorkflowMode: 1
|
||||
- _ZTest: 3
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef085a6fe167a7e47ae0264e8cf12fea
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8354dc0f4d4edc439c5ce3383c196cf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2372a4d5e24f5d48affa88948874eb8
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f21267322df7d0f409cee65a011925ef
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace EdgeFusion {
|
||||
|
||||
[Serializable]
|
||||
public class IdExclusionPair {
|
||||
public bool enabled = true;
|
||||
[Range(1, 32)] public int idA = 1;
|
||||
[Range(1, 32)] public int idB = 31;
|
||||
}
|
||||
|
||||
public enum QualityPreset {
|
||||
VeryLow = 4,
|
||||
Low = 8,
|
||||
Medium = 16,
|
||||
High = 24,
|
||||
VeryHigh = 32
|
||||
}
|
||||
|
||||
public enum DebugMode {
|
||||
None,
|
||||
ObjectIds,
|
||||
Edges,
|
||||
Blending,
|
||||
Normals,
|
||||
Depth,
|
||||
SpecialGroup
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public sealed class QualityPresetParameter : VolumeParameter<QualityPreset> {
|
||||
public QualityPresetParameter (QualityPreset value, bool overrideState = false) : base(value, overrideState) { }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class DebugModeParameter : VolumeParameter<DebugMode> {
|
||||
public DebugModeParameter (DebugMode value, bool overrideState = false) : base(value, overrideState) { }
|
||||
}
|
||||
|
||||
// PORT NOTE (Unity 2022.3 / URP 14): RenderingLayerMaskParameter is part of
|
||||
// com.unity.render-pipelines.core 17.x (Unity 6) and does not exist in URP 14.
|
||||
// This is a minimal drop-in replacement (same constructor signature and the
|
||||
// inherited `.value` uint property used everywhere in this codebase) so the
|
||||
// component compiles and the rendering-layer filters still work as plain
|
||||
// bitmasks. It draws as a generic uint field in the Inspector rather than the
|
||||
// specialized Rendering Layer Mask picker Unity 6 provides - you'll type the
|
||||
// bitmask manually (or wire your own PropertyDrawer) rather than pick names
|
||||
// from a dropdown.
|
||||
[Serializable]
|
||||
public sealed class RenderingLayerMaskParameter : VolumeParameter<uint> {
|
||||
public RenderingLayerMaskParameter (uint value, bool overrideState = false) : base(value, overrideState) { }
|
||||
}
|
||||
|
||||
|
||||
[HelpURL("https://kronnect.com/guides/edge-fusion-introduction/")]
|
||||
[VolumeComponentMenu("Kronnect/Edge Fusion")]
|
||||
public class EdgeFusion : VolumeComponent {
|
||||
|
||||
[Tooltip("Default layers that will be considered for edge fusion blending.")]
|
||||
public LayerMaskParameter blendLayers = new LayerMaskParameter(-1);
|
||||
|
||||
[Tooltip("Optional Rendering Layer Filter for single-sided objects selected by Blend Layers (rendering layer can be specified in the object renderer).")]
|
||||
public RenderingLayerMaskParameter renderingLayerFilter = new RenderingLayerMaskParameter(uint.MaxValue);
|
||||
|
||||
[Tooltip("When enabled, visible geometry not selected by any layer setting will also blend with marked objects. Disable to restrict blending to explicitly selected layers only.")]
|
||||
public BoolParameter blendWithOthers = new BoolParameter(true);
|
||||
|
||||
[Tooltip("Layers that will be rendered double-sided in the ObjectID pass.")]
|
||||
public LayerMaskParameter doubleSidedLayers = new LayerMaskParameter(0);
|
||||
|
||||
[Tooltip("Rendering layer filter for Double-Sided selection.")]
|
||||
public RenderingLayerMaskParameter doubleSidedRenderingLayerFilter = new RenderingLayerMaskParameter(uint.MaxValue);
|
||||
|
||||
[Tooltip("GameObjects that use special vertex shaders that should also blend")]
|
||||
public LayerMaskParameter specialGroupLayers = new LayerMaskParameter(0);
|
||||
|
||||
[Tooltip("Rendering layer filter for Special Group selection.")]
|
||||
public RenderingLayerMaskParameter specialGroupRenderingLayerFilter = new RenderingLayerMaskParameter(uint.MaxValue);
|
||||
|
||||
[Tooltip("Overall intensity of the edge fusion effect.")]
|
||||
public ClampedFloatParameter intensity = new ClampedFloatParameter(0.0f, 0.0f, 1.0f);
|
||||
|
||||
[Tooltip("Default blur radius in world units (meters). Use Distance Compensation to increase effective radius at distance.")]
|
||||
public ClampedFloatParameter radius = new ClampedFloatParameter(0.035f, 0.0001f, 0.5f);
|
||||
|
||||
[Tooltip("Scales the effective radius proportionally with distance from the camera. Use this to keep a small radius up close while increasing it for distant objects.")]
|
||||
public ClampedFloatParameter distanceCompensation = new ClampedFloatParameter(0f, 0f, 0.1f);
|
||||
|
||||
[Tooltip("Enable fusion of edges within the same object (based on normal/depth discontinuities)")]
|
||||
[InspectorName("Intra-Object Fusion")]
|
||||
public BoolParameter enableIntraObjectFusion = new BoolParameter(false);
|
||||
|
||||
[Tooltip("When enabled, intra-object fusion only runs on renderers that have an Edge Fusion Object component with the effect allowed; fill-pass normals are skipped.")]
|
||||
public BoolParameter intraObjectFusionPerObject = new BoolParameter(false);
|
||||
|
||||
[Tooltip("Fuse concave edges only. When disabled, both concave and convex edges are fused.")]
|
||||
public BoolParameter concavityTest = new BoolParameter(false);
|
||||
|
||||
[Tooltip("Normal threshold for detecting edges within the same object (only used when Intra-Object Fusion is enabled)")]
|
||||
public ClampedFloatParameter normalThreshold = new ClampedFloatParameter(0.6f, 0.001f, 0.95f);
|
||||
|
||||
public ClampedIntParameter sampleCount = new ClampedIntParameter(24, 4, 32);
|
||||
|
||||
public BoolParameter jitter = new BoolParameter(false);
|
||||
|
||||
public FloatParameter maxBlendDistance = new FloatParameter(50f);
|
||||
|
||||
[Tooltip("Number of binary search refinement steps when locating nearest edge")]
|
||||
public ClampedIntParameter binarySearchSteps = new ClampedIntParameter(7, 1, 10);
|
||||
|
||||
[Tooltip("Max number of edge hits before early exiting the neighbour search loop")]
|
||||
public ClampedIntParameter earlyExitHits = new ClampedIntParameter(5, 1, 32);
|
||||
|
||||
[Tooltip("Stabilises the blending when two edge candidates are near-tie in distance. Instead of picking one winner per frame, averages them with a soft-argmin so edgeUV moves continuously. Helps in any scene with sub-pixel motion between frames (VR pose, slow cameras, animated objects, FPS mouse look, camera shake). Disable to match the classic pick-one behaviour.")]
|
||||
public BoolParameter antiFlicker = new BoolParameter(false);
|
||||
|
||||
public DebugModeParameter debugMode = new DebugModeParameter(DebugMode.None);
|
||||
public ClampedFloatParameter depthDebugMultiplier = new ClampedFloatParameter(1.0f, 0.01f, 100.0f);
|
||||
[Tooltip("Adjusts color sampling to reduce MSAA edge artifacts (MSAA only).")]
|
||||
public MinFloatParameter msaaEdgeFixPower = new MinFloatParameter(0f, 0f);
|
||||
public BoolParameter compareMode = new BoolParameter(false);
|
||||
public BoolParameter compareSameSide = new BoolParameter(false);
|
||||
public ClampedFloatParameter comparePanning = new ClampedFloatParameter(0.25f, 0f, 0.5f);
|
||||
public ClampedFloatParameter compareLineAngle = new ClampedFloatParameter(1.4f, -Mathf.PI, Mathf.PI);
|
||||
public ClampedFloatParameter compareLineWidth = new ClampedFloatParameter(0.002f, 0.0001f, 0.05f);
|
||||
public ColorParameter compareLineColor = new ColorParameter(Color.white);
|
||||
|
||||
public ClampedFloatParameter maxScreenRadius = new ClampedFloatParameter(0.1f, 0.01f, 0.5f);
|
||||
|
||||
[Tooltip("Threshold for shadow detection and blending adjustment")]
|
||||
public ClampedFloatParameter shadowProtection = new ClampedFloatParameter(0.0002f, 0f, 0.01f);
|
||||
|
||||
[Tooltip("Intensity of the 3D noise texture applied to blending (0 = no noise, 1 = full noise influence)")]
|
||||
public ClampedFloatParameter noiseIntensity = new ClampedFloatParameter(0.0f, 0.0f, 1.0f);
|
||||
|
||||
[Tooltip("Scale of the 3D noise texture sampling (higher values = finer noise)")]
|
||||
public ClampedFloatParameter noiseScale = new ClampedFloatParameter(5.0f, 0.1f, 10.0f);
|
||||
|
||||
[Tooltip("Contrast of the noise applied to edge positions (0 = soft, 1 = sharp)")]
|
||||
public ClampedFloatParameter noiseContrast = new ClampedFloatParameter(0.75f, 0.0f, 1.0f);
|
||||
|
||||
[Header("ID Exclusions")]
|
||||
[Tooltip("Pairs of custom Object IDs that should not blend with each other. IDs 1-30 are user-assignable, 31 = Terrain (non-blend-layer objects), 32 = Special Group.")]
|
||||
public IdExclusionPair[] idExclusionPairs = System.Array.Empty<IdExclusionPair>();
|
||||
|
||||
public bool IsActive () {
|
||||
return (blendLayers.value != 0 || doubleSidedLayers.value != 0 || specialGroupLayers.value != 0) && intensity.value > 0f;
|
||||
}
|
||||
|
||||
public void OnValidate () {
|
||||
maxBlendDistance.value = Mathf.Max(maxBlendDistance.value, 0f);
|
||||
msaaEdgeFixPower.value = Mathf.Max(msaaEdgeFixPower.value, 0f);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fee512453ccf15742b376d2f6e1ddeb9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,159 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EdgeFusion {
|
||||
|
||||
public enum IncludeMode {
|
||||
OnlyThisObject,
|
||||
IncludeChildren
|
||||
}
|
||||
|
||||
[AddComponentMenu("Kronnect/Edge Fusion/Edge Fusion Object")]
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
[HelpURL("https://kronnect.com/guides/edge-fusion-introduction/")]
|
||||
public class EdgeFusionObject : MonoBehaviour {
|
||||
|
||||
public bool overrideRadius;
|
||||
[Tooltip("Custom blend radius in meters (0 = disables blending for this object)")]
|
||||
[Range(0f, 0.5f)]
|
||||
public float customRadius = 0.05f;
|
||||
|
||||
public bool overrideObjectId;
|
||||
[Tooltip("Custom Object ID (0 = auto, >0 = override). Objects with the same ID are considered the same for inter-object detection.")]
|
||||
public int customObjectId;
|
||||
[Tooltip("Use a random Object ID, instead of an Id derived from object position")]
|
||||
public bool useRandomId;
|
||||
[Tooltip("Assign a different random ID to each child renderer")]
|
||||
public bool randomIdPerChild;
|
||||
|
||||
[Tooltip("Disable intra-object fusion for this object/children while keeping inter-object fusion.")]
|
||||
public bool disallowIntraObjectFusion;
|
||||
|
||||
public IncludeMode includeMode = IncludeMode.OnlyThisObject;
|
||||
public LayerMask childLayerMask = ~0;
|
||||
|
||||
static MaterialPropertyBlock props;
|
||||
static readonly List<Material> sharedMaterialsBuffer = new List<Material>(8);
|
||||
|
||||
public List<Renderer> renderers { get; private set; } = new List<Renderer>();
|
||||
[System.NonSerialized] public Material nonInstancingMaterial;
|
||||
bool initialized;
|
||||
|
||||
void BuildRendererList () {
|
||||
renderers.Clear();
|
||||
|
||||
switch (includeMode) {
|
||||
case IncludeMode.OnlyThisObject:
|
||||
Renderer thisRenderer = GetComponent<Renderer>();
|
||||
if (thisRenderer != null) {
|
||||
renderers.Add(thisRenderer);
|
||||
}
|
||||
break;
|
||||
case IncludeMode.IncludeChildren:
|
||||
GetComponentsInChildren(true, renderers);
|
||||
for (int i = renderers.Count - 1; i >= 0; i--) {
|
||||
Renderer r = renderers[i];
|
||||
if (r == null || ((1 << r.gameObject.layer) & childLayerMask) == 0) {
|
||||
renderers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ValidateRendererMaterials();
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
if (initialized || !Application.isPlaying) {
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Start () {
|
||||
if (Application.isPlaying) {
|
||||
Refresh();
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnValidate () {
|
||||
customObjectId = Mathf.Abs(customObjectId);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
UpdateObjectProperties(false);
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
renderers.Clear();
|
||||
}
|
||||
|
||||
int GetRandomId () {
|
||||
return Random.Range(1, 851);
|
||||
}
|
||||
|
||||
public void Refresh () {
|
||||
BuildRendererList();
|
||||
UpdateObjectProperties(enabled);
|
||||
}
|
||||
|
||||
void UpdateObjectProperties (bool isEnabled) {
|
||||
if (props == null) props = new MaterialPropertyBlock();
|
||||
|
||||
bool isActive = gameObject.activeInHierarchy && isEnabled;
|
||||
int rnd = useRandomId && !randomIdPerChild ? GetRandomId() : 0;
|
||||
int rendererCount = renderers.Count;
|
||||
for (int i = 0; i < rendererCount; i++) {
|
||||
Renderer renderer = renderers[i];
|
||||
if (renderer == null) continue;
|
||||
renderer.GetPropertyBlock(props);
|
||||
float objectId = 0;
|
||||
float radius = 0;
|
||||
float disallowIntraFusion = 0f;
|
||||
if (isActive) {
|
||||
if (overrideRadius) {
|
||||
radius = customRadius > 0 ? customRadius : 1;
|
||||
}
|
||||
if (overrideObjectId) {
|
||||
if (useRandomId) {
|
||||
objectId = randomIdPerChild ? GetRandomId() : rnd;
|
||||
}
|
||||
else if (customObjectId > 0) {
|
||||
objectId = customObjectId;
|
||||
}
|
||||
}
|
||||
disallowIntraFusion = disallowIntraObjectFusion ? 1f : 0f;
|
||||
}
|
||||
props.SetFloat(ShaderParams.EdgeFusionRadius, radius);
|
||||
props.SetFloat(ShaderParams.CustomObjectId, objectId);
|
||||
props.SetFloat(ShaderParams.DisallowIntraFusion, disallowIntraFusion);
|
||||
renderer.SetPropertyBlock(props);
|
||||
}
|
||||
}
|
||||
|
||||
void ValidateRendererMaterials () {
|
||||
nonInstancingMaterial = null;
|
||||
int rendererCount = renderers.Count;
|
||||
for (int i = 0; i < rendererCount; i++) {
|
||||
Renderer r = renderers[i];
|
||||
if (r == null) continue;
|
||||
sharedMaterialsBuffer.Clear();
|
||||
r.GetSharedMaterials(sharedMaterialsBuffer);
|
||||
int matCount = sharedMaterialsBuffer.Count;
|
||||
for (int j = 0; j < matCount; j++) {
|
||||
Material mat = sharedMaterialsBuffer[j];
|
||||
if (mat == null) continue;
|
||||
if (!mat.enableInstancing) {
|
||||
nonInstancingMaterial = mat;
|
||||
sharedMaterialsBuffer.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedMaterialsBuffer.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 425113f99a2d55f4db2ca1642d02f0b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace EdgeFusion {
|
||||
|
||||
[HelpURL("https://kronnect.com/guides/edge-fusion-introduction/")]
|
||||
public class EdgeFusionRenderFeature : ScriptableRendererFeature {
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Whether to show the effect in Scene View")]
|
||||
public bool showInSceneView = true;
|
||||
|
||||
[Tooltip("Allows Edge Fusion to be executed even if camera has Post Processing option disabled")]
|
||||
public bool ignorePostProcessingOption = true;
|
||||
|
||||
[Tooltip("If enabled, Edge Fusion skips overlay cameras in a stack and only runs on base cameras. Disable to run on a stacked overlay camera (its layer must still pass the Cameras Layer Mask).")]
|
||||
public bool ignoreOverlayCameras = true;
|
||||
|
||||
[Tooltip("Filter which cameras can render Edge Fusion by their GameObject layer")]
|
||||
public LayerMask camerasLayerMask = -1;
|
||||
|
||||
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
|
||||
|
||||
|
||||
EdgeFusionRenderPass renderPass;
|
||||
|
||||
public override void Create () {
|
||||
renderPass = new EdgeFusionRenderPass(this);
|
||||
}
|
||||
|
||||
public override void AddRenderPasses (ScriptableRenderer renderer, ref RenderingData renderingData) {
|
||||
if (renderPass == null)
|
||||
return;
|
||||
|
||||
if (renderingData.cameraData.isPreviewCamera || renderingData.cameraData.cameraType == CameraType.Reflection || (!showInSceneView && renderingData.cameraData.cameraType == CameraType.SceneView))
|
||||
return;
|
||||
|
||||
if (ignoreOverlayCameras && renderingData.cameraData.renderType != CameraRenderType.Base)
|
||||
return;
|
||||
|
||||
if ((camerasLayerMask & (1 << renderingData.cameraData.camera.gameObject.layer)) == 0)
|
||||
return;
|
||||
|
||||
if (!ignorePostProcessingOption && !renderingData.cameraData.postProcessEnabled)
|
||||
return;
|
||||
|
||||
if (IsDepthOnlyTarget(renderingData.cameraData.cameraTargetDescriptor))
|
||||
return;
|
||||
|
||||
var settings = VolumeManager.instance.stack.GetComponent<EdgeFusion>();
|
||||
bool hasSettings = settings != null;
|
||||
if (!hasSettings) return;
|
||||
|
||||
int cameraMask = renderingData.cameraData.camera.cullingMask;
|
||||
int effectiveBlendLayers = settings.blendLayers.value & cameraMask;
|
||||
int effectiveDoubleSided = settings.doubleSidedLayers.value & cameraMask;
|
||||
int effectiveSpecialGroup = settings.specialGroupLayers.value & cameraMask;
|
||||
int dsMask = effectiveDoubleSided;
|
||||
int ssMask = effectiveBlendLayers & ~effectiveDoubleSided;
|
||||
bool hasAnySubset = (dsMask | ssMask | effectiveSpecialGroup) != 0;
|
||||
if (!hasAnySubset) return;
|
||||
|
||||
bool effectActive = settings.intensity.value > 0f;
|
||||
bool debugging = settings.debugMode.value != DebugMode.None;
|
||||
if (!debugging && !effectActive) return;
|
||||
|
||||
renderPass.UpdateInputRequirement(settings);
|
||||
|
||||
renderPass.renderPassEvent = renderPassEvent;
|
||||
renderer.EnqueuePass(renderPass);
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing) {
|
||||
renderPass?.Cleanup();
|
||||
}
|
||||
|
||||
internal static bool IsDepthOnlyTarget(in RenderTextureDescriptor descriptor) {
|
||||
if (descriptor.colorFormat == RenderTextureFormat.Depth) {
|
||||
return true;
|
||||
}
|
||||
if (descriptor.graphicsFormat == GraphicsFormat.None) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec88e1d72445d4942b578e59d9a6e6a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,600 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace EdgeFusion {
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// PORT NOTES (Unity 2022.3 LTS / URP 14 - classic ScriptableRenderPass API)
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// The original (Unity 6 / URP 17) implementation is built entirely on the Render Graph API
|
||||
// (RecordRenderGraph / TextureHandle / RasterGraphContext). URP 14 does not have Render Graph,
|
||||
// so this version uses the classic Execute(ScriptableRenderContext, ref RenderingData) API with
|
||||
// manually managed RTHandles, matching the same pass structure and shader contract 1:1:
|
||||
// 1) ObjectID pass -> objectID RT (+ its own depth RT if the camera uses MSAA)
|
||||
// 2) Special-group pass -> customGroup RT (only if a special group is configured)
|
||||
// 3) Fill ObjectID pass -> objectIDFilled RT (only if needed, same condition as original)
|
||||
// 4) Blend pass -> compare RT (if comparing) or camera color
|
||||
// 5) Compare pass -> camera color (if comparing)
|
||||
// 6) Debug pass -> camera color (if a debug mode is active and not comparing)
|
||||
// 7) Copy-back -> not needed here, see note at the bottom of Execute()
|
||||
//
|
||||
// Differences from the Unity 6 version worth knowing about:
|
||||
// - RTHandles are allocated once and reused/reallocated on demand (RenderingUtils.ReAllocateIfNeeded)
|
||||
// instead of being requested fresh from the render graph every frame.
|
||||
// - `Object.FindObjectsByType` (used in the original for id-exclusion lookup) does not exist in
|
||||
// 2022.3; this file uses the classic `Object.FindObjectsOfType<Volume>()` instead.
|
||||
// - The classic renderer exposes `ScriptableRenderer.cameraColorTargetHandle` /
|
||||
// `cameraDepthTargetHandle` directly; there is no `UniversalResourceData`/`UniversalCameraData`
|
||||
// frame-data container to reroute.
|
||||
// - Camera-stack detection uses the same UniversalAdditionalCameraData.cameraStack check as the
|
||||
// original, kept here for parity/documentation even though the classic renderer doesn't need an
|
||||
// explicit copy-back step (see the note at the end of Execute()).
|
||||
//
|
||||
// This file has been ported by careful, line-by-line reading of the Unity 6 implementation and the
|
||||
// URP 14 classic API; it has not been compiled/run inside the Editor. Please test in your 2022.3
|
||||
// project (single camera, stacked cameras, MSAA on/off, and XR if you use it) before shipping.
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public class EdgeFusionRenderPass : ScriptableRenderPass {
|
||||
|
||||
// Shader names
|
||||
const string OBJECT_ID_SHADER_PATH = "Kronnect/EdgeFusion/ObjectID";
|
||||
const string FUSION_SHADER_PATH = "Hidden/Kronnect/EdgeFusion/EdgeFusion";
|
||||
const string FUSION_FILL_SHADER_PATH = "Hidden/Kronnect/EdgeFusion/EdgeFusionFill";
|
||||
|
||||
// Texture names
|
||||
const string OBJECT_ID_TEXTURE_NAME = "EdgeFusion_ObjectID";
|
||||
const string OBJECT_ID_FILLED_TEXTURE_NAME = "EdgeFusion_ObjectID_Filled";
|
||||
const string OBJECT_ID_DEPTH_TEXTURE_NAME = "EdgeFusion_ObjectID_Depth";
|
||||
const string CUSTOM_GROUP_TEXTURE_NAME = "EdgeFusion_CustomGroups";
|
||||
const string CAMERA_COLOR_TEXTURE_NAME = "EdgeFusion_CameraColor";
|
||||
const string COMPARE_TEXTURE_NAME = "EdgeFusion_CompareTex";
|
||||
|
||||
// Profiling / pass name
|
||||
const string PROFILING_NAME = "Edge Fusion";
|
||||
|
||||
static readonly ProfilingSampler profilingSampler = new ProfilingSampler(PROFILING_NAME);
|
||||
|
||||
EdgeFusionRenderFeature feature;
|
||||
Material objectIDMaterial;
|
||||
Material fusionMaterial;
|
||||
Material fusionFillMaterial;
|
||||
readonly List<ShaderTagId> shaderTags;
|
||||
GraphicsFormat objectIdTextureFormat;
|
||||
Texture3D noiseTex;
|
||||
static readonly float[] exclusionMask = new float[32];
|
||||
static readonly uint[] exclusionMaskBits = new uint[32];
|
||||
static Volume cachedExclusionVolume;
|
||||
static EdgeFusion cachedEdgeFusion;
|
||||
|
||||
// RTHandles reused/reallocated across frames
|
||||
RTHandle objectIDHandle;
|
||||
RTHandle objectIDFilledHandle;
|
||||
RTHandle objectIDDepthHandle;
|
||||
RTHandle customGroupHandle;
|
||||
RTHandle compareHandle;
|
||||
RTHandle cameraColorCopyHandle; // holds the pre-effect camera color (blend source & compare source)
|
||||
|
||||
static bool CanStoreMaskValue (uint value) {
|
||||
return (uint)(float)value == value;
|
||||
}
|
||||
|
||||
public EdgeFusionRenderPass (EdgeFusionRenderFeature feature) {
|
||||
this.feature = feature;
|
||||
shaderTags = new List<ShaderTagId> {
|
||||
new ShaderTagId("SRPDefaultUnlit"),
|
||||
new ShaderTagId("UniversalForward"),
|
||||
new ShaderTagId("UniversalForwardOnly"),
|
||||
new ShaderTagId("LightweightForward")
|
||||
};
|
||||
|
||||
var rg32Format = GraphicsFormat.R32G32_SFloat;
|
||||
bool canUseRG32Format = SystemInfo.IsFormatSupported(rg32Format, FormatUsage.Render);
|
||||
objectIdTextureFormat = canUseRG32Format ? rg32Format : GraphicsFormat.R32G32B32A32_SFloat;
|
||||
|
||||
InitializeMaterials();
|
||||
}
|
||||
|
||||
public void UpdateInputRequirement (EdgeFusion settings) {
|
||||
ConfigureInput(ScriptableRenderPassInput.Depth);
|
||||
}
|
||||
|
||||
void InitializeMaterials () {
|
||||
if (objectIDMaterial == null) {
|
||||
var shader = Shader.Find(OBJECT_ID_SHADER_PATH);
|
||||
if (shader != null) objectIDMaterial = CoreUtils.CreateEngineMaterial(shader);
|
||||
if (objectIDMaterial != null) objectIDMaterial.enableInstancing = true;
|
||||
}
|
||||
|
||||
if (fusionMaterial == null) {
|
||||
var shader = Shader.Find(FUSION_SHADER_PATH);
|
||||
if (shader != null) fusionMaterial = CoreUtils.CreateEngineMaterial(shader);
|
||||
}
|
||||
|
||||
if (fusionFillMaterial == null) {
|
||||
var shader = Shader.Find(FUSION_FILL_SHADER_PATH);
|
||||
if (shader != null) fusionFillMaterial = CoreUtils.CreateEngineMaterial(shader);
|
||||
}
|
||||
|
||||
noiseTex = Resources.Load<Texture3D>("EdgeFusion/Textures/NoiseTex3D");
|
||||
}
|
||||
|
||||
void UpdateMaterialProperties (EdgeFusion settings, bool taa, float effectiveMaxBlendDistance, bool cameraUsesMsaa, bool specialGroupActive) {
|
||||
|
||||
float mappedNoiseContrast = Mathf.Lerp(0.5f, 0.01f, settings.noiseContrast.value);
|
||||
|
||||
Vector4 blendData1 = new Vector4(
|
||||
settings.intensity.value, // x: GlobalIntensity
|
||||
effectiveMaxBlendDistance, // y: MaxBlendDistance
|
||||
settings.normalThreshold.value, // z: NormalThreshold
|
||||
mappedNoiseContrast // w: NoiseContrast
|
||||
);
|
||||
|
||||
Vector4 blendData2 = new Vector4(
|
||||
settings.maxScreenRadius.value, // x: MaxScreenRadius
|
||||
settings.shadowProtection.value, // y: ShadowProtection
|
||||
settings.noiseIntensity.value, // z: NoiseIntensity
|
||||
settings.noiseScale.value // w: NoiseScale
|
||||
);
|
||||
|
||||
float radiusScale = Mathf.Max(1f, settings.radius.value / 0.89f);
|
||||
|
||||
fusionMaterial.SetVector(ShaderParams.BlendData1, blendData1);
|
||||
fusionMaterial.SetVector(ShaderParams.BlendData2, blendData2);
|
||||
fusionMaterial.SetFloat(ShaderParams.DefaultRadiusWorld, settings.radius.value);
|
||||
fusionMaterial.SetFloat(ShaderParams.RadiusScale, radiusScale);
|
||||
fusionMaterial.SetFloat(ShaderParams.DistanceCompensation, settings.distanceCompensation.value);
|
||||
fusionMaterial.SetInt(ShaderParams.SampleCount, settings.sampleCount.value);
|
||||
fusionMaterial.SetInt(ShaderParams.BinarySearchSteps, settings.binarySearchSteps.value);
|
||||
fusionMaterial.SetInt(ShaderParams.EarlyExitHits, settings.earlyExitHits.value);
|
||||
if (settings.antiFlicker.value) {
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_ANTI_FLICKER);
|
||||
}
|
||||
else {
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_ANTI_FLICKER);
|
||||
}
|
||||
fusionMaterial.SetTexture(ShaderParams.NoiseTex3D, noiseTex);
|
||||
|
||||
fusionFillMaterial.SetVector(ShaderParams.BlendData1, blendData1);
|
||||
fusionFillMaterial.SetVector(ShaderParams.BlendData2, blendData2);
|
||||
fusionFillMaterial.SetFloat(ShaderParams.DefaultRadiusWorld, settings.radius.value);
|
||||
fusionFillMaterial.SetFloat(ShaderParams.RadiusScale, radiusScale);
|
||||
fusionFillMaterial.SetFloat(ShaderParams.DistanceCompensation, settings.distanceCompensation.value);
|
||||
|
||||
float msaaFixPower = settings.msaaEdgeFixPower.value;
|
||||
if (cameraUsesMsaa && msaaFixPower > 0f) {
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_MSAA_EDGE_FIX);
|
||||
fusionMaterial.SetFloat(ShaderParams.MsaaFixPower, msaaFixPower);
|
||||
}
|
||||
else {
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_MSAA_EDGE_FIX);
|
||||
}
|
||||
|
||||
// ObjectID material setup
|
||||
objectIDMaterial.SetFloat(ShaderParams.MaxBlendDistance, effectiveMaxBlendDistance);
|
||||
objectIDMaterial.SetFloat(ShaderParams.DefaultRadiusWorld, settings.radius.value);
|
||||
objectIDMaterial.SetFloat(ShaderParams.RadiusScale, radiusScale);
|
||||
objectIDMaterial.SetFloat(ShaderParams.DistanceCompensation, settings.distanceCompensation.value);
|
||||
objectIDMaterial.SetVector(ShaderParams.BlendData2, blendData2);
|
||||
objectIDMaterial.SetInt(ShaderParams.ZWrite, cameraUsesMsaa ? 1 : 0);
|
||||
objectIDMaterial.SetInt(ShaderParams.ZTest, cameraUsesMsaa ? (int)CompareFunction.LessEqual : (int)CompareFunction.Equal);
|
||||
objectIDMaterial.SetFloat(ShaderParams.DisallowIntraFusion, settings.intraObjectFusionPerObject.value ? 1f : 0f);
|
||||
|
||||
if (cameraUsesMsaa) {
|
||||
objectIDMaterial.EnableKeyword(ShaderParams.SKW_MSAA_ON);
|
||||
}
|
||||
else {
|
||||
objectIDMaterial.DisableKeyword(ShaderParams.SKW_MSAA_ON);
|
||||
}
|
||||
|
||||
DebugMode debugMode = settings.debugMode.value;
|
||||
if (debugMode == DebugMode.SpecialGroup && !specialGroupActive) debugMode = DebugMode.None;
|
||||
fusionMaterial.SetInt(ShaderParams.DebugMode, (int)debugMode);
|
||||
fusionMaterial.SetFloat(ShaderParams.DepthDebugMultiplier, settings.depthDebugMultiplier.value);
|
||||
|
||||
bool normalsDebug = debugMode == DebugMode.Normals;
|
||||
if (normalsDebug) {
|
||||
objectIDMaterial.EnableKeyword(ShaderParams.SKW_DEBUG_BLEND_NORMALS);
|
||||
fusionFillMaterial.EnableKeyword(ShaderParams.SKW_DEBUG_BLEND_NORMALS);
|
||||
}
|
||||
else {
|
||||
objectIDMaterial.DisableKeyword(ShaderParams.SKW_DEBUG_BLEND_NORMALS);
|
||||
fusionFillMaterial.DisableKeyword(ShaderParams.SKW_DEBUG_BLEND_NORMALS);
|
||||
}
|
||||
|
||||
// Jitter keyword toggle
|
||||
if (settings.jitter.value) {
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_ENABLE_JITTER);
|
||||
fusionMaterial.SetFloat(ShaderParams.JitterFrame, taa ? Time.frameCount : 0);
|
||||
}
|
||||
else {
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_ENABLE_JITTER);
|
||||
}
|
||||
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_INTRA_OBJECT_FUSION);
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_CONCAVE_ONLY);
|
||||
objectIDMaterial.DisableKeyword(ShaderParams.SKW_INTRA_OBJECT_FUSION);
|
||||
objectIDMaterial.DisableKeyword(ShaderParams.SKW_CONCAVE_ONLY);
|
||||
fusionFillMaterial.DisableKeyword(ShaderParams.SKW_INTRA_OBJECT_FUSION);
|
||||
fusionFillMaterial.DisableKeyword(ShaderParams.SKW_CONCAVE_ONLY);
|
||||
bool intraObjectFusionPerObject = settings.intraObjectFusionPerObject.value;
|
||||
if (settings.enableIntraObjectFusion.value) {
|
||||
if (settings.concavityTest.value) {
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_CONCAVE_ONLY);
|
||||
objectIDMaterial.EnableKeyword(ShaderParams.SKW_CONCAVE_ONLY);
|
||||
if (!intraObjectFusionPerObject) {
|
||||
fusionFillMaterial.EnableKeyword(ShaderParams.SKW_CONCAVE_ONLY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_INTRA_OBJECT_FUSION);
|
||||
objectIDMaterial.EnableKeyword(ShaderParams.SKW_INTRA_OBJECT_FUSION);
|
||||
if (!intraObjectFusionPerObject) {
|
||||
fusionFillMaterial.EnableKeyword(ShaderParams.SKW_INTRA_OBJECT_FUSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.noiseIntensity.value > 0.0f) {
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_NOISE);
|
||||
}
|
||||
else {
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_NOISE);
|
||||
}
|
||||
|
||||
if (specialGroupActive) {
|
||||
fusionFillMaterial.EnableKeyword(ShaderParams.SKW_SPECIAL_GROUP);
|
||||
}
|
||||
else {
|
||||
fusionFillMaterial.DisableKeyword(ShaderParams.SKW_SPECIAL_GROUP);
|
||||
}
|
||||
|
||||
var exclusionPairs = GetActiveExclusionPairs();
|
||||
if (exclusionPairs != null && exclusionPairs.Length > 0) {
|
||||
System.Array.Clear(exclusionMask, 0, 32);
|
||||
System.Array.Clear(exclusionMaskBits, 0, 32);
|
||||
bool hasEnabledPairs = false;
|
||||
int exclusionPairCount = exclusionPairs.Length;
|
||||
for (int i = 0; i < exclusionPairCount; i++) {
|
||||
var pair = exclusionPairs[i];
|
||||
if (!pair.enabled) continue;
|
||||
|
||||
hasEnabledPairs = true;
|
||||
int a = Mathf.Clamp(pair.idA, 1, 32) - 1;
|
||||
int b = Mathf.Clamp(pair.idB, 1, 32) - 1;
|
||||
uint bitForA = 1u << b;
|
||||
uint bitForB = 1u << a;
|
||||
|
||||
if (a == b) {
|
||||
uint candidate = exclusionMaskBits[a] | bitForA;
|
||||
if (CanStoreMaskValue(candidate)) {
|
||||
exclusionMaskBits[a] = candidate;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
uint candidateA = exclusionMaskBits[a] | bitForA;
|
||||
uint candidateB = exclusionMaskBits[b] | bitForB;
|
||||
|
||||
bool canA = CanStoreMaskValue(candidateA);
|
||||
bool canB = CanStoreMaskValue(candidateB);
|
||||
|
||||
if (canA && (!canB || candidateA <= candidateB)) {
|
||||
exclusionMaskBits[a] = candidateA;
|
||||
}
|
||||
else if (canB) {
|
||||
exclusionMaskBits[b] = candidateB;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEnabledPairs) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
uint maskValue = exclusionMaskBits[i];
|
||||
exclusionMask[i] = maskValue;
|
||||
}
|
||||
fusionMaterial.EnableKeyword(ShaderParams.SKW_ID_EXCLUSION);
|
||||
fusionMaterial.SetFloatArray(ShaderParams.ExclusionMask, exclusionMask);
|
||||
}
|
||||
else {
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_ID_EXCLUSION);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
fusionMaterial.DisableKeyword(ShaderParams.SKW_ID_EXCLUSION);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// Classic execution
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
public override void Execute (ScriptableRenderContext context, ref RenderingData renderingData) {
|
||||
|
||||
if (feature == null) return;
|
||||
var settings = VolumeManager.instance.stack.GetComponent<EdgeFusion>();
|
||||
if (settings == null || !settings.IsActive()) return;
|
||||
|
||||
if (objectIDMaterial == null || fusionMaterial == null || fusionFillMaterial == null) {
|
||||
InitializeMaterials();
|
||||
if (objectIDMaterial == null || fusionMaterial == null || fusionFillMaterial == null) return;
|
||||
}
|
||||
|
||||
ref var cameraData = ref renderingData.cameraData;
|
||||
if (EdgeFusionRenderFeature.IsDepthOnlyTarget(cameraData.cameraTargetDescriptor)) return;
|
||||
|
||||
var renderer = cameraData.renderer;
|
||||
RTHandle cameraColorTargetHandle = renderer.cameraColorTargetHandle;
|
||||
RTHandle cameraDepthTargetHandle = renderer.cameraDepthTargetHandle;
|
||||
if (cameraColorTargetHandle == null) return;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get(PROFILING_NAME);
|
||||
using (new ProfilingScope(cmd, profilingSampler)) {
|
||||
|
||||
bool cameraUsesMsaa = cameraData.cameraTargetDescriptor.msaaSamples > 1;
|
||||
bool taa = cameraData.antialiasing == AntialiasingMode.TemporalAntiAliasing && !cameraUsesMsaa;
|
||||
|
||||
int cameraCullingMask = cameraData.camera.cullingMask;
|
||||
int effectiveBlendLayers = settings.blendLayers.value & cameraCullingMask;
|
||||
int effectiveDoubleSided = settings.doubleSidedLayers.value & cameraCullingMask;
|
||||
int effectiveSpecialGroup = settings.specialGroupLayers.value & cameraCullingMask;
|
||||
|
||||
uint rlMaskSingle = settings.renderingLayerFilter.value;
|
||||
uint rlMaskDouble = settings.doubleSidedRenderingLayerFilter.value;
|
||||
uint rlMaskSpecial = settings.specialGroupRenderingLayerFilter.value;
|
||||
|
||||
int ssMask = effectiveBlendLayers;
|
||||
int dsMask = effectiveDoubleSided;
|
||||
int specialMask = effectiveSpecialGroup;
|
||||
|
||||
if ((dsMask & specialMask) != 0 && RenderingLayersOverlap(rlMaskSpecial, rlMaskDouble)) {
|
||||
dsMask &= ~specialMask;
|
||||
}
|
||||
if ((ssMask & specialMask) != 0 && RenderingLayersOverlap(rlMaskSpecial, rlMaskSingle)) {
|
||||
ssMask &= ~specialMask;
|
||||
}
|
||||
if ((ssMask & dsMask) != 0 && RenderingLayersOverlap(rlMaskDouble, rlMaskSingle)) {
|
||||
ssMask &= ~dsMask;
|
||||
}
|
||||
|
||||
bool hasSingleSided = ssMask != 0 && rlMaskSingle != 0u;
|
||||
bool hasDoubleSided = dsMask != 0 && rlMaskDouble != 0u;
|
||||
bool hasSpecialGroup = specialMask != 0 && rlMaskSpecial != 0u;
|
||||
uint specialGroupCombined = hasSpecialGroup ? rlMaskSpecial : 0u;
|
||||
|
||||
if (!hasSingleSided && !hasDoubleSided && !hasSpecialGroup) {
|
||||
CommandBufferPool.Release(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
float effectiveMaxBlendDistance = settings.maxBlendDistance.value;
|
||||
if (cameraData.camera.orthographic) {
|
||||
effectiveMaxBlendDistance = 1e10f;
|
||||
}
|
||||
|
||||
UpdateMaterialProperties(settings, taa, effectiveMaxBlendDistance, cameraUsesMsaa, hasSpecialGroup);
|
||||
|
||||
var baseDesc = cameraData.cameraTargetDescriptor;
|
||||
|
||||
// Kept for parity/documentation with the original camera-stack handling; the classic
|
||||
// renderer already shares cameraColorTargetHandle between base and overlay cameras, so no
|
||||
// extra copy-back is required (see the note at the bottom of this method).
|
||||
bool isStackedBase = cameraData.camera.TryGetComponent(out UniversalAdditionalCameraData additionalCameraData)
|
||||
&& additionalCameraData.cameraStack != null && additionalCameraData.cameraStack.Count > 0;
|
||||
|
||||
// ---- Working copy of the pre-effect camera color (blend/compare source) ----
|
||||
var colorCopyDesc = baseDesc;
|
||||
colorCopyDesc.depthBufferBits = 0;
|
||||
colorCopyDesc.msaaSamples = 1;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref cameraColorCopyHandle, colorCopyDesc, name: CAMERA_COLOR_TEXTURE_NAME);
|
||||
Blitter.BlitCameraTexture(cmd, cameraColorTargetHandle, cameraColorCopyHandle);
|
||||
|
||||
RTHandle colorTarget = cameraColorTargetHandle;
|
||||
|
||||
// ---- ObjectID texture ----
|
||||
var objIdDesc = baseDesc;
|
||||
bool forcedRGBA32F = settings.enableIntraObjectFusion.value || settings.debugMode.value == DebugMode.Normals;
|
||||
objIdDesc.graphicsFormat = forcedRGBA32F ? GraphicsFormat.R32G32B32A32_SFloat : objectIdTextureFormat;
|
||||
objIdDesc.depthBufferBits = 0;
|
||||
objIdDesc.msaaSamples = 1;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref objectIDHandle, objIdDesc, name: OBJECT_ID_TEXTURE_NAME);
|
||||
|
||||
Color clearColor = SystemInfo.usesReversedZBuffer ? Color.clear : new Color(0, 1, 0, 0);
|
||||
|
||||
// ---- Special-group marker texture ----
|
||||
bool useCustomGroups = hasSpecialGroup;
|
||||
if (useCustomGroups) {
|
||||
var customDesc = objIdDesc;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref customGroupHandle, customDesc, name: CUSTOM_GROUP_TEXTURE_NAME);
|
||||
}
|
||||
|
||||
// ---- Depth used while filling the ObjectID buffer ----
|
||||
RTHandle depthForObjectID;
|
||||
if (cameraUsesMsaa) {
|
||||
var objIdDepthDesc = baseDesc;
|
||||
objIdDepthDesc.msaaSamples = 1;
|
||||
objIdDepthDesc.graphicsFormat = GraphicsFormat.None;
|
||||
objIdDepthDesc.depthBufferBits = 24;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref objectIDDepthHandle, objIdDepthDesc, name: OBJECT_ID_DEPTH_TEXTURE_NAME);
|
||||
depthForObjectID = objectIDDepthHandle;
|
||||
}
|
||||
else {
|
||||
// No MSAA: reuse camera depth directly (read/testable, matches original ZTest.Equal setup)
|
||||
depthForObjectID = cameraDepthTargetHandle != null ? cameraDepthTargetHandle : objectIDDepthHandle;
|
||||
}
|
||||
|
||||
// =========================== 1) ObjectID pass ===========================
|
||||
// Only clear depth when we own a dedicated depth buffer (MSAA path). In the non-MSAA
|
||||
// path depthForObjectID is the camera's shared depth target, already populated by the
|
||||
// opaque pass and needed as-is for the ZTest.Equal comparison below (and by later passes
|
||||
// that sample _CameraDepthTexture) - clearing it here would corrupt it for the rest of the frame.
|
||||
ClearFlag objectIdClearFlag = cameraUsesMsaa ? ClearFlag.All : ClearFlag.Color;
|
||||
CoreUtils.SetRenderTarget(cmd, objectIDHandle, depthForObjectID, objectIdClearFlag, clearColor);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
var drawSettingsObjId = CreateDrawingSettings(shaderTags, ref renderingData, SortingCriteria.CommonOpaque);
|
||||
drawSettingsObjId.overrideMaterial = objectIDMaterial;
|
||||
drawSettingsObjId.overrideMaterialPassIndex = 0;
|
||||
drawSettingsObjId.perObjectData = PerObjectData.None;
|
||||
drawSettingsObjId.enableInstancing = true;
|
||||
|
||||
if (hasSingleSided) {
|
||||
cmd.SetGlobalInt(ShaderParams.Cull, (int)CullMode.Back);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
var filterSingle = new FilteringSettings(RenderQueueRange.opaque, ssMask, rlMaskSingle);
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettingsObjId, ref filterSingle);
|
||||
}
|
||||
if (hasDoubleSided) {
|
||||
cmd.SetGlobalInt(ShaderParams.Cull, (int)CullMode.Off);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
var filterDouble = new FilteringSettings(RenderQueueRange.opaque, dsMask, rlMaskDouble);
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettingsObjId, ref filterDouble);
|
||||
cmd.SetGlobalInt(ShaderParams.Cull, (int)CullMode.Back);
|
||||
}
|
||||
|
||||
// =========================== 2) Special-group pass ===========================
|
||||
if (useCustomGroups) {
|
||||
CoreUtils.SetRenderTarget(cmd, customGroupHandle, depthForObjectID, ClearFlag.Color, Color.black);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
var drawSettingsSpecial = CreateDrawingSettings(shaderTags, ref renderingData, SortingCriteria.CommonOpaque);
|
||||
drawSettingsSpecial.overrideMaterial = null; // use original materials to preserve displacement
|
||||
drawSettingsSpecial.enableInstancing = true;
|
||||
|
||||
var filterCustom = new FilteringSettings(RenderQueueRange.opaque, specialMask, specialGroupCombined);
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettingsSpecial, ref filterCustom);
|
||||
}
|
||||
|
||||
// =========================== 3) Fill ObjectID pass ===========================
|
||||
int layerUnion = ssMask | dsMask;
|
||||
bool layersExcluded = layerUnion != cameraCullingMask;
|
||||
bool renderingLayersExcluded = (rlMaskSingle != uint.MaxValue) || (rlMaskDouble != uint.MaxValue) || (rlMaskSpecial != uint.MaxValue);
|
||||
bool needsOthersFill = (layersExcluded || renderingLayersExcluded) && settings.blendWithOthers.value;
|
||||
|
||||
RTHandle objectIDForBlend = objectIDHandle;
|
||||
if (useCustomGroups || needsOthersFill) {
|
||||
fusionFillMaterial.SetFloat(ShaderParams.TerrainObjectId, settings.blendWithOthers.value ? 31f : 0f);
|
||||
|
||||
RenderingUtils.ReAllocateIfNeeded(ref objectIDFilledHandle, objIdDesc, name: OBJECT_ID_FILLED_TEXTURE_NAME);
|
||||
|
||||
cmd.SetGlobalTexture(ShaderParams.ObjectIDTexture, objectIDHandle);
|
||||
if (useCustomGroups) {
|
||||
cmd.SetGlobalTexture(ShaderParams.CustomGroupTexture, customGroupHandle);
|
||||
}
|
||||
CoreUtils.SetRenderTarget(cmd, objectIDFilledHandle, ClearFlag.None, Color.clear);
|
||||
CoreUtils.DrawFullScreen(cmd, fusionFillMaterial, null, ShaderParams.FillPassIndex);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
objectIDForBlend = objectIDFilledHandle;
|
||||
}
|
||||
|
||||
// =========================== 4) Blend pass ===========================
|
||||
bool compareActive = settings.compareMode.value && settings.debugMode.value == DebugMode.None;
|
||||
if (compareActive) {
|
||||
RenderingUtils.ReAllocateIfNeeded(ref compareHandle, colorCopyDesc, name: COMPARE_TEXTURE_NAME);
|
||||
}
|
||||
|
||||
cmd.SetGlobalTexture(ShaderParams.SrcColorTexture, cameraColorCopyHandle);
|
||||
cmd.SetGlobalTexture(ShaderParams.ObjectIDTexture, objectIDForBlend);
|
||||
|
||||
RTHandle blendDestination = (compareActive && compareHandle != null) ? compareHandle : colorTarget;
|
||||
CoreUtils.SetRenderTarget(cmd, blendDestination, ClearFlag.None, Color.clear);
|
||||
CoreUtils.DrawFullScreen(cmd, fusionMaterial, null, (int)ShaderParams.Passes.Blend);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
// =========================== 5) Compare pass ===========================
|
||||
if (compareActive && compareHandle != null) {
|
||||
float angle = settings.compareSameSide.value ? Mathf.PI * 0.5f : settings.compareLineAngle.value;
|
||||
Vector4 compareParamsValue = new Vector4(Mathf.Cos(angle), Mathf.Sin(angle), settings.compareSameSide.value ? settings.comparePanning.value : -10f, settings.compareLineWidth.value);
|
||||
Color lineColor = settings.compareLineColor.value;
|
||||
Vector4 compareLineColor = new Vector4(lineColor.r, lineColor.g, lineColor.b, lineColor.a);
|
||||
|
||||
cmd.SetGlobalTexture(ShaderParams.SrcColorTexture, cameraColorCopyHandle);
|
||||
cmd.SetGlobalTexture(ShaderParams.CompareTex, compareHandle);
|
||||
cmd.SetGlobalVector(ShaderParams.CompareParams, compareParamsValue);
|
||||
cmd.SetGlobalVector(ShaderParams.CompareLineColor, compareLineColor);
|
||||
|
||||
CoreUtils.SetRenderTarget(cmd, colorTarget, ClearFlag.None, Color.clear);
|
||||
CoreUtils.DrawFullScreen(cmd, fusionMaterial, null, (int)ShaderParams.Passes.Compare);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
}
|
||||
|
||||
// =========================== 6) Debug pass ===========================
|
||||
if (!compareActive && settings.debugMode.value != DebugMode.None) {
|
||||
cmd.SetGlobalTexture(ShaderParams.ObjectIDTexture, objectIDForBlend);
|
||||
CoreUtils.SetRenderTarget(cmd, colorTarget, ClearFlag.None, Color.clear);
|
||||
CoreUtils.DrawFullScreen(cmd, fusionMaterial, null, (int)ShaderParams.Passes.Debug);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
}
|
||||
|
||||
// NOTE on camera stacking: the Unity 6 / Render Graph version has to reroute
|
||||
// resourceData.cameraColor to a fresh texture and then copy it back into the shared stack
|
||||
// color at the end, because render-graph resources are re-requested every frame. The
|
||||
// classic (2022.3) renderer already gives base and overlay cameras the SAME persistent
|
||||
// cameraColorTargetHandle, and every pass above wrote directly into `colorTarget`
|
||||
// (== cameraColorTargetHandle), so overlay cameras in the stack already see the result -
|
||||
// no extra copy-back blit is needed here. `isStackedBase` is kept above only for
|
||||
// documentation / in case you need to special-case stacked cameras later.
|
||||
_ = isStackedBase;
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
static bool RenderingLayersOverlap (uint maskA, uint maskB) {
|
||||
if (maskA == 0u || maskB == 0u) return false;
|
||||
if (maskA == uint.MaxValue || maskB == uint.MaxValue) return true;
|
||||
return (maskA & maskB) != 0u;
|
||||
}
|
||||
|
||||
static IdExclusionPair[] GetActiveExclusionPairs () {
|
||||
if (cachedExclusionVolume != null && cachedExclusionVolume.isActiveAndEnabled && cachedEdgeFusion != null) {
|
||||
return cachedEdgeFusion.idExclusionPairs;
|
||||
}
|
||||
cachedExclusionVolume = null;
|
||||
cachedEdgeFusion = null;
|
||||
|
||||
// Unity 2022.3: Object.FindObjectsByType does not exist yet - use the classic (obsolete but
|
||||
// functional) FindObjectsOfType instead of the Unity 6 FindObjectsByType overload.
|
||||
var volumes = Object.FindObjectsOfType<Volume>();
|
||||
int volumeCount = volumes.Length;
|
||||
for (int i = 0; i < volumeCount; i++) {
|
||||
var volume = volumes[i];
|
||||
if (volume == null || !volume.isActiveAndEnabled || volume.sharedProfile == null) continue;
|
||||
if (!volume.sharedProfile.TryGet<EdgeFusion>(out var ef)) continue;
|
||||
if (ef.idExclusionPairs != null) {
|
||||
cachedExclusionVolume = volume;
|
||||
cachedEdgeFusion = ef;
|
||||
return cachedEdgeFusion.idExclusionPairs;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Cleanup () {
|
||||
CoreUtils.Destroy(objectIDMaterial);
|
||||
CoreUtils.Destroy(fusionMaterial);
|
||||
CoreUtils.Destroy(fusionFillMaterial);
|
||||
objectIDHandle?.Release();
|
||||
objectIDFilledHandle?.Release();
|
||||
objectIDDepthHandle?.Release();
|
||||
customGroupHandle?.Release();
|
||||
compareHandle?.Release();
|
||||
cameraColorCopyHandle?.Release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3328f16041cee34b8fc0db1a14cdf0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace EdgeFusion {
|
||||
|
||||
public static class ShaderParams {
|
||||
|
||||
public enum Passes {
|
||||
Blend = 0,
|
||||
Debug = 1,
|
||||
Compare = 2
|
||||
}
|
||||
|
||||
public const int FillPassIndex = 0;
|
||||
|
||||
// ========== Texture Properties ==========
|
||||
public static readonly int ObjectIDTexture = Shader.PropertyToID("_ObjectIDTexture");
|
||||
public static readonly int SrcColorTexture = Shader.PropertyToID("_SrcColorTexture");
|
||||
public static readonly int CompareTex = Shader.PropertyToID("_CompareTex");
|
||||
public static readonly int NoiseTex3D = Shader.PropertyToID("_NoiseTex3D");
|
||||
public static readonly int CustomGroupTexture = Shader.PropertyToID("_CustomGroupTexture");
|
||||
|
||||
// ========== ObjectID Pass Parameters ==========
|
||||
public static readonly int EdgeFusionRadius = Shader.PropertyToID("_EdgeFusionRadius");
|
||||
public static readonly int DefaultRadiusWorld = Shader.PropertyToID("_DefaultRadiusWorld");
|
||||
public static readonly int RadiusScale = Shader.PropertyToID("_RadiusScale");
|
||||
public static readonly int MaxBlendDistance = Shader.PropertyToID("_MaxBlendDistance");
|
||||
public static readonly int DistanceCompensation = Shader.PropertyToID("_DistanceCompensation");
|
||||
public static readonly int TerrainObjectId = Shader.PropertyToID("_TerrainObjectId");
|
||||
public static readonly int ZWrite = Shader.PropertyToID("_ZWrite");
|
||||
public static readonly int ZTest = Shader.PropertyToID("_ZTest");
|
||||
public static readonly int Cull = Shader.PropertyToID("_Cull");
|
||||
public static readonly int CustomObjectId = Shader.PropertyToID("_CustomObjectId");
|
||||
public static readonly int DisallowIntraFusion = Shader.PropertyToID("_DisallowIntraFusion");
|
||||
|
||||
// ========== Blend Pass Parameters ==========
|
||||
public static readonly int BlendData1 = Shader.PropertyToID("_BlendData1"); // x: GlobalIntensity, y: MaxBlendDistance
|
||||
public static readonly int BlendData2 = Shader.PropertyToID("_BlendData2"); // x: MaxScreenRadius, y: ShadowProtection, z: NoiseIntensity, w: NoiseScale
|
||||
public static readonly int SampleCount = Shader.PropertyToID("_SampleCount");
|
||||
public static readonly int BinarySearchSteps = Shader.PropertyToID("_BinarySearchSteps");
|
||||
public static readonly int JitterFrame = Shader.PropertyToID("_JitterFrame");
|
||||
public static readonly int EarlyExitHits = Shader.PropertyToID("_EarlyExitHits");
|
||||
public static readonly int CompareParams = Shader.PropertyToID("_CompareParams");
|
||||
public static readonly int CompareLineColor = Shader.PropertyToID("_CompareLineColor");
|
||||
public static readonly int DepthDebugMultiplier = Shader.PropertyToID("_DepthDebugMultiplier");
|
||||
public static readonly int DebugMode = Shader.PropertyToID("_DebugMode");
|
||||
public static readonly int MsaaFixPower = Shader.PropertyToID("_MSAAFixPower");
|
||||
|
||||
// ========== ID Exclusion Parameters ==========
|
||||
public static readonly int ExclusionMask = Shader.PropertyToID("_ExclusionMask");
|
||||
|
||||
// ========== Shader Keywords ==========
|
||||
public const string SKW_DEBUG_BLEND_NORMALS = "EDGE_FUSION_DEBUG_BLEND_NORMALS";
|
||||
public const string SKW_INTRA_OBJECT_FUSION = "EDGE_FUSION_INTRA_OBJECT";
|
||||
public const string SKW_ENABLE_JITTER = "EDGE_FUSION_ENABLE_JITTER";
|
||||
public const string SKW_NOISE = "EDGE_FUSION_NOISE";
|
||||
public const string SKW_CONCAVE_ONLY = "EDGE_FUSION_CONCAVE_ONLY";
|
||||
public const string SKW_SPECIAL_GROUP = "EDGE_FUSION_SPECIAL_GROUP";
|
||||
public const string SKW_MSAA_ON = "EDGE_FUSION_MSAA_ON";
|
||||
public const string SKW_MSAA_EDGE_FIX = "EDGE_FUSION_MSAA_EDGE_FIX";
|
||||
public const string SKW_ID_EXCLUSION = "EDGE_FUSION_ID_EXCLUSION";
|
||||
public const string SKW_ANTI_FLICKER = "EDGE_FUSION_ANTI_FLICKER";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 493b1e6314df04c40b747bddf88b7184
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user