mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-24 00:00:13 +00:00
Add shader-driven tree beautification: wind sway, SSS, color variation, AO
- Vertex wind animation: 3-layer displacement (trunk/branch/leaf) with quadratic height scaling so bases stay grounded - Shadow pass: matching vertex displacement split into foliage/non-foliage passes, removed UV-wiggle approach - Leaf subsurface scattering: warm backlit glow when looking toward sun - Per-instance color variation: hue/brightness from position hash via flat varying to avoid interpolation flicker - Canopy ambient occlusion: height-based darkening of tree interiors - Detail normal perturbation: UV-only procedural normals to break flat cards - Bayer 4x4 ordered dither replacing sin-hash noise for alpha edges - Foliage skips shadow map sampling and specular to prevent flicker from swaying geometry sampling unstable shadow/highlight values
This commit is contained in:
parent
4511de8d38
commit
ef1e5abe8e
11 changed files with 199 additions and 69 deletions
|
|
@ -401,7 +401,7 @@ bool M2Renderer::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout
|
|||
VkPushConstantRange pushRange{};
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
pushRange.offset = 0;
|
||||
pushRange.size = 80; // mat4(64) + vec2(8) + int(4) + int(4)
|
||||
pushRange.size = 84; // mat4(64) + vec2(8) + int(4) + int(4) + int(4)
|
||||
|
||||
VkPipelineLayoutCreateInfo ci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
ci.setLayoutCount = 3;
|
||||
|
|
@ -2109,6 +2109,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
glm::vec2 uvOffset;
|
||||
int texCoordSet;
|
||||
int useBones;
|
||||
int isFoliage;
|
||||
};
|
||||
|
||||
// Bind per-frame descriptor set (set 0) — shared across all draws
|
||||
|
|
@ -2382,6 +2383,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
pc.uvOffset = uvOffset;
|
||||
pc.texCoordSet = static_cast<int>(batch.textureUnit);
|
||||
pc.useBones = useBones ? 1 : 0;
|
||||
pc.isFoliage = model.shadowWindFoliage ? 1 : 0;
|
||||
vkCmdPushConstants(cmd, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pc), &pc);
|
||||
|
||||
vkCmdDrawIndexed(cmd, batch.indexCount, 1, batch.indexStart, 0, 0);
|
||||
|
|
@ -2625,46 +2627,72 @@ bool M2Renderer::initializeShadow(VkRenderPass shadowRenderPass) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void M2Renderer::renderShadow(VkCommandBuffer cmd, const glm::mat4& lightSpaceMatrix) {
|
||||
void M2Renderer::renderShadow(VkCommandBuffer cmd, const glm::mat4& lightSpaceMatrix, float globalTime) {
|
||||
if (!shadowPipeline_ || !shadowParamsSet_) return;
|
||||
if (instances.empty() || models.empty()) return;
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, shadowPipeline_);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, shadowPipelineLayout_,
|
||||
0, 1, &shadowParamsSet_, 0, nullptr);
|
||||
|
||||
struct ShadowPush { glm::mat4 lightSpaceMatrix; glm::mat4 model; };
|
||||
|
||||
uint32_t currentModelId = UINT32_MAX;
|
||||
const M2ModelGPU* currentModel = nullptr;
|
||||
// Helper lambda to draw instances with a given foliageSway setting
|
||||
auto drawPass = [&](bool foliagePass) {
|
||||
// Update ShadowParams UBO for this pass
|
||||
struct ShadowParamsUBO {
|
||||
int32_t useBones = 0;
|
||||
int32_t useTexture = 0;
|
||||
int32_t alphaTest = 0;
|
||||
int32_t foliageSway = 0;
|
||||
float windTime = 0.0f;
|
||||
float foliageMotionDamp = 1.0f;
|
||||
};
|
||||
ShadowParamsUBO params{};
|
||||
params.foliageSway = foliagePass ? 1 : 0;
|
||||
params.windTime = globalTime;
|
||||
params.foliageMotionDamp = 1.0f;
|
||||
|
||||
for (const auto& instance : instances) {
|
||||
auto modelIt = models.find(instance.modelId);
|
||||
if (modelIt == models.end()) continue;
|
||||
const M2ModelGPU& model = modelIt->second;
|
||||
if (!model.isValid() || model.isSmoke || model.isInvisibleTrap) continue;
|
||||
VmaAllocationInfo allocInfo{};
|
||||
vmaGetAllocationInfo(vkCtx_->getAllocator(), shadowParamsAlloc_, &allocInfo);
|
||||
std::memcpy(allocInfo.pMappedData, ¶ms, sizeof(params));
|
||||
|
||||
// Bind vertex/index buffers when model changes
|
||||
if (instance.modelId != currentModelId) {
|
||||
currentModelId = instance.modelId;
|
||||
currentModel = &model;
|
||||
VkDeviceSize offset = 0;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, ¤tModel->vertexBuffer, &offset);
|
||||
vkCmdBindIndexBuffer(cmd, currentModel->indexBuffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, shadowPipeline_);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, shadowPipelineLayout_,
|
||||
0, 1, &shadowParamsSet_, 0, nullptr);
|
||||
|
||||
uint32_t currentModelId = UINT32_MAX;
|
||||
const M2ModelGPU* currentModel = nullptr;
|
||||
|
||||
for (const auto& instance : instances) {
|
||||
auto modelIt = models.find(instance.modelId);
|
||||
if (modelIt == models.end()) continue;
|
||||
const M2ModelGPU& model = modelIt->second;
|
||||
if (!model.isValid() || model.isSmoke || model.isInvisibleTrap) continue;
|
||||
|
||||
// Filter: only draw foliage models in foliage pass, non-foliage in non-foliage pass
|
||||
if (model.shadowWindFoliage != foliagePass) continue;
|
||||
|
||||
// Bind vertex/index buffers when model changes
|
||||
if (instance.modelId != currentModelId) {
|
||||
currentModelId = instance.modelId;
|
||||
currentModel = &model;
|
||||
VkDeviceSize offset = 0;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, ¤tModel->vertexBuffer, &offset);
|
||||
vkCmdBindIndexBuffer(cmd, currentModel->indexBuffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
|
||||
ShadowPush push{lightSpaceMatrix, instance.modelMatrix};
|
||||
vkCmdPushConstants(cmd, shadowPipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT,
|
||||
0, 128, &push);
|
||||
|
||||
for (const auto& batch : model.batches) {
|
||||
if (batch.submeshLevel > 0) continue;
|
||||
vkCmdDrawIndexed(cmd, batch.indexCount, 1, batch.indexStart, 0, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ShadowPush push{lightSpaceMatrix, instance.modelMatrix};
|
||||
vkCmdPushConstants(cmd, shadowPipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT,
|
||||
0, 128, &push);
|
||||
|
||||
// Draw all batches in shadow pass.
|
||||
// Blend-mode filtering was excluding many valid world casters after
|
||||
// Vulkan material path changes (trees/buildings losing shadows).
|
||||
for (const auto& batch : model.batches) {
|
||||
if (batch.submeshLevel > 0) continue; // skip LOD submeshes
|
||||
vkCmdDrawIndexed(cmd, batch.indexCount, 1, batch.indexStart, 0, 0);
|
||||
}
|
||||
}
|
||||
// Pass 1: non-foliage (no wind displacement)
|
||||
drawPass(false);
|
||||
// Pass 2: foliage (wind displacement enabled)
|
||||
drawPass(true);
|
||||
}
|
||||
|
||||
// --- M2 Particle Emitter Helpers ---
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue