mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-08-02 19:10:49 -04:00
fix(character): render ghost opacity through blended pipeline
Route faded character instances through a dedicated translucent character pipeline so dead and ghost characters fade as a whole model instead of only showing translucent hair. Opaque body batches previously wrote the shader alpha from instance opacity, but the opaque pipeline had blending disabled, so the body stayed fully solid. Hair went through the alpha-test path, where reduced alpha affected coverage, making only hair appear faded. Add a translucent pipeline with alpha blending and depth writes enabled. This keeps normal self-occlusion for faded characters while allowing the whole model to respect instance opacity. Reuse the existing per-batch alpha-test flag so cutout materials such as hair keep crisp edges while fading with the rest of the body. Also make the whole-model fallback path bind an explicit opaque or translucent pipeline instead of inheriting the previously bound pipeline. Create, recreate, validate, and destroy the new pipeline alongside the other character pipelines. Build passes.
This commit is contained in:
@@ -317,6 +317,10 @@ private:
|
||||
VkPipeline alphaTestPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline alphaPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline additivePipeline_ = VK_NULL_HANDLE;
|
||||
// Whole-instance fades (ghost form, spawn fade-in): alpha blend with depth
|
||||
// write kept on, so the faded model still self-occludes instead of showing
|
||||
// backfaces and under-armor skin through the body.
|
||||
VkPipeline translucentPipeline_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor set layouts
|
||||
|
||||
@@ -402,6 +402,7 @@ bool CharacterRenderer::initialize(VkContext* ctx, VkDescriptorSetLayout perFram
|
||||
alphaTestPipeline_ = buildCharPipeline(PipelineBuilder::blendDisabled(), true, true);
|
||||
alphaPipeline_ = buildCharPipeline(PipelineBuilder::blendAlpha(), false);
|
||||
additivePipeline_ = buildCharPipeline(PipelineBuilder::blendAdditive(), false);
|
||||
translucentPipeline_ = buildCharPipeline(PipelineBuilder::blendAlpha(), true);
|
||||
|
||||
// Clean up shader modules
|
||||
charVert.destroy();
|
||||
@@ -473,6 +474,7 @@ void CharacterRenderer::shutdown() {
|
||||
destroyPipeline(alphaTestPipeline_);
|
||||
destroyPipeline(alphaPipeline_);
|
||||
destroyPipeline(additivePipeline_);
|
||||
destroyPipeline(translucentPipeline_);
|
||||
|
||||
if (pipelineLayout_) { vkDestroyPipelineLayout(device, pipelineLayout_, nullptr); pipelineLayout_ = VK_NULL_HANDLE; }
|
||||
|
||||
@@ -2722,6 +2724,14 @@ void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
// declare Mod/alpha blending, which would composite that black
|
||||
// background as an opaque quad — force additive so only the light adds.
|
||||
desiredPipeline = additivePipeline_;
|
||||
} else if (instance.opacity < 0.999f) {
|
||||
// Whole-instance fade (ghost form, spawn fade-in): the opaque and
|
||||
// alpha-test pipelines have blending disabled, so the shader's
|
||||
// texColor.a * opacity output is discarded and only hair (via
|
||||
// alpha-to-coverage) ever looked translucent. Route every batch
|
||||
// through the blend pipeline; the per-batch alphaTest UBO flag
|
||||
// still handles cutout materials in the shader.
|
||||
desiredPipeline = translucentPipeline_;
|
||||
} else if (hairMaterial) {
|
||||
desiredPipeline = alphaTestPipeline_;
|
||||
} else {
|
||||
@@ -2849,6 +2859,15 @@ void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
if (pomQuality_ == 0) pomSamples2 = 16;
|
||||
else if (pomQuality_ == 2) pomSamples2 = 64;
|
||||
|
||||
// Whole-model fallback inherits whatever pipeline was bound last;
|
||||
// pick it explicitly so instance fades blend here too.
|
||||
VkPipeline fallbackPipeline = (instance.opacity < 0.999f)
|
||||
? translucentPipeline_ : opaquePipeline_;
|
||||
if (fallbackPipeline != currentPipeline) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, fallbackPipeline);
|
||||
currentPipeline = fallbackPipeline;
|
||||
}
|
||||
|
||||
CharMaterialUBO matData{};
|
||||
matData.opacity = instance.opacity;
|
||||
matData.alphaTest = 0;
|
||||
@@ -3774,6 +3793,7 @@ void CharacterRenderer::recreatePipelines() {
|
||||
if (alphaTestPipeline_) { vkDestroyPipeline(device, alphaTestPipeline_, nullptr); alphaTestPipeline_ = VK_NULL_HANDLE; }
|
||||
if (alphaPipeline_) { vkDestroyPipeline(device, alphaPipeline_, nullptr); alphaPipeline_ = VK_NULL_HANDLE; }
|
||||
if (additivePipeline_) { vkDestroyPipeline(device, additivePipeline_, nullptr); additivePipeline_ = VK_NULL_HANDLE; }
|
||||
if (translucentPipeline_) { vkDestroyPipeline(device, translucentPipeline_, nullptr); translucentPipeline_ = VK_NULL_HANDLE; }
|
||||
|
||||
// --- Load shaders ---
|
||||
rendering::VkShaderModule charVert, charFrag;
|
||||
@@ -3830,11 +3850,13 @@ void CharacterRenderer::recreatePipelines() {
|
||||
alphaTestPipeline_ = buildCharPipeline(PipelineBuilder::blendDisabled(), true, true);
|
||||
alphaPipeline_ = buildCharPipeline(PipelineBuilder::blendAlpha(), false);
|
||||
additivePipeline_ = buildCharPipeline(PipelineBuilder::blendAdditive(), false);
|
||||
translucentPipeline_ = buildCharPipeline(PipelineBuilder::blendAlpha(), true);
|
||||
|
||||
charVert.destroy();
|
||||
charFrag.destroy();
|
||||
|
||||
if (!opaquePipeline_ || !alphaTestPipeline_ || !alphaPipeline_ || !additivePipeline_) {
|
||||
if (!opaquePipeline_ || !alphaTestPipeline_ || !alphaPipeline_ || !additivePipeline_ ||
|
||||
!translucentPipeline_) {
|
||||
LOG_ERROR("CharacterRenderer::recreatePipelines FAILED: opaque=", (void*)opaquePipeline_,
|
||||
" alphaTest=", (void*)alphaTestPipeline_,
|
||||
" alpha=", (void*)alphaPipeline_,
|
||||
|
||||
Reference in New Issue
Block a user