diff --git a/CMakeLists.txt b/CMakeLists.txt index 91a64d608c2f9a39fbb583dcd835444b4c40225d..806df0144fb4bd98fe5161b88f1a1803d5243e50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,5 +9,13 @@ add_library(lava-vr ${LAVA_LINK_TYPE} ${HEADER_FILES} ) +list(APPEND lava_pack_shaders + "${CMAKE_CURRENT_LIST_DIR}/shaders/vrmesh.vert" "lava-vr/vrmesh.vert" + "${CMAKE_CURRENT_LIST_DIR}/shaders/vrmesh.frag" "lava-vr/vrmesh.frag" + "${CMAKE_CURRENT_LIST_DIR}/shaders/albedo.vert" "lava-vr/albedo.vert" + "${CMAKE_CURRENT_LIST_DIR}/shaders/albedo.frag" "lava-vr/albedo.frag" + ) +set(lava_pack_shaders "${lava_pack_shaders}" CACHE INTERNAL "lava_pack_shaders") + target_include_directories(lava-vr PUBLIC ./src) -target_link_libraries(lava-vr PUBLIC lava lava-extras-openvr lava-extras-pipeline lava-extras-geometry z) +target_link_libraries(lava-vr PUBLIC lava lava-extras-openvr lava-extras-pipeline lava-extras-geometry lava-extras-pack z) diff --git a/shaders/albedo.frag b/shaders/albedo.frag new file mode 100644 index 0000000000000000000000000000000000000000..4fd721b17b95d63661f7e7df04aff53a1f61b269 --- /dev/null +++ b/shaders/albedo.frag @@ -0,0 +1,15 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + + +layout (location = 0) in vec3 vNormal; +layout (location = 1) in vec2 vTexCoord; + +layout (location = 0) out vec4 fColor; + +layout (set = 1, binding = 0) uniform sampler2D uTexture; + +void main() { + fColor = texture(uTexture, vTexCoord * vec2(1, -1)) * (0.95 * normalize(vNormal).y + 0.05); +} diff --git a/shaders/albedo.vert b/shaders/albedo.vert new file mode 100644 index 0000000000000000000000000000000000000000..3988cfb1137d67c0f3222af87da85f6780e4882d --- /dev/null +++ b/shaders/albedo.vert @@ -0,0 +1,44 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +#extension GL_EXT_multiview : enable +#extension GL_NVX_multiview_per_view_attributes : enable + + +layout(location=0) in vec3 aPosition; +layout(location=1) in vec3 aNormal; +layout(location=2) in vec2 aTexCoord; + +layout (location = 0) out vec3 vNormal; +layout (location = 1) out vec2 vTexCoord; + +layout(push_constant) uniform PushConstants { + mat4 modelMatrix; +}; + + +layout(set = 0, binding = 0) uniform CameraMatrices { + mat4 view[2]; + mat4 proj[2]; +} cams; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() { + vTexCoord = aTexCoord; + vNormal = mat3(modelMatrix) * aNormal; + + //vec4 worldPos = modelMatrix * vec4(aPosition, 1.0); + vec4 worldPos = modelMatrix * vec4(aPosition, 1.0); + + gl_PositionPerViewNV[0] = + cams.proj[0] * cams.view[0] * worldPos; + gl_PositionPerViewNV[1] = + cams.proj[1] * cams.view[1] * worldPos; + gl_Position = cams.proj[gl_ViewIndex] + * cams.view[gl_ViewIndex] + * worldPos; +} diff --git a/shaders/vrmesh.frag b/shaders/vrmesh.frag new file mode 100644 index 0000000000000000000000000000000000000000..6704cfccaaef76e601db7e871a49bd9b2f71d78d --- /dev/null +++ b/shaders/vrmesh.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + + +layout (location = 0) in vec2 vTexCoord; +layout (location = 1) flat in uint vLayer; + +layout (location = 0) out vec4 fColor; + +layout (set = 1, binding = 0) uniform sampler2DArray uTexture; + +void main() { + fColor = texture(uTexture, vec3(vTexCoord,vLayer)); + if (fColor.a < 0.5) discard; + //if (pu.premultiplied && false) fColor.rgb /= fColor.a; +} diff --git a/shaders/vrmesh.vert b/shaders/vrmesh.vert new file mode 100644 index 0000000000000000000000000000000000000000..6c017ec336a703700e61336aa98fc70612c20258 --- /dev/null +++ b/shaders/vrmesh.vert @@ -0,0 +1,44 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +#extension GL_EXT_multiview : enable +#extension GL_NVX_multiview_per_view_attributes : enable + + +layout(location=0) in vec3 aPosition; +layout(location=1) in vec2 aTexCoord; +layout(location=2) in uint aLayer; + +layout (location = 0) out vec2 vTexCoord; +layout (location = 1) flat out uint vLayer; + +layout(push_constant) uniform PushConstants { + mat4 modelMatrix; +}; + + +layout(set = 0, binding = 0) uniform CameraMatrices { + mat4 view[2]; + mat4 proj[2]; +} cams; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() { + vTexCoord = aTexCoord; + vLayer = aLayer; + + //vec4 worldPos = modelMatrix * vec4(aPosition, 1.0); + vec4 worldPos = modelMatrix * vec4(aPosition, 1.0); + + gl_PositionPerViewNV[0] = + cams.proj[0] * cams.view[0] * worldPos; + gl_PositionPerViewNV[1] = + cams.proj[1] * cams.view[1] * worldPos; + gl_Position = cams.proj[gl_ViewIndex] + * cams.view[gl_ViewIndex] + * worldPos; +} diff --git a/src/lava-vr/BatchingRenderer.cc b/src/lava-vr/BatchingRenderer.cc index 4b17e8af3e9a5cea6ecbbf7df73de90bb30f0ca5..67f1b1af9fa5a3457541b6230c06cd5777150cf3 100644 --- a/src/lava-vr/BatchingRenderer.cc +++ b/src/lava-vr/BatchingRenderer.cc @@ -40,8 +40,8 @@ BatchingRenderer::BatchingRenderer( ci.depthStencilState.setDepthTestEnable(true).setDepthWriteEnable(true); ci.rasterizationState.setFrontFace(vk::FrontFace::eClockwise); - ci.addStage(lava::pack::shader(device, "albedo.vert")); - ci.addStage(lava::pack::shader(device, "albedo.frag")); + ci.addStage(lava::pack::shader(device, "lava-vr/albedo.vert")); + ci.addStage(lava::pack::shader(device, "lava-vr/albedo.frag")); ci.vertexInputState.addAttribute(&Vertex::pos, 0); ci.vertexInputState.addAttribute(&Vertex::normal, 1); diff --git a/src/lava-vr/VRMeshRenderer.cc b/src/lava-vr/VRMeshRenderer.cc new file mode 100644 index 0000000000000000000000000000000000000000..f4b937ee096ae89684822b43bade6808c4f71fd4 --- /dev/null +++ b/src/lava-vr/VRMeshRenderer.cc @@ -0,0 +1,167 @@ +#include "VRMeshRenderer.hh" +#include <glm/glm.hpp> +#include <lava-extras/pack/pack.hh> +#include <lava/common/log.hh> +#include <lava/createinfos/Buffers.hh> +#include <lava/createinfos/DescriptorSetLayoutCreateInfo.hh> +#include <lava/createinfos/GraphicsPipelineCreateInfo.hh> +#include <lava/createinfos/Images.hh> +#include <lava/createinfos/Sampler.hh> +#include <lava/objects/Buffer.hh> +#include <lava/objects/DescriptorSet.hh> +#include <lava/objects/DescriptorSetLayout.hh> +#include <lava/objects/Device.hh> +#include <lava/objects/GraphicsPipeline.hh> +#include <lava/objects/Image.hh> +#include <lava/objects/RenderPass.hh> +#include <lava/raii/ActiveRenderPass.hh> + +#include "VRMesh.hh" + +namespace lava { +namespace vr { + +namespace { +using Vertex = VRMesh::Vertex; + +struct OffsetHelper { + size_t width, height, depth, bytes_per_pixel, mip_layers; + + ptrdiff_t getOffset(size_t layer, size_t mip) { + ptrdiff_t result = 0; + for (size_t i = 0; i < mip; i++) { + size_t block = + (width >> i) * (height >> i) * depth * bytes_per_pixel; + result += block; + } + + size_t partial = + (width >> mip) * (height >> mip) * layer * bytes_per_pixel; + result += partial; + + return result; + } + + size_t bytesPerLayer(size_t mip) { + return (width >> mip) * (height >> mip) * bytes_per_pixel; + } +}; +} // namespace + +VRMeshRenderer::VRMeshRenderer( + const lava::Subpass &forwardPass, + const lava::SharedDescriptorSetLayout &cameraLayout) { + auto device = forwardPass.pass->device(); + + mTextureLayout = lava::DescriptorSetLayoutCreateInfo() // + .addCombinedImageSampler() + .create(device); + mLayout = + device->createPipelineLayout<glm::mat4>({cameraLayout, mTextureLayout}); + + auto ci = lava::GraphicsPipelineCreateInfo::defaults(); + ci.setLayout(mLayout); + + ci.depthStencilState.setDepthTestEnable(true).setDepthWriteEnable(true); + ci.rasterizationState.setFrontFace(vk::FrontFace::eClockwise); + + ci.addStage(lava::pack::shader(device, "lava-vr/vrmesh.vert")); + ci.addStage(lava::pack::shader(device, "lava-vr/vrmesh.frag")); + + ci.vertexInputState.addAttribute(&Vertex::position, 0); + ci.vertexInputState.addAttribute(&Vertex::texCoord, 1); + ci.vertexInputState.addAttribute(&Vertex::layer, 2); + + mPipeline = forwardPass.createPipeline(ci); + + mSampler = device->createSampler({}); +} + +void VRMeshRenderer::loadFile(const std::string &filename) { + auto mesh = VRMesh::readFromFile(filename); + upload(mesh); +} + +void VRMeshRenderer::upload(const VRMesh &mesh) { + auto const &device = mPipeline->device(); + + lava::debug() << "Loaded mesh with " << mesh.indices.size() / 3u + << " triangles:"; + + mVertexBuffer = device->createBuffer(lava::arrayBuffer()); + mVertexBuffer->setDataVRAM(mesh.vertices); + + mIndexBuffer = device->createBuffer(lava::indexBuffer()); + mIndexBuffer->setDataVRAM(mesh.indices); + + { + auto meta = mesh.colorTexture.meta; + + mTexture = lava::texture2DArray(meta.width, meta.height, meta.depth, + vk::Format::eBc7SrgbBlock) + .setMipLevels(meta.mipLayers) + .create(device); + + mTexture->realizeVRAM(); + mTexture->changeLayout(vk::ImageLayout::eTransferDstOptimal); + + auto staging = device->createBuffer(lava::stagingBuffer()); + staging->setDataRAM(mesh.colorTexture.data); + + std::vector<vk::BufferImageCopy> copies; + + OffsetHelper helper; + helper.width = meta.width; + helper.height = meta.height; + helper.depth = meta.depth; + helper.bytes_per_pixel = meta.bytes_per_pixel; + helper.mip_layers = meta.mipLayers; + + for (uint32_t mip = 0; mip < meta.mipLayers; mip++) { + for (uint32_t layer = 0; layer < meta.depth; layer++) { + vk::ImageSubresourceLayers sub; + sub.setAspectMask(vk::ImageAspectFlagBits::eColor) + .setMipLevel(mip) + .setBaseArrayLayer(layer) + .setLayerCount(1); + + vk::Extent3D extent; + extent.setWidth(meta.width >> mip) + .setHeight(meta.height >> mip) + .setDepth(1); + + vk::BufferImageCopy copy; + copy.setImageSubresource(sub) + .setImageExtent(extent) + .setBufferOffset(helper.getOffset(layer, mip)); + + copies.push_back(copy); + } + } + + auto cmd = device->graphicsQueue().beginCommandBuffer(); + cmd->copyBufferToImage(staging->handle(), mTexture->handle(), + vk::ImageLayout::eTransferDstOptimal, copies); + mTexture->changeLayout(vk::ImageLayout::eTransferDstOptimal, + vk::ImageLayout::eShaderReadOnlyOptimal, cmd); + } + + mTextureSet = mTextureLayout->createDescriptorSet(); + mTextureSet->write().combinedImageSampler(mSampler, mTexture->createView()); +} + +void VRMeshRenderer::draw(lava::InlineSubpass &sub, + const lava::SharedDescriptorSet &cameraSet) { + sub.bindPipeline(mPipeline); + + sub.bindDescriptorSets({cameraSet, mTextureSet}); + sub.bindVertexBuffers({mVertexBuffer}); + sub.bindIndexBuffer(mIndexBuffer); + + sub.pushConstantBlock(mModelMatrix); + + sub.drawIndexed(mIndexBuffer->size() / sizeof(uint32_t)); +} + +} // namespace vr +} // namespace lava diff --git a/src/lava-vr/VRMeshRenderer.hh b/src/lava-vr/VRMeshRenderer.hh new file mode 100644 index 0000000000000000000000000000000000000000..87fb25cf6245170c387663a41c491956be70f485 --- /dev/null +++ b/src/lava-vr/VRMeshRenderer.hh @@ -0,0 +1,37 @@ +#pragma once +#include "VRMesh.hh" +#include <glm/mat4x4.hpp> +#include <lava/fwd.hh> + +namespace lava { +namespace vr { + +class VRMeshRenderer { + public: + VRMeshRenderer(lava::Subpass const &forwardPass, + lava::SharedDescriptorSetLayout const &cameraLayout); + void loadFile(std::string const &filename); + void upload(VRMesh const &mesh); + + void draw(lava::InlineSubpass &sub, + lava::SharedDescriptorSet const &cameraSet); + + void setTransform(glm::mat4 const &t) { mModelMatrix = t; } + glm::mat4 const &getTransform() const { return mModelMatrix; } + + protected: + glm::mat4 mModelMatrix; + + lava::SharedGraphicsPipeline mPipeline; + lava::SharedPipelineLayout mLayout; + + lava::SharedBuffer mVertexBuffer; + lava::SharedBuffer mIndexBuffer; + lava::SharedImage mTexture; + + lava::SharedDescriptorSetLayout mTextureLayout; + lava::SharedDescriptorSet mTextureSet; + lava::SharedSampler mSampler; +}; +} // namespace vr +} // namespace lava