7#include <glm/gtc/matrix_transform.hpp>
13glm::mat4 cascadeVP(
const glm::vec3 &lightDir,
const glm::vec3 &
eye,
const glm::vec3 &target,
14 const glm::vec3 &up,
float fovYRad,
float aspect,
float nearZ,
float farZ,
15 float *texelWorldOut,
float *zRangeOut) {
16 const glm::mat4
view = glm::lookAtRH(
eye, target, up);
19 const glm::mat4 inv = glm::inverse(
proj *
view);
24 for (
int z = 0;
z < 2; ++
z)
25 for (
int y = 0;
y < 2; ++
y)
26 for (
int x = 0;
x < 2; ++
x) {
27 const glm::vec4 ndc(
x ? 1.f : -1.f,
y ? 1.f : -1.f,
z ? 1.f : 0.f, 1.f);
28 glm::vec4
w = inv * ndc;
30 corners[
idx++] = glm::vec3(
w);
33 glm::vec3 worldCenter(0.f);
34 for (
int i = 0; i < 8; ++i) worldCenter += corners[i];
35 worldCenter *= 0.125f;
37 for (
int i = 0; i < 8; ++i)
38 radius = std::max(radius, glm::length(corners[i] - worldCenter));
40 glm::vec3 L = glm::normalize(lightDir);
41 if (glm::length(L) < 1e-6f) L = glm::vec3(0.f, 1.f, 0.f);
42 glm::vec3 lightUp(0.f, 1.f, 0.f);
43 if (std::abs(glm::dot(L, lightUp)) > 0.95f) lightUp = glm::vec3(0.f, 0.f, 1.f);
47 const glm::mat4 lightView = glm::lookAtRH(L * 50.f, glm::vec3(0.f), lightUp);
49 glm::vec3 minB(1e9f), maxB(-1e9f);
50 for (
int i = 0; i < 8; ++i) {
51 const glm::vec3 lp = glm::vec3(lightView * glm::vec4(corners[i], 1.f));
52 minB = glm::min(minB, lp);
53 maxB = glm::max(maxB, lp);
55 glm::vec3 centerLS = glm::vec3(lightView * glm::vec4(worldCenter, 1.f));
57 const float zPad = (maxB.z - minB.z) * 0.1f + 0.5f;
63 if (zRangeOut) *zRangeOut = std::max(-minB.z - (-maxB.z), 1e-3f);
68 const float texelWorld = (2.f * radius) / mapSize;
69 if (texelWorldOut) *texelWorldOut = texelWorld;
71 auto snap = [&](
float v) {
return std::floor(
v / texelWorld) * texelWorld; };
72 centerLS.x = snap(centerLS.x);
73 centerLS.y = snap(centerLS.y);
75 const glm::mat4 lightProj =
76 orthoVulkanRH_ZO(centerLS.x - radius, centerLS.x + radius, centerLS.y - radius,
77 centerLS.y + radius, -maxB.z, -minB.z);
78 return lightProj * lightView;
84 const glm::vec3 &target,
const glm::vec3 &up,
float fovYRad,
85 float aspect,
float nearZ,
float farZ,
float bias,
float strength) {
88 const float n = std::max(nearZ, 1e-3f);
89 const float fCam = std::max(farZ,
n + 1e-2f);
91 const float ratio =
f /
n;
96 const float logS =
n * std::pow(ratio,
p);
97 const float uniS =
n + (
f -
n) *
p;
104 float texelWorld = 0.f;
107 cascadeVP(lightDirTowardSurface,
eye, target, up, fovYRad, aspect, splits[i],
108 splits[i + 1], &texelWorld, &zRange);
112 const float autoNdc = std::clamp((0.6f * texelWorld) / zRange, 1e-5f, 0.01f);
113 cascadeNdc[i] = std::max(std::max(0.f, bias), autoNdc);
114 cascadeTexel[i] = texelWorld;
116 out.ubo.splits = glm::vec4(splits[1], splits[2], splits[3], std::max(0.f, strength));
117 out.ubo.cascadeBias = glm::vec4(cascadeNdc[0], cascadeNdc[1], cascadeNdc[2], 0.f);
118 out.ubo.cascadeTexel = glm::vec4(cascadeTexel[0], cascadeTexel[1], cascadeTexel[2], 0.f);
119 out.ubo.bias = glm::vec4(cascadeNdc[0], 1.f, 1.f, 0.f);
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
glm::mat4 perspectiveVulkanRH_ZO(float fovyRad, float aspect, float zNear, float zFar)
Right-handed, zero-to-one depth perspective for Vulkan swapchains.
ShadowUpload buildDirectionalCSM(const glm::vec3 &lightDirTowardSurface, const glm::vec3 &eye, const glm::vec3 &target, const glm::vec3 &up, float fovYRad, float aspect, float nearZ, float farZ, float bias, float strength)
Build 3 cascade light view-proj matrices for a directional light.
glm::mat4 orthoVulkanRH_ZO(float left, float right, float bottom, float top, float zNear, float zFar)
static constexpr float kSplitLambda
static constexpr int kMapSize
static constexpr float kMaxDistance
static constexpr int kCascades