mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
Add water refraction toggle with per-frame scene history
Fix VK_ERROR_DEVICE_LOST crash by allocating per-frame scene history images (color + depth) instead of a single shared image that raced between frames in flight. Water refraction can now be toggled via Settings > Video > Water Refraction. Without refraction: richer blue base colors, animated caustic shimmer, and normal-based color shifts give the water visible life. With refraction: clean screen-space refraction with Beer-Lambert absorption. Disabling clears scene history to black for immediate fallback.
This commit is contained in:
parent
7630c7aec7
commit
5a227c0376
8 changed files with 323 additions and 191 deletions
|
|
@ -226,11 +226,32 @@ void main() {
|
|||
float depthFade = 1.0 - exp(-verticalDepth * 0.15);
|
||||
vec3 waterBody = mix(shallowColor, deepColor, depthFade);
|
||||
|
||||
vec3 refractedColor = mix(foggedScene * absorbed, waterBody, depthFade * 0.7);
|
||||
// Detect if scene history is available (scene data captured for refraction)
|
||||
float sceneBrightness = dot(sceneRefract, vec3(0.299, 0.587, 0.114));
|
||||
bool hasSceneData = (sceneBrightness > 0.003);
|
||||
|
||||
if (verticalDepth < 0.01) {
|
||||
float opticalDepth = 1.0 - exp(-dist * 0.004);
|
||||
refractedColor = mix(foggedScene, waterBody, opticalDepth * 0.6);
|
||||
// Animated caustic shimmer — only without refraction (refraction already provides movement)
|
||||
if (!hasSceneData) {
|
||||
float caustic1 = noiseValue(FragPos.xy * 1.8 + time * vec2(0.3, 0.15));
|
||||
float caustic2 = noiseValue(FragPos.xy * 3.2 - time * vec2(0.2, 0.35));
|
||||
float causticPattern = caustic1 * 0.6 + caustic2 * 0.4;
|
||||
vec3 causticTint = vec3(0.08, 0.18, 0.28) * smoothstep(0.35, 0.75, causticPattern);
|
||||
waterBody += causticTint;
|
||||
}
|
||||
|
||||
vec3 refractedColor;
|
||||
if (hasSceneData) {
|
||||
refractedColor = mix(foggedScene * absorbed, waterBody, depthFade * 0.7);
|
||||
if (verticalDepth < 0.01) {
|
||||
float opticalDepth = 1.0 - exp(-dist * 0.004);
|
||||
refractedColor = mix(foggedScene, waterBody, opticalDepth * 0.6);
|
||||
}
|
||||
} else {
|
||||
// No refraction data — use lit water body with animated variation
|
||||
vec3 litWater = waterBody * (ambientColor.rgb * 0.8 + NdotL * lightColor.rgb * 0.6);
|
||||
float normalShift = dot(detailNorm.xy, vec2(0.5, 0.5));
|
||||
litWater += vec3(0.02, 0.06, 0.10) * normalShift;
|
||||
refractedColor = litWater;
|
||||
}
|
||||
|
||||
vec3 litBase = waterBody * (ambientColor.rgb * 0.7 + NdotL * lightColor.rgb * 0.5);
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -256,6 +256,9 @@ public:
|
|||
bool areShadowsEnabled() const { return shadowsEnabled; }
|
||||
void setMsaaSamples(VkSampleCountFlagBits samples);
|
||||
|
||||
void setWaterRefractionEnabled(bool enabled);
|
||||
bool isWaterRefractionEnabled() const;
|
||||
|
||||
private:
|
||||
void applyMsaaChange();
|
||||
VkSampleCountFlagBits pendingMsaaSamples_ = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
|
|
|||
|
|
@ -93,12 +93,13 @@ public:
|
|||
bool hasWater1xPass() const { return water1xRenderPass != VK_NULL_HANDLE; }
|
||||
VkRenderPass getWater1xRenderPass() const { return water1xRenderPass; }
|
||||
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, float time, bool use1x = false);
|
||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, float time, bool use1x = false, uint32_t frameIndex = 0);
|
||||
void captureSceneHistory(VkCommandBuffer cmd,
|
||||
VkImage srcColorImage,
|
||||
VkImage srcDepthImage,
|
||||
VkExtent2D srcExtent,
|
||||
bool srcDepthIsMsaa);
|
||||
bool srcDepthIsMsaa,
|
||||
uint32_t frameIndex = 0);
|
||||
|
||||
// --- Planar reflection pass ---
|
||||
// Call sequence: beginReflectionPass → [render scene] → endReflectionPass
|
||||
|
|
@ -124,6 +125,9 @@ public:
|
|||
void setEnabled(bool enabled) { renderingEnabled = enabled; }
|
||||
bool isEnabled() const { return renderingEnabled; }
|
||||
|
||||
void setRefractionEnabled(bool enabled);
|
||||
bool isRefractionEnabled() const { return refractionEnabled; }
|
||||
|
||||
std::optional<float> getWaterHeightAt(float glX, float glY) const;
|
||||
/// Like getWaterHeightAt but only returns water surfaces whose height is
|
||||
/// close to the query Z (within maxAbove units above). Avoids false
|
||||
|
|
@ -159,17 +163,22 @@ private:
|
|||
VkDescriptorPool materialDescPool = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout sceneSetLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorPool sceneDescPool = VK_NULL_HANDLE;
|
||||
VkDescriptorSet sceneSet = VK_NULL_HANDLE;
|
||||
static constexpr uint32_t MAX_WATER_SETS = 16384;
|
||||
|
||||
VkSampler sceneColorSampler = VK_NULL_HANDLE;
|
||||
VkSampler sceneDepthSampler = VK_NULL_HANDLE;
|
||||
VkImage sceneColorImage = VK_NULL_HANDLE;
|
||||
VmaAllocation sceneColorAlloc = VK_NULL_HANDLE;
|
||||
VkImageView sceneColorView = VK_NULL_HANDLE;
|
||||
VkImage sceneDepthImage = VK_NULL_HANDLE;
|
||||
VmaAllocation sceneDepthAlloc = VK_NULL_HANDLE;
|
||||
VkImageView sceneDepthView = VK_NULL_HANDLE;
|
||||
// Per-frame scene history to avoid race between frames in flight
|
||||
static constexpr uint32_t SCENE_HISTORY_FRAMES = 2;
|
||||
struct PerFrameSceneHistory {
|
||||
VkImage colorImage = VK_NULL_HANDLE;
|
||||
VmaAllocation colorAlloc = VK_NULL_HANDLE;
|
||||
VkImageView colorView = VK_NULL_HANDLE;
|
||||
VkImage depthImage = VK_NULL_HANDLE;
|
||||
VmaAllocation depthAlloc = VK_NULL_HANDLE;
|
||||
VkImageView depthView = VK_NULL_HANDLE;
|
||||
VkDescriptorSet sceneSet = VK_NULL_HANDLE;
|
||||
};
|
||||
PerFrameSceneHistory sceneHistory[SCENE_HISTORY_FRAMES];
|
||||
VkExtent2D sceneHistoryExtent = {0, 0};
|
||||
bool sceneHistoryReady = false;
|
||||
mutable uint32_t renderDiagCounter_ = 0;
|
||||
|
|
@ -200,6 +209,7 @@ private:
|
|||
|
||||
std::vector<WaterSurface> surfaces;
|
||||
bool renderingEnabled = true;
|
||||
bool refractionEnabled = false;
|
||||
};
|
||||
|
||||
} // namespace rendering
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ private:
|
|||
bool pendingVsync = false;
|
||||
int pendingResIndex = 0;
|
||||
bool pendingShadows = true;
|
||||
bool pendingWaterRefraction = false;
|
||||
int pendingMasterVolume = 100;
|
||||
int pendingMusicVolume = 30;
|
||||
int pendingAmbientVolume = 100;
|
||||
|
|
@ -123,6 +124,7 @@ private:
|
|||
bool minimapSettingsApplied_ = false;
|
||||
bool volumeSettingsApplied_ = false; // True once saved volume settings applied to audio managers
|
||||
bool msaaSettingsApplied_ = false; // True once saved MSAA setting applied to renderer
|
||||
bool waterRefractionApplied_ = false;
|
||||
bool normalMapSettingsApplied_ = false; // True once saved normal map/POM settings applied
|
||||
|
||||
// Mute state: mute bypasses master volume without touching slider values
|
||||
|
|
|
|||
|
|
@ -855,6 +855,14 @@ void Renderer::unregisterPreview(CharacterPreview* preview) {
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::setWaterRefractionEnabled(bool enabled) {
|
||||
if (waterRenderer) waterRenderer->setRefractionEnabled(enabled);
|
||||
}
|
||||
|
||||
bool Renderer::isWaterRefractionEnabled() const {
|
||||
return waterRenderer && waterRenderer->isRefractionEnabled();
|
||||
}
|
||||
|
||||
void Renderer::setMsaaSamples(VkSampleCountFlagBits samples) {
|
||||
if (!vkCtx) return;
|
||||
|
||||
|
|
@ -1054,20 +1062,27 @@ void Renderer::endFrame() {
|
|||
|
||||
vkCmdEndRenderPass(currentCmd);
|
||||
|
||||
// Scene-history capture is disabled: with MAX_FRAMES_IN_FLIGHT=2, the single
|
||||
// sceneColorImage can race between frame N-1's water shader read and frame N's
|
||||
// transfer write, eventually causing VK_ERROR_DEVICE_LOST. Water renders
|
||||
// without refraction until per-frame scene-history images are implemented.
|
||||
// TODO: allocate per-frame sceneColor/Depth images to fix the race.
|
||||
uint32_t frame = vkCtx->getCurrentFrame();
|
||||
|
||||
// Render water in separate 1x pass (without scene refraction for now)
|
||||
// Capture scene color/depth into per-frame history images for water refraction
|
||||
if (waterRenderer && waterRenderer->isRefractionEnabled() && waterRenderer->hasSurfaces()
|
||||
&& currentImageIndex < vkCtx->getSwapchainImages().size()) {
|
||||
waterRenderer->captureSceneHistory(
|
||||
currentCmd,
|
||||
vkCtx->getSwapchainImages()[currentImageIndex],
|
||||
vkCtx->getDepthCopySourceImage(),
|
||||
vkCtx->getSwapchainExtent(),
|
||||
vkCtx->isDepthCopySourceMsaa(),
|
||||
frame);
|
||||
}
|
||||
|
||||
// Render water in separate 1x pass after MSAA resolve + scene capture
|
||||
bool waterDeferred = waterRenderer && waterRenderer->hasSurfaces() && waterRenderer->hasWater1xPass()
|
||||
&& vkCtx->getMsaaSamples() != VK_SAMPLE_COUNT_1_BIT;
|
||||
if (waterDeferred && camera) {
|
||||
VkExtent2D ext = vkCtx->getSwapchainExtent();
|
||||
uint32_t frame = vkCtx->getCurrentFrame();
|
||||
if (waterRenderer->beginWater1xPass(currentCmd, currentImageIndex, ext)) {
|
||||
waterRenderer->render(currentCmd, perFrameDescSets[frame], *camera, globalTime, true);
|
||||
waterRenderer->render(currentCmd, perFrameDescSets[frame], *camera, globalTime, true, frame);
|
||||
waterRenderer->endWater1xPass(currentCmd);
|
||||
}
|
||||
}
|
||||
|
|
@ -3268,7 +3283,7 @@ void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) {
|
|||
bool waterDeferred = waterRenderer && waterRenderer->hasWater1xPass()
|
||||
&& vkCtx->getMsaaSamples() != VK_SAMPLE_COUNT_1_BIT;
|
||||
if (waterRenderer && camera && !waterDeferred) {
|
||||
waterRenderer->render(currentCmd, perFrameSet, *camera, globalTime);
|
||||
waterRenderer->render(currentCmd, perFrameSet, *camera, globalTime, false, vkCtx->getCurrentFrame());
|
||||
}
|
||||
|
||||
// Weather particles
|
||||
|
|
|
|||
|
|
@ -118,15 +118,15 @@ bool WaterRenderer::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLay
|
|||
return false;
|
||||
}
|
||||
|
||||
// Pool needs 3 combined image samplers + 1 uniform buffer
|
||||
// Pool needs 3 combined image samplers + 1 uniform buffer per frame
|
||||
std::array<VkDescriptorPoolSize, 2> scenePoolSizes{};
|
||||
scenePoolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
scenePoolSizes[0].descriptorCount = 3;
|
||||
scenePoolSizes[0].descriptorCount = 3 * SCENE_HISTORY_FRAMES;
|
||||
scenePoolSizes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
scenePoolSizes[1].descriptorCount = 1;
|
||||
scenePoolSizes[1].descriptorCount = SCENE_HISTORY_FRAMES;
|
||||
VkDescriptorPoolCreateInfo scenePoolInfo{};
|
||||
scenePoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
scenePoolInfo.maxSets = 1;
|
||||
scenePoolInfo.maxSets = SCENE_HISTORY_FRAMES;
|
||||
scenePoolInfo.poolSizeCount = static_cast<uint32_t>(scenePoolSizes.size());
|
||||
scenePoolInfo.pPoolSizes = scenePoolSizes.data();
|
||||
if (vkCreateDescriptorPool(device, &scenePoolInfo, nullptr, &sceneDescPool) != VK_SUCCESS) {
|
||||
|
|
@ -267,6 +267,47 @@ void WaterRenderer::recreatePipelines() {
|
|||
}
|
||||
}
|
||||
|
||||
void WaterRenderer::setRefractionEnabled(bool enabled) {
|
||||
if (refractionEnabled == enabled) return;
|
||||
refractionEnabled = enabled;
|
||||
|
||||
// When turning off, clear scene history images to black so the shader
|
||||
// detects "no data" and uses the non-refraction path.
|
||||
if (!enabled && vkCtx) {
|
||||
vkCtx->immediateSubmit([&](VkCommandBuffer cmd) {
|
||||
for (uint32_t f = 0; f < SCENE_HISTORY_FRAMES; f++) {
|
||||
auto& sh = sceneHistory[f];
|
||||
if (!sh.colorImage) continue;
|
||||
|
||||
VkImageMemoryBarrier toTransfer{};
|
||||
toTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
toTransfer.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
toTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.image = sh.colorImage;
|
||||
toTransfer.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
toTransfer.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &toTransfer);
|
||||
|
||||
VkClearColorValue clearColor = {{0.0f, 0.0f, 0.0f, 0.0f}};
|
||||
VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
vkCmdClearColorImage(cmd, sh.colorImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range);
|
||||
|
||||
VkImageMemoryBarrier toRead = toTransfer;
|
||||
toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &toRead);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void WaterRenderer::shutdown() {
|
||||
clear();
|
||||
|
||||
|
|
@ -304,13 +345,15 @@ VkDescriptorSet WaterRenderer::allocateMaterialSet() {
|
|||
void WaterRenderer::destroySceneHistoryResources() {
|
||||
if (!vkCtx) return;
|
||||
VkDevice device = vkCtx->getDevice();
|
||||
if (sceneColorView) { vkDestroyImageView(device, sceneColorView, nullptr); sceneColorView = VK_NULL_HANDLE; }
|
||||
if (sceneDepthView) { vkDestroyImageView(device, sceneDepthView, nullptr); sceneDepthView = VK_NULL_HANDLE; }
|
||||
if (sceneColorImage) { vmaDestroyImage(vkCtx->getAllocator(), sceneColorImage, sceneColorAlloc); sceneColorImage = VK_NULL_HANDLE; sceneColorAlloc = VK_NULL_HANDLE; }
|
||||
if (sceneDepthImage) { vmaDestroyImage(vkCtx->getAllocator(), sceneDepthImage, sceneDepthAlloc); sceneDepthImage = VK_NULL_HANDLE; sceneDepthAlloc = VK_NULL_HANDLE; }
|
||||
for (auto& sh : sceneHistory) {
|
||||
if (sh.colorView) { vkDestroyImageView(device, sh.colorView, nullptr); sh.colorView = VK_NULL_HANDLE; }
|
||||
if (sh.depthView) { vkDestroyImageView(device, sh.depthView, nullptr); sh.depthView = VK_NULL_HANDLE; }
|
||||
if (sh.colorImage) { vmaDestroyImage(vkCtx->getAllocator(), sh.colorImage, sh.colorAlloc); sh.colorImage = VK_NULL_HANDLE; sh.colorAlloc = VK_NULL_HANDLE; }
|
||||
if (sh.depthImage) { vmaDestroyImage(vkCtx->getAllocator(), sh.depthImage, sh.depthAlloc); sh.depthImage = VK_NULL_HANDLE; sh.depthAlloc = VK_NULL_HANDLE; }
|
||||
sh.sceneSet = VK_NULL_HANDLE;
|
||||
}
|
||||
if (sceneColorSampler) { vkDestroySampler(device, sceneColorSampler, nullptr); sceneColorSampler = VK_NULL_HANDLE; }
|
||||
if (sceneDepthSampler) { vkDestroySampler(device, sceneDepthSampler, nullptr); sceneDepthSampler = VK_NULL_HANDLE; }
|
||||
sceneSet = VK_NULL_HANDLE;
|
||||
sceneHistoryExtent = {0, 0};
|
||||
sceneHistoryReady = false;
|
||||
}
|
||||
|
|
@ -323,54 +366,7 @@ void WaterRenderer::createSceneHistoryResources(VkExtent2D extent, VkFormat colo
|
|||
vkResetDescriptorPool(device, sceneDescPool, 0);
|
||||
sceneHistoryExtent = extent;
|
||||
|
||||
VkImageCreateInfo colorImgInfo{};
|
||||
colorImgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
colorImgInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
colorImgInfo.format = colorFormat;
|
||||
colorImgInfo.extent = {extent.width, extent.height, 1};
|
||||
colorImgInfo.mipLevels = 1;
|
||||
colorImgInfo.arrayLayers = 1;
|
||||
colorImgInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorImgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
colorImgInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
colorImgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VmaAllocationCreateInfo allocCI{};
|
||||
allocCI.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
if (vmaCreateImage(vkCtx->getAllocator(), &colorImgInfo, &allocCI, &sceneColorImage, &sceneColorAlloc, nullptr) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene color history image");
|
||||
return;
|
||||
}
|
||||
|
||||
VkImageCreateInfo depthImgInfo = colorImgInfo;
|
||||
depthImgInfo.format = depthFormat;
|
||||
if (vmaCreateImage(vkCtx->getAllocator(), &depthImgInfo, &allocCI, &sceneDepthImage, &sceneDepthAlloc, nullptr) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene depth history image");
|
||||
return;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo colorViewInfo{};
|
||||
colorViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
colorViewInfo.image = sceneColorImage;
|
||||
colorViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
colorViewInfo.format = colorFormat;
|
||||
colorViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
colorViewInfo.subresourceRange.levelCount = 1;
|
||||
colorViewInfo.subresourceRange.layerCount = 1;
|
||||
if (vkCreateImageView(device, &colorViewInfo, nullptr, &sceneColorView) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene color history view");
|
||||
return;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo depthViewInfo = colorViewInfo;
|
||||
depthViewInfo.image = sceneDepthImage;
|
||||
depthViewInfo.format = depthFormat;
|
||||
depthViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (vkCreateImageView(device, &depthViewInfo, nullptr, &sceneDepthView) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene depth history view");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create shared samplers
|
||||
VkSamplerCreateInfo sampCI{};
|
||||
sampCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
sampCI.magFilter = VK_FILTER_LINEAR;
|
||||
|
|
@ -389,99 +385,155 @@ void WaterRenderer::createSceneHistoryResources(VkExtent2D extent, VkFormat colo
|
|||
return;
|
||||
}
|
||||
|
||||
VkDescriptorSetAllocateInfo ai{};
|
||||
ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
ai.descriptorPool = sceneDescPool;
|
||||
ai.descriptorSetCount = 1;
|
||||
ai.pSetLayouts = &sceneSetLayout;
|
||||
if (vkAllocateDescriptorSets(device, &ai, &sceneSet) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to allocate scene descriptor set");
|
||||
sceneSet = VK_NULL_HANDLE;
|
||||
return;
|
||||
VkImageCreateInfo colorImgInfo{};
|
||||
colorImgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
colorImgInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
colorImgInfo.format = colorFormat;
|
||||
colorImgInfo.extent = {extent.width, extent.height, 1};
|
||||
colorImgInfo.mipLevels = 1;
|
||||
colorImgInfo.arrayLayers = 1;
|
||||
colorImgInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorImgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
colorImgInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
colorImgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImageCreateInfo depthImgInfo = colorImgInfo;
|
||||
depthImgInfo.format = depthFormat;
|
||||
|
||||
VmaAllocationCreateInfo allocCI{};
|
||||
allocCI.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
|
||||
// Create per-frame images, views, and descriptor sets
|
||||
for (uint32_t f = 0; f < SCENE_HISTORY_FRAMES; f++) {
|
||||
auto& sh = sceneHistory[f];
|
||||
|
||||
if (vmaCreateImage(vkCtx->getAllocator(), &colorImgInfo, &allocCI, &sh.colorImage, &sh.colorAlloc, nullptr) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene color history image [", f, "]");
|
||||
return;
|
||||
}
|
||||
if (vmaCreateImage(vkCtx->getAllocator(), &depthImgInfo, &allocCI, &sh.depthImage, &sh.depthAlloc, nullptr) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene depth history image [", f, "]");
|
||||
return;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo colorViewInfo{};
|
||||
colorViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
colorViewInfo.image = sh.colorImage;
|
||||
colorViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
colorViewInfo.format = colorFormat;
|
||||
colorViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
colorViewInfo.subresourceRange.levelCount = 1;
|
||||
colorViewInfo.subresourceRange.layerCount = 1;
|
||||
if (vkCreateImageView(device, &colorViewInfo, nullptr, &sh.colorView) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene color history view [", f, "]");
|
||||
return;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo depthViewInfo = colorViewInfo;
|
||||
depthViewInfo.image = sh.depthImage;
|
||||
depthViewInfo.format = depthFormat;
|
||||
depthViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (vkCreateImageView(device, &depthViewInfo, nullptr, &sh.depthView) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to create scene depth history view [", f, "]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate descriptor set for this frame
|
||||
VkDescriptorSetAllocateInfo ai{};
|
||||
ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
ai.descriptorPool = sceneDescPool;
|
||||
ai.descriptorSetCount = 1;
|
||||
ai.pSetLayouts = &sceneSetLayout;
|
||||
if (vkAllocateDescriptorSets(device, &ai, &sh.sceneSet) != VK_SUCCESS) {
|
||||
LOG_ERROR("WaterRenderer: failed to allocate scene descriptor set [", f, "]");
|
||||
sh.sceneSet = VK_NULL_HANDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo colorInfo{};
|
||||
colorInfo.sampler = sceneColorSampler;
|
||||
colorInfo.imageView = sh.colorView;
|
||||
colorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkDescriptorImageInfo depthInfo{};
|
||||
depthInfo.sampler = sceneDepthSampler;
|
||||
depthInfo.imageView = sh.depthView;
|
||||
depthInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkDescriptorImageInfo reflColorInfo{};
|
||||
reflColorInfo.sampler = sceneColorSampler;
|
||||
reflColorInfo.imageView = reflectionColorView ? reflectionColorView : sh.colorView;
|
||||
reflColorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkDescriptorBufferInfo reflUBOInfo{};
|
||||
reflUBOInfo.buffer = reflectionUBO;
|
||||
reflUBOInfo.offset = 0;
|
||||
reflUBOInfo.range = sizeof(ReflectionUBOData);
|
||||
|
||||
std::vector<VkWriteDescriptorSet> writes;
|
||||
|
||||
VkWriteDescriptorSet w0{};
|
||||
w0.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w0.dstSet = sh.sceneSet;
|
||||
w0.dstBinding = 0;
|
||||
w0.descriptorCount = 1;
|
||||
w0.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
w0.pImageInfo = &colorInfo;
|
||||
writes.push_back(w0);
|
||||
|
||||
VkWriteDescriptorSet w1{};
|
||||
w1.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w1.dstSet = sh.sceneSet;
|
||||
w1.dstBinding = 1;
|
||||
w1.descriptorCount = 1;
|
||||
w1.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
w1.pImageInfo = &depthInfo;
|
||||
writes.push_back(w1);
|
||||
|
||||
VkWriteDescriptorSet w2{};
|
||||
w2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w2.dstSet = sh.sceneSet;
|
||||
w2.dstBinding = 2;
|
||||
w2.descriptorCount = 1;
|
||||
w2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
w2.pImageInfo = &reflColorInfo;
|
||||
writes.push_back(w2);
|
||||
|
||||
if (reflectionUBO) {
|
||||
VkWriteDescriptorSet w3{};
|
||||
w3.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w3.dstSet = sh.sceneSet;
|
||||
w3.dstBinding = 3;
|
||||
w3.descriptorCount = 1;
|
||||
w3.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
w3.pBufferInfo = &reflUBOInfo;
|
||||
writes.push_back(w3);
|
||||
}
|
||||
|
||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo colorInfo{};
|
||||
colorInfo.sampler = sceneColorSampler;
|
||||
colorInfo.imageView = sceneColorView;
|
||||
colorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
VkDescriptorImageInfo depthInfo{};
|
||||
depthInfo.sampler = sceneDepthSampler;
|
||||
depthInfo.imageView = sceneDepthView;
|
||||
depthInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
// Reflection color texture (binding 2) — use scene color as placeholder until reflection is created
|
||||
VkDescriptorImageInfo reflColorInfo{};
|
||||
reflColorInfo.sampler = sceneColorSampler;
|
||||
reflColorInfo.imageView = reflectionColorView ? reflectionColorView : sceneColorView;
|
||||
reflColorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
// Reflection UBO (binding 3)
|
||||
VkDescriptorBufferInfo reflUBOInfo{};
|
||||
reflUBOInfo.buffer = reflectionUBO;
|
||||
reflUBOInfo.offset = 0;
|
||||
reflUBOInfo.range = sizeof(ReflectionUBOData);
|
||||
|
||||
// Write bindings 0,1 always; write 2,3 only if reflection resources exist
|
||||
std::vector<VkWriteDescriptorSet> writes;
|
||||
|
||||
VkWriteDescriptorSet w0{};
|
||||
w0.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w0.dstSet = sceneSet;
|
||||
w0.dstBinding = 0;
|
||||
w0.descriptorCount = 1;
|
||||
w0.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
w0.pImageInfo = &colorInfo;
|
||||
writes.push_back(w0);
|
||||
|
||||
VkWriteDescriptorSet w1{};
|
||||
w1.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w1.dstSet = sceneSet;
|
||||
w1.dstBinding = 1;
|
||||
w1.descriptorCount = 1;
|
||||
w1.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
w1.pImageInfo = &depthInfo;
|
||||
writes.push_back(w1);
|
||||
|
||||
VkWriteDescriptorSet w2{};
|
||||
w2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w2.dstSet = sceneSet;
|
||||
w2.dstBinding = 2;
|
||||
w2.descriptorCount = 1;
|
||||
w2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
w2.pImageInfo = &reflColorInfo;
|
||||
writes.push_back(w2);
|
||||
|
||||
if (reflectionUBO) {
|
||||
VkWriteDescriptorSet w3{};
|
||||
w3.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
w3.dstSet = sceneSet;
|
||||
w3.dstBinding = 3;
|
||||
w3.descriptorCount = 1;
|
||||
w3.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
w3.pBufferInfo = &reflUBOInfo;
|
||||
writes.push_back(w3);
|
||||
}
|
||||
|
||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||
|
||||
// Initialize history images to shader-read layout so first frame samples are defined.
|
||||
// Initialize all per-frame history images to shader-read layout
|
||||
vkCtx->immediateSubmit([&](VkCommandBuffer cmd) {
|
||||
VkImageMemoryBarrier barriers[2]{};
|
||||
barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[0].image = sceneColorImage;
|
||||
barriers[0].subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
barriers[1] = barriers[0];
|
||||
barriers[1].image = sceneDepthImage;
|
||||
barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
std::vector<VkImageMemoryBarrier> barriers;
|
||||
for (uint32_t f = 0; f < SCENE_HISTORY_FRAMES; f++) {
|
||||
VkImageMemoryBarrier b{};
|
||||
b.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
b.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
b.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
b.image = sceneHistory[f].colorImage;
|
||||
b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
barriers.push_back(b);
|
||||
|
||||
b.image = sceneHistory[f].depthImage;
|
||||
b.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
barriers.push_back(b);
|
||||
}
|
||||
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 2, barriers);
|
||||
0, 0, nullptr, 0, nullptr, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -986,7 +1038,7 @@ void WaterRenderer::clear() {
|
|||
// ==============================================================
|
||||
|
||||
void WaterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
const Camera& /*camera*/, float /*time*/, bool use1x) {
|
||||
const Camera& /*camera*/, float /*time*/, bool use1x, uint32_t frameIndex) {
|
||||
VkPipeline pipeline = (use1x && water1xPipeline) ? water1xPipeline : waterPipeline;
|
||||
if (!renderingEnabled || surfaces.empty() || !pipeline) {
|
||||
if (renderDiagCounter_++ % 300 == 0 && !surfaces.empty()) {
|
||||
|
|
@ -997,7 +1049,9 @@ void WaterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
|||
}
|
||||
return;
|
||||
}
|
||||
if (!sceneSet) {
|
||||
uint32_t fi = frameIndex % SCENE_HISTORY_FRAMES;
|
||||
VkDescriptorSet activeSceneSet = sceneHistory[fi].sceneSet;
|
||||
if (!activeSceneSet) {
|
||||
if (renderDiagCounter_++ % 300 == 0) {
|
||||
LOG_WARNING("Water: render skipped — sceneSet is null, surfaces=", surfaces.size());
|
||||
}
|
||||
|
|
@ -1009,7 +1063,7 @@ void WaterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
|||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout,
|
||||
0, 1, &perFrameSet, 0, nullptr);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout,
|
||||
2, 1, &sceneSet, 0, nullptr);
|
||||
2, 1, &activeSceneSet, 0, nullptr);
|
||||
|
||||
for (const auto& surface : surfaces) {
|
||||
if (surface.vertexBuffer == VK_NULL_HANDLE || surface.indexCount == 0) continue;
|
||||
|
|
@ -1050,8 +1104,11 @@ void WaterRenderer::captureSceneHistory(VkCommandBuffer cmd,
|
|||
VkImage srcColorImage,
|
||||
VkImage srcDepthImage,
|
||||
VkExtent2D srcExtent,
|
||||
bool srcDepthIsMsaa) {
|
||||
if (!vkCtx || !cmd || !sceneColorImage || !sceneDepthImage || srcExtent.width == 0 || srcExtent.height == 0) {
|
||||
bool srcDepthIsMsaa,
|
||||
uint32_t frameIndex) {
|
||||
uint32_t fi = frameIndex % SCENE_HISTORY_FRAMES;
|
||||
auto& sh = sceneHistory[fi];
|
||||
if (!vkCtx || !cmd || !sh.colorImage || !sh.depthImage || srcExtent.width == 0 || srcExtent.height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1091,7 +1148,7 @@ void WaterRenderer::captureSceneHistory(VkCommandBuffer cmd,
|
|||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
0, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
barrier2(sceneColorImage, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
barrier2(sh.colorImage, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
|
@ -1101,9 +1158,9 @@ void WaterRenderer::captureSceneHistory(VkCommandBuffer cmd,
|
|||
colorCopy.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
colorCopy.extent = {copyExtent.width, copyExtent.height, 1};
|
||||
vkCmdCopyImage(cmd, srcColorImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
sceneColorImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &colorCopy);
|
||||
sh.colorImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &colorCopy);
|
||||
|
||||
barrier2(sceneColorImage, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
barrier2(sh.colorImage, VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
|
@ -1118,7 +1175,7 @@ void WaterRenderer::captureSceneHistory(VkCommandBuffer cmd,
|
|||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
barrier2(sceneDepthImage, VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
barrier2(sh.depthImage, VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
|
@ -1128,9 +1185,9 @@ void WaterRenderer::captureSceneHistory(VkCommandBuffer cmd,
|
|||
depthCopy.dstSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1};
|
||||
depthCopy.extent = {copyExtent.width, copyExtent.height, 1};
|
||||
vkCmdCopyImage(cmd, srcDepthImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
sceneDepthImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &depthCopy);
|
||||
sh.depthImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &depthCopy);
|
||||
|
||||
barrier2(sceneDepthImage, VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
barrier2(sh.depthImage, VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
|
@ -1543,11 +1600,11 @@ bool WaterRenderer::isWmoWaterAt(float glX, float glY) const {
|
|||
glm::vec4 WaterRenderer::getLiquidColor(uint16_t liquidType) const {
|
||||
uint8_t basicType = (liquidType == 0) ? 0 : ((liquidType - 1) % 4);
|
||||
switch (basicType) {
|
||||
case 0: return glm::vec4(0.12f, 0.32f, 0.48f, 1.0f); // inland: blue-green
|
||||
case 1: return glm::vec4(0.04f, 0.14f, 0.30f, 1.0f); // ocean: deep blue
|
||||
case 0: return glm::vec4(0.10f, 0.28f, 0.55f, 1.0f); // inland: richer blue
|
||||
case 1: return glm::vec4(0.04f, 0.16f, 0.38f, 1.0f); // ocean: deep blue
|
||||
case 2: return glm::vec4(0.9f, 0.3f, 0.05f, 1.0f); // magma
|
||||
case 3: return glm::vec4(0.2f, 0.6f, 0.1f, 1.0f); // slime
|
||||
default: return glm::vec4(0.12f, 0.32f, 0.48f, 1.0f);
|
||||
default: return glm::vec4(0.10f, 0.28f, 0.55f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1815,21 +1872,28 @@ void WaterRenderer::endReflectionPass(VkCommandBuffer cmd) {
|
|||
vkCmdEndRenderPass(cmd);
|
||||
reflectionColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
// Update scene descriptor set with the freshly rendered reflection texture
|
||||
if (sceneSet && reflectionColorView && reflectionSampler) {
|
||||
// Update all per-frame scene descriptor sets with the freshly rendered reflection texture
|
||||
if (reflectionColorView && reflectionSampler) {
|
||||
VkDescriptorImageInfo reflInfo{};
|
||||
reflInfo.sampler = reflectionSampler;
|
||||
reflInfo.imageView = reflectionColorView;
|
||||
reflInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = sceneSet;
|
||||
write.dstBinding = 2;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &reflInfo;
|
||||
vkUpdateDescriptorSets(vkCtx->getDevice(), 1, &write, 0, nullptr);
|
||||
std::vector<VkWriteDescriptorSet> writes;
|
||||
for (uint32_t f = 0; f < SCENE_HISTORY_FRAMES; f++) {
|
||||
if (!sceneHistory[f].sceneSet) continue;
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = sceneHistory[f].sceneSet;
|
||||
write.dstBinding = 2;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &reflInfo;
|
||||
writes.push_back(write);
|
||||
}
|
||||
if (!writes.empty()) {
|
||||
vkUpdateDescriptorSets(vkCtx->getDevice(), static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -288,6 +288,15 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
msaaSettingsApplied_ = true;
|
||||
}
|
||||
|
||||
// Apply saved water refraction setting once when renderer is available
|
||||
if (!waterRefractionApplied_) {
|
||||
auto* renderer = core::Application::getInstance().getRenderer();
|
||||
if (renderer) {
|
||||
renderer->setWaterRefractionEnabled(pendingWaterRefraction);
|
||||
waterRefractionApplied_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply saved normal mapping / POM settings once when WMO renderer is available
|
||||
if (!normalMapSettingsApplied_) {
|
||||
auto* renderer = core::Application::getInstance().getRenderer();
|
||||
|
|
@ -6237,6 +6246,10 @@ void GameScreen::renderSettingsWindow() {
|
|||
if (renderer) renderer->setShadowsEnabled(pendingShadows);
|
||||
saveSettings();
|
||||
}
|
||||
if (ImGui::Checkbox("Water Refraction", &pendingWaterRefraction)) {
|
||||
if (renderer) renderer->setWaterRefractionEnabled(pendingWaterRefraction);
|
||||
saveSettings();
|
||||
}
|
||||
{
|
||||
const char* aaLabels[] = { "Off", "2x MSAA", "4x MSAA", "8x MSAA" };
|
||||
if (ImGui::Combo("Anti-Aliasing", &pendingAntiAliasing, aaLabels, 4)) {
|
||||
|
|
@ -6336,7 +6349,9 @@ void GameScreen::renderSettingsWindow() {
|
|||
window->setFullscreen(pendingFullscreen);
|
||||
window->setVsync(pendingVsync);
|
||||
window->applyResolution(kResolutions[pendingResIndex][0], kResolutions[pendingResIndex][1]);
|
||||
pendingWaterRefraction = false;
|
||||
if (renderer) renderer->setShadowsEnabled(pendingShadows);
|
||||
if (renderer) renderer->setWaterRefractionEnabled(pendingWaterRefraction);
|
||||
if (renderer) renderer->setMsaaSamples(VK_SAMPLE_COUNT_1_BIT);
|
||||
if (renderer) {
|
||||
if (auto* tm = renderer->getTerrainManager()) {
|
||||
|
|
@ -7349,6 +7364,7 @@ void GameScreen::saveSettings() {
|
|||
out << "auto_loot=" << (pendingAutoLoot ? 1 : 0) << "\n";
|
||||
out << "ground_clutter_density=" << pendingGroundClutterDensity << "\n";
|
||||
out << "shadows=" << (pendingShadows ? 1 : 0) << "\n";
|
||||
out << "water_refraction=" << (pendingWaterRefraction ? 1 : 0) << "\n";
|
||||
out << "antialiasing=" << pendingAntiAliasing << "\n";
|
||||
out << "normal_mapping=" << (pendingNormalMapping ? 1 : 0) << "\n";
|
||||
out << "normal_map_strength=" << pendingNormalMapStrength << "\n";
|
||||
|
|
@ -7433,6 +7449,7 @@ void GameScreen::loadSettings() {
|
|||
else if (key == "auto_loot") pendingAutoLoot = (std::stoi(val) != 0);
|
||||
else if (key == "ground_clutter_density") pendingGroundClutterDensity = std::clamp(std::stoi(val), 0, 150);
|
||||
else if (key == "shadows") pendingShadows = (std::stoi(val) != 0);
|
||||
else if (key == "water_refraction") pendingWaterRefraction = (std::stoi(val) != 0);
|
||||
else if (key == "antialiasing") pendingAntiAliasing = std::clamp(std::stoi(val), 0, 3);
|
||||
else if (key == "normal_mapping") pendingNormalMapping = (std::stoi(val) != 0);
|
||||
else if (key == "normal_map_strength") pendingNormalMapStrength = std::clamp(std::stof(val), 0.0f, 2.0f);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue