Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
ac8490d
feat: check feature compatibility (#1136)
alandtse Jun 14, 2025
6cc1f85
ci: build cpp only when cpp changed (#1140)
alandtse Jun 14, 2025
961dc21
build: remove pause from build script (#1142)
alandtse Jun 14, 2025
32215bf
ci: create fallback for tj-actions/changed-files (#1146)
alandtse Jun 15, 2025
ca045b4
ci: add hlsl validation (#1145)
alandtse Jun 15, 2025
5061c39
ci: treat skipped build or validation as success (#1148)
alandtse Jun 16, 2025
ea0c052
fix: detect core features properly (#1147)
alandtse Jun 16, 2025
6b340e8
fix(extended materials): green channel detection (#1152)
doodlum Jun 16, 2025
77ce8e2
ci: always run cpp-build (#1149)
alandtse Jun 17, 2025
c51ad1e
fix: support seasons swaps in PBR and TerrainHelper (#1099)
hakasapl Jun 17, 2025
c9ca0f8
fix: fix detection of deleted obsolete features (#1157)
alandtse Jun 17, 2025
f51d371
feat: add fast random float gen based on pcg (#1158)
sicsix Jun 18, 2025
b161382
feat(ui): organize features under subheadings (#1155)
davo0411 Jun 18, 2025
8c64f5d
feat: add raindrop ripples on water (#577)
TheRiverwoodModder Jun 18, 2025
8a3b7f6
feat(ui): add Icon Support (#1107)
davo0411 Jun 19, 2025
fb52eae
refactor: move BRDFs to separate file (#1161)
jiayev Jun 19, 2025
8363e96
squash
jiayev May 26, 2025
37c6a2a
initial
jiayev May 27, 2025
1e699f9
style: 🎨 apply clang-format changes
jiayev May 27, 2025
6303182
get it work in setupmaterial
jiayev May 27, 2025
8dfe692
style: 🎨 apply clang-format changes
jiayev May 27, 2025
479378a
kinda work
jiayev May 27, 2025
130764f
rework on skin wetness
jiayev May 28, 2025
025bdf6
small work on wetness
jiayev May 28, 2025
af96381
fix crash on situations
jiayev May 28, 2025
c1500db
separate wetness map
jiayev May 28, 2025
05b846e
fix crash and loading
jiayev May 28, 2025
b431d31
fix pbrglossiness for wet
jiayev May 28, 2025
9c6cf49
small ao trick
jiayev May 28, 2025
e43ab29
wetness rework
jiayev May 29, 2025
7ca88b1
keep working on sweat
jiayev May 29, 2025
7e530da
mix instead of mult
jiayev May 30, 2025
97d9f4d
keep working on skin wetness
jiayev May 30, 2025
980bb01
smoother, fix indirect
jiayev May 30, 2025
a3ef96c
slightly better
jiayev May 31, 2025
3114bbd
dynamic wetness
jiayev Jun 11, 2025
a004cfa
work for hair
jiayev Jun 12, 2025
c784bb7
add wetness to clothes
jiayev Jun 12, 2025
f0ac7df
making it more obvious
jiayev Jun 12, 2025
548231d
more detailed wetness
jiayev Jun 12, 2025
cfa32a2
prevent sweat on metal
jiayev Jun 12, 2025
633b3f2
not applying extra on other stuff
jiayev Jun 12, 2025
6133c29
change to per geom
jiayev Jun 14, 2025
b0e0af2
add support for wet normal
jiayev Jun 15, 2025
5f9e717
use pow to make it more like droplets
jiayev Jun 15, 2025
c305210
extra wetness for hair
jiayev Jun 15, 2025
a0d799f
smol change
jiayev Jun 16, 2025
8ba2f6d
revert to the detail before
jiayev Jun 16, 2025
2345d2b
fix conditions
jiayev Jun 16, 2025
81af8d5
just use combined wet normal
jiayev Jun 17, 2025
8bf33e2
set to 0 to disable
jiayev Jun 18, 2025
f36dcb6
apply dual lobe on indirect
jiayev Jun 18, 2025
4aa6bbe
add info
jiayev Jun 19, 2025
923b26b
use new BRDF
jiayev Jun 19, 2025
f14289b
fix typo
jiayev Jun 20, 2025
32aac4f
refactor to avoid sampling when disabled
jiayev Jun 20, 2025
dc84cff
Merge branch 'dev' into advanced-skin
jiayev Jun 24, 2025
1910bd4
had to apply skin on non deferred
jiayev Jun 24, 2025
7aab8de
fix some nondeferred flickering
jiayev Jun 25, 2025
3c61b61
disable hook when disable skin
jiayev Jun 27, 2025
f997ea1
Merge branch 'dev' into advanced-skin
jiayev Jun 27, 2025
34fbeda
Merge branch 'dev' into advanced-skin
jiayev Jul 1, 2025
cd151a6
Merge branch 'dev' into advanced-skin
jiayev Jul 3, 2025
d534a2c
remove the detail ao because it looks bad
jiayev Jul 3, 2025
68996d8
Merge branch 'dev' into advanced-skin
jiayev Jul 18, 2025
3486a2a
Merge branch 'dev' into advanced-skin
jiayev Jul 20, 2025
b929b3f
fix hlsl error
jiayev Jul 20, 2025
6973bf8
Merge branch 'dev' into advanced-skin
jiayev Jul 27, 2025
610a9a5
Merge branch 'dev' into advanced-skin
jiayev Jul 29, 2025
f35c3a5
Merge branch 'dev' into advanced-skin
jiayev Jul 29, 2025
58a4e37
Merge branch 'dev' into advanced-skin
jiayev Jul 30, 2025
ebaddec
fix compile error
jiayev Jul 30, 2025
e73623b
skin getsingleton fix
jiayev Jul 30, 2025
13b3eb6
separate skin ao
jiayev Aug 3, 2025
b5cb19e
fix mask
jiayev Aug 3, 2025
d46caff
fix skin off
jiayev Aug 3, 2025
8a9c3d4
change pi to PBRLightingCompensation
jiayev Aug 3, 2025
4468881
fix skin decal
jiayev Aug 3, 2025
6535aa4
temp disable wetness, fix tex loading
jiayev Aug 6, 2025
f25906a
revert special skin reflectance
jiayev Aug 7, 2025
4fe7c7d
skip in another way
jiayev Aug 8, 2025
fb652e3
fix wetness logic
jiayev Aug 8, 2025
2642d4a
really disable
jiayev Aug 8, 2025
0119bf6
Merge branch 'dev' into advanced-skin
jiayev Aug 8, 2025
c809cfc
better handling extra texture
jiayev Aug 10, 2025
6c05c15
fix compile error
jiayev Aug 10, 2025
5828dc3
fix compile error
jiayev Aug 10, 2025
8241fbd
reenable wetness
jiayev Aug 10, 2025
626ca94
Merge branch 'dev' into advanced-skin
jiayev Aug 14, 2025
ffb0205
Merge branch 'dev' into advanced-skin
jiayev Aug 17, 2025
56b025f
Merge branch 'dev' into advanced-skin
jiayev Aug 25, 2025
fef2275
saturate curvature to avoid negative value
jiayev Aug 25, 2025
a86541f
Merge branch 'dev' into advanced-skin
jiayev Sep 12, 2025
83641bb
Merge branch 'dev' into advanced-skin
jiayev Sep 15, 2025
0abc151
Merge branch 'dev' into advanced-skin
jiayev Sep 24, 2025
a7dcf2d
Merge branch 'dev' into advanced-skin
jiayev Sep 27, 2025
5c26a31
adapt to dev
jiayev Sep 27, 2025
3635ce7
Merge branch 'dev' into advanced-skin
jiayev Oct 1, 2025
5036bb5
separate wet normal for specular
jiayev Oct 8, 2025
40ccf3a
Merge branch 'dev' into advanced-skin
jiayev Oct 8, 2025
133186f
fix broken detail
jiayev Oct 28, 2025
7d254f0
Merge branch 'dev' into advanced-skin
jiayev Oct 28, 2025
b6e69f6
Merge branch 'dev' into advanced-skin
jiayev Dec 4, 2025
2e2db8c
no more sweat at dead
jiayev Dec 4, 2025
fd01c8c
add dw api
jiayev Dec 4, 2025
ed07ade
fix code
jiayev Dec 5, 2025
0f1f592
Merge branch 'dev' into advanced-skin
jiayev Dec 14, 2025
541368c
Merge remote-tracking branch 'origin/dev' into advanced-skin
jiayev Jan 29, 2026
0c2566d
reduce skin detail bias
jiayev Jan 29, 2026
c6a3459
Merge branch 'dev' into advanced-skin
jiayev Feb 26, 2026
3aba25b
fix(Lighting): enhance soft lighting calculations in EvaluateLighting…
jiayev Feb 26, 2026
7051ba8
Merge branch 'dev' into advanced-skin
jiayev Mar 19, 2026
006d2ab
Merge branch 'dev' into advanced-skin
jiayev Mar 21, 2026
8354492
fix shader
jiayev Mar 22, 2026
2171ed2
Merge branch 'dev' into advanced-skin
jiayev May 6, 2026
33ca1c9
Merge branch 'dev' into advanced-skin
jiayev May 11, 2026
e1fd330
fix(skin): install hooks in PostPostLoad for BSLightingShader
jiayev May 11, 2026
7266bbe
fix: rename SetShaderResouces to SetShaderResources for consistency
jiayev May 13, 2026
459a362
feat(skin): bone-anchored water wetness with temporal persistence
jiayev May 13, 2026
b18638e
fix(skin): improve tooltip descriptions for skin wetness settings
jiayev May 14, 2026
4b8a9e2
fix(skin): decouple bone wetness from skinPerGeometry.y fade
jiayev May 14, 2026
e2100b3
Merge branch 'dev' into advanced-skin
jiayev May 18, 2026
9ba5678
fix double to float
jiayev May 18, 2026
32e69f3
Merge branch 'dev' into advanced-skin
jiayev May 27, 2026
50039fc
revert per bone wetness
jiayev May 27, 2026
eacb63f
Merge branch 'dev' into advanced-skin
jiayev Jun 1, 2026
27c674e
feat(skin): implement water height caching for improved wetness calcu…
jiayev Jun 1, 2026
6662499
prevent 0 fade time
jiayev Jun 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions features/Skin/Shaders/Features/Skin.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[Info]
Version = 1-0-0
300 changes: 300 additions & 0 deletions features/Skin/Shaders/Skin/Skin.hlsli
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
#ifndef __SKIN_HLSLI__
#define __SKIN_HLSLI__

#include "Common/BRDF.hlsli"
#include "Common/Color.hlsli"
#include "Common/LightingCommon.hlsli"
#include "Common/Math.hlsli"
#include "Common/Shading.hlsli"
#include "Common/SharedData.hlsli"

namespace Skin
{
float CalculateCurvature(float3 N)
{
const float3 dNdx = ddx(N);
const float3 dNdy = ddy(N);
return length(float2(dot(dNdx, dNdx), dot(dNdy, dNdy)));
}

#if defined(PSHADER)
cbuffer SkinPerGeometry : register(b7)
{
float4 skinPerGeometry;
};
#endif
#if defined(SKIN)
Texture2D<float4> TexSkinDetailNormal : register(t72);

// [Jorge Jimenez, Diego Gutierrez 2015, "Separable Subsurface Scattering"]
// https://www.iryoku.com/separable-sss/
float3 SSSSTransmittance(float translucency, float sssWidth, float3 worldNormal, float3 light, float d)
{
/**
* Calculate the scale of the effect.
*/
float scale = 8.25 * (1.0 - translucency) / sssWidth;

/**
* First we shrink the position inwards the surface to avoid artifacts:
* (Note that this can be done once for all the lights)
*/
// float4 shrinkedPos = float4(worldPosition - 0.005 * worldNormal, 1.0);

/**
* Now we calculate the thickness from the light point of view:
*/
// float4 shadowPosition = mul(shrinkedPos, lightViewProjection);
// float d1 = SSSSSampleShadowmap(shadowPosition.xy / shadowPosition.w).r; // 'd1' has a range of 0..1
// float d2 = shadowPosition.z; // 'd2' has a range of 0..'lightFarPlane'
// d1 *= lightFarPlane; // So we scale 'd1' accordingly:
// float d = scale * abs(d1 - d2);
d = scale * abs(d); // Use the passed 'd' value instead of calculating it here.

/**
* Armed with the thickness, we can now calculate the color by means of the
* precalculated transmittance profile.
* (It can be precomputed into a texture, for maximum performance):
*/
float dd = -d * d;
float3 profile = float3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
float3(0.1, 0.336, 0.344) * exp(dd / 0.0484) +
float3(0.118, 0.198, 0.0) * exp(dd / 0.187) +
float3(0.113, 0.007, 0.007) * exp(dd / 0.567) +
float3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
float3(0.078, 0.0, 0.0) * exp(dd / 7.41);

/**
* Using the profile, we finally approximate the transmitted lighting from
* the back of the object:
*/
return profile * saturate(0.3 + dot(light, -worldNormal));
}

float3 DualSpecularGGX(float AverageRoughness, float Lobe0Roughness, float Lobe1Roughness, float LobeMix, float3 SpecularColor, float NdotL, float NdotV, float NdotH, float VdotH, out float3 F)
{
float D = lerp(BRDF::D_GGX(Lobe0Roughness, NdotH), BRDF::D_GGX(Lobe1Roughness, NdotH), LobeMix);
float G = BRDF::Vis_SmithJointApprox(AverageRoughness, NdotV, NdotL);
F = BRDF::F_Schlick(SpecularColor, VdotH);

return D * G * F;
}

// a contact shadow approximation, totally not physically correct; a riff on "Chan 2018, "Material Advances in Call of Duty: WWII" and "The Technical Art of Uncharted 4" http://advances.realtimerendering.com/other/2016/naughty_dog/NaughtyDog_TechArt_Final.pdf (microshadowing)"
float ApproximateDirectOcculusion(float aoVisibility, float NdotL)
{
float aperture = rsqrt(1.0000001 - aoVisibility);
NdotL += 0.1; // when using bent normals, avoids overshadowing - bent normals are just approximation anyhow
return saturate(NdotL * aperture);
}

void SkinDirectLightInput(
out DirectLightingOutput lightingOutput,
DirectContext context,
MaterialProperties material)
{
lightingOutput = (DirectLightingOutput)0;
context.lightColor *= Color::PBRLightingCompensation * context.detailedShadow;

const float3 N = context.worldNormal;
const float3 V = context.viewDir;
const float3 L = context.lightDir;
const float3 H = context.halfVector;

const float oNdotL = dot(N, L);
float NdotL = clamp(oNdotL, 1e-5, 1.0);
float NdotV = saturate(abs(dot(N, V)) + 1e-5);
float NdotH = saturate(dot(N, H));
float VdotH = saturate(dot(V, H));

context.lightColor *= ApproximateDirectOcculusion(material.AO, NdotL);

float averageRoughness = lerp(material.Roughness, material.RoughnessSecondary, material.SecondarySpecIntensity);

lightingOutput.diffuse += context.lightColor * NdotL * BRDF::Diffuse_Burley(averageRoughness, NdotV, NdotL, VdotH);
float3 F;
float3 F0 = material.F0 * saturate(1 - material.Curvature);

lightingOutput.specular += DualSpecularGGX(averageRoughness, material.Roughness, material.RoughnessSecondary, material.SecondarySpecIntensity, F0, NdotL, NdotV, NdotH, VdotH, F) * context.lightColor * NdotL;

float2 specularBRDF = BRDF::EnvBRDF(averageRoughness, NdotV);
lightingOutput.specular *= 1 + F0 * (1 / (specularBRDF.x + specularBRDF.y) - 1);
lightingOutput.diffuse *= 1 - F;

if (material.FuzzWeight > 0.0) {
float3 FuzzF0 = material.FuzzColor * saturate(1 - material.Curvature);
float fuzzD = BRDF::D_Charlie(material.FuzzRoughness, NdotH);
float fuzzG = BRDF::Vis_Neubelt(NdotV, NdotL);
float3 fuzzF = BRDF::F_Schlick(FuzzF0, VdotH);
float3 fuzzSpecular = fuzzD * fuzzG * fuzzF * context.lightColor * NdotL;
float2 fuzzSpecularBRDF = BRDF::EnvBRDFApproxLazarov(material.FuzzRoughness, NdotV);
fuzzSpecular *= 1 + material.FuzzColor * (1 / (fuzzSpecularBRDF.x + fuzzSpecularBRDF.y) - 1);

lightingOutput.specular += fuzzSpecular * material.FuzzWeight;
}

float3 sssTransmittance = SSSSTransmittance(
SharedData::skinData.sssParams.x,
SharedData::skinData.sssParams.y,
N,
L,
material.Thickness) *
SharedData::skinData.sssParams.w;
lightingOutput.transmission = min(sssTransmittance * context.lightColor * context.softShadow * material.BaseColor, context.lightColor);
}

void SkinIndirectLobeWeights(
out IndirectLobeWeights lobeWeights,
MaterialProperties material,
IndirectContext context)
{
lobeWeights = (IndirectLobeWeights)0;

const float3 N = context.worldNormal;
const float3 V = context.viewDir;
const float3 VN = context.vertexNormal;

float NdotV = saturate(dot(N, V));

float averageRoughness = lerp(material.Roughness, material.RoughnessSecondary, material.SecondarySpecIntensity);

float2 specularBRDF = BRDF::EnvBRDF(averageRoughness, NdotV);

lobeWeights.specular = material.F0 * specularBRDF.x + specularBRDF.y;

lobeWeights.diffuse = material.BaseColor * (1.0 - lobeWeights.specular.x - lobeWeights.specular.y);
lobeWeights.specular *= 1 + material.F0 * (1 / (specularBRDF.x + specularBRDF.y) - 1);
Comment thread
jiayev marked this conversation as resolved.

float3 R = reflect(-V, N);
float horizon = min(1.0 + dot(R, VN), 1.0);
horizon *= horizon;
lobeWeights.specular *= horizon;

float3 diffuseAO = material.AO;
float3 specularAO = SpecularAOLagarde(NdotV, material.AO, averageRoughness);

diffuseAO = MultiBounceAO(material.BaseColor, diffuseAO.x).y;
specularAO = MultiBounceAO(material.F0, specularAO.x).y;

lobeWeights.diffuse *= diffuseAO;
lobeWeights.specular *= specularAO;

lobeWeights.specular *= saturate(1 - material.Curvature);
}

// https://blog.selfshadow.com/publications/blending-in-detail/
// geometric normal s, a base normal t and a secondary (or detail) normal u
float3 ReorientNormal(float3 u, float3 t, float3 s)
{
// Build the shortest-arc quaternion
float4 q = float4(cross(s, t), dot(s, t) + 1) / sqrt(2 * (dot(s, t) + 1));

// Rotate the normal
return u * (q.w * q.w - dot(q.xyz, q.xyz)) + 2 * q.xyz * dot(q.xyz, u) + 2 * q.w * cross(q.xyz, u);
Comment thread
jiayev marked this conversation as resolved.
}

// for when s = (0,0,1)
float3 ReorientNormal(float3 n1, float3 n2)
{
n1 += float3(0, 0, 1);
n2 *= float3(-1, -1, 1);

return n1 * dot(n1, n2) / n1.z - n2;
}

float3x3 ReconstructTBN(float3 worldPos, float3 worldNormal, float2 uv)
{
float3 dFdx = ddx(worldPos);
float3 dFdy = ddy(worldPos);
float2 dUVdx = ddx(uv);
float2 dUVdy = ddy(uv);
float3 tangent = normalize(dFdx * dUVdy.y - dFdy * dUVdx.y);
float3 bitangent = normalize(dFdy * dUVdx.x - dFdx * dUVdy.x);
tangent = normalize(tangent - worldNormal * dot(worldNormal, tangent));
bitangent = normalize(bitangent - worldNormal * dot(worldNormal, bitangent));

return float3x3(tangent, bitangent, normalize(worldNormal));
}

float3 CalculateNormalFromHeight(float height, float heightScale, float2 uv)
{
float dHdx = ddx(height);
float dHdy = ddy(height);
float2 dUVdx = ddx(uv);
float2 dUVdy = ddy(uv);

float det = dUVdx.x * dUVdy.y - dUVdx.y * dUVdy.x;
if (det == 0.0f) {
return float3(0, 0, 1); // Avoid division by zero
}

float dHdx_Tex = (dHdx * dUVdy.y - dHdy * dUVdx.y) / det;
float dHdy_Tex = (dHdy * dUVdx.x - dHdx * dUVdy.x) / det;
float3 normal = float3(-dHdx_Tex, -dHdy_Tex, 0);
return normal * heightScale + float3(0, 0, 1);
}

float FBM(float2 uv, float base_scale, int octaves, float lacunarity, float persistence, float z_offset_multiplier)
{
float total = 0.0;
float frequency = base_scale;
float amplitude = 1.0;
float max_amplitude = 0.0;
for (int i = 0; i < octaves; i++) {
total += amplitude * (Random::perlinNoise(float3(uv * frequency, (float)i * z_offset_multiplier)) + 1.0) * 0.5;

max_amplitude += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
if (max_amplitude > 0.0) {
return total / max_amplitude;
}
return 0.0;
}

float PerlinNoise(float2 uv, float scale, float lacunarity, float persistence, float strength)
{
if (strength <= 0.001f) {
return 0.0f;
}
if (strength >= 0.999f) {
return 1.0f;
}
int octaves = 5;
float z_offset_multiplier = 7.375f;

float noise_value = FBM(uv, scale, octaves, lacunarity, persistence, z_offset_multiplier);

float dynamic_threshold = 1.0f - strength;

float sweat_intensity = saturate((noise_value - dynamic_threshold) / strength);

sweat_intensity = pow(sweat_intensity, 1.5f);

if (strength > 0.8f) {
sweat_intensity = sweat_intensity * saturate(0.99f - (strength - 0.8f) * 5.0f) + (strength - 0.8f) * 5.0f;
}
return pow(sweat_intensity, 0.1f);
}
#endif

float2 GetWetness(float z, float3 modelNormal)
{
if (skinPerGeometry.x == 0.f && skinPerGeometry.y == 0.f)
return 0.f;

float waterWet = 0.0f;
float waterLevel = skinPerGeometry.z + skinPerGeometry.w;

waterWet = skinPerGeometry.y * (1 - smoothstep(waterLevel - 2.5f, waterLevel + 2.5f, z));

float sweatWet = skinPerGeometry.x;
#if !defined(SKIN)
sweatWet *= 1.0f - saturate(dot(modelNormal, float3(0, 0, 1)));
#endif
return float2(sweatWet, waterWet);
}
}

#endif // __SKIN_HLSLI__
Binary file added features/Skin/Shaders/Skin/skin_detail_n.dds
Binary file not shown.
Loading
Loading