feat: add simple transparency
This commit is contained in:
@@ -414,6 +414,16 @@ void MeshRendererComponent::DrawMaterialEditor(RenderResources& resources, GameR
|
||||
changed = finished = true;
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 2> blendModeNames{"Opaque", "Transparent"};
|
||||
int blendMode = static_cast<int>(material.blendMode);
|
||||
if (ImGui::Combo("Blend", &blendMode, blendModeNames.data(), static_cast<int>(blendModeNames.size()))) {
|
||||
material.blendMode = static_cast<BlendMode>(blendMode);
|
||||
changed = finished = true;
|
||||
}
|
||||
if (material.blendMode == BlendMode::Transparent) {
|
||||
track(ImGui::SliderFloat("Opacity", &material.opacity, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Textures");
|
||||
for (const TextureSlot& slot : TextureSlots) {
|
||||
ImGui::PushID(slot.label);
|
||||
|
||||
@@ -184,7 +184,7 @@ MaterialData MaterialCache::toMaterialData(const Material& material) const
|
||||
};
|
||||
|
||||
return MaterialData{
|
||||
.baseColor = glm::vec4(material.baseColor, 1.0f),
|
||||
.baseColor = glm::vec4(material.baseColor, material.blendMode == BlendMode::Transparent ? material.opacity : 1.0f),
|
||||
.emissiveColor = glm::vec4(material.emissiveColor, 0.0f),
|
||||
|
||||
.metallicRoughnessNormalOcclusion = glm::vec4{
|
||||
|
||||
@@ -97,15 +97,9 @@ IndirectDrawList::View IndirectDrawList::addView(
|
||||
++view.instanceCount;
|
||||
};
|
||||
|
||||
if (!sortedDrawCommands.empty()) {
|
||||
for (std::size_t drawIndex : sortedDrawCommands) {
|
||||
assert(drawIndex < drawCommands.size());
|
||||
addDraw(drawCommands[drawIndex]);
|
||||
}
|
||||
} else {
|
||||
for (const MeshDrawCommand& dc : drawCommands) {
|
||||
addDraw(dc);
|
||||
}
|
||||
for (std::size_t drawIndex : sortedDrawCommands) {
|
||||
assert(drawIndex < drawCommands.size());
|
||||
addDraw(drawCommands[drawIndex]);
|
||||
}
|
||||
|
||||
return view;
|
||||
|
||||
@@ -131,6 +131,36 @@ void MeshPipeline::init(
|
||||
pipelineConfig
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
PipelineConfigInfo pipelineConfig{};
|
||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||
|
||||
pipelineConfig.name = "Mesh Transparent Pipeline";
|
||||
pipelineConfig.pipelineLayout = m_pipelineLayout;
|
||||
|
||||
pipelineConfig.vertexAttributeDescriptions = {};
|
||||
pipelineConfig.vertexBindingDescriptions = {};
|
||||
|
||||
pipelineConfig.colorAttachments = {drawImageFormat};
|
||||
pipelineConfig.depthAttachment = depthImageFormat;
|
||||
|
||||
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
|
||||
pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
|
||||
pipelineConfig.colorBlendAttachment.blendEnable = VK_TRUE;
|
||||
pipelineConfig.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
pipelineConfig.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
pipelineConfig.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
pipelineConfig.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
m_transparentPipeline = std::make_unique<Pipeline>(
|
||||
gfxDevice,
|
||||
vertexShader.string(),
|
||||
fragShader.string(),
|
||||
pipelineConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -187,6 +217,29 @@ void MeshPipeline::draw(
|
||||
view);
|
||||
}
|
||||
|
||||
void MeshPipeline::drawTransparent(
|
||||
VkCommandBuffer cmd,
|
||||
VkExtent2D renderExtent,
|
||||
const RenderResources& resources,
|
||||
const GPUBuffer& sceneDataBuffer,
|
||||
const IndirectDrawList& drawList,
|
||||
const IndirectDrawList::View& view)
|
||||
{
|
||||
if (!m_transparentPipeline || view.commandCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
drawMeshes(
|
||||
cmd,
|
||||
*m_transparentPipeline,
|
||||
renderExtent,
|
||||
resources,
|
||||
sceneDataBuffer,
|
||||
drawList,
|
||||
view);
|
||||
}
|
||||
|
||||
void MeshPipeline::drawMeshes(
|
||||
VkCommandBuffer cmd,
|
||||
const Pipeline& pipeline,
|
||||
@@ -254,6 +307,7 @@ void MeshPipeline::drawMeshes(
|
||||
void MeshPipeline::cleanup(VkDevice device)
|
||||
{
|
||||
m_pipeline.reset();
|
||||
m_transparentPipeline.reset();
|
||||
m_depthPrepassPipeline.reset();
|
||||
m_selectionPrepassPipeline.reset();
|
||||
|
||||
|
||||
@@ -101,13 +101,24 @@ void GameRenderer::endDrawing()
|
||||
|
||||
// Grouping by mesh lets IndirectDrawList merge each run into one instanced draw.
|
||||
// Material and vertex buffer are per instance, so they don't break a batch.
|
||||
std::stable_sort(
|
||||
sortedMeshDrawCommands.begin(),
|
||||
sortedMeshDrawCommands.end(),
|
||||
[this](std::size_t left, std::size_t right)
|
||||
std::ranges::stable_sort(sortedMeshDrawCommands
|
||||
,
|
||||
[this](std::size_t left, std::size_t right)
|
||||
{
|
||||
return meshDrawCommands[left].meshId < meshDrawCommands[right].meshId;
|
||||
});
|
||||
|
||||
const auto& materials = resources->materials();
|
||||
transparentMeshDrawCommands.clear();
|
||||
std::erase_if(sortedMeshDrawCommands, [&](std::size_t index)
|
||||
{
|
||||
if (materials.getMaterial(meshDrawCommands[index].materialId).blendMode != BlendMode::Transparent)
|
||||
{
|
||||
return meshDrawCommands[left].meshId < meshDrawCommands[right].meshId;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
transparentMeshDrawCommands.push_back(index);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData)
|
||||
@@ -161,7 +172,23 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
// Culling stays on the CPU: every view (camera, directional light, each spot light)
|
||||
// gets its own range of indirect commands in this frame's draw list.
|
||||
{
|
||||
ZoneScopedN("Sort Transparent Draws");
|
||||
|
||||
const glm::vec3 cameraPos = camera.GetPosition();
|
||||
const auto distanceSq = [&](std::size_t index)
|
||||
{
|
||||
const glm::vec3 offset = meshDrawCommands[index].worldBoundingSphere.center - cameraPos;
|
||||
return glm::dot(offset, offset);
|
||||
};
|
||||
std::ranges::sort(transparentMeshDrawCommands, [&](std::size_t left, std::size_t right)
|
||||
{
|
||||
return distanceSq(left) > distanceSq(right);
|
||||
});
|
||||
}
|
||||
|
||||
IndirectDrawList::View mainView;
|
||||
IndirectDrawList::View transparentView;
|
||||
IndirectDrawList::View directionalShadowView;
|
||||
std::vector<IndirectDrawList::View> spotShadowViews;
|
||||
{
|
||||
@@ -177,6 +204,13 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
edge::createFrustumFromCamera(camera),
|
||||
IndirectDrawList::Filter::All);
|
||||
|
||||
transparentView = drawList.addView(
|
||||
meshes,
|
||||
meshDrawCommands,
|
||||
transparentMeshDrawCommands,
|
||||
edge::createFrustumFromCamera(camera),
|
||||
IndirectDrawList::Filter::All);
|
||||
|
||||
if (shadowLightDirection)
|
||||
{
|
||||
directionalShadowView = drawList.addView(
|
||||
@@ -416,6 +450,19 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
skyboxPipeline->draw(cmd, *resources, camera, sceneData.skyboxRotation);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::drawTransparent");
|
||||
vkutil::ScopedCmdLabel label(cmd, "MeshPipeline::drawTransparent", vkutil::labelColors::opaque);
|
||||
|
||||
meshPipeline->drawTransparent(
|
||||
cmd,
|
||||
drawImage.getExtent2D(),
|
||||
*resources,
|
||||
sceneDataBuffer.getBuffer(),
|
||||
drawList,
|
||||
transparentView);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdEndRendering");
|
||||
|
||||
@@ -600,6 +647,7 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
||||
pendingMaterialUploads.clear();
|
||||
meshDrawCommands.clear();
|
||||
sortedMeshDrawCommands.clear();
|
||||
transparentMeshDrawCommands.clear();
|
||||
initialized = false;
|
||||
GameState::GetInstance().SetRenderer(nullptr);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ namespace {
|
||||
return TextureFilteringMode::Linear;
|
||||
}
|
||||
|
||||
constexpr std::array<std::string_view, 2> BlendModeNames{"opaque", "transparent"};
|
||||
|
||||
[[nodiscard]] BlendMode ParseBlendMode(std::string_view name)
|
||||
{
|
||||
for (std::size_t i = 0; i < BlendModeNames.size(); ++i) {
|
||||
if (BlendModeNames[i] == name) {
|
||||
return static_cast<BlendMode>(i);
|
||||
}
|
||||
}
|
||||
return BlendMode::Opaque;
|
||||
}
|
||||
|
||||
[[nodiscard]] RenderResources* GetResources()
|
||||
{
|
||||
auto& state = GameState::GetInstance();
|
||||
@@ -110,6 +122,8 @@ namespace MaterialSerialization {
|
||||
{"normalScale", material.normalScale},
|
||||
{"occlusionStrength", material.occlusionStrength},
|
||||
{"filtering", FilteringNames.at(static_cast<std::size_t>(material.textureFilteringMode))},
|
||||
{"blend", BlendModeNames.at(static_cast<std::size_t>(material.blendMode))},
|
||||
{"opacity", material.opacity},
|
||||
};
|
||||
|
||||
json textures = json::object();
|
||||
@@ -138,6 +152,8 @@ namespace MaterialSerialization {
|
||||
material.normalScale = data.value("normalScale", material.normalScale);
|
||||
material.occlusionStrength = data.value("occlusionStrength", material.occlusionStrength);
|
||||
material.textureFilteringMode = ParseFiltering(data.value("filtering", std::string{"linear"}));
|
||||
material.blendMode = ParseBlendMode(data.value("blend", std::string{"opaque"}));
|
||||
material.opacity = data.value("opacity", material.opacity);
|
||||
|
||||
if (data.contains("textures")) {
|
||||
const json& textures = data.at("textures");
|
||||
|
||||
Reference in New Issue
Block a user