载入中...
搜索中...
未找到
CubemapPrefilter.h
浏览该文件的文档.
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <cstddef>
6#include <cstdint>
7#include <mutex>
8#include <vector>
9
10#include <glm/glm.hpp>
11
12namespace eve::graphics {
13
14namespace cubemap_prefilter_detail {
15
17inline glm::vec3 faceDirection(uint32_t face, float u, float v) {
18 switch (face) {
20 case 0: return glm::normalize(glm::vec3(1.f, -v, -u));
22 case 1: return glm::normalize(glm::vec3(-1.f, -v, u));
24 case 2: return glm::normalize(glm::vec3(u, 1.f, v));
26 case 3: return glm::normalize(glm::vec3(u, -1.f, -v));
28 case 4: return glm::normalize(glm::vec3(u, -v, 1.f));
30 default: return glm::normalize(glm::vec3(-u, -v, -1.f));
31 }
32}
33
35inline void directionFaceUv(const glm::vec3 &d, uint32_t &face, float &u, float &v) {
36 const glm::vec3 a = glm::abs(d);
37 if (a.x >= a.y && a.x >= a.z) {
38 if (d.x >= 0.f) {
39 face = 0;
40 u = -d.z / a.x;
41 v = -d.y / a.x;
42 } else {
43 face = 1;
44 u = d.z / a.x;
45 v = -d.y / a.x;
46 }
47 } else if (a.y >= a.z) {
48 if (d.y >= 0.f) {
49 face = 2;
50 u = d.x / a.y;
51 v = d.z / a.y;
52 } else {
53 face = 3;
54 u = d.x / a.y;
55 v = -d.z / a.y;
56 }
57 } else if (d.z >= 0.f) {
58 face = 4;
59 u = d.x / a.z;
60 v = -d.y / a.z;
61 } else {
62 face = 5;
63 u = -d.x / a.z;
64 v = -d.y / a.z;
65 }
66}
67
69inline glm::vec4 sampleBase(const uint8_t *faces, uint32_t size, const glm::vec3 &direction) {
70 uint32_t face = 0;
71 float u = 0.f;
72 float v = 0.f;
75 const float x = (u * 0.5f + 0.5f) * float(size) - 0.5f;
76 const float y = (v * 0.5f + 0.5f) * float(size) - 0.5f;
77 const int x0 = int(std::floor(x));
78 const int y0 = int(std::floor(y));
79 const int x1 = x0 + 1;
80 const int y1 = y0 + 1;
81 const float tx = x - float(x0);
82 const float ty = y - float(y0);
83 auto load = [&](int px, int py) {
84 const float tapU = (2.f * (float(px) + 0.5f) / float(size)) - 1.f;
85 const float tapV = (2.f * (float(py) + 0.5f) / float(size)) - 1.f;
86 const glm::vec3 tapDirection = faceDirection(face, tapU, tapV);
87 uint32_t tapFace = 0;
88 float resolvedU = 0.f;
89 float resolvedV = 0.f;
91 directionFaceUv(tapDirection, tapFace, resolvedU, resolvedV);
92 const uint32_t resolvedX = uint32_t(std::clamp(
94 int((resolvedU * 0.5f + 0.5f) * float(size)), 0, int(size) - 1));
95 const uint32_t resolvedY = uint32_t(std::clamp(
97 int((resolvedV * 0.5f + 0.5f) * float(size)), 0, int(size) - 1));
98 const size_t faceBase = size_t(tapFace) * size_t(size) * size_t(size) * 4u;
99 const size_t i = faceBase + (size_t(resolvedY) * size + resolvedX) * 4u;
101 return glm::vec4(faces[i], faces[i + 1u], faces[i + 2u], faces[i + 3u]);
102 };
104 return glm::mix(glm::mix(load(x0, y0), load(x1, y0), tx),
106 glm::mix(load(x0, y1), load(x1, y1), tx), ty);
107}
108
110inline float radicalInverse(uint32_t bits) {
111 bits = (bits << 16u) | (bits >> 16u);
112 bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
113 bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
114 bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
115 bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
117 return float(bits) * 2.3283064365386963e-10f;
118}
119
121inline glm::vec3 importanceSample(float x, float y, const glm::vec3 &n, float roughness) {
122 constexpr float kTau = 6.2831853071795864769f;
123 const float a = std::max(roughness * roughness, 0.001f);
124 const float a2 = a * a;
125 const float phi = kTau * x;
126 const float cosTheta = std::sqrt((1.f - y) / std::max(1.f + (a2 - 1.f) * y, 1e-6f));
127 const float sinTheta = std::sqrt(std::max(1.f - cosTheta * cosTheta, 0.f));
129 const glm::vec3 hT(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
130 const glm::vec3 up = std::abs(n.z) < 0.999f ? glm::vec3(0.f, 0.f, 1.f)
132 : glm::vec3(1.f, 0.f, 0.f);
133 const glm::vec3 tangent = glm::normalize(glm::cross(up, n));
134 const glm::vec3 bitangent = glm::cross(n, tangent);
136 return glm::normalize(tangent * hT.x + bitangent * hT.y + n * hT.z);
137}
138
140inline glm::vec3 cosineSample(float x, float y, const glm::vec3 &n) {
141 constexpr float kTau = 6.2831853071795864769f;
142 const float phi = kTau * x;
143 const float radius = std::sqrt(y);
145 const glm::vec3 local(radius * std::cos(phi), radius * std::sin(phi),
147 std::sqrt(std::max(1.f - y, 0.f)));
148 const glm::vec3 up = std::abs(n.z) < 0.999f ? glm::vec3(0.f, 0.f, 1.f)
150 : glm::vec3(1.f, 0.f, 0.f);
151 const glm::vec3 tangent = glm::normalize(glm::cross(up, n));
152 const glm::vec3 bitangent = glm::cross(n, tangent);
154 return glm::normalize(tangent * local.x + bitangent * local.y + n * local.z);
155}
156
157} // namespace cubemap_prefilter_detail
158
167inline std::vector<uint8_t> buildGgxCubemapMipChain(const uint8_t *rgbaFaces, uint32_t faceSize,
168 uint32_t mipLevels,
169 uint32_t sampleCount = 32u) {
170 if (!rgbaFaces || faceSize == 0u || mipLevels == 0u) return {};
171 sampleCount = std::max(sampleCount, 1u);
173 struct CacheEntry {
174 uint64_t key = 0;
175 uint32_t faceSize = 0;
176 uint32_t mipLevels = 0;
177 uint32_t sampleCount = 0;
178 std::vector<uint8_t> bytes;
179 };
180 static std::mutex cacheMutex;
181 static std::vector<CacheEntry> cache;
182 const size_t sourceBytes = size_t(faceSize) * faceSize * 4u * 6u;
183 uint64_t key = 1469598103934665603ull;
184 for (size_t i = 0; i < sourceBytes; ++i) {
185 key ^= uint64_t(rgbaFaces[i]);
186 key *= 1099511628211ull;
187 }
188 key ^= uint64_t(faceSize) | (uint64_t(mipLevels) << 32u);
189 key *= 1099511628211ull;
190 key ^= uint64_t(sampleCount);
191 if (mipLevels > 1u) {
193 std::lock_guard<std::mutex> lock(cacheMutex);
194 for (const CacheEntry &entry : cache) {
195 if (entry.key == key && entry.faceSize == faceSize &&
196 entry.mipLevels == mipLevels && entry.sampleCount == sampleCount)
197 return entry.bytes;
198 }
199 }
200
201 std::vector<uint8_t> packed;
202 size_t totalBytes = 0;
203 for (uint32_t level = 0, size = faceSize; level < mipLevels;
204 ++level, size = std::max(size >> 1u, 1u))
205 totalBytes += size_t(size) * size * 4u * 6u;
206 packed.reserve(totalBytes);
207 packed.insert(packed.end(), rgbaFaces, rgbaFaces + sourceBytes);
208 if (mipLevels <= 1u) return packed;
209
210 for (uint32_t level = 1, size = std::max(faceSize >> 1u, 1u); level < mipLevels;
211 ++level, size = std::max(size >> 1u, 1u)) {
212 const float roughness = float(level) / float(mipLevels - 1u);
213 const bool diffuseIrradiance = mipLevels >= 3u && level + 1u == mipLevels;
214 const uint32_t levelSamples =
215 diffuseIrradiance
216 ? std::max(64u, sampleCount * 2u)
218 : std::max(8u, uint32_t(std::ceil(float(sampleCount) * roughness)));
219 for (uint32_t face = 0; face < 6u; ++face) {
220 for (uint32_t y = 0; y < size; ++y) {
221 for (uint32_t x = 0; x < size; ++x) {
222 const float u = (2.f * (float(x) + 0.5f) / float(size)) - 1.f;
223 const float v = (2.f * (float(y) + 0.5f) / float(size)) - 1.f;
224 const glm::vec3 n = cubemap_prefilter_detail::faceDirection(face, u, v);
226 glm::vec4 sum(0.f);
227 float weight = 0.f;
228 for (uint32_t i = 0; i < levelSamples; ++i) {
229 const float xiX = (float(i) + 0.5f) / float(levelSamples);
230 const float xiY = cubemap_prefilter_detail::radicalInverse(i);
231 if (diffuseIrradiance) {
232 const glm::vec3 l =
235 sum += cubemap_prefilter_detail::sampleBase(rgbaFaces, faceSize, l);
236 weight += 1.f;
237 continue;
238 }
240 xiX, xiY, n, roughness);
241 const glm::vec3 l = glm::normalize(2.f * glm::dot(n, h) * h - n);
242 const float noL = std::max(glm::dot(n, l), 0.f);
243 if (noL <= 0.f) continue;
244 sum += cubemap_prefilter_detail::sampleBase(rgbaFaces, faceSize, l) * noL;
245 weight += noL;
246 }
247 const glm::vec4 c = sum / std::max(weight, 1e-6f);
248 for (int channel = 0; channel < 4; ++channel)
249 packed.push_back(static_cast<uint8_t>(
251 std::clamp(std::lround(c[channel]), 0l, 255l)));
252 }
253 }
254 }
255 }
256 {
258 std::lock_guard<std::mutex> lock(cacheMutex);
259 if (cache.size() >= 2u) cache.erase(cache.begin());
260 cache.push_back(CacheEntry{key, faceSize, mipLevels, sampleCount, packed});
261 }
262 return packed;
263}
264
265} // namespace eve::graphics
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
Vec3 tangent
Definition CaveMesh.cpp:80
float py
std::uint32_t key
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
float v
HexVec3 up
std::int32_t c
int h
std::vector< Colorf > px
std::string local
std::uint64_t bytes
MeleePoint3 a
Definition MeleeHit.cpp:40
float roughness
int level
float radius
float d
RoadLaneDirection direction
float size
Definition TreeMesh.cpp:156
std::vector< double > phi
glm::vec3 importanceSample(float x, float y, const glm::vec3 &n, float roughness)
Importance sample.
glm::vec4 sampleBase(const uint8_t *faces, uint32_t size, const glm::vec3 &direction)
Sample base.
void directionFaceUv(const glm::vec3 &d, uint32_t &face, float &u, float &v)
Direction face uv.
float radicalInverse(uint32_t bits)
Radical inverse.
glm::vec3 cosineSample(float x, float y, const glm::vec3 &n)
Cosine sample.
glm::vec3 faceDirection(uint32_t face, float u, float v)
Face direction.
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
std::vector< uint8_t > buildGgxCubemapMipChain(const uint8_t *rgbaFaces, uint32_t faceSize, uint32_t mipLevels, uint32_t sampleCount=32u)
Build a mip-major RGBA8 IBL chain: GGX specular mips plus final diffuse irradiance.