| 1286 | } |
| 1287 | |
| 1288 | [[nodiscard]] float sampleShadow(const SoftwareShadowMap& shadow, |
| 1289 | const glm::vec3& world_position, |
| 1290 | const float bias = 0.0025f) { |
| 1291 | if (!shadow.active || shadow.size <= 0 || shadow.depth.empty()) { |
| 1292 | return 1.0f; |
| 1293 | } |
| 1294 | |
| 1295 | const glm::vec4 clip = shadow.light_vp * glm::vec4(world_position, 1.0f); |
| 1296 | if (std::abs(clip.w) <= 1.0e-6f) { |
| 1297 | return 1.0f; |
| 1298 | } |
| 1299 | glm::vec3 proj = glm::vec3(clip) / clip.w; |
| 1300 | proj = proj * 0.5f + 0.5f; |
| 1301 | if (proj.z < 0.0f || proj.z > 1.0f || |
| 1302 | proj.x < 0.0f || proj.x > 1.0f || |
| 1303 | proj.y < 0.0f || proj.y > 1.0f) { |
| 1304 | return 1.0f; |
| 1305 | } |
| 1306 | |
| 1307 | const float sx = proj.x * static_cast<float>(shadow.size - 1); |
| 1308 | const float sy = proj.y * static_cast<float>(shadow.size - 1); |
| 1309 | const int ix = static_cast<int>(std::round(sx)); |
| 1310 | const int iy = static_cast<int>(std::round(sy)); |
| 1311 | float lit = 0.0f; |
| 1312 | int samples = 0; |
| 1313 | for (int dy = -1; dy <= 1; ++dy) { |
| 1314 | for (int dx = -1; dx <= 1; ++dx) { |
| 1315 | const int x = ix + dx; |
| 1316 | const int y = iy + dy; |
| 1317 | if (x < 0 || y < 0 || x >= shadow.size || y >= shadow.size) { |
| 1318 | lit += 1.0f; |
| 1319 | } else { |
| 1320 | const float depth = shadow.depth[static_cast<size_t>(y) * shadow.size + x]; |
| 1321 | lit += (proj.z - bias <= depth) ? 1.0f : 0.0f; |
| 1322 | } |
| 1323 | ++samples; |
| 1324 | } |
| 1325 | } |
| 1326 | return samples > 0 ? lit / static_cast<float>(samples) : 1.0f; |
| 1327 | } |
| 1328 | |
| 1329 | [[nodiscard]] glm::vec3 shadeMeshPixel(const lfs::core::MeshData& mesh, |
| 1330 | const size_t material_index, |
no test coverage detected