diff --git a/projects/mesh_shader/resources/shaders/meshlet.inc b/projects/mesh_shader/resources/shaders/meshlet.inc new file mode 100644 index 0000000000000000000000000000000000000000..0594f62ceead8ffca09b585305075eb6046f3c46 --- /dev/null +++ b/projects/mesh_shader/resources/shaders/meshlet.inc @@ -0,0 +1,8 @@ +struct Meshlet{ + uint vertexOffset; + uint vertexCount; + uint indexOffset; + uint indexCount; + vec3 meanPosition; + float boundingSphereRadius; +}; \ No newline at end of file diff --git a/projects/mesh_shader/resources/shaders/shader.mesh b/projects/mesh_shader/resources/shaders/shader.mesh index 97e73cbddb3ed222ef44b72ba4275ffa1c58ff6d..30c98610f4776204ff526c57c1f793e371194629 100644 --- a/projects/mesh_shader/resources/shaders/shader.mesh +++ b/projects/mesh_shader/resources/shaders/shader.mesh @@ -1,16 +1,15 @@ #version 460 #extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable #extension GL_NV_mesh_shader : require +#include "meshlet.inc" + layout(local_size_x=32) in; layout(triangles) out; layout(max_vertices=64, max_primitives=126) out; -layout( push_constant ) uniform constants{ - mat4 mvp; -}; - layout(location = 0) out vec3 passNormal[]; layout(location = 1) out uint passTaskIndex[]; @@ -30,15 +29,6 @@ layout(std430, binding = 1) readonly buffer indexBuffer uint localIndices[]; // breaks for 16 bit indices }; -struct Meshlet{ - uint vertexOffset; - uint vertexCount; - uint indexOffset; - uint indexCount; - vec3 meanPosition; - float boundingSphereRadius; -}; - layout(std430, binding = 2) readonly buffer meshletBuffer { Meshlet meshlets[]; @@ -46,6 +36,7 @@ layout(std430, binding = 2) readonly buffer meshletBuffer taskNV in Task { uint meshletIndices[32]; + mat4 mvp; } IN; void main() { @@ -64,7 +55,7 @@ void main() { uint vertexIndex = meshlet.vertexOffset + workIndex; Vertex vertex = vertices[vertexIndex]; - gl_MeshVerticesNV[workIndex].gl_Position = mvp * vec4(vertex.position, 1); + gl_MeshVerticesNV[workIndex].gl_Position = IN.mvp * vec4(vertex.position, 1); passNormal[workIndex] = vertex.normal; passTaskIndex[workIndex] = meshletIndex; } diff --git a/projects/mesh_shader/resources/shaders/shader.task b/projects/mesh_shader/resources/shaders/shader.task index 0ac1169eca1507eb49231a7174530631fa2e8ecd..3f666fbbe6e4031ff2063171992c375301e45c7e 100644 --- a/projects/mesh_shader/resources/shaders/shader.task +++ b/projects/mesh_shader/resources/shaders/shader.task @@ -1,24 +1,79 @@ #version 460 #extension GL_ARB_separate_shader_objects : enable #extension GL_NV_mesh_shader : require +#extension GL_GOOGLE_include_directive : enable + +#include "meshlet.inc" layout(local_size_x=32) in; taskNV out Task { uint meshletIndices[32]; + mat4 mvp; } OUT; layout( push_constant ) uniform constants{ - mat4 mvp; uint meshletCount; + uint matrixIndex; +}; + +layout(std430, binding = 2) readonly buffer meshletBuffer +{ + Meshlet meshlets[]; +}; + +struct Plane{ + vec3 pointOnPlane; + float padding0; + vec3 normal; + float padding1; +}; + +layout(set=0, binding=3, std140) uniform cameraPlaneBuffer{ + Plane cameraPlanes[6]; +}; + +struct ObjectMatrices{ + mat4 model; + mat4 mvp; }; +layout(std430, binding = 4) readonly buffer matrixBuffer +{ + ObjectMatrices objectMatrices[]; +}; + +shared uint taskCount; + +bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius, Plane cameraPlanes[6]){ + bool isInside = true; + for(int i = 0; i < 6; i++){ + Plane p = cameraPlanes[i]; + isInside = isInside && dot(p.normal, spherePos - p.pointOnPlane) + sphereRadius < 0; + } + return isInside; +} + void main() { + + if(gl_LocalInvocationID.x >= meshletCount){ + return; + } + + uint meshletIndex = gl_GlobalInvocationID.x; + Meshlet meshlet = meshlets[meshletIndex]; + + if(gl_LocalInvocationID.x == 0){ + taskCount = 0; + } + + if(isSphereInsideFrustum(meshlet.meanPosition, meshlet.boundingSphereRadius, cameraPlanes)){ + uint outIndex = atomicAdd(taskCount, 1); + OUT.meshletIndices[outIndex] = gl_GlobalInvocationID.x; + } + if(gl_LocalInvocationID.x == 0){ - int taskCount = int(gl_WorkGroupID.x * 32); - // use signed ints to avoid underflow - int superflousTaskCount = max(taskCount - int(meshletCount), 0); - gl_TaskCountNV = 32 - superflousTaskCount; + gl_TaskCountNV = taskCount; + OUT.mvp = objectMatrices[matrixIndex].mvp; } - OUT.meshletIndices[gl_LocalInvocationID.x] = gl_GlobalInvocationID.x; } \ No newline at end of file diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp index 37855e9b10d5c19a1c91ac66e60196826de0ed23..d08564f3f85eb34b08945fd2039ab308dbfc7f0e 100644 --- a/projects/mesh_shader/src/main.cpp +++ b/projects/mesh_shader/src/main.cpp @@ -10,6 +10,70 @@ #include <vkcv/meshlet/Meshlet.hpp> //#include <vkcv/meshlet/Tipsify.hpp> +struct Plane { + glm::vec3 pointOnPlane; + float padding0; + glm::vec3 normal; + float padding1; +}; + +struct CameraPlanes { + Plane planes[6]; +}; + +CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) { + const float fov = camera.getFov(); + const glm::vec3 pos = camera.getPosition(); + const float ratio = camera.getRatio(); + const glm::vec3 forward = glm::normalize(camera.getFront()); + float near; + float far; + camera.getNearFar(near, far); + + glm::vec3 up = glm::dot(forward, glm::vec3(0, -1, 0)) < 0.99 ? glm::vec3(0, -1, 0) : glm::vec3(1, 0, 0); + glm::vec3 right = glm::normalize(glm::cross(forward, up)); + up = glm::cross(forward, right); + + const glm::vec3 nearCenter = pos + forward * near; + const glm::vec3 farCenter = pos + forward * far; + + const float tanFovHalf = glm::tan(fov / 2); + + const glm::vec3 nearUpCenter = nearCenter + up * tanFovHalf * near; + const glm::vec3 nearDownCenter = nearCenter - up * tanFovHalf * near; + const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio; + const glm::vec3 nearLeftCenter = nearCenter - right * tanFovHalf * near * ratio; + + const glm::vec3 farUpCenter = farCenter + up * tanFovHalf * far; + const glm::vec3 farDownCenter = farCenter - up * tanFovHalf * far; + const glm::vec3 farRightCenter = farCenter + right * tanFovHalf * far * ratio; + const glm::vec3 farLeftCenter = farCenter - right * tanFovHalf * far * ratio; + + CameraPlanes cameraPlanes; + // near + cameraPlanes.planes[0].pointOnPlane = nearCenter; + cameraPlanes.planes[0].normal = -forward; + // far + cameraPlanes.planes[1].pointOnPlane = farCenter; + cameraPlanes.planes[1].normal = forward; + + // top + cameraPlanes.planes[2].pointOnPlane = nearUpCenter; + cameraPlanes.planes[2].normal = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right)); + // bot + cameraPlanes.planes[3].pointOnPlane = nearDownCenter; + cameraPlanes.planes[3].normal = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter)); + + // right + cameraPlanes.planes[4].pointOnPlane = nearRightCenter; + cameraPlanes.planes[4].normal = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter)); + // left + cameraPlanes.planes[5].pointOnPlane = nearLeftCenter; + cameraPlanes.planes[5].normal = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up)); + + return cameraPlanes; +} + int main(int argc, const char** argv) { const char* applicationName = "Mesh shader"; @@ -194,11 +258,25 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = core.createBuffer<CameraPlanes>(vkcv::BufferType::UNIFORM, 1); + + struct ObjectMatrices { + glm::mat4 model; + glm::mat4 mvp; + }; + const size_t objectCount = 1; + vkcv::Buffer<ObjectMatrices> matrixBuffer = core.createBuffer<ObjectMatrices>(vkcv::BufferType::STORAGE, objectCount); + vkcv::DescriptorWrites meshShaderWrites; meshShaderWrites.storageBufferWrites = { - vkcv::StorageBufferDescriptorWrite(0, meshShaderVertexBuffer.getHandle()), + vkcv::StorageBufferDescriptorWrite(0, meshShaderVertexBuffer.getHandle()), vkcv::StorageBufferDescriptorWrite(1, meshShaderIndexBuffer.getHandle()), - vkcv::StorageBufferDescriptorWrite(2, meshletBuffer.getHandle()) }; + vkcv::StorageBufferDescriptorWrite(2, meshletBuffer.getHandle()), + vkcv::StorageBufferDescriptorWrite(4, matrixBuffer.getHandle()) + }; + meshShaderWrites.uniformBufferWrites = { + vkcv::UniformBufferDescriptorWrite(3, cameraPlaneBuffer.getHandle()) + }; core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites); @@ -212,14 +290,14 @@ int main(int argc, const char** argv) { const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + vkcv::camera::CameraManager cameraManager(window); + uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2)); while (window.isWindowOpen()) { - vkcv::Window::pollEvents(); + vkcv::Window::pollEvents(); uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem if (!core.beginFrame(swapchainWidth, swapchainHeight)) { @@ -232,14 +310,22 @@ int main(int argc, const char** argv) { cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - glm::mat4 modelMatrix = *reinterpret_cast<glm::mat4*>(&mesh.meshes.front().modelMatrix); - glm::mat4 mvp = cameraManager.getActiveCamera().getMVP() * modelMatrix; + const vkcv::camera::Camera& camera = cameraManager.getActiveCamera(); + + ObjectMatrices objectMatrices; + objectMatrices.model = *reinterpret_cast<glm::mat4*>(&mesh.meshes.front().modelMatrix); + objectMatrices.mvp = camera.getMVP() * objectMatrices.model; + + matrixBuffer.fill({ objectMatrices }); struct PushConstants { - glm::mat4 mvp; uint32_t meshletCount; + uint32_t matrixIndex; }; - PushConstants pushConstants{ mvp, meshShaderModelData.meshlets.size() }; + PushConstants pushConstants{ meshShaderModelData.meshlets.size(), 0 }; + + const CameraPlanes cameraPlanes = computeCameraPlanes(camera); + cameraPlaneBuffer.fill({ cameraPlanes }); const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);