mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
support rain shader in experimental
This commit is contained in:
@@ -188,6 +188,45 @@ void GbufferBlitPass::UpdateConstants(nvrhi::ICommandList* command_list,
|
||||
constants.m_altitude = Global.pCamera.Pos.y;
|
||||
constants.m_time = Timer::GetTime();
|
||||
|
||||
{
|
||||
float percipitation_intensity = glm::saturate(Global.Overcast - 1.);
|
||||
constants.m_rain_params.x = percipitation_intensity; // % amount of droplets
|
||||
constants.m_rain_params.y =
|
||||
glm::mix(15., 1., percipitation_intensity); // Regeneration time
|
||||
static glm::vec4 wiper_timer_out;
|
||||
static glm::vec4 wiper_timer_return;
|
||||
if (TDynamicObject const* owner = Global.pCamera.m_owner;
|
||||
owner && !!owner->MoverParameters->CabActive) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i < owner->dWiperPos.size()) {
|
||||
int index = owner->MoverParameters->CabActive > 0
|
||||
? i
|
||||
: static_cast<int>(owner->dWiperPos.size() - 1) - i;
|
||||
constants.m_wiper_pos[i] = owner->dWiperPos[index];
|
||||
if (owner->dWiperPos[index] > 0. && owner->wiperDirection[index]) {
|
||||
constants.m_wiper_pos[i] += 1.;
|
||||
}
|
||||
if (owner->dWiperPos[index] < .025) {
|
||||
wiper_timer_out[i] = constants.m_time;
|
||||
}
|
||||
if (owner->dWiperPos[index] > .975) {
|
||||
wiper_timer_return[i] = constants.m_time;
|
||||
}
|
||||
constants.m_wiper_timer_out[i] = wiper_timer_out[i];
|
||||
constants.m_wiper_timer_return[i] = wiper_timer_return[i];
|
||||
} else {
|
||||
constants.m_wiper_pos[i] = 0.;
|
||||
wiper_timer_out[i] = constants.m_wiper_timer_out[i] = -1000.;
|
||||
wiper_timer_return[i] = constants.m_wiper_timer_return[i] = -1000.;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
constants.m_wiper_pos = glm::vec4{0.};
|
||||
wiper_timer_out = constants.m_wiper_timer_out = glm::vec4{-1000.};
|
||||
wiper_timer_return = constants.m_wiper_timer_return = glm::vec4{-1000.};
|
||||
}
|
||||
}
|
||||
|
||||
command_list->writeBuffer(m_draw_constants, &constants, sizeof(constants));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry {
|
||||
float m_altitude;
|
||||
glm::vec3 m_light_color;
|
||||
float m_time;
|
||||
glm::vec4 m_rain_params;
|
||||
glm::vec4 m_wiper_pos;
|
||||
glm::vec4 m_wiper_timer_out;
|
||||
glm::vec4 m_wiper_timer_return;
|
||||
};
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
|
||||
@@ -133,6 +133,9 @@ std::string_view MaterialAdapterLegacyMatFile::GetShader() const {
|
||||
if (m_shader == "water") {
|
||||
return "legacy_water";
|
||||
}
|
||||
if (m_shader == "rain_windscreen") {
|
||||
return "windshield_rain";
|
||||
}
|
||||
if (IsSpecGlossShader() && HasSpecGlossMap()) {
|
||||
if (IsNormalMapShader() && HasNormalMap()) {
|
||||
return "legacy_normalmap_specgloss";
|
||||
|
||||
39
betterRenderer/shaders/manul/random.hlsli
Normal file
39
betterRenderer/shaders/manul/random.hlsli
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
uint Hash(uint s);
|
||||
uint Hash(uint2 s);
|
||||
uint Hash(uint3 s);
|
||||
uint Hash(float s);
|
||||
uint HashCombine(uint seed, uint value);
|
||||
|
||||
// Evolving Sub-Grid Turbulence for Smoke Animation
|
||||
// H. Schechter and R. Bridson
|
||||
uint Hash(uint s) {
|
||||
s ^= 2747636419u;
|
||||
s *= 2654435769u;
|
||||
s ^= s >> 16u;
|
||||
s *= 2654435769u;
|
||||
s ^= s >> 16u;
|
||||
s *= 2654435769u;
|
||||
return s;
|
||||
}
|
||||
|
||||
uint Hash(uint2 s) {
|
||||
return HashCombine(Hash(s.x), s.y);
|
||||
}
|
||||
|
||||
uint Hash(uint3 s) {
|
||||
return HashCombine(Hash(s.xy), s.z);
|
||||
}
|
||||
|
||||
uint HashCombine(uint seed, uint value) {
|
||||
return seed ^ (Hash(value) + 0x9e3779b9u + (seed<<6u) + (seed>>2u));
|
||||
}
|
||||
|
||||
uint Rand(inout uint seed) {
|
||||
seed = Hash(seed);
|
||||
return seed;
|
||||
}
|
||||
|
||||
float RandF(inout uint seed) {
|
||||
return float(Rand(seed)) * 2.3283064365386963e-10;
|
||||
}
|
||||
@@ -8,6 +8,10 @@ cbuffer DrawConstants : register(b2) {
|
||||
float g_Altitude;
|
||||
float3 g_LightColor;
|
||||
float g_Time;
|
||||
float4 g_RainParams;
|
||||
float4 g_WiperPos;
|
||||
float4 g_WiperTimerOut;
|
||||
float4 g_WiperTimerReturn;
|
||||
}
|
||||
|
||||
float2 PixelToCS(in float2 pixel, in float2 size) {
|
||||
|
||||
@@ -47,18 +47,6 @@ shaders:
|
||||
binding: 2
|
||||
hint: normalmap
|
||||
default: normalmap
|
||||
# paramx: # Metalness.Roughness.[UNUSED].Normal[X]
|
||||
# binding: 1
|
||||
# hint: linear
|
||||
# default: legacy_params
|
||||
# paramy: # Specular.Occlusion.[UNUSED].Normal[Y]
|
||||
# binding: 2
|
||||
# hint: linear
|
||||
# default: legacy_params
|
||||
# selfillum:
|
||||
# binding: 3
|
||||
# hint: color
|
||||
# default: black
|
||||
masked_shadow_texture: albedo
|
||||
source: ps_default_lit
|
||||
legacy:
|
||||
@@ -137,6 +125,22 @@ shaders:
|
||||
default: normalmap
|
||||
masked_shadow_texture: diffuse
|
||||
source: ps_legacy_water
|
||||
windshield_rain:
|
||||
textures:
|
||||
diffuse:
|
||||
binding: 0
|
||||
hint: color # so that texture will be marked as sRGB
|
||||
default: white
|
||||
raindropsatlas:
|
||||
binding: 1
|
||||
hint: color
|
||||
default: white
|
||||
wipermask:
|
||||
binding: 2
|
||||
hint: color
|
||||
default: white
|
||||
masked_shadow_texture: diffuse
|
||||
source: ps_windshield_rain
|
||||
utility:
|
||||
# Everything that does not belong to scene graph rendering
|
||||
# ImGui shaders
|
||||
|
||||
165
betterRenderer/shaders/ps_windshield_rain.hlsl
Normal file
165
betterRenderer/shaders/ps_windshield_rain.hlsl
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "manul/math.hlsli"
|
||||
#include "manul/material.hlsli"
|
||||
#include "manul/color_transform.hlsli"
|
||||
#include "manul/random.hlsli"
|
||||
|
||||
sampler diffuse_sampler : register(s0);
|
||||
Texture2D<float4> diffuse : register(t0);
|
||||
Texture2D<float4> raindropsatlas : register(t1);
|
||||
Texture2D<float4> wipermask : register(t2);
|
||||
|
||||
float4 getDropTex(float choice, float2 uv) {
|
||||
float2 offset;
|
||||
if (choice < .25) offset = float2(0.0, 0.0);
|
||||
else if (choice < .5) offset = float2(0.5, 0.0);
|
||||
else if (choice < .75) offset = float2(0.0, 0.5);
|
||||
else offset = float2(0.5, 0.5);
|
||||
return raindropsatlas.Sample(diffuse_sampler, offset + uv * 0.5);
|
||||
}
|
||||
|
||||
float GetMixFactor(in float2 co, out float side);
|
||||
|
||||
void MaterialPass(inout MaterialData material) {
|
||||
#if PASS & FORWARD_LIGHTING
|
||||
const float specular_intensity = 1.;
|
||||
const float wobble_strength = .002;
|
||||
const float wobble_speed = 30.;
|
||||
|
||||
float4 tex_color = diffuse.Sample(diffuse_sampler, material.m_TexCoord);
|
||||
if (tex_color.a < .01) discard;
|
||||
|
||||
float2 rainCoord = material.m_TexCoord;
|
||||
float gridSize = ceil(200.);
|
||||
|
||||
const float numDrops = 20000.;
|
||||
const float cycleDuration = 4.;
|
||||
|
||||
float squareMin = .5 / gridSize;
|
||||
float squareMax = 1.2 / gridSize;
|
||||
|
||||
float2 cell = floor(rainCoord * gridSize);
|
||||
|
||||
float3 dropLayer = 0.;
|
||||
float dropMaskSum = 0.;
|
||||
|
||||
// Grid of 9 droplets in immediate neighbourhood
|
||||
for (int oy = -1; oy <= 1; oy++) {
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
|
||||
float2 neighborCell = cell + float2(ox, oy);
|
||||
float2 neighborCenter = (neighborCell + .5) / gridSize;
|
||||
|
||||
float side;
|
||||
float mixFactor = GetMixFactor(neighborCenter, side);
|
||||
|
||||
uint seed = Hash(uint3(neighborCell, side));
|
||||
|
||||
if(mixFactor < RandF(seed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show a percentage of droplets given by rain intensity param
|
||||
float activationSeed = RandF(seed);
|
||||
if (activationSeed > g_RainParams.x)
|
||||
continue; // kropla nieaktywna
|
||||
|
||||
// Randomly modulate droplet center & size
|
||||
float2 dropCenter = (neighborCell + float2(RandF(seed), RandF(seed))) / gridSize;
|
||||
float squareSize = lerp(squareMin, squareMax, RandF(seed));
|
||||
|
||||
float lifeTime = g_Time + RandF(seed) * cycleDuration;
|
||||
float phase = frac(lifeTime / cycleDuration);
|
||||
float active = saturate(1. - phase);
|
||||
|
||||
// Gravity influence (TODO add vehicle speed & wind here!)
|
||||
float gravityStart = .5;
|
||||
float gravityPhase = smoothstep(gravityStart, 1., phase);
|
||||
float dropMass = lerp(.3, 1.2, RandF(seed));
|
||||
float gravitySpeed = .15 * dropMass;
|
||||
float2 gravityOffset = float2(0., gravityPhase * gravitySpeed * phase);
|
||||
|
||||
// Random wobble
|
||||
bool hasWobble = (RandF(seed) < .10);
|
||||
float2 wobbleOffset = 0.;
|
||||
if (hasWobble && gravityPhase > 0.) {
|
||||
float intensity = sin(g_Time * wobble_speed + RandF(seed) * 100.) * wobble_strength * gravityPhase;
|
||||
wobbleOffset = float2(intensity, 0.);
|
||||
}
|
||||
|
||||
float2 slideOffset = gravityOffset + wobbleOffset;
|
||||
|
||||
// Flatten droplets influenced by gravity
|
||||
float flattenAmount = smoothstep(0.1, 0.5, gravityPhase);
|
||||
float flattenX = lerp(1.0, 0.4, flattenAmount);
|
||||
float stretchY = lerp(1.0, 1.6, flattenAmount);
|
||||
|
||||
// Droplet local position & mask
|
||||
float2 diff = (rainCoord + slideOffset) - dropCenter;
|
||||
diff.x *= 1.0 / flattenX;
|
||||
diff.y *= 1.0 / stretchY;
|
||||
float mask = smoothstep(squareSize * 0.5, squareSize * 0.45, max(abs(diff.x), abs(diff.y)));
|
||||
|
||||
if (mask > .001) {
|
||||
float2 localUV = (diff + squareSize * 0.5) / squareSize;
|
||||
float choice = RandF(seed);
|
||||
float4 dropTex = getDropTex(choice, localUV);
|
||||
float sharpAlpha = smoothstep(0.3, 0.9, dropTex.a);
|
||||
|
||||
float colorLuma = length(dropTex.rgb);
|
||||
float alphaRange = smoothstep(0.1, 0.3, colorLuma);
|
||||
float blackAlpha = lerp(0.25, 0.85, alphaRange);
|
||||
|
||||
dropLayer += dropTex.rgb * sharpAlpha * active * blackAlpha * mask;
|
||||
dropMaskSum += sharpAlpha * active * blackAlpha * mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
float3 finalMix = dropLayer;
|
||||
float alphaOut = clamp(dropMaskSum, 0.0, 1.0);
|
||||
material.m_MaterialAlbedoAlpha = float4(finalMix, alphaOut);
|
||||
|
||||
{ // Overlay windshield texture with alpha
|
||||
material.m_MaterialAlbedoAlpha.xyz = lerp(material.m_MaterialAlbedoAlpha.xyz, tex_color.xyz, tex_color.a);
|
||||
material.m_MaterialAlbedoAlpha.a = lerp(material.m_MaterialAlbedoAlpha.a, 1., tex_color.a);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PASS & FORWARD_LIGHTING
|
||||
float GetMixFactor(in float2 co, out float side) {
|
||||
float4 movePhase = g_WiperPos;
|
||||
bool4 is_out = movePhase <= 1.;
|
||||
movePhase = select(is_out, movePhase, 2. - movePhase);
|
||||
|
||||
float4 mask = wipermask.Sample(diffuse_sampler, co);
|
||||
|
||||
float4 areaMask = step(.001, mask);
|
||||
|
||||
float4 maskVal = select(is_out, mask, 1. - mask);
|
||||
float4 wipeWidth = smoothstep(1., .9, movePhase) * .25;
|
||||
float4 cleaned = smoothstep(movePhase - wipeWidth, movePhase, maskVal) * areaMask;
|
||||
float4 side_v = step(maskVal, movePhase);
|
||||
cleaned *= side_v;
|
||||
side_v = select(is_out, 1. - side_v, side_v);
|
||||
|
||||
// "regeneration", raindrops gradually returning after wiper pass:
|
||||
float4 regenPhase = saturate((g_Time - lerp(g_WiperTimerOut, g_WiperTimerReturn, side_v) - .2) / g_RainParams.y);
|
||||
|
||||
side_v = lerp(0., 1. - side_v, areaMask);
|
||||
|
||||
float4 factor_v = lerp(1., regenPhase * (1. - cleaned), areaMask);
|
||||
|
||||
side = 0.;
|
||||
float out_factor = 1.;
|
||||
|
||||
// Find out the wiper blade that influences given grid cell the most
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
bool is_candidate = factor_v[i] < out_factor;
|
||||
out_factor = select(is_candidate, factor_v[i], out_factor);
|
||||
side = select(is_candidate, side_v[i], side);
|
||||
}
|
||||
|
||||
return out_factor;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user