feat: House of leaves, details added to wall shader

This commit is contained in:
Alexandre Coirier
2026-07-01 01:16:06 +02:00
parent 36e968ebf3
commit 85ab8ebabd
16 changed files with 1174 additions and 138 deletions
@@ -0,0 +1,112 @@
#ifndef ENKI_BILAL_INCLUDED
#define ENKI_BILAL_INCLUDED
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Assets/_Content/Shaders/Include/Noises.hlsl"
#include "Assets/_Content/Shaders/Include/Details.hlsl"
#include "Assets/_Content/Shaders/Include/Utilities.hlsl"
//#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/AmbientOcclusion.hlsl"
float GetShadow(float3 PositionWS)
{
float4 shadowCoord = TransformWorldToShadowCoord(PositionWS);
Light mainLight = GetMainLight(shadowCoord);
float shadow = 1 - mainLight.shadowAttenuation;
#if defined(_ADDITIONAL_LIGHTS)
uint lightsCount = GetAdditionalLightsCount();
[loop]
for (uint i = 0; i < lightsCount; i++)
{
Light light = GetAdditionalLight(i, PositionWS);
shadow = max(shadow, 1 - light.shadowAttenuation);
}
#endif
return saturate(shadow);
}
void EnkiBillal_float
(
float4 Color,
UnityTexture2D DetailTexA,
UnityTexture2D DetailTexB,
float2 ScreenUV,
float3 PositionWS,
float3 NormalWS,
float ProjectionsBlend,
float DetailIntensityA,
float DetailIntensityB,
float DetailSizeA,
float DetailSizeB,
uint FogUse,
float4 FogColor,
float FogMin,
float FogMax,
out float3 Output,
out float Alpha
)
{
// Common
float depth = length(PositionWS - _WorldSpaceCameraPos.xyz);
float3 pivotWS = TransformObjectToWorld(float3(0, 0, 0));
float3 scale = GetScale(pivotWS);
float3 positionOS = TransformWorldToObject(PositionWS) * scale;
float3 normalOS = normalize(TransformWorldToObjectDir(NormalWS));
//float ao = GetScreenSpaceAmbientOcclusion(ScreenUV).directAmbientOcclusion;
// Shadow
float shadow = GetShadow(PositionWS);
float shadowAtten = Remap(1, 0, .6, 1, shadow);
// Detail
float4 detailA = SampleProjectedDetailLOD
(
DetailTexA,
positionOS,
normalOS,
ProjectionsBlend,
DetailSizeA,
depth
);
float4 detailB = SampleProjectedDetailLOD
(
DetailTexB,
positionOS,
normalOS,
ProjectionsBlend,
DetailSizeB,
depth
);
float detailMaskA = smoothstep(0, .8, detailA.a);
float detailMaskB = smoothstep(0, .8, detailB.a);
// Fog
// Output
Output = Color.rgb;
Output = lerp(Output.rgb, detailA.rgb, detailMaskA * DetailIntensityA);
Output = lerp(Output.rgb, detailB.rgb, detailMaskB * DetailIntensityB);
if (FogUse)
{
Output = lerp(Output.rgb, FogColor.rgb, smoothstep(FogMin, FogMax, depth) * FogColor.a);
}
Alpha = Color.a;
}
#endif
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 411dec433a23f2c4da8f56e83e7230da
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,123 @@
#ifndef MASK_INCLUDED
#define MASK_INCLUDED
float ProjectionMask(
float3 PositionWS,
float3 CamPositionWS,
float3 RectPositionWS,
float3 RectNormalWS,
float3 RectUpWS,
float2 RectSize,
float Oval)
{
// Build the rectangle local frame
float3 N = normalize(RectNormalWS);
float3 U = normalize(RectUpWS);
float3 R = normalize(cross(N, U)); // Right axis
U = normalize(cross(R, N)); // Recompute Up orthogonally in case RectUpWS is not perfectly perpendicular to RectNormalWS
// Ray from camera toward the fragment
float3 rayDir = normalize(PositionWS - CamPositionWS);
// Ray-plane intersection
// Plane equation: dot(P - RectPositionWS, N) = 0
float denom = dot(rayDir, N);
// Ray is parallel to the plane or facing away — no hit
if (abs(denom) < 1e-6)
return 0;
float t = dot(RectPositionWS - CamPositionWS, N) / denom;
// Intersection must be in front of the camera (t > 0)
// and not beyond the fragment (t <= distance from camera to fragment)
float tFrag = dot(PositionWS - CamPositionWS, rayDir);
if (t < 0.0 || t > tFrag + 1e-4)
return 0;
// Compute the hit point on the plane
float3 hitPoint = CamPositionWS + rayDir * t;
// Project hit point into the rectangle local space
float3 localVec = hitPoint - RectPositionWS;
float localU = dot(localVec, R); // horizontal axis
float localV = dot(localVec, U); // vertical axis
float halfW = RectSize.x * 0.5;
float halfH = RectSize.y * 0.5;
// Corner radius: 0 = rectangle, min(halfW, halfH) = oval
float radius = saturate(Oval) * min(halfW, halfH);
// Rounded rectangle SDF
float2 uv = float2(localU, localV);
float2 ext = float2(halfW, halfH);
float2 q = abs(uv) - ext + radius;
float sdf = length(max(q, 0.0)) - radius;
return sdf <= 0.0 ? 1.0 : 0.0;
}
float SampleProjectionMask(float3 PositionWS, float4x4 M)
{
if (M[0].w == 0)
return 0;
return ProjectionMask(
PositionWS,
M[0].xyz, // CamPositionWS
M[1].xyz, // RectPositionWS
M[3].xyz, // RectNormalWS
M[2].xyz, // RectUpWS
float2(M[2].w, M[3].w), // RectSize
M[1].w // Shape
);
}
void Mask_float
(
float3 PositionWS,
float MaskID,
float4x4 Mask0,
float4x4 Mask1,
float4x4 Mask2,
out float Alpha
)
{
Alpha = 1;
if (Mask0[0].w == 1 && Mask1[0].w == 1 && Mask2[0].w == 1)
{
float mask = SampleProjectionMask(PositionWS, Mask0);
mask += SampleProjectionMask(PositionWS, Mask1);
mask += SampleProjectionMask(PositionWS, Mask2);
mask = saturate(mask);
Alpha = (MaskID <= 1) ? 1 - mask : mask;
}
else if (MaskID == 0)
{
Alpha = 1;
}
else
{
float Active = Mask0[0].w + Mask1[0].w + Mask2[0].w;
if (Active > 0)
{
float mask = saturate(
SampleProjectionMask(PositionWS, Mask0) +
SampleProjectionMask(PositionWS, Mask1) +
SampleProjectionMask(PositionWS, Mask2)
);
Alpha = (MaskID == 1) ? 1 - mask : mask;
}
else
{
Alpha = (MaskID == 1) ? 0 : 1;
}
}
}
#endif
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2f64e213049952b44ac4bed6f62cafda
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,71 @@
#ifndef STRIPES_INCLUDED
#define STRIPES_INCLUDED
// Returns a value in [0, 1)
float VanDerCorput(uint index)
{
// Reverse bits of the integer and scale to [0,1]
return (float) reversebits(index) * 2.3283064365386963e-10;
}
void Stripes_float(
float2 UV,
float Density,
float Count,
float Thickness,
float Offset,
float DistortionAmplitude,
float DistortionFrequency,
out float Stripes
)
{
float density = saturate(Density);
float offset = frac(Offset);
float distortionAmplitude = DistortionAmplitude * .1;
uint stripeCount = max((uint) round(Count), 1u);
float2 uv = UV;
uv.x += offset;
float2 uvA = uv;
float2 uvB = uv;
uvA.x = frac(uvA.x + cos((uv.y + offset) * sin(uv.x) * DistortionFrequency + offset * 7.8) * distortionAmplitude);
uvB.x = frac(uvB.x + sin((uv.y + offset) * cos(uv.x) * DistortionFrequency + offset * 5.3) * distortionAmplitude);
float stripDistanceY = abs(saturate(uv.y) - .5);
float stripeMax = 0;
for (uint i = 0u; i < stripeCount; i++)
{
float vdc = VanDerCorput(i);
bool usesSecondDistortion = vdc >= .5;
float thickness = vdc < .3 ? 1 : (vdc < .6 ? 1.8 : 2.6);
thickness *= saturate(Thickness);
float wrappedX = usesSecondDistortion ? uvB.x : uvA.x;
float localProgress = saturate(density * stripeCount - vdc * stripeCount);
float stripeCenterX = frac((i + .5) / stripeCount);
float stripDistanceX = abs(wrappedX - stripeCenterX);
stripDistanceX = min(stripDistanceX, 1.0 - stripDistanceX);
float stepX = (1. / stripeCount) * localProgress * thickness * .5;
float stepY = localProgress * .5 - .01;
float softEdgeX = .01;
float softEdgeY = .05;
float widthMask = 1 - smoothstep(stepX - softEdgeX, stepX, stripDistanceX);
float heightMask = 1 - smoothstep(stepY - softEdgeY, stepY, stripDistanceY);
float stripeMask = widthMask * heightMask;
stripeMax = max(stripeMax, stripeMask);
}
Stripes = stripeMax;
}
#endif
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1e32323363e459a4fa93de6067070a10
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: