11float smoothstep(
float edge0,
float edge1,
float x) {
12 const float t = std::clamp((
x - edge0) / (edge1 - edge0), 0.f, 1.f);
13 return t *
t * (3.f - 2.f *
t);
17float cellNoise(
int x,
int y) {
18 const float v = std::sin(
float(
x) * 12.9898f +
float(
y) * 78.233f) * 43758.5453f;
19 return v - std::floor(
v);
29 width_ = std::max(
width, 0);
30 height_ = std::max(
height, 0);
31 data_.assign(
size_t(width_) *
size_t(height_), 0.f);
36 return x >= 0 &&
y >= 0 &&
x < width_ &&
y < height_;
40 const float c = clamp01(
v);
41 std::fill(data_.begin(), data_.end(),
c);
47 data_[size_t(
y) * width_ +
x] = clamp01(
h);
53 return data_[size_t(
y) * width_ +
x];
58 if (width_ <= 0 || height_ <= 0)
return;
60 float len = std::sqrt(dirX * dirX + dirZ * dirZ);
69 const float r = std::max(
radius, 0.25f);
70 const float d = std::clamp(
depth, 0.f, 1.f);
73 const int x0 = int(std::floor(
cx -
margin));
74 const int x1 = int(std::floor(
cx +
margin));
75 const int z0 = int(std::floor(cz -
margin));
76 const int z1 = int(std::floor(cz +
margin));
78 for (
int z = z0;
z <= z1; ++
z) {
79 for (
int x = x0;
x <= x1; ++
x) {
81 const float dx = float(
x) -
cx;
82 const float dz = float(
z) - cz;
83 const float along =
dx * dirX +
dz * dirZ;
84 const float perp = -
dx * dirZ +
dz * dirX;
85 const float n = std::sqrt((along /
r) * (along /
r) +
87 if (
n > 1.2f)
continue;
92 const float depression =
d * (1.f - smoothstep(0.72f, 1.f,
n));
96 const float ring = smoothstep(0.72f, 1.f,
n) *
97 (1.f - smoothstep(1.f, 1.18f,
n));
98 const float rnd = 0.35f + 0.65f * cellNoise(
x,
z);
99 const float rim =
d * 0.5f * ring * rnd;
100 float &
cell = data_[size_t(
z) * width_ +
x];
101 cell = clamp01(
cell - depression + rim);
108 if (width_ <= 0 || height_ <= 0)
return;
110 const float r = std::max(
radius, 0.25f);
111 const float d = std::clamp(
depth, 0.f, 1.f);
112 const float margin =
r * 1.45f;
113 const int x0 = int(std::floor(
cx -
margin));
114 const int x1 = int(std::floor(
cx +
margin));
115 const int z0 = int(std::floor(cz -
margin));
116 const int z1 = int(std::floor(cz +
margin));
118 for (
int z = z0;
z <= z1; ++
z) {
119 for (
int x = x0;
x <= x1; ++
x) {
121 const float dx = float(
x) -
cx;
122 const float dz = float(
z) - cz;
123 const float dist = std::sqrt(
dx *
dx +
dz *
dz);
124 const float n = dist /
r;
125 if (
n > 1.45f)
continue;
127 const float bowl =
d * (1.f -
n) * (1.f -
n);
130 const float ring = smoothstep(0.85f, 1.f,
n) *
131 (1.f - smoothstep(1.f, 1.4f,
n));
132 const float rnd = 0.3f + 0.7f * cellNoise(
x,
z);
133 const float rim =
d * 0.75f * ring * rnd;
134 float &
cell = data_[size_t(
z) * width_ +
x];
142 if (amount <= 0.f || data_.empty())
return;
143 for (
float &
v : data_) {
144 v = std::min(1.f,
v + amount);
150 std::vector<uint8_t> rgba(
size_t(width_) *
size_t(height_) * 4, 0);
151 for (
int z = 0;
z < height_; ++
z) {
152 for (
int x = 0;
x < width_; ++
x) {
153 const float s = data_[size_t(
z) * width_ +
x];
154 const size_t i = (size_t(
z) * width_ +
x) * 4;
155 rgba[i + 0] = uint8_t(std::clamp(
s * 255.f, 0.f, 255.f) + 0.5f);
165 constexpr float kSnow[3] = {0.93f, 0.96f, 1.00f};
166 constexpr float kGround[3] = {0.36f, 0.30f, 0.24f};
167 std::vector<uint8_t> rgba(
size_t(width_) *
size_t(height_) * 4, 0);
168 for (
int z = 0;
z < height_; ++
z) {
169 for (
int x = 0;
x < width_; ++
x) {
170 const float s = data_[size_t(
z) * width_ +
x];
173 const float cover = smoothstep(0.015f, 0.14f,
s);
174 const float powder = smoothstep(0.18f, 0.85f,
s);
175 constexpr float kPacked[3] = {0.76f, 0.84f, 0.94f};
176 const float n = (cellNoise(
x,
z) - 0.5f) * 0.012f;
177 const size_t i = (size_t(
z) * width_ +
x) * 4;
178 for (
int c = 0;
c < 3; ++
c) {
179 const float snow = kPacked[
c] + (kSnow[
c] - kPacked[
c]) * powder;
180 const float v = kGround[
c] + (snow - kGround[
c]) * cover +
n;
181 rgba[i +
c] = uint8_t(std::clamp(
v * 255.f, 0.f, 255.f) + 0.5f);
190 constexpr float kStrength = 9.f;
191 std::vector<uint8_t> rgba(
size_t(width_) *
size_t(height_) * 4, 0);
192 if (width_ <= 0 || height_ <= 0)
return rgba;
193 auto sample = [&](
int x,
int z) ->
float {
194 x = std::clamp(
x, 0, width_ - 1);
195 z = std::clamp(
z, 0, height_ - 1);
196 return data_[size_t(
z) * width_ +
x];
198 for (
int z = 0;
z < height_; ++
z) {
199 for (
int x = 0;
x < width_; ++
x) {
200 const float dhdu = (sample(
x + 1,
z) - sample(
x - 1,
z)) * 0.5f * kStrength;
201 const float dhdv = (sample(
x,
z + 1) - sample(
x,
z - 1)) * 0.5f * kStrength;
202 const float len = std::sqrt(dhdu * dhdu + dhdv * dhdv + 1.f);
203 const float nx = -dhdu / len;
204 const float ny = -dhdv / len;
205 const float nz = 1.f / len;
206 const size_t i = (size_t(
z) * width_ +
x) * 4;
207 rgba[i + 0] = uint8_t(std::clamp((
nx * 0.5f + 0.5f) * 255.f, 0.f, 255.f) + 0.5f);
208 rgba[i + 1] = uint8_t(std::clamp((
ny * 0.5f + 0.5f) * 255.f, 0.f, 255.f) + 0.5f);
209 rgba[i + 2] = uint8_t(std::clamp((
nz * 0.5f + 0.5f) * 255.f, 0.f, 255.f) + 0.5f);
216float SnowField::clamp01(
float v) {
217 return std::clamp(
v, 0.f, 1.f);
std::vector< uint8_t > toAlbedoRGBA() const
Albedo as RGBA8: cool-white snow blended to dark ground by depth, plus a tiny per-cell hash,...
bool inBounds(int x, int y) const
Compatibility predicate returning whether a cell is inside the field.
void stampFootprint(float cx, float cz, float dirX, float dirZ, float radius, float depth)
Press a footprint into the snow: an ellipse along (dirX, dirZ) with a soft bowl depression and a rais...
std::vector< uint8_t > toHeightRGBA() const
POM height map as RGBA8 (width x height x 4): R = snow depth, G = B = 0, A = 255 (white = raised towa...
void resize(int width, int height)
Resize.
float height(int x, int y) const
Cell value in [0,1]; out-of-bounds reads 0.
std::vector< uint8_t > toNormalRGBA() const
Tangent-space normal map as RGBA8 derived from the snow-depth gradient (flat = 128,...
SnowField()=default
Snow field.
void addSnowfall(float amount)
Snowfall recovery: raise every cell toward 1 by amount. Call with dt * snowfallRate each frame; clamp...
void stampImpact(float cx, float cz, float radius, float depth)
Carve an impact crater: a parabolic bowl (deep center, shallow edge) plus a raised snow rim just outs...
void setHeight(int x, int y, float h)
Set one cell (clamped to [0,1]); out-of-bounds is a no-op.
void fill(float v)
Set every cell to v (clamped to [0,1]).