├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .idea ├── .gitignore ├── LuisaShaderToy.iml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── gallery └── protean_clouds.mp4 ├── src ├── CMakeLists.txt ├── apps │ ├── CMakeLists.txt │ ├── energy_plant.cpp │ ├── flame.cpp │ ├── fractal_pyramid.cpp │ ├── heartwing_angel.cpp │ ├── protean_clouds.cpp │ ├── rainbow_spaghetti.cpp │ ├── sdf_renderer.cpp │ ├── seascape.cpp │ ├── sphere_in_cable.cpp │ └── star_nest.cpp ├── ext │ ├── CMakeLists.txt │ ├── glad │ │ ├── CMakeLists.txt │ │ └── glad │ │ │ ├── glad.c │ │ │ ├── glad.h │ │ │ └── khrplatform.h │ ├── imgui │ │ └── CMakeLists.txt │ └── stb │ │ ├── CMakeLists.txt │ │ └── stb.cpp └── gui │ ├── CMakeLists.txt │ ├── framerate.cpp │ ├── framerate.h │ ├── gl_texture.cpp │ ├── gl_texture.h │ ├── hid.h │ ├── imgui_impl_glfw.cpp │ ├── imgui_impl_glfw.h │ ├── shader_toy.cpp │ ├── shader_toy.h │ ├── window.cpp │ └── window.h └── tools └── record.py /.clang-format: -------------------------------------------------------------------------------- 1 | # Generated from CLion C/C++ Code Style settings 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: None 6 | AlignOperands: Align 7 | AllowAllArgumentsOnNextLine: false 8 | AllowAllConstructorInitializersOnNextLine: false 9 | AllowAllParametersOfDeclarationOnNextLine: false 10 | AllowShortBlocksOnASingleLine: Always 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: Always 14 | AllowShortLambdasOnASingleLine: All 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakTemplateDeclarations: Yes 18 | BreakBeforeBraces: Custom 19 | BraceWrapping: 20 | AfterCaseLabel: false 21 | AfterClass: false 22 | AfterControlStatement: Never 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterUnion: false 27 | BeforeCatch: false 28 | BeforeElse: false 29 | IndentBraces: false 30 | SplitEmptyFunction: false 31 | SplitEmptyRecord: true 32 | BreakBeforeBinaryOperators: NonAssignment 33 | BreakBeforeTernaryOperators: true 34 | BreakConstructorInitializers: BeforeColon 35 | BreakInheritanceList: BeforeColon 36 | ColumnLimit: 0 37 | CompactNamespaces: true 38 | ContinuationIndentWidth: 4 39 | IndentCaseLabels: true 40 | IndentPPDirectives: None 41 | IndentWidth: 4 42 | KeepEmptyLinesAtTheStartOfBlocks: true 43 | MaxEmptyLinesToKeep: 1 44 | NamespaceIndentation: None 45 | ObjCSpaceAfterProperty: false 46 | ObjCSpaceBeforeProtocolList: false 47 | PointerAlignment: Right 48 | ReflowComments: false 49 | SpaceAfterCStyleCast: false 50 | SpaceAfterLogicalNot: false 51 | SpaceAfterTemplateKeyword: false 52 | SpaceBeforeAssignmentOperators: true 53 | SpaceBeforeCpp11BracedList: false 54 | SpaceBeforeCtorInitializerColon: true 55 | SpaceBeforeInheritanceColon: true 56 | SpaceBeforeParens: ControlStatements 57 | SpaceBeforeRangeBasedForLoopColon: true 58 | SpaceInEmptyParentheses: false 59 | SpacesBeforeTrailingComments: 0 60 | SpacesInAngles: false 61 | SpacesInCStyleCastParentheses: false 62 | SpacesInContainerLiterals: false 63 | SpacesInParentheses: false 64 | SpacesInSquareBrackets: false 65 | TabWidth: 4 66 | UseTab: Never 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build-linux: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | with: 11 | submodules: recursive 12 | - name: "Install Dependencies" 13 | run: | 14 | sudo apt-get purge --auto-remove cmake 15 | wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null 16 | sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' 17 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 18 | sudo apt-get update 19 | sudo apt-get -y install cmake 20 | sudo apt-get -y install gcc-11 g++-11 build-essential ninja-build git file libopencv-dev uuid-dev libglfw3-dev libxinerama-dev libxcursor-dev libxi-dev 21 | wget https://apt.llvm.org/llvm.sh 22 | chmod +x llvm.sh 23 | sudo ./llvm.sh 14 24 | # - name: "Setup CUDA" 25 | # uses: Jimver/cuda-toolkit@v0.2.4 26 | # with: 27 | # cuda: "11.4.0" 28 | # linux-local-args: '["--toolkit"]' 29 | - name: "Configure and Build" 30 | run: | 31 | cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release -D CMAKE_C_COMPILER=gcc-11 -D CMAKE_CXX_COMPILER=g++-11 -D LLVM_ROOT=/usr/lib/llvm-14 32 | cmake --build build 33 | 34 | build-macos: 35 | runs-on: macos-latest 36 | steps: 37 | - uses: actions/checkout@v2 38 | with: 39 | submodules: recursive 40 | - name: "Install Dependencies" 41 | run: | 42 | brew install cmake ninja opencv llvm sccache glfw 43 | - name: "Configure and Build" 44 | run: | 45 | export PATH=$PATH:/usr/local/opt/llvm/bin 46 | cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release -D LUISA_COMPUTE_ENABLE_METAL=OFF -D CMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang -D CMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++ 47 | cmake --build build 48 | 49 | build-windows: 50 | runs-on: windows-latest 51 | steps: 52 | - uses: actions/checkout@v2 53 | with: 54 | submodules: recursive 55 | # - name: "Setup CUDA" 56 | # uses: Jimver/cuda-toolkit@v0.2.4 57 | # with: 58 | # cuda: "11.4.0" 59 | - name: "Setup Ninja" 60 | uses: ashutoshvarma/setup-ninja@master 61 | with: 62 | version: 1.10.2 63 | - name: "Configure and Build" 64 | shell: cmd 65 | run: | 66 | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" 67 | cmake -S . -G Ninja -B build -D CMAKE_BUILD_TYPE=Release -D CMAKE_C_COMPILER:FILEPATH=cl.exe -D CMAKE_CXX_COMPILER:FILEPATH=cl.exe 68 | cmake --build build 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### VisualStudioCode template 2 | .vscode/* 3 | !.vscode/settings.json 4 | !.vscode/tasks.json 5 | !.vscode/launch.json 6 | !.vscode/extensions.json 7 | *.code-workspace 8 | 9 | # Local History for Visual Studio Code 10 | .history/ 11 | 12 | ### Linux template 13 | *~ 14 | 15 | # temporary files which can be created if a process still has a handle open of a deleted file 16 | .fuse_hidden* 17 | 18 | # KDE directory preferences 19 | .directory 20 | 21 | # Linux trash folder which might appear on any partition or disk 22 | .Trash-* 23 | 24 | # .nfs files are created when an open file is removed but is still being accessed 25 | .nfs* 26 | 27 | ### macOS template 28 | # General 29 | .DS_Store 30 | .AppleDouble 31 | .LSOverride 32 | 33 | # Icon must end with two \r 34 | Icon 35 | 36 | # Thumbnails 37 | ._* 38 | 39 | # Files that might appear in the root of a volume 40 | .DocumentRevisions-V100 41 | .fseventsd 42 | .Spotlight-V100 43 | .TemporaryItems 44 | .Trashes 45 | .VolumeIcon.icns 46 | .com.apple.timemachine.donotpresent 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | 55 | ### VisualStudio template 56 | ## Ignore Visual Studio temporary files, build results, and 57 | ## files generated by popular Visual Studio add-ons. 58 | ## 59 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 60 | 61 | # User-specific files 62 | *.rsuser 63 | *.suo 64 | *.user 65 | *.userosscache 66 | *.sln.docstates 67 | 68 | # User-specific files (MonoDevelop/Xamarin Studio) 69 | *.userprefs 70 | 71 | # Mono auto generated files 72 | mono_crash.* 73 | 74 | # Build results 75 | [Dd]ebug/ 76 | [Dd]ebugPublic/ 77 | [Rr]elease/ 78 | [Rr]eleases/ 79 | x64/ 80 | x86/ 81 | [Ww][Ii][Nn]32/ 82 | [Aa][Rr][Mm]/ 83 | [Aa][Rr][Mm]64/ 84 | bld/ 85 | [Bb]in/ 86 | [Oo]bj/ 87 | [Ll]og/ 88 | [Ll]ogs/ 89 | 90 | # Visual Studio 2015/2017 cache/options directory 91 | .vs/ 92 | # Uncomment if you have tasks that create the project's static files in wwwroot 93 | #wwwroot/ 94 | 95 | # Visual Studio 2017 auto generated files 96 | Generated\ Files/ 97 | 98 | # MSTest test Results 99 | [Tt]est[Rr]esult*/ 100 | [Bb]uild[Ll]og.* 101 | 102 | # NUnit 103 | *.VisualState.xml 104 | TestResult.xml 105 | nunit-*.xml 106 | 107 | # Build Results of an ATL Project 108 | [Dd]ebugPS/ 109 | [Rr]eleasePS/ 110 | dlldata.c 111 | 112 | # Benchmark Results 113 | BenchmarkDotNet.Artifacts/ 114 | 115 | # .NET Core 116 | project.lock.json 117 | project.fragment.lock.json 118 | artifacts/ 119 | 120 | # ASP.NET Scaffolding 121 | ScaffoldingReadMe.txt 122 | 123 | # StyleCop 124 | StyleCopReport.xml 125 | 126 | # Files built by Visual Studio 127 | *_i.c 128 | *_p.c 129 | *_h.h 130 | *.ilk 131 | *.meta 132 | *.obj 133 | *.iobj 134 | *.pch 135 | *.pdb 136 | *.ipdb 137 | *.pgc 138 | *.pgd 139 | *.rsp 140 | *.sbr 141 | *.tlb 142 | *.tli 143 | *.tlh 144 | *.tmp 145 | *.tmp_proj 146 | *_wpftmp.csproj 147 | *.log 148 | *.vspscc 149 | *.vssscc 150 | .builds 151 | *.pidb 152 | *.svclog 153 | *.scc 154 | 155 | # Chutzpah Test files 156 | _Chutzpah* 157 | 158 | # Visual C++ cache files 159 | ipch/ 160 | *.aps 161 | *.ncb 162 | *.opendb 163 | *.opensdf 164 | *.sdf 165 | *.cachefile 166 | *.VC.db 167 | *.VC.VC.opendb 168 | 169 | # Visual Studio profiler 170 | *.psess 171 | *.vsp 172 | *.vspx 173 | *.sap 174 | 175 | # Visual Studio Trace Files 176 | *.e2e 177 | 178 | # TFS 2012 Local Workspace 179 | $tf/ 180 | 181 | # Guidance Automation Toolkit 182 | *.gpState 183 | 184 | # ReSharper is a .NET coding add-in 185 | _ReSharper*/ 186 | *.[Rr]e[Ss]harper 187 | *.DotSettings.user 188 | 189 | # TeamCity is a build add-in 190 | _TeamCity* 191 | 192 | # DotCover is a Code Coverage Tool 193 | *.dotCover 194 | 195 | # AxoCover is a Code Coverage Tool 196 | .axoCover/* 197 | !.axoCover/settings.json 198 | 199 | # Coverlet is a free, cross platform Code Coverage Tool 200 | coverage*.json 201 | coverage*.xml 202 | coverage*.info 203 | 204 | # Visual Studio code coverage results 205 | *.coverage 206 | *.coveragexml 207 | 208 | # NCrunch 209 | _NCrunch_* 210 | .*crunch*.local.xml 211 | nCrunchTemp_* 212 | 213 | # MightyMoose 214 | *.mm.* 215 | AutoTest.Net/ 216 | 217 | # Web workbench (sass) 218 | .sass-cache/ 219 | 220 | # Installshield output folder 221 | [Ee]xpress/ 222 | 223 | # DocProject is a documentation generator add-in 224 | DocProject/buildhelp/ 225 | DocProject/Help/*.HxT 226 | DocProject/Help/*.HxC 227 | DocProject/Help/*.hhc 228 | DocProject/Help/*.hhk 229 | DocProject/Help/*.hhp 230 | DocProject/Help/Html2 231 | DocProject/Help/html 232 | 233 | # Click-Once directory 234 | publish/ 235 | 236 | # Publish Web Output 237 | *.[Pp]ublish.xml 238 | *.azurePubxml 239 | # Note: Comment the next line if you want to checkin your web deploy settings, 240 | # but database connection strings (with potential passwords) will be unencrypted 241 | *.pubxml 242 | *.publishproj 243 | 244 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 245 | # checkin your Azure Web App publish settings, but sensitive information contained 246 | # in these scripts will be unencrypted 247 | PublishScripts/ 248 | 249 | # NuGet Packages 250 | *.nupkg 251 | # NuGet Symbol Packages 252 | *.snupkg 253 | # The packages folder can be ignored because of Package Restore 254 | **/[Pp]ackages/* 255 | # except build/, which is used as an MSBuild target. 256 | !**/[Pp]ackages/build/ 257 | # Uncomment if necessary however generally it will be regenerated when needed 258 | #!**/[Pp]ackages/repositories.config 259 | # NuGet v3's project.json files produces more ignorable files 260 | *.nuget.props 261 | *.nuget.targets 262 | 263 | # Microsoft Azure Build Output 264 | csx/ 265 | *.build.csdef 266 | 267 | # Microsoft Azure Emulator 268 | ecf/ 269 | rcf/ 270 | 271 | # Windows Store app package directories and files 272 | AppPackages/ 273 | BundleArtifacts/ 274 | Package.StoreAssociation.xml 275 | _pkginfo.txt 276 | *.appx 277 | *.appxbundle 278 | *.appxupload 279 | 280 | # Visual Studio cache files 281 | # files ending in .cache can be ignored 282 | *.[Cc]ache 283 | # but keep track of directories ending in .cache 284 | !?*.[Cc]ache/ 285 | 286 | # Others 287 | ClientBin/ 288 | ~$* 289 | *.dbmdl 290 | *.dbproj.schemaview 291 | *.jfm 292 | *.pfx 293 | *.publishsettings 294 | orleans.codegen.cs 295 | 296 | # Including strong name files can present a security risk 297 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 298 | #*.snk 299 | 300 | # Since there are multiple workflows, uncomment next line to ignore bower_components 301 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 302 | #bower_components/ 303 | 304 | # RIA/Silverlight projects 305 | Generated_Code/ 306 | 307 | # Backup & report files from converting an old project file 308 | # to a newer Visual Studio version. Backup files are not needed, 309 | # because we have git ;-) 310 | _UpgradeReport_Files/ 311 | Backup*/ 312 | UpgradeLog*.XML 313 | UpgradeLog*.htm 314 | ServiceFabricBackup/ 315 | *.rptproj.bak 316 | 317 | # SQL Server files 318 | *.mdf 319 | *.ldf 320 | *.ndf 321 | 322 | # Business Intelligence projects 323 | *.rdl.data 324 | *.bim.layout 325 | *.bim_*.settings 326 | *.rptproj.rsuser 327 | *- [Bb]ackup.rdl 328 | *- [Bb]ackup ([0-9]).rdl 329 | *- [Bb]ackup ([0-9][0-9]).rdl 330 | 331 | # Microsoft Fakes 332 | FakesAssemblies/ 333 | 334 | # GhostDoc plugin setting file 335 | *.GhostDoc.xml 336 | 337 | # Node.js Tools for Visual Studio 338 | .ntvs_analysis.dat 339 | node_modules/ 340 | 341 | # Visual Studio 6 build log 342 | *.plg 343 | 344 | # Visual Studio 6 workspace options file 345 | *.opt 346 | 347 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 348 | *.vbw 349 | 350 | # Visual Studio LightSwitch build output 351 | **/*.HTMLClient/GeneratedArtifacts 352 | **/*.DesktopClient/GeneratedArtifacts 353 | **/*.DesktopClient/ModelManifest.xml 354 | **/*.Server/GeneratedArtifacts 355 | **/*.Server/ModelManifest.xml 356 | _Pvt_Extensions 357 | 358 | # Paket dependency manager 359 | .paket/paket.exe 360 | paket-files/ 361 | 362 | # FAKE - F# Make 363 | .fake/ 364 | 365 | # CodeRush personal settings 366 | .cr/personal 367 | 368 | # Python Tools for Visual Studio (PTVS) 369 | __pycache__/ 370 | *.pyc 371 | 372 | # Cake - Uncomment if you are using it 373 | # tools/** 374 | # !tools/packages.config 375 | 376 | # Tabs Studio 377 | *.tss 378 | 379 | # Telerik's JustMock configuration file 380 | *.jmconfig 381 | 382 | # BizTalk build output 383 | *.btp.cs 384 | *.btm.cs 385 | *.odx.cs 386 | *.xsd.cs 387 | 388 | # OpenCover UI analysis results 389 | OpenCover/ 390 | 391 | # Azure Stream Analytics local run output 392 | ASALocalRun/ 393 | 394 | # MSBuild Binary and Structured Log 395 | *.binlog 396 | 397 | # NVidia Nsight GPU debugger configuration file 398 | *.nvuser 399 | 400 | # MFractors (Xamarin productivity tool) working folder 401 | .mfractor/ 402 | 403 | # Local History for Visual Studio 404 | .localhistory/ 405 | 406 | # BeatPulse healthcheck temp database 407 | healthchecksdb 408 | 409 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 410 | MigrationBackup/ 411 | 412 | # Ionide (cross platform F# VS Code tools) working folder 413 | .ionide/ 414 | 415 | # Fody - auto-generated XML schema 416 | FodyWeavers.xsd 417 | 418 | ### CMake template 419 | CMakeLists.txt.user 420 | CMakeCache.txt 421 | CMakeFiles 422 | CMakeScripts 423 | Testing 424 | Makefile 425 | cmake_install.cmake 426 | install_manifest.txt 427 | compile_commands.json 428 | CTestTestfile.cmake 429 | _deps 430 | 431 | ### Vim template 432 | # Swap 433 | [._]*.s[a-v][a-z] 434 | !*.svg # comment out if you don't need vector files 435 | [._]*.sw[a-p] 436 | [._]s[a-rt-v][a-z] 437 | [._]ss[a-gi-z] 438 | [._]sw[a-p] 439 | 440 | # Session 441 | Session.vim 442 | Sessionx.vim 443 | 444 | # Temporary 445 | .netrwhist 446 | # Auto-generated tag files 447 | tags 448 | # Persistent undo 449 | [._]*.un~ 450 | 451 | ### JetBrains template 452 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 453 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 454 | 455 | # User-specific stuff 456 | .idea/**/workspace.xml 457 | .idea/**/tasks.xml 458 | .idea/**/usage.statistics.xml 459 | .idea/**/dictionaries 460 | .idea/**/shelf 461 | 462 | # Generated files 463 | .idea/**/contentModel.xml 464 | 465 | # Sensitive or high-churn files 466 | .idea/**/dataSources/ 467 | .idea/**/dataSources.ids 468 | .idea/**/dataSources.local.xml 469 | .idea/**/sqlDataSources.xml 470 | .idea/**/dynamic.xml 471 | .idea/**/uiDesigner.xml 472 | .idea/**/dbnavigator.xml 473 | 474 | # Gradle 475 | .idea/**/gradle.xml 476 | .idea/**/libraries 477 | 478 | # Gradle and Maven with auto-import 479 | # When using Gradle or Maven with auto-import, you should exclude module files, 480 | # since they will be recreated, and may cause churn. Uncomment if using 481 | # auto-import. 482 | # .idea/artifacts 483 | # .idea/compiler.xml 484 | # .idea/jarRepositories.xml 485 | # .idea/modules.xml 486 | # .idea/*.iml 487 | # .idea/modules 488 | # *.iml 489 | # *.ipr 490 | 491 | # CMake 492 | cmake-build-*/ 493 | 494 | # Mongo Explorer plugin 495 | .idea/**/mongoSettings.xml 496 | 497 | # File-based project format 498 | *.iws 499 | 500 | # IntelliJ 501 | out/ 502 | 503 | # mpeltonen/sbt-idea plugin 504 | .idea_modules/ 505 | 506 | # JIRA plugin 507 | atlassian-ide-plugin.xml 508 | 509 | # Cursive Clojure plugin 510 | .idea/replstate.xml 511 | 512 | # Crashlytics plugin (for Android Studio and IntelliJ) 513 | com_crashlytics_export_strings.xml 514 | crashlytics.properties 515 | crashlytics-build.properties 516 | fabric.properties 517 | 518 | # Editor-based Rest Client 519 | .idea/httpRequests 520 | 521 | # Android studio 3.1+ serialized cache file 522 | .idea/caches/build_file_checksums.ser 523 | 524 | ### Windows template 525 | # Windows thumbnail cache files 526 | Thumbs.db 527 | Thumbs.db:encryptable 528 | ehthumbs.db 529 | ehthumbs_vista.db 530 | 531 | # Dump file 532 | *.stackdump 533 | 534 | # Folder config file 535 | [Dd]esktop.ini 536 | 537 | # Recycle Bin used on file shares 538 | $RECYCLE.BIN/ 539 | 540 | # Windows Installer files 541 | *.cab 542 | *.msi 543 | *.msix 544 | *.msm 545 | *.msp 546 | 547 | # Windows shortcuts 548 | *.lnk 549 | 550 | ### Emacs template 551 | # -*- mode: gitignore; -*- 552 | \#*\# 553 | /.emacs.desktop 554 | /.emacs.desktop.lock 555 | *.elc 556 | auto-save-list 557 | tramp 558 | .\#* 559 | 560 | # Org-mode 561 | .org-id-locations 562 | *_archive 563 | 564 | # flymake-mode 565 | *_flymake.* 566 | 567 | # eshell files 568 | /eshell/history 569 | /eshell/lastdir 570 | 571 | # elpa packages 572 | /elpa/ 573 | 574 | # reftex files 575 | *.rel 576 | 577 | # AUCTeX auto folder 578 | /auto/ 579 | 580 | # cask packages 581 | .cask/ 582 | dist/ 583 | 584 | # Flycheck 585 | flycheck_*.el 586 | 587 | # server auth directory 588 | /server/ 589 | 590 | # projectiles files 591 | .projectile 592 | 593 | # directory configuration 594 | .dir-locals.el 595 | 596 | # network security 597 | /network-security.data 598 | 599 | 600 | build/src/compute/src/ext/spdlog/spdlog_pch.h 601 | build/src/ext/glfw/src/glfw3.pc 602 | build/src/ext/glfw/src/glfw3Config.cmake 603 | build/src/ext/glfw/src/glfw3ConfigVersion.cmake 604 | build/src/ext/glfw/src/glfw_config.h 605 | *.json 606 | *.a 607 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/ext/glfw"] 2 | path = src/ext/glfw 3 | url = https://github.com/glfw/glfw.git 4 | [submodule "src/ext/imgui/imgui"] 5 | path = src/ext/imgui/imgui 6 | url = https://github.com/ocornut/imgui.git 7 | [submodule "src/compute"] 8 | path = src/compute 9 | url = https://github.com/LuisaGroup/LuisaCompute.git 10 | [submodule "src/ext/stb/stb"] 11 | path = src/ext/stb/stb 12 | url = https://github.com/nothings/stb.git 13 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/LuisaShaderToy.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18...3.20) 2 | cmake_policy(VERSION 3.18) 3 | 4 | project(LuisaShaderToy LANGUAGES C CXX VERSION 0.1) 5 | 6 | set(CMAKE_C_STANDARD 11) 7 | set(CMAKE_CXX_STANDARD 20) 8 | set(CMAKE_C_STANDARD_REQUIRED ON) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | set(CMAKE_C_EXTENSIONS OFF) 11 | set(CMAKE_CXX_EXTENSIONS OFF) 12 | set(CMAKE_OSX_DEPLOYMENT_TARGET 12) 13 | 14 | set(BUILD_SHARED_LIBS ON) 15 | 16 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 17 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 18 | foreach (CONFIG ${CMAKE_CONFIGURATION_TYPES}) 19 | string(TOUPPER ${CONFIG} CONFIG_UPPER) 20 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_UPPER} "${CMAKE_BINARY_DIR}/${CONFIG}/bin") 21 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_UPPER} "${CMAKE_BINARY_DIR}/${CONFIG}/bin") 22 | endforeach () 23 | 24 | set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) 25 | set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) 26 | 27 | if (MSVC) 28 | string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") 30 | endif () 31 | 32 | add_subdirectory(src) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, LuisaGroup 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LuisaShaderToy 2 | 3 | LuisaShaderToy is a playground for testing shaders with [LuisaCompute](https://github.com/LuisaGroup/LuisaCompute). 4 | 5 | See `src/apps` for some demo toys we port from the original [ShaderToy](https://www.shadertoy.com) website. 6 | 7 | # Demos 8 | 9 | https://user-images.githubusercontent.com/7614925/191537869-baf78cde-dbf7-46b4-860f-f87470e800ea.mp4 10 | 11 | 12 | 13 | https://user-images.githubusercontent.com/7614925/191538018-1a50b50d-bf8f-49d3-a4b9-c6a4ae30c1d9.mp4 14 | 15 | 16 | 17 | https://user-images.githubusercontent.com/7614925/191538042-c589829f-9273-4506-a45d-c0e474b9a226.mp4 18 | 19 | -------------------------------------------------------------------------------- /gallery/protean_clouds.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisaGroup/LuisaShaderToy/01c968a379fcd7d810851311607a5dcdba46be3c/gallery/protean_clouds.mp4 -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}") 2 | 3 | set(LUISA_COMPUTE_ENABLE_GUI OFF CACHE BOOL "" FORCE) 4 | add_subdirectory(compute) 5 | 6 | add_subdirectory(ext) 7 | add_subdirectory(gui) 8 | 9 | add_library(luisa::shader-toy ALIAS luisa-shader-toy-gui) 10 | 11 | add_subdirectory(apps) 12 | -------------------------------------------------------------------------------- /src/apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function(luisa_shader_toy_add_executable name) 2 | add_executable(${name} ${name}.cpp) 3 | target_link_libraries(${name} PRIVATE luisa::shader-toy) 4 | endfunction() 5 | 6 | luisa_shader_toy_add_executable(fractal_pyramid) 7 | luisa_shader_toy_add_executable(energy_plant) 8 | luisa_shader_toy_add_executable(star_nest) 9 | luisa_shader_toy_add_executable(protean_clouds) 10 | luisa_shader_toy_add_executable(heartwing_angel) 11 | luisa_shader_toy_add_executable(flame) 12 | luisa_shader_toy_add_executable(rainbow_spaghetti) 13 | luisa_shader_toy_add_executable(sphere_in_cable) 14 | luisa_shader_toy_add_executable(sdf_renderer) 15 | luisa_shader_toy_add_executable(seascape) 16 | -------------------------------------------------------------------------------- /src/apps/energy_plant.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | // Credit: https://www.shadertoy.com/view/WdjBWc 11 | int main(int argc, char *argv[]) {// FIXME... 12 | 13 | gui::ShaderToy toy{argc, argv}; 14 | 15 | Callable rot = [](Float a) noexcept { 16 | Var s = sin(a); 17 | Var c = cos(a); 18 | return make_float2x2(c, -s, s, c); 19 | }; 20 | 21 | Callable lpNorm = [](Float3 p, Float n) noexcept { 22 | p = pow(abs(p), make_float3(n)); 23 | return pow(p.x + p.y + p.z, 1.0f / n); 24 | }; 25 | 26 | Callable pSFold = [](Float2 p, Float n) noexcept { 27 | Var h = floor(log2(n)); 28 | Var a = 6.2831f * exp2(h) / n; 29 | for (auto i : range(cast(h) + 2u)) { 30 | Var v = make_float2(-cos(a), sin(a)); 31 | Var g = dot(p, v); 32 | p -= (g - sqrt(g * g + 5e-3f)) * v; 33 | a *= 0.5f; 34 | } 35 | return p; 36 | }; 37 | 38 | Callable sFold45 = [](Float2 p, Float k) noexcept { 39 | Var v = float2(-1.0f, 1.0f) * 0.7071f; 40 | Var g = dot(p, v); 41 | return p - (g - sqrt(g * g + k)) * v; 42 | }; 43 | 44 | Callable frameBox = [&](Float3 p, Float3 s, Float r) noexcept { 45 | p = abs(p) - s; 46 | p = make_float3(p.x, sFold45(p.yz(), 1e-3f)); 47 | p = make_float3(sFold45(p.xy(), 1e-3f), p.z); 48 | p.x = max(0.0f, p.x); 49 | return lpNorm(p, 5.0f) - r; 50 | }; 51 | 52 | Callable sdRoundBox = [](Float3 p, Float3 b, Float r) noexcept { 53 | Var q = abs(p) - b; 54 | return length(max(q, 0.0f)) + min(max(q.x, max(q.y, q.z)), 0.0f) - r; 55 | }; 56 | 57 | Callable deObj = [&](Float3 p) noexcept { 58 | return min(min(sdRoundBox(p, float3(0.3f), 0.1f), 59 | frameBox(p, float3(0.7f), 0.05f)), 60 | frameBox(p, float3(0.5f), 0.01f)); 61 | }; 62 | 63 | Callable map = [&](Float3 p, Float &g, Float time) noexcept { 64 | Var de = 1e9f; 65 | p.z -= time * 1.5f; 66 | p.z = mod(p.z, 12.0f) - 6.0f; 67 | Var q = p; 68 | p = make_float3(pSFold(p.xy(), 6.0f), p.z); 69 | p.y -= 5.0f; 70 | Var s = 1.0f; 71 | for (auto i = 0; i < 6; i++) { 72 | p = make_float3(abs(p.xy()) - 0.5f, p.z); 73 | p.z = abs(p.z) - 0.3f; 74 | p = make_float3(rot(-0.05f) * p.xy(), p.z); 75 | p = make_float3(p.x, rot(0.1f) * p.zy()).xzy(); 76 | s *= 0.7f; 77 | p *= s; 78 | p = make_float3(rot(0.05f) * p.xy(), p.z); 79 | p.y -= 0.3f; 80 | Var sp = p / s; 81 | de = min(de, min(sdRoundBox(sp, float3(0.3f), 0.1f), 82 | frameBox(sp, float3(0.7f), 0.05f))); 83 | } 84 | q.z -= clamp(q.z, -1.f, 1.f); 85 | Var d = length(q) - 0.5f; 86 | g += 0.1f / (0.2f + d * d * 5.0f);// Distance glow by balkhan 87 | de = min(de, d + 0.2f); 88 | return de; 89 | }; 90 | 91 | Callable calcNormal = [&](Float3 pos, Float &g, Float time) noexcept { 92 | auto e = float2(1.0f, -1.0f) * 0.002f; 93 | auto m1 = map(pos + e.xyy(), g, time); 94 | auto m2 = map(pos + e.yyx(), g, time); 95 | auto m3 = map(pos + e.yxy(), g, time); 96 | auto m4 = map(pos + e.xxx(), g, time); 97 | return normalize(e.xyy() * m1 + e.yyx() * m2 + e.yxy() * m3 + e.xxx() * m4); 98 | }; 99 | 100 | Callable march = [&](Float3 ro, Float3 rd, Float t_near, Float t_far, Float &g, Float time) noexcept { 101 | Var t = t_near; 102 | Var ret = t_far; 103 | for (auto i : range(100)) { 104 | auto d = map(ro + rd * t, g, time); 105 | t += d; 106 | if_(d < 0.001f, [&] { 107 | ret = t; 108 | break_(); 109 | }); 110 | if_(t >= t_far, break_); 111 | } 112 | return ret; 113 | }; 114 | 115 | Callable calcShadow = [&](Float3 light, Float3 ld, Float len, Float &g, Float time) noexcept { 116 | auto depth = march(light, ld, 0.0f, len, g, time); 117 | return step(len - depth, 0.01f); 118 | }; 119 | 120 | Callable doColor = [](Float3 p) noexcept { 121 | return float3(0.3, 0.5, 0.8) + cos(p * 0.2f) * .5f + .5f; 122 | }; 123 | 124 | Callable reflect = [](Float3 I, Float3 N) noexcept { 125 | return I - 2.0f * dot(N, I) * N; 126 | }; 127 | 128 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float time, Float4 cursor) noexcept { 129 | Var uv = (fragCoord * 2.0f - iResolution.xy()) / iResolution.y; 130 | auto ro = float3(2.5, 3.5, 8); 131 | auto ta = float3(-1, 0, 0); 132 | auto w = normalize(ta - ro); 133 | auto u = normalize(cross(w, float3(0.0f, 1.0f, 0.0f))); 134 | Var rd = float3x3(u, cross(u, w), w) * normalize(make_float3(uv, 2.0f)); 135 | Var col = make_float3(0.05f, 0.05f, 0.1f); 136 | static constexpr auto maxd = 80.0f; 137 | auto g = def(0.0f); 138 | auto t = march(ro, rd, 0.0f, maxd, g, time); 139 | if_(t < maxd, [&] { 140 | Var p = ro + rd * t; 141 | col = doColor(p); 142 | auto n = calcNormal(p, g, time); 143 | auto lightPos = float3(5.0f, 5.0f, 1.0f); 144 | Var li = lightPos - p; 145 | Var len = length(li); 146 | li /= len; 147 | Var dif = clamp(dot(n, li), 0.0f, 1.0f); 148 | auto sha = calcShadow(lightPos, -li, len, g, time); 149 | col *= max(sha * dif, 0.2f); 150 | Var rimd = pow(clamp(1.0f - dot(reflect(-li, n), -rd), 0.0f, 1.0f), 2.5f); 151 | Var frn = rimd + 2.2f * (1.0f - rimd); 152 | col *= frn * 0.8f; 153 | col *= max(0.5f + 0.5f * n.y, 0.0f); 154 | auto m = map(p + n * 0.3f, g, time); 155 | col *= exp2(-2.f * pow(max(0.0f, 1.0f - m / 0.3f), 2.0f)); 156 | col += float3(0.8f, 0.6f, 0.2f) * pow(clamp(dot(reflect(rd, n), li), 0.0f, 1.0f), 20.0f); 157 | col = lerp(float3(0.1f, 0.1f, 0.2f), col, exp(-0.001f * t * t)); 158 | col += float3(0.7f, 0.3f, 0.1f) * g * (1.5f + 0.8f * sin(time * 3.5f)); 159 | col = clamp(col, 0.0f, 1.0f); 160 | }); 161 | return pow(col, float3(1.5f)); 162 | }); 163 | } 164 | -------------------------------------------------------------------------------- /src/apps/flame.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | // Credit: https://www.shadertoy.com/view/MdX3zr 11 | int main(int argc, char *argv[]) { 12 | 13 | gui::ShaderToy toy{argc, argv}; 14 | 15 | Callable noise = [](Float3 p) noexcept { 16 | Var i = floor(p); 17 | Var a = dot(i, float3(1.f, 57.f, 21.f)) + float4(0.f, 57.f, 21.f, 78.f); 18 | Var f = cos((p - i) * acos(-1.f)) * (-.5f) + .5f; 19 | a = lerp(sin(cos(a) * a), sin(cos(1.f + a) * (1.f + a)), f.x); 20 | Var axy = lerp(a.xz(), a.yw(), f.y); 21 | return lerp(axy.x, axy.y, f.z); 22 | }; 23 | 24 | Callable sphere = [](Float3 p, Float4 spr) noexcept { 25 | return length(spr.xyz() - p) - spr.w; 26 | }; 27 | 28 | Callable flame = [&](Float3 p, Float iTime) noexcept { 29 | Var d = sphere(p * float3(1.f, .5f, 1.f), float4(.0f, -1.f, .0f, 1.f)); 30 | return d + (noise(p + make_float3(.0f, iTime * 2.f, .0f)) + noise(p * 3.f) * .5f) * .25f * p.y; 31 | }; 32 | 33 | Callable scene = [&](Float3 p, Float iTime) noexcept { 34 | return min(100.f - length(p), abs(flame(p, iTime))); 35 | }; 36 | 37 | Callable raymarch = [&](Float3 org, Float3 dir, Float iTime) noexcept { 38 | static constexpr auto eps = 0.02f; 39 | Var d = 0.0f; 40 | Var glow = 0.0f; 41 | Var p = org; 42 | Var glowed = false; 43 | 44 | for (int i = 0; i < 64; i++) { 45 | d = scene(p, iTime) + eps; 46 | p += d * dir; 47 | if_(d > eps, [&] { 48 | glowed = glowed | flame(p, iTime) < .0f; 49 | glow = ite(glowed, static_cast(i) / 64.0f, glow); 50 | }); 51 | } 52 | return make_float4(p, glow); 53 | }; 54 | 55 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) noexcept { 56 | Var v = 2.0f * fragCoord / iResolution - 1.0f; 57 | v.x *= iResolution.x / iResolution.y; 58 | 59 | static constexpr auto org = float3(0.f, -2.f, 4.f); 60 | Var dir = normalize(make_float3(v.x * 1.6f, -v.y, -1.5f)); 61 | 62 | Var p = raymarch(org, dir, iTime); 63 | Var glow = p.w; 64 | Var col = lerp(float3(1.f, .5f, .1f), float3(0.1f, .5f, 1.f), p.y * .02f + .4f); 65 | return lerp(float3(0.f), col, pow(glow * 2.f, 4.f)); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /src/apps/fractal_pyramid.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | // Credit: https://www.shadertoy.com/view/tsXBzS 11 | int main(int argc, char *argv[]) { 12 | 13 | gui::ShaderToy toy{argc, argv}; 14 | 15 | Callable palette = [](Float d) noexcept { 16 | return lerp(float3(0.2f, 0.7f, 0.9f), float3(1.0f, 0.0f, 1.0f), d); 17 | }; 18 | 19 | Callable rotate = [](Float2 p, Float a) noexcept { 20 | Var c = cos(a); 21 | Var s = sin(a); 22 | return make_float2x2(c, -s, s, c) * p; 23 | }; 24 | 25 | Callable map = [&rotate](Float3 p, Float time) noexcept { 26 | for (auto i = 0u; i < 8u; i++) { 27 | Var t = time * 0.2f; 28 | p = make_float3(rotate(p.xz(), t), p.y).xzy(); 29 | p = make_float3(rotate(p.xy(), t * 1.89f), p.z); 30 | p = make_float3(abs(p.x) - 0.5f, p.y, abs(p.z) - 0.5f); 31 | } 32 | return dot(sign(p), p) * 0.2f; 33 | }; 34 | 35 | Callable rm = [&map, &palette](Float3 ro, Float3 rd, Float time) noexcept { 36 | Var t = 0.0f; 37 | Var col = make_float3(0.0f); 38 | Var d = 0.0f; 39 | for (auto i : range(64)) { 40 | Var p = ro + rd * t; 41 | d = map(p, time) * 0.5f; 42 | if_(d < 0.02f | d > 100.0f, [] { break_(); }); 43 | col += palette(length(p) * 0.1f) / (400.0f * d); 44 | t += d; 45 | } 46 | return make_float4(col, 1.0f / (d * 100.0f)); 47 | }; 48 | 49 | toy.run([&](Float2 xy, Float2 resolution, Float time, Float4) noexcept { 50 | Var uv = (xy - resolution * 0.5f) / min(resolution.x, resolution.y); 51 | Var ro = make_float3(rotate(make_float2(0.0f, -50.0f), time), 0.0f).xzy(); 52 | Var cf = normalize(-ro); 53 | Var cs = normalize(cross(cf, make_float3(0.0f, 1.0f, 0.0f))); 54 | Var cu = normalize(cross(cf, cs)); 55 | Var uuv = ro + cf * 3.0f + uv.x * cs + uv.y * cu; 56 | Var rd = normalize(uuv - ro); 57 | return rm(ro, rd, time).xyz(); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /src/apps/heartwing_angel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | // Credit: https://www.shadertoy.com/view/4l23zc 11 | int main(int argc, char *argv[]) { 12 | 13 | gui::ShaderToy toy{argc, argv}; 14 | 15 | Callable JuliaFractal = [](Float2 c, Float2 c2, Float animparam, Float anim2) noexcept { 16 | Var z = c; 17 | Var mean = 0.0f; 18 | for (int i = 0; i < 64; i++) { 19 | Var a = make_float2(z.x, abs(z.y)); 20 | Var b = atan2(a.y * (0.99f + animparam * 9.0f), a.x + .110765432f + animparam); 21 | b = ite(b > 0.0f, b - 6.303431307f + animparam * 3.1513f, b); 22 | z = make_float2(log(length(a * (0.98899f - (animparam * 2.70f * anim2)))), b) + c2; 23 | if (i > 0) { mean += length(z / a * b); } 24 | mean += a.x - (b * 77.0f / length(a * b)); 25 | mean = clamp(mean, 111.0f, 99999.0f); 26 | } 27 | mean /= 131.21f; 28 | Var ci = 1.0f - fract(log2(.5f * log2(mean / (0.57891895f - abs(animparam * 141.0f))))); 29 | return make_float3(.5f + .5f * cos(6.f * ci + 0.0f), 30 | .5f + .75f * cos(6.f * ci + 0.14f), 31 | .5f + .5f * cos(6.f * ci + 0.7f)); 32 | }; 33 | 34 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) noexcept { 35 | static constexpr auto timeVal = 56.48f - 20.1601f; 36 | static constexpr auto rot = 3.141592654f * 0.5f; 37 | Var animWings = 0.004f * cos(iTime * 0.5f); 38 | Var animFlap = 0.011f * sin(iTime * 1.0f); 39 | Var uv = fragCoord - iResolution * .5f; 40 | uv /= iResolution.x * 1.5113f * abs(sin(timeVal)); 41 | uv.y -= animWings * 5.0f; 42 | Var tuv = uv * 125.0f; 43 | uv.x = tuv.x * cos(rot) - tuv.y * sin(rot); 44 | uv.y = 1.05f * tuv.x * sin(rot) + tuv.y * cos(rot); 45 | Var juliax = tan(timeVal) * 0.011f + 0.02f / (fragCoord.y * 0.19531f * (1.0f - animFlap)); 46 | Var juliay = cos(timeVal * 0.213f) * (0.022f + animFlap) + 5.66752f - (juliax * 1.5101f); 47 | Var tapU = 25.5f / iResolution.x; 48 | Var tapV = 25.5f / iResolution.y; 49 | Var color = JuliaFractal(uv + float2(0.0f), make_float2(juliax, juliay), animWings, animFlap); 50 | color += JuliaFractal(uv + make_float2(tapU, tapV), make_float2(juliax, juliay), animWings, animFlap); 51 | color += JuliaFractal(uv + make_float2(-tapU, -tapV), make_float2(juliax, juliay), animWings, animFlap); 52 | color *= 0.3333f; 53 | color = float3(1.0f) - color.zyx(); 54 | return color; 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /src/apps/protean_clouds.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | // Credit: https://www.shadertoy.com/view/3l23Rh 11 | int main(int argc, char *argv[]) { 12 | 13 | gui::ShaderToy toy{argc, argv}; 14 | 15 | Callable rot = [](Float a) noexcept { 16 | Var c = cos(a); 17 | Var s = sin(a); 18 | return make_float2x2(c, -s, s, c); 19 | }; 20 | 21 | static constexpr auto m3 = transpose(make_float3x3( 22 | 0.33338f, 0.56034f, -0.71817f, 23 | -0.87887f, 0.32651f, -0.15323f, 24 | 0.15162f, 0.69596f, 0.61339f)) 25 | * 1.93f; 26 | 27 | Callable mag2 = [](Float2 p) noexcept { return dot(p, p); }; 28 | Callable linstep = [](Float mn, Float mx, Float x) noexcept { return clamp((x - mn) / (mx - mn), 0.f, 1.f); }; 29 | 30 | Callable disp = [](Float t) noexcept { 31 | return make_float2(sin(t * 0.22f) * 1.f, cos(t * 0.175f) * 1.f) * 2.f; 32 | }; 33 | 34 | Callable map = [&](Float3 p, Float iTime, Float prm1, Float2 bsMo) noexcept { 35 | Var p2 = p; 36 | p2 = make_float3(p2.xy() - disp(p.z).xy(), p2.z); 37 | p = make_float3(rot(sin(p.z + iTime) * (0.1f + prm1 * 0.05f) + iTime * 0.09f) * p.xy(), p.z); 38 | Var cl = mag2(p2.xy()); 39 | Var d = 0.0f; 40 | p *= .61f; 41 | Var z = 1.f; 42 | Var trk = 1.f; 43 | Var dspAmp = 0.1f + prm1 * 0.2f; 44 | for (int i = 0; i < 5; i++) { 45 | p += sin(p.zxy() * 0.75f * trk + iTime * trk * .8f) * dspAmp; 46 | d -= abs(dot(cos(p), sin(p.yzx())) * z); 47 | z *= 0.57f; 48 | trk *= 1.4f; 49 | p = m3 * p; 50 | } 51 | d = abs(d + prm1 * 3.f) + prm1 * .3f - 2.5f + bsMo.y; 52 | return make_float2(d + cl * .2f + 0.25f, cl); 53 | }; 54 | 55 | Callable render = [&](Float3 ro, Float3 rd, Float time, Float iTime, Float prm1, Float2 bsMo) noexcept { 56 | static constexpr auto ldst = 8.f; 57 | Var rez = make_float4(0.0f); 58 | Var lpos = make_float3(disp(time + ldst) * 0.5f, time + ldst); 59 | Var t = 1.5f; 60 | Var fogT = 0.f; 61 | for (auto i : range(130)) { 62 | 63 | if_(rez.w > 0.99f, [] { break_(); }); 64 | 65 | Var pos = ro + t * rd; 66 | Var mpv = map(pos, iTime, prm1, bsMo); 67 | Var den = clamp(mpv.x - 0.3f, 0.f, 1.f) * 1.12f; 68 | Var dn = clamp((mpv.x + 2.f), 0.f, 3.f); 69 | 70 | Var col = make_float4(0.0f); 71 | if_(mpv.x > 0.6f, [&] { 72 | col = make_float4(sin(float3(5.f, 0.4f, 0.2f) + mpv.y * 0.1f + sin(pos.z * 0.4f) * 0.5f + 1.8f) * 0.5f + 0.5f, 0.08f); 73 | col *= den * den * den; 74 | col = make_float4(col.xyz() * linstep(4.f, -2.5f, mpv.x) * 2.3f, col.w); 75 | Var dif = clamp((den - map(pos + .8f, iTime, prm1, bsMo).x) / 9.f, 0.001f, 1.f); 76 | dif += clamp((den - map(pos + .35f, iTime, prm1, bsMo).x) / 2.5f, 0.001f, 1.f); 77 | col = make_float4(col.xyz() * den * (float3(0.005f, .045f, .075f) + 1.5f * float3(0.033f, 0.07f, 0.03f) * dif), col.w); 78 | }); 79 | 80 | Var fogC = exp(t * 0.2f - 2.2f); 81 | col += float4(0.06f, 0.11f, 0.11f, 0.1) * clamp(fogC - fogT, 0.f, 1.f); 82 | fogT = fogC; 83 | rez = rez + col * (1.f - rez.w); 84 | t += clamp(0.5f - dn * dn * .05f, 0.09f, 0.3f); 85 | } 86 | return clamp(rez, 0.0f, 1.0f); 87 | }; 88 | 89 | Callable getsat = [](Float3 c) noexcept { 90 | Var mi = min(min(c.x, c.y), c.z); 91 | Var ma = max(max(c.x, c.y), c.z); 92 | return (ma - mi) / (ma + 1e-7f); 93 | }; 94 | 95 | //from my "Will it blend" shader (https://www.shadertoy.com/view/lsdGzN) 96 | Callable iLerp = [&](Float3 a, Float3 b, Float x) noexcept { 97 | Var ic = lerp(a, b, x) + float3(1e-6f, 0.f, 0.f); 98 | Var sd = abs(getsat(ic) - lerp(getsat(a), getsat(b), x)); 99 | Var dir = normalize(make_float3(2.f * ic.x - ic.y - ic.z, 2.f * ic.y - ic.x - ic.z, 2.f * ic.z - ic.y - ic.x)); 100 | Var lgt = dot(float3(1.0f), ic); 101 | Var ff = dot(dir, normalize(ic)); 102 | ic += 1.5f * dir * sd * ff * lgt; 103 | return clamp(ic, 0.f, 1.f); 104 | }; 105 | 106 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) noexcept { 107 | Var q = fragCoord.xy() / iResolution.xy(); 108 | Var p = (fragCoord.xy() - 0.5f * iResolution.xy()) / iResolution.y; 109 | Var bsMo = (iMouse.xy() - 0.5f * iResolution.xy()) / iResolution.y; 110 | 111 | static constexpr auto dspAmp = .85f; 112 | static constexpr auto tgtDst = 3.5f; 113 | 114 | Var time = iTime * 3.f; 115 | Var ro = make_float3(0.0f, 0.0f, time); 116 | 117 | ro += make_float3(sin(iTime) * 0.5f, sin(iTime * 1.f) * 0.f, 0.0f); 118 | ro = make_float3(ro.xy() + disp(ro.z) * dspAmp, ro.z); 119 | 120 | Var target = normalize(ro - make_float3(disp(time + tgtDst) * dspAmp, time + tgtDst)); 121 | ro.x -= bsMo.x * 2.f; 122 | Var rightdir = normalize(cross(target, float3(0.0f, 1.0f, 0.0f))); 123 | Var updir = normalize(cross(rightdir, target)); 124 | rightdir = normalize(cross(updir, target)); 125 | Var rd = normalize((p.x * rightdir + p.y * updir) * 1.f - target); 126 | rd = make_float3(rot(-disp(time + 3.5f).x * 0.2f + bsMo.x) * rd.xy(), rd.z); 127 | Var prm1 = smoothstep(-0.4f, 0.4f, sin(iTime * 0.3f)); 128 | Var scn = render(ro, rd, time, iTime, prm1, bsMo); 129 | Var col = scn.xyz(); 130 | col = iLerp(col.zyx(), col.xyz(), clamp(1.f - prm1, 0.05f, 1.f)); 131 | col = pow(col, float3(.55f, 0.65f, 0.6)) * float3(1.f, .97f, .9f); 132 | return col * pow(16.0f * q.x * q.y * (1.0f - q.x) * (1.0f - q.y), 0.12f) * 0.7f + 0.3f;//Vign 133 | }); 134 | } 135 | -------------------------------------------------------------------------------- /src/apps/rainbow_spaghetti.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Yawanari.fst on 2021/6/28. 3 | // Credit: https://www.shadertoy.com/view/lsjGRV 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | int main(int argc, char *argv[]) { 11 | 12 | static constexpr auto i3 = 0.5773502691896258f; 13 | static constexpr auto r = 0.40824829046386302f; 14 | 15 | static constexpr auto i = 0.3333333333333333f; 16 | static constexpr auto j = 0.6666666666666666f; 17 | 18 | static constexpr auto lrad = 0.015f; 19 | static constexpr auto trad = 0.06f; 20 | static constexpr auto fogv = 0.025f; 21 | 22 | static constexpr auto dmax = 20.0f; 23 | static constexpr auto rayiter = 60; 24 | 25 | static constexpr auto wrap = 64.0f; 26 | 27 | auto L = normalize(float3(0.1f, 1.0f, 0.5f)); 28 | 29 | static constexpr auto axis = float3(1.0f, 1.0f, 0.0f); 30 | static constexpr auto tgt = float3(1.0f, 1.7f, 1.1f); 31 | static constexpr auto cpos = tgt + axis; 32 | static constexpr auto vel = 0.2f * axis; 33 | static constexpr auto key_G = 71.5f / 256.0f; 34 | 35 | Callable hash = [](Float3 x) { 36 | return fract(87.3f * dot(x, float3(0.1f, 0.9f, 0.7f))); 37 | }; 38 | 39 | Callable line = [](Float3 p0, Float3 p1, Float3 p) { 40 | Var dp0 = p - p0; 41 | Var dp10 = p1 - p0; 42 | Var u = clamp(dot(dp0, dp10) / dot(dp10, dp10), -5.0f, 5.0f); 43 | return distance(lerp(p0, p1, u), p) - 0.5f * lrad; 44 | }; 45 | 46 | Callable opU = [](Float2 a, Float2 b) { 47 | return ite(a.x < b.x, a, b); 48 | }; 49 | 50 | Callable hueOf = [](Float3 pos) { 51 | return cos(2.0f * dot(2.0f * pos, make_float3(0.3f, 0.7f, 0.4f))) * 0.49f + 0.5f; 52 | }; 53 | 54 | Callable round2 = [](Float3 x, Float3 a) { 55 | return 2.0f * floor(0.5f * (x + 1.0f - a)) + a; 56 | }; 57 | 58 | Callable pdist = [](Float3 p, Float3 q) { 59 | Var pq = p - q; 60 | return make_float4(q, dot(pq, pq)); 61 | }; 62 | 63 | Callable pselect = [](Float4 a, Float4 b) { 64 | return ite(a.w < b.w, a, b); 65 | }; 66 | 67 | Callable torus = [](Float3 a, Float3 b, Float3 pos) { 68 | pos -= 0.5f * (a + b); 69 | Var n = normalize(b - a); 70 | return distance(pos, r * normalize(pos - n * dot(n, pos))) - trad; 71 | }; 72 | 73 | Callable permute = [](Float3 e, Float3 f, Float3 g, Float3 h, Float p) { 74 | return ite(p < i, 75 | make_float4x4(make_float4(e, 1.0f), make_float4(f, 1.0f), make_float4(g, 1.0f), make_float4(h, 1.0f)), 76 | ite(p < j, make_float4x4(make_float4(e, 1.0f), make_float4(g, 1.0f), make_float4(f, 1.0f), make_float4(h, 1.0f)), 77 | make_float4x4(make_float4(e, 1.0f), make_float4(h, 1.0f), make_float4(f, 1.0f), make_float4(g, 1.0f)))); 78 | }; 79 | 80 | Callable randomBasis = [](Float p) { 81 | return ite(p < i, make_float3(1.0f, 0.0f, 0.0f), 82 | ite(p < j, make_float3(0.0f, 1.0f, 0.0f), make_float3(0.0f, 0.0f, 1.0f))); 83 | }; 84 | 85 | Callable randomPerp = [](Float3 v, Float p) { 86 | return ite(v.x > 0.0f, ite(p < 0.5f, make_float3(0.0f, 1.0f, 0.0f), make_float3(0.0f, 0.0f, 1.0f)), 87 | ite(v.y > 0.0f, ite(p < 0.5f, make_float3(1.0f, 0.0f, 0.0f), make_float3(0.0f, 0.0f, 1.0f)), 88 | ite(p < 0.5f, make_float3(1.0f, 0.0f, 0.0f), make_float3(0.0f, 1.0f, 0.0f)))); 89 | }; 90 | 91 | Callable map = [&](Float3 pos, Float iTime) { 92 | Var orig = pos; 93 | pos = mod(pos + mod(iTime * vel, wrap), wrap); 94 | Var a = round2(pos, float3(1.0f)); 95 | Var h = round2(pos, float3(0.0f)); 96 | 97 | Var b = make_float3(a.x, h.y, h.z); 98 | Var c = make_float3(h.x, a.y, h.z); 99 | Var d = make_float3(h.x, h.y, a.z); 100 | Var e = make_float3(h.x, a.y, a.z); 101 | Var f = make_float3(a.x, h.y, a.z); 102 | Var g = make_float3(a.x, a.y, h.z); 103 | 104 | // o is the closest octahedron center 105 | Var o = pselect(pselect(pdist(pos, a), pdist(pos, b)), 106 | pselect(pdist(pos, c), pdist(pos, d))) 107 | .xyz(); 108 | 109 | // t is the closest tetrahedron center 110 | Var t = floor(pos) + 0.5f; 111 | 112 | // normal points towards o 113 | // so bd is positive inside octahedron, negative inside tetrahedron 114 | Var omt = o.xyz() - t.xyz(); 115 | Var bd = dot(pos - o.xyz(), omt * 2.0f * i3) + i3; 116 | 117 | Var m = permute(e, f, g, h, hash(mod(t, wrap))); 118 | 119 | Var t1 = torus(m[0].xyz(), m[1].xyz(), pos); 120 | Var t2 = torus(m[2].xyz(), m[3].xyz(), pos); 121 | 122 | Var p = hash(mod(o, wrap)); 123 | Var b1 = randomBasis(fract(85.17f * p)); 124 | Var b2 = randomPerp(b1, fract(63.61f * p + 4.2f)); 125 | Var b3 = randomPerp(b1, fract(43.79f * p + 8.3f)); 126 | 127 | Var po = pos - o; 128 | 129 | Var o1 = torus(b1, b2, po); 130 | Var o2 = torus(b1, -b2, po); 131 | Var o3 = torus(-b1, b3, po); 132 | Var o4 = torus(-b1, -b3, po); 133 | 134 | Var noodle = make_float2(min(max(bd, min(t1, t2)), 135 | max(-bd, min(min(o1, o2), min(o3, o4)))), 136 | hueOf(orig + 0.5f * vel * iTime)); 137 | 138 | #define SHOW_GRIDS 1 139 | #if SHOW_GRIDS 140 | Var dline = line(e, f, pos); 141 | dline = min(dline, line(e, g, pos)); 142 | dline = min(dline, line(e, h, pos)); 143 | dline = min(dline, line(f, g, pos)); 144 | dline = min(dline, line(f, h, pos)); 145 | dline = min(dline, line(g, h, pos)); 146 | Var grid = make_float2(dline, 2.0f); 147 | noodle.x += 0.1f * trad; 148 | noodle.y = hash(mod(ite(bd < 0.0f, t, o), wrap)); 149 | noodle = opU(grid, noodle); 150 | #endif 151 | return noodle; 152 | }; 153 | 154 | Callable hue = [](Float h) { 155 | Var c = mod(h * 6.0f + float3(2.0f, 0.0f, 4.0f), 6.0f); 156 | return ite(h > 1.0f, float3(0.5f), clamp(min(c, -c + 4.0f), 0.0f, 1.0f)); 157 | }; 158 | 159 | Callable castRay = [&map](Float3 ro, Float3 rd, Float maxd, Float iTime) { 160 | static constexpr auto precis = 0.0001f; 161 | Var h = precis * 2.0f; 162 | Var t = 0.0f; 163 | Var m = -1.0f; 164 | for (auto i = 0; i < rayiter; i++) { 165 | if_(!(abs(h) < precis | t > maxd), [&] { 166 | t += h; 167 | Var res = map(ro + rd * t, iTime); 168 | h = res.x; 169 | m = res.y; 170 | }); 171 | } 172 | return make_float2(t, m); 173 | }; 174 | 175 | Callable calcNormal = [&map](Float3 pos, Float iTime) { 176 | static constexpr auto eps = float3(0.0001f, 0.0f, 0.0f); 177 | Var nor = make_float3( 178 | map(pos + eps.xyy(), iTime).x - map(pos - eps.xyy(), iTime).x, 179 | map(pos + eps.yxy(), iTime).x - map(pos - eps.yxy(), iTime).x, 180 | map(pos + eps.yyx(), iTime).x - map(pos - eps.yyx(), iTime).x); 181 | return normalize(nor); 182 | }; 183 | 184 | Callable shade = [&hue, &calcNormal, &castRay, &L](Float3 ro, Float3 rd, Float iTime) { 185 | Var tm = castRay(ro, rd, dmax, iTime); 186 | Var ret = float3(1.0f); 187 | if_(tm.y >= 0.0f, [&] { 188 | Var n = calcNormal(ro + tm.x * rd, iTime); 189 | Var fog = exp(-tm.x * tm.x * fogv); 190 | Var color = hue(tm.y) * 0.55f + 0.45f; 191 | Var diffamb = (0.5f * dot(n, L) + 0.5f) * color; 192 | Var R = 2.0f * n * dot(n, L) - L; 193 | Var spec = 0.2f * pow(clamp(-dot(R, rd), 0.0f, 1.0f), 6.0f); 194 | ret = fog * (diffamb + spec); 195 | }); 196 | return ret; 197 | }; 198 | 199 | gui::ShaderToy toy{argc, argv}; 200 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) { 201 | static constexpr auto yscl = 720.0f; 202 | static constexpr auto f = 900.0f; 203 | static constexpr auto up = float3(0.0f, 1.0f, 0.0f); 204 | 205 | Var uv = (fragCoord - 0.5f * iResolution) * yscl / iResolution.y; 206 | 207 | Var rz = normalize(tgt - cpos); 208 | Var rx = normalize(cross(rz, up)); 209 | Var ry = cross(rx, rz); 210 | 211 | Var thetax = 0.0f; 212 | Var thetay = 0.0f; 213 | 214 | if_(max(iMouse.x, iMouse.y) > 20.0f, [&] { 215 | thetax = (iMouse.y - 0.5f * iResolution.y) * 3.14f / iResolution.y; 216 | thetay = (iMouse.x - 0.5f * iResolution.x) * -6.28f / iResolution.x; 217 | }); 218 | 219 | Var cx = cos(thetax); 220 | Var sx = sin(thetax); 221 | Var cy = cos(thetay); 222 | Var sy = sin(thetay); 223 | 224 | Var Rx = make_float3x3(1.0f, 0.0f, 0.0f, 225 | 0.0f, cx, sx, 226 | 0.0f, -sx, cx); 227 | Var Ry = make_float3x3(cy, 0.0f, -sy, 228 | 0.0f, 1.0f, 0.0f, 229 | sy, 0.0f, cy); 230 | Var R = make_float3x3(rx, ry, rz); 231 | Var Rt = make_float3x3(rx.x, ry.x, rz.x, 232 | rx.y, ry.y, rz.y, 233 | rx.z, ry.z, rz.z); 234 | 235 | Var rd = R * Rx * Ry * normalize(make_float3(uv, f)); 236 | Var ro = tgt + R * Rx * Ry * Rt * (cpos - tgt); 237 | 238 | return shade(ro, rd, iTime); 239 | }); 240 | } 241 | -------------------------------------------------------------------------------- /src/apps/sdf_renderer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace luisa; 10 | using namespace luisa::compute; 11 | 12 | // Credit: https://github.com/taichi-dev/taichi/blob/master/examples/rendering/sdf_renderer.py 13 | int main(int argc, char *argv[]) { 14 | 15 | static constexpr auto max_ray_depth = 6; 16 | static constexpr auto eps = 1e-4f; 17 | static constexpr auto inf = 1e10f; 18 | static constexpr auto fov = 0.23f; 19 | static constexpr auto dist_limit = 100.0f; 20 | static constexpr auto camera_pos = make_float3(0.0f, 0.32f, 3.7f); 21 | static constexpr auto light_pos = make_float3(-1.5f, 0.6f, 0.3f); 22 | static constexpr auto light_normal = make_float3(1.0f, 0.0f, 0.0f); 23 | static constexpr auto light_radius = 2.0f; 24 | 25 | Callable intersect_light = [](Float3 pos, Float3 d) noexcept { 26 | auto cos_w = dot(-d, light_normal); 27 | auto dist = dot(d, light_pos - pos); 28 | auto dist_to_light = def(inf); 29 | $if(cos_w > 0.0f & dist > 0.0f) { 30 | auto D = dist / cos_w; 31 | auto dist_to_center = distance_squared(light_pos, pos + D * d); 32 | $if(dist_to_center < light_radius * light_radius) { 33 | dist_to_light = D; 34 | }; 35 | }; 36 | return dist_to_light; 37 | }; 38 | 39 | Callable tea = [](UInt v0, UInt v1) noexcept { 40 | Var s0 = 0u; 41 | for (auto n = 0u; n < 4u; n++) { 42 | s0 += 0x9e3779b9u; 43 | v0 += ((v1 << 4) + 0xa341316cu) ^ (v1 + s0) ^ ((v1 >> 5u) + 0xc8013ea4u); 44 | v1 += ((v0 << 4) + 0xad90777du) ^ (v0 + s0) ^ ((v0 >> 5u) + 0x7e95761eu); 45 | } 46 | return v0; 47 | }; 48 | 49 | Callable rand = [](UInt &state) noexcept { 50 | constexpr auto lcg_a = 1664525u; 51 | constexpr auto lcg_c = 1013904223u; 52 | state = lcg_a * state + lcg_c; 53 | return cast(state & 0x00ffffffu) * (1.0f / static_cast(0x01000000u)); 54 | }; 55 | 56 | Callable out_dir = [&rand](Float3 n, UInt &seed) noexcept { 57 | auto u = def(1.0f, 0.0f, 0.0f); 58 | $if(abs(n.y) < 1.0f - eps) { 59 | u = normalize(cross(n, make_float3(0.0f, 1.0f, 0.0f))); 60 | }; 61 | auto v = cross(n, u); 62 | auto phi = 2.0f * std::numbers::pi_v * rand(seed); 63 | auto ay = sqrt(rand(seed)); 64 | auto ax = sqrt(1.0f - ay * ay); 65 | return ax * (cos(phi) * u + sin(phi) * v) + ay * n; 66 | }; 67 | 68 | Callable make_nested = [](Float f) noexcept { 69 | f *= 40.0f; 70 | auto i = f.cast(); 71 | $if(f < 0.0f) { 72 | f = ite(i % 2 == 0, floor(f) + 1.0f - f, f - floor(f)); 73 | }; 74 | return (f - 0.2f) * (1.0f / 40.0f); 75 | }; 76 | 77 | Callable sdf = [&make_nested](Float3 o) noexcept { 78 | auto wall = min(o.y + 0.1f, o.z + 0.4f); 79 | auto sphere = distance(o, make_float3(0.0f, 0.35f, 0.0f)) - 0.36f; 80 | auto q = abs(o - make_float3(0.8f, 0.3f, 0.0f)) - 0.3f; 81 | auto box = length(max(q, 0.0f)) + min(max(max(q.x, q.y), q.z), 0.0f); 82 | auto O = o - make_float3(-0.8f, 0.3f, 0.0f); 83 | auto d = make_float2(length(make_float2(O.x, O.z)) - 0.3f, abs(O.y) - 0.3f); 84 | auto cylinder = min(max(d.x, d.y), 0.0f) + length(max(d, 0.0f)); 85 | auto geometry = make_nested(min(min(sphere, box), cylinder)); 86 | auto g = max(geometry, -0.32f + o.y * 0.6f + o.z * 0.8f); 87 | return min(wall, g); 88 | }; 89 | 90 | Callable ray_march = [&sdf](Float3 p, Float3 d) noexcept { 91 | auto dist = def(0.0f); 92 | $for(j, 100) { 93 | auto s = sdf(p + dist * d); 94 | $if(s <= 1e-6f | dist >= inf) { $break; }; 95 | dist += s; 96 | }; 97 | return min(dist, inf); 98 | }; 99 | 100 | Callable sdf_normal = [&sdf](Float3 p) noexcept { 101 | auto d = 1e-3f; 102 | auto n = def(); 103 | auto sdf_center = sdf(p); 104 | for (auto i = 0; i < 3; i++) { 105 | auto inc = p; 106 | inc[i] += d; 107 | n[i] = (1.0f / d) * (sdf(inc) - sdf_center); 108 | } 109 | return normalize(n); 110 | }; 111 | 112 | Callable next_hit = [&ray_march, &sdf_normal](Float &closest, Float3 &normal, Float3 &c, Float3 pos, Float3 d) noexcept { 113 | closest = inf; 114 | normal = make_float3(); 115 | c = make_float3(); 116 | auto ray_march_dist = ray_march(pos, d); 117 | $if(ray_march_dist < min(dist_limit, closest)) { 118 | closest = ray_march_dist; 119 | auto hit_pos = pos + d * closest; 120 | normal = sdf_normal(hit_pos); 121 | auto t = cast((hit_pos.x + 10.0f) * 1.1f + 0.5f) % 3; 122 | c = make_float3(0.4f) + make_float3(0.3f, 0.2f, 0.3f) * ite(t == make_int3(0, 1, 2), 1.0f, 0.0f); 123 | }; 124 | }; 125 | 126 | gui::ShaderToy toy{argc, argv}; 127 | auto accum = toy.device().create_image(PixelStorage::FLOAT4, toy.size()); 128 | auto clear = toy.device().compile<2>([&] { 129 | accum.write(dispatch_id().xy(), make_float4()); 130 | }); 131 | toy.stream() << clear().dispatch(toy.size()); 132 | 133 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) noexcept { 134 | auto aspect_ratio = iResolution.x / iResolution.y; 135 | auto pos = def(camera_pos); 136 | auto seed = tea(fragCoord.x.cast(), fragCoord.y.cast()) + cast(iTime * 1e4f); 137 | auto ux = rand(seed); 138 | auto uy = rand(seed); 139 | auto d = make_float3( 140 | 2.0f * fov * (fragCoord + make_float2(ux, uy)) / iResolution.y.cast() - fov * make_float2(aspect_ratio, 1.0f) - 1e-5f, 141 | -1.0f); 142 | d = normalize(d); 143 | auto throughput = def(1.0f, 1.0f, 1.0f); 144 | auto hit_light = def(0.0f); 145 | $for(depth, max_ray_depth) { 146 | auto closest = def(0.0f); 147 | auto normal = def(); 148 | auto c = def(); 149 | next_hit(closest, normal, c, pos, d); 150 | auto dist_to_light = intersect_light(pos, d); 151 | $if(dist_to_light < closest) { 152 | hit_light = 1.0f; 153 | $break; 154 | }; 155 | $if(length_squared(normal) == 0.0f) { $break; }; 156 | auto hit_pos = pos + closest * d; 157 | d = out_dir(normal, seed); 158 | pos = hit_pos + 1e-4f * d; 159 | throughput *= c; 160 | }; 161 | auto old = accum.read(make_uint2(fragCoord)); 162 | auto new_col = lerp(old.xyz(), throughput * hit_light, 1.f / (old.w + 1.f)); 163 | accum.write(make_uint2(fragCoord), make_float4(new_col, old.w + 1.f)); 164 | return sqrt(new_col * 2.5f); 165 | }); 166 | } 167 | -------------------------------------------------------------------------------- /src/apps/seascape.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by AirGuanZ on 2021/11/13. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | using namespace luisa; 9 | using namespace compute; 10 | 11 | // Credit: https://www.shadertoy.com/view/Ms2SD1 12 | int main(int argc, char *argv[]) { 13 | 14 | constexpr int NUM_STEPS = 8; 15 | constexpr float PI = 3.14159265f; 16 | 17 | constexpr int ITER_GEOMETRY = 3; 18 | constexpr int ITER_FRAGMENT = 5; 19 | 20 | constexpr float SEA_HEIGHT = 0.6f; 21 | constexpr float SEA_CHOPPY = 4.0f; 22 | constexpr float SEA_SPEED = 0.8f; 23 | constexpr float SEA_FREQ = 0.16f; 24 | constexpr float3 SEA_BASE = float3(0.0f, 0.09f, 0.18f); 25 | constexpr float3 SEA_WATER_COLOR = float3(0.8f, 0.9f, 0.6f) * 0.6f; 26 | 27 | Callable fromEuler = [](Float3 ang) { 28 | Float2 a1 = make_float2(sin(ang.x), cos(ang.x)); 29 | Float2 a2 = make_float2(sin(ang.y), cos(ang.y)); 30 | Float2 a3 = make_float2(sin(ang.z), cos(ang.z)); 31 | Float3x3 m; 32 | m[0] = make_float3(a1.y * a3.y + a1.x * a2.x * a3.x, a1.y * a2.x * a3.x + a3.y * a1.x, -a2.y * a3.x); 33 | m[1] = make_float3(-a2.y * a1.x, a1.y * a2.y, a2.x); 34 | m[2] = make_float3(a3.y * a1.x * a2.x + a1.y * a3.x, a1.x * a3.x - a1.y * a3.y * a2.x, a2.y * a3.y); 35 | return m; 36 | }; 37 | 38 | Callable hash = [](Float2 p) { 39 | Float h = dot(p, make_float2(127.1f, 311.7f)); 40 | Float f = fract(sin(h) * 43758.5453123f); 41 | return f - floor(f); 42 | }; 43 | 44 | Callable noise = [&](Float2 p) { 45 | Float2 i = floor(p); 46 | Float2 f = p - i; 47 | Float2 u = f * f * (3.0f - 2.0f * f); 48 | return -1.0f + 2.0f * lerp( 49 | lerp(hash(i + make_float2(0.0f, 0.0f)), hash(i + make_float2(1.0, 0.0)), u.x), 50 | lerp(hash(i + make_float2(0.0f, 1.0f)), hash(i + make_float2(1.0f, 1.0f)), u.x), 51 | u.y); 52 | }; 53 | 54 | Callable reflect = [](Float3 I, Float3 N) noexcept { 55 | return I - 2.0f * dot(N, I) * N; 56 | }; 57 | 58 | Callable diffuse = [](Float3 n, Float3 l, Float p) { 59 | return pow(dot(n, l) * 0.4f + 0.6f, p); 60 | }; 61 | 62 | Callable specular = [&](Float3 n, Float3 l, Float3 e, Float s) { 63 | Float nrm = (s + 8.0f) / (PI * 8.0f); 64 | return pow(max(dot(reflect(e, n), l), 0.0f), s) * nrm; 65 | }; 66 | 67 | Callable getSkyColor = [](Float3 e) { 68 | e.y = (max(e.y, 0.0f) * 0.8f + 0.2f) * 0.8f; 69 | return make_float3(pow(1.0f - e.y, 2.0f), 1.0f - e.y, 0.6f + (1.0f - e.y) * 0.4f) * 1.1f; 70 | }; 71 | 72 | Callable seaOctave = [&](Float2 uv, Float choppy) { 73 | uv = uv + noise(uv); 74 | Float2 wv = 1.0f - abs(sin(uv)); 75 | Float2 swv = abs(cos(uv)); 76 | wv = lerp(wv, swv, wv); 77 | return pow(1.0f - pow(wv.x * wv.y, 0.65f), choppy); 78 | }; 79 | 80 | Callable map = [&](Float3 p, Float time) { 81 | Float2x2 octave_m = make_float2x2(1.6f, -1.2f, 1.2f, 1.6f); 82 | Float freq = SEA_FREQ; 83 | Float amp = SEA_HEIGHT; 84 | Float choppy = SEA_CHOPPY; 85 | Float2 uv = p.xz(); 86 | uv.x *= 0.75f; 87 | Float h = 0.0f; 88 | for (auto i = 0; i < ITER_GEOMETRY; i++) { 89 | Float d = seaOctave((uv + (1 + time * SEA_SPEED)) * freq, choppy); 90 | d += seaOctave((uv - (1 + time * SEA_SPEED)) * freq, choppy); 91 | h += d * amp; 92 | uv = octave_m * uv; 93 | freq *= 1.9f; 94 | amp *= 0.22f; 95 | choppy = lerp(choppy, 1.0f, 0.2f); 96 | } 97 | return p.y - h; 98 | }; 99 | 100 | Callable mapDetailed = [&](Float3 p, Float time) { 101 | Float2x2 octave_m = make_float2x2(1.6f, -1.2f, 1.2f, 1.6f); 102 | Float freq = SEA_FREQ; 103 | Float amp = SEA_HEIGHT; 104 | Float choppy = SEA_CHOPPY; 105 | Float2 uv = p.xz(); 106 | uv.x *= 0.75f; 107 | Float h = 0.0f; 108 | for (auto i = 0; i < ITER_FRAGMENT; i++) { 109 | Float d = seaOctave((uv + (1 + time * SEA_SPEED)) * freq, choppy); 110 | d += seaOctave((uv - (1 + time * SEA_SPEED)) * freq, choppy); 111 | h += d * amp; 112 | uv = octave_m * uv; 113 | freq *= 1.9f; 114 | amp *= 0.22f; 115 | choppy = lerp(choppy, 1.0f, 0.2f); 116 | } 117 | return p.y - h; 118 | }; 119 | 120 | Callable getSeaColor = [&](Float3 p, Float3 n, Float3 l, Float3 eye, Float3 dist) { 121 | Float fresnel = clamp(1.0f - dot(n, -eye), 0.0f, 1.0f); 122 | fresnel = pow(fresnel, 3.0f) * 0.5f; 123 | Float3 reflected = getSkyColor(reflect(eye, n)); 124 | Float3 refracted = make_float3(SEA_BASE.x, SEA_BASE.y, SEA_BASE.z) 125 | + diffuse(n, l, 80.0f) * 0.12f * make_float3(SEA_WATER_COLOR.x, SEA_WATER_COLOR.y, SEA_WATER_COLOR.z); 126 | Float3 color = lerp(refracted, reflected, fresnel); 127 | Float atten = max(1.0f - dot(dist, dist) * 0.001f, 0.0f); 128 | color += make_float3(SEA_WATER_COLOR.x, SEA_WATER_COLOR.y, SEA_WATER_COLOR.z) 129 | * (p.y - SEA_HEIGHT) * 0.18f * atten; 130 | color += make_float3(0.2f * specular(n, l, eye, 60.0f)); 131 | return color; 132 | }; 133 | 134 | Callable getNormal = [&](Float3 p, Float eps, Float time) { 135 | Float3 n; 136 | n.y = mapDetailed(p, time); 137 | n.x = mapDetailed(make_float3(p.x + eps, p.y, p.z), time) - n.y; 138 | n.z = mapDetailed(make_float3(p.x, p.y, p.z + eps), time) - n.y; 139 | n.y = eps; 140 | return normalize(n); 141 | }; 142 | 143 | Callable heightMapTracing = [&](Float3 ori, Float3 dir, Float3 &p, Float time) { 144 | Float tm = 0.0f; 145 | Float tx = 1000.0f; 146 | Float hx = map(ori + dir * tx, time); 147 | $if(hx > 0.0f) { 148 | p = ori + dir * tx; 149 | return tx; 150 | }; 151 | Float hm = map(ori + dir * tm, time); 152 | Float tmid = 0.0f; 153 | for (auto i = 0; i < NUM_STEPS; i++) { 154 | tmid = lerp(tm, tx, hm / (hm - hx)); 155 | p = ori + dir * tmid; 156 | Float hmid = map(p, time); 157 | $if(hmid < 0.0f) { 158 | tx = tmid; 159 | hx = hmid; 160 | } 161 | $else { 162 | tm = tmid; 163 | hm = hmid; 164 | }; 165 | } 166 | }; 167 | 168 | Callable getPixel = [&](Float2 coord, Float time, Float2 iResolution) { 169 | Float2 uv = coord / iResolution.xy(); 170 | uv = uv * 2.0f - 1.0f; 171 | uv.x *= iResolution.x / iResolution.y; 172 | // ray 173 | Float3 ang = make_float3(sin(time * 3.0f) * 0.1f, sin(time) * 0.2f + 0.3f, time); 174 | Float3 ori = make_float3(0.0f, 3.5f, time * 5.0f); 175 | Float3 dir = normalize(make_float3(uv.xy(), -2.0f)); 176 | dir.z += length(uv) * 0.14f; 177 | dir = transpose(fromEuler(ang)) * normalize(dir); 178 | // tracing 179 | Float3 p; 180 | heightMapTracing(ori, dir, p, time); 181 | Float3 dist = p - ori; 182 | Float3 n = getNormal(p, dot(dist, dist) * (0.1f / iResolution.x), time); 183 | Float3 light = normalize(make_float3(0.0f, 1.0f, 0.8f)); 184 | // color 185 | return lerp( 186 | getSkyColor(dir), 187 | getSeaColor(p, n, light, dir, dist), 188 | pow(smoothstep(0.0f, -0.02f, dir.y), 0.2f)); 189 | }; 190 | 191 | gui::ShaderToy toy{argc, argv}; 192 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) { 193 | Float time = iTime * 0.3f + iMouse.x * 0.01f; 194 | std::array offsets = { 195 | make_float2(-0.25f, -0.25f), 196 | make_float2(-0.25f, 0.25f), 197 | make_float2(0.25f, -0.25f), 198 | make_float2(0.25f, 0.25f), 199 | }; 200 | auto color = def(make_float3()); 201 | for (auto o : offsets) { 202 | color += 0.25f * getPixel(fragCoord + o, time, iResolution); 203 | } 204 | return pow(color, 0.65f); 205 | }); 206 | } 207 | -------------------------------------------------------------------------------- /src/apps/sphere_in_cable.cpp: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Created by Yawanari.fst on 2021/6/28. 4 | // Credit: https://www.shadertoy.com/view/wlKXWc 5 | 6 | #include 7 | 8 | using namespace luisa; 9 | using namespace luisa::compute; 10 | 11 | struct Ray { 12 | float3 pos; 13 | float3 dir; 14 | }; 15 | 16 | LUISA_STRUCT(Ray, pos, dir) {}; 17 | 18 | int main(int argc, char *argv[]) { 19 | 20 | static constexpr auto PI = 3.1415926f; 21 | static constexpr auto TAU = PI * 2.0f; 22 | static constexpr auto E = 0.01f; 23 | 24 | Callable rotate2D = [](Float rad) { 25 | Var c = cos(rad); 26 | Var s = sin(rad); 27 | return make_float2x2(c, -s, s, c); 28 | }; 29 | 30 | Callable de = [&rotate2D](Float3 p, Float iTime) { 31 | Var d = 100.0f; 32 | Var a = 0.0f; 33 | p = make_float3(p.x, rotate2D(PI / 5.0f) * p.yz()); 34 | p.y -= 0.5f; 35 | 36 | //reaction 37 | Var reaction = make_float3(cos(iTime), 0.0f, sin(iTime)) * 3.0f; 38 | p += exp(-length(reaction - p) * 1.0f) * normalize(reaction - p); 39 | 40 | //cables 41 | Var r = atan2(p.z, p.x) * 3.0f; 42 | static constexpr auto iter_count = 50; 43 | for (auto i = 0; i < iter_count; i++) { 44 | r += 0.5f / static_cast(iter_count) * TAU; 45 | Var s = 0.5f + sin(static_cast(i) * 1.618f * TAU) * 0.25f; 46 | s += sin(iTime + static_cast(i)) * 0.1f; 47 | Var q = make_float2(length(p.xz()) + cos(r) * s - 3.0f, p.y + sin(r) * s); 48 | Var dd = length(q) - 0.035f; 49 | a = ite(dd < d, static_cast(i), a); 50 | d = min(d, dd); 51 | } 52 | 53 | // sphere 54 | Var dd = length(p - reaction) - 0.1f; 55 | a = ite(dd < d, 0.0f, a); 56 | d = min(d, dd); 57 | 58 | return make_float2(d, a); 59 | }; 60 | 61 | Callable trace = [&](Var ray, Float3 color, Float md, Float iTime) { 62 | Var ad = 0.0f; 63 | Var early_return = false; 64 | for (auto i : range(128)) { 65 | Var o = de(ray.pos, iTime); 66 | if_(o.x < E, [&] { 67 | color = lerp(make_float3(0.1f, 0.1f, 0.5f), make_float3(0.0f, 0.0f, 1.0f), fract(o.y * 1.618f)); 68 | color = lerp(make_float3(1.0f, 1.0f, 1.0f), color, step(0.05f, fract(o.y * 1.618f))); 69 | color = lerp(make_float3(0.175f, 0.1f, 0.1f), color, step(0.35f, fract(o.y * 1.618f + 0.9f))); 70 | color *= exp(-i / 128.0f * 15.0f); 71 | early_return = true; 72 | break_(); 73 | }); 74 | o.x *= 0.6f; 75 | ray.pos += ray.dir * o.x; 76 | ad += o.x; 77 | if_(ad > md, [&] { break_(); }); 78 | } 79 | return ite(early_return, color, lerp(float3(0.0f), float3(1.0f), ray.dir.y * ray.dir.y)); 80 | }; 81 | 82 | gui::ShaderToy toy{argc, argv}; 83 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) { 84 | Var p = (fragCoord * 2.0f - iResolution) / min(iResolution.x, iResolution.y); 85 | Var color = float3(0.0f); 86 | 87 | Var view = float3(0.0f, 0.0f, 10.0f); 88 | Var at = normalize(float3(0.0f, 0.0f, 0.0f) - view); 89 | Var right = normalize(cross(at, float3(0.0f, 1.0f, 0.0f))); 90 | Var up = cross(right, at); 91 | Var focallength = 3.0f; 92 | 93 | Var ray; 94 | ray.pos = view; 95 | ray.dir = normalize(right * p.x + up * p.y + at * focallength); 96 | 97 | color = trace(ray, color, 20.0f, iTime); 98 | return pow(color, float3(0.454545f)); 99 | }); 100 | } 101 | -------------------------------------------------------------------------------- /src/apps/star_nest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/25. 3 | // 4 | 5 | #include 6 | 7 | using namespace luisa; 8 | using namespace luisa::compute; 9 | 10 | // Credit: https://www.shadertoy.com/view/XlfGRj 11 | int main(int argc, char *argv[]) { 12 | 13 | static constexpr auto iterations = 17u; 14 | static constexpr auto formuparam = 0.53f; 15 | static constexpr auto volsteps = 20u; 16 | static constexpr auto stepsize = 0.1f; 17 | static constexpr auto zoom = 0.800f; 18 | static constexpr auto tile = 0.850f; 19 | static constexpr auto speed = 0.010f; 20 | static constexpr auto brightness = 0.0015f; 21 | static constexpr auto darkmatter = 0.300f; 22 | static constexpr auto distfading = 0.730f; 23 | static constexpr auto saturation = 0.850f; 24 | 25 | gui::ShaderToy toy{argc, argv}; 26 | toy.run([&](Float2 fragCoord, Float2 iResolution, Float iTime, Float4 iMouse) noexcept { 27 | //get coords and direction 28 | Var uv = fragCoord.xy() / iResolution.xy() - .5f; 29 | uv.y *= iResolution.y / iResolution.x; 30 | Var dir = make_float3(uv * zoom, 1.0f); 31 | Var time = iTime * speed + .25f; 32 | 33 | //mouse rotation 34 | Var a1 = .5f + iMouse.x / iResolution.x * 2.f; 35 | Var a2 = .8f + iMouse.y / iResolution.y * 2.f; 36 | Var rot1 = make_float2x2(cos(a1), -sin(a1), sin(a1), cos(a1)); 37 | Var rot2 = make_float2x2(cos(a2), -sin(a2), sin(a2), cos(a2)); 38 | dir = make_float3(rot1 * dir.xz(), dir.y).xzy(); 39 | dir = make_float3(rot2 * dir.xy(), dir.z); 40 | Var from = make_float3(1.0f, 0.5f, 0.5f); 41 | from += make_float3(time * 2.f, time, -2.f); 42 | from = make_float3(rot1 * from.xz(), from.y).xzy(); 43 | from = make_float3(rot2 * from.xy(), from.z); 44 | 45 | //volumetric rendering 46 | Var s = 0.1f; 47 | Var fade = 1.f; 48 | Var v = make_float3(0.f); 49 | for (auto r = 0u; r < volsteps; r++) { 50 | Var p = from + s * dir * .5f; 51 | p = abs(float3(tile) - mod(p, float3(tile * 2.f)));// tiling fold 52 | Var pa = 0.0f; 53 | Var a = 0.0f; 54 | for (auto i = 0u; i < iterations; i++) { 55 | p = abs(p) / dot(p, p) - formuparam;// the magic formula 56 | a += abs(length(p) - pa); // absolute sum of average change 57 | pa = length(p); 58 | } 59 | Var dm = max(0.f, darkmatter - a * a * .001f);//dark matter 60 | a *= a * a; // add contrast 61 | if (r > 6) fade *= 1.f - dm; // dark matter, don't render near 62 | v += fade; 63 | v += make_float3(s, s * s, s * s * s * s) * a * brightness * fade;// coloring based on distance 64 | fade *= distfading; // distance fading 65 | s += stepsize; 66 | } 67 | v = lerp(make_float3(length(v)), v, saturation);//color adjust 68 | return v * .01f; 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /src/ext/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (APPLE) 2 | enable_language(OBJC OBJCXX) 3 | endif () 4 | 5 | add_library(luisa-shader-toy-ext INTERFACE) 6 | 7 | # OpenGL 8 | find_package(OpenGL REQUIRED) 9 | target_link_libraries(luisa-shader-toy-ext INTERFACE OpenGL::GL) 10 | 11 | # glfw 12 | if (NOT APPLE) 13 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 14 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) 15 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) 16 | set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) 17 | set(GLFW_LIBRARY_TYPE SHARED CACHE STRING "" FORCE) 18 | add_subdirectory(glfw) 19 | else () 20 | find_package(GLFW3 REQUIRED) 21 | endif () 22 | target_link_libraries(luisa-shader-toy-ext INTERFACE glfw) 23 | 24 | # glad 25 | add_subdirectory(glad) 26 | target_link_libraries(luisa-shader-toy-ext INTERFACE glad) 27 | 28 | # ImGui 29 | add_subdirectory(imgui) 30 | target_link_libraries(luisa-shader-toy-ext INTERFACE imgui) 31 | -------------------------------------------------------------------------------- /src/ext/glad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(glad SHARED glad/glad.c glad/glad.h glad/khrplatform.h) 2 | target_link_libraries(glad PUBLIC OpenGL::GL) 3 | target_include_directories(glad PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 4 | target_compile_definitions(glad PRIVATE GLAD_GLAPI_EXPORT_BUILD PUBLIC GLAD_GLAPI_EXPORT) 5 | -------------------------------------------------------------------------------- /src/ext/glad/glad/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | 157 | #elif defined(__VMS ) || defined(__sgi) 158 | 159 | /* 160 | * Using 161 | */ 162 | #include 163 | typedef int32_t khronos_int32_t; 164 | typedef uint32_t khronos_uint32_t; 165 | typedef int64_t khronos_int64_t; 166 | typedef uint64_t khronos_uint64_t; 167 | #define KHRONOS_SUPPORT_INT64 1 168 | #define KHRONOS_SUPPORT_FLOAT 1 169 | 170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 171 | 172 | /* 173 | * Win32 174 | */ 175 | typedef __int32 khronos_int32_t; 176 | typedef unsigned __int32 khronos_uint32_t; 177 | typedef __int64 khronos_int64_t; 178 | typedef unsigned __int64 khronos_uint64_t; 179 | #define KHRONOS_SUPPORT_INT64 1 180 | #define KHRONOS_SUPPORT_FLOAT 1 181 | 182 | #elif defined(__sun__) || defined(__digital__) 183 | 184 | /* 185 | * Sun or Digital 186 | */ 187 | typedef int khronos_int32_t; 188 | typedef unsigned int khronos_uint32_t; 189 | #if defined(__arch64__) || defined(_LP64) 190 | typedef long int khronos_int64_t; 191 | typedef unsigned long int khronos_uint64_t; 192 | #else 193 | typedef long long int khronos_int64_t; 194 | typedef unsigned long long int khronos_uint64_t; 195 | #endif /* __arch64__ */ 196 | #define KHRONOS_SUPPORT_INT64 1 197 | #define KHRONOS_SUPPORT_FLOAT 1 198 | 199 | #elif 0 200 | 201 | /* 202 | * Hypothetical platform with no float or int64 support 203 | */ 204 | typedef int khronos_int32_t; 205 | typedef unsigned int khronos_uint32_t; 206 | #define KHRONOS_SUPPORT_INT64 0 207 | #define KHRONOS_SUPPORT_FLOAT 0 208 | 209 | #else 210 | 211 | /* 212 | * Generic fallback 213 | */ 214 | #include 215 | typedef int32_t khronos_int32_t; 216 | typedef uint32_t khronos_uint32_t; 217 | typedef int64_t khronos_int64_t; 218 | typedef uint64_t khronos_uint64_t; 219 | #define KHRONOS_SUPPORT_INT64 1 220 | #define KHRONOS_SUPPORT_FLOAT 1 221 | 222 | #endif 223 | 224 | 225 | /* 226 | * Types that are (so far) the same on all platforms 227 | */ 228 | typedef signed char khronos_int8_t; 229 | typedef unsigned char khronos_uint8_t; 230 | typedef signed short int khronos_int16_t; 231 | typedef unsigned short int khronos_uint16_t; 232 | 233 | /* 234 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 236 | * to be the only LLP64 architecture in current use. 237 | */ 238 | #ifdef _WIN64 239 | typedef signed long long int khronos_intptr_t; 240 | typedef unsigned long long int khronos_uintptr_t; 241 | typedef signed long long int khronos_ssize_t; 242 | typedef unsigned long long int khronos_usize_t; 243 | #else 244 | typedef signed long int khronos_intptr_t; 245 | typedef unsigned long int khronos_uintptr_t; 246 | typedef signed long int khronos_ssize_t; 247 | typedef unsigned long int khronos_usize_t; 248 | #endif 249 | 250 | #if KHRONOS_SUPPORT_FLOAT 251 | /* 252 | * Float type 253 | */ 254 | typedef float khronos_float_t; 255 | #endif 256 | 257 | #if KHRONOS_SUPPORT_INT64 258 | /* Time types 259 | * 260 | * These types can be used to represent a time interval in nanoseconds or 261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 262 | * of nanoseconds since some arbitrary system event (e.g. since the last 263 | * time the system booted). The Unadjusted System Time is an unsigned 264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 265 | * may be either signed or unsigned. 266 | */ 267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 268 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 269 | #endif 270 | 271 | /* 272 | * Dummy value used to pad enum types to 32 bits. 273 | */ 274 | #ifndef KHRONOS_MAX_ENUM 275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 276 | #endif 277 | 278 | /* 279 | * Enumerated boolean type 280 | * 281 | * Values other than zero should be considered to be true. Therefore 282 | * comparisons should not be made against KHRONOS_TRUE. 283 | */ 284 | typedef enum { 285 | KHRONOS_FALSE = 0, 286 | KHRONOS_TRUE = 1, 287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 288 | } khronos_boolean_enum_t; 289 | 290 | #endif /* __khrplatform_h_ */ 291 | -------------------------------------------------------------------------------- /src/ext/imgui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(imgui SHARED 2 | imgui/imgui.cpp imgui/imgui.h 3 | imgui/imgui_demo.cpp 4 | imgui/imgui_draw.cpp 5 | imgui/imgui_tables.cpp 6 | imgui/imgui_widgets.cpp 7 | # imgui/backends/imgui_impl_glfw.cpp imgui/backends/imgui_impl_glfw.h 8 | imgui/backends/imgui_impl_opengl3.cpp imgui/backends/imgui_impl_opengl3.h) 9 | target_include_directories(imgui PUBLIC imgui "${CMAKE_CURRENT_SOURCE_DIR}") 10 | target_link_libraries(imgui PUBLIC glad glfw) 11 | target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_LOADER_GLAD) 12 | set_target_properties(imgui PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) 13 | -------------------------------------------------------------------------------- /src/ext/stb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(stb STATIC stb.cpp) 2 | target_include_directories(stb PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 3 | -------------------------------------------------------------------------------- /src/ext/stb/stb.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/13. 3 | // 4 | 5 | #define STB_IMAGE_IMPLEMENTATION 6 | #include 7 | 8 | #define STB_IMAGE_WRITE_IMPLEMENTATION 9 | #include 10 | -------------------------------------------------------------------------------- /src/gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LUISA_SHADER_TOY_GUI_SOURCES 2 | framerate.cpp framerate.h 3 | window.cpp window.h 4 | hid.h 5 | imgui_impl_glfw.cpp imgui_impl_glfw.h 6 | gl_texture.cpp gl_texture.h 7 | shader_toy.cpp shader_toy.h) 8 | 9 | add_library(luisa-shader-toy-gui SHARED ${LUISA_SHADER_TOY_GUI_SOURCES}) 10 | target_link_libraries(luisa-shader-toy-gui PUBLIC luisa-shader-toy-ext luisa::compute) 11 | target_compile_definitions(luisa-shader-toy-gui PUBLIC GLFW_INCLUDE_NONE) 12 | set_target_properties(luisa-shader-toy-gui PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) 13 | 14 | find_package(OpenCV CONFIG) 15 | if (OpenCV_FOUND) 16 | target_link_libraries(luisa-shader-toy-gui PUBLIC ${OpenCV_LIBS}) 17 | target_compile_definitions(luisa-shader-toy-gui PUBLIC LUISA_SHADERTOY_HAS_OPENCV=1) 18 | endif () 19 | -------------------------------------------------------------------------------- /src/gui/framerate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/13. 3 | // 4 | 5 | #include 6 | 7 | namespace luisa::gui { 8 | 9 | Framerate::Framerate(double smoothness) noexcept 10 | : _last{Clock::now()}, 11 | _alpha{smoothness}, 12 | _dt{0.0f}, 13 | _count{0u} {} 14 | 15 | double Framerate::tick() noexcept { 16 | using namespace std::chrono_literals; 17 | auto last = _last; 18 | _last = Clock::now(); 19 | _count++; 20 | auto dt = static_cast((_last - last) / 1ns) * 1e-6; 21 | _dt = _count == 0u ? dt : (_alpha * _dt + (1.0f - _alpha) * dt); 22 | return dt; 23 | } 24 | 25 | double Framerate::fps() const noexcept { 26 | return _count == 0u ? 0.0 : 1000.0 / _dt; 27 | } 28 | 29 | void Framerate::clear() noexcept { 30 | _last = Clock::now(); 31 | _dt = 0.0; 32 | _count = 0u; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/gui/framerate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | namespace luisa::gui { 10 | 11 | class Framerate { 12 | 13 | using Clock = std::chrono::high_resolution_clock; 14 | using Tick = std::chrono::high_resolution_clock::time_point; 15 | 16 | private: 17 | Tick _last; 18 | double _alpha; 19 | double _dt; 20 | uint64_t _count; 21 | 22 | public: 23 | explicit Framerate(double smoothness = 0.5) noexcept; 24 | void clear() noexcept; 25 | double tick() noexcept; 26 | [[nodiscard]] auto count() const noexcept { return _count; } 27 | [[nodiscard]] double fps() const noexcept; 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/gui/gl_texture.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/27. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | namespace luisa::gui { 9 | 10 | int GLTexture::_gl_format(PixelFormat format) noexcept { 11 | switch (format) { 12 | case PixelFormat::R8UNorm: 13 | case PixelFormat::R32F: 14 | return GL_RED; 15 | case PixelFormat::RG8UNorm: 16 | case PixelFormat::RG32F: 17 | return GL_RG; 18 | case PixelFormat::RGBA8UNorm: 19 | case PixelFormat::RGBA32F: 20 | return GL_RGBA; 21 | default: 22 | LUISA_ERROR_WITH_LOCATION("Invalid pixel format."); 23 | } 24 | } 25 | 26 | int GLTexture::_gl_type(PixelFormat format) noexcept { 27 | switch (format) { 28 | case PixelFormat::R8UNorm: 29 | case PixelFormat::RG8UNorm: 30 | case PixelFormat::RGBA8UNorm: 31 | return GL_UNSIGNED_BYTE; 32 | case PixelFormat::R32F: 33 | case PixelFormat::RG32F: 34 | case PixelFormat::RGBA32F: 35 | return GL_FLOAT; 36 | default: 37 | LUISA_ERROR_WITH_LOCATION("Invalid pixel format."); 38 | } 39 | } 40 | 41 | void GLTexture::_destroy() noexcept { 42 | if (_handle != 0u) { 43 | glDeleteTextures(1, &_handle); 44 | _handle = 0u; 45 | } 46 | } 47 | 48 | size_t GLTexture::_size_bytes() const noexcept { return _size.x * _size.y * _pixel_size; } 49 | 50 | void GLTexture::resize(uint2 size) noexcept { 51 | _size = size; 52 | glTexImage2D(GL_TEXTURE_2D, 0, _format, size.x, size.y, 0, _format, _type, nullptr); 53 | } 54 | 55 | GLTexture::GLTexture(PixelFormat format, uint2 size) noexcept 56 | : _format{_gl_format(format)}, 57 | _type{_gl_type(format)}, 58 | _pixel_size{static_cast(compute::pixel_format_size(format))} { 59 | glGenTextures(1, &_handle); 60 | glBindTexture(GL_TEXTURE_2D, _handle); 61 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 62 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 63 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 64 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 65 | resize(size); 66 | } 67 | 68 | GLTexture::~GLTexture() noexcept { _destroy(); } 69 | 70 | bool GLTexture::_upload() noexcept { 71 | auto valid = _front_buffer.size() == _size_bytes(); 72 | if (valid) { 73 | glBindTexture(GL_TEXTURE_2D, _handle); 74 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _size.x, _size.y, _format, _type, _front_buffer.data()); 75 | } 76 | std::swap(_front_buffer, _back_buffer); 77 | return valid; 78 | } 79 | 80 | uint64_t GLTexture::handle() const noexcept { return _handle; } 81 | 82 | }// namespace luisa::gui 83 | -------------------------------------------------------------------------------- /src/gui/gl_texture.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace luisa::gui { 18 | 19 | using compute::PixelFormat; 20 | 21 | class GLTexture { 22 | 23 | private: 24 | uint2 _size; 25 | int _format; 26 | int _type; 27 | uint _handle{0u}; 28 | uint _pixel_size; 29 | std::vector _front_buffer; 30 | std::vector _back_buffer; 31 | 32 | private: 33 | [[nodiscard]] static int _gl_format(PixelFormat format) noexcept; 34 | [[nodiscard]] static int _gl_type(PixelFormat format) noexcept; 35 | [[nodiscard]] size_t _size_bytes() const noexcept; 36 | void _destroy() noexcept; 37 | [[nodiscard]] bool _upload() noexcept; 38 | 39 | public: 40 | GLTexture(PixelFormat format, uint2 size) noexcept; 41 | GLTexture(PixelFormat format, uint width, uint height) noexcept : GLTexture{format, {width, height}} {} 42 | GLTexture(GLTexture &&) noexcept = delete; 43 | GLTexture(const GLTexture &) noexcept = delete; 44 | GLTexture &operator=(GLTexture &&) noexcept = delete; 45 | GLTexture &operator=(const GLTexture &) noexcept = delete; 46 | ~GLTexture() noexcept; 47 | 48 | [[nodiscard]] uint64_t handle() const noexcept; 49 | void resize(uint2 size) noexcept; 50 | 51 | template, int> = 0> 52 | bool present(Update &&update) { 53 | _back_buffer.resize(_size_bytes()); 54 | update(_back_buffer.data()); 55 | return _upload(); 56 | } 57 | }; 58 | 59 | }// namespace luisa::gui 60 | -------------------------------------------------------------------------------- /src/gui/hid.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/27. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace luisa::gui { 12 | 13 | enum Key : uint32_t { 14 | KEY_SPACE = 32u, 15 | KEY_APOSTROPHE = 39u, /* ' */ 16 | KEY_COMMA = 44u, /* , */ 17 | KEY_MINUS = 45u, /* - */ 18 | KEY_PERIOD = 46u, /* . */ 19 | KEY_SLASH = 47u, /* / */ 20 | KEY_0 = 48u, 21 | KEY_1 = 49u, 22 | KEY_2 = 50u, 23 | KEY_3 = 51u, 24 | KEY_4 = 52u, 25 | KEY_5 = 53u, 26 | KEY_6 = 54u, 27 | KEY_7 = 55u, 28 | KEY_8 = 56u, 29 | KEY_9 = 57u, 30 | KEY_SEMICOLON = 59u, /* ; */ 31 | KEY_EQUAL = 61u, /* = */ 32 | KEY_A = 65u, 33 | KEY_B = 66u, 34 | KEY_C = 67u, 35 | KEY_D = 68u, 36 | KEY_E = 69u, 37 | KEY_F = 70u, 38 | KEY_G = 71u, 39 | KEY_H = 72u, 40 | KEY_I = 73u, 41 | KEY_J = 74u, 42 | KEY_K = 75u, 43 | KEY_L = 76u, 44 | KEY_M = 77u, 45 | KEY_N = 78u, 46 | KEY_O = 79u, 47 | KEY_P = 80u, 48 | KEY_Q = 81u, 49 | KEY_R = 82u, 50 | KEY_S = 83u, 51 | KEY_T = 84u, 52 | KEY_U = 85u, 53 | KEY_V = 86u, 54 | KEY_W = 87u, 55 | KEY_X = 88u, 56 | KEY_Y = 89u, 57 | KEY_Z = 90u, 58 | KEY_LEFT_BRACKET = 91u, /* [ */ 59 | KEY_BACKSLASH = 92u, /* \ */ 60 | KEY_RIGHT_BRACKET = 93u, /* ] */ 61 | KEY_GRAVE_ACCENT = 96u, /* ` */ 62 | KEY_WORLD_1 = 161u, /* non-US #1 */ 63 | KEY_WORLD_2 = 162u, /* non-US #2 */ 64 | KEY_ESCAPE = 256u, 65 | KEY_ENTER = 257u, 66 | KEY_TAB = 258u, 67 | KEY_BACKSPACE = 259u, 68 | KEY_INSERT = 260u, 69 | KEY_DELETE = 261u, 70 | KEY_RIGHT = 262u, 71 | KEY_LEFT = 263u, 72 | KEY_DOWN = 264u, 73 | KEY_UP = 265u, 74 | KEY_PAGE_UP = 266u, 75 | KEY_PAGE_DOWN = 267u, 76 | KEY_HOME = 268u, 77 | KEY_END = 269u, 78 | KEY_CAPS_LOCK = 280u, 79 | KEY_SCROLL_LOCK = 281u, 80 | KEY_NUM_LOCK = 282u, 81 | KEY_PRINT_SCREEN = 283u, 82 | KEY_PAUSE = 284u, 83 | KEY_F1 = 290u, 84 | KEY_F2 = 291u, 85 | KEY_F3 = 292u, 86 | KEY_F4 = 293u, 87 | KEY_F5 = 294u, 88 | KEY_F6 = 295u, 89 | KEY_F7 = 296u, 90 | KEY_F8 = 297u, 91 | KEY_F9 = 298u, 92 | KEY_F10 = 299u, 93 | KEY_F11 = 300u, 94 | KEY_F12 = 301u, 95 | KEY_F13 = 302u, 96 | KEY_F14 = 303u, 97 | KEY_F15 = 304u, 98 | KEY_F16 = 305u, 99 | KEY_F17 = 306u, 100 | KEY_F18 = 307u, 101 | KEY_F19 = 308u, 102 | KEY_F20 = 309u, 103 | KEY_F21 = 310u, 104 | KEY_F22 = 311u, 105 | KEY_F23 = 312u, 106 | KEY_F24 = 313u, 107 | KEY_F25 = 314u, 108 | KEY_KP_0 = 320u, 109 | KEY_KP_1 = 321u, 110 | KEY_KP_2 = 322u, 111 | KEY_KP_3 = 323u, 112 | KEY_KP_4 = 324u, 113 | KEY_KP_5 = 325u, 114 | KEY_KP_6 = 326u, 115 | KEY_KP_7 = 327u, 116 | KEY_KP_8 = 328u, 117 | KEY_KP_9 = 329u, 118 | KEY_KP_DECIMAL = 330u, 119 | KEY_KP_DIVIDE = 331u, 120 | KEY_KP_MULTIPLY = 332u, 121 | KEY_KP_SUBTRACT = 333u, 122 | KEY_KP_ADD = 334u, 123 | KEY_KP_ENTER = 335u, 124 | KEY_KP_EQUAL = 336u, 125 | KEY_LEFT_SHIFT = 340u, 126 | KEY_LEFT_CONTROL = 341u, 127 | KEY_LEFT_ALT = 342u, 128 | KEY_LEFT_SUPER = 343u, 129 | KEY_RIGHT_SHIFT = 344u, 130 | KEY_RIGHT_CONTROL = 345u, 131 | KEY_RIGHT_ALT = 346u, 132 | KEY_RIGHT_SUPER = 347u, 133 | KEY_MENU = 348u 134 | }; 135 | 136 | enum Mouse : uint32_t { 137 | MOUSE_LEFT = 0u, 138 | MOUSE_RIGHT = 1u, 139 | MOUSE_MIDDLE = 2u 140 | }; 141 | 142 | }// namespace luisa::gui 143 | -------------------------------------------------------------------------------- /src/gui/imgui_impl_glfw.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | // (Requires: GLFW 3.1+) 5 | 6 | // Implemented features: 7 | // [X] Platform: Clipboard support. 8 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). 10 | // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). 11 | 12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 14 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 15 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 16 | 17 | // CHANGELOG 18 | // (minor and older changes stripped away, please see git history for details) 19 | // 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. 20 | // 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). 21 | // 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. 22 | // 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. 23 | // 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). 24 | // 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. 25 | // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. 26 | // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. 27 | // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. 28 | // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. 29 | // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. 30 | // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. 31 | // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). 32 | // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. 33 | // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. 34 | // 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. 35 | // 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). 36 | // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. 37 | // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. 38 | // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). 39 | // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. 40 | 41 | #include 42 | 43 | #include "imgui.h" 44 | #include "imgui_impl_glfw.h" 45 | 46 | // GLFW 47 | #include 48 | #ifdef _WIN32 49 | #undef APIENTRY 50 | #define GLFW_EXPOSE_NATIVE_WIN32 51 | #include // for glfwGetWin32Window 52 | #endif 53 | #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING 54 | #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED 55 | #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity 56 | #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300)// 3.3+ glfwGetMonitorContentScale 57 | #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface 58 | #ifdef GLFW_RESIZE_NESW_CURSOR // let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? 59 | #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR 60 | #else 61 | #define GLFW_HAS_NEW_CURSORS (0) 62 | #endif 63 | 64 | // Data 65 | struct ImGuiGlfwContext { 66 | double m_Time = 0.0; 67 | bool m_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; 68 | GLFWcursor *m_MouseCursors[ImGuiMouseCursor_COUNT] = {}; 69 | }; 70 | 71 | [[nodiscard]] static auto &get_registry() noexcept { 72 | static std::unordered_map registry; 73 | return registry; 74 | } 75 | 76 | static const char *ImGui_ImplGlfw_GetClipboardText(void *user_data) { 77 | return glfwGetClipboardString((GLFWwindow *)user_data); 78 | } 79 | 80 | static void ImGui_ImplGlfw_SetClipboardText(void *user_data, const char *text) { 81 | glfwSetClipboardString((GLFWwindow *)user_data, text); 82 | } 83 | 84 | void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { 85 | auto &&ctx = get_registry()[window]; 86 | if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(ctx.m_MouseJustPressed)) { 87 | ctx.m_MouseJustPressed[button] = true; 88 | } 89 | } 90 | 91 | void ImGui_ImplGlfw_ScrollCallback(GLFWwindow *window, double xoffset, double yoffset) { 92 | ImGuiIO &io = ImGui::GetIO(); 93 | io.MouseWheelH += (float)xoffset; 94 | io.MouseWheel += (float)yoffset; 95 | } 96 | 97 | void ImGui_ImplGlfw_KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { 98 | 99 | ImGuiIO &io = ImGui::GetIO(); 100 | if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) { 101 | if (action == GLFW_PRESS) 102 | io.KeysDown[key] = true; 103 | if (action == GLFW_RELEASE) 104 | io.KeysDown[key] = false; 105 | } 106 | 107 | // Modifiers are not reliable across systems 108 | io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; 109 | io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; 110 | io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; 111 | #ifdef _WIN32 112 | io.KeySuper = false; 113 | #else 114 | io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; 115 | #endif 116 | } 117 | 118 | void ImGui_ImplGlfw_CharCallback(GLFWwindow *window, unsigned int c) { 119 | ImGuiIO &io = ImGui::GetIO(); 120 | io.AddInputCharacter(c); 121 | } 122 | 123 | static bool ImGui_ImplGlfw_Init(GLFWwindow *window) { 124 | // Setup backend capabilities flags 125 | ImGuiIO &io = ImGui::GetIO(); 126 | io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;// We can honor GetMouseCursor() values (optional) 127 | io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 128 | io.BackendPlatformName = "imgui_impl_glfw"; 129 | 130 | // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. 131 | io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; 132 | io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; 133 | io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; 134 | io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; 135 | io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; 136 | io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; 137 | io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; 138 | io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; 139 | io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; 140 | io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; 141 | io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; 142 | io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; 143 | io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; 144 | io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; 145 | io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; 146 | io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER; 147 | io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; 148 | io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; 149 | io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; 150 | io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; 151 | io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; 152 | io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; 153 | 154 | io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; 155 | io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; 156 | io.ClipboardUserData = window; 157 | #if defined(_WIN32) 158 | io.ImeWindowHandle = (void *)glfwGetWin32Window(window); 159 | #endif 160 | 161 | // Create mouse cursors 162 | // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, 163 | // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. 164 | // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) 165 | ImGuiGlfwContext ctx; 166 | ctx.m_Time = 0.0; 167 | GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); 168 | ctx.m_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 169 | ctx.m_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); 170 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); 171 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); 172 | ctx.m_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); 173 | #if GLFW_HAS_NEW_CURSORS 174 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); 175 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); 176 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); 177 | ctx.m_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); 178 | #else 179 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 180 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 181 | ctx.m_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 182 | ctx.m_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 183 | #endif 184 | glfwSetErrorCallback(prev_error_callback); 185 | 186 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 187 | glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); 188 | glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); 189 | glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); 190 | glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); 191 | 192 | get_registry().emplace(window, ctx); 193 | return true; 194 | } 195 | 196 | bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow *window) { 197 | return ImGui_ImplGlfw_Init(window); 198 | } 199 | 200 | void ImGui_ImplGlfw_Shutdown(GLFWwindow *window) { 201 | get_registry().erase(window); 202 | } 203 | 204 | static void ImGui_ImplGlfw_UpdateMousePosAndButtons(GLFWwindow *window, ImGuiGlfwContext *ctx) { 205 | // Update buttons 206 | ImGuiIO &io = ImGui::GetIO(); 207 | for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { 208 | // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. 209 | io.MouseDown[i] = ctx->m_MouseJustPressed[i] || glfwGetMouseButton(window, i) != 0; 210 | ctx->m_MouseJustPressed[i] = false; 211 | } 212 | 213 | // Update mouse position 214 | const ImVec2 mouse_pos_backup = io.MousePos; 215 | io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 216 | #ifdef __EMSCRIPTEN__ 217 | const bool focused = true;// Emscripten 218 | #else 219 | const bool focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; 220 | #endif 221 | if (focused) { 222 | if (io.WantSetMousePos) { 223 | glfwSetCursorPos(window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); 224 | } else { 225 | double mouse_x, mouse_y; 226 | glfwGetCursorPos(window, &mouse_x, &mouse_y); 227 | io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); 228 | } 229 | } 230 | } 231 | 232 | static void ImGui_ImplGlfw_UpdateMouseCursor(GLFWwindow *window, const ImGuiGlfwContext *ctx) { 233 | ImGuiIO &io = ImGui::GetIO(); 234 | if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) 235 | return; 236 | 237 | ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); 238 | if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { 239 | // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor 240 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); 241 | } else { 242 | // Show OS mouse cursor 243 | // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. 244 | glfwSetCursor(window, ctx->m_MouseCursors[imgui_cursor] ? ctx->m_MouseCursors[imgui_cursor] : ctx->m_MouseCursors[ImGuiMouseCursor_Arrow]); 245 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 246 | } 247 | } 248 | 249 | void ImGui_ImplGlfw_NewFrame(GLFWwindow *window) { 250 | ImGuiIO &io = ImGui::GetIO(); 251 | IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); 252 | 253 | // Setup display size (every frame to accommodate for window resizing) 254 | int w, h; 255 | int display_w, display_h; 256 | glfwGetWindowSize(window, &w, &h); 257 | glfwGetFramebufferSize(window, &display_w, &display_h); 258 | io.DisplaySize = ImVec2((float)w, (float)h); 259 | if (w > 0 && h > 0) 260 | io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); 261 | 262 | auto &&ctx = get_registry()[window]; 263 | // Setup time step 264 | double current_time = glfwGetTime(); 265 | io.DeltaTime = ctx.m_Time > 0.0 ? (float)(current_time - ctx.m_Time) : (float)(1.0f / 60.0f); 266 | ctx.m_Time = current_time; 267 | 268 | ImGui_ImplGlfw_UpdateMousePosAndButtons(window, &ctx); 269 | ImGui_ImplGlfw_UpdateMouseCursor(window, &ctx); 270 | } 271 | -------------------------------------------------------------------------------- /src/gui/imgui_impl_glfw.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support. 7 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 8 | // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. 9 | // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). 10 | 11 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 12 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 13 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 14 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 15 | 16 | // About GLSL version: 17 | // The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. 18 | // Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | 23 | struct GLFWwindow; 24 | struct ImGuiGlfwContext; 25 | 26 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window); 27 | IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(GLFWwindow *window); 28 | IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(GLFWwindow *window); 29 | 30 | // GLFW callbacks 31 | // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any. 32 | // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks. 33 | IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 34 | IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 35 | IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 36 | IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); 37 | -------------------------------------------------------------------------------- /src/gui/shader_toy.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/27. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #if LUISA_SHADERTOY_HAS_OPENCV 9 | #include 10 | #endif 11 | 12 | namespace luisa::gui { 13 | 14 | using namespace compute; 15 | 16 | template 17 | static void with_panel(const char *name, F &&f) { 18 | ImGui::Begin(name, nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); 19 | f(); 20 | ImGui::End(); 21 | } 22 | 23 | void ShaderToy::run(const MainShader &main_shader) noexcept { 24 | auto shader = _device->compile(Kernel2D{[&](ImageFloat image, Float time, Float4 cursor) noexcept { 25 | using namespace compute; 26 | auto xy = dispatch_id().xy(); 27 | auto resolution = dispatch_size().xy(); 28 | auto col = main_shader(make_float2(make_uint2(xy.x, resolution.y - 1u - xy.y)) + 0.5f, 29 | make_float2(resolution), time, cursor); 30 | image.write(xy, make_float4(col, 1.0f)); 31 | }}); 32 | if (_dump_file.empty()) { 33 | _run_display(shader); 34 | } else { 35 | _run_dump(shader); 36 | } 37 | } 38 | 39 | ShaderToy::ShaderToy(int argc, const char *const *argv) noexcept 40 | : _context{argv[0]} { 41 | Context context{argv[0]}; 42 | luisa::string backend{"unknown"}; 43 | auto device_id = 0u; 44 | for (auto i = 1u; i < argc; i++) { 45 | using namespace std::string_view_literals; 46 | auto next_arg = [&] { 47 | if (i + 1u >= argc) { 48 | LUISA_ERROR_WITH_LOCATION( 49 | "Missing argument for option: ", 50 | argv[i]); 51 | } 52 | return argv[++i]; 53 | }; 54 | if (argv[i] == "-b"sv || argv[i] == "--backend"sv) { 55 | backend = next_arg(); 56 | } else if (argv[i] == "-s"sv || argv[i] == "--size"sv) { 57 | auto s = next_arg(); 58 | auto n = std::sscanf(s, "%ux%u", &_size.x, &_size.y); 59 | LUISA_ASSERT(n != 0, "Invalid size: {}", s); 60 | if (n == 1) { _size.y = _size.x; } 61 | _size = luisa::clamp(_size, 1u, 4096u); 62 | } else if (argv[i] == "-d"sv || argv[i] == "--device"sv) { 63 | device_id = std::atoi(next_arg()); 64 | } else if (argv[i] == "-t"sv || argv[i] == "--step"sv) { 65 | _step = std::clamp(std::atof(next_arg()), 0., 1000.); 66 | } else if (argv[i] == "-o"sv || argv[i] == "--dump"sv) { 67 | _dump_file = next_arg(); 68 | } else if (argv[i] == "-n"sv || argv[i] == "--frames"sv) { 69 | _dump_frames = std::atoi(next_arg()); 70 | } else if (argv[i] == "--fps"sv) { 71 | _dump_fps = std::clamp(std::atof(next_arg()), 1., 200.); 72 | } else { 73 | LUISA_ERROR_WITH_LOCATION("Unknown option: {}", argv[i]); 74 | } 75 | } 76 | _title = std::filesystem::canonical(argv[0]).filename().replace_extension("").string(); 77 | for (auto &c : _title) { c = c == '_' ? ' ' : c; } 78 | auto is_first = true; 79 | for (auto &c : _title) { 80 | if (is_first) { c = static_cast(std::toupper(c)); } 81 | is_first = c == ' '; 82 | } 83 | _device = luisa::make_unique(context.create_device( 84 | backend, luisa::format("{{\"index\": {}}}", device_id))); 85 | _stream = _device->create_stream(); 86 | } 87 | 88 | void ShaderToy::_run_display(const compute::Shader2D, float, float4> &shader) noexcept { 89 | 90 | auto device_image = _device->create_image(PixelStorage::BYTE4, _size); 91 | auto event = _device->create_event(); 92 | Window window{_title, _size}; 93 | GLTexture texture{PixelFormat::RGBA8UNorm, _size}; 94 | _stream << event.signal(); 95 | 96 | auto prev_key_up = false; 97 | auto show_console = true; 98 | auto cursor = float4(0.0f); 99 | auto dragging = false; 100 | Framerate framerate{0.8}; 101 | window.run([&] { 102 | auto render_size = device_image.size(); 103 | auto window_size = window.size(); 104 | if (window.mouse_down(MOUSE_LEFT)) { 105 | auto curr = window.cursor(); 106 | curr = float2(curr.x, static_cast(window_size.y) - curr.y); 107 | if (dragging) { 108 | cursor = make_float4(curr, cursor.zw()); 109 | } else { 110 | cursor = make_float4(curr, curr * float2(1.0f, -1.0f)); 111 | dragging = true; 112 | } 113 | } else if (window.mouse_up(MOUSE_LEFT)) { 114 | cursor = make_float4(cursor.xy(), -abs(cursor.zw())); 115 | dragging = false; 116 | } 117 | 118 | auto time = _step == 0. ? window.time() : static_cast(framerate.count()) * _step; 119 | if (texture.present([&](void *pixels) noexcept { 120 | event.synchronize(); 121 | _stream << shader(device_image, static_cast(time), cursor).dispatch(window_size) 122 | << device_image.copy_to(pixels) 123 | << event.signal(); 124 | })) { 125 | ImVec2 background_size{static_cast(render_size.x), static_cast(render_size.y)}; 126 | ImGui::GetBackgroundDrawList()->AddImage(reinterpret_cast(texture.handle()), {}, background_size); 127 | } 128 | 129 | framerate.tick(); 130 | auto fps = framerate.fps(); 131 | auto spp = framerate.count(); 132 | if (show_console) { 133 | with_panel("Console", [&] { 134 | ImGui::Text("Frame: %llu", static_cast(spp)); 135 | ImGui::Text("Time: %.2lfs", time); 136 | ImGui::Text("FPS: %.1lf", fps); 137 | ImGui::Text("Size: %ux%u", window_size.x, window_size.y); 138 | }); 139 | } 140 | if (window.key_down(KEY_ESCAPE)) { 141 | window.notify_close(); 142 | } 143 | if (prev_key_up && (window.key_down(KEY_LEFT_CONTROL) || window.key_down(KEY_RIGHT_CONTROL))) { 144 | show_console = !show_console; 145 | } 146 | prev_key_up = window.key_up(KEY_LEFT_CONTROL) && window.key_up(KEY_RIGHT_CONTROL); 147 | }); 148 | } 149 | 150 | void ShaderToy::_run_dump(const compute::Shader2D, float, float4> &shader) noexcept { 151 | #if LUISA_SHADERTOY_HAS_OPENCV 152 | auto device_image = _device->create_image(PixelStorage::BYTE4, _size); 153 | cv::Size size{static_cast(_size.x), static_cast(_size.y)}; 154 | cv::Mat frame{size, CV_8UC4, cv::Scalar::all(0)}; 155 | cv::Mat cvt_frame{size, CV_8UC3, cv::Scalar::all(0)}; 156 | auto fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v'); 157 | auto path = std::filesystem::absolute(_dump_file); 158 | cv::VideoWriter video{path.string(), fourcc, _dump_fps, size}; 159 | LUISA_ASSERT(video.isOpened(), "Failed to open video file: {}", path.string()); 160 | for (auto i = 0u; i < _dump_frames; i++) { 161 | _stream << shader(device_image, static_cast(i * _step), float4(0.0f)).dispatch(_size) 162 | << device_image.copy_to(frame.data) 163 | << synchronize(); 164 | LUISA_INFO("Frame {} / {}", i + 1u, _dump_frames); 165 | cv::cvtColor(frame, cvt_frame, cv::COLOR_RGBA2BGR); 166 | video << cvt_frame; 167 | } 168 | #else 169 | LUISA_WARNING("OpenCV is not available. Dumping is disabled."); 170 | #endif 171 | } 172 | 173 | }// namespace luisa::gui 174 | -------------------------------------------------------------------------------- /src/gui/shader_toy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/27. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace luisa::gui { 18 | 19 | using compute::Callable; 20 | using compute::Context; 21 | using compute::Device; 22 | using compute::Event; 23 | using compute::Float; 24 | using compute::Float2; 25 | using compute::Float4; 26 | using compute::Image; 27 | using compute::ImageFloat; 28 | using compute::ImageVar; 29 | using compute::Kernel2D; 30 | using compute::Stream; 31 | using compute::Shader; 32 | 33 | class ShaderToy { 34 | 35 | public: 36 | using MainShader = Callable; 41 | 42 | private: 43 | Context _context; 44 | luisa::unique_ptr _device; 45 | Stream _stream; 46 | std::string _title{}; 47 | uint2 _size{1280u, 720u}; 48 | double _step{0.}; 49 | luisa::string _dump_file{}; 50 | uint _dump_frames{1u}; 51 | double _dump_fps{24.}; 52 | 53 | private: 54 | void _run_display(const compute::Shader2D, float, float4> &shader) noexcept; 55 | void _run_dump(const compute::Shader2D, float, float4> &shader) noexcept; 56 | 57 | public: 58 | ShaderToy(int argc, const char *const *argv) noexcept; 59 | void run(const MainShader &shader) noexcept; 60 | [[nodiscard]] auto &device() noexcept { return *_device; } 61 | [[nodiscard]] auto &stream() noexcept { return _stream; } 62 | [[nodiscard]] auto size() const noexcept { return _size; } 63 | }; 64 | 65 | }// namespace luisa::gui 66 | -------------------------------------------------------------------------------- /src/gui/window.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/13. 3 | // 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace luisa::gui { 16 | 17 | Window::Window(std::string_view title, uint32_t width, uint32_t height) noexcept 18 | : _time_start{glfwGetTime()} { 19 | 20 | static std::once_flag once_flag; 21 | std::call_once(once_flag, [] { 22 | glfwSetErrorCallback([](int error, const char *description) noexcept { 23 | LUISA_ERROR_WITH_LOCATION("GLFW Error {}: {}.", error, description); 24 | }); 25 | if (!glfwInit()) { LUISA_ERROR_WITH_LOCATION("Failed to initialize GLFW."); } 26 | }); 27 | 28 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 29 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); 30 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 31 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 32 | glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 33 | _handle = glfwCreateWindow(static_cast(width), static_cast(height), 34 | std::string{title}.c_str(), nullptr, nullptr); 35 | if (_handle == nullptr) { 36 | LUISA_ERROR_WITH_LOCATION("Failed to create window '{}' with size {}x{}.", title, width, height); 37 | } 38 | 39 | glfwMakeContextCurrent(_handle); 40 | glfwSwapInterval(0); 41 | 42 | if (gladLoadGLLoader(reinterpret_cast(glfwGetProcAddress)) == 0) { 43 | LUISA_ERROR_WITH_LOCATION("Failed to initialize OpenGL loader."); 44 | } 45 | 46 | // Setup Dear ImGui context 47 | IMGUI_CHECKVERSION(); 48 | ImGui::SetCurrentContext(nullptr); 49 | _context = ImGui::CreateContext(); 50 | ImGui::StyleColorsLight(); 51 | ImGui_ImplGlfw_InitForOpenGL(_handle); 52 | ImGui_ImplOpenGL3_Init("#version 150"); 53 | ImGui_ImplOpenGL3_CreateDeviceObjects(); 54 | ImGui::SetCurrentContext(nullptr); 55 | } 56 | 57 | void Window::_begin_frame() noexcept { 58 | glfwMakeContextCurrent(_handle); 59 | ImGui::SetCurrentContext(_context); 60 | glfwPollEvents(); 61 | ImGui_ImplOpenGL3_NewFrame(); 62 | ImGui_ImplGlfw_NewFrame(_handle); 63 | ImGui::NewFrame(); 64 | int display_w, display_h; 65 | glfwGetFramebufferSize(_handle, &display_w, &display_h); 66 | glViewport(0, 0, display_w, display_h); 67 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 68 | glClear(GL_COLOR_BUFFER_BIT); 69 | } 70 | 71 | void Window::_end_frame() noexcept { 72 | ImGui::Render(); 73 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 74 | glfwSwapBuffers(_handle); 75 | ImGui::SetCurrentContext(nullptr); 76 | glfwMakeContextCurrent(nullptr); 77 | } 78 | 79 | Window::~Window() noexcept { _destroy(); } 80 | 81 | bool Window::should_close() const noexcept { 82 | return _handle == nullptr || glfwWindowShouldClose(_handle); 83 | } 84 | 85 | void Window::notify_close() noexcept { 86 | if (_handle != nullptr) { glfwSetWindowShouldClose(_handle, true); } 87 | } 88 | 89 | bool Window::key_down(Key key) const noexcept { 90 | return glfwGetKey(_handle, static_cast(key)) == GLFW_PRESS; 91 | } 92 | 93 | bool Window::mouse_down(Mouse button) const noexcept { 94 | return glfwGetMouseButton(_handle, static_cast(button)) == GLFW_PRESS; 95 | } 96 | 97 | bool Window::key_up(Key key) const noexcept { 98 | return glfwGetKey(_handle, static_cast(key)) == GLFW_RELEASE; 99 | } 100 | 101 | bool Window::mouse_up(Mouse button) const noexcept { 102 | return glfwGetMouseButton(_handle, static_cast(button)) == GLFW_RELEASE; 103 | } 104 | 105 | float2 Window::cursor() const noexcept { 106 | auto x = 0.0; 107 | auto y = 0.0; 108 | glfwGetCursorPos(_handle, &x, &y); 109 | auto window_size = size(); 110 | return {std::clamp(static_cast(x), 0.0f, static_cast(window_size.x)), 111 | std::clamp(static_cast(y), 0.0f, static_cast(window_size.y))}; 112 | } 113 | 114 | void Window::_destroy() noexcept { 115 | if (_context != nullptr) { 116 | ImGui::SetCurrentContext(_context); 117 | ImGui_ImplOpenGL3_Shutdown(); 118 | ImGui_ImplGlfw_Shutdown(_handle); 119 | ImGui::DestroyContext(_context); 120 | ImGui::SetCurrentContext(nullptr); 121 | } 122 | if (_handle != nullptr) { 123 | glfwDestroyWindow(_handle); 124 | } 125 | _context = nullptr; 126 | _handle = nullptr; 127 | } 128 | 129 | Window &Window::operator=(Window &&rhs) noexcept { 130 | if (&rhs != this) { 131 | _destroy(); 132 | _time_start = rhs._time_start; 133 | _handle = rhs._handle; 134 | _context = rhs._context; 135 | rhs._handle = nullptr; 136 | rhs._context = nullptr; 137 | } 138 | return *this; 139 | } 140 | 141 | Window::Window(Window &&another) noexcept 142 | : _time_start{another._time_start}, 143 | _handle{another._handle}, 144 | _context{another._context} { 145 | another._handle = nullptr; 146 | another._context = nullptr; 147 | } 148 | 149 | uint2 Window::size() const noexcept { 150 | auto w = 0; 151 | auto h = 0; 152 | glfwGetWindowSize(_handle, &w, &h); 153 | return make_uint2(w, h); 154 | } 155 | 156 | uint2 Window::framebuffer_size() const noexcept { 157 | auto w = 0; 158 | auto h = 0; 159 | glfwGetFramebufferSize(_handle, &w, &h); 160 | return make_uint2(w, h); 161 | } 162 | 163 | float Window::time() const noexcept { 164 | return static_cast(glfwGetTime() - _time_start); 165 | } 166 | 167 | }// namespace luisa::gui 168 | -------------------------------------------------------------------------------- /src/gui/window.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mike Smith on 2021/6/13. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace luisa::gui { 17 | 18 | class Window : concepts::Noncopyable { 19 | 20 | public: 21 | using Handle = struct GLFWwindow *; 22 | 23 | private: 24 | double _time_start; 25 | Handle _handle{nullptr}; 26 | ImGuiContext *_context{nullptr}; 27 | 28 | private: 29 | void _begin_frame() noexcept; 30 | void _end_frame() noexcept; 31 | void _destroy() noexcept; 32 | 33 | public: 34 | Window(std::string_view title, uint32_t width, uint32_t height) noexcept; 35 | Window(std::string_view title, uint2 size) noexcept : Window{title, size.x, size.y} {} 36 | ~Window() noexcept; 37 | 38 | Window(Window &&another) noexcept; 39 | Window &operator=(Window &&rhs) noexcept; 40 | 41 | [[nodiscard]] bool should_close() const noexcept; 42 | void notify_close() noexcept; 43 | 44 | template, int> = 0> 45 | void with_frame(Frame &&frame) { 46 | if (!should_close()) { 47 | _begin_frame(); 48 | frame(); 49 | _end_frame(); 50 | } else { 51 | _destroy(); 52 | } 53 | } 54 | 55 | template, int> = 0> 56 | void run(Frame &&frame) { 57 | while (!should_close()) { 58 | _begin_frame(); 59 | frame(); 60 | _end_frame(); 61 | } 62 | _destroy(); 63 | } 64 | 65 | [[nodiscard]] bool key_down(Key key) const noexcept; 66 | [[nodiscard]] bool key_up(Key key) const noexcept; 67 | [[nodiscard]] bool mouse_down(Mouse button) const noexcept; 68 | [[nodiscard]] bool mouse_up(Mouse button) const noexcept; 69 | [[nodiscard]] float2 cursor() const noexcept; 70 | [[nodiscard]] uint2 size() const noexcept; 71 | [[nodiscard]] uint2 framebuffer_size() const noexcept; 72 | [[nodiscard]] float time() const noexcept; 73 | }; 74 | 75 | }// namespace luisa::gui 76 | -------------------------------------------------------------------------------- /tools/record.py: -------------------------------------------------------------------------------- 1 | from sys import argv 2 | from os import listdir, system 3 | from os.path import dirname, abspath 4 | 5 | 6 | if __name__ == "__main__": 7 | exe_dir = argv[1] 8 | script_dir = f"{dirname(abspath(__file__))}" 9 | apps = [f[:-4] for f in listdir(f"{script_dir}/../src/apps") if f.endswith(".cpp")] 10 | print(apps) 11 | for app in apps: 12 | print(f"Recording {app}") 13 | system(f"{exe_dir}/{app} -o {script_dir}/{app}.mp4 -t 0.033333 --size 1280x720 --fps 30 -n 300 -b metal") 14 | --------------------------------------------------------------------------------