422 lines
12 KiB
HLSL
422 lines
12 KiB
HLSL
#ifndef ENKI_BILLAL_INCLUDED
|
|
#define ENKI_BILLAL_INCLUDED
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
|
//#include "Assets/_Content/Shaders/EnkiBillal/Stripes.hlsl"
|
|
#include "Assets/_Content/Shaders/Include/Noises.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/AmbientOcclusion.hlsl"
|
|
|
|
float MainLightShadowAtten(float3 PositionWS, float3 NormalWS, float CastAtten)
|
|
{
|
|
float4 shadowCoord = TransformWorldToShadowCoord(PositionWS);
|
|
Light mainLight = GetMainLight(shadowCoord);
|
|
|
|
float lightGradient = dot(normalize(NormalWS), normalize(mainLight.direction));
|
|
lightGradient = smoothstep(-.5, 1, lightGradient);
|
|
|
|
float shadow = 1 - mainLight.shadowAttenuation;
|
|
shadow = lerp(0, shadow, lightGradient) * CastAtten;
|
|
shadow = lerp(1, shadow, lightGradient);
|
|
shadow = 1 - shadow;
|
|
|
|
return shadow;
|
|
}
|
|
|
|
float3 TriplanarMask(float3 NormalWS, float Sharpness)
|
|
{
|
|
float sharpness = Remap(0, 1, 1, 16, smoothstep(0, .9, Sharpness));
|
|
float3 hard = abs(NormalWS) == max(abs(NormalWS.x), max(abs(NormalWS.y), abs(NormalWS.z)));
|
|
float3 blend = pow(abs(NormalWS), sharpness);
|
|
blend /= max(dot(blend, float3(1, 1, 1)), 0.0001);
|
|
blend = lerp(blend, hard, smoothstep(.9, 1, Sharpness));
|
|
return blend;
|
|
}
|
|
|
|
float2 RotateUV(float2 UV, float2 Pivot, float Angle)
|
|
{
|
|
float s = sin(Angle);
|
|
float c = cos(Angle);
|
|
|
|
UV -= Pivot;
|
|
|
|
float2 rotated = float2(
|
|
UV.x * c - UV.y * s,
|
|
UV.x * s + UV.y * c
|
|
);
|
|
|
|
return rotated + Pivot;
|
|
}
|
|
|
|
float StableStripes(float x, float depth, float width, float smooth)
|
|
{
|
|
float phase = frac(depth);
|
|
float index = floor(depth);
|
|
float scale = exp2(-index);
|
|
|
|
float rawX = x * scale - 0.5;
|
|
|
|
float cellIndex = floor(rawX);
|
|
|
|
float2 uv = float2(frac(rawX), phase);
|
|
|
|
float isContinuing = abs(fmod(cellIndex, 2));
|
|
|
|
float w = lerp(width, lerp(-smooth, width * 2, isContinuing), uv.y);
|
|
float s = smooth;
|
|
|
|
return smoothstep(w + s, w - s, abs(uv.x - .5) * 2);
|
|
}
|
|
|
|
float WorldStripes()
|
|
{
|
|
/*float StripesScale = StripesDensity;
|
|
//float StripesWidth = .5;
|
|
//float StripesSmooth = .05;
|
|
|
|
// Octahedral face normals (4 faces + 3 world axes)
|
|
float3 octaFaceNormals[7] =
|
|
{
|
|
normalize(float3(1, 1, 1)),
|
|
normalize(float3(-1, 1, 1)),
|
|
normalize(float3(1, -1, 1)),
|
|
normalize(float3(-1, -1, 1)),
|
|
float3(1, 0, 0),
|
|
float3(0, 1, 0),
|
|
float3(0, 0, 1)
|
|
};
|
|
|
|
// Octahedral blending mask
|
|
float sharpness = Remap(0, 1, 1, 16, smoothstep(0, .9, TriplanarBlend));
|
|
float octaMask[7];
|
|
float hardMax = -1;
|
|
int hardIdx = 0;
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
float d = abs(dot(NormalWS, octaFaceNormals[i]));
|
|
octaMask[i] = pow(saturate(d), sharpness);
|
|
if (d > hardMax)
|
|
{
|
|
hardMax = d;
|
|
hardIdx = i;
|
|
}
|
|
}
|
|
float maskSum = 0;
|
|
for (int i = 0; i < 7; i++)
|
|
maskSum += octaMask[i];
|
|
for (int i = 0; i < 7; i++)
|
|
octaMask[i] /= max(maskSum, 0.0001);
|
|
float hardFactor = smoothstep(.9, 1, TriplanarBlend);
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
float hard = (i == hardIdx) ? 1.0 : 0.0;
|
|
octaMask[i] = lerp(octaMask[i], hard, hardFactor);
|
|
}
|
|
|
|
// Patern
|
|
float3 stripesPos = PositionWS;
|
|
float3 stripesPos0 = stripesPos; // + PerlinColor3D(PositionWS, .63) * .10;
|
|
float3 stripesPos1 = stripesPos; // + PerlinColor3D(PositionWS, 5.7) * .02;
|
|
|
|
float width = StripesWidth; // * .5;
|
|
float smooth = max(StripesSmooth, 1e-5);
|
|
|
|
float stripes0 = 0;
|
|
float stripes1 = 0;
|
|
|
|
[unroll]
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
float3 n = octaFaceNormals[i];
|
|
|
|
float3 up = abs(n.y) < 0.999 ? float3(0, 1, 0) : float3(1, 0, 0);
|
|
float3 tangent = normalize(cross(up, n));
|
|
float3 bitangent = normalize(cross(n, tangent));
|
|
|
|
float3 ref = normalize(mainLightDir - dot(mainLightDir, n) * n);
|
|
float3 axis = normalize(up - dot(up, n) * n);
|
|
float angle = -atan2(dot(cross(axis, ref), n), dot(axis, ref));
|
|
|
|
float2 p0 = RotateUV(float2(dot(stripesPos0, tangent), dot(stripesPos0, bitangent)), 0, angle) * StripesScale;
|
|
float2 p1 = (RotateUV(float2(dot(stripesPos1, tangent), dot(stripesPos1, bitangent)), 0, angle) + .5) * StripesScale;
|
|
|
|
float stripe0 = StableStripes(p0.x, log2(depth), width, smooth);
|
|
float stripe1 = StableStripes(p1.x + p1.y, log2(depth), width, smooth);
|
|
|
|
stripes0 += stripe0 * octaMask[i];
|
|
stripes1 += stripe1 * octaMask[i];
|
|
}
|
|
|
|
//float stripes = stripes0 * smoothstep(.5, 0, lightGradient);
|
|
float stripes = max(stripes0, stripes1 * .5);
|
|
stripes *= smoothstep(.5, 0, lightGradient) * smoothstep(.97, .93, ao);*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
float WorldDistort(float3 PositionWS, float3 NormalWS, float Angle, float TriplanarBlend)
|
|
{
|
|
float StripesScale = 60;
|
|
|
|
// Octahedral face normals (4 faces + 3 world axes)
|
|
float3 octaFaceNormals[7] =
|
|
{
|
|
normalize(float3(1, 1, 1)),
|
|
normalize(float3(-1, 1, 1)),
|
|
normalize(float3(1, -1, 1)),
|
|
normalize(float3(-1, -1, 1)),
|
|
float3(1, 0, 0),
|
|
float3(0, 1, 0),
|
|
float3(0, 0, 1)
|
|
};
|
|
|
|
// Octahedral blending mask
|
|
float sharpness = Remap(0, 1, 1, 16, smoothstep(0, .9, TriplanarBlend));
|
|
float octaMask[7];
|
|
float hardMax = -1;
|
|
int hardIdx = 0;
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
float d = abs(dot(NormalWS, octaFaceNormals[i]));
|
|
octaMask[i] = pow(saturate(d), sharpness);
|
|
if (d > hardMax)
|
|
{
|
|
hardMax = d;
|
|
hardIdx = i;
|
|
}
|
|
}
|
|
float maskSum = 0;
|
|
for (int i = 0; i < 7; i++)
|
|
maskSum += octaMask[i];
|
|
for (int i = 0; i < 7; i++)
|
|
octaMask[i] /= max(maskSum, 0.0001);
|
|
float hardFactor = smoothstep(.9, 1, TriplanarBlend);
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
float hard = (i == hardIdx) ? 1.0 : 0.0;
|
|
octaMask[i] = lerp(octaMask[i], hard, hardFactor);
|
|
}
|
|
|
|
// Patern
|
|
float stripes = 0;
|
|
|
|
[unroll]
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
float3 n = octaFaceNormals[i];
|
|
|
|
float3 up = abs(n.y) < 0.999 ? float3(0, 1, 0) : float3(1, 0, 0);
|
|
float3 tangent = normalize(cross(up, n));
|
|
float3 bitangent = normalize(cross(n, tangent));
|
|
|
|
float angle = i / 7.0 * PI;
|
|
|
|
float2 p = RotateUV(float2(dot(PositionWS, tangent), dot(PositionWS, bitangent)), 0, angle) * StripesScale;
|
|
|
|
float stripe = cos(p.x);
|
|
|
|
stripes += stripe * octaMask[i];
|
|
}
|
|
|
|
return stripes;
|
|
}
|
|
|
|
|
|
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 EnkiBillal_float
|
|
(
|
|
float4 Color,
|
|
float2 ScreenUV,
|
|
float3 PositionWS,
|
|
float3 PositionOS,
|
|
float3 NormalWS,
|
|
float3 NormalOS,
|
|
float3 ObjectScale,
|
|
float TriplanarBlend,
|
|
float StripesDensity,
|
|
float StripesWidth,
|
|
float StripesSmooth,
|
|
int Step,
|
|
float MaskID,
|
|
float4x4 Mask0,
|
|
float4x4 Mask1,
|
|
float4x4 Mask2,
|
|
out float3 Output,
|
|
out float Alpha
|
|
)
|
|
{
|
|
float3 scaledPositionOS = PositionOS * ObjectScale;
|
|
float depth = length(PositionWS - _WorldSpaceCameraPos.xyz);
|
|
//Light mainLight = GetMainLight(0);
|
|
float3 mainLightDir = normalize(float3(1, -1, 1)); //normalize(float3(0, 13, 8) - PositionWS); //float3(0, -1, 0);//normalize(mainLight.direction);
|
|
float lightGradient = dot(normalize(NormalWS), normalize(mainLightDir));
|
|
lightGradient = smoothstep(-.5, 1, lightGradient);
|
|
//float ao = GetScreenSpaceAmbientOcclusion(ScreenUV).directAmbientOcclusion;
|
|
|
|
// Shadow
|
|
|
|
float shadowMask = lightGradient;//MainLightShadowAtten(PositionWS, NormalWS, .8);
|
|
float shadowAtten = Remap(0, 1, .6, 1, shadowMask);
|
|
|
|
// Grain
|
|
|
|
/*float grain = ValueNoise3DLOD(PositionWS, scaledPositionOS, 600, 3.5);
|
|
float grainMin = lerp(.4, .8, shadowMask);
|
|
float grainAtten = Remap(0, 1, grainMin, 1, grain);
|
|
|
|
// Dirt
|
|
|
|
float dirt = FBMNoise3D(scaledPositionOS, 1, 3, 2, .5);
|
|
float dirtMin = lerp(.2, .7, shadowMask);
|
|
float dirtAtten = Remap(0, 1, dirtMin, 1, dirt);*/
|
|
|
|
// Stripes
|
|
|
|
// float stripes = WorldStripes();
|
|
|
|
|
|
// Test
|
|
|
|
//float test = ValueNoise3D(PositionWS, 40);
|
|
//float test = PerlinNoise3D(PositionWS, 40);
|
|
//float test = PerlinNoise3DLOD(PositionWS, 100, 3.5);
|
|
//float test = SimplexNoise3D(PositionWS, 40);
|
|
//float test = WorleyNoise3D(PositionWS, 40, 1);
|
|
//float id;
|
|
//float test = VoronoiNoise3D(PositionWS, 10, 1, .05, id);
|
|
//float test = FBMNoise3D(PositionWS, 10, 2, 2, .5);
|
|
//float test = RidgedNoise3D(PositionWS, 10, 2, 2, .5);
|
|
|
|
/*float3 pWS = scaledPositionOS;
|
|
float n0 = PerlinNoise3D(pWS, 2);
|
|
float distort = WorldDistort(pWS + n0 * .2, NormalWS, 1, TriplanarBlend);
|
|
float3 p = pWS + n0 * 2 + distort * smoothstep(.5, .7, n0) * .06;
|
|
float grad = 0;
|
|
float cell = 0;
|
|
VoronoiNoise3D_float(p, 2, 1, 1, grad, cell);
|
|
|
|
|
|
float3 v = cell * .8;// * (grad + distort * .1);
|
|
v += smoothstep(.05, .02, grad) * smoothstep(.4, .7, n0) * 2;
|
|
float3 c = Color * lerp(.9, 1, v);*/
|
|
|
|
// Output
|
|
|
|
//Output = 1;
|
|
//if (Step > 0 ) Output *= Color;
|
|
//if (Step > 1 ) Output *= grainAtten;
|
|
//if (Step > 2 ) Output *= dirtAtten;
|
|
//if (Step > 3 ) Output *= shadowAtten;
|
|
//if (Step > 4 ) Output = lerp(Output, Output * .1, stripes * .8);
|
|
|
|
Output = Color;
|
|
|
|
//Output = PerlinColor3D(PositionWS, 5);
|
|
//Output = smoothstep(.97, .93, ao);
|
|
|
|
// Mask
|
|
|
|
if (Mask2[0].w == 1)
|
|
{
|
|
float mask = SampleProjectionMask(PositionWS, Mask2);
|
|
|
|
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
|