ADD: cloud
This commit is contained in:
226
shaders/cloud_voxel_advect.comp
Normal file
226
shaders/cloud_voxel_advect.comp
Normal file
@@ -0,0 +1,226 @@
|
||||
#version 450
|
||||
|
||||
// Simple voxel advection + noise injection for volumetric clouds.
|
||||
// This is not a full fluid sim, but provides "fluid-like" evolving density in a voxel grid.
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
|
||||
|
||||
layout(std430, set = 0, binding = 0) readonly buffer DensityIn
|
||||
{
|
||||
float density[];
|
||||
} vox_in;
|
||||
|
||||
layout(std430, set = 0, binding = 1) buffer DensityOut
|
||||
{
|
||||
float density[];
|
||||
} vox_out;
|
||||
|
||||
layout(push_constant) uniform Push
|
||||
{
|
||||
vec4 wind_dt; // xyz windVelocityLocal (units/sec), w dt_sec
|
||||
vec4 volume_size_time; // xyz volume size (units), w time_sec
|
||||
vec4 sim_params; // x dissipation (1/sec), y noiseStrength, z noiseScale, w noiseSpeed
|
||||
vec4 emitter_params; // xyz emitterUVW, w emitterRadius
|
||||
ivec4 misc; // x gridResolution, y volumeType (0=cloud,1=smoke,2=flame)
|
||||
} pc;
|
||||
|
||||
uint hash_u32(uint x)
|
||||
{
|
||||
x ^= x >> 16;
|
||||
x *= 0x7feb352du;
|
||||
x ^= x >> 15;
|
||||
x *= 0x846ca68bu;
|
||||
x ^= x >> 16;
|
||||
return x;
|
||||
}
|
||||
|
||||
float hash3_to_unit_float(ivec3 p)
|
||||
{
|
||||
uint h = 0u;
|
||||
h ^= hash_u32(uint(p.x) * 73856093u);
|
||||
h ^= hash_u32(uint(p.y) * 19349663u);
|
||||
h ^= hash_u32(uint(p.z) * 83492791u);
|
||||
return float(h & 0x00FFFFFFu) / float(0x01000000u);
|
||||
}
|
||||
|
||||
float smoothstep01(float x)
|
||||
{
|
||||
x = clamp(x, 0.0, 1.0);
|
||||
return x * x * (3.0 - 2.0 * x);
|
||||
}
|
||||
|
||||
float value_noise3(vec3 p)
|
||||
{
|
||||
ivec3 i0 = ivec3(floor(p));
|
||||
ivec3 i1 = i0 + ivec3(1);
|
||||
|
||||
vec3 f = fract(p);
|
||||
f = vec3(smoothstep01(f.x), smoothstep01(f.y), smoothstep01(f.z));
|
||||
|
||||
float c000 = hash3_to_unit_float(ivec3(i0.x, i0.y, i0.z));
|
||||
float c100 = hash3_to_unit_float(ivec3(i1.x, i0.y, i0.z));
|
||||
float c010 = hash3_to_unit_float(ivec3(i0.x, i1.y, i0.z));
|
||||
float c110 = hash3_to_unit_float(ivec3(i1.x, i1.y, i0.z));
|
||||
float c001 = hash3_to_unit_float(ivec3(i0.x, i0.y, i1.z));
|
||||
float c101 = hash3_to_unit_float(ivec3(i1.x, i0.y, i1.z));
|
||||
float c011 = hash3_to_unit_float(ivec3(i0.x, i1.y, i1.z));
|
||||
float c111 = hash3_to_unit_float(ivec3(i1.x, i1.y, i1.z));
|
||||
|
||||
float x00 = mix(c000, c100, f.x);
|
||||
float x10 = mix(c010, c110, f.x);
|
||||
float x01 = mix(c001, c101, f.x);
|
||||
float x11 = mix(c011, c111, f.x);
|
||||
|
||||
float y0 = mix(x00, x10, f.y);
|
||||
float y1 = mix(x01, x11, f.y);
|
||||
|
||||
return mix(y0, y1, f.z);
|
||||
}
|
||||
|
||||
float fbm3(vec3 p)
|
||||
{
|
||||
float sum = 0.0;
|
||||
float amp = 0.55;
|
||||
float freq = 1.0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
sum += amp * value_noise3(p * freq);
|
||||
freq *= 2.02;
|
||||
amp *= 0.5;
|
||||
}
|
||||
return clamp(sum, 0.0, 1.0);
|
||||
}
|
||||
|
||||
int idx3(ivec3 c, int res)
|
||||
{
|
||||
return c.x + c.y * res + c.z * res * res;
|
||||
}
|
||||
|
||||
float sample_density_trilinear(vec3 uvw, int res)
|
||||
{
|
||||
uvw = clamp(uvw, vec3(0.0), vec3(1.0));
|
||||
|
||||
float fres = float(res);
|
||||
vec3 g = uvw * (fres - 1.0);
|
||||
|
||||
ivec3 base = ivec3(floor(g));
|
||||
base = clamp(base, ivec3(0), ivec3(res - 1));
|
||||
vec3 f = fract(g);
|
||||
|
||||
ivec3 b1 = min(base + ivec3(1), ivec3(res - 1));
|
||||
|
||||
float d000 = vox_in.density[idx3(ivec3(base.x, base.y, base.z), res)];
|
||||
float d100 = vox_in.density[idx3(ivec3(b1.x, base.y, base.z), res)];
|
||||
float d010 = vox_in.density[idx3(ivec3(base.x, b1.y, base.z), res)];
|
||||
float d110 = vox_in.density[idx3(ivec3(b1.x, b1.y, base.z), res)];
|
||||
|
||||
float d001 = vox_in.density[idx3(ivec3(base.x, base.y, b1.z), res)];
|
||||
float d101 = vox_in.density[idx3(ivec3(b1.x, base.y, b1.z), res)];
|
||||
float d011 = vox_in.density[idx3(ivec3(base.x, b1.y, b1.z), res)];
|
||||
float d111 = vox_in.density[idx3(ivec3(b1.x, b1.y, b1.z), res)];
|
||||
|
||||
float x00 = mix(d000, d100, f.x);
|
||||
float x10 = mix(d010, d110, f.x);
|
||||
float x01 = mix(d001, d101, f.x);
|
||||
float x11 = mix(d011, d111, f.x);
|
||||
|
||||
float y0 = mix(x00, x10, f.y);
|
||||
float y1 = mix(x01, x11, f.y);
|
||||
|
||||
return mix(y0, y1, f.z);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int res = max(pc.misc.x, 1);
|
||||
int vol_type = pc.misc.y;
|
||||
|
||||
ivec3 c = ivec3(gl_GlobalInvocationID.xyz);
|
||||
if (c.x >= res || c.y >= res || c.z >= res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Voxel center in [0,1].
|
||||
vec3 uvw = (vec3(c) + vec3(0.5)) / float(res);
|
||||
|
||||
float dt = max(pc.wind_dt.w, 0.0);
|
||||
|
||||
vec3 volSize = max(pc.volume_size_time.xyz, vec3(0.001));
|
||||
vec3 wind_uv = pc.wind_dt.xyz / volSize; // normalized per-second
|
||||
|
||||
// Semi-Lagrangian advection: backtrace.
|
||||
vec3 back = uvw - wind_uv * dt;
|
||||
if (vol_type == 0)
|
||||
{
|
||||
back.xz = fract(back.xz); // wrap XZ for continuous motion (clouds)
|
||||
back.y = clamp(back.y, 0.0, 1.0); // clamp Y
|
||||
}
|
||||
else
|
||||
{
|
||||
back = clamp(back, vec3(0.0), vec3(1.0)); // clamp for localized effects (smoke/flame)
|
||||
}
|
||||
|
||||
float advected = sample_density_trilinear(back, res);
|
||||
|
||||
// Dissipation.
|
||||
float dissipation = max(pc.sim_params.x, 0.0);
|
||||
advected *= exp(-dissipation * dt);
|
||||
|
||||
// Inject new density from procedural noise to keep volume evolving.
|
||||
float time = pc.volume_size_time.w;
|
||||
float noise_scale = max(pc.sim_params.z, 0.001);
|
||||
float noise_speed = pc.sim_params.w;
|
||||
float n = fbm3(uvw * noise_scale + vec3(0.0, time * noise_speed, 0.0));
|
||||
float injected = 0.0;
|
||||
|
||||
if (vol_type == 0)
|
||||
{
|
||||
// Clouds: broad slab with continuous XZ wrapping.
|
||||
injected = smoothstep(0.55, 0.80, n);
|
||||
|
||||
// Height shaping (keep density within a slab).
|
||||
float low = smoothstep(0.00, 0.18, uvw.y);
|
||||
float high = 1.0 - smoothstep(0.78, 1.00, uvw.y);
|
||||
injected *= clamp(low * high, 0.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Smoke/flame: inject near an emitter in UVW space.
|
||||
vec3 e = clamp(pc.emitter_params.xyz, vec3(0.0), vec3(1.0));
|
||||
float r = max(pc.emitter_params.w, 1e-4);
|
||||
|
||||
vec3 dpos = uvw - e;
|
||||
dpos.y *= 1.5; // squash vertically for a more column-like source
|
||||
float dist = length(dpos);
|
||||
float shape = 1.0 - smoothstep(r, r * 1.25, dist);
|
||||
|
||||
if (vol_type == 1)
|
||||
{
|
||||
// Smoke: softer noise threshold.
|
||||
injected = smoothstep(0.45, 0.75, n) * shape;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flame: spikier + stronger flicker.
|
||||
float f = smoothstep(0.35, 0.90, n);
|
||||
injected = (f * f) * shape;
|
||||
}
|
||||
}
|
||||
|
||||
float rate = clamp(pc.sim_params.y * dt, 0.0, 1.0);
|
||||
float out_d = advected;
|
||||
if (vol_type == 2)
|
||||
{
|
||||
// Flames flicker: blend toward injected field (avoid accumulating a "fog").
|
||||
out_d = mix(advected, injected, rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_d = mix(advected, max(advected, injected), rate);
|
||||
}
|
||||
out_d = clamp(out_d, 0.0, 1.0);
|
||||
|
||||
vox_out.density[idx3(c, res)] = out_d;
|
||||
}
|
||||
|
||||
208
shaders/clouds.frag
Normal file
208
shaders/clouds.frag
Normal file
@@ -0,0 +1,208 @@
|
||||
#version 460
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#include "input_structures.glsl"
|
||||
|
||||
layout(location = 0) in vec2 inUV;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
// Set 1: cloud inputs
|
||||
layout(set = 1, binding = 0) uniform sampler2D hdrInput;
|
||||
layout(set = 1, binding = 1) uniform sampler2D posTex;
|
||||
|
||||
layout(set = 1, binding = 2, std430) readonly buffer VoxelDensity
|
||||
{
|
||||
float density[];
|
||||
} voxel;
|
||||
|
||||
layout(push_constant) uniform VolumePush
|
||||
{
|
||||
vec4 volume_center_follow; // xyz: center_local (or offset), w: followCameraXZ (0/1)
|
||||
vec4 volume_half_extents; // xyz: half extents (local)
|
||||
vec4 density_params; // x: densityScale, y: coverage, z: extinction, w: time_sec
|
||||
vec4 scatter_params; // rgb: albedo/tint, w: scatterStrength
|
||||
vec4 emission_params; // rgb: emissionColor, w: emissionStrength
|
||||
ivec4 misc; // x: stepCount, y: gridResolution, z: volumeType (0=cloud,1=smoke,2=flame)
|
||||
} pc;
|
||||
|
||||
vec3 getCameraWorldPosition()
|
||||
{
|
||||
mat3 rotT = mat3(sceneData.view); // R^T
|
||||
mat3 rot = transpose(rotT); // R
|
||||
vec3 T = sceneData.view[3].xyz;
|
||||
return -rot * T;
|
||||
}
|
||||
|
||||
float hash12(vec2 p)
|
||||
{
|
||||
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
bool intersectAABB(vec3 ro, vec3 rd, vec3 bmin, vec3 bmax, out float tmin, out float tmax)
|
||||
{
|
||||
vec3 invD = 1.0 / rd;
|
||||
vec3 t0s = (bmin - ro) * invD;
|
||||
vec3 t1s = (bmax - ro) * invD;
|
||||
vec3 tsmaller = min(t0s, t1s);
|
||||
vec3 tbigger = max(t0s, t1s);
|
||||
tmin = max(max(tsmaller.x, tsmaller.y), tsmaller.z);
|
||||
tmax = min(min(tbigger.x, tbigger.y), tbigger.z);
|
||||
return tmax >= max(tmin, 0.0);
|
||||
}
|
||||
|
||||
int idx3(ivec3 c, int res)
|
||||
{
|
||||
return c.x + c.y * res + c.z * res * res;
|
||||
}
|
||||
|
||||
float sample_voxel_density(vec3 p, vec3 bmin, vec3 bmax)
|
||||
{
|
||||
vec3 uvw = (p - bmin) / (bmax - bmin);
|
||||
if (any(lessThan(uvw, vec3(0.0))) || any(greaterThan(uvw, vec3(1.0))))
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int res = max(pc.misc.y, 1);
|
||||
float fres = float(res);
|
||||
vec3 g = uvw * (fres - 1.0);
|
||||
|
||||
ivec3 base = ivec3(floor(g));
|
||||
base = clamp(base, ivec3(0), ivec3(res - 1));
|
||||
vec3 f = fract(g);
|
||||
|
||||
ivec3 b1 = min(base + ivec3(1), ivec3(res - 1));
|
||||
|
||||
float d000 = voxel.density[idx3(ivec3(base.x, base.y, base.z), res)];
|
||||
float d100 = voxel.density[idx3(ivec3(b1.x, base.y, base.z), res)];
|
||||
float d010 = voxel.density[idx3(ivec3(base.x, b1.y, base.z), res)];
|
||||
float d110 = voxel.density[idx3(ivec3(b1.x, b1.y, base.z), res)];
|
||||
|
||||
float d001 = voxel.density[idx3(ivec3(base.x, base.y, b1.z), res)];
|
||||
float d101 = voxel.density[idx3(ivec3(b1.x, base.y, b1.z), res)];
|
||||
float d011 = voxel.density[idx3(ivec3(base.x, b1.y, b1.z), res)];
|
||||
float d111 = voxel.density[idx3(ivec3(b1.x, b1.y, b1.z), res)];
|
||||
|
||||
float x00 = mix(d000, d100, f.x);
|
||||
float x10 = mix(d010, d110, f.x);
|
||||
float x01 = mix(d001, d101, f.x);
|
||||
float x11 = mix(d011, d111, f.x);
|
||||
|
||||
float y0 = mix(x00, x10, f.y);
|
||||
float y1 = mix(x01, x11, f.y);
|
||||
|
||||
return mix(y0, y1, f.z);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 baseColor = texture(hdrInput, inUV).rgb;
|
||||
|
||||
vec3 camPos = getCameraWorldPosition();
|
||||
|
||||
// Reconstruct a world-space ray for this pixel (Vulkan depth range 0..1).
|
||||
mat4 invViewProj = inverse(sceneData.viewproj);
|
||||
vec2 ndc = inUV * 2.0 - 1.0;
|
||||
vec4 farH = invViewProj * vec4(ndc, 1.0, 1.0);
|
||||
vec3 farP = farH.xyz / max(farH.w, 1e-6);
|
||||
vec3 rd = normalize(farP - camPos);
|
||||
|
||||
// Define a local-space cloud volume (optionally anchored to camera XZ).
|
||||
vec3 center = pc.volume_center_follow.xyz;
|
||||
if (pc.volume_center_follow.w > 0.5)
|
||||
{
|
||||
center.xz += camPos.xz;
|
||||
}
|
||||
vec3 halfExt = max(pc.volume_half_extents.xyz, vec3(0.01));
|
||||
vec3 bmin = center - halfExt;
|
||||
vec3 bmax = center + halfExt;
|
||||
|
||||
float t0, t1;
|
||||
if (!intersectAABB(camPos, rd, bmin, bmax, t0, t1))
|
||||
{
|
||||
outColor = vec4(baseColor, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp march to geometry distance (gbufferPosition.w == 1 for valid surfaces).
|
||||
vec4 posSample = texture(posTex, inUV);
|
||||
if (posSample.w > 0.0)
|
||||
{
|
||||
float surfT = dot(posSample.xyz - camPos, rd);
|
||||
if (surfT > 0.0)
|
||||
{
|
||||
t1 = min(t1, surfT);
|
||||
}
|
||||
}
|
||||
|
||||
int steps = clamp(pc.misc.x, 8, 256);
|
||||
if (t1 <= t0 || steps <= 0)
|
||||
{
|
||||
outColor = vec4(baseColor, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
float dt = (t1 - t0) / float(steps);
|
||||
float jitter = hash12(inUV * 1024.0);
|
||||
float t = max(t0, 0.0) + (jitter - 0.5) * dt;
|
||||
|
||||
vec3 Lsun = normalize(-sceneData.sunlightDirection.xyz);
|
||||
vec3 sunCol = sceneData.sunlightColor.rgb * sceneData.sunlightColor.a;
|
||||
vec3 ambCol = sceneData.ambientColor.rgb;
|
||||
|
||||
float trans = 1.0;
|
||||
vec3 scatter = vec3(0.0);
|
||||
int volType = pc.misc.z;
|
||||
|
||||
for (int i = 0; i < steps; ++i)
|
||||
{
|
||||
vec3 p = camPos + rd * (t + 0.5 * dt);
|
||||
|
||||
float d = sample_voxel_density(p, bmin, bmax);
|
||||
d = max(0.0, d - pc.density_params.y) / max(1.0 - pc.density_params.y, 1e-3);
|
||||
d *= max(pc.density_params.x, 0.0);
|
||||
d *= max(pc.density_params.z, 0.0);
|
||||
|
||||
if (d > 1e-4)
|
||||
{
|
||||
// Exponential absorption / single-scattering approximation.
|
||||
float alpha = 1.0 - exp(-d * dt);
|
||||
|
||||
if (volType == 2)
|
||||
{
|
||||
// Flames: emissive contribution.
|
||||
float flicker = mix(0.65, 1.0, hash12(p.xz * 0.35 + pc.density_params.w * 0.25));
|
||||
vec3 emit = pc.emission_params.rgb * (pc.emission_params.w * flicker);
|
||||
scatter += trans * alpha * emit;
|
||||
}
|
||||
else
|
||||
{
|
||||
float cosTheta = clamp(dot(rd, Lsun), 0.0, 1.0);
|
||||
float phase = 0.30 + 0.70 * pow(cosTheta, 4.0); // cheap forward-scatter bias
|
||||
vec3 light = ambCol * 0.25 + sunCol * phase;
|
||||
|
||||
vec3 albedo = clamp(pc.scatter_params.rgb, vec3(0.0), vec3(1.0));
|
||||
float s = max(pc.scatter_params.w, 0.0);
|
||||
scatter += trans * alpha * light * albedo * s;
|
||||
}
|
||||
trans *= (1.0 - alpha);
|
||||
|
||||
if (trans < 0.01)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
t += dt;
|
||||
if (t > t1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 outRgb = scatter + trans * baseColor;
|
||||
outColor = vec4(outRgb, 1.0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user