vsg-dev / VulkanSceneGraph

Vulkan & C++17 based Scene Graph Project
http://www.vulkanscenegraph.org
MIT License
1.28k stars 206 forks source link

the rendering order about transparent node and opaque node #818

Closed orchidshelley closed 1 year ago

orchidshelley commented 1 year ago

Describe the bug I have two nodes to add scenegraph, one is transparent , another is opaque . I hope the rendering is correct regardless of whether the transparent node is added first or later. I found if I add opaque first, then add transparent , the rendering is correct. But if I add transparent first , then add opaque, the rendering is incorrect.

The code looks like this: first create root sceneGraph:

auto rootScenegraph = vsg::StateGroup::create();

then create transparent subscenegraph:

// translucency subscenegraph auto transparentScenegraph = vsg::StateGroup::create(); auto transform1 = vsg::MatrixTransform::create(); transparentScenegraph->addChild(transform1);

// colorBlendState with alphablend

auto colorBlendState = vsg::ColorBlendState::create();
colorBlendState->attachments[0].blendEnable = true;
colorBlendState->attachments[0].srcColorBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_CONSTANT_ALPHA;
colorBlendState->attachments[0].dstColorBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
colorBlendState->attachments[0].colorBlendOp = VkBlendOp::VK_BLEND_OP_ADD;

// depthStencilState: no write depth
auto depthStencilState = vsg::DepthStencilState::create();
depthStencilState->depthTestEnable = VK_TRUE;
depthStencilState->depthWriteEnable = VK_FALSE;

 vsg::GraphicsPipelineStates pipelineStates{
    vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions),
    vsg::InputAssemblyState::create(),
    vsg::RasterizationState::create(),
    vsg::MultisampleState::create(),
    colorBlendState, 
    depthStencilState
};

auto pipelineLayout = vsg::PipelineLayout::create(vsg::DescriptorSetLayouts{ descriptorSetLayout }, pushConstantRanges);
auto graphicsPipeline = vsg::GraphicsPipeline::create(pipelineLayout, vsg::ShaderStages{ vertexShader, fragmentShader }, 
pipelineStates);
auto bindGraphicsPipeline = vsg::BindGraphicsPipeline::create(graphicsPipeline);

transparentScenegraph->add(bindGraphicsPipeline);
transparentScenegraph->add(bindDescriptorSet);

auto drawCommands1 = vsg::Commands::create();
drawCommands1->addChild(vsg::BindVertexBuffers::create(0, vsg::DataList{ vertices, colors }));
drawCommands1->addChild(vsg::BindIndexBuffer::create(indices));
drawCommands1->addChild(vsg::DrawIndexed::create(indicesNum, 1, 0, 0, 0));

transform1->addChild(drawCommands);

then create opacity subscenegraph

// opacity subscenegraph

auto opacityScenegraph = vsg::StateGroup::create();
auto transform2 = vsg::MatrixTransform::create(); 
opacityScenegraph->addChild(transform2);

...

vsg::GraphicsPipelineStates pipelineStates2{
    vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions),
    vsg::InputAssemblyState::create(),
    vsg::RasterizationState::create(),
    vsg::MultisampleState::create(),
    vsg::ColorBlendState::create(),
    vsg::DepthStencilState::create()
};

...

auto drawCommands2 = vsg::Commands::create();
transform2->addChild(drawCommands2);

I add them to root scenegraph. If I add opacity first, then transparent, the result is correct, code is below:

rootScenegraph->addChild(opacityScenegraph); rootScenegraph->addChild(transparentScenegraph);

But If I add transparent first, then opacity , the result is incorrect, code is below:

rootScenegraph->addChild(transparentScenegraph); rootScenegraph->addChild(opacityScenegraph);

Expected behavior I don't think I need care about which one add first. Am I using it wrong?

Screenshots there are two planes, the position of opaque plane is below transparent plane. opaque add first ,the rendering is correct:

opaqueFirst

transparent add first, the rendering is incorrect:

transparentfirst
robertosfield commented 1 year ago

The VSG records commands to the Vulkan CommandBuffer in order of traversal unless you use rendering bins that control the record/draw order. The VSG has the vsg::DepthSorted node that you can use to decorate subgraphs that you intend to be depth sorted, the VSG's compile traversal will configure the vsg::Bin these will drop into on each frame, these are placed into the associated vsg::View.

There isn't present an vsgExample that illustrates this but the vsgXchange::assimp implementation supports transparency using vsg::DepthSorted so can be looked as an example.

https://github.com/vsg-dev/vsgXchange/blob/master/src/assimp/assimp.cpp#L706