wip: Main shader detail map

This commit is contained in:
Alexandre Coirier
2026-06-01 21:55:23 +02:00
parent d873676d01
commit 2f3cd8dc7e
13 changed files with 1004 additions and 1602 deletions
File diff suppressed because it is too large Load Diff
@@ -2,36 +2,9 @@
#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);
@@ -47,34 +20,110 @@ float2 RotateUV(float2 UV, float2 Pivot, float Angle)
return rotated + Pivot;
}
float StableStripes(float x, float depth, float width, float smooth)
float3 GetScale(float3 PivotWS)
{
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);
return float3
(
length(TransformObjectToWorld(float3(1, 0, 0)) - PivotWS),
length(TransformObjectToWorld(float3(0, 1, 0)) - PivotWS),
length(TransformObjectToWorld(float3(0, 0, 1)) - PivotWS)
);
}
float WorldStripes()
float GetShadow(float3 PositionWS)
{
/*float StripesScale = StripesDensity;
//float StripesWidth = .5;
//float StripesSmooth = .05;
float4 shadowCoord = TransformWorldToShadowCoord(PositionWS);
// Octahedral face normals (4 faces + 3 world axes)
float3 octaFaceNormals[7] =
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);
}
float4 SampleBombedTexture
(
UnityTexture2D Tex,
float2 UV
)
{
const float cellBlend = 4;
float2 cell = floor(UV);
float2 local = frac(UV);
float4 result = 0;
float weightSum = 0;
[unroll]
for (int y = 0; y < 2; y++)
{
[unroll]
for (int x = 0; x < 2; x++)
{
float2 currentCell = cell + float2(x, y);
float2 randomOffset = (Hash2D(currentCell) - .5) * .35;
float randomAngle = Hash1From2D(currentCell) * TWO_PI;
float2 center = float2(x, y) + randomOffset;
float2 delta = local - center;
float weight =
pow(saturate(1 - abs(delta.x)), cellBlend) *
pow(saturate(1 - abs(delta.y)), cellBlend);
float2 sampleUV = UV + randomOffset;
sampleUV = RotateUV
(
sampleUV,
currentCell + .5,
randomAngle
);
result += Tex.tex.Sample
(
Tex.samplerstate,
sampleUV
) * weight;
weightSum += weight;
}
}
return result / max(weightSum, 1e-5);
}
struct ProjectionData
{
float2 uv[7];
float mask[7];
};
ProjectionData GetProjections
(
float3 PositionOS,
float3 NormalOS,
float Blend
)
{
ProjectionData data;
float3 normal = normalize(NormalOS);
float3 projectionNormals[7] =
{
normalize(float3(1, 1, 1)),
normalize(float3(-1, 1, 1)),
@@ -85,226 +134,105 @@ float WorldStripes()
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 sharpness = lerp(2, 32, saturate(Blend));
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 n = projectionNormals[i];
float d = abs(dot(normal, n));
data.mask[i] = pow(saturate(d), sharpness);
maskSum += data.mask[i];
float3 up = abs(n.y) < .999
? float3(0, 1, 0)
: float3(1, 0, 0);
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];
data.uv[i] = float2
(
dot(PositionOS, tangent),
dot(PositionOS, bitangent)
);
}
//float stripes = stripes0 * smoothstep(.5, 0, lightGradient);
float stripes = max(stripes0, stripes1 * .5);
stripes *= smoothstep(.5, 0, lightGradient) * smoothstep(.97, .93, ao);*/
return 0;
}
maskSum = max(maskSum, 1e-5);
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];
data.mask[i] /= maskSum;
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;
return data;
}
void EnkiBillal_float
(
float4 Color,
UnityTexture2D DetailTex,
float2 ScreenUV,
float3 PositionWS,
float3 PositionOS,
float3 NormalWS,
float3 NormalOS,
float3 ObjectScale,
float TriplanarBlend,
float StripesDensity,
float StripesWidth,
float StripesSmooth,
int Step,
float ProjectionsBlend,
out float3 Output
)
{
float3 scaledPositionOS = PositionOS * ObjectScale;
// Common
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);
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 shadowMask = lightGradient;//MainLightShadowAtten(PositionWS, NormalWS, .8);
float shadowAtten = Remap(0, 1, .6, 1, shadowMask);
float shadow = GetShadow(PositionWS);
float shadowAtten = Remap(1, 0, .6, 1, shadow);
// 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();
// Detail
float4 detail = 0;
// Test
ProjectionData projections = GetProjections
(
positionOS,
normalOS,
ProjectionsBlend
);
//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);*/
float totalMask = 0;
[unroll]
for (int i = 0; i < 7; i++)
{
float mask = projections.mask[i];
detail += SampleBombedTexture
(
DetailTex,
projections.uv[i]
) * mask;
totalMask += mask;
}
detail /= max(totalMask, 1e-5);
// 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);
Output = Color.rgb;
Output = lerp(Color.rgb, Color.rgb * detail.rgb, detail.a);
//Output *= shadowAtten;
}
#endif
#endif
@@ -246,7 +246,9 @@ void EnkiBillalFullscreen_float
// COLOR (WIP)
float3 pWS = positionWS; //scaledPositionOS;
float3 c = sceneColor;
/*float3 pWS = positionWS; //scaledPositionOS;
float n0 = PerlinNoise3D(pWS, 2);
float distort = WorldDistort(pWS + n0 * .2, normalWS, 1, .1);
float3 p = pWS + n0 * 2 + distort * smoothstep(.5, .7, n0) * .06;
@@ -257,7 +259,7 @@ void EnkiBillalFullscreen_float
float3 v = cell * .8; // * (grad + distort * .1);
v += smoothstep(.05, .02, grad) * smoothstep(.4, .7, n0) * 2;
float3 c = sceneColor * lerp(.9, 1, v);
c *= lerp(.9, 1, v);
// DIRT
@@ -267,7 +269,7 @@ void EnkiBillalFullscreen_float
float dirt0 = smoothstep(.2, .22, dirtNoise0);
float dirt1 = saturate(smoothstep(.01, .02, abs(dot(dirtNoise0, dirtNoise1) - .4)) + dirtNoise1 + outlineAtten);
c *= dirt0 * dirt1;
c *= dirt0 * dirt1;*/
// SHADOW