载入中...
搜索中...
未找到
SparseVolumeTexture.cpp
浏览该文件的文档.
2
4
5#include <algorithm>
6#include <cmath>
7#include <fstream>
8
9#include <glm/common.hpp>
10
11namespace eve::graphics {
12namespace {
13
14constexpr char kMagic[4] = {'E', 'V', 'S', 'V'};
15constexpr std::uint32_t kVersion = 1;
16
17template <typename T>
18bool writeValue(std::ofstream &stream, const T &value) {
19 stream.write(reinterpret_cast<const char *>(&value), sizeof(T));
20 return bool(stream);
21}
22
23template <typename T>
24bool readValue(std::ifstream &stream, T &value) {
25 stream.read(reinterpret_cast<char *>(&value), sizeof(T));
26 return bool(stream);
27}
28
29bool isVacuum(const FogFroxel &f) {
30 return f.extinction == 0.f && f.scattering == glm::vec3(0.f) && f.emissive == glm::vec3(0.f);
31}
32
33} // namespace
34
35void SparseVolumeTexture::resize(int width, int height, int depth, int brickSize) {
36 width_ = std::max(width, 1);
37 height_ = std::max(height, 1);
38 depth_ = std::max(depth, 1);
39 brickSize_ = std::clamp(brickSize, 2, 32);
40 bricks_.clear();
41}
42
43void SparseVolumeTexture::clear() { bricks_.clear(); }
44
46 const std::size_t perBrick = std::size_t(brickSize_) * std::size_t(brickSize_) *
47 std::size_t(brickSize_);
48 return bricks_.size() * perBrick;
49}
50
51bool SparseVolumeTexture::inBounds(int x, int y, int z) const {
52 return x >= 0 && y >= 0 && z >= 0 && x < width_ && y < height_ && z < depth_;
53}
54
55std::uint64_t SparseVolumeTexture::brickKey(int bx, int by, int bz) const {
56 return (std::uint64_t(std::uint32_t(bx)) << 42) |
57 (std::uint64_t(std::uint32_t(by)) << 21) | std::uint64_t(std::uint32_t(bz));
58}
59
60std::size_t SparseVolumeTexture::localIndex(int x, int y, int z) const {
61 const int lx = x % brickSize_;
62 const int ly = y % brickSize_;
63 const int lz = z % brickSize_;
64 return (std::size_t(lz) * std::size_t(brickSize_) + std::size_t(ly)) *
65 std::size_t(brickSize_) + std::size_t(lx);
66}
67
68void SparseVolumeTexture::setVoxel(int x, int y, int z, const FogFroxel &voxel) {
69 if (!inBounds(x, y, z)) return;
70 const std::uint64_t key = brickKey(x / brickSize_, y / brickSize_, z / brickSize_);
71 auto found = bricks_.find(key);
72 if (found == bricks_.end()) {
73 if (isVacuum(voxel)) return;
74 Brick brick;
75 brick.voxels.resize(std::size_t(brickSize_) * std::size_t(brickSize_) *
76 std::size_t(brickSize_));
77 found = bricks_.emplace(key, std::move(brick)).first;
78 }
79 found->second.voxels[localIndex(x, y, z)] = voxel;
80}
81
83 if (!inBounds(x, y, z)) return {};
84 const auto found = bricks_.find(brickKey(x / brickSize_, y / brickSize_, z / brickSize_));
85 return found == bricks_.end() ? FogFroxel{} : found->second.voxels[localIndex(x, y, z)];
86}
87
88FogFroxel SparseVolumeTexture::sample(float u, float v, float w) const {
89 return getVoxel(std::clamp(int(u * float(width_)), 0, width_ - 1),
90 std::clamp(int(v * float(height_)), 0, height_ - 1),
91 std::clamp(int(w * float(depth_)), 0, depth_ - 1));
92}
93
94void SparseVolumeTexture::bake(const VolumeDensityGraph &graph, const glm::vec3 &worldMin,
95 const glm::vec3 &worldMax, float extinctionScale,
96 const glm::vec3 &albedo, float emptyThreshold, float time) {
97 clear();
98 const glm::vec3 omega = glm::clamp(albedo, glm::vec3(0.f), glm::vec3(1.f));
99 for (int z = 0; z < depth_; ++z) {
100 for (int y = 0; y < height_; ++y) {
101 for (int x = 0; x < width_; ++x) {
102 const glm::vec3 uvw((float(x) + 0.5f) / float(width_),
103 (float(y) + 0.5f) / float(height_),
104 (float(z) + 0.5f) / float(depth_));
105 const float extinction = std::max(0.f, graph.evaluate(
106 worldMin + (worldMax - worldMin) * uvw, time) * extinctionScale);
107 if (extinction <= emptyThreshold) continue;
108 FogFroxel voxel;
109 voxel.extinction = extinction;
110 voxel.scattering = omega * extinction;
111 setVoxel(x, y, z, voxel);
112 }
113 }
114 }
115}
116
117bool SparseVolumeTexture::save(const std::string &path) const {
118 std::ofstream stream(path, std::ios::binary | std::ios::trunc);
119 if (!stream) return false;
120 stream.write(kMagic, sizeof(kMagic));
121 const std::uint32_t width = std::uint32_t(width_), height = std::uint32_t(height_);
122 const std::uint32_t depth = std::uint32_t(depth_), brickSize = std::uint32_t(brickSize_);
123 const std::uint64_t brickCount = std::uint64_t(bricks_.size());
124 if (!writeValue(stream, kVersion) || !writeValue(stream, width) || !writeValue(stream, height) ||
125 !writeValue(stream, depth) || !writeValue(stream, brickSize) ||
126 !writeValue(stream, brickCount)) return false;
127 std::vector<std::uint64_t> keys;
128 keys.reserve(bricks_.size());
129 for (const auto &entry : bricks_) keys.push_back(entry.first);
130 std::sort(keys.begin(), keys.end());
131 for (std::uint64_t key : keys) {
132 if (!writeValue(stream, key)) return false;
133 const Brick &brick = bricks_.at(key);
134 for (const FogFroxel &f : brick.voxels) {
135 const float values[9] = {f.scattering.x, f.scattering.y, f.scattering.z, f.extinction,
136 f.emissive.x, f.emissive.y, f.emissive.z, f.anisotropy,
137 f.lightVisibility};
138 stream.write(reinterpret_cast<const char *>(values), sizeof(values));
139 if (!stream) return false;
140 }
141 }
142 return true;
143}
144
145bool SparseVolumeTexture::load(const std::string &path) {
146 std::ifstream stream(path, std::ios::binary);
147 char magic[4]{};
148 stream.read(magic, sizeof(magic));
149 std::uint32_t version = 0, width = 0, height = 0, depth = 0, brickSize = 0;
150 std::uint64_t brickCount = 0;
151 if (!stream || !std::equal(std::begin(magic), std::end(magic), std::begin(kMagic)) ||
152 !readValue(stream, version) || version != kVersion || !readValue(stream, width) ||
153 !readValue(stream, height) || !readValue(stream, depth) || !readValue(stream, brickSize) ||
154 !readValue(stream, brickCount) || width == 0 || height == 0 || depth == 0 ||
155 brickSize < 2 || brickSize > 32) return false;
156 SparseVolumeTexture loaded;
157 loaded.resize(int(width), int(height), int(depth), int(brickSize));
158 const std::size_t voxelsPerBrick = std::size_t(brickSize) * std::size_t(brickSize) *
159 std::size_t(brickSize);
160 for (std::uint64_t i = 0; i < brickCount; ++i) {
161 std::uint64_t key = 0;
162 if (!readValue(stream, key)) return false;
163 Brick brick;
164 brick.voxels.resize(voxelsPerBrick);
165 for (FogFroxel &f : brick.voxels) {
166 float values[9]{};
167 stream.read(reinterpret_cast<char *>(values), sizeof(values));
168 if (!stream) return false;
169 f.scattering = {values[0], values[1], values[2]};
170 f.extinction = values[3];
171 f.emissive = {values[4], values[5], values[6]};
172 f.anisotropy = values[7];
173 f.lightVisibility = values[8];
174 }
175 loaded.bricks_.emplace(key, std::move(brick));
176 }
177 *this = std::move(loaded);
178 return true;
179}
180
181} // namespace eve::graphics
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int bz
Definition CaveMesh.cpp:114
int bx
Definition CaveMesh.cpp:114
int by
Definition CaveMesh.cpp:114
graphics::Texture * albedo
std::map< std::string, Var > values
std::uint32_t key
float u
Definition Grass.cpp:233
float v
std::uint32_t height
std::uint32_t width
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
float f
std::string path
Definition PlayHost.cpp:110
bool found
Vec extinction
std::uint32_t depth
Brick-sparse, pre-bakeable participating-media texture.
bool save(const std::string &path) const
Save the deterministic EVSV v1 binary format.
FogFroxel sample(float u, float v, float w) const
Nearest sample using normalized coordinates.
std::size_t getAllocatedVoxelCount() const
Returns the allocated voxel count.
void resize(int width, int height, int depth, int brickSize=8)
Allocate a logical volume; bricks are created only for non-empty voxels.
void bake(const VolumeDensityGraph &graph, const glm::vec3 &worldMin, const glm::vec3 &worldMax, float extinctionScale, const glm::vec3 &albedo, float emptyThreshold=1e-5f, float time=0.f)
Pre-bake a procedural graph into sparse bricks.
FogFroxel getVoxel(int x, int y, int z) const
Read one voxel; missing bricks return vacuum.
void setVoxel(int x, int y, int z, const FogFroxel &voxel)
Store one voxel; all-zero voxels do not allocate a new brick.
bool load(const std::string &path)
Load the deterministic EVSV v1 binary format, replacing current data.
Compact acyclic program that evaluates procedural volume density.
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
Participating-media coefficients stored in one froxel.