Load the four grass and two leaf masks from test/Texture instead of
painting a procedural strip. Pack them into one 2x2-per-variant atlas,
sample frames by instance id, and tint with the existing two-tone green.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
0.48s at 0.12s/frame is exactly 4 frames and can land on the same atlas
index after modulo. Use 0.36s (3 frames) so the CHECK is stable.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
zeroerr Expression copies the compared value, which is illegal for
std::unique_ptr. Compare raw pointers instead.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
Bake alwaysDark into billboard normals so dense and sparse tufts can share
one mesh shader in the 3D pass, then add a windowed GPU test that draws a
field with a shadow-casting occluder and writes grass_field.png.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
Sample terrain meshes with Poisson-disk or Halton blue noise, expand
root-pivoted billboard quads, and shade them with a 4-frame sway atlas.
Instance ids desync the animation, the CSM shadow map picks between
light and dark green, and a second sparse pass adds always-dark tufts.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>