Created
July 20, 2024 20:53
-
-
Save dov/f7d8e0adf755d6ced43ce685efeb1c43 to your computer and use it in GitHub Desktop.
Testing using a custom uniform in a shader in VSG
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//====================================================================== | |
// This is an example of using a custom uniform in a shader. | |
// | |
// 2024-07-20 Sat | |
// Dov Grobgeld <[email protected]> | |
//---------------------------------------------------------------------- | |
#include <vsgXchange/all.h> | |
#include <iostream> | |
#include <vsg/all.h> | |
using namespace std; | |
struct ToyUniform { | |
vsg::ivec2 iResolution; | |
vsg::vec2 iMouse; | |
float iTime; | |
}; | |
using ToyUniformValue = vsg::Value<ToyUniform>; | |
// Create a vsg node containing an image | |
vsg::ref_ptr<vsg::Node> createToyNode(vsg::ref_ptr<vsg::Options> options, | |
// output | |
vsg::ref_ptr<ToyUniformValue>& toyUniform) | |
{ | |
// load shaders | |
vsg::ref_ptr<vsg::ShaderStage> vertexShader = vsg::read_cast<vsg::ShaderStage>("shaders/shader-toy.vert", options); | |
vsg::ref_ptr<vsg::ShaderStage> fragmentShader = vsg::read_cast<vsg::ShaderStage>("shaders/shader-toy.frag", options); | |
if (!vertexShader || !fragmentShader) | |
throw std::runtime_error("Could not create shaders."); | |
toyUniform = ToyUniformValue::create(); | |
toyUniform->properties.dataVariance = vsg::DataVariance::DYNAMIC_DATA; | |
toyUniform->value().iResolution = {800,600}; | |
toyUniform->value().iTime = 0; | |
toyUniform->value().iMouse = {0,0}; | |
toyUniform->dirty(); | |
// set up graphics pipeline | |
vsg::DescriptorSetLayoutBindings descriptorBindings{ | |
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr} // { binding, descriptorType, descriptorCount, stageFlags, pImmutableSamplers} | |
}; | |
auto descriptorSetLayout = vsg::DescriptorSetLayout::create(descriptorBindings); | |
vsg::VertexInputState::Bindings vertexBindingsDescriptions{ | |
VkVertexInputBindingDescription{0, sizeof(vsg::vec2), VK_VERTEX_INPUT_RATE_VERTEX}, // vertex data | |
VkVertexInputBindingDescription{1, sizeof(vsg::vec2), VK_VERTEX_INPUT_RATE_VERTEX} // tex coord data | |
}; | |
vsg::VertexInputState::Attributes vertexAttributeDescriptions{ | |
VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32_SFLOAT, 0}, // vertex data | |
VkVertexInputAttributeDescription{1, 1, VK_FORMAT_R32G32_SFLOAT, 0} // tex coord data | |
}; | |
vsg::GraphicsPipelineStates pipelineStates{ | |
vsg::VertexInputState::create(vertexBindingsDescriptions, vertexAttributeDescriptions), | |
vsg::InputAssemblyState::create(), | |
vsg::RasterizationState::create(), | |
vsg::MultisampleState::create(), | |
vsg::ColorBlendState::create(), | |
vsg::DepthStencilState::create()}; | |
// Do I need this when having a constant V,M, and P? | |
vsg::PushConstantRanges pushConstantRanges{ | |
{VK_SHADER_STAGE_VERTEX_BIT, 0, 128} // projection, view, and model matrices, actual push constant calls automatically provided by the VSG's RecordTraversal | |
}; | |
auto toyUniformDescriptor = vsg::DescriptorBuffer::create(toyUniform, 0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); | |
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); | |
auto descriptorSet = vsg::DescriptorSet::create(descriptorSetLayout, vsg::Descriptors{toyUniformDescriptor}); | |
auto bindDescriptorSet = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, descriptorSet); | |
// create StateGroup as the root of the scene/command graph to hold the GraphicsPipeline, and binding of Descriptors to decorate the whole graph | |
auto node = vsg::StateGroup::create(); | |
node->add(bindGraphicsPipeline); | |
node->add(bindDescriptorSet); | |
// set up vertex and index arrays | |
auto vertices = vsg::vec2Array::create( | |
{{-1.0f, -1.0f}, | |
{1.0f, -1.0f}, | |
{1.0f, 1.0f}, | |
{-1.0f, 1.0f}}); // VK_FORMAT_R32G32_SFLOAT, VK_VERTEX_INPUT_RATE_INSTANCE, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE | |
auto texcoords = vsg::vec2Array::create( | |
{{0.0f, 1.0f}, | |
{1.0f, 1.0f}, | |
{1.0f, 0.0f}, | |
{0.0f, 0.0f}}); // VK_FORMAT_R32G32_SFLOAT, VK_VERTEX_INPUT_RATE_VERTEX, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE | |
auto indices = vsg::ushortArray::create( | |
{0, 1, 2, | |
2, 3, 0}); // VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE | |
// setup geometry | |
auto drawCommands = vsg::Commands::create(); | |
drawCommands->addChild(vsg::BindVertexBuffers::create(0, vsg::DataList{vertices, texcoords})); | |
drawCommands->addChild(vsg::BindIndexBuffer::create(indices)); | |
drawCommands->addChild(vsg::DrawIndexed::create(6, 1, 0, 0, 0)); | |
// add drawCommands to transform | |
node->addChild(drawCommands); | |
return node; | |
} | |
int main(int argc, char** argv) | |
{ | |
// set up defaults and read command line arguments to override them | |
vsg::CommandLine arguments(&argc, argv); | |
auto options = vsg::Options::create(); | |
options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); | |
options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); | |
// add vsgXchange's support for reading and writing 3rd party file formats | |
options->add(vsgXchange::all::create()); | |
auto windowTraits = vsg::WindowTraits::create(); | |
windowTraits->debugLayer = arguments.read({"--debug", "-d"}); | |
windowTraits->apiDumpLayer = arguments.read({"--api", "-a"}); | |
arguments.read({"--window", "-w"}, windowTraits->width, windowTraits->height); | |
if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); | |
windowTraits->windowTitle = "hello-shader"; | |
windowTraits->height = 600; | |
windowTraits->width = 800; | |
auto scene = vsg::Group::create(); | |
// Add the image to the scene | |
vsg::ref_ptr<ToyUniformValue> toyUniform; | |
scene->addChild(createToyNode(options, | |
// output | |
toyUniform | |
)); | |
toyUniform->dirty(); | |
if (1) | |
{ | |
// Test adding a quad to the scene to make sure that I can | |
// render anything | |
auto builder = vsg::Builder::create(); | |
vsg::GeometryInfo geomInfo; | |
vsg::StateInfo stateInfo; | |
geomInfo.position = vsg::vec3 {0.0f, 0.0f, -0.49f}; | |
geomInfo.color = vsg::vec4 {0.0f, 1.0f, 0.0f, 0.3f}; // green | |
auto node = builder->createQuad(geomInfo, stateInfo); | |
scene->addChild(node); | |
} | |
// create the viewer and assign window(s) to it | |
auto viewer = vsg::Viewer::create(); | |
auto window = vsg::Window::create(windowTraits); | |
if (!window) | |
{ | |
std::cout << "Could not create window." << std::endl; | |
return 1; | |
} | |
viewer->addWindow(window); | |
// camera related details | |
auto viewport = vsg::ViewportState::create(0, 0, window->extent2D().width, window->extent2D().height); | |
auto ortho = vsg::Orthographic::create(); | |
ortho->nearDistance = -1000; | |
ortho->farDistance = 1000; | |
auto lookAt = vsg::LookAt::create(vsg::dvec3(0, 0, 1.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 1.0, 0.0)); | |
auto camera = vsg::Camera::create(ortho, lookAt, viewport); | |
auto commandGraph = vsg::createCommandGraphForView(window, camera, scene); | |
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); | |
// compile the Vulkan objects | |
viewer->compile(); | |
// assign a CloseHandler to the Viewer to respond to pressing Escape or the window close button | |
viewer->addEventHandlers({vsg::CloseHandler::create(viewer)}); | |
// main frame loop | |
while (viewer->advanceToNextFrame()) | |
{ | |
toyUniform->value().iTime += 1; | |
toyUniform->dirty(); | |
viewer->handleEvents(); | |
viewer->update(); | |
viewer->recordAndSubmit(); | |
viewer->present(); | |
} | |
// clean up done automatically thanks to ref_ptr<> | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 450 | |
layout(set = 0, binding = 0) uniform UBO { | |
ivec2 iResolution; | |
vec2 iMouse; | |
float iTime; | |
} ubo; | |
layout(location = 0) in vec2 fragCoord; | |
layout(location = 0) out vec4 fragColor; | |
void main() | |
{ | |
// Do a dummy test to see if iResolution has been initialized | |
if (abs(ubo.iResolution.x) == 0) | |
fragColor = vec4(1.0,0.0,0.0,1.0); // red | |
else | |
fragColor = vec4(0.0,1.0,0.0,1.0); // green | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 450 | |
layout(push_constant) uniform PushConstants { | |
mat4 projection; | |
mat4 modelView; | |
} pc; | |
layout(set = 0, binding = 0) uniform UBO { | |
ivec2 iResolution; | |
vec2 iMouse; | |
float iTime; | |
} ubo; | |
layout(location = 0) in vec2 inVertex; | |
layout(location = 1) in vec2 inTexture; | |
layout(location = 0) out vec2 fragCoord; | |
out gl_PerVertex{ vec4 gl_Position; }; | |
void main() | |
{ | |
vec4 vertex = vec4(inVertex, 0, 1.0); | |
// How can I "fill the quad" without using the redundant | |
// project and model view matrixes? | |
gl_Position = (pc.projection * pc.modelView) * vertex; | |
fragCoord = vec2(inTexture.x * ubo.iResolution.x, | |
inTexture.y * ubo.iResolution.y); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment