diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89942d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,560 @@ +*.orig +*.filters +*.sln +*.vcxproj +*.xcodeproj +build + +# Created by https://www.gitignore.io/api/linux,osx,sublimetext,windows,jetbrains,vim,emacs,cmake,c++,cuda,visualstudio,webstorm,eclipse,xcode + +### Linux ### +*~ + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### SublimeText ### +# cache files for sublime text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# workspace files are user-specific +*.sublime-workspace + +# project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using SublimeText +# *.sublime-project + +# sftp configuration file +sftp-config.json + + +### Windows ### +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +#.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + +### Vim ### +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ + + +### CMake ### +CMakeCache.txt +CMakeFiles +CMakeScripts +Makefile +cmake_install.cmake +install_manifest.txt + + +### C++ ### +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + + +### CUDA ### +*.i +*.ii +*.gpu +*.ptx +*.cubin +*.fatbin + + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +#.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +#*.launch + +# CDT-specific +#.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + +### Xcode ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate diff --git a/README.md b/README.md index 20ee451..9d114c0 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,50 @@ Vulkan Grass Rendering **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Licheng CAO + * [LinkedIn](https://www.linkedin.com/in/licheng-cao-6a523524b/) +* Tested on: Windows 10, i7-10870H @ 2.20GHz 32GB, GTX 3060 6009MB -### (TODO: Your README) +Result +==== + * This project is implemented based on [Responsive Real-Time Grass Rendering for General 3D Scenes](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf). +

+ +

-*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. +Features +=== +### Blade Geometry + * The [paper](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf) presented 4 kinds of blade shapes as shown. I picked triangle-tip as my basic blade geometry. +

+ +

+ +### Force Simulation + * In the [paper](https://www.cg.tuwien.ac.at/research/publications/2017/JAHRMANN-2017-RRTG/JAHRMANN-2017-RRTG-draft.pdf), The authors constructed a physical model to simulate grass, as depicted below. According to the paper, the parameters required to simulate grass include `v0`, `v1`, `v2`, `up`, `height`, `direction`, `width`, and `stiffness`. The first four values are of type `vec3`, while the subsequent values are simply of type `float`. In my implementation, I passed four `vec4` values to the shader, with the float value stored in `vec4.w`. This application simulates three types of forces: `gravity`, `wind force`, and `recover force`. The `recover force` characterizes the force that restores the grass blade to its equilibrium position and is computed using the parameter stiffness. +

+ +

+ +### Culling +* To enhance performance, the paper also introduced several culling methods aimed at eliminating unnecessary blades, thereby reducing the overall number of shapes that need to be rendered. + +|Orientation Culling|Frustum Culling|Distance Culling| +|:--:|:--:|:--:| +|||| + +Performance +=== + * As expected, the average frames per second (FPS) decreases as the number of blades increases. However, as depicted in the figure, the FPS remains nearly constant when the number of blades is below 2^13, highlighting the efficiency of the algorithm. +

+ +

+ +* I recorded the average FPS with 2^18 blades, and the result is shown below. The average fps indicates that all culling methods offer some improvement for rendering. With my test scene, the distance culling contributes the most to fps, the reason might be that I picked a relatively small max distance for culling test, so that many blades are culled. Frustum culling gives the least improvement, this may due to the fact that I tried to record every blades in the output so most blades fall in the frustum and therefore are not culled. + +|culling method applied|all cullings|orientation culling|frustum culling|distance culling|none| +|:--:|:--:|:--:|:--:|:--:|:--:| +|average fps(higher better)|150.5|94.1|79.7|98.4|65.3| +

+ +

diff --git a/bin/Release/vulkan_grass_rendering.exe b/bin/Release/vulkan_grass_rendering.exe index f68db3a..c3d45a9 100644 Binary files a/bin/Release/vulkan_grass_rendering.exe and b/bin/Release/vulkan_grass_rendering.exe differ diff --git a/mdassets/distCull.gif b/mdassets/distCull.gif new file mode 100644 index 0000000..cd746aa Binary files /dev/null and b/mdassets/distCull.gif differ diff --git a/mdassets/fpscull.PNG b/mdassets/fpscull.PNG new file mode 100644 index 0000000..74bff3f Binary files /dev/null and b/mdassets/fpscull.PNG differ diff --git a/mdassets/fpsnum.PNG b/mdassets/fpsnum.PNG new file mode 100644 index 0000000..71f9a2f Binary files /dev/null and b/mdassets/fpsnum.PNG differ diff --git a/mdassets/frusCull.gif b/mdassets/frusCull.gif new file mode 100644 index 0000000..a7b7f4f Binary files /dev/null and b/mdassets/frusCull.gif differ diff --git a/mdassets/geom.PNG b/mdassets/geom.PNG new file mode 100644 index 0000000..e336fb4 Binary files /dev/null and b/mdassets/geom.PNG differ diff --git a/mdassets/oriCull.gif b/mdassets/oriCull.gif new file mode 100644 index 0000000..056164b Binary files /dev/null and b/mdassets/oriCull.gif differ diff --git a/mdassets/phy.PNG b/mdassets/phy.PNG new file mode 100644 index 0000000..3637b90 Binary files /dev/null and b/mdassets/phy.PNG differ diff --git a/mdassets/result.gif b/mdassets/result.gif new file mode 100644 index 0000000..463adbf Binary files /dev/null and b/mdassets/result.gif differ diff --git a/src/Blades.cpp b/src/Blades.cpp index 80e3d76..0142372 100644 --- a/src/Blades.cpp +++ b/src/Blades.cpp @@ -45,7 +45,7 @@ Blades::Blades(Device* device, VkCommandPool commandPool, float planeDim) : Mode indirectDraw.firstInstance = 0; BufferUtils::CreateBufferFromData(device, commandPool, blades.data(), NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, bladesBuffer, bladesBufferMemory); - BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); + BufferUtils::CreateBuffer(device, NUM_BLADES * sizeof(Blade), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, culledBladesBuffer, culledBladesBufferMemory); BufferUtils::CreateBufferFromData(device, commandPool, &indirectDraw, sizeof(BladeDrawIndirect), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, numBladesBuffer, numBladesBufferMemory); } diff --git a/src/Camera.cpp b/src/Camera.cpp index 3afb5b8..8c60461 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -8,13 +8,41 @@ #include "Camera.h" #include "BufferUtils.h" +void Camera::updateAttrb() +{ + forward = glm::normalize(ref - eye); + right = glm::cross(forward, worldUp); + up = glm::cross(right, forward); +} + +glm::mat4 Camera::getViewMatrix() +{ + glm::mat4 rot(glm::vec4(right,0), glm::vec4(up,0), glm::vec4(-forward,0), glm::vec4(0, 0, 0, 1)); + rot = glm::transpose(rot); + glm::mat4 trans(glm::vec4(1,0,0,0), glm::vec4(0,1,0,0), glm::vec4(0,0,1,0), glm::vec4(-eye, 1)); + glm::mat4 res = rot * trans; + return res; +} + Camera::Camera(Device* device, float aspectRatio) : device(device) { r = 10.0f; theta = 0.0f; phi = 0.0f; - cameraBufferObject.viewMatrix = glm::lookAt(glm::vec3(0.0f, 1.0f, 10.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + eye = glm::vec3(0, 1, r); + ref = glm::vec3(0,1,0); + updateAttrb(); + cameraBufferObject.forward = glm::vec4(forward,0); + cameraBufferObject.position = glm::vec4(eye,1); + cameraBufferObject.viewMatrix = getViewMatrix(); + //for (int i = 0;i < 4;++i) { + // for (int j = 0;j < 4;++j) { + // std::cout << cameraBufferObject.viewMatrix[j][i] << " "; + // } + // std::cout << std::endl; + //} cameraBufferObject.projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f); cameraBufferObject.projectionMatrix[1][1] *= -1; // y-coordinate is flipped + BufferUtils::CreateBuffer(device, sizeof(CameraBufferObject), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer, bufferMemory); vkMapMemory(device->GetVkDevice(), bufferMemory, 0, sizeof(CameraBufferObject), 0, &mappedData); @@ -33,10 +61,33 @@ void Camera::UpdateOrbit(float deltaX, float deltaY, float deltaZ) { float radTheta = glm::radians(theta); float radPhi = glm::radians(phi); - glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radTheta, glm::vec3(0.0f, 1.0f, 0.0f)) * glm::rotate(glm::mat4(1.0f), radPhi, glm::vec3(1.0f, 0.0f, 0.0f)); - glm::mat4 finalTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f)) * rotation * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, r)); + //glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), radTheta, glm::vec3(0.0f, 1.0f, 0.0f)) * glm::rotate(glm::mat4(1.0f), radPhi, glm::vec3(1.0f, 0.0f, 0.0f)); + //glm::mat4 finalTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f)) + // * rotation + // * glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, r)); + float cosPhi = glm::cos(radPhi); + float sinPhi = glm::sin(radPhi); + float sinTheta = glm::sin(radTheta); + float cosTheta = glm::cos(radTheta); + eye = ref - glm::vec3(cosPhi * sinTheta, sinPhi, cosPhi * cosTheta) * r; + updateAttrb(); + cameraBufferObject.forward = glm::vec4(forward, 0); + cameraBufferObject.position = glm::vec4(eye, 1); + cameraBufferObject.viewMatrix = getViewMatrix();//glm::inverse(finalTransform); + + memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject)); +} - cameraBufferObject.viewMatrix = glm::inverse(finalTransform); +void Camera::UpdatePosition(float fwd, float rt) +{ + glm::vec3 diff = glm::normalize(glm::vec3(forward.x, 0, forward.z)) * fwd + glm::normalize(glm::vec3(right.x, 0, right.z))*rt; + eye += diff; + ref += diff; + cameraBufferObject.forward = glm::vec4(forward,0); + cameraBufferObject.position = glm::vec4(eye, 1); + cameraBufferObject.viewMatrix = getViewMatrix(); + //std::cout << "act" << forward[0] << ", " << forward[1] << ", " << forward[2] << std::endl; + //std::cout << "mat" << cameraBufferObject.viewMatrix[0][2] << ", " << cameraBufferObject.viewMatrix[1][2] << ", " << cameraBufferObject.viewMatrix[2][2] << std::endl; memcpy(mappedData, &cameraBufferObject, sizeof(CameraBufferObject)); } diff --git a/src/Camera.h b/src/Camera.h index 6b10747..0c7bf76 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -5,8 +5,10 @@ #include "Device.h" struct CameraBufferObject { - glm::mat4 viewMatrix; - glm::mat4 projectionMatrix; + glm::vec4 forward;//add + glm::vec4 position; + glm::mat4 viewMatrix; + glm::mat4 projectionMatrix; }; class Camera { @@ -21,7 +23,15 @@ class Camera { void* mappedData; float r, theta, phi; - + glm::vec3 eye; + glm::vec3 ref; + glm::vec3 forward; + glm::vec3 right; + glm::vec3 up; + glm::vec3 worldUp = glm::vec3(0,1, 0); + + void updateAttrb(); + glm::mat4 getViewMatrix(); public: Camera(Device* device, float aspectRatio); ~Camera(); @@ -29,4 +39,5 @@ class Camera { VkBuffer GetBuffer() const; void UpdateOrbit(float deltaX, float deltaY, float deltaZ); + void UpdatePosition(float fwd, float rt); }; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b445d04..a32bf92 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -122,6 +122,10 @@ void Renderer::CreateRenderPass() { } } +//********************************************** +// Create DescriptorSetLayout, which is used in +// CreateDescriptorSet(), CreatePipeline() +//********************************************** void Renderer::CreateCameraDescriptorSetLayout() { // Describe the binding of the descriptor set layout VkDescriptorSetLayoutBinding uboLayoutBinding = {}; @@ -193,13 +197,43 @@ void Renderer::CreateTimeDescriptorSetLayout() { throw std::runtime_error("Failed to create descriptor set layout"); } } - +//TODO: ok void Renderer::CreateComputeDescriptorSetLayout() { // TODO: Create the descriptor set layout for the compute pipeline // Remember this is like a class definition stating why types of information // will be stored at each binding -} + VkDescriptorSetLayoutBinding bladesLayoutBinding = {}; + bladesLayoutBinding.binding = 0; + bladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bladesLayoutBinding.descriptorCount = 1; + bladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bladesLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding culledBladesLayoutBinding = {}; + culledBladesLayoutBinding.binding = 1; + culledBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + culledBladesLayoutBinding.descriptorCount = 1; + culledBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + culledBladesLayoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutBinding numBladesLayoutBinding = {}; + numBladesLayoutBinding.binding = 2; + numBladesLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + numBladesLayoutBinding.descriptorCount = 1; + numBladesLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + numBladesLayoutBinding.pImmutableSamplers = nullptr; + + std::vector bindings = { bladesLayoutBinding, culledBladesLayoutBinding, numBladesLayoutBinding }; + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, nullptr, &computeDescriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout"); + } +} +//TODO: ok? void Renderer::CreateDescriptorPool() { // Describe which descriptor types that the descriptor sets will contain std::vector poolSizes = { @@ -216,19 +250,33 @@ void Renderer::CreateDescriptorPool() { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 }, // TODO: Add any additional types and counts of descriptors you will need to allocate + // blades + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,static_cast(scene->GetBlades().size()) }, + + // culled blades + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,static_cast(scene->GetBlades().size()) }, + + // num blades + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,static_cast(scene->GetBlades().size()) }, }; VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = static_cast(poolSizes.size()); poolInfo.pPoolSizes = poolSizes.data(); - poolInfo.maxSets = 5; + //https://stackoverflow.com/questions/65540753/what-is-the-purpose-of-the-maximum-number-of-sets-in-a-vulkan-descriptor-set-poo + poolInfo.maxSets = poolSizes.size();//5; if (vkCreateDescriptorPool(logicalDevice, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { throw std::runtime_error("Failed to create descriptor pool"); } } + +//********************************************** +// Create DescriptorSet based on DescriptorSet Layout with Descriptor Pool +// Bind VkBuffer with DescriptorSet +//********************************************** void Renderer::CreateCameraDescriptorSet() { // Describe the desciptor set VkDescriptorSetLayout layouts[] = { cameraDescriptorSetLayout }; @@ -316,10 +364,48 @@ void Renderer::CreateModelDescriptorSets() { // Update descriptor sets vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } - +//TODO: ok void Renderer::CreateGrassDescriptorSets() { // TODO: Create Descriptor sets for the grass. // This should involve creating descriptor sets which point to the model matrix of each group of grass blades + int n = scene->GetBlades().size(); + + grassDescriptorSets.resize(n); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { modelDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(n); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, grassDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate compute descriptor set"); + } + + std::vector descriptorWrites(n); + + for (uint32_t i = 0; i < n; ++i) { + VkDescriptorBufferInfo bladeInfo = {}; + bladeInfo.buffer = scene->GetBlades()[i]->GetModelBuffer(); + bladeInfo.offset = 0; + bladeInfo.range = sizeof(ModelBufferObject); + + descriptorWrites[i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i + 0].dstSet = grassDescriptorSets[i]; + descriptorWrites[i + 0].dstBinding = 0; + descriptorWrites[i + 0].dstArrayElement = 0; + descriptorWrites[i + 0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[i + 0].descriptorCount = 1; + descriptorWrites[i + 0].pBufferInfo = &bladeInfo; + descriptorWrites[i + 0].pImageInfo = nullptr; + descriptorWrites[i + 0].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateTimeDescriptorSet() { @@ -356,10 +442,78 @@ void Renderer::CreateTimeDescriptorSet() { // Update descriptor sets vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } - +//TODO OK void Renderer::CreateComputeDescriptorSets() { // TODO: Create Descriptor sets for the compute pipeline // The descriptors should point to Storage buffers which will hold the grass blades, the culled grass blades, and the output number of grass blades + int n = scene->GetBlades().size(); + + computeDescriptorSets.resize(n); + + // Describe the desciptor set + VkDescriptorSetLayout layouts[] = { computeDescriptorSetLayout }; + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = static_cast(n); + allocInfo.pSetLayouts = layouts; + + // Allocate descriptor sets + if (vkAllocateDescriptorSets(logicalDevice, &allocInfo, computeDescriptorSets.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate compute descriptor set"); + } + + std::vector descriptorWrites(3 * n); + + for (uint32_t i = 0; i < n; ++i) { + VkDescriptorBufferInfo bladeInfo = {}; + bladeInfo.buffer = scene->GetBlades()[i]->GetBladesBuffer(); + bladeInfo.offset = 0; + bladeInfo.range = NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo culledBladeInfo = {}; + culledBladeInfo.buffer = scene->GetBlades()[i]->GetCulledBladesBuffer(); + culledBladeInfo.offset = 0; + culledBladeInfo.range = NUM_BLADES * sizeof(Blade); + + VkDescriptorBufferInfo numBladesInfo = {}; + numBladesInfo.buffer = scene->GetBlades()[i]->GetNumBladesBuffer(); + numBladesInfo.offset = 0; + numBladesInfo.range = sizeof(BladeDrawIndirect); + + descriptorWrites[3 * i + 0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 0].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 0].dstBinding = 0; + descriptorWrites[3 * i + 0].dstArrayElement = 0; + descriptorWrites[3 * i + 0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 0].descriptorCount = 1; + descriptorWrites[3 * i + 0].pBufferInfo = &bladeInfo; + descriptorWrites[3 * i + 0].pImageInfo = nullptr; + descriptorWrites[3 * i + 0].pTexelBufferView = nullptr; + + descriptorWrites[3 * i + 1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 1].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 1].dstBinding = 1; + descriptorWrites[3 * i + 1].dstArrayElement = 0; + descriptorWrites[3 * i + 1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 1].descriptorCount = 1; + descriptorWrites[3 * i + 1].pBufferInfo = &culledBladeInfo; + descriptorWrites[3 * i + 1].pImageInfo = nullptr; + descriptorWrites[3 * i + 1].pTexelBufferView = nullptr; + + descriptorWrites[3 * i + 2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[3 * i + 2].dstSet = computeDescriptorSets[i]; + descriptorWrites[3 * i + 2].dstBinding = 2; + descriptorWrites[3 * i + 2].dstArrayElement = 0; + descriptorWrites[3 * i + 2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrites[3 * i + 2].descriptorCount = 1; + descriptorWrites[3 * i + 2].pBufferInfo = &numBladesInfo; + descriptorWrites[3 * i + 2].pImageInfo = nullptr; + descriptorWrites[3 * i + 2].pTexelBufferView = nullptr; + } + + // Update descriptor sets + vkUpdateDescriptorSets(logicalDevice, static_cast(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr); } void Renderer::CreateGraphicsPipeline() { @@ -705,7 +859,7 @@ void Renderer::CreateGrassPipeline() { vkDestroyShaderModule(logicalDevice, teseShaderModule, nullptr); vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); } - +//TODO ok void Renderer::CreateComputePipeline() { // Set up programmable shaders VkShaderModule computeShaderModule = ShaderModule::Create("shaders/compute.comp.spv", logicalDevice); @@ -717,7 +871,7 @@ void Renderer::CreateComputePipeline() { computeShaderStageInfo.pName = "main"; // TODO: Add the compute dsecriptor set layout you create to this list - std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout }; + std::vector descriptorSetLayouts = { cameraDescriptorSetLayout, timeDescriptorSetLayout, computeDescriptorSetLayout }; // Create pipeline layout VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; @@ -749,6 +903,10 @@ void Renderer::CreateComputePipeline() { vkDestroyShaderModule(logicalDevice, computeShaderModule, nullptr); } +//********************************************** +// Create DescriptorSet based on DescriptorSet Layout with Descriptor Pool +// Bind VkBuffer with DescriptorSet +//********************************************** void Renderer::CreateFrameResources() { imageViews.resize(swapChain->GetCount()); @@ -851,7 +1009,7 @@ void Renderer::RecreateFrameResources() { CreateGrassPipeline(); RecordCommandBuffers(); } - +//TODO ok void Renderer::RecordComputeCommandBuffer() { // Specify the command pool and number of buffers to allocate VkCommandBufferAllocateInfo allocInfo = {}; @@ -884,13 +1042,19 @@ void Renderer::RecordComputeCommandBuffer() { vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 1, 1, &timeDescriptorSet, 0, nullptr); // TODO: For each group of blades bind its descriptor set and dispatch + int n = scene->GetBlades().size(); + for (int i = 0;i < n;++i) { + vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipelineLayout, 2, 1, &(computeDescriptorSets[i]), 0, nullptr); + int invokeNum = std::ceil( float(NUM_BLADES) / float(WORKGROUP_SIZE)); + vkCmdDispatch(computeCommandBuffer, invokeNum , 1, 1); + } // ~ End recording ~ if (vkEndCommandBuffer(computeCommandBuffer) != VK_SUCCESS) { throw std::runtime_error("Failed to record compute command buffer"); } } - +//TODO void Renderer::RecordCommandBuffers() { commandBuffers.resize(swapChain->GetCount()); @@ -972,17 +1136,20 @@ void Renderer::RecordCommandBuffers() { // Bind the grass pipeline vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, grassPipeline); + uint32_t modelCnt = scene->GetModels().size(); + for (uint32_t j = 0; j < scene->GetBlades().size(); ++j) { VkBuffer vertexBuffers[] = { scene->GetBlades()[j]->GetCulledBladesBuffer() }; VkDeviceSize offsets[] = { 0 }; // TODO: Uncomment this when the buffers are populated - // vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); + vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); // TODO: Bind the descriptor set for each grass blades model + vkCmdBindDescriptorSets(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 1, 1, &grassDescriptorSets[j], 0, nullptr); // Draw // TODO: Uncomment this when the buffers are populated - // vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect)); + vkCmdDrawIndirect(commandBuffers[i], scene->GetBlades()[j]->GetNumBladesBuffer(), 0, 1, sizeof(BladeDrawIndirect)); } // End render pass @@ -1037,7 +1204,7 @@ void Renderer::Frame() { RecreateFrameResources(); } } - +//TODO ok Renderer::~Renderer() { vkDeviceWaitIdle(logicalDevice); @@ -1057,6 +1224,7 @@ Renderer::~Renderer() { vkDestroyDescriptorSetLayout(logicalDevice, cameraDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, modelDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(logicalDevice, timeDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(logicalDevice, computeDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(logicalDevice, descriptorPool, nullptr); diff --git a/src/Renderer.h b/src/Renderer.h index 95e025f..fa963c9 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -56,12 +56,15 @@ class Renderer { VkDescriptorSetLayout cameraDescriptorSetLayout; VkDescriptorSetLayout modelDescriptorSetLayout; VkDescriptorSetLayout timeDescriptorSetLayout; + VkDescriptorSetLayout computeDescriptorSetLayout;//add VkDescriptorPool descriptorPool; VkDescriptorSet cameraDescriptorSet; std::vector modelDescriptorSets; VkDescriptorSet timeDescriptorSet; + std::vector< VkDescriptorSet> computeDescriptorSets;//add + std::vector grassDescriptorSets; //add VkPipelineLayout graphicsPipelineLayout; VkPipelineLayout grassPipelineLayout; diff --git a/src/Scene.h b/src/Scene.h index 7699d78..8df69e4 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -19,7 +19,7 @@ class Scene { VkBuffer timeBuffer; VkDeviceMemory timeBufferMemory; - Time time; + void* mappedData; @@ -32,7 +32,7 @@ high_resolution_clock::time_point startTime = high_resolution_clock::now(); Scene() = delete; Scene(Device* device); ~Scene(); - + Time time; const std::vector& GetModels() const; const std::vector& GetBlades() const; diff --git a/src/main.cpp b/src/main.cpp index 8bf822b..689976c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "Camera.h" #include "Scene.h" #include "Image.h" +#include Device* device; SwapChain* swapChain; @@ -143,12 +144,30 @@ int main() { glfwSetMouseButtonCallback(GetGLFWWindow(), mouseDownCallback); glfwSetCursorPosCallback(GetGLFWWindow(), mouseMoveCallback); + float frameCnt = 0; while (!ShouldQuit()) { glfwPollEvents(); scene->UpdateTime(); + { + float cameraSpeed = 5.f; + if (glfwGetKey(GetGLFWWindow(), GLFW_KEY_W) == GLFW_PRESS) { + camera->UpdatePosition(cameraSpeed * scene->time.deltaTime, 0); + } + if (glfwGetKey(GetGLFWWindow(), GLFW_KEY_S) == GLFW_PRESS) { + camera->UpdatePosition(-cameraSpeed * scene->time.deltaTime, 0); + } + if (glfwGetKey(GetGLFWWindow(), GLFW_KEY_A) == GLFW_PRESS) { + camera->UpdatePosition(0,-cameraSpeed * scene->time.deltaTime); + } + if (glfwGetKey(GetGLFWWindow(), GLFW_KEY_D) == GLFW_PRESS) { + camera->UpdatePosition(0,cameraSpeed * scene->time.deltaTime); + } + } + ++frameCnt; renderer->Frame(); } - + std::cout << "time pass: " << scene->time.totalTime << std::endl; + std::cout << "avg fps: " << frameCnt / scene->time.totalTime << std::endl; vkDeviceWaitIdle(device->GetVkDevice()); vkDestroyImage(device->GetVkDevice(), grassImage, nullptr); @@ -163,5 +182,6 @@ int main() { delete device; delete instance; DestroyWindow(); + system("pause"); return 0; } diff --git a/src/shaders/compute.comp b/src/shaders/compute.comp index 0fd0224..e075d66 100644 --- a/src/shaders/compute.comp +++ b/src/shaders/compute.comp @@ -2,9 +2,16 @@ #extension GL_ARB_separate_shader_objects : enable #define WORKGROUP_SIZE 32 +#define DISTANCE_LEVEL 4 +#define MAX_DISTANCE 25.0 +#define GRASS_MASS 0.1 +#define WIND_FORCE 0.2 + layout(local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in; layout(set = 0, binding = 0) uniform CameraBufferObject { + vec4 fwd; + vec4 pos; mat4 view; mat4 proj; } camera; @@ -12,7 +19,7 @@ layout(set = 0, binding = 0) uniform CameraBufferObject { layout(set = 1, binding = 0) uniform Time { float deltaTime; float totalTime; -}; +} time; struct Blade { vec4 v0; @@ -28,22 +35,120 @@ struct Blade { // The project is using vkCmdDrawIndirect to use a buffer as the arguments for a draw call // This is sort of an advanced feature so we've showed you what this buffer should look like -// -// layout(set = ???, binding = ???) buffer NumBlades { -// uint vertexCount; // Write the number of blades remaining here -// uint instanceCount; // = 1 -// uint firstVertex; // = 0 -// uint firstInstance; // = 0 -// } numBlades; + +layout(set = 2, binding = 0) buffer Blades { + Blade blades[]; +}; + +layout(set = 2, binding = 1) buffer CulledBlades { + Blade culledBlades[]; +}; + +layout(set = 2, binding = 2) buffer NumBlades { + uint vertexCount; // Write the number of blades remaining here + uint instanceCount; // = 1 + uint firstVertex; // = 0 + uint firstInstance; // = 0 +} numBlades; bool inBounds(float value, float bounds) { return (value >= -bounds) && (value <= bounds); } +// v0: the position of the grass blade on the geomtry +// v1: a Bezier curve guide that is always "above" v0 with respect to the grass blade's up vector (explained soon) +// v2: a physical guide for which we simulate forces on +// v0.w holds orientation, v1.w holds height, v2.w holds width, and up.w holds the stiffness coefficient. +bool orientationCull(Blade blade){ + float orientation = blade.v0.w; + vec3 dirB = vec3(sin(orientation), 0, cos(orientation)); + return abs(dot(dirB,camera.fwd.xyz)) > 0.9; +} +bool inFrustum(mat4 VP, vec4 pt){ + vec4 clipPt = VP * pt; + float h = clipPt.w + 0.02; + clipPt.z = (clipPt.z - 0.5 * clipPt.w) * 2;//current glm::perspective z is 0-1 + if( + (!inBounds(clipPt.x,h)) + ||(!inBounds(clipPt.y,h)) + ||(!inBounds(clipPt.z,h)) + ){ + return false; + } + return true; +} +bool frustumCull(Blade blade){ + vec4 v0 = vec4(blade.v0.xyz,1); + vec4 v2 = vec4(blade.v2.xyz,1); + vec4 m = vec4(0.25*blade.v0.xyz * 0.5*blade.v1.xyz * 0.25*blade.v2.xyz,1); + mat4 VP = camera.proj * camera.view; + if(!inFrustum(VP,v0) && !inFrustum(VP,v2) && !inFrustum(VP,m)){ + return true; + } + return false; +} +bool distanceCull(Blade blade, uint idx){ + vec3 v0 = blade.v0.xyz; + vec3 c = camera.pos.xyz; + vec3 up = blade.up.xyz; + float dproj = length(v0 - c - up * dot(v0-c,up)); + int cmp = int(floor(DISTANCE_LEVEL * (1.0 - (dproj/MAX_DISTANCE)))); + return mod(idx, DISTANCE_LEVEL) > cmp; +} + +void physicalSimulation(inout Blade blade){ + vec3 v0 = blade.v0.xyz; + vec3 v1 = blade.v1.xyz; + vec3 v2 = blade.v2.xyz; + vec3 up = blade.up.xyz; + float s = blade.up.w;//stiffness + float h = blade.v1.w; + float orientation = blade.v0.w; + vec4 horizontal = vec4(sin(orientation), 0, cos(orientation), 0);//along width direction + vec3 f = cross(horizontal.xyz, vec3(0,1,0)); + + //float collsionStrength; + vec3 Iv2 = v0 + up*h; + vec3 r = (Iv2 - v2) * s * 0.1; + vec3 gE = vec3(0,-9.8,0) * GRASS_MASS; + vec3 gF = 0.25 * 9.8 * GRASS_MASS * f; + vec3 g = gE + gF; + + vec3 wi = WIND_FORCE * vec3(1 + sin(time.totalTime + v0.x),0,0);//wi(v0) + float fd = dot(wi,wi) > 0 ?1 - abs(dot(normalize(wi),normalize(v2-v0))) : 1; + float fr = dot(v2 - v0,up)/h; + vec3 w = fd * fr * wi; + + //apply force + vec3 segma = (r + g + w) * time.deltaTime; + v2 = v2 + segma; + + v2 = v2 - up * min(dot(up,v2-v0),0); + float lproj = length(v2 - v0 - up*dot(v2-v0,up)); + v1 = v0 + h * up * max(1 - lproj/h, 0.05 * max(lproj/h,1.0)); + + float L0 = distance(v0,v2); + float L1 = L0 + distance(v1,v2); + float L = (2*L0 + (2-1)*L1)/(2+1); + float rc = h/L; + vec3 v1c = v0 + rc * (v1 - v0); + v2 = v1c + rc *(v2 - v1); + v1 = v1c; + + blade.v1 = vec4(v1, blade.v1.w); + blade.v2 = vec4(v2, blade.v2.w); +} + void main() { // Reset the number of blades to 0 + uint threadInOneGroup = gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z; + uint groupOffset = + gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupID.z + + gl_NumWorkGroups.x * gl_WorkGroupID.y + + gl_WorkGroupID.x; + uint idx = gl_LocalInvocationIndex + groupOffset * threadInOneGroup; if (gl_GlobalInvocationID.x == 0) { - // numBlades.vertexCount = 0; + numBlades.vertexCount = 0; } barrier(); // Wait till all threads reach this point @@ -53,4 +158,17 @@ void main() { // to the culled blades buffer // Note: to do this, you will need to use an atomic operation to read and update numBlades.vertexCount // You want to write the visible blades to the buffer without write conflicts between threads + Blade currentBlade = blades[idx]; + physicalSimulation(currentBlade); + blades[idx] = currentBlade; + bool culled = false + || orientationCull(currentBlade) + || frustumCull(currentBlade) + || distanceCull(currentBlade, idx) + ; + + if(!culled){ + uint dstIdx = atomicAdd(numBlades.vertexCount,1); + culledBlades[dstIdx] = currentBlade; + } } diff --git a/src/shaders/graphics.vert b/src/shaders/graphics.vert index fb9bf8e..5027215 100644 --- a/src/shaders/graphics.vert +++ b/src/shaders/graphics.vert @@ -2,6 +2,8 @@ #extension GL_ARB_separate_shader_objects : enable layout(set = 0, binding = 0) uniform CameraBufferObject { + vec4 fwd; + vec4 pos; mat4 view; mat4 proj; } camera; diff --git a/src/shaders/grass.frag b/src/shaders/grass.frag index c7df157..0118e97 100644 --- a/src/shaders/grass.frag +++ b/src/shaders/grass.frag @@ -2,16 +2,18 @@ #extension GL_ARB_separate_shader_objects : enable layout(set = 0, binding = 0) uniform CameraBufferObject { + vec4 fwd; + vec4 pos; mat4 view; mat4 proj; } camera; // TODO: Declare fragment shader inputs +layout(location = 0) in vec3 inColor; layout(location = 0) out vec4 outColor; void main() { // TODO: Compute fragment color - - outColor = vec4(1.0); + outColor = vec4(inColor,1.0); } diff --git a/src/shaders/grass.tesc b/src/shaders/grass.tesc index f9ffd07..6ab1bf2 100644 --- a/src/shaders/grass.tesc +++ b/src/shaders/grass.tesc @@ -1,26 +1,42 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#define TENSSCOUNT 5.0 layout(vertices = 1) out; layout(set = 0, binding = 0) uniform CameraBufferObject { + vec4 fwd; + vec4 pos; mat4 view; mat4 proj; } camera; // TODO: Declare tessellation control shader inputs and outputs +layout(location = 0) in vec4 inV0[]; +layout(location = 1) in vec4 inV1[]; +layout(location = 2) in vec4 inV2[]; +layout(location = 3) in vec4 inUp[]; + +layout(location = 0) out vec4 outV0[]; +layout(location = 1) out vec4 outV1[]; +layout(location = 2) out vec4 outV2[]; +layout(location = 3) out vec4 outUp[]; void main() { // Don't move the origin location of the patch gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; // TODO: Write any shader outputs + outV0[gl_InvocationID] = inV0[gl_InvocationID]; + outV1[gl_InvocationID] = inV1[gl_InvocationID]; + outV2[gl_InvocationID] = inV2[gl_InvocationID]; + outUp[gl_InvocationID] = inUp[gl_InvocationID]; // TODO: Set level of tesselation - // gl_TessLevelInner[0] = ??? - // gl_TessLevelInner[1] = ??? - // gl_TessLevelOuter[0] = ??? - // gl_TessLevelOuter[1] = ??? - // gl_TessLevelOuter[2] = ??? - // gl_TessLevelOuter[3] = ??? + gl_TessLevelInner[0] = TENSSCOUNT; + gl_TessLevelInner[1] = TENSSCOUNT; + gl_TessLevelOuter[0] = TENSSCOUNT; + gl_TessLevelOuter[1] = TENSSCOUNT; + gl_TessLevelOuter[2] = TENSSCOUNT; + gl_TessLevelOuter[3] = TENSSCOUNT; } diff --git a/src/shaders/grass.tese b/src/shaders/grass.tese index 751fff6..5a3534f 100644 --- a/src/shaders/grass.tese +++ b/src/shaders/grass.tese @@ -4,15 +4,56 @@ layout(quads, equal_spacing, ccw) in; layout(set = 0, binding = 0) uniform CameraBufferObject { + vec4 fwd; + vec4 pos; mat4 view; mat4 proj; } camera; // TODO: Declare tessellation evaluation shader inputs and outputs +// v0: the position of the grass blade on the geomtry +// v1: a Bezier curve guide that is always "above" v0 with respect to the grass blade's up vector (explained soon) +// v2: a physical guide for which we simulate forces on +// v0.w holds orientation, v1.w holds height, v2.w holds width, and up.w holds the stiffness coefficient. +layout(location = 0) in vec4 inV0[]; +layout(location = 1) in vec4 inV1[]; +layout(location = 2) in vec4 inV2[]; +layout(location = 3) in vec4 inUp[]; + +layout(location = 0) out vec3 color; void main() { float u = gl_TessCoord.x; float v = gl_TessCoord.y; // TODO: Use u and v to parameterize along the grass blade and output positions for each vertex of the grass blade + vec3 v0 = inV0[0].xyz; + vec3 v1 = inV1[0].xyz; + vec3 v2 = inV2[0].xyz; + + float orientation = inV0[0].w; + float width = inV2[0].w; + float height = inV1[0].w; + vec4 vertical = vec4(inUp[0].xyz,0.0); + vec4 horizontal = vec4(sin(orientation), 0, cos(orientation), 0);//along width direction + vec4 p0 = gl_in[0].gl_Position;//calculated by v0 + + vec3 a = v0 + v * (v1 - v0); + vec3 b = v1 + v * (v2 - v1); + vec3 c = a + v * (b - a); + vec3 c0 = c - width * horizontal.xyz; + vec3 c1 = c + width * horizontal.xyz; + vec3 t0 = normalize(b-a); + vec3 norm = cross(t0,horizontal.xyz); + float t = + // u;//quad + // u + 0.5*v - u*v;//triangle + // u - u * v * v;//quadratic + 0.5 + (u - 0.5) * (1 - max(v - 0.4,0)/(1 - 0.4));//tip trig + + vec3 d = width * norm * (0.5 - abs(u - 0.5)*(1 - v)); + + gl_Position = vec4(mix(c0,c1,t) + d,1); + gl_Position = camera.proj * camera.view * gl_Position; + color = mix(vec3(55.0/255, 77.0/255,34.0/255), vec3(95.0/255, 129.0/255,57.0/255), v); } diff --git a/src/shaders/grass.vert b/src/shaders/grass.vert index db9dfe9..715a8fc 100644 --- a/src/shaders/grass.vert +++ b/src/shaders/grass.vert @@ -7,11 +7,38 @@ layout(set = 1, binding = 0) uniform ModelBufferObject { }; // TODO: Declare vertex shader inputs and outputs +// v0: the position of the grass blade on the geomtry +// v1: a Bezier curve guide that is always "above" v0 with respect to the grass blade's up vector (explained soon) +// v2: a physical guide for which we simulate forces on +// v0.w holds orientation, v1.w holds height, v2.w holds width, and up.w holds the stiffness coefficient. +layout(location = 0) in vec4 inV0; +layout(location = 1) in vec4 inV1; +layout(location = 2) in vec4 inV2; +layout(location = 3) in vec4 inUp; out gl_PerVertex { vec4 gl_Position; }; + +// layout(location = 0) out vec3 fragColor; +// layout(location = 1) out vec2 fragTexCoord; +layout(location = 0) out vec4 outV0; +layout(location = 1) out vec4 outV1; +layout(location = 2) out vec4 outV2; +layout(location = 3) out vec4 outUp; + void main() { // TODO: Write gl_Position and any other shader outputs + gl_Position = model * vec4(inV0.xyz,1.0); + + outV0 = gl_Position; + outV1 = model * vec4(inV1.xyz,1.0); + outV2 = model * vec4(inV2.xyz,1.0); + outUp = model * vec4(inUp.xyz,0.0); + + outV0.w = inV0.w; + outV1.w = inV1.w; + outV2.w = inV2.w; + outUp.w = inUp.w; }