├── .appveyor.yml ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── assets ├── README.md ├── cmap_divergent.png ├── cmap_hot.png ├── dmap.png ├── gold-metallic-paint2.binary └── topanga.hdr ├── demo-fisheye ├── README.md ├── fisheye-preview.gif ├── fisheye.cpp ├── glad │ ├── glad.c │ └── glad.h └── shaders │ ├── background.glsl │ └── viewer.glsl ├── demo-hello-imgui ├── glad │ ├── glad.c │ └── glad.h └── hello-imgui.cpp ├── demo-hello-window ├── glad │ ├── glad.c │ └── glad.h └── hello-window.cpp ├── demo-isubd-bs ├── glad │ ├── glad.c │ └── glad.h ├── isubd-bs.cpp └── shaders │ ├── bs.glsl │ ├── bsnet_edges.glsl │ ├── bsnet_vertices.glsl │ ├── isubd_bs.glsl │ └── viewer.glsl ├── demo-isubd-cc ├── glad │ ├── glad.c │ └── glad.h ├── isubd-cc.cpp └── shaders │ ├── cc.glsl │ ├── cc_gs.glsl │ ├── ccnet.glsl │ ├── fcull.glsl │ ├── isubd.glsl │ └── viewer.glsl ├── demo-isubd-ccmesh ├── glad │ ├── glad.c │ └── glad.h ├── halfedge.inl ├── isubd-ccmesh.cpp └── shaders │ ├── cc_ts.glsl │ ├── cull.glsl │ ├── fcull.glsl │ ├── isubd.glsl │ ├── terrain_cs_lod.glsl │ ├── terrain_cs_render.glsl │ ├── terrain_gs.glsl │ ├── terrain_ts.glsl │ └── viewer.glsl ├── demo-isubd-terrain ├── README.md ├── glad │ ├── glad.c │ └── glad.h ├── isubd-terrain.cpp ├── isubd-terrain.exe ├── preview.png └── shaders │ ├── fcull.glsl │ ├── isubd.glsl │ ├── terrain_common.glsl │ ├── terrain_cs_lod.glsl │ ├── terrain_cs_render.glsl │ ├── terrain_gs.glsl │ ├── terrain_ms.glsl │ ├── terrain_ts.glsl │ ├── terrain_updateIndirect_cs.glsl │ └── viewer.glsl ├── demo-merl ├── README.md ├── dj_brdf.h ├── glad │ ├── glad.c │ └── glad.h ├── merl.cpp ├── npf.bin ├── preview.png └── shaders │ ├── background.glsl │ ├── brdf_merl.glsl │ ├── ggx.glsl │ ├── npf.glsl │ ├── pivot.glsl │ ├── sphere.glsl │ └── viewer.glsl ├── imgui ├── imgui_impl.cpp └── imgui_impl.h └── plot-brdf ├── README.md ├── dj_brdf.h ├── glad ├── glad.c └── glad.h ├── plot-brdf.cpp ├── preview.png ├── shaders ├── background.glsl ├── brdf_merl.glsl ├── ggx.glsl ├── parametric.glsl ├── samples.glsl ├── sphere.glsl ├── viewer.glsl ├── wi_angle.glsl ├── wi_dir.glsl └── wire.glsl └── vidgen.py /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | os: Visual Studio 2017 5 | test: off 6 | branches: 7 | only: 8 | - master 9 | clone_folder: C:\gl 10 | install: 11 | - git submodule update --init --recursive 12 | build_script: 13 | - cd C:\gl 14 | - mkdir build 15 | - cd build 16 | - cmake -G "Visual Studio 15 2017 Win64" .. 17 | - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 18 | - set MSBuildOptions=/v:m /p:Configuration=Release /logger:%MSBuildLogger% 19 | - msbuild %MSBuildOptions% opengl.sln 20 | 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/glfw"] 2 | path = submodules/glfw 3 | url = https://github.com/glfw/glfw.git 4 | [submodule "submodules/imgui"] 5 | path = submodules/imgui 6 | url = https://github.com/ocornut/imgui.git 7 | [submodule "submodules/stb"] 8 | path = submodules/stb 9 | url = https://github.com/nothings/stb.git 10 | [submodule "submodules/dj_opengl"] 11 | path = submodules/dj_opengl 12 | url = https://github.com/jdupuy/dj_opengl.git 13 | [submodule "submodules/dj_algebra"] 14 | path = submodules/dj_algebra 15 | url = https://github.com/jdupuy/dj_algebra.git 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: false 3 | dist: trusty 4 | branches: 5 | only: 6 | - master 7 | matrix: 8 | include: 9 | - os: linux 10 | compiler: gcc-4.8 11 | addons: 12 | apt: 13 | packages: 14 | - cmake 15 | - g++-4.8 16 | - libglu1-mesa-dev 17 | - libxxf86vm-dev 18 | - libxrandr-dev 19 | - libxinerama-dev 20 | - libxcursor-dev 21 | - libxi-dev 22 | - libx11-dev 23 | script: 24 | - cmake --version 25 | - cmake . 26 | - make -j 2 27 | 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.1) 2 | set(CMAKE_CXX_STANDARD 11) 3 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 4 | set(CMAKE_BUILD_TYPE Debug) 5 | 6 | # disable GLFW docs, examples and tests 7 | # see http://www.glfw.org/docs/latest/build_guide.html 8 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) 9 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) 10 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 11 | 12 | # set path to dependencies 13 | add_subdirectory(submodules/glfw) 14 | include_directories(submodules/glfw/include) 15 | include_directories(submodules/imgui) 16 | include_directories(submodules/stb) 17 | include_directories(submodules/dj_opengl) 18 | include_directories(submodules/dj_algebra) 19 | 20 | # imgui implementation (compiled as a library) 21 | aux_source_directory(submodules/imgui IMGUI_SRC_FILES) 22 | set(IMGUI_INCLUDE_DIR imgui submodules/imgui) 23 | add_library(imgui STATIC imgui/imgui_impl.cpp ${IMGUI_SRC_FILES}) 24 | 25 | 26 | # ------------------------------------------------------------------------------ 27 | project (opengl) 28 | 29 | # compiler specific options 30 | if(MSVC OR MSVC_IDE) 31 | add_definitions("/D_CRT_SECURE_NO_WARNINGS") 32 | endif() 33 | 34 | 35 | # ------------------------------------------------------------------------------ 36 | set(SRC_DIR demo-hello-window) 37 | include_directories(${SRC_DIR}) 38 | add_executable(hello-window ${SRC_DIR}/hello-window.cpp ${SRC_DIR}/glad/glad.c) 39 | target_link_libraries(hello-window glfw) 40 | 41 | # ------------------------------------------------------------------------------ 42 | set(SRC_DIR demo-hello-imgui) 43 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 44 | aux_source_directory(${SRC_DIR} SRC_FILES) 45 | add_executable(hello-imgui ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 46 | target_link_libraries(hello-imgui glfw imgui) 47 | unset(SRC_FILES) 48 | 49 | # ------------------------------------------------------------------------------ 50 | set(SRC_DIR demo-fisheye) 51 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 52 | aux_source_directory(${SRC_DIR} SRC_FILES) 53 | add_executable(fisheye ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 54 | target_link_libraries(fisheye glfw imgui) 55 | target_compile_definitions(fisheye PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 56 | unset(SRC_FILES) 57 | 58 | # ------------------------------------------------------------------------------ 59 | set(SRC_DIR demo-merl) 60 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 61 | aux_source_directory(${SRC_DIR} SRC_FILES) 62 | add_executable(merl ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 63 | target_link_libraries(merl glfw imgui) 64 | target_compile_definitions(merl PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 65 | unset(SRC_FILES) 66 | 67 | # ------------------------------------------------------------------------------ 68 | set(SRC_DIR plot-brdf) 69 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 70 | aux_source_directory(${SRC_DIR} SRC_FILES) 71 | add_executable(brdf-plot ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 72 | target_link_libraries(brdf-plot glfw imgui) 73 | target_compile_definitions(brdf-plot PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 74 | unset(SRC_FILES) 75 | 76 | # ------------------------------------------------------------------------------ 77 | set(SRC_DIR demo-isubd-terrain) 78 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 79 | aux_source_directory(${SRC_DIR} SRC_FILES) 80 | add_executable(isubd-terrain ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 81 | target_link_libraries(isubd-terrain glfw imgui) 82 | target_compile_definitions(isubd-terrain PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 83 | unset(SRC_FILES) 84 | 85 | # ------------------------------------------------------------------------------ 86 | set(SRC_DIR demo-isubd-bs) 87 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 88 | aux_source_directory(${SRC_DIR} SRC_FILES) 89 | add_executable(isubd-bs ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 90 | target_link_libraries(isubd-bs glfw imgui) 91 | target_compile_definitions(isubd-bs PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 92 | unset(SRC_FILES) 93 | 94 | # ------------------------------------------------------------------------------ 95 | set(SRC_DIR demo-isubd-cc) 96 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 97 | aux_source_directory(${SRC_DIR} SRC_FILES) 98 | add_executable(isubd-cc ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 99 | target_link_libraries(isubd-cc glfw imgui) 100 | target_compile_definitions(isubd-cc PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 101 | unset(SRC_FILES) 102 | 103 | # ------------------------------------------------------------------------------ 104 | set(SRC_DIR demo-isubd-ccmesh) 105 | include_directories(${SRC_DIR} ${IMGUI_INCLUDE_DIR}) 106 | aux_source_directory(${SRC_DIR} SRC_FILES) 107 | add_executable(isubd-ccmesh ${SRC_FILES} ${SRC_DIR}/glad/glad.c) 108 | target_link_libraries(isubd-ccmesh glfw imgui) 109 | target_compile_definitions(isubd-ccmesh PUBLIC -DPATH_TO_SRC_DIRECTORY="${CMAKE_SOURCE_DIR}/${SRC_DIR}/" -DPATH_TO_ASSET_DIRECTORY="${CMAKE_SOURCE_DIR}/assets/") 110 | unset(SRC_FILES) 111 | 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenGL Demos 2 | 3 | [![Build Status](https://travis-ci.org/jdupuy/opengl-framework.svg?branch=master)](https://travis-ci.org/jdupuy/opengl-framework) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/19er6t9j73qx1jvs?svg=true)](https://ci.appveyor.com/project/jdupuy/opengl-framework) 5 | 6 | ### Details 7 | 8 | The goal of this repository is to compile OpenGL programs I have written and I find worth sharing. This includes programs I used to generate results of technical papers I have published, or written just for fun :) 9 | 10 | ### License 11 | 12 | Apart from the submodule folder, the code from this repository is released in public domain. 13 | 14 | ### Cloning 15 | 16 | Clone the repository and all its submodules using the following command: 17 | ```sh 18 | git clone --recursive git@github.com:jdupuy/opengl-framework.git 19 | ``` 20 | 21 | If you accidentally omitted the `--recursive` flag when cloning the repository you can retrieve the submodules like so: 22 | ```sh 23 | git submodule update --init --recursive 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # Asset Directory 2 | 3 | This directory contains various file I load in some of the demos I provide. 4 | 5 | * dmap.png -- public domain displacement map by Cyril Jover (https://twitter.com/jovercyril) 6 | * cmap_divergent.png -- public domain color map 7 | * cmap_hot.png -- public domain color map 8 | * gold-metallic-paint2.binary -- MERL BRDF (from https://www.merl.com/brdf/; © Copyright 2007 Mitsubishi Electric Research Laboratories) 9 | * topanga.hdr -- HDR envmap (Topanga Forest by Blochi, available for download at http://www.hdrlabs.com/sibl/archive.html; Creative Commons Attribution-Noncommercial-Share Alike 3.0 License) 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/cmap_divergent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/assets/cmap_divergent.png -------------------------------------------------------------------------------- /assets/cmap_hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/assets/cmap_hot.png -------------------------------------------------------------------------------- /assets/dmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/assets/dmap.png -------------------------------------------------------------------------------- /assets/gold-metallic-paint2.binary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/assets/gold-metallic-paint2.binary -------------------------------------------------------------------------------- /assets/topanga.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/assets/topanga.hdr -------------------------------------------------------------------------------- /demo-fisheye/README.md: -------------------------------------------------------------------------------- 1 | ## Fisheye Projection Demo 2 | 3 | This code compares the fisheye projection with the conventional perspective projection 4 | by rendering an HDR cubemap. 5 | 6 | Compared to the perspective projection, the fisheye projection is conformal, i.e., 7 | any 3D spherical objects remains spherical in screen space. It also behaves much 8 | better for wide field of views. 9 | 10 | I got inspired to do this demo after seeing this cool quake mod: http://strlen.com/gfxengine/fisheyequake/ 11 | 12 | ![alt text](fisheye-preview.gif "Preview") 13 | -------------------------------------------------------------------------------- /demo-fisheye/fisheye-preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/demo-fisheye/fisheye-preview.gif -------------------------------------------------------------------------------- /demo-fisheye/shaders/background.glsl: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // Uniforms 3 | // -------------------------------------------------- 4 | uniform vec3 u_ClearColor; 5 | uniform float u_Fovy; 6 | uniform sampler2D u_EnvmapSampler; 7 | 8 | struct Transform { 9 | mat4 modelView; 10 | mat4 projection; 11 | mat4 modelViewProjection; 12 | mat4 viewInv; 13 | }; 14 | 15 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 16 | uniform Transforms { 17 | Transform u_Transform; 18 | }; 19 | 20 | vec3 evalEnvmap(vec3 dir) 21 | { 22 | float pi = 3.14159265359; 23 | float u1 = atan(dir.x, dir.y) / pi * 0.5 + 0.5; 24 | float u2 = 1.0 - acos(dir.z) / pi; 25 | return texture(u_EnvmapSampler, vec2(u1, u2)).rgb; 26 | } 27 | 28 | vec3 inv(vec3 x) 29 | { 30 | return x / dot(x, x); 31 | } 32 | 33 | vec3 proj(vec3 x) 34 | { 35 | const vec3 e0 = vec3(1, 0, 0); 36 | return 2.0 * inv(x + e0) - e0; 37 | } 38 | 39 | // -------------------------------------------------- 40 | // Vertex shader 41 | // -------------------------------------------------- 42 | #ifdef VERTEX_SHADER 43 | layout(location = 0) out vec3 o_TexCoord; 44 | void main() { 45 | // draw a full screen quad in modelview space 46 | vec2 p = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1) * 2.0 - 1.0; 47 | vec3 e = vec3(-900.0, p * 1e5); 48 | 49 | gl_Position = u_Transform.projection * vec4(e, 1); 50 | gl_Position.z = 0.99999 * gl_Position.w; // make sure the cubemap is visible 51 | o_TexCoord = vec3(u_Transform.viewInv * vec4(normalize(e), 0)); 52 | } 53 | #endif 54 | 55 | // -------------------------------------------------- 56 | // Fragment shader 57 | // -------------------------------------------------- 58 | #ifdef FRAGMENT_SHADER 59 | layout(location = 0) in vec3 i_TexCoord; 60 | layout(location = 0) out vec4 o_FragColor; 61 | 62 | void main() { 63 | vec2 texCoord = gl_FragCoord.xy / vec2(1680, 1050); 64 | //texCoord = floor(texCoord * 256.0f) / 256.0f; 65 | texCoord = 2.0 * texCoord - 1.0; 66 | texCoord*= tan(u_Fovy / 2.0) * vec2(1680.0/1050.0, 1.0); 67 | 68 | vec3 w; 69 | #if FLAG_FISHEYE 70 | w = proj(vec3(texCoord, 0.0).zxy); 71 | #else 72 | w = normalize(vec3(texCoord * 2.0, 1).zxy); 73 | #endif 74 | 75 | // to world 76 | w = normalize((u_Transform.viewInv * vec4(w, 0)).xyz); 77 | 78 | vec3 emap = evalEnvmap(w); 79 | o_FragColor = vec4(emap, 1); 80 | } 81 | #endif 82 | 83 | 84 | -------------------------------------------------------------------------------- /demo-fisheye/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | uniform float u_Exposure; 2 | uniform float u_Gamma; 3 | uniform vec3 u_Viewport; 4 | 5 | #if MSAA_FACTOR 6 | uniform sampler2DMS u_FramebufferSampler; 7 | #else 8 | uniform sampler2D u_FramebufferSampler; 9 | #endif 10 | 11 | // ------------------------------------------------------------------------------------------------- 12 | /** 13 | * Vertex Shader 14 | * 15 | * This vertex shader draws a fullscreen quad 16 | */ 17 | #ifdef VERTEX_SHADER 18 | layout(location = 0) out vec2 o_TexCoord; 19 | 20 | void main(void) 21 | { 22 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 23 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 24 | } 25 | #endif 26 | 27 | // ------------------------------------------------------------------------------------------------- 28 | /** 29 | * Fragment Shader 30 | * 31 | * This fragment shader post-processes the scene framebuffer by applying 32 | * tone mapping, gamma correction and image scaling. 33 | */ 34 | #ifdef FRAGMENT_SHADER 35 | layout(location = 0) in vec2 i_TexCoord; 36 | layout(location = 0) out vec4 o_FragColor; 37 | 38 | void main(void) 39 | { 40 | vec4 color = vec4(0); 41 | ivec2 P = ivec2(gl_FragCoord.xy); 42 | 43 | // get framebuffer data 44 | #if MSAA_FACTOR 45 | for (int i = 0; i < MSAA_FACTOR; ++i) { 46 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 47 | if (c.a > 0.0) color+= c / c.a; // normalize by number of samples 48 | } 49 | color/= vec4(MSAA_FACTOR); 50 | #else 51 | color = texelFetch(u_FramebufferSampler, P, 0); 52 | if (color.a > 0.0) color.rgb/= color.a; 53 | #endif 54 | 55 | // make fragments store positive values 56 | if (any(lessThan(color.rgb, vec3(0)))) { 57 | o_FragColor = vec4(1, 0, 0, 1); 58 | return; 59 | } 60 | 61 | // exposure 62 | color.rgb*= exp2(u_Exposure); 63 | 64 | // gamma 65 | color.rgb = pow(color.rgb, vec3(1.0 / u_Gamma)); 66 | 67 | // final color 68 | o_FragColor = vec4(color.rgb, 1.0); 69 | 70 | // make sure the fragments store real values 71 | if (any(isnan(color.rgb))) 72 | o_FragColor = vec4(1, 0, 0, 1); 73 | 74 | // o_FragColor = vec4(i_TexCoord, 0, 1); 75 | } 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /demo-hello-imgui/hello-imgui.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "glad/glad.h" 3 | #include "GLFW/glfw3.h" 4 | #include "imgui.h" 5 | #include "imgui_impl.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define LOG(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__); fflush(stdout); 12 | 13 | // ----------------------------------------------------------------------------- 14 | void renderGui() 15 | { 16 | static bool showDemoWindow = false; 17 | ImGui::Begin("Window"); 18 | { 19 | static float f = 0.0f; 20 | static int counter = 0; 21 | ImGui::Text("Hello, ImGui!"); 22 | ImGui::SliderFloat("float", &f, 0.0f, 1.0f); 23 | 24 | if (ImGui::Button("Button")) 25 | counter++; 26 | ImGui::SameLine(); 27 | ImGui::Text("counter = %d", counter); 28 | 29 | ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); 30 | ImGui::Checkbox("ShowDemoWindow", &showDemoWindow); 31 | } 32 | ImGui::End(); 33 | 34 | if (showDemoWindow) 35 | ImGui::ShowDemoWindow(&showDemoWindow); 36 | 37 | ImGui::Render(); 38 | } 39 | 40 | // ----------------------------------------------------------------------------- 41 | void 42 | keyboardCallback( 43 | GLFWwindow* window, 44 | int key, int scancode, int action, int modsls 45 | ) { 46 | ImGuiIO& io = ImGui::GetIO(); 47 | if (io.WantCaptureKeyboard) 48 | return; 49 | 50 | if (action == GLFW_PRESS) { 51 | switch (key) { 52 | case GLFW_KEY_ESCAPE: 53 | glfwSetWindowShouldClose(window, GL_TRUE); 54 | break; 55 | default: break; 56 | } 57 | } 58 | } 59 | 60 | void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) 61 | { 62 | ImGuiIO& io = ImGui::GetIO(); 63 | if (io.WantCaptureMouse) 64 | return; 65 | } 66 | 67 | void mouseMotionCallback(GLFWwindow* window, double x, double y) 68 | { 69 | static double x0 = 0, y0 = 0; 70 | double dx = x - x0, 71 | dy = y - y0; 72 | 73 | ImGuiIO& io = ImGui::GetIO(); 74 | if (io.WantCaptureMouse) 75 | return; 76 | 77 | x0 = x; 78 | y0 = y; 79 | } 80 | 81 | void mouseScrollCallback(GLFWwindow* window, double xoffset, double yoffset) 82 | { 83 | ImGuiIO& io = ImGui::GetIO(); 84 | if (io.WantCaptureMouse) 85 | return; 86 | } 87 | 88 | // ----------------------------------------------------------------------------- 89 | int main(int argc, char **argv) 90 | { 91 | glfwInit(); 92 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 93 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); 94 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 95 | 96 | // Create the Window 97 | LOG("Loading {Window-Main}\n"); 98 | GLFWwindow* window = glfwCreateWindow(1024, 768, "Hello Imgui", NULL, NULL); 99 | if (window == NULL) { 100 | LOG("=> Failure <=\n"); 101 | glfwTerminate(); 102 | return -1; 103 | } 104 | glfwMakeContextCurrent(window); 105 | glfwSetKeyCallback(window, &keyboardCallback); 106 | glfwSetCursorPosCallback(window, &mouseMotionCallback); 107 | glfwSetMouseButtonCallback(window, &mouseButtonCallback); 108 | glfwSetScrollCallback(window, &mouseScrollCallback); 109 | 110 | // Load OpenGL functions 111 | LOG("Loading {OpenGL}\n"); 112 | if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { 113 | LOG("gladLoadGLLoader failed\n"); 114 | return -1; 115 | } 116 | 117 | LOG("-- Begin -- Demo\n"); 118 | try { 119 | ImGui::CreateContext(); 120 | ImGui_ImplGlfwGL3_Init(window, false); 121 | ImGui::StyleColorsDark(); 122 | 123 | while (!glfwWindowShouldClose(window)) { 124 | glfwPollEvents(); 125 | 126 | glClearColor(0.8f, 0.8f, 0.8f, 1.0f); 127 | glClear(GL_COLOR_BUFFER_BIT); 128 | ImGui_ImplGlfwGL3_NewFrame(); 129 | renderGui(); 130 | ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData()); 131 | glfwSwapBuffers(window); 132 | } 133 | 134 | ImGui_ImplGlfwGL3_Shutdown(); 135 | ImGui::DestroyContext(); 136 | glfwTerminate(); 137 | } catch (std::exception& e) { 138 | LOG("%s", e.what()); 139 | ImGui_ImplGlfwGL3_Shutdown(); 140 | ImGui::DestroyContext(); 141 | glfwTerminate(); 142 | LOG("(!) Demo Killed (!)\n"); 143 | 144 | return EXIT_FAILURE; 145 | } catch (...) { 146 | ImGui_ImplGlfwGL3_Shutdown(); 147 | ImGui::DestroyContext(); 148 | glfwTerminate(); 149 | LOG("(!) Demo Killed (!)\n"); 150 | 151 | return EXIT_FAILURE; 152 | } 153 | LOG("-- End -- Demo\n"); 154 | 155 | 156 | return 0; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /demo-hello-window/hello-window.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "glad/glad.h" 3 | #include "GLFW/glfw3.h" 4 | #include "imgui.h" 5 | 6 | #include 7 | #include 8 | 9 | int main(int argc, char **argv) 10 | { 11 | glfwInit(); 12 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 13 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); 14 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 15 | 16 | GLFWwindow* window = glfwCreateWindow(800, 600, "Hello OpenGL Window", NULL, NULL); 17 | if (window == NULL) 18 | { 19 | printf("window creation failed!\n"); 20 | glfwTerminate(); 21 | return -1; 22 | } 23 | glfwMakeContextCurrent(window); 24 | 25 | if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 26 | { 27 | printf("window creation failed!\n"); 28 | return -1; 29 | } 30 | 31 | while (!glfwWindowShouldClose(window)) 32 | { 33 | glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 34 | glClear(GL_COLOR_BUFFER_BIT); 35 | glfwSwapBuffers(window); 36 | glfwPollEvents(); 37 | } 38 | 39 | glfwTerminate(); 40 | 41 | return 0; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /demo-isubd-bs/shaders/bs.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_SUBD1) 7 | readonly buffer SubdBufferIn { 8 | uvec2 u_SubdBufferIn[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_SUBD2) 12 | buffer SubdBufferOut { 13 | uvec2 u_SubdBufferOut[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_PATCH) 17 | readonly buffer VertexBuffer { 18 | vec4 u_VertexBuffer[]; 19 | }; 20 | 21 | layout (binding = BUFFER_BINDING_SUBD_COUNTER) 22 | uniform atomic_uint u_SubdBufferCounter; 23 | 24 | uniform float u_LodFactor = 1.0; 25 | 26 | float distanceToLod(float z, float lodFactor) 27 | { 28 | return -log2(clamp(z * lodFactor, 0.0f, 1.0f)); 29 | } 30 | 31 | float computeLod(vec3 c) 32 | { 33 | vec3 cxf = c.xyz; 34 | float z = length(cxf); 35 | 36 | return distanceToLod(z, u_LodFactor); 37 | } 38 | 39 | float computeLod(in vec4 v[4]) 40 | { 41 | vec3 c = (v[0].xyz + v[1].xyz + v[2].xyz + v[3].xyz) / 4.0; 42 | return computeLod(c); 43 | } 44 | 45 | // ----------------------------------------------------------------------------- 46 | /** 47 | * Vertex Shader 48 | * 49 | * The vertex shader is empty 50 | */ 51 | #ifdef VERTEX_SHADER 52 | void main() 53 | { } 54 | #endif 55 | 56 | // ----------------------------------------------------------------------------- 57 | /** 58 | * Tessellation Control Shader 59 | * 60 | * This tessellaction control shader is responsible for updating the 61 | * subdivision buffer and sending visible geometry to the rasterizer. 62 | */ 63 | #ifdef TESS_CONTROL_SHADER 64 | layout (vertices = 1) out; 65 | out Patch { 66 | vec4 vertices[4]; 67 | float u[2]; 68 | } o_Patch[]; 69 | 70 | void writeKey(uint primID, uint key) 71 | { 72 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 73 | 74 | u_SubdBufferOut[idx] = uvec2(primID, key); 75 | } 76 | 77 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 78 | { 79 | // extract subdivision level associated to the key 80 | int keyLod = findMSB(key) / 2; 81 | 82 | // update the key accordingly 83 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key)) { 84 | uint children[4]; childrenKeys(key, children); 85 | 86 | writeKey(primID, children[0]); 87 | writeKey(primID, children[1]); 88 | writeKey(primID, children[2]); 89 | writeKey(primID, children[3]); 90 | } else if (/* keep ? */ keyLod < (parentLod + 1)) { 91 | writeKey(primID, key); 92 | } else /* merge ? */ { 93 | if (/* is root ? */isRootKey(key)) { 94 | writeKey(primID, key); 95 | } else if (/* is zero child ? */isChildZeroKey(key)) { 96 | writeKey(primID, parentKey(key)); 97 | } 98 | } 99 | } 100 | 101 | void main() 102 | { 103 | // get threadID (each key is associated to a thread) 104 | int threadID = gl_PrimitiveID; 105 | 106 | // get coarse line associated to the key 107 | uint primID = u_SubdBufferIn[threadID].x; 108 | vec4 v_in[4] = vec4[4]( 109 | u_VertexBuffer[0], 110 | u_VertexBuffer[1], 111 | u_VertexBuffer[2], 112 | u_VertexBuffer[3] 113 | ); 114 | 115 | // compute distance-based LOD 116 | uint key = u_SubdBufferIn[threadID].y; 117 | vec4 v[4], vp[4]; float u[2]; subd(key, v_in, v, vp, u); 118 | int targetLod = int(computeLod(v)); 119 | int parentLod = int(computeLod(vp)); 120 | #if FLAG_FREEZE 121 | parentLod = targetLod = findMSB(key) / 2; 122 | #endif 123 | #if FLAG_UNIFORM 124 | parentLod = targetLod = UNIFORM_SUBD_FACTOR; 125 | #endif 126 | updateSubdBuffer(primID, key, targetLod, parentLod); 127 | 128 | if (true) { 129 | // set tess levels 130 | int tessLevel = PATCH_TESS_LEVEL; 131 | gl_TessLevelInner[0] = 132 | gl_TessLevelInner[1] = 133 | gl_TessLevelOuter[0] = 134 | gl_TessLevelOuter[1] = 135 | gl_TessLevelOuter[2] = 136 | gl_TessLevelOuter[3] = tessLevel; 137 | 138 | // set output data 139 | o_Patch[gl_InvocationID].vertices = v; 140 | o_Patch[gl_InvocationID].u = u; 141 | } 142 | } 143 | #endif 144 | 145 | // ----------------------------------------------------------------------------- 146 | /** 147 | * Tessellation Evaluation Shader 148 | * 149 | * This tessellaction evaluation shader is responsible for placing the 150 | * geometry properly on the input mesh (here a terrain). 151 | */ 152 | #ifdef TESS_EVALUATION_SHADER 153 | layout (isolines, equal_spacing) in; 154 | in Patch { 155 | vec4 vertices[4]; 156 | float u[2]; 157 | } i_Patch[]; 158 | 159 | layout(location = 0) out float o_TexCoord; 160 | 161 | void main() 162 | { 163 | float u[2] = i_Patch[0].u; 164 | vec4 v[4] = i_Patch[0].vertices; 165 | vec4 finalVertex = mix(v[1], v[2], gl_TessCoord.x); 166 | 167 | o_TexCoord = mix(u[0], u[1], gl_TessCoord.x);; 168 | gl_Position = finalVertex; 169 | } 170 | #endif 171 | 172 | // ----------------------------------------------------------------------------- 173 | /** 174 | * Fragment Shader 175 | * 176 | * This fragment shader is responsible for shading the final geometry. 177 | */ 178 | #ifdef FRAGMENT_SHADER 179 | layout(location = 0) in float i_TexCoord; 180 | layout(location = 0) out vec4 o_FragColor; 181 | 182 | void main() 183 | { 184 | vec3 myColor = vec3(0.10,0.50,0.10); 185 | o_FragColor = vec4(myColor, 1); 186 | o_FragColor = vec4(i_TexCoord, 0, 0, 1); 187 | } 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /demo-isubd-bs/shaders/bsnet_edges.glsl: -------------------------------------------------------------------------------- 1 | layout (std430, binding = BUFFER_BINDING_PATCH) 2 | readonly buffer VertexBuffer { 3 | vec4 u_VertexBuffer[]; 4 | }; 5 | 6 | uniform vec2 u_MousePos; 7 | 8 | // ----------------------------------------------------------------------------- 9 | /** 10 | * Vertex Shader 11 | * 12 | */ 13 | #ifdef VERTEX_SHADER 14 | void main() 15 | { 16 | } 17 | #endif 18 | 19 | // ----------------------------------------------------------------------------- 20 | /** 21 | * Geometry Shader 22 | * 23 | * The vertex shader is empty 24 | */ 25 | #ifdef GEOMETRY_SHADER 26 | layout(points) in; 27 | layout(line_strip, max_vertices = 2) out; 28 | 29 | void main() 30 | { 31 | int primID = gl_PrimitiveIDIn; 32 | if (primID < 3) { 33 | gl_Position = u_VertexBuffer[primID]; 34 | EmitVertex(); 35 | gl_Position = u_VertexBuffer[primID + 1]; 36 | EmitVertex(); 37 | EndPrimitive(); 38 | } 39 | } 40 | #endif 41 | 42 | 43 | // ----------------------------------------------------------------------------- 44 | /** 45 | * Fragment Shader 46 | * 47 | */ 48 | #ifdef FRAGMENT_SHADER 49 | layout(location = 0) out vec4 o_FragColor; 50 | 51 | void main() 52 | { 53 | vec3 myColor = vec3(0.00,0.20,0.70); 54 | 55 | o_FragColor = vec4(myColor, 1.0); 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /demo-isubd-bs/shaders/bsnet_vertices.glsl: -------------------------------------------------------------------------------- 1 | layout (std430, binding = BUFFER_BINDING_PATCH) 2 | readonly buffer VertexBuffer { 3 | vec4 u_VertexBuffer[]; 4 | }; 5 | 6 | uniform vec2 u_MousePos; 7 | 8 | // ----------------------------------------------------------------------------- 9 | /** 10 | * Vertex Shader 11 | * 12 | */ 13 | #ifdef VERTEX_SHADER 14 | void main() 15 | { 16 | } 17 | #endif 18 | 19 | // ----------------------------------------------------------------------------- 20 | /** 21 | * Geometry Shader 22 | * 23 | * The vertex shader is empty 24 | */ 25 | #ifdef GEOMETRY_SHADER 26 | layout(points) in; 27 | layout(triangle_strip, max_vertices = 12) out; 28 | 29 | layout(location = 0) out vec2 o_TexCoord; 30 | 31 | void main() 32 | { 33 | vec2 screenRes = vec2(SCREEN_XRES, SCREEN_YRES); 34 | float pointSize = SCREEN_XRES / 32.0; 35 | int vertexID = gl_PrimitiveIDIn; 36 | vec4 vertexPos = u_VertexBuffer[vertexID]; 37 | 38 | for (int i = 0; i < 4; ++i) { 39 | vec2 uv = vec2(i & 1, i >> 1 & 1); 40 | vec2 offset = (uv - 0.5) * pointSize / screenRes; 41 | vec4 pos = vertexPos + vec4(offset, 0, 0); 42 | 43 | o_TexCoord = uv; 44 | gl_Position = pos; 45 | EmitVertex(); 46 | } 47 | EndPrimitive(); 48 | } 49 | #endif 50 | 51 | 52 | // ----------------------------------------------------------------------------- 53 | /** 54 | * Fragment Shader 55 | * 56 | */ 57 | #ifdef FRAGMENT_SHADER 58 | layout(location = 0) in vec2 i_TexCoord; 59 | layout(location = 0) out vec4 o_FragColor; 60 | 61 | float sqr(float x) {return x*x;} 62 | 63 | void main() 64 | { 65 | vec2 uv = (i_TexCoord - 0.5); 66 | float rTest = dot(uv, uv) - sqr(0.35); 67 | float alpha = 1.0 - sqr(smoothstep(0.00, 0.07, rTest)); 68 | float inner = sqr(smoothstep(0.00, 0.07, -rTest)); 69 | vec3 myColor = vec3(0.00, 0.20, 0.70); 70 | vec3 myInnerColor = vec3(220./255.); 71 | vec3 color = mix(myColor, myColor, inner); 72 | 73 | o_FragColor = vec4(color, alpha); 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /demo-isubd-bs/shaders/isubd_bs.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | 3 | uint parentKey(in uint key) 4 | { 5 | return (key >> 2u); 6 | } 7 | 8 | void childrenKeys(in uint key, out uint children[4]) 9 | { 10 | children[0] = (key << 2u) | 0u; 11 | children[1] = (key << 2u) | 1u; 12 | children[2] = (key << 2u) | 2u; 13 | children[3] = (key << 2u) | 3u; 14 | } 15 | 16 | bool isRootKey(in uint key) 17 | { 18 | return (key == 1u); 19 | } 20 | 21 | bool isLeafKey(in uint key) 22 | { 23 | return findMSB(key) == 13; 24 | } 25 | 26 | bool isChildZeroKey(in uint key) 27 | { 28 | return ((key & 3u) == 0u); 29 | } 30 | 31 | // get xform from bit value 32 | mat4 bitToXform(in uint bit) 33 | { 34 | const mat4 mcc = mat4(4, 1, 0, 0, 35 | 4, 6, 4, 1, 36 | 0, 1, 4, 6, 37 | 0, 0, 0, 1) / 8.0f; 38 | float b = float(bit); 39 | float c = 1.0f - b; 40 | mat4 m = mat4(c, 0, 0, b, 41 | 0, c, b, 0, 42 | 0, b, c, 0, 43 | b, 0, 0, c); 44 | 45 | return mcc * m; 46 | } 47 | 48 | // get xform from key 49 | mat4 keyToXform(in uint key) 50 | { 51 | mat4 xf = mat4(1.0f); 52 | 53 | while (key > 1u) { 54 | xf = bitToXform(key & 1u) * xf; 55 | key = key >> 1u; 56 | } 57 | 58 | return xf; 59 | } 60 | 61 | // get xform from key 62 | mat4 keyToXform(in uint key, out mat4 xfp) 63 | { 64 | xfp = keyToXform(parentKey(key)); 65 | return keyToXform(key); 66 | } 67 | 68 | // subdivision routine (vertex position only) with parents 69 | void 70 | subd( 71 | in uint key, 72 | in vec4 v_in[4], 73 | out vec4 v_out[4], 74 | out vec4 v_out_p[4], 75 | out float u[2] 76 | ) { 77 | mat4 xfp; mat4 xf = keyToXform(key, xfp); 78 | vec4 u_in = vec4(-1, 0, 1, 2); 79 | vec4 x_in = vec4(v_in[0].x, v_in[1].x, v_in[2].x, v_in[3].x); 80 | vec4 y_in = vec4(v_in[0].y, v_in[1].y, v_in[2].y, v_in[3].y); 81 | vec4 z_in = vec4(v_in[0].z, v_in[1].z, v_in[2].z, v_in[3].z); 82 | vec4 u_out = xf * u_in; 83 | vec4 x_out = xf * x_in; 84 | vec4 y_out = xf * y_in; 85 | vec4 z_out = xf * z_in; 86 | vec4 x_out_p = xfp * x_in; 87 | vec4 y_out_p = xfp * y_in; 88 | vec4 z_out_p = xfp * z_in; 89 | 90 | v_out[0] = vec4(x_out[0], y_out[0], z_out[0], 1); 91 | v_out[1] = vec4(x_out[1], y_out[1], z_out[1], 1); 92 | v_out[2] = vec4(x_out[2], y_out[2], z_out[2], 1); 93 | v_out[3] = vec4(x_out[3], y_out[3], z_out[3], 1); 94 | 95 | v_out_p[0] = vec4(x_out_p[0], y_out_p[0], z_out_p[0], 1); 96 | v_out_p[1] = vec4(x_out_p[1], y_out_p[1], z_out_p[1], 1); 97 | v_out_p[2] = vec4(x_out_p[2], y_out_p[2], z_out_p[2], 1); 98 | v_out_p[3] = vec4(x_out_p[3], y_out_p[3], z_out_p[3], 1); 99 | 100 | u[0] = u_out[1]; 101 | u[1] = u_out[2]; 102 | } 103 | -------------------------------------------------------------------------------- /demo-isubd-bs/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | #if MSAA_FACTOR 2 | uniform sampler2DMS u_FramebufferSampler; 3 | #else 4 | uniform sampler2D u_FramebufferSampler; 5 | #endif 6 | 7 | // ------------------------------------------------------------------------------------------------- 8 | /** 9 | * Vertex Shader 10 | * 11 | * This vertex shader draws a fullscreen quad 12 | */ 13 | #ifdef VERTEX_SHADER 14 | layout(location = 0) out vec2 o_TexCoord; 15 | 16 | void main(void) 17 | { 18 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 19 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 20 | } 21 | #endif 22 | 23 | // ------------------------------------------------------------------------------------------------- 24 | /** 25 | * Fragment Shader 26 | * 27 | * This fragment shader post-processes the scene framebuffer by applying 28 | * tone mapping, gamma correction and image scaling. 29 | */ 30 | #ifdef FRAGMENT_SHADER 31 | layout(location = 0) in vec2 i_TexCoord; 32 | layout(location = 0) out vec4 o_FragColor; 33 | 34 | void main(void) 35 | { 36 | vec4 color = vec4(0); 37 | ivec2 P = ivec2(gl_FragCoord.xy); 38 | 39 | #if MSAA_FACTOR 40 | for (int i = 0; i < MSAA_FACTOR; ++i) { 41 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 42 | 43 | color+= vec4(c.a * c.rgb, c.a); 44 | } 45 | #else 46 | color = texelFetch(u_FramebufferSampler, P, 0); 47 | #endif 48 | if (color.a > 0.0) color.rgb/= color.a; 49 | 50 | // make fragments store positive values 51 | if (any(lessThan(color.rgb, vec3(0)))) { 52 | o_FragColor = vec4(1, 0, 0, 1); 53 | return; 54 | } 55 | 56 | // final color 57 | o_FragColor = vec4(color.rgb, 1.0); 58 | } 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /demo-isubd-cc/shaders/cc.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_SUBD1) 7 | readonly buffer SubdBufferIn { 8 | uvec2 u_SubdBufferIn[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_SUBD2) 12 | buffer SubdBufferOut { 13 | uvec2 u_SubdBufferOut[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_PATCH) 17 | readonly buffer VertexBuffer { 18 | vec4 u_VertexBuffer[]; 19 | }; 20 | 21 | layout (binding = BUFFER_BINDING_SUBD_COUNTER) 22 | uniform atomic_uint u_SubdBufferCounter; 23 | 24 | struct Transform { 25 | mat4 modelView; 26 | mat4 projection; 27 | mat4 modelViewProjection; 28 | mat4 viewInv; 29 | }; 30 | 31 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 32 | uniform Transforms { 33 | Transform u_Transform; 34 | }; 35 | 36 | uniform sampler2D u_DmapSampler; 37 | uniform float u_DmapFactor; 38 | uniform float u_LodFactor; 39 | 40 | // ----------------------------------------------------------------------------- 41 | /** 42 | * Vertex Shader 43 | * 44 | * The vertex shader is empty 45 | */ 46 | #ifdef VERTEX_SHADER 47 | void main() 48 | { } 49 | #endif 50 | 51 | // ----------------------------------------------------------------------------- 52 | /** 53 | * Tessellation Control Shader 54 | * 55 | * This tessellaction control shader is responsible for updating the 56 | * subdivision buffer and sending visible geometry to the rasterizer. 57 | */ 58 | #ifdef TESS_CONTROL_SHADER 59 | layout (vertices = 1) out; 60 | layout (location = 0) out ccpatch o_Patch[]; 61 | 62 | float distanceToLod(float z, float lodFactor) 63 | { 64 | return -log2(clamp(z * lodFactor, 0.0f, 1.0f)); 65 | } 66 | 67 | float computeLod(vec3 c) 68 | { 69 | vec3 cxf = (u_Transform.modelView * vec4(c, 1)).xyz; 70 | float z = length(cxf); 71 | 72 | return distanceToLod(z, u_LodFactor); 73 | } 74 | 75 | float computeLod(in vec4 v[4]) 76 | { 77 | vec3 c = (v[0].xyz + v[1].xyz + v[2].xyz + v[3].xyz) / 4.0; 78 | return computeLod(c); 79 | } 80 | 81 | void writeKey(uint primID, uint key) 82 | { 83 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 84 | 85 | u_SubdBufferOut[idx] = uvec2(primID, key); 86 | } 87 | 88 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 89 | { 90 | // extract subdivision level associated to the key 91 | int keyLod = findMSB(key) / 2; 92 | 93 | // update the key accordingly 94 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key)) { 95 | uint children[4]; childrenKeys(key, children); 96 | 97 | writeKey(primID, children[0]); 98 | writeKey(primID, children[1]); 99 | writeKey(primID, children[2]); 100 | writeKey(primID, children[3]); 101 | } else if (/* keep ? */ keyLod < (parentLod + 1)) { 102 | writeKey(primID, key); 103 | } else /* merge ? */ { 104 | if (/* is root ? */isRootKey(key)) { 105 | writeKey(primID, key); 106 | } else if (/* is zero child ? */isChildZeroKey(key)) { 107 | writeKey(primID, parentKey(key)); 108 | } 109 | } 110 | } 111 | 112 | void main() 113 | { 114 | // get threadID (each key is associated to a thread) 115 | int threadID = gl_PrimitiveID; 116 | 117 | // get coarse triangle associated to the key 118 | uint primID = u_SubdBufferIn[threadID].x; 119 | vec4 v_in[16] = vec4[16]( 120 | u_VertexBuffer[0], 121 | u_VertexBuffer[1], 122 | u_VertexBuffer[2], 123 | u_VertexBuffer[3], 124 | u_VertexBuffer[4], 125 | u_VertexBuffer[5], 126 | u_VertexBuffer[6], 127 | u_VertexBuffer[7], 128 | u_VertexBuffer[8], 129 | u_VertexBuffer[9], 130 | u_VertexBuffer[10], 131 | u_VertexBuffer[11], 132 | u_VertexBuffer[12], 133 | u_VertexBuffer[13], 134 | u_VertexBuffer[14], 135 | u_VertexBuffer[15] 136 | ); 137 | 138 | // compute distance-based LOD 139 | uint key = u_SubdBufferIn[threadID].y; 140 | ccpatch ccp; subd(key, v_in, ccp); 141 | int targetLod = int(computeLod(ccp.v)); 142 | int parentLod = targetLod; 143 | #if FLAG_FREEZE 144 | parentLod = targetLod = findMSB(key) / 2; 145 | #endif 146 | #if FLAG_UNIFORM 147 | parentLod = targetLod = UNIFORM_SUBD_FACTOR; 148 | #endif 149 | updateSubdBuffer(primID, key, targetLod, parentLod); 150 | 151 | #if FLAG_CULL 152 | // Cull invisible nodes 153 | mat4 mvp = u_Transform.modelViewProjection; 154 | vec3 bmin = min(min(v[0], v[1]), v[2]).xyz; 155 | vec3 bmax = max(max(v[0], v[1]), v[2]).xyz; 156 | 157 | if (/* is visible ? */frustumCullingTest(mvp, bmin, bmax)) { 158 | #else 159 | if (true) { 160 | #endif // FLAG_CULL 161 | // set tess levels 162 | int tessLevel = PATCH_TESS_LEVEL; 163 | gl_TessLevelInner[0] = 164 | gl_TessLevelInner[1] = 165 | gl_TessLevelOuter[0] = 166 | gl_TessLevelOuter[1] = 167 | gl_TessLevelOuter[2] = 168 | gl_TessLevelOuter[3] = tessLevel; 169 | 170 | // set output data 171 | o_Patch[gl_InvocationID] = ccp; 172 | } else /* is not visible ? */ { 173 | // cull the geometry 174 | gl_TessLevelInner[0] = 175 | gl_TessLevelInner[1] = 176 | gl_TessLevelOuter[0] = 177 | gl_TessLevelOuter[1] = 178 | gl_TessLevelOuter[2] = 179 | gl_TessLevelOuter[3] = 0; 180 | } 181 | } 182 | #endif 183 | 184 | // ----------------------------------------------------------------------------- 185 | /** 186 | * Tessellation Evaluation Shader 187 | * 188 | * This tessellaction evaluation shader is responsible for placing the 189 | * geometry properly on the input mesh (here a terrain). 190 | */ 191 | #ifdef TESS_EVALUATION_SHADER 192 | layout (quads, ccw, equal_spacing) in; 193 | layout (location = 0) in ccpatch i_Patch[]; 194 | 195 | out FragData { 196 | vec4 tgU; 197 | vec4 bgV; 198 | } o_FragData; 199 | 200 | vec2 berp(in vec2 v_in[4], vec2 u) 201 | { 202 | return mix(mix(v_in[0], v_in[1], u.x), mix(v_in[3], v_in[2], u.x), u.y); 203 | } 204 | 205 | vec4 berp(in vec4 v_in[4], vec2 u) 206 | { 207 | return mix(mix(v_in[0], v_in[1], u.x), mix(v_in[3], v_in[2], u.x), u.y); 208 | } 209 | 210 | void main() 211 | { 212 | vec4 v[4] = i_Patch[0].v; 213 | vec4 finalVertex = berp(v, gl_TessCoord.xy); 214 | vec2 uv = berp(i_Patch[0].uv, gl_TessCoord.xy); 215 | 216 | #if FLAG_DISPLACE 217 | finalVertex.z+= dmap(finalVertex.xy); 218 | #endif 219 | 220 | o_FragData.tgU.xyz = berp(i_Patch[0].tg, gl_TessCoord.xy).xyz; 221 | o_FragData.bgV.xyz = berp(i_Patch[0].bg, gl_TessCoord.xy).xyz; 222 | o_FragData.tgU.w = uv.x; 223 | o_FragData.bgV.w = uv.y; 224 | gl_Position = u_Transform.modelViewProjection * finalVertex; 225 | } 226 | #endif 227 | 228 | // ----------------------------------------------------------------------------- 229 | /** 230 | * Fragment Shader 231 | * 232 | * This fragment shader is responsible for shading the final geometry. 233 | */ 234 | #ifdef FRAGMENT_SHADER 235 | in FragData { 236 | vec4 tgU; 237 | vec4 bgV; 238 | } i_FragData; 239 | 240 | layout(location = 0) out vec4 o_FragColor; 241 | 242 | void main() 243 | { 244 | vec3 tg = i_FragData.tgU.xyz; 245 | vec3 bg = i_FragData.bgV.xyz; 246 | vec3 n = normalize(cross(tg, bg)); 247 | 248 | vec2 uv = vec2(i_FragData.tgU.w, i_FragData.bgV.w); 249 | o_FragColor = vec4(clamp((normalize(n)), 0.0, 1.0), 1); 250 | o_FragColor = vec4(clamp(n.zzz, 0.0, 1.0), 1); 251 | //o_FragColor = vec4(uv, 0.0, 1.0); 252 | } 253 | 254 | #endif 255 | -------------------------------------------------------------------------------- /demo-isubd-cc/shaders/cc_gs.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering (using a geometry shader) 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_SUBD1) 7 | readonly buffer SubdBufferIn { 8 | uvec2 u_SubdBufferIn[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_SUBD2) 12 | buffer SubdBufferOut { 13 | uvec2 u_SubdBufferOut[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_VERTICES) 17 | readonly buffer VertexBuffer { 18 | vec4 u_VertexBuffer[]; 19 | }; 20 | 21 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_INDEXES) 22 | readonly buffer IndexBuffer { 23 | uint u_IndexBuffer[]; 24 | }; 25 | 26 | layout (binding = BUFFER_BINDING_SUBD_COUNTER) 27 | uniform atomic_uint u_SubdBufferCounter; 28 | 29 | struct Transform { 30 | mat4 modelView; 31 | mat4 projection; 32 | mat4 modelViewProjection; 33 | mat4 viewInv; 34 | }; 35 | 36 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 37 | uniform Transforms { 38 | Transform u_Transform; 39 | }; 40 | 41 | uniform sampler2D u_DmapSampler; 42 | uniform float u_DmapFactor; 43 | uniform float u_LodFactor; 44 | 45 | // displacement map 46 | float dmap(vec2 pos) 47 | { 48 | #if 0 49 | return cos(20.0 * pos.x) * cos(20.0 * pos.y) / 2.0 * u_DmapFactor; 50 | #else 51 | return (texture(u_DmapSampler, pos * 0.5 + 0.5).x) * u_DmapFactor; 52 | #endif 53 | } 54 | 55 | float distanceToLod(float z, float lodFactor) 56 | { 57 | // Note that we multiply the result by two because the triangle's 58 | // edge lengths decreases by half every two subdivision steps. 59 | return -2.0 * log2(clamp(z * lodFactor, 0.0f, 1.0f)); 60 | } 61 | 62 | 63 | // ----------------------------------------------------------------------------- 64 | /** 65 | * Vertex Shader 66 | * 67 | * The vertex shader is empty 68 | */ 69 | #ifdef VERTEX_SHADER 70 | void main(void) 71 | { } 72 | #endif 73 | 74 | // ----------------------------------------------------------------------------- 75 | /** 76 | * Geometry Shader 77 | * 78 | * This geometry shader is responsible for updating the 79 | * subdivision buffer and sending visible geometry to the rasterizer. 80 | */ 81 | #ifdef GEOMETRY_SHADER 82 | layout(points) in; 83 | layout(triangle_strip, max_vertices = VERTICES_OUT) out; 84 | layout(location = 0) out vec2 o_TexCoord; 85 | 86 | float computeLod(vec3 c) 87 | { 88 | #if FLAG_DISPLACE 89 | c.z+= dmap(u_Transform.viewInv[3].xy); 90 | #endif 91 | 92 | vec3 cxf = (u_Transform.modelView * vec4(c, 1)).xyz; 93 | float z = length(cxf); 94 | 95 | return distanceToLod(z, u_LodFactor); 96 | } 97 | 98 | float computeLod(in vec3 v[3]) 99 | { 100 | vec3 c = (v[1] + v[2]) / 2.0; 101 | return computeLod(c); 102 | } 103 | 104 | void writeKey(uint primID, uint key) 105 | { 106 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 107 | 108 | u_SubdBufferOut[idx] = uvec2(primID, key); 109 | } 110 | 111 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 112 | { 113 | // extract subdivision level associated to the key 114 | int keyLod = findMSB(key); 115 | 116 | // update the key accordingly 117 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key)) { 118 | uint children[2]; childrenKeys(key, children); 119 | 120 | writeKey(primID, children[0]); 121 | writeKey(primID, children[1]); 122 | } else if (/* keep ? */ keyLod <= (parentLod + 1)) { 123 | writeKey(primID, key); 124 | } else /* merge ? */ { 125 | if (/* is root ? */isRootKey(key)) { 126 | writeKey(primID, key); 127 | } else if (/* is zero child ? */isChildZeroKey(key)) { 128 | writeKey(primID, parentKey(key)); 129 | } 130 | } 131 | } 132 | 133 | void main() 134 | { 135 | // get threadID (each key is associated to a thread) 136 | int threadID = gl_PrimitiveIDIn; 137 | 138 | // get coarse triangle associated to the key 139 | uint primID = u_SubdBufferIn[threadID].x; 140 | vec3 v_in[3] = vec3[3]( 141 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 ]].xyz), 142 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]].xyz), 143 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]].xyz) 144 | ); 145 | 146 | // compute distance-based LOD 147 | uint key = u_SubdBufferIn[threadID].y; 148 | vec3 v[3], vp[3]; subd(key, v_in, v, vp); 149 | int targetLod = int(computeLod(v)); 150 | int parentLod = int(computeLod(vp)); 151 | #if FLAG_FREEZE 152 | targetLod = parentLod = findMSB(key); 153 | #endif 154 | updateSubdBuffer(primID, key, targetLod, parentLod); 155 | 156 | #if FLAG_CULL 157 | // Cull invisible nodes 158 | mat4 mvp = u_Transform.modelViewProjection; 159 | vec3 bmin = min(min(v[0], v[1]), v[2]); 160 | vec3 bmax = max(max(v[0], v[1]), v[2]); 161 | 162 | // account for displacement in bound computations 163 | # if FLAG_DISPLACE 164 | bmin.z = 0; 165 | bmax.z = u_DmapFactor; 166 | # endif 167 | 168 | if (/* is visible ? */frustumCullingTest(mvp, bmin, bmax)) { 169 | #else 170 | if (true) { 171 | #endif // FLAG_CULL 172 | // set tess levels 173 | int edgeCnt = PATCH_TESS_LEVEL; 174 | float edgeLength = 1.0 / float(edgeCnt); 175 | 176 | for (int i = 0; i < edgeCnt; ++i) { 177 | int vertexCnt = 2 * i + 3; 178 | 179 | // start a strip 180 | for (int j = 0; j < vertexCnt; ++j) { 181 | int ui = j >> 1; 182 | int vi = (edgeCnt - 1) - (i - (j & 1)); 183 | vec2 tessCoord = vec2(ui, vi) * edgeLength; 184 | vec3 finalVertex = berp(v, tessCoord); 185 | 186 | #if FLAG_DISPLACE 187 | finalVertex.z+= dmap(finalVertex.xy); 188 | #endif 189 | 190 | o_TexCoord = finalVertex.xy; 191 | gl_Position = u_Transform.modelViewProjection * vec4(finalVertex, 1); 192 | EmitVertex(); 193 | } 194 | EndPrimitive(); 195 | } 196 | } 197 | 198 | } 199 | #endif 200 | 201 | // ----------------------------------------------------------------------------- 202 | /** 203 | * Fragment Shader 204 | * 205 | * This fragment shader is responsible for shading the final geometry. 206 | */ 207 | #ifdef FRAGMENT_SHADER 208 | layout(location = 0) in vec2 i_TexCoord; 209 | layout(location = 0) out vec4 o_FragColor; 210 | 211 | void main() 212 | { 213 | float z = dmap(i_TexCoord); 214 | o_FragColor = vec4(i_TexCoord, 0, 1); 215 | o_FragColor.rgb = vec3(z*z); 216 | } 217 | 218 | #endif 219 | -------------------------------------------------------------------------------- /demo-isubd-cc/shaders/ccnet.glsl: -------------------------------------------------------------------------------- 1 | struct Transform { 2 | mat4 modelView; 3 | mat4 projection; 4 | mat4 modelViewProjection; 5 | mat4 viewInv; 6 | }; 7 | 8 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 9 | uniform Transforms { 10 | Transform u_Transform; 11 | }; 12 | 13 | 14 | layout (std430, binding = BUFFER_BINDING_PATCH) 15 | readonly buffer VertexBuffer { 16 | vec4 u_VertexBuffer[]; 17 | }; 18 | 19 | // ----------------------------------------------------------------------------- 20 | /** 21 | * Vertex Shader 22 | * 23 | */ 24 | #ifdef VERTEX_SHADER 25 | void main() 26 | { 27 | } 28 | #endif 29 | 30 | // ----------------------------------------------------------------------------- 31 | /** 32 | * Geometry Shader 33 | * 34 | * The vertex shader is empty 35 | */ 36 | #ifdef GEOMETRY_SHADER 37 | layout(points) in; 38 | layout(triangle_strip, max_vertices = 12) out; 39 | void main() 40 | { 41 | float pointSize = 1.0 / 20.0; 42 | mat4 modelView = u_Transform.modelView; 43 | int vertexID = gl_PrimitiveIDIn; 44 | vec4 vertexPos = modelView * u_VertexBuffer[vertexID]; 45 | 46 | for (int i = 0; i < 4; ++i) { 47 | vec2 u = (vec2(i & 1, i >> 1 & 1) - 0.5) * pointSize; 48 | vec4 v = vertexPos + vec4(0, u, 0); 49 | gl_Position = u_Transform.projection * v; 50 | EmitVertex(); 51 | } 52 | EndPrimitive(); 53 | 54 | int row = vertexID % 4; 55 | if (row < 3) { 56 | float lineScale = 1.0 / 60.0; 57 | vec4 nextVertexPos = modelView * u_VertexBuffer[vertexID + 1]; 58 | vec4 p0 = vertexPos + vec4(0, 0, -0.5, 0) * lineScale; 59 | vec4 p1 = nextVertexPos + vec4(0, 0, -0.5, 0) * lineScale; 60 | vec4 p2 = nextVertexPos + vec4(0, 0, +0.5, 0) * lineScale; 61 | vec4 p3 = vertexPos + vec4(0, 0, +0.5, 0) * lineScale; 62 | 63 | for (int i = 0; i < 4; ++i) { 64 | vec2 u = vec2(i & 1, i >> 1 & 1); 65 | vec4 v = mix(mix(p0, p1, u.x), mix(p3, p2, u.x), u.y); 66 | 67 | gl_Position = u_Transform.projection * v; 68 | EmitVertex(); 69 | } 70 | EndPrimitive(); 71 | } 72 | 73 | int col = vertexID / 4; 74 | if (col < 3) { 75 | float lineScale = 1.0 / 60.0; 76 | vec4 nextVertexPos = modelView * u_VertexBuffer[vertexID + 4]; 77 | vec4 p0 = vertexPos + vec4(0, 0, -0.5, 0) * lineScale; 78 | vec4 p1 = nextVertexPos + vec4(0, 0, -0.5, 0) * lineScale; 79 | vec4 p2 = nextVertexPos + vec4(0, 0, +0.5, 0) * lineScale; 80 | vec4 p3 = vertexPos + vec4(0, 0, +0.5, 0) * lineScale; 81 | 82 | for (int i = 0; i < 4; ++i) { 83 | vec2 u = vec2(i & 1, i >> 1 & 1); 84 | vec4 v = mix(mix(p0, p1, u.x), mix(p3, p2, u.x), u.y); 85 | 86 | gl_Position = u_Transform.projection * v; 87 | EmitVertex(); 88 | } 89 | EndPrimitive(); 90 | } 91 | } 92 | #endif 93 | 94 | 95 | // ----------------------------------------------------------------------------- 96 | /** 97 | * Fragment Shader 98 | * 99 | */ 100 | #ifdef FRAGMENT_SHADER 101 | layout(location = 0) out vec4 o_FragColor; 102 | void main() 103 | { 104 | o_FragColor = vec4(1, 1, 0, 1); 105 | } 106 | #endif 107 | -------------------------------------------------------------------------------- /demo-isubd-cc/shaders/fcull.glsl: -------------------------------------------------------------------------------- 1 | /* fcull.glsl - v1.0 - public domain frustum culling GLSL code 2 | (Created by Jonathan Dupuy 2014.11.13) 3 | 4 | */ 5 | ////////////////////////////////////////////////////////////////////////////// 6 | // 7 | // Frustum Culling API 8 | // 9 | 10 | bool frustumCullingTest(mat4 mvp, vec3 bmin, vec3 bmax); 11 | 12 | // 13 | // 14 | //// end header file ///////////////////////////////////////////////////// 15 | 16 | 17 | // ***************************************************************************** 18 | // Frustum Implementation 19 | 20 | struct Frustum { 21 | vec4 planes[6]; 22 | }; 23 | 24 | /** 25 | * Extract Frustum Planes from MVP Matrix 26 | * 27 | * Based on "Fast Extraction of Viewing Frustum Planes from the World- 28 | * View-Projection Matrix", by Gil Gribb and Klaus Hartmann. 29 | * This procedure computes the planes of the frustum and normalizes 30 | * them. 31 | */ 32 | void loadFrustum(out Frustum f, mat4 mvp) 33 | { 34 | for (int i = 0; i < 3; ++i) 35 | for (int j = 0; j < 2; ++j) { 36 | f.planes[i*2+j].x = mvp[0][3] + (j == 0 ? mvp[0][i] : -mvp[0][i]); 37 | f.planes[i*2+j].y = mvp[1][3] + (j == 0 ? mvp[1][i] : -mvp[1][i]); 38 | f.planes[i*2+j].z = mvp[2][3] + (j == 0 ? mvp[2][i] : -mvp[2][i]); 39 | f.planes[i*2+j].w = mvp[3][3] + (j == 0 ? mvp[3][i] : -mvp[3][i]); 40 | f.planes[i*2+j]*= length(f.planes[i*2+j].xyz); 41 | } 42 | } 43 | 44 | /** 45 | * Negative Vertex of an AABB 46 | * 47 | * This procedure computes the negative vertex of an AABB 48 | * given a normal. 49 | * See the View Frustum Culling tutorial @ LightHouse3D.com 50 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 51 | */ 52 | vec3 negativeVertex(vec3 bmin, vec3 bmax, vec3 n) 53 | { 54 | bvec3 b = greaterThan(n, vec3(0)); 55 | return mix(bmin, bmax, b); 56 | } 57 | 58 | /** 59 | * Frustum-AABB Culling Test 60 | * 61 | * This procedure returns true if the AABB is either inside, or in 62 | * intersection with the frustum, and false otherwise. 63 | * The test is based on the View Frustum Culling tutorial @ LightHouse3D.com 64 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 65 | */ 66 | bool frustumCullingTest(mat4 mvp, vec3 bmin, vec3 bmax) 67 | { 68 | float a = 1.0f; 69 | Frustum f; 70 | 71 | loadFrustum(f, mvp); 72 | for (int i = 0; i < 6 && a >= 0.0f; ++i) { 73 | vec3 n = negativeVertex(bmin, bmax, f.planes[i].xyz); 74 | 75 | a = dot(vec4(n, 1.0f), f.planes[i]); 76 | } 77 | 78 | return (a >= 0.0); 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /demo-isubd-cc/shaders/isubd.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | 3 | uint parentKey(in uint key) 4 | { 5 | return (key >> 2u); 6 | } 7 | 8 | void childrenKeys(in uint key, out uint children[4]) 9 | { 10 | children[0] = (key << 2u) | 0u; 11 | children[1] = (key << 2u) | 1u; 12 | children[2] = (key << 2u) | 2u; 13 | children[3] = (key << 2u) | 3u; 14 | } 15 | 16 | bool isRootKey(in uint key) 17 | { 18 | return (key == 1u); 19 | } 20 | 21 | bool isLeafKey(in uint key) 22 | { 23 | return findMSB(key) == 13; 24 | } 25 | 26 | bool isChildZeroKey(in uint key) 27 | { 28 | return ((key & 3u) == 0u); 29 | } 30 | 31 | // get xform from bit value 32 | mat4 bitToXform(in uint bit) 33 | { 34 | #if 1 35 | const mat4 mcc = mat4(4, 1, 0, 0, 36 | 4, 6, 4, 1, 37 | 0, 1, 4, 6, 38 | 0, 0, 0, 1) / 8.0f; 39 | #else 40 | const mat4 mcc = mat4( 0.686887 , 0.0833333, -0.0202201, 0.0226732, 41 | 0.3333333, 0.833333 , 0.3333333, -0.106006, 42 | -0.0202201, 0.0833333, 0.686887 , 0.72978, 43 | 0 , 0 , 0 , 0.353553); 44 | //mcc = mcc*mcc; 45 | #endif 46 | float b = float(bit); 47 | float c = 1.0f - b; 48 | mat4 m = mat4(c, 0, 0, b, 49 | 0, c, b, 0, 50 | 0, b, c, 0, 51 | b, 0, 0, c); 52 | 53 | return mcc * m; 54 | } 55 | 56 | // get xform from key 57 | void keyToXform(in uint key, out mat4 xfu, out mat4 xfv) 58 | { 59 | xfu = xfv = mat4(1.0f); 60 | 61 | while (key > 1u) { 62 | xfu = bitToXform(key & 1u) * xfu; 63 | key = key >> 1u; 64 | xfv = bitToXform(key & 1u) * xfv; 65 | key = key >> 1u; 66 | } 67 | } 68 | 69 | // get xform from key 70 | void 71 | keyToXform( 72 | in uint key, 73 | out mat4 xfu, out mat4 xfv, 74 | out mat4 xfup, out mat4 xfvp 75 | ) { 76 | keyToXform(parentKey(key), xfup, xfvp); 77 | keyToXform(key, xfu, xfv); 78 | } 79 | 80 | // subdivision routine (vertex position only) with parents 81 | struct ccpatch { 82 | vec4 v[4]; 83 | vec4 tg[4]; 84 | vec4 bg[4]; 85 | vec2 uv[4]; 86 | }; 87 | 88 | void 89 | subd( 90 | in uint key, 91 | in vec4 c_in[16], 92 | out ccpatch p 93 | ) { 94 | mat4 xfu, xfv; keyToXform(key, xfu, xfv); 95 | mat4 x_in = mat4(c_in[0].x, c_in[4].x, c_in[ 8].x, c_in[12].x, 96 | c_in[1].x, c_in[5].x, c_in[ 9].x, c_in[13].x, 97 | c_in[2].x, c_in[6].x, c_in[10].x, c_in[14].x, 98 | c_in[3].x, c_in[7].x, c_in[11].x, c_in[15].x); 99 | mat4 y_in = mat4(c_in[0].y, c_in[4].y, c_in[ 8].y, c_in[12].y, 100 | c_in[1].y, c_in[5].y, c_in[ 9].y, c_in[13].y, 101 | c_in[2].y, c_in[6].y, c_in[10].y, c_in[14].y, 102 | c_in[3].y, c_in[7].y, c_in[11].y, c_in[15].y); 103 | mat4 z_in = mat4(c_in[0].z, c_in[4].z, c_in[ 8].z, c_in[12].z, 104 | c_in[1].z, c_in[5].z, c_in[ 9].z, c_in[13].z, 105 | c_in[2].z, c_in[6].z, c_in[10].z, c_in[14].z, 106 | c_in[3].z, c_in[7].z, c_in[11].z, c_in[15].z); 107 | mat4 u_in = mat4(-1, -1, -1, -1, 108 | 0, 0, 0, 0, 109 | 1, 1, 1, 1, 110 | 2, 2, 2, 2); 111 | mat4 v_in = mat4(-1, 0, 1, 2, 112 | -1, 0, 1, 2, 113 | -1, 0, 1, 2, 114 | -1, 0, 1, 2); 115 | mat4 x_out = xfv * transpose(xfu * x_in); 116 | mat4 y_out = xfv * transpose(xfu * y_in); 117 | mat4 z_out = xfv * transpose(xfu * z_in); 118 | mat4 u_out = xfv * transpose(xfu * u_in); 119 | mat4 v_out = xfv * transpose(xfu * v_in); 120 | 121 | #define v00 vec4(x_out[0][0], y_out[0][0], z_out[0][0], 1) 122 | #define v01 vec4(x_out[1][0], y_out[1][0], z_out[1][0], 1) 123 | #define v02 vec4(x_out[2][0], y_out[2][0], z_out[2][0], 1) 124 | #define v03 vec4(x_out[3][0], y_out[3][0], z_out[3][0], 1) 125 | #define v10 vec4(x_out[0][1], y_out[0][1], z_out[0][1], 1) 126 | #define v11 vec4(x_out[1][1], y_out[1][1], z_out[1][1], 1) 127 | #define v12 vec4(x_out[2][1], y_out[2][1], z_out[2][1], 1) 128 | #define v13 vec4(x_out[3][1], y_out[3][1], z_out[3][1], 1) 129 | #define v20 vec4(x_out[0][2], y_out[0][2], z_out[0][2], 1) 130 | #define v21 vec4(x_out[1][2], y_out[1][2], z_out[1][2], 1) 131 | #define v22 vec4(x_out[2][2], y_out[2][2], z_out[2][2], 1) 132 | #define v23 vec4(x_out[3][2], y_out[3][2], z_out[3][2], 1) 133 | #define v30 vec4(x_out[0][3], y_out[0][3], z_out[0][3], 1) 134 | #define v31 vec4(x_out[1][3], y_out[1][3], z_out[1][3], 1) 135 | #define v32 vec4(x_out[2][3], y_out[2][3], z_out[2][3], 1) 136 | #define v33 vec4(x_out[3][3], y_out[3][3], z_out[3][3], 1) 137 | 138 | #define uv01 vec2(u_out[1][0], v_out[1][0]) 139 | #define uv00 vec2(u_out[0][0], v_out[0][0]) 140 | #define uv02 vec2(u_out[2][0], v_out[2][0]) 141 | #define uv03 vec2(u_out[3][0], v_out[3][0]) 142 | #define uv10 vec2(u_out[0][1], v_out[0][1]) 143 | #define uv11 vec2(u_out[1][1], v_out[1][1]) 144 | #define uv12 vec2(u_out[2][1], v_out[2][1]) 145 | #define uv13 vec2(u_out[3][1], v_out[3][1]) 146 | #define uv20 vec2(u_out[0][2], v_out[0][2]) 147 | #define uv21 vec2(u_out[1][2], v_out[1][2]) 148 | #define uv22 vec2(u_out[2][2], v_out[2][2]) 149 | #define uv23 vec2(u_out[3][2], v_out[3][2]) 150 | #define uv30 vec2(u_out[0][3], v_out[0][3]) 151 | #define uv31 vec2(u_out[1][3], v_out[1][3]) 152 | #define uv32 vec2(u_out[2][3], v_out[2][3]) 153 | #define uv33 vec2(u_out[3][3], v_out[3][3]) 154 | 155 | // vertex positions 156 | p.v[0] = v11; 157 | p.v[1] = v12; 158 | p.v[2] = v22; 159 | p.v[3] = v21; 160 | 161 | // parametric coords 162 | p.uv[0] = uv11; 163 | p.uv[1] = uv12; 164 | p.uv[2] = uv22; 165 | p.uv[3] = uv21; 166 | 167 | // C1 tangents 168 | p.tg[0] = (v21 - v01) / (uv21.x - uv01.x); 169 | p.tg[1] = (v22 - v02) / (uv22.x - uv02.x); 170 | p.tg[2] = (v32 - v12) / (uv32.x - uv12.x); 171 | p.tg[3] = (v31 - v11) / (uv31.x - uv11.x); 172 | 173 | // C1 bitangents 174 | p.bg[0] = (v12 - v10) / (uv12.y - uv01.y); 175 | p.bg[1] = (v13 - v11) / (uv13.y - uv11.y); 176 | p.bg[2] = (v23 - v21) / (uv23.y - uv21.y); 177 | p.bg[3] = (v22 - v20) / (uv22.y - uv20.y); 178 | 179 | #undef uv00 180 | #undef uv01 181 | #undef uv02 182 | #undef uv03 183 | #undef uv10 184 | #undef uv11 185 | #undef uv12 186 | #undef uv13 187 | #undef uv20 188 | #undef uv21 189 | #undef uv22 190 | #undef uv23 191 | #undef uv30 192 | #undef uv31 193 | #undef uv32 194 | #undef uv33 195 | #undef v00 196 | #undef v01 197 | #undef v02 198 | #undef v03 199 | #undef v10 200 | #undef v11 201 | #undef v12 202 | #undef v13 203 | #undef v20 204 | #undef v21 205 | #undef v22 206 | #undef v23 207 | #undef v30 208 | #undef v31 209 | #undef v32 210 | #undef v33 211 | } 212 | -------------------------------------------------------------------------------- /demo-isubd-cc/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | uniform float u_Exposure; 2 | uniform float u_Gamma; 3 | 4 | #if MSAA_FACTOR 5 | uniform sampler2DMS u_FramebufferSampler; 6 | #else 7 | uniform sampler2D u_FramebufferSampler; 8 | #endif 9 | 10 | // ------------------------------------------------------------------------------------------------- 11 | /** 12 | * Vertex Shader 13 | * 14 | * This vertex shader draws a fullscreen quad 15 | */ 16 | #ifdef VERTEX_SHADER 17 | layout(location = 0) out vec2 o_TexCoord; 18 | 19 | void main(void) 20 | { 21 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 22 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 23 | } 24 | #endif 25 | 26 | // ------------------------------------------------------------------------------------------------- 27 | /** 28 | * Fragment Shader 29 | * 30 | * This fragment shader post-processes the scene framebuffer by applying 31 | * tone mapping, gamma correction and image scaling. 32 | */ 33 | #ifdef FRAGMENT_SHADER 34 | layout(location = 0) in vec2 i_TexCoord; 35 | layout(location = 0) out vec4 o_FragColor; 36 | 37 | void main(void) 38 | { 39 | vec4 color = vec4(0); 40 | ivec2 P = ivec2(gl_FragCoord.xy); 41 | 42 | #if MSAA_FACTOR 43 | for (int i = 0; i < MSAA_FACTOR; ++i) { 44 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 45 | 46 | color+= vec4(c.a * c.rgb, c.a); 47 | } 48 | #else 49 | color = texelFetch(u_FramebufferSampler, P, 0); 50 | #endif 51 | if (color.a > 0.0) color.rgb/= color.a; 52 | 53 | // make fragments store positive values 54 | if (any(lessThan(color.rgb, vec3(0)))) { 55 | o_FragColor = vec4(1, 0, 0, 1); 56 | return; 57 | } 58 | 59 | // exposure 60 | color.rgb*= exp2(u_Exposure); 61 | 62 | // gamma 63 | color.rgb = pow(color.rgb, vec3(1.0 / u_Gamma)); 64 | 65 | // final color 66 | o_FragColor = vec4(color.rgb, 1.0); 67 | 68 | // make sure the fragments store real values 69 | if (any(isnan(color.rgb))) 70 | o_FragColor = vec4(1, 0, 0, 1); 71 | 72 | //o_FragColor = vec4(i_TexCoord, 0, 1); 73 | } 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/halfedge.inl: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Halfedge Mesh Loader 3 | // 4 | //////////////////////////////////////////////////////////////////////////////// 5 | template 6 | struct halfedge { 7 | struct vertex { float x, y, z, w; }; 8 | struct edge { int v0, ef, eb, en; }; /* vertex + next, previous, and neighbour edges */ 9 | std::vector vbuf; 10 | std::vector ebuf; 11 | 12 | explicit halfedge(const char *path_to_obj); 13 | private: 14 | // OBJ File 15 | struct objloader { 16 | std::vector vbuf; 17 | std::vector fbuf; 18 | 19 | objloader(const char *filename); 20 | }; 21 | // Conversion 22 | halfedge(const objloader &obj); 23 | }; 24 | typedef halfedge<3> halfedge3; 25 | typedef halfedge<4> halfedge4; 26 | 27 | template 28 | halfedge::halfedge(const char *path_to_obj): 29 | halfedge(halfedge::objloader(path_to_obj)) 30 | {} 31 | 32 | template 33 | halfedge::objloader::objloader(const char *filename) 34 | { 35 | FILE *pf = fopen(filename, "r"); 36 | char buf[1024]; 37 | float v[3], vmin[3] = {1e5}, vmax[3] = {-1e5}; 38 | int dummy, f; 39 | 40 | while(fscanf(pf, "%s", buf) != EOF) { 41 | switch(buf[0]) { 42 | case 'v': 43 | switch(buf[1]) { 44 | case '\0': 45 | fscanf(pf, "%f %f %f", &v[0], &v[1], &v[2]); 46 | for (int i = 0; i < 3; ++i) { 47 | vmin[i] = std::min(vmin[i], v[i]); 48 | vmax[i] = std::max(vmax[i], v[i]); 49 | } 50 | vbuf.push_back({v[0], v[1], v[2], 1.0f}); 51 | break; 52 | default: 53 | fgets(buf, sizeof(buf), pf); 54 | break; 55 | } 56 | break; 57 | 58 | case 'f': 59 | f = 0; 60 | fscanf(pf, "%s", buf); 61 | if (strstr(buf, "//")) { 62 | /* v//n */ 63 | sscanf(buf, "%d//%d", &f, &dummy); 64 | --f; 65 | fbuf.push_back(f); 66 | for (int i = 0; i < N - 1; ++i) { 67 | fscanf(pf, "%d//%d", &f, &dummy); 68 | --f; 69 | fbuf.push_back(f); 70 | } 71 | } else if (sscanf(buf, "%d/%d/%d", &f, &dummy, &dummy) == 3) { 72 | /* v/t/n */ 73 | --f; 74 | fbuf.push_back(f); 75 | for (int i = 0; i < N - 1; ++i) { 76 | fscanf(pf, "%d/%d/%d", &f, &dummy, &dummy); 77 | --f; 78 | fbuf.push_back(f); 79 | } 80 | } else if (sscanf(buf, "%d/%d", &f, &dummy) == 2) { 81 | /* v/t */ 82 | --f; 83 | fbuf.push_back(f); 84 | for (int i = 0; i < N - 1; ++i) { 85 | fscanf(pf, "%d/%d", &f, &dummy); 86 | --f; 87 | fbuf.push_back(f); 88 | } 89 | } else if (sscanf(buf, "%d", &f) == 1) { 90 | /* v */ 91 | --f; 92 | fbuf.push_back(f); 93 | for (int i = 0; i < N - 1; ++i) { 94 | fscanf(pf, "%d", &f); 95 | --f; 96 | fbuf.push_back(f); 97 | } 98 | } 99 | break; 100 | 101 | default: 102 | fgets(buf, sizeof(buf), pf); 103 | break; 104 | } 105 | } 106 | 107 | fclose(pf); 108 | 109 | // unitize 110 | float s1 = vmax[0] - vmin[0]; 111 | float s2 = vmax[1] - vmin[1]; 112 | float s3 = vmax[2] - vmin[2]; 113 | float s = 1.0f / std::max(s3, std::max(s1, s2)); 114 | for (int i = 0; i < (int)vbuf.size(); ++i) { 115 | vbuf[i].x = (vbuf[i].x - vmin[0]) * s; 116 | vbuf[i].y = (vbuf[i].y - vmin[1]) * s; 117 | vbuf[i].z = (vbuf[i].z - vmin[2]) * s; 118 | } 119 | } 120 | 121 | template 122 | halfedge::halfedge(const halfedge::objloader &obj) 123 | { 124 | // get number of vertices and faces 125 | const int vertexCount = (int)obj.vbuf.size(); 126 | const int faceCount = (int)obj.fbuf.size() / N; 127 | std::map hashMap; 128 | 129 | // get all vertices 130 | vbuf = obj.vbuf; 131 | 132 | // build topology 133 | for (int i = 0; i < faceCount; ++i) { 134 | int edgeCount = i * N; 135 | 136 | for (int j = 0; j < N; ++j) { 137 | int v0 = obj.fbuf[edgeCount + j]; 138 | int v1 = obj.fbuf[edgeCount + ((j + 1) % N)]; 139 | edge e = { 140 | v0, 141 | edgeCount + ((j + 1) % N), 142 | edgeCount + (((j - 1) % N + N) % N), 143 | -1 144 | }; 145 | int edgeIndex = edgeCount + j; 146 | int hashID = v0 + vertexCount * v1; 147 | auto it = hashMap.find(hashID); 148 | 149 | if (/* neighbour in ebuf */ it != hashMap.end()) { 150 | int neighbourEdgeIndex = it->second; 151 | 152 | e.en = neighbourEdgeIndex; 153 | ebuf[neighbourEdgeIndex].en = edgeIndex; 154 | } else /* neighbour not in ebuf */ { 155 | int hashID = v1 + vertexCount * v0; 156 | 157 | hashMap.insert(std::pair(hashID, edgeIndex)); 158 | } 159 | 160 | ebuf.push_back(e); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/cull.glsl: -------------------------------------------------------------------------------- 1 | /* dc_frustum - v1.0 - public domain frustum culling GLSL code 2 | (Created by Jonathan Dupuy 2014.11.13) 3 | 4 | */ 5 | #ifndef DCF_INCLUDE_DC_FRUSTUM_GLSL 6 | #define DCF_INCLUDE_DC_FRUSTUM_GLSL 7 | #line 7 8 | 9 | ////////////////////////////////////////////////////////////////////////////// 10 | // 11 | // Frustum Culling API 12 | // 13 | 14 | bool dj_culltest(mat4 mvp, vec3 bmin, vec3 bmax); 15 | 16 | // 17 | // 18 | //// end header file ///////////////////////////////////////////////////// 19 | #endif // DCF_INCLUDE_DC_FRUSTUM_GLSL 20 | 21 | // ************************************************************************************************* 22 | // Frustum Implementation 23 | 24 | struct dc__frustum { 25 | vec4 planes[6]; 26 | }; 27 | 28 | /** 29 | * Extract Frustum Planes from MVP Matrix 30 | * 31 | * Based on "Fast Extraction of Viewing Frustum Planes from the World- 32 | * View-Projection Matrix", by Gil Gribb and Klaus Hartmann. 33 | * This procedure computes the planes of the frustum and normalizes 34 | * them. 35 | */ 36 | void dcf__load(out dc__frustum frustum, mat4 mvp) 37 | { 38 | for (int i = 0; i < 3; ++i) 39 | for (int j = 0; j < 2; ++j) { 40 | frustum.planes[i*2+j].x = mvp[0][3] + (j == 0 ? mvp[0][i] : -mvp[0][i]); 41 | frustum.planes[i*2+j].y = mvp[1][3] + (j == 0 ? mvp[1][i] : -mvp[1][i]); 42 | frustum.planes[i*2+j].z = mvp[2][3] + (j == 0 ? mvp[2][i] : -mvp[2][i]); 43 | frustum.planes[i*2+j].w = mvp[3][3] + (j == 0 ? mvp[3][i] : -mvp[3][i]); 44 | frustum.planes[i*2+j]*= length(frustum.planes[i*2+j].xyz); 45 | } 46 | } 47 | 48 | /** 49 | * Negative Vertex of an AABB 50 | * 51 | * This procedure computes the negative vertex of an AABB 52 | * given a normal. 53 | * See the View Frustum Culling tutorial @ LightHouse3D.com 54 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 55 | */ 56 | vec3 dcf__nvertex(vec3 bmin, vec3 bmax, vec3 n) 57 | { 58 | bvec3 b = greaterThan(n, vec3(0)); 59 | return mix(bmin, bmax, b); 60 | } 61 | 62 | /** 63 | * Frustum-AABB Culling Test 64 | * 65 | * This procedure returns true if the AABB is either inside, or in 66 | * intersection with the frustum, and false otherwise. 67 | * The test is based on the View Frustum Culling tutorial @ LightHouse3D.com 68 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 69 | */ 70 | bool dj_culltest(mat4 mvp, vec3 bmin, vec3 bmax) 71 | { 72 | float a = 1.0; 73 | dc__frustum f; 74 | 75 | dcf__load(f, mvp); 76 | for (int i = 0; i < 6 && a >= 0.0; ++i) { 77 | vec3 n = dcf__nvertex(bmin, bmax, f.planes[i].xyz); 78 | 79 | a = dot(vec4(n, 1.0), f.planes[i]); 80 | } 81 | 82 | return (a >= 0.0); 83 | } 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/fcull.glsl: -------------------------------------------------------------------------------- 1 | /* fcull.glsl - v1.0 - public domain frustum culling GLSL code 2 | (Created by Jonathan Dupuy 2014.11.13) 3 | 4 | */ 5 | ////////////////////////////////////////////////////////////////////////////// 6 | // 7 | // Frustum Culling API 8 | // 9 | 10 | bool frustumCullingTest(mat4 mvp, vec3 bmin, vec3 bmax); 11 | 12 | // 13 | // 14 | //// end header file ///////////////////////////////////////////////////// 15 | 16 | 17 | // ***************************************************************************** 18 | // Frustum Implementation 19 | 20 | struct Frustum { 21 | vec4 planes[6]; 22 | }; 23 | 24 | /** 25 | * Extract Frustum Planes from MVP Matrix 26 | * 27 | * Based on "Fast Extraction of Viewing Frustum Planes from the World- 28 | * View-Projection Matrix", by Gil Gribb and Klaus Hartmann. 29 | * This procedure computes the planes of the frustum and normalizes 30 | * them. 31 | */ 32 | void loadFrustum(out Frustum f, mat4 mvp) 33 | { 34 | for (int i = 0; i < 3; ++i) 35 | for (int j = 0; j < 2; ++j) { 36 | f.planes[i*2+j].x = mvp[0][3] + (j == 0 ? mvp[0][i] : -mvp[0][i]); 37 | f.planes[i*2+j].y = mvp[1][3] + (j == 0 ? mvp[1][i] : -mvp[1][i]); 38 | f.planes[i*2+j].z = mvp[2][3] + (j == 0 ? mvp[2][i] : -mvp[2][i]); 39 | f.planes[i*2+j].w = mvp[3][3] + (j == 0 ? mvp[3][i] : -mvp[3][i]); 40 | f.planes[i*2+j]*= length(f.planes[i*2+j].xyz); 41 | } 42 | } 43 | 44 | /** 45 | * Negative Vertex of an AABB 46 | * 47 | * This procedure computes the negative vertex of an AABB 48 | * given a normal. 49 | * See the View Frustum Culling tutorial @ LightHouse3D.com 50 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 51 | */ 52 | vec3 negativeVertex(vec3 bmin, vec3 bmax, vec3 n) 53 | { 54 | bvec3 b = greaterThan(n, vec3(0)); 55 | return mix(bmin, bmax, b); 56 | } 57 | 58 | /** 59 | * Frustum-AABB Culling Test 60 | * 61 | * This procedure returns true if the AABB is either inside, or in 62 | * intersection with the frustum, and false otherwise. 63 | * The test is based on the View Frustum Culling tutorial @ LightHouse3D.com 64 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 65 | */ 66 | bool frustumCullingTest(mat4 mvp, vec3 bmin, vec3 bmax) 67 | { 68 | float a = 1.0f; 69 | Frustum f; 70 | 71 | loadFrustum(f, mvp); 72 | for (int i = 0; i < 6 && a >= 0.0f; ++i) { 73 | vec3 n = negativeVertex(bmin, bmax, f.planes[i].xyz); 74 | 75 | a = dot(vec4(n, 1.0f), f.planes[i]); 76 | } 77 | 78 | return (a >= 0.0); 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/isubd.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | 3 | uint parentKey(in uint key) 4 | { 5 | return (key >> 2u); 6 | } 7 | 8 | void childrenKeys(in uint key, out uint children[4]) 9 | { 10 | children[0] = (key << 2u) | 0u; 11 | children[1] = (key << 2u) | 1u; 12 | children[2] = (key << 2u) | 2u; 13 | children[3] = (key << 2u) | 3u; 14 | } 15 | 16 | bool isRootKey(in uint key) 17 | { 18 | return (key == 1u); 19 | } 20 | 21 | bool isLeafKey(in uint key) 22 | { 23 | return findMSB(key) == 13; 24 | } 25 | 26 | bool isChildZeroKey(in uint key) 27 | { 28 | return ((key & 3u) == 0u); 29 | } 30 | 31 | // get xform from bit value 32 | mat4 bitToXform(in uint bit) 33 | { 34 | #if 1 35 | const mat4 mcc = mat4(4, 1, 0, 0, 36 | 4, 6, 4, 1, 37 | 0, 1, 4, 6, 38 | 0, 0, 0, 1) / 8.0f; 39 | #else 40 | const mat4 mcc = mat4( 0.686887 , 0.0833333, -0.0202201, 0.0226732, 41 | 0.3333333, 0.833333 , 0.3333333, -0.106006, 42 | -0.0202201, 0.0833333, 0.686887 , 0.72978, 43 | 0 , 0 , 0 , 0.353553); 44 | //mcc = mcc*mcc; 45 | #endif 46 | float b = float(bit); 47 | float c = 1.0f - b; 48 | mat4 m = mat4(c, 0, 0, b, 49 | 0, c, b, 0, 50 | 0, b, c, 0, 51 | b, 0, 0, c); 52 | 53 | return mcc * m; 54 | } 55 | 56 | // get xform from key 57 | void keyToXform(in uint key, out mat4 xfu, out mat4 xfv) 58 | { 59 | xfu = xfv = mat4(1.0f); 60 | 61 | while (key > 1u) { 62 | xfu = bitToXform(key & 1u) * xfu; 63 | key = key >> 1u; 64 | xfv = bitToXform(key & 1u) * xfv; 65 | key = key >> 1u; 66 | } 67 | } 68 | 69 | // get xform from key 70 | void 71 | keyToXform( 72 | in uint key, 73 | out mat4 xfu, out mat4 xfv, 74 | out mat4 xfup, out mat4 xfvp 75 | ) { 76 | keyToXform(parentKey(key), xfup, xfvp); 77 | keyToXform(key, xfu, xfv); 78 | } 79 | 80 | // subdivision routine (vertex position only) with parents 81 | struct ccpatch { 82 | vec4 v[4]; 83 | vec4 tg[4]; 84 | vec4 bg[4]; 85 | vec2 uv[4]; 86 | }; 87 | 88 | void 89 | subd( 90 | in uint key, 91 | in vec4 c_in[16], 92 | out ccpatch p 93 | ) { 94 | mat4 xfu, xfv; keyToXform(key, xfu, xfv); 95 | mat4 x_in = mat4(c_in[0].x, c_in[4].x, c_in[ 8].x, c_in[12].x, 96 | c_in[1].x, c_in[5].x, c_in[ 9].x, c_in[13].x, 97 | c_in[2].x, c_in[6].x, c_in[10].x, c_in[14].x, 98 | c_in[3].x, c_in[7].x, c_in[11].x, c_in[15].x); 99 | mat4 y_in = mat4(c_in[0].y, c_in[4].y, c_in[ 8].y, c_in[12].y, 100 | c_in[1].y, c_in[5].y, c_in[ 9].y, c_in[13].y, 101 | c_in[2].y, c_in[6].y, c_in[10].y, c_in[14].y, 102 | c_in[3].y, c_in[7].y, c_in[11].y, c_in[15].y); 103 | mat4 z_in = mat4(c_in[0].z, c_in[4].z, c_in[ 8].z, c_in[12].z, 104 | c_in[1].z, c_in[5].z, c_in[ 9].z, c_in[13].z, 105 | c_in[2].z, c_in[6].z, c_in[10].z, c_in[14].z, 106 | c_in[3].z, c_in[7].z, c_in[11].z, c_in[15].z); 107 | mat4 u_in = mat4(-1, -1, -1, -1, 108 | 0, 0, 0, 0, 109 | 1, 1, 1, 1, 110 | 2, 2, 2, 2); 111 | mat4 v_in = mat4(-1, 0, 1, 2, 112 | -1, 0, 1, 2, 113 | -1, 0, 1, 2, 114 | -1, 0, 1, 2); 115 | mat4 x_out = xfv * transpose(xfu * x_in); 116 | mat4 y_out = xfv * transpose(xfu * y_in); 117 | mat4 z_out = xfv * transpose(xfu * z_in); 118 | mat4 u_out = xfv * transpose(xfu * u_in); 119 | mat4 v_out = xfv * transpose(xfu * v_in); 120 | 121 | #define v00 vec4(x_out[0][0], y_out[0][0], z_out[0][0], 1) 122 | #define v01 vec4(x_out[1][0], y_out[1][0], z_out[1][0], 1) 123 | #define v02 vec4(x_out[2][0], y_out[2][0], z_out[2][0], 1) 124 | #define v03 vec4(x_out[3][0], y_out[3][0], z_out[3][0], 1) 125 | #define v10 vec4(x_out[0][1], y_out[0][1], z_out[0][1], 1) 126 | #define v11 vec4(x_out[1][1], y_out[1][1], z_out[1][1], 1) 127 | #define v12 vec4(x_out[2][1], y_out[2][1], z_out[2][1], 1) 128 | #define v13 vec4(x_out[3][1], y_out[3][1], z_out[3][1], 1) 129 | #define v20 vec4(x_out[0][2], y_out[0][2], z_out[0][2], 1) 130 | #define v21 vec4(x_out[1][2], y_out[1][2], z_out[1][2], 1) 131 | #define v22 vec4(x_out[2][2], y_out[2][2], z_out[2][2], 1) 132 | #define v23 vec4(x_out[3][2], y_out[3][2], z_out[3][2], 1) 133 | #define v30 vec4(x_out[0][3], y_out[0][3], z_out[0][3], 1) 134 | #define v31 vec4(x_out[1][3], y_out[1][3], z_out[1][3], 1) 135 | #define v32 vec4(x_out[2][3], y_out[2][3], z_out[2][3], 1) 136 | #define v33 vec4(x_out[3][3], y_out[3][3], z_out[3][3], 1) 137 | 138 | #define uv01 vec2(u_out[1][0], v_out[1][0]) 139 | #define uv00 vec2(u_out[0][0], v_out[0][0]) 140 | #define uv02 vec2(u_out[2][0], v_out[2][0]) 141 | #define uv03 vec2(u_out[3][0], v_out[3][0]) 142 | #define uv10 vec2(u_out[0][1], v_out[0][1]) 143 | #define uv11 vec2(u_out[1][1], v_out[1][1]) 144 | #define uv12 vec2(u_out[2][1], v_out[2][1]) 145 | #define uv13 vec2(u_out[3][1], v_out[3][1]) 146 | #define uv20 vec2(u_out[0][2], v_out[0][2]) 147 | #define uv21 vec2(u_out[1][2], v_out[1][2]) 148 | #define uv22 vec2(u_out[2][2], v_out[2][2]) 149 | #define uv23 vec2(u_out[3][2], v_out[3][2]) 150 | #define uv30 vec2(u_out[0][3], v_out[0][3]) 151 | #define uv31 vec2(u_out[1][3], v_out[1][3]) 152 | #define uv32 vec2(u_out[2][3], v_out[2][3]) 153 | #define uv33 vec2(u_out[3][3], v_out[3][3]) 154 | 155 | // vertex positions 156 | p.v[0] = v11; 157 | p.v[1] = v12; 158 | p.v[2] = v22; 159 | p.v[3] = v21; 160 | 161 | // parametric coords 162 | p.uv[0] = uv11; 163 | p.uv[1] = uv12; 164 | p.uv[2] = uv22; 165 | p.uv[3] = uv21; 166 | 167 | // C1 tangents 168 | p.tg[0] = (v21 - v01) / (uv21.x - uv01.x); 169 | p.tg[1] = (v22 - v02) / (uv22.x - uv02.x); 170 | p.tg[2] = (v32 - v12) / (uv32.x - uv12.x); 171 | p.tg[3] = (v31 - v11) / (uv31.x - uv11.x); 172 | 173 | // C1 bitangents 174 | p.bg[0] = (v12 - v10) / (uv12.y - uv01.y); 175 | p.bg[1] = (v13 - v11) / (uv13.y - uv11.y); 176 | p.bg[2] = (v23 - v21) / (uv23.y - uv21.y); 177 | p.bg[3] = (v22 - v20) / (uv22.y - uv20.y); 178 | 179 | #undef uv00 180 | #undef uv01 181 | #undef uv02 182 | #undef uv03 183 | #undef uv10 184 | #undef uv11 185 | #undef uv12 186 | #undef uv13 187 | #undef uv20 188 | #undef uv21 189 | #undef uv22 190 | #undef uv23 191 | #undef uv30 192 | #undef uv31 193 | #undef uv32 194 | #undef uv33 195 | #undef v00 196 | #undef v01 197 | #undef v02 198 | #undef v03 199 | #undef v10 200 | #undef v11 201 | #undef v12 202 | #undef v13 203 | #undef v20 204 | #undef v21 205 | #undef v22 206 | #undef v23 207 | #undef v30 208 | #undef v31 209 | #undef v32 210 | #undef v33 211 | } 212 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/terrain_cs_lod.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_SUBD1) 7 | readonly buffer SubdBufferIn { 8 | uvec2 u_SubdBufferIn[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_SUBD2) 12 | buffer SubdBufferOut { 13 | uvec2 u_SubdBufferOut[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_CULLED_SUBD) 17 | buffer CulledSubdBuffer { 18 | uvec2 u_CulledSubdBuffer[]; 19 | }; 20 | 21 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_VERTICES) 22 | readonly buffer VertexBuffer { 23 | vec4 u_VertexBuffer[]; 24 | }; 25 | 26 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_INDEXES) 27 | readonly buffer IndexBuffer { 28 | uint u_IndexBuffer[]; 29 | }; 30 | 31 | layout (binding = BUFFER_BINDING_SUBD_COUNTER) 32 | uniform atomic_uint u_SubdBufferCounter; 33 | 34 | layout (binding = BUFFER_BINDING_CULLED_SUBD_COUNTER) 35 | uniform atomic_uint u_CulledSubdBufferCounter[2]; 36 | 37 | struct Transform { 38 | mat4 modelView; 39 | mat4 projection; 40 | mat4 modelViewProjection; 41 | mat4 viewInv; 42 | }; 43 | 44 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 45 | uniform Transforms { 46 | Transform u_Transform; 47 | }; 48 | 49 | uniform sampler2D u_DmapSampler; 50 | uniform float u_DmapFactor; 51 | uniform float u_LodFactor; 52 | 53 | // displacement map 54 | float dmap(vec2 pos) 55 | { 56 | #if 0 57 | return cos(20.0 * pos.x) * cos(20.0 * pos.y) / 2.0 * u_DmapFactor; 58 | #else 59 | return (texture(u_DmapSampler, pos * 0.5 + 0.5).x) * u_DmapFactor; 60 | #endif 61 | } 62 | 63 | float distanceToLod(float z, float lodFactor) 64 | { 65 | // Note that we multiply the result by two because the triangle's 66 | // edge lengths decreases by half every two subdivision steps. 67 | return -2.0 * log2(clamp(z * lodFactor, 0.0f, 1.0f)); 68 | } 69 | 70 | float computeLod(vec3 c) 71 | { 72 | #if FLAG_DISPLACE 73 | c.z+= dmap(u_Transform.viewInv[3].xy); 74 | #endif 75 | 76 | vec3 cxf = (u_Transform.modelView * vec4(c, 1)).xyz; 77 | float z = length(cxf); 78 | 79 | return distanceToLod(z, u_LodFactor); 80 | } 81 | 82 | float computeLod(in vec3 v[3]) 83 | { 84 | vec3 c = (v[1] + v[2]) / 2.0; 85 | return computeLod(c); 86 | } 87 | 88 | // ----------------------------------------------------------------------------- 89 | /** 90 | * Compute LoD Shader 91 | * 92 | * This compute shader is responsible for updating the subdivision 93 | * buffer and visible buffer that will be sent to the rasterizer. 94 | */ 95 | #ifdef COMPUTE_SHADER 96 | layout (local_size_x = 1, 97 | local_size_y = 1, 98 | local_size_z = 1) in; 99 | 100 | void writeKey(uint primID, uint key) 101 | { 102 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 103 | 104 | u_SubdBufferOut[idx] = uvec2(primID, key); 105 | } 106 | 107 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 108 | { 109 | // extract subdivision level associated to the key 110 | int keyLod = findMSB(key); 111 | 112 | // update the key accordingly 113 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key)) { 114 | uint children[2]; childrenKeys(key, children); 115 | 116 | writeKey(primID, children[0]); 117 | writeKey(primID, children[1]); 118 | } else if (/* keep ? */ keyLod < (parentLod + 1)) { 119 | writeKey(primID, key); 120 | } else /* merge ? */ { 121 | if (/* is root ? */isRootKey(key)) { 122 | writeKey(primID, key); 123 | } else if (/* is zero child ? */isChildZeroKey(key)) { 124 | writeKey(primID, parentKey(key)); 125 | } 126 | } 127 | } 128 | 129 | void main() 130 | { 131 | // get threadID (each key is associated to a thread) 132 | int threadID = int(gl_GlobalInvocationID.x); 133 | 134 | // get coarse triangle associated to the key 135 | uint primID = u_SubdBufferIn[threadID].x; 136 | vec3 v_in[3] = vec3[3]( 137 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 ]].xyz), 138 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]].xyz), 139 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]].xyz) 140 | ); 141 | 142 | // compute distance-based LOD 143 | uint key = u_SubdBufferIn[threadID].y; 144 | vec3 v[3], vp[3]; subd(key, v_in, v, vp); 145 | int targetLod = int(computeLod(v)); 146 | int parentLod = int(computeLod(vp)); 147 | #if FLAG_FREEZE 148 | targetLod = parentLod = findMSB(key); 149 | #endif 150 | updateSubdBuffer(primID, key, targetLod, parentLod); 151 | 152 | #if FLAG_CULL 153 | // Cull invisible nodes 154 | mat4 mvp = u_Transform.modelViewProjection; 155 | vec3 bmin = min(min(v[0], v[1]), v[2]); 156 | vec3 bmax = max(max(v[0], v[1]), v[2]); 157 | 158 | // account for displacement in bound computations 159 | # if FLAG_DISPLACE 160 | bmin.z = 0; 161 | bmax.z = u_DmapFactor; 162 | # endif 163 | 164 | // update CulledSubdBuffer 165 | if (/* is visible ? */frustumCullingTest(mvp, bmin, bmax)) { 166 | #else 167 | if (true) { 168 | #endif // FLAG_CULL 169 | // write key 170 | uint idx = atomicCounterIncrement(u_CulledSubdBufferCounter[1]); 171 | 172 | u_CulledSubdBuffer[idx] = uvec2(primID, key); 173 | } 174 | } 175 | #endif 176 | 177 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/terrain_cs_render.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_CULLED_SUBD) 7 | buffer CulledSubdBuffer { 8 | uvec2 u_CulledSubdBuffer[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_VERTICES) 12 | readonly buffer VertexBuffer { 13 | vec4 u_VertexBuffer[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_INDEXES) 17 | readonly buffer IndexBuffer { 18 | uint u_IndexBuffer[]; 19 | }; 20 | 21 | struct Transform { 22 | mat4 modelView; 23 | mat4 projection; 24 | mat4 modelViewProjection; 25 | mat4 viewInv; 26 | }; 27 | 28 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 29 | uniform Transforms { 30 | Transform u_Transform; 31 | }; 32 | 33 | uniform sampler2D u_DmapSampler; 34 | uniform float u_DmapFactor; 35 | uniform float u_LodFactor; 36 | 37 | // displacement map 38 | float dmap(vec2 pos) 39 | { 40 | #if 0 41 | return cos(20.0 * pos.x) * cos(20.0 * pos.y) / 2.0 * u_DmapFactor; 42 | #else 43 | return (texture(u_DmapSampler, pos * 0.5 + 0.5).x) * u_DmapFactor; 44 | #endif 45 | } 46 | 47 | // ----------------------------------------------------------------------------- 48 | /** 49 | * Compute LoD Shader 50 | * 51 | * This compute shader is responsible for updating the subdivision 52 | * buffer and visible buffer that will be sent to the rasterizer. 53 | */ 54 | #ifdef VERTEX_SHADER 55 | layout(location = 0) in vec2 i_TessCoord; 56 | layout(location = 0) out vec2 o_TexCoord; 57 | 58 | void main() 59 | { 60 | // get threadID (each key is associated to a thread) 61 | int threadID = gl_InstanceID; 62 | 63 | // get coarse triangle associated to the key 64 | uint primID = u_CulledSubdBuffer[threadID].x; 65 | vec3 v_in[3] = vec3[3]( 66 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 ]].xyz), 67 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]].xyz), 68 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]].xyz) 69 | ); 70 | 71 | // compute sub-triangle associated to the key 72 | uint key = u_CulledSubdBuffer[threadID].y; 73 | vec3 v[3]; subd(key, v_in, v); 74 | 75 | // compute vertex location 76 | vec3 finalVertex = berp(v, i_TessCoord); 77 | #if FLAG_DISPLACE 78 | finalVertex.z+= dmap(finalVertex.xy); 79 | #endif 80 | 81 | o_TexCoord = i_TessCoord; 82 | gl_Position = u_Transform.modelViewProjection * vec4(finalVertex, 1); 83 | } 84 | #endif 85 | 86 | // ----------------------------------------------------------------------------- 87 | /** 88 | * Fragment Shader 89 | * 90 | * This fragment shader is responsible for shading the final geometry. 91 | */ 92 | #ifdef FRAGMENT_SHADER 93 | layout(location = 0) in vec2 i_TexCoord; 94 | layout(location = 0) out vec4 o_FragColor; 95 | 96 | void main() 97 | { 98 | vec3 c[3] = vec3[3](vec3(0.0,1.0,1.0)/4.0, 99 | vec3(0.86,0.00,0.00), 100 | vec3(1.0,0.50,0.00)/1.0); 101 | vec3 color = berp(c, i_TexCoord); 102 | 103 | o_FragColor = vec4(color, 1); 104 | } 105 | #endif 106 | 107 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/terrain_gs.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering (using a geometry shader) 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_SUBD1) 7 | readonly buffer SubdBufferIn { 8 | uvec2 u_SubdBufferIn[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_SUBD2) 12 | buffer SubdBufferOut { 13 | uvec2 u_SubdBufferOut[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_VERTICES) 17 | readonly buffer VertexBuffer { 18 | vec4 u_VertexBuffer[]; 19 | }; 20 | 21 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_INDEXES) 22 | readonly buffer IndexBuffer { 23 | uint u_IndexBuffer[]; 24 | }; 25 | 26 | layout (binding = BUFFER_BINDING_SUBD_COUNTER) 27 | uniform atomic_uint u_SubdBufferCounter; 28 | 29 | struct Transform { 30 | mat4 modelView; 31 | mat4 projection; 32 | mat4 modelViewProjection; 33 | mat4 viewInv; 34 | }; 35 | 36 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 37 | uniform Transforms { 38 | Transform u_Transform; 39 | }; 40 | 41 | uniform sampler2D u_DmapSampler; 42 | uniform float u_DmapFactor; 43 | uniform float u_LodFactor; 44 | 45 | // displacement map 46 | float dmap(vec2 pos) 47 | { 48 | #if 0 49 | return cos(20.0 * pos.x) * cos(20.0 * pos.y) / 2.0 * u_DmapFactor; 50 | #else 51 | return (texture(u_DmapSampler, pos * 0.5 + 0.5).x) * u_DmapFactor; 52 | #endif 53 | } 54 | 55 | float distanceToLod(float z, float lodFactor) 56 | { 57 | // Note that we multiply the result by two because the triangle's 58 | // edge lengths decreases by half every two subdivision steps. 59 | return -2.0 * log2(clamp(z * lodFactor, 0.0f, 1.0f)); 60 | } 61 | 62 | 63 | // ----------------------------------------------------------------------------- 64 | /** 65 | * Vertex Shader 66 | * 67 | * The vertex shader is empty 68 | */ 69 | #ifdef VERTEX_SHADER 70 | void main(void) 71 | { } 72 | #endif 73 | 74 | // ----------------------------------------------------------------------------- 75 | /** 76 | * Geometry Shader 77 | * 78 | * This geometry shader is responsible for updating the 79 | * subdivision buffer and sending visible geometry to the rasterizer. 80 | */ 81 | #ifdef GEOMETRY_SHADER 82 | layout(points) in; 83 | layout(triangle_strip, max_vertices = VERTICES_OUT) out; 84 | layout(location = 0) out vec2 o_TexCoord; 85 | 86 | float computeLod(vec3 c) 87 | { 88 | #if FLAG_DISPLACE 89 | c.z+= dmap(u_Transform.viewInv[3].xy); 90 | #endif 91 | 92 | vec3 cxf = (u_Transform.modelView * vec4(c, 1)).xyz; 93 | float z = length(cxf); 94 | 95 | return distanceToLod(z, u_LodFactor); 96 | } 97 | 98 | float computeLod(in vec3 v[3]) 99 | { 100 | vec3 c = (v[1] + v[2]) / 2.0; 101 | return computeLod(c); 102 | } 103 | 104 | void writeKey(uint primID, uint key) 105 | { 106 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 107 | 108 | u_SubdBufferOut[idx] = uvec2(primID, key); 109 | } 110 | 111 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 112 | { 113 | // extract subdivision level associated to the key 114 | int keyLod = findMSB(key); 115 | 116 | // update the key accordingly 117 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key)) { 118 | uint children[2]; childrenKeys(key, children); 119 | 120 | writeKey(primID, children[0]); 121 | writeKey(primID, children[1]); 122 | } else if (/* keep ? */ keyLod < (parentLod + 1)) { 123 | writeKey(primID, key); 124 | } else /* merge ? */ { 125 | if (/* is root ? */isRootKey(key)) { 126 | writeKey(primID, key); 127 | } else if (/* is zero child ? */isChildZeroKey(key)) { 128 | writeKey(primID, parentKey(key)); 129 | } 130 | } 131 | } 132 | 133 | void main() 134 | { 135 | // get threadID (each key is associated to a thread) 136 | int threadID = gl_PrimitiveIDIn; 137 | 138 | // get coarse triangle associated to the key 139 | uint primID = u_SubdBufferIn[threadID].x; 140 | vec3 v_in[3] = vec3[3]( 141 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 ]].xyz), 142 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]].xyz), 143 | vec3(u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]].xyz) 144 | ); 145 | 146 | // compute distance-based LOD 147 | uint key = u_SubdBufferIn[threadID].y; 148 | vec3 v[3], vp[3]; subd(key, v_in, v, vp); 149 | int targetLod = int(computeLod(v)); 150 | int parentLod = int(computeLod(vp)); 151 | #if FLAG_FREEZE 152 | targetLod = parentLod = findMSB(key); 153 | #endif 154 | updateSubdBuffer(primID, key, targetLod, parentLod); 155 | 156 | #if FLAG_CULL 157 | // Cull invisible nodes 158 | mat4 mvp = u_Transform.modelViewProjection; 159 | vec3 bmin = min(min(v[0], v[1]), v[2]); 160 | vec3 bmax = max(max(v[0], v[1]), v[2]); 161 | 162 | // account for displacement in bound computations 163 | # if FLAG_DISPLACE 164 | bmin.z = 0; 165 | bmax.z = u_DmapFactor; 166 | # endif 167 | 168 | if (/* is visible ? */frustumCullingTest(mvp, bmin, bmax)) { 169 | #else 170 | if (true) { 171 | #endif // FLAG_CULL 172 | // set tess levels 173 | int edgeCnt = PATCH_TESS_LEVEL; 174 | float edgeLength = 1.0 / float(edgeCnt); 175 | 176 | for (int i = 0; i < edgeCnt; ++i) { 177 | int vertexCnt = 2 * i + 3; 178 | 179 | // start a strip 180 | for (int j = 0; j < vertexCnt; ++j) { 181 | int ui = j >> 1; 182 | int vi = (edgeCnt - 1) - (i - (j & 1)); 183 | vec2 tessCoord = vec2(ui, vi) * edgeLength; 184 | vec3 finalVertex = berp(v, tessCoord); 185 | 186 | #if FLAG_DISPLACE 187 | finalVertex.z+= dmap(finalVertex.xy); 188 | #endif 189 | 190 | o_TexCoord = tessCoord; 191 | gl_Position = u_Transform.modelViewProjection * vec4(finalVertex, 1); 192 | EmitVertex(); 193 | } 194 | EndPrimitive(); 195 | } 196 | } 197 | 198 | } 199 | #endif 200 | 201 | // ----------------------------------------------------------------------------- 202 | /** 203 | * Fragment Shader 204 | * 205 | * This fragment shader is responsible for shading the final geometry. 206 | */ 207 | #ifdef FRAGMENT_SHADER 208 | layout(location = 0) in vec2 i_TexCoord; 209 | layout(location = 0) out vec4 o_FragColor; 210 | 211 | void main() 212 | { 213 | vec3 c[3] = vec3[3](vec3(0.0,1.0,1.0)/4.0, 214 | vec3(0.86,0.00,0.00), 215 | vec3(1.0,0.50,0.00)/1.0); 216 | vec3 color = berp(c, i_TexCoord); 217 | 218 | o_FragColor = vec4(color, 1); 219 | } 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/terrain_ts.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // Implicit Subdivition Sahder for Terrain Rendering 4 | // 5 | 6 | layout (std430, binding = BUFFER_BINDING_SUBD1) 7 | readonly buffer SubdBufferIn { 8 | uvec2 u_SubdBufferIn[]; 9 | }; 10 | 11 | layout (std430, binding = BUFFER_BINDING_SUBD2) 12 | buffer SubdBufferOut { 13 | uvec2 u_SubdBufferOut[]; 14 | }; 15 | 16 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_VERTICES) 17 | readonly buffer VertexBuffer { 18 | vec4 u_VertexBuffer[]; 19 | }; 20 | 21 | layout (std430, binding = BUFFER_BINDING_GEOMETRY_EDGES) 22 | readonly buffer EdgeBuffer { 23 | ivec4 u_EdgeBuffer[]; 24 | }; 25 | 26 | layout (binding = BUFFER_BINDING_SUBD_COUNTER) 27 | uniform atomic_uint u_SubdBufferCounter; 28 | 29 | struct Transform { 30 | mat4 modelView; 31 | mat4 projection; 32 | mat4 modelViewProjection; 33 | mat4 viewInv; 34 | }; 35 | 36 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 37 | uniform Transforms { 38 | Transform u_Transform; 39 | }; 40 | 41 | uniform sampler2D u_DmapSampler; 42 | uniform float u_DmapFactor; 43 | uniform float u_LodFactor; 44 | 45 | // displacement map 46 | float dmap(vec2 pos) 47 | { 48 | #if 0 49 | return cos(20.0 * pos.x) * cos(20.0 * pos.y) / 2.0 * u_DmapFactor; 50 | #else 51 | return (texture(u_DmapSampler, pos * 0.5 + 0.5).x) * u_DmapFactor; 52 | #endif 53 | } 54 | 55 | float distanceToLod(float z, float lodFactor) 56 | { 57 | // Note that we multiply the result by two because the triangle's 58 | // edge lengths decreases by half every two subdivision steps. 59 | return -2.0 * log2(clamp(z * lodFactor, 0.0f, 1.0f)); 60 | } 61 | 62 | float computeLod(vec3 c) 63 | { 64 | #if FLAG_DISPLACE 65 | c.z+= dmap(u_Transform.viewInv[3].xy); 66 | #endif 67 | 68 | vec3 cxf = (u_Transform.modelView * vec4(c, 1)).xyz; 69 | float z = length(cxf); 70 | 71 | return distanceToLod(z, u_LodFactor); 72 | } 73 | 74 | float computeLod(in vec3 v[3]) 75 | { 76 | vec3 c = (v[1] + v[2]) / 2.0; 77 | return computeLod(c); 78 | } 79 | 80 | // ----------------------------------------------------------------------------- 81 | /** 82 | * Vertex Shader 83 | * 84 | * The vertex shader is empty 85 | */ 86 | #ifdef VERTEX_SHADER 87 | void main() 88 | { } 89 | #endif 90 | 91 | // ----------------------------------------------------------------------------- 92 | /** 93 | * Tessellation Control Shader 94 | * 95 | * This tessellaction control shader is responsible for updating the 96 | * subdivision buffer and sending visible geometry to the rasterizer. 97 | */ 98 | #ifdef TESS_CONTROL_SHADER 99 | layout (vertices = 1) out; 100 | out Patch { 101 | vec3 vertices[3]; 102 | flat uint key; 103 | } o_Patch[]; 104 | 105 | void writeKey(uint primID, uint key) 106 | { 107 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 108 | 109 | u_SubdBufferOut[idx] = uvec2(primID, key); 110 | } 111 | 112 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 113 | { 114 | // extract subdivision level associated to the key 115 | int keyLod = findMSB(key); 116 | 117 | // update the key accordingly 118 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key)) { 119 | uint children[2]; childrenKeys(key, children); 120 | 121 | writeKey(primID, children[0]); 122 | writeKey(primID, children[1]); 123 | } else if (/* keep ? */ keyLod < (parentLod + 1)) { 124 | writeKey(primID, key); 125 | } else /* merge ? */ { 126 | if (/* is root ? */isRootKey(key)) { 127 | writeKey(primID, key); 128 | } else if (/* is zero child ? */isChildZeroKey(key)) { 129 | writeKey(primID, parentKey(key)); 130 | } 131 | } 132 | } 133 | 134 | void main() 135 | { 136 | // get threadID (each key is associated to a thread) 137 | int threadID = gl_PrimitiveID; 138 | 139 | // get coarse triangle associated to the key 140 | uint primID = u_SubdBufferIn[threadID].x; 141 | int i0 = u_EdgeBuffer[4*primID ].x; 142 | int i1 = u_EdgeBuffer[4*primID+1].x; 143 | int i2 = u_EdgeBuffer[4*primID+2].x; 144 | vec3 v_in[3] = vec3[3]( 145 | vec3(u_VertexBuffer[i0].zxy), 146 | vec3(u_VertexBuffer[i1].zxy), 147 | vec3(u_VertexBuffer[i2].zxy) 148 | ); 149 | 150 | // compute distance-based LOD 151 | uint key = u_SubdBufferIn[threadID].y; 152 | vec3 v[3], vp[3]; subd(key, v_in, v, vp); 153 | int targetLod = int(computeLod(v)); 154 | int parentLod = int(computeLod(vp)); 155 | #if FLAG_FREEZE 156 | targetLod = parentLod = findMSB(key); 157 | #endif 158 | updateSubdBuffer(primID, key, targetLod, parentLod); 159 | 160 | #if FLAG_CULL 161 | // Cull invisible nodes 162 | mat4 mvp = u_Transform.modelViewProjection; 163 | vec3 bmin = min(min(v[0], v[1]), v[2]); 164 | vec3 bmax = max(max(v[0], v[1]), v[2]); 165 | 166 | // account for displacement in bound computations 167 | # if FLAG_DISPLACE 168 | bmin.z = 0; 169 | bmax.z = u_DmapFactor; 170 | # endif 171 | 172 | if (/* is visible ? */frustumCullingTest(mvp, bmin, bmax)) { 173 | #else 174 | if (true) { 175 | #endif // FLAG_CULL 176 | // set tess levels 177 | int tessLevel = PATCH_TESS_LEVEL; 178 | gl_TessLevelInner[0] = 179 | gl_TessLevelInner[1] = 180 | gl_TessLevelOuter[0] = 181 | gl_TessLevelOuter[1] = 182 | gl_TessLevelOuter[2] = tessLevel; 183 | 184 | // set output data 185 | o_Patch[gl_InvocationID].vertices = v; 186 | o_Patch[gl_InvocationID].key = key; 187 | } else /* is not visible ? */ { 188 | // cull the geometry 189 | gl_TessLevelInner[0] = 190 | gl_TessLevelInner[1] = 191 | gl_TessLevelOuter[0] = 192 | gl_TessLevelOuter[1] = 193 | gl_TessLevelOuter[2] = 0; 194 | } 195 | } 196 | #endif 197 | 198 | // ----------------------------------------------------------------------------- 199 | /** 200 | * Tessellation Evaluation Shader 201 | * 202 | * This tessellaction evaluation shader is responsible for placing the 203 | * geometry properly on the input mesh (here a terrain). 204 | */ 205 | #ifdef TESS_EVALUATION_SHADER 206 | layout (triangles, ccw, equal_spacing) in; 207 | in Patch { 208 | vec3 vertices[3]; 209 | flat uint key; 210 | } i_Patch[]; 211 | 212 | layout(location = 0) out vec2 o_TexCoord; 213 | 214 | void main() 215 | { 216 | vec3 v[3] = i_Patch[0].vertices; 217 | vec3 finalVertex = berp(v, gl_TessCoord.xy); 218 | 219 | #if FLAG_DISPLACE 220 | finalVertex.z+= dmap(finalVertex.xy); 221 | #endif 222 | 223 | o_TexCoord = gl_TessCoord.xy; 224 | gl_Position = u_Transform.modelViewProjection * vec4(finalVertex, 1); 225 | } 226 | #endif 227 | 228 | // ----------------------------------------------------------------------------- 229 | /** 230 | * Fragment Shader 231 | * 232 | * This fragment shader is responsible for shading the final geometry. 233 | */ 234 | #ifdef FRAGMENT_SHADER 235 | layout(location = 0) in vec2 i_TexCoord; 236 | layout(location = 0) out vec4 o_FragColor; 237 | 238 | void main() 239 | { 240 | vec3 c[3] = vec3[3](vec3(0.0,1.0,1.0)/4.0, 241 | vec3(0.86,0.00,0.00), 242 | vec3(1.0,0.50,0.00)/1.0); 243 | vec3 color = berp(c, i_TexCoord); 244 | 245 | o_FragColor = vec4(color, 1); 246 | } 247 | 248 | #endif 249 | -------------------------------------------------------------------------------- /demo-isubd-ccmesh/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | uniform float u_Exposure; 2 | uniform float u_Gamma; 3 | 4 | #if MSAA_FACTOR 5 | uniform sampler2DMS u_FramebufferSampler; 6 | #else 7 | uniform sampler2D u_FramebufferSampler; 8 | #endif 9 | 10 | // ------------------------------------------------------------------------------------------------- 11 | /** 12 | * Vertex Shader 13 | * 14 | * This vertex shader draws a fullscreen quad 15 | */ 16 | #ifdef VERTEX_SHADER 17 | layout(location = 0) out vec2 o_TexCoord; 18 | 19 | void main(void) 20 | { 21 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 22 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 23 | } 24 | #endif 25 | 26 | // ------------------------------------------------------------------------------------------------- 27 | /** 28 | * Fragment Shader 29 | * 30 | * This fragment shader post-processes the scene framebuffer by applying 31 | * tone mapping, gamma correction and image scaling. 32 | */ 33 | #ifdef FRAGMENT_SHADER 34 | layout(location = 0) in vec2 i_TexCoord; 35 | layout(location = 0) out vec4 o_FragColor; 36 | 37 | void main(void) 38 | { 39 | vec4 color = vec4(0); 40 | ivec2 P = ivec2(gl_FragCoord.xy); 41 | 42 | #if MSAA_FACTOR 43 | for (int i = 0; i < MSAA_FACTOR; ++i) { 44 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 45 | 46 | color+= vec4(c.a * c.rgb, c.a); 47 | } 48 | #else 49 | color = texelFetch(u_FramebufferSampler, P, 0); 50 | #endif 51 | if (color.a > 0.0) color.rgb/= color.a; 52 | 53 | // make fragments store positive values 54 | if (any(lessThan(color.rgb, vec3(0)))) { 55 | o_FragColor = vec4(1, 0, 0, 1); 56 | return; 57 | } 58 | 59 | // exposure 60 | color.rgb*= exp2(u_Exposure); 61 | 62 | // gamma 63 | color.rgb = pow(color.rgb, vec3(1.0 / u_Gamma)); 64 | 65 | // final color 66 | o_FragColor = vec4(color.rgb, 1.0); 67 | 68 | // make sure the fragments store real values 69 | if (any(isnan(color.rgb))) 70 | o_FragColor = vec4(1, 0, 0, 1); 71 | 72 | //o_FragColor = vec4(i_TexCoord, 0, 1); 73 | } 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /demo-isubd-terrain/README.md: -------------------------------------------------------------------------------- 1 | # Implicit Subdivision on the GPU 2 | 3 | ![alt text](preview.png "Preview") 4 | 5 | ## License 6 | This code is public domain. 7 | 8 | ## Details 9 | This demo provides additional implementations for the article 10 | "[Adaptive GPU Tessellation with Compute Shaders](http://onrendering.com/data/papers/isubd/isubd.pdf)" by 11 | [Jad Khoury](https://github.com/jadkhoury), 12 | [Jonathan Dupuy](http://onrendering.com/) (myself) and 13 | [Christophe Riccio](https://github.com/g-truc); 14 | the paper is available on my website: . 15 | 16 | Many thanks to [Cyril Crassin](https://twitter.com/Icare3D) (from NVIDIA) for 17 | helping me putting this demo up (the mesh shader pipeline exists thanks to 18 | his help). 19 | 20 | Specifically, the demo renders a terrain with an adaptive mesh that is handled entirely on the GPU. The adaptive mesh is built from an implicit representation that works similarly to a binary tree, which can undergo up to 31 subdivisions (this exceeds by far the capabilities of current GPU tessellation units, which are limited to 6 levels). Each node of the tree is further triangulated with a constant tessellation factor that can be modified by the user. Our code can achieve this via 4 different rendering techniques: 21 | 22 | * *Compute Shader* -- Similar to that of the original article; original demo available here: https://github.com/jadkhoury/TessellationDemo. 23 | 24 | * *Tessellation Shader* -- Single-pass rendering with tessellation shaders; the tessellation control shader produces the triangulation at each node of the implicit binary tree (via *fixed* tessellation factors). 25 | 26 | * *Geometry Shader* -- Single-pass rendering with a geometry shader; the geometry shader produces the triangulation at each node of the implicit binary tree. 27 | 28 | * *Mesh Shader* -- Single-pass rendering with a task and mesh shader (Turing GPUs only); the mesh shader produces the triangulation at each node of the implicit binary tree. 29 | 30 | The demo loads a 16-bit displacement map and allows the user to play with the subdivision parameters. 31 | This source code is released in order to facilitate adoption by developpers. 32 | 33 | ## Some Notes for Performance Comparisons 34 | The user can interactively change the rendering pipeline at run-time and see how performance scales. Please do keep in mind however: 35 | * We did not extensively optimize the rendering pipelines: We believe that our algorithms can be made faster with careful optimization. This demo is meant to provide the algorithms to implement a fast terrain renderer, but we leave it up to the developper to make it run optimally on the platforms they target. 36 | * The tessellation shader pipeline produces more triangles than the other pipelines: Although all rendering pipelines target the same polygon per pixel density, the tessellation produced by the tessellation shader pipeline produces more triangles than the other pipelines. This is because GPU tessellation units have a fixed, hardware accelerated triangulation algorithm that differs from the one we use in the other rendering pipelines. This is something to keep in mind if rasterization becomes the bottleneck. 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /demo-isubd-terrain/isubd-terrain.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/demo-isubd-terrain/isubd-terrain.exe -------------------------------------------------------------------------------- /demo-isubd-terrain/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/demo-isubd-terrain/preview.png -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/fcull.glsl: -------------------------------------------------------------------------------- 1 | /* fcull.glsl - v1.0 - public domain frustum culling GLSL code 2 | (Created by Jonathan Dupuy 2014.11.13) 3 | 4 | */ 5 | ////////////////////////////////////////////////////////////////////////////// 6 | // 7 | // Frustum Culling API 8 | // 9 | 10 | bool frustumCullingTest(mat4 mvp, vec3 bmin, vec3 bmax); 11 | 12 | // 13 | // 14 | //// end header file ///////////////////////////////////////////////////// 15 | 16 | 17 | // ***************************************************************************** 18 | // Frustum Implementation 19 | 20 | struct Frustum { 21 | vec4 planes[6]; 22 | }; 23 | 24 | /** 25 | * Extract Frustum Planes from MVP Matrix 26 | * 27 | * Based on "Fast Extraction of Viewing Frustum Planes from the World- 28 | * View-Projection Matrix", by Gil Gribb and Klaus Hartmann. 29 | * This procedure computes the planes of the frustum and normalizes 30 | * them. 31 | */ 32 | void loadFrustum(out Frustum f, mat4 mvp) 33 | { 34 | for (int i = 0; i < 3; ++i) 35 | for (int j = 0; j < 2; ++j) { 36 | f.planes[i*2+j].x = mvp[0][3] + (j == 0 ? mvp[0][i] : -mvp[0][i]); 37 | f.planes[i*2+j].y = mvp[1][3] + (j == 0 ? mvp[1][i] : -mvp[1][i]); 38 | f.planes[i*2+j].z = mvp[2][3] + (j == 0 ? mvp[2][i] : -mvp[2][i]); 39 | f.planes[i*2+j].w = mvp[3][3] + (j == 0 ? mvp[3][i] : -mvp[3][i]); 40 | f.planes[i*2+j]*= length(f.planes[i*2+j].xyz); 41 | } 42 | } 43 | 44 | /** 45 | * Negative Vertex of an AABB 46 | * 47 | * This procedure computes the negative vertex of an AABB 48 | * given a normal. 49 | * See the View Frustum Culling tutorial @ LightHouse3D.com 50 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 51 | */ 52 | vec3 negativeVertex(vec3 bmin, vec3 bmax, vec3 n) 53 | { 54 | bvec3 b = greaterThan(n, vec3(0)); 55 | return mix(bmin, bmax, b); 56 | } 57 | 58 | /** 59 | * Frustum-AABB Culling Test 60 | * 61 | * This procedure returns true if the AABB is either inside, or in 62 | * intersection with the frustum, and false otherwise. 63 | * The test is based on the View Frustum Culling tutorial @ LightHouse3D.com 64 | * http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/ 65 | */ 66 | bool frustumCullingTest(mat4 mvp, vec3 bmin, vec3 bmax) 67 | { 68 | float a = 1.0f; 69 | Frustum f; 70 | 71 | loadFrustum(f, mvp); 72 | for (int i = 0; i < 6 && a >= 0.0f; ++i) { 73 | vec3 n = negativeVertex(bmin, bmax, f.planes[i].xyz); 74 | 75 | a = dot(vec4(n, 1.0f), f.planes[i]); 76 | } 77 | 78 | return (a >= 0.0); 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/isubd.glsl: -------------------------------------------------------------------------------- 1 | /* isubd.glsl - public domain implicit subdivision on the GPU 2 | (created by Jonathan Dupuy) 3 | */ 4 | uint parentKey(in uint key) 5 | { 6 | return (key >> 1u); 7 | } 8 | 9 | void childrenKeys(in uint key, out uint children[2]) 10 | { 11 | children[0] = (key << 1u) | 0u; 12 | children[1] = (key << 1u) | 1u; 13 | } 14 | 15 | bool isRootKey(in uint key) 16 | { 17 | return (key == 1u); 18 | } 19 | 20 | bool isLeafKey(in uint key) 21 | { 22 | return findMSB(key) == 31; 23 | } 24 | 25 | bool isChildZeroKey(in uint key) 26 | { 27 | return ((key & 1u) == 0u); 28 | } 29 | 30 | // barycentric interpolation 31 | vec3 berp(in vec3 v[3], in vec2 u) 32 | { 33 | return v[0] + u.x * (v[1] - v[0]) + u.y * (v[2] - v[0]); 34 | } 35 | vec4 berp(in vec4 v[3], in vec2 u) 36 | { 37 | return v[0] + u.x * (v[1] - v[0]) + u.y * (v[2] - v[0]); 38 | } 39 | 40 | // get xform from bit value 41 | mat3 bitToXform(in uint bit) 42 | { 43 | float b = float(bit); 44 | float c = 1.0f - b; 45 | vec3 c1 = vec3(0.0f, c , b ); 46 | vec3 c2 = vec3(0.5f, b , 0.0f); 47 | vec3 c3 = vec3(0.5f, 0.0f, c ); 48 | 49 | return mat3(c1, c2, c3); 50 | } 51 | 52 | // get xform from key 53 | mat3 keyToXform(in uint key) 54 | { 55 | mat3 xf = mat3(1.0f); 56 | 57 | while (key > 1u) { 58 | xf*= bitToXform(key & 1u); 59 | key = key >> 1u; 60 | } 61 | 62 | return xf; 63 | } 64 | 65 | // get xform from key as well as xform from parent key 66 | mat3 keyToXform(in uint key, out mat3 xfp) 67 | { 68 | // TODO: optimize 69 | xfp = keyToXform(parentKey(key)); 70 | return keyToXform(key); 71 | } 72 | 73 | // subdivision routine (vertex position only) 74 | void subd(in uint key, in vec4 v_in[3], out vec4 v_out[3]) 75 | { 76 | mat3 xf = keyToXform(key); 77 | mat4x3 v = xf * transpose(mat3x4(v_in[0], v_in[1], v_in[2])); 78 | 79 | v_out[0] = vec4(v[0][0], v[1][0], v[2][0], v[3][0]); 80 | v_out[1] = vec4(v[0][1], v[1][1], v[2][1], v[3][1]); 81 | v_out[2] = vec4(v[0][2], v[1][2], v[2][2], v[3][2]); 82 | } 83 | 84 | // subdivision routine (vertex position only) 85 | // also computes parent position 86 | void subd(in uint key, in vec4 v_in[3], out vec4 v_out[3], out vec4 v_out_p[3]) 87 | { 88 | mat3 xfp; mat3 xf = keyToXform(key, xfp); 89 | mat4x3 v = xf * transpose(mat3x4(v_in[0], v_in[1], v_in[2])); 90 | mat4x3 vp = xfp * transpose(mat3x4(v_in[0], v_in[1], v_in[2])); 91 | 92 | v_out[0] = vec4(v[0][0], v[1][0], v[2][0], v[3][0]); 93 | v_out[1] = vec4(v[0][1], v[1][1], v[2][1], v[3][1]); 94 | v_out[2] = vec4(v[0][2], v[1][2], v[2][2], v[3][2]); 95 | 96 | v_out_p[0] = vec4(vp[0][0], vp[1][0], vp[2][0], vp[3][0]); 97 | v_out_p[1] = vec4(vp[0][1], vp[1][1], vp[2][1], vp[3][1]); 98 | v_out_p[2] = vec4(vp[0][2], vp[1][2], vp[2][2], vp[3][2]); 99 | } 100 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_common.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_common.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | */ 4 | 5 | //The rest of the code is inside those headers which are included by the C-code: 6 | //Include isubd.glsl 7 | 8 | layout(std430, binding = BUFFER_BINDING_GEOMETRY_VERTICES) 9 | readonly buffer VertexBuffer { 10 | vec4 u_VertexBuffer[]; 11 | }; 12 | 13 | layout(std430, binding = BUFFER_BINDING_GEOMETRY_INDEXES) 14 | readonly buffer IndexBuffer { 15 | uint u_IndexBuffer[]; 16 | }; 17 | 18 | layout(binding = BUFFER_BINDING_SUBD_COUNTER) 19 | uniform atomic_uint u_SubdBufferCounter; 20 | 21 | layout(std430, binding = BUFFER_BINDING_SUBD2) 22 | buffer SubdBufferOut { 23 | uvec2 u_SubdBufferOut[]; 24 | }; 25 | 26 | struct Transform { 27 | mat4 modelView; 28 | mat4 projection; 29 | mat4 modelViewProjection; 30 | mat4 viewInv; 31 | }; 32 | 33 | layout(std140, column_major, binding = BUFFER_BINDING_TRANSFORMS) 34 | uniform Transforms { 35 | Transform u_Transform; 36 | }; 37 | 38 | uniform sampler2D u_DmapSampler; // displacement map 39 | uniform sampler2D u_SmapSampler; // slope map 40 | uniform float u_DmapFactor; 41 | uniform float u_LodFactor; 42 | 43 | 44 | vec2 intValToColor2(int keyLod) { 45 | keyLod = keyLod % 64; 46 | 47 | int bx = (keyLod & 0x1) | ((keyLod >> 1) & 0x2) | ((keyLod >> 2) & 0x4); 48 | int by = ((keyLod >> 1) & 0x1) | ((keyLod >> 2) & 0x2) | ((keyLod >> 3) & 0x4); 49 | 50 | return vec2(float(bx) / 7.0f, float(by) / 7.0f); 51 | } 52 | 53 | // displacement map 54 | float dmap(vec2 pos) 55 | { 56 | return (texture(u_DmapSampler, pos * 0.5 + 0.5).x) * u_DmapFactor; 57 | } 58 | 59 | float distanceToLod(float z, float lodFactor) 60 | { 61 | // Note that we multiply the result by two because the triangle's 62 | // edge lengths decreases by half every two subdivision steps. 63 | return -2.0 * log2(clamp(z * lodFactor, 0.0f, 1.0f)); 64 | } 65 | 66 | 67 | float computeLod(vec3 c) 68 | { 69 | #if FLAG_DISPLACE 70 | c.z += dmap(u_Transform.viewInv[3].xy); 71 | #endif 72 | 73 | vec3 cxf = (u_Transform.modelView * vec4(c, 1)).xyz; 74 | float z = length(cxf); 75 | 76 | return distanceToLod(z, u_LodFactor); 77 | } 78 | 79 | float computeLod(in vec4 v[3]) 80 | { 81 | vec3 c = (v[1].xyz + v[2].xyz) / 2.0; 82 | return computeLod(c); 83 | } 84 | float computeLod(in vec3 v[3]) 85 | { 86 | vec3 c = (v[1].xyz + v[2].xyz) / 2.0; 87 | return computeLod(c); 88 | } 89 | 90 | void writeKey(uint primID, uint key) 91 | { 92 | uint idx = atomicCounterIncrement(u_SubdBufferCounter); 93 | 94 | u_SubdBufferOut[idx] = uvec2(primID, key); 95 | } 96 | 97 | 98 | void updateSubdBuffer( 99 | uint primID, 100 | uint key, 101 | int targetLod, 102 | int parentLod, 103 | bool isVisible 104 | ) { 105 | // extract subdivision level associated to the key 106 | int keyLod = findMSB(key); 107 | 108 | // update the key accordingly 109 | if (/* subdivide ? */ keyLod < targetLod && !isLeafKey(key) && isVisible) { 110 | uint children[2]; childrenKeys(key, children); 111 | 112 | writeKey(primID, children[0]); 113 | writeKey(primID, children[1]); 114 | } 115 | else if (/* keep ? */ keyLod < (parentLod + 1) && isVisible) { 116 | writeKey(primID, key); 117 | } 118 | else /* merge ? */ { 119 | 120 | if (/* is root ? */isRootKey(key)) 121 | { 122 | writeKey(primID, key); 123 | } 124 | #if 1 125 | else if (/* is zero child ? */isChildZeroKey(key)) { 126 | writeKey(primID, parentKey(key)); 127 | } 128 | #else 129 | //Experiments to fix missing triangles when merging 130 | else { 131 | int numMergeLevels = keyLod - (parentLod); 132 | 133 | uint mergeMask = (key & ((1 << numMergeLevels) - 1)); 134 | if (mergeMask == 0) 135 | { 136 | key = (key >> numMergeLevels); 137 | writeKey(primID, key); 138 | } 139 | 140 | } 141 | #endif 142 | } 143 | } 144 | 145 | void updateSubdBuffer(uint primID, uint key, int targetLod, int parentLod) 146 | { 147 | updateSubdBuffer(primID, key, targetLod, parentLod, true); 148 | } 149 | 150 | vec4 shadeFragment(vec2 texCoord) 151 | { 152 | #if SHADING_LOD 153 | return vec4(texCoord, 0, 1); 154 | 155 | #elif SHADING_DIFFUSE 156 | vec2 s = texture(u_SmapSampler, texCoord).rg * u_DmapFactor; 157 | vec3 n = normalize(vec3(-s, 1)); 158 | float d = clamp(n.z, 0.0, 1.0); 159 | 160 | return vec4(vec3(d / 3.14159), 1); 161 | 162 | #elif SHADING_NORMALS 163 | vec2 s = texture(u_SmapSampler, texCoord).rg * u_DmapFactor; 164 | vec3 n = normalize(vec3(-s, 1)); 165 | 166 | return vec4(abs(n), 1); 167 | 168 | #else 169 | return vec4(1, 0, 0, 1); 170 | #endif 171 | } 172 | 173 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_cs_lod.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_cs_lod.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | 4 | This code has dependencies on the following GLSL sources: 5 | - fcull.glsl 6 | - isubd.glsl 7 | - terrain_common.glsl 8 | */ 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // Implicit Subdivision Shader for Terrain Rendering 12 | // 13 | 14 | layout (std430, binding = BUFFER_BINDING_SUBD1) 15 | readonly buffer SubdBufferIn { 16 | uvec2 u_SubdBufferIn[]; 17 | }; 18 | 19 | layout (std430, binding = BUFFER_BINDING_CULLED_SUBD) 20 | buffer CulledSubdBuffer { 21 | uvec2 u_CulledSubdBuffer[]; 22 | }; 23 | 24 | layout(std430, binding = BUFFER_BINDING_INDIRECT_COMMAND) 25 | buffer IndirectCommandBuffer { 26 | uint u_IndirectCommand[8]; 27 | }; 28 | 29 | //layout (binding = BUFFER_BINDING_SUBD_COUNTER, offset = 4) 30 | layout(binding = BUFFER_BINDING_CULLED_SUBD_COUNTER) 31 | uniform atomic_uint u_CulledSubdBufferCounter; 32 | 33 | 34 | 35 | // ----------------------------------------------------------------------------- 36 | /** 37 | * Compute LoD Shader 38 | * 39 | * This compute shader is responsible for updating the subdivision 40 | * buffer and visible buffer that will be sent to the rasterizer. 41 | */ 42 | #ifdef COMPUTE_SHADER 43 | layout (local_size_x = COMPUTE_THREAD_COUNT, 44 | local_size_y = 1, 45 | local_size_z = 1) in; 46 | 47 | 48 | void main() 49 | { 50 | // get threadID (each key is associated to a thread) 51 | uint threadID = gl_GlobalInvocationID.x; 52 | 53 | // early abort if the threadID exceeds the size of the subdivision buffer 54 | //if (threadID >= atomicCounter(u_PreviousSubdBufferCounter)) 55 | if (threadID >= u_IndirectCommand[7]) 56 | return; 57 | 58 | // get coarse triangle associated to the key 59 | uint primID = u_SubdBufferIn[threadID].x; 60 | vec4 v_in[3] = vec4[3]( 61 | u_VertexBuffer[u_IndexBuffer[primID * 3]], 62 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]], 63 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]] 64 | ); 65 | 66 | // compute distance-based LOD 67 | uint key = u_SubdBufferIn[threadID].y; 68 | vec4 v[3], vp[3]; subd(key, v_in, v, vp); 69 | int targetLod = int(computeLod(v)); 70 | int parentLod = int(computeLod(vp)); 71 | #if FLAG_FREEZE 72 | targetLod = parentLod = findMSB(key); 73 | #endif 74 | updateSubdBuffer(primID, key, targetLod, parentLod); 75 | 76 | #if FLAG_CULL 77 | // Cull invisible nodes 78 | mat4 mvp = u_Transform.modelViewProjection; 79 | vec4 bmin = min(min(v[0], v[1]), v[2]); 80 | vec4 bmax = max(max(v[0], v[1]), v[2]); 81 | 82 | // account for displacement in bound computations 83 | # if FLAG_DISPLACE 84 | bmin.z = 0; 85 | bmax.z = u_DmapFactor; 86 | # endif 87 | 88 | // update CulledSubdBuffer 89 | if (/* is visible ? */frustumCullingTest(mvp, bmin.xyz, bmax.xyz)) { 90 | #else 91 | if (true) { 92 | #endif // FLAG_CULL 93 | // write key 94 | //uint idx = atomicCounterIncrement(u_CulledSubdBufferCounter[1]); 95 | uint idx = atomicCounterIncrement(u_CulledSubdBufferCounter); 96 | 97 | u_CulledSubdBuffer[idx] = uvec2(primID, key); 98 | } 99 | } 100 | #endif 101 | 102 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_cs_render.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_cs_render.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | 4 | This code has dependencies on the following GLSL sources: 5 | - isubd.glsl 6 | - terrain_common.glsl 7 | */ 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | // Implicit Subdivition Shader for Terrain Rendering 11 | // 12 | 13 | layout (std430, binding = BUFFER_BINDING_CULLED_SUBD) 14 | buffer CulledSubdBuffer { 15 | uvec2 u_CulledSubdBuffer[]; 16 | }; 17 | 18 | 19 | // ----------------------------------------------------------------------------- 20 | /** 21 | * Compute LoD Shader 22 | * 23 | * This compute shader is responsible for updating the subdivision 24 | * buffer and visible buffer that will be sent to the rasterizer. 25 | */ 26 | #ifdef VERTEX_SHADER 27 | layout(location = 0) in vec2 i_TessCoord; 28 | layout(location = 0) out vec2 o_TexCoord; 29 | 30 | void main() 31 | { 32 | // get threadID (each key is associated to a thread) 33 | int threadID = gl_InstanceID; 34 | 35 | // get coarse triangle associated to the key 36 | uint primID = u_CulledSubdBuffer[threadID].x; 37 | vec4 v_in[3] = vec4[3]( 38 | u_VertexBuffer[u_IndexBuffer[primID * 3 ]], 39 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]], 40 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]] 41 | ); 42 | 43 | // compute sub-triangle associated to the key 44 | uint key = u_CulledSubdBuffer[threadID].y; 45 | vec4 v[3]; subd(key, v_in, v); 46 | 47 | // compute vertex location 48 | vec4 finalVertex = berp(v, i_TessCoord); 49 | #if FLAG_DISPLACE 50 | finalVertex.z+= dmap(finalVertex.xy); 51 | #endif 52 | 53 | #if SHADING_LOD 54 | //o_TexCoord = i_TessCoord.xy; 55 | int keyLod = findMSB(key); 56 | o_TexCoord = intValToColor2(keyLod); 57 | #else 58 | o_TexCoord = finalVertex.xy * 0.5 + 0.5; 59 | #endif 60 | 61 | gl_Position = u_Transform.modelViewProjection * finalVertex; 62 | } 63 | #endif 64 | 65 | // ----------------------------------------------------------------------------- 66 | /** 67 | * Fragment Shader 68 | * 69 | * This fragment shader is responsible for shading the final geometry. 70 | */ 71 | #ifdef FRAGMENT_SHADER 72 | layout(location = 0) in vec2 i_TexCoord; 73 | layout(location = 0) out vec4 o_FragColor; 74 | 75 | void main() 76 | { 77 | o_FragColor = shadeFragment(i_TexCoord); 78 | } 79 | #endif 80 | 81 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_gs.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_gs.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | 4 | This code has dependencies on the following GLSL sources: 5 | - fcull.glsl 6 | - isubd.glsl 7 | - terrain_common.glsl 8 | */ 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // Implicit Subdivition Shader for Terrain Rendering (using a geometry shader) 12 | // 13 | 14 | layout (std430, binding = BUFFER_BINDING_SUBD1) 15 | readonly buffer SubdBufferIn { 16 | uvec2 u_SubdBufferIn[]; 17 | }; 18 | 19 | 20 | // ----------------------------------------------------------------------------- 21 | /** 22 | * Vertex Shader 23 | * 24 | * The vertex shader is empty 25 | */ 26 | #ifdef VERTEX_SHADER 27 | void main(void) 28 | { } 29 | #endif 30 | 31 | // ----------------------------------------------------------------------------- 32 | /** 33 | * Geometry Shader 34 | * 35 | * This geometry shader is responsible for updating the 36 | * subdivision buffer and sending visible geometry to the rasterizer. 37 | */ 38 | #ifdef GEOMETRY_SHADER 39 | layout(points) in; 40 | layout(triangle_strip, max_vertices = MAX_VERTICES) out; 41 | layout(location = 0) out vec2 o_TexCoord; 42 | 43 | void genVertex(in vec4 v[3], vec2 tessCoord, vec2 lodColor) 44 | { 45 | vec4 finalVertex = berp(v, tessCoord); 46 | 47 | #if FLAG_DISPLACE 48 | finalVertex.z+= dmap(finalVertex.xy); 49 | #endif 50 | 51 | #if SHADING_LOD 52 | o_TexCoord = lodColor; 53 | #else 54 | o_TexCoord = finalVertex.xy * 0.5 + 0.5; 55 | #endif 56 | gl_Position = u_Transform.modelViewProjection * finalVertex; 57 | EmitVertex(); 58 | } 59 | 60 | void main() 61 | { 62 | // get threadID (each key is associated to a thread) 63 | int threadID = gl_PrimitiveIDIn; 64 | 65 | // get coarse triangle associated to the key 66 | uint primID = u_SubdBufferIn[threadID].x; 67 | vec4 v_in[3] = vec4[3]( 68 | u_VertexBuffer[u_IndexBuffer[primID * 3 ]], 69 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]], 70 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]] 71 | ); 72 | 73 | // compute distance-based LOD 74 | uint key = u_SubdBufferIn[threadID].y; 75 | vec4 v[3], vp[3]; subd(key, v_in, v, vp); 76 | int targetLod = int(computeLod(v)); 77 | int parentLod = int(computeLod(vp)); 78 | #if FLAG_FREEZE 79 | targetLod = parentLod = findMSB(key); 80 | #endif 81 | updateSubdBuffer(primID, key, targetLod, parentLod); 82 | 83 | #if FLAG_CULL 84 | // Cull invisible nodes 85 | mat4 mvp = u_Transform.modelViewProjection; 86 | vec4 bmin = min(min(v[0], v[1]), v[2]); 87 | vec4 bmax = max(max(v[0], v[1]), v[2]); 88 | 89 | // account for displacement in bound computations 90 | # if FLAG_DISPLACE 91 | bmin.z = 0; 92 | bmax.z = u_DmapFactor; 93 | # endif 94 | 95 | if (/* is visible ? */frustumCullingTest(mvp, bmin.xyz, bmax.xyz)) { 96 | #else 97 | if (true) { 98 | #endif // FLAG_CULL 99 | 100 | int keyLod = findMSB(key); 101 | vec2 lodColor = intValToColor2(keyLod); 102 | 103 | /* 104 | The code below generates a tessellated triangle with a single triangle strip. 105 | The algorithm instances strips of 4 vertices, which produces 2 triangles. 106 | This is why there is a special case for subd_level == 0, where we expect 107 | only one triangle. 108 | */ 109 | #if PATCH_SUBD_LEVEL == 0 110 | genVertex(v, vec2(0, 0), lodColor); 111 | genVertex(v, vec2(1, 0), lodColor); 112 | genVertex(v, vec2(0, 1), lodColor); 113 | EndPrimitive(); 114 | #else 115 | int subdLevel = 2 * PATCH_SUBD_LEVEL - 1; 116 | int stripCnt = 1 << subdLevel; 117 | 118 | for (int i = 0; i < stripCnt; ++i) { 119 | uint key = i + stripCnt; 120 | vec4 vs[3]; subd(key, v, vs); 121 | 122 | genVertex(vs, vec2(0.0f, 1.0f), lodColor); 123 | genVertex(vs, vec2(0.0f, 0.0f), lodColor); 124 | genVertex(vs, vec2(0.5f, 0.5f), lodColor); 125 | genVertex(vs, vec2(1.0f, 0.0f), lodColor); 126 | } 127 | EndPrimitive(); 128 | #endif 129 | } 130 | 131 | } 132 | #endif 133 | 134 | // ----------------------------------------------------------------------------- 135 | /** 136 | * Fragment Shader 137 | * 138 | * This fragment shader is responsible for shading the final geometry. 139 | */ 140 | #ifdef FRAGMENT_SHADER 141 | layout(location = 0) in vec2 i_TexCoord; 142 | layout(location = 0) out vec4 o_FragColor; 143 | 144 | void main() 145 | { 146 | o_FragColor = shadeFragment(i_TexCoord); 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_ms.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_cs_lod.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | 4 | This code has dependencies on the following GLSL sources: 5 | - fcull.glsl 6 | - isubd.glsl 7 | - terrain_common.glsl 8 | */ 9 | 10 | #define USE_OPTIMIZED_TASK_PARAMETER_BLOCK 1 11 | #define USE_SUBD_KEYS_CULLING 0 //Experimental 12 | 13 | #define NUM_CLIPPING_PLANES 6 14 | 15 | #define MeshPatchAttributes() vec4 vertices[3]; uint key; 16 | 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // Implicit Subdivition Sahder for Terrain Rendering 20 | // 21 | 22 | layout(std430, binding = BUFFER_BINDING_SUBD1) 23 | readonly buffer SubdBufferIn { 24 | uvec2 u_SubdBufferIn[]; 25 | }; 26 | 27 | layout(std430, binding = BUFFER_BINDING_INSTANCED_GEOMETRY_VERTICES) 28 | readonly buffer VertexBufferInstanced { 29 | vec2 u_VertexBufferInstanced[]; 30 | }; 31 | 32 | layout(std430, binding = BUFFER_BINDING_INSTANCED_GEOMETRY_INDEXES) 33 | readonly buffer IndexBufferInstanced { 34 | uint16_t u_IndexBufferInstanced[]; 35 | }; 36 | 37 | layout(std430, binding = BUFFER_BINDING_INDIRECT_COMMAND) 38 | buffer IndirectCommandBuffer { 39 | uint u_IndirectCommand[8]; 40 | }; 41 | 42 | 43 | // ----------------------------------------------------------------------------- 44 | /** 45 | * Task Shader 46 | * 47 | * This task shader is responsible for updating the 48 | * subdivision buffer and sending visible geometry to the mesh shader. 49 | */ 50 | #ifdef TASK_SHADER 51 | layout(local_size_x = COMPUTE_THREAD_COUNT) in; 52 | 53 | #if USE_OPTIMIZED_TASK_PARAMETER_BLOCK == 0 54 | taskNV out Patch{ 55 | MeshPatchAttributes() 56 | } o_Patch[COMPUTE_THREAD_COUNT]; 57 | #else 58 | taskNV out Patch{ 59 | vec4 vertices[3 * COMPUTE_THREAD_COUNT]; 60 | } o_Patch; 61 | #endif 62 | 63 | 64 | 65 | void main() 66 | { 67 | 68 | // get threadID (each key is associated to a thread) 69 | uint threadID = gl_GlobalInvocationID.x; 70 | 71 | bool isVisible = true; 72 | 73 | uint key; vec4 v[3]; 74 | 75 | // early abort if the threadID exceeds the size of the subdivision buffer 76 | if (threadID >= u_IndirectCommand[7]) { //Num triangles is stored in the last reserved field of the draw indiretc structure 77 | 78 | isVisible = false; 79 | 80 | } else { 81 | 82 | // get coarse triangle associated to the key 83 | uint primID = u_SubdBufferIn[threadID].x; 84 | vec4 v_in[3] = vec4[3]( 85 | u_VertexBuffer[u_IndexBuffer[primID * 3 ]], 86 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]], 87 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]] 88 | ); 89 | 90 | // compute distance-based LOD 91 | key = u_SubdBufferIn[threadID].y; 92 | vec4 vp[3]; subd(key, v_in, v, vp); 93 | int targetLod = int(computeLod(v)); 94 | int parentLod = int(computeLod(vp)); 95 | #if FLAG_FREEZE 96 | targetLod = parentLod = findMSB(key); 97 | #endif 98 | 99 | 100 | 101 | #if FLAG_CULL 102 | # if USE_SUBD_KEYS_CULLING==0 103 | # define CULLING_USED_TRIANGLE v 104 | # else 105 | # define CULLING_USED_TRIANGLE vp 106 | # endif 107 | 108 | // Cull invisible nodes 109 | mat4 mvp = u_Transform.modelViewProjection; 110 | vec4 bmin = min(min(CULLING_USED_TRIANGLE[0], CULLING_USED_TRIANGLE[1]), CULLING_USED_TRIANGLE[2]); 111 | vec4 bmax = max(max(CULLING_USED_TRIANGLE[0], CULLING_USED_TRIANGLE[1]), CULLING_USED_TRIANGLE[2]); 112 | 113 | // account for displacement in bound computations 114 | # if FLAG_DISPLACE 115 | bmin.z = 0; 116 | bmax.z = u_DmapFactor; 117 | # endif 118 | # if USE_SUBD_KEYS_CULLING==0 || FLAG_FREEZE == 0 119 | isVisible = frustumCullingTest(mvp, bmin.xyz, bmax.xyz); 120 | # endif 121 | #endif // FLAG_CULL 122 | 123 | 124 | updateSubdBuffer(primID, key, targetLod, parentLod 125 | #if USE_SUBD_KEYS_CULLING 126 | , isVisible 127 | #endif 128 | ); 129 | } 130 | 131 | 132 | uint laneID = gl_LocalInvocationID.x; 133 | uint voteVisible = ballotThreadNV(isVisible); 134 | uint numTasks = bitCount(voteVisible); 135 | 136 | if (laneID == 0) { 137 | gl_TaskCountNV = numTasks; 138 | } 139 | 140 | 141 | if (isVisible) { 142 | uint idxOffset = bitCount(voteVisible & gl_ThreadLtMaskNV); 143 | 144 | // set output data 145 | #if USE_OPTIMIZED_TASK_PARAMETER_BLOCK == 0 146 | o_Patch[idxOffset].vertices = vec4[3](vec4(v[0], 1.0), vec4(v[1], 1.0), vec4(v[2], 1.0)); 147 | o_Patch[idxOffset].key = key; 148 | #else 149 | o_Patch.vertices[idxOffset * 3 + 0] = vec4(v[0].xyz, v[1].x); 150 | o_Patch.vertices[idxOffset * 3 + 1] = vec4(v[1].yz, v[2].xy); 151 | o_Patch.vertices[idxOffset * 3 + 2] = vec4(v[2].z, uintBitsToFloat(key), 0.0, 0.0); 152 | #endif 153 | 154 | } 155 | 156 | } 157 | #endif 158 | 159 | // ----------------------------------------------------------------------------- 160 | /** 161 | * Mesh Shader 162 | * 163 | * This mesh shader is responsible for placing the 164 | * geometry properly on the input mesh (here a terrain). 165 | */ 166 | #ifdef MESH_SHADER 167 | 168 | const int gpuSubd = PATCH_SUBD_LEVEL; 169 | 170 | layout(local_size_x = COMPUTE_THREAD_COUNT) in; 171 | layout(max_vertices = INSTANCED_MESH_VERTEX_COUNT, max_primitives = INSTANCED_MESH_PRIMITIVE_COUNT) out; 172 | layout(triangles) out; 173 | 174 | #if USE_OPTIMIZED_TASK_PARAMETER_BLOCK == 0 175 | taskNV in Patch{ 176 | MeshPatchAttributes() 177 | } i_Patch[COMPUTE_THREAD_COUNT]; 178 | #else 179 | taskNV in Patch{ 180 | vec4 vertices[3 * COMPUTE_THREAD_COUNT]; 181 | } i_Patch; 182 | #endif 183 | 184 | 185 | layout(location = 0) out Interpolants{ 186 | vec2 o_TexCoord; 187 | } OUT[]; 188 | 189 | void main() 190 | { 191 | 192 | int id = int(gl_WorkGroupID.x); 193 | uint laneID = gl_LocalInvocationID.x; 194 | 195 | 196 | //Multi-threads, *load* instanced geom 197 | #if USE_OPTIMIZED_TASK_PARAMETER_BLOCK == 0 198 | vec3 v[3] = vec3[3]( 199 | i_Patch[id].vertices[0].xyz, 200 | i_Patch[id].vertices[1].xyz, 201 | i_Patch[id].vertices[2].xyz 202 | ); 203 | 204 | uint key = i_Patch[id].key; 205 | #else 206 | vec3 v[3] = vec3[3]( 207 | i_Patch.vertices[id * 3 + 0].xyz, 208 | vec3(i_Patch.vertices[id * 3 + 0].w, i_Patch.vertices[id * 3 + 1].xy), 209 | vec3(i_Patch.vertices[id * 3 + 1].zw, i_Patch.vertices[id * 3 + 2].x) 210 | ); 211 | 212 | uint key = floatBitsToUint(i_Patch.vertices[id * 3 + 2].y); 213 | #endif 214 | 215 | 216 | const int vertexCnt = INSTANCED_MESH_VERTEX_COUNT; 217 | const int triangleCnt = INSTANCED_MESH_PRIMITIVE_COUNT; 218 | const int indexCnt = triangleCnt * 3; 219 | 220 | gl_PrimitiveCountNV = triangleCnt; 221 | 222 | 223 | const int numLoop = (vertexCnt + COMPUTE_THREAD_COUNT-1) / COMPUTE_THREAD_COUNT; 224 | for (int l = 0; l < numLoop; ++l) { 225 | int curVert = int(laneID) + l * COMPUTE_THREAD_COUNT; 226 | 227 | curVert = min(curVert, vertexCnt - 1); 228 | { 229 | 230 | vec2 instancedBaryCoords = u_VertexBufferInstanced[curVert]; 231 | 232 | vec3 finalVertex = berp(v, instancedBaryCoords); 233 | 234 | 235 | 236 | #if FLAG_DISPLACE 237 | finalVertex.z += dmap(finalVertex.xy); 238 | #endif 239 | #if SHADING_LOD 240 | //vec2 tessCoord = instancedBaryCoords; 241 | int keyLod = findMSB(key); 242 | 243 | vec2 tessCoord = intValToColor2(keyLod); 244 | #else 245 | vec2 tessCoord = finalVertex.xy * 0.5 + 0.5; 246 | #endif 247 | 248 | OUT[curVert].o_TexCoord = tessCoord; 249 | gl_MeshVerticesNV[curVert].gl_Position = u_Transform.modelViewProjection * vec4(finalVertex, 1.0); 250 | for (int d = 0; d < NUM_CLIPPING_PLANES; d++) { 251 | gl_MeshVerticesNV[curVert].gl_ClipDistance[d] = 1.0; 252 | } 253 | } 254 | 255 | } 256 | 257 | 258 | const int numLoopIdx = (indexCnt + COMPUTE_THREAD_COUNT -1) / COMPUTE_THREAD_COUNT; 259 | for (int l = 0; l < numLoopIdx; ++l) { 260 | int curIdx = int(laneID) + l * COMPUTE_THREAD_COUNT; 261 | 262 | curIdx = min(curIdx, indexCnt - 1); 263 | { 264 | uint indexVal = u_IndexBufferInstanced[curIdx]; 265 | 266 | gl_PrimitiveIndicesNV[curIdx] = indexVal; 267 | } 268 | 269 | } 270 | 271 | } 272 | #endif 273 | 274 | // ----------------------------------------------------------------------------- 275 | /** 276 | * Fragment Shader 277 | * 278 | * This fragment shader is responsible for shading the final geometry. 279 | */ 280 | #ifdef FRAGMENT_SHADER 281 | //layout(location = 0) in vec2 i_TexCoord; 282 | layout(location = 0) in Interpolants{ 283 | vec2 o_TexCoord; 284 | } IN; 285 | 286 | layout(location = 0) out vec4 o_FragColor; 287 | 288 | void main() 289 | { 290 | o_FragColor = shadeFragment(IN.o_TexCoord); 291 | } 292 | 293 | #endif 294 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_ts.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_cs_lod.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | 4 | This code has dependencies on the following GLSL sources: 5 | - fcull.glsl 6 | - isubd.glsl 7 | - terrain_common.glsl 8 | */ 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // Implicit Subdivition Shader for Terrain Rendering 12 | // 13 | 14 | layout (std430, binding = BUFFER_BINDING_SUBD1) 15 | readonly buffer SubdBufferIn { 16 | uvec2 u_SubdBufferIn[]; 17 | }; 18 | 19 | 20 | // ----------------------------------------------------------------------------- 21 | /** 22 | * Vertex Shader 23 | * 24 | * The vertex shader is empty 25 | */ 26 | #ifdef VERTEX_SHADER 27 | void main() 28 | { } 29 | #endif 30 | 31 | // ----------------------------------------------------------------------------- 32 | /** 33 | * Tessellation Control Shader 34 | * 35 | * This tessellaction control shader is responsible for updating the 36 | * subdivision buffer and sending visible geometry to the rasterizer. 37 | */ 38 | #ifdef TESS_CONTROL_SHADER 39 | layout (vertices = 1) out; 40 | out Patch { 41 | vec4 vertices[3]; 42 | flat uint key; 43 | } o_Patch[]; 44 | 45 | 46 | 47 | void main() 48 | { 49 | // get threadID (each key is associated to a thread) 50 | int threadID = gl_PrimitiveID; 51 | 52 | // get coarse triangle associated to the key 53 | uint primID = u_SubdBufferIn[threadID].x; 54 | vec4 v_in[3] = vec4[3]( 55 | u_VertexBuffer[u_IndexBuffer[primID * 3 ]], 56 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 1]], 57 | u_VertexBuffer[u_IndexBuffer[primID * 3 + 2]] 58 | ); 59 | 60 | // compute distance-based LOD 61 | uint key = u_SubdBufferIn[threadID].y; 62 | vec4 v[3], vp[3]; subd(key, v_in, v, vp); 63 | int targetLod = int(computeLod(v)); 64 | int parentLod = int(computeLod(vp)); 65 | #if FLAG_FREEZE 66 | targetLod = parentLod = findMSB(key); 67 | #endif 68 | updateSubdBuffer(primID, key, targetLod, parentLod); 69 | 70 | #if FLAG_CULL 71 | // Cull invisible nodes 72 | mat4 mvp = u_Transform.modelViewProjection; 73 | vec4 bmin = min(min(v[0], v[1]), v[2]); 74 | vec4 bmax = max(max(v[0], v[1]), v[2]); 75 | 76 | // account for displacement in bound computations 77 | # if FLAG_DISPLACE 78 | bmin.z = 0; 79 | bmax.z = u_DmapFactor; 80 | # endif 81 | 82 | if (/* is visible ? */frustumCullingTest(mvp, bmin.xyz, bmax.xyz)) { 83 | #else 84 | if (true) { 85 | #endif // FLAG_CULL 86 | // set tess levels 87 | int tessLevel = PATCH_TESS_LEVEL; 88 | gl_TessLevelInner[0] = 89 | gl_TessLevelInner[1] = 90 | gl_TessLevelOuter[0] = 91 | gl_TessLevelOuter[1] = 92 | gl_TessLevelOuter[2] = tessLevel; 93 | 94 | // set output data 95 | o_Patch[gl_InvocationID].vertices = v; 96 | o_Patch[gl_InvocationID].key = key; 97 | } else /* is not visible ? */ { 98 | // cull the geometry 99 | gl_TessLevelInner[0] = 100 | gl_TessLevelInner[1] = 101 | gl_TessLevelOuter[0] = 102 | gl_TessLevelOuter[1] = 103 | gl_TessLevelOuter[2] = 0; 104 | } 105 | } 106 | #endif 107 | 108 | // ----------------------------------------------------------------------------- 109 | /** 110 | * Tessellation Evaluation Shader 111 | * 112 | * This tessellaction evaluation shader is responsible for placing the 113 | * geometry properly on the input mesh (here a terrain). 114 | */ 115 | #ifdef TESS_EVALUATION_SHADER 116 | layout (triangles, ccw, equal_spacing) in; 117 | in Patch { 118 | vec4 vertices[3]; 119 | flat uint key; 120 | } i_Patch[]; 121 | 122 | layout(location = 0) out vec2 o_TexCoord; 123 | 124 | void main() 125 | { 126 | vec4 v[3] = i_Patch[0].vertices; 127 | vec4 finalVertex = berp(v, gl_TessCoord.xy); 128 | 129 | #if FLAG_DISPLACE 130 | finalVertex.z+= dmap(finalVertex.xy); 131 | #endif 132 | 133 | #if SHADING_LOD 134 | //o_TexCoord = gl_TessCoord.xy; 135 | int keyLod = findMSB(i_Patch[0].key); 136 | vec2 lodColor = intValToColor2(keyLod); 137 | o_TexCoord = lodColor; 138 | #else 139 | o_TexCoord = finalVertex.xy * 0.5 + 0.5; 140 | #endif 141 | 142 | gl_Position = u_Transform.modelViewProjection * finalVertex; 143 | } 144 | #endif 145 | 146 | // ----------------------------------------------------------------------------- 147 | /** 148 | * Fragment Shader 149 | * 150 | * This fragment shader is responsible for shading the final geometry. 151 | */ 152 | #ifdef FRAGMENT_SHADER 153 | layout(location = 0) in vec2 i_TexCoord; 154 | layout(location = 0) out vec4 o_FragColor; 155 | 156 | void main() 157 | { 158 | o_FragColor = shadeFragment(i_TexCoord); 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/terrain_updateIndirect_cs.glsl: -------------------------------------------------------------------------------- 1 | /* terrain_updateIndirect_cs.glsl - public domain 2 | (created by Jonathan Dupuy and Cyril Crassin) 3 | 4 | */ 5 | 6 | #ifdef COMPUTE_SHADER 7 | layout(binding = BINDING_ATOMIC_COUNTER) 8 | uniform atomic_uint u_Counter; 9 | 10 | //Just for reseting 11 | layout(binding = BINDING_ATOMIC_COUNTER2) 12 | uniform atomic_uint u_Counter2; 13 | 14 | 15 | layout(std430, binding = BUFFER_BINDING_INDIRECT_COMMAND) //BUFFER_DISPATCH_INDIRECT 16 | buffer IndirectCommandBuffer { 17 | uint u_IndirectCommand[8]; 18 | }; 19 | 20 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 21 | 22 | /** 23 | * This function is implemented to intel, AMD, and NVidia GPUs. 24 | */ 25 | uint atomicCounterExchangeImpl(atomic_uint c, uint data) 26 | { 27 | #if ATOMIC_COUNTER_EXCHANGE_ARB 28 | return atomicCounterExchangeARB(c, data); 29 | #elif ATOMIC_COUNTER_EXCHANGE_AMD 30 | return atomicCounterExchange(c, data); 31 | #else 32 | #error please configure atomicCounterExchange for your platform 33 | #endif 34 | } 35 | 36 | void main() 37 | { 38 | 39 | #if UPDATE_INDIRECT_STRUCT 40 | uint cnt = atomicCounter(u_Counter) / UPDATE_INDIRECT_VALUE_DIVIDE + UPDATE_INDIRECT_VALUE_ADD; 41 | 42 | u_IndirectCommand[UPDATE_INDIRECT_OFFSET] = cnt; 43 | 44 | //Hack: Store counter value in the last reserved field of the draw/dispatch indirect structure 45 | u_IndirectCommand[7] = atomicCounter(u_Counter); 46 | #endif 47 | 48 | 49 | //Reset atomic counters 50 | #if UPDATE_INDIRECT_RESET_COUNTER1 51 | atomicCounterExchangeImpl(u_Counter, 0); 52 | #endif 53 | #if UPDATE_INDIRECT_RESET_COUNTER2 54 | atomicCounterExchangeImpl(u_Counter2, 0); 55 | #endif 56 | 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /demo-isubd-terrain/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | /* viewer.glsl - public domain 2 | (created by Jonathan Dupuy) 3 | */ 4 | 5 | uniform float u_Exposure; 6 | uniform float u_Gamma; 7 | 8 | #if MSAA_FACTOR 9 | uniform sampler2DMS u_FramebufferSampler; 10 | #else 11 | uniform sampler2D u_FramebufferSampler; 12 | #endif 13 | 14 | // ------------------------------------------------------------------------------------------------- 15 | /** 16 | * Vertex Shader 17 | * 18 | * This vertex shader draws a fullscreen quad 19 | */ 20 | #ifdef VERTEX_SHADER 21 | layout(location = 0) out vec2 o_TexCoord; 22 | 23 | void main(void) 24 | { 25 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 26 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 27 | } 28 | #endif 29 | 30 | // ------------------------------------------------------------------------------------------------- 31 | /** 32 | * Fragment Shader 33 | * 34 | * This fragment shader post-processes the scene framebuffer by applying 35 | * tone mapping, gamma correction and image scaling. 36 | */ 37 | #ifdef FRAGMENT_SHADER 38 | layout(location = 0) in vec2 i_TexCoord; 39 | layout(location = 0) out vec4 o_FragColor; 40 | 41 | void main(void) 42 | { 43 | vec4 color = vec4(0); 44 | ivec2 P = ivec2(gl_FragCoord.xy); 45 | 46 | #if MSAA_FACTOR 47 | for (int i = 0; i < MSAA_FACTOR; ++i) { 48 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 49 | 50 | color+= vec4(c.a * c.rgb, c.a); 51 | } 52 | #else 53 | color = texelFetch(u_FramebufferSampler, P, 0); 54 | #endif 55 | if (color.a > 0.0) color.rgb/= color.a; 56 | 57 | // make fragments store positive values 58 | if (any(lessThan(color.rgb, vec3(0)))) { 59 | o_FragColor = vec4(1, 0, 0, 1); 60 | return; 61 | } 62 | 63 | // exposure 64 | color.rgb*= exp2(u_Exposure); 65 | 66 | // gamma 67 | color.rgb = pow(color.rgb, vec3(1.0 / u_Gamma)); 68 | 69 | // final color 70 | o_FragColor = vec4(color.rgb, 1.0); 71 | 72 | // make sure the fragments store real values 73 | if (any(isnan(color.rgb))) 74 | o_FragColor = vec4(1, 0, 0, 1); 75 | 76 | //o_FragColor = vec4(i_TexCoord, 0, 1); 77 | } 78 | #endif 79 | 80 | -------------------------------------------------------------------------------- /demo-merl/README.md: -------------------------------------------------------------------------------- 1 | ## MERL BRDF Viewer 2 | 3 | This code renders a MERL BRDF with progressive Monte Carlo integration of an HDR environment map. 4 | 5 | 6 | ![alt text](preview.png "Preview") 7 | -------------------------------------------------------------------------------- /demo-merl/npf.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/demo-merl/npf.bin -------------------------------------------------------------------------------- /demo-merl/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/demo-merl/preview.png -------------------------------------------------------------------------------- /demo-merl/shaders/background.glsl: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // Uniforms 3 | // -------------------------------------------------- 4 | uniform vec3 u_ClearColor; 5 | uniform sampler2D u_EnvmapSampler; 6 | 7 | struct Transform { 8 | mat4 modelView; 9 | mat4 projection; 10 | mat4 modelViewProjection; 11 | mat4 viewInv; 12 | }; 13 | 14 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 15 | uniform Transforms { 16 | Transform u_Transform; 17 | }; 18 | 19 | vec3 evalEnvmap(vec3 dir) 20 | { 21 | float pi = 3.14159265359; 22 | float u1 = atan(dir.x, dir.y) / pi * 0.5 + 0.5; 23 | float u2 = 1.0 - acos(dir.z) / pi; 24 | return texture(u_EnvmapSampler, vec2(u1, u2)).rgb; 25 | } 26 | 27 | // -------------------------------------------------- 28 | // Vertex shader 29 | // -------------------------------------------------- 30 | #ifdef VERTEX_SHADER 31 | layout(location = 0) out vec3 o_TexCoord; 32 | void main() { 33 | // draw a full screen quad in modelview space 34 | vec2 p = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1) * 2.0 - 1.0; 35 | vec3 e = vec3(-900.0, p * 1e5); 36 | 37 | gl_Position = u_Transform.projection * vec4(e, 1); 38 | gl_Position.z = 0.99999 * gl_Position.w; // make sure the cubemap is visible 39 | o_TexCoord = vec3(u_Transform.viewInv * vec4(normalize(e), 0)); 40 | } 41 | #endif 42 | 43 | // -------------------------------------------------- 44 | // Fragment shader 45 | // -------------------------------------------------- 46 | #ifdef FRAGMENT_SHADER 47 | layout(location = 0) in vec3 i_TexCoord; 48 | layout(location = 0) out vec4 o_FragColor; 49 | 50 | void main() { 51 | vec3 dir = normalize(i_TexCoord); 52 | o_FragColor = vec4(evalEnvmap(dir), 1); 53 | } 54 | #endif 55 | 56 | 57 | -------------------------------------------------------------------------------- /demo-merl/shaders/brdf_merl.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Disney Enterprises, Inc. All rights reserved. 3 | 4 | This license governs use of the accompanying software. If you use the software, you 5 | accept this license. If you do not accept the license, do not use the software. 6 | 7 | 1. Definitions 8 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have 9 | the same meaning here as under U.S. copyright law. A "contribution" is the original 10 | software, or any additions or changes to the software. A "contributor" is any person 11 | that distributes its contribution under this license. "Licensed patents" are a 12 | contributor's patent claims that read directly on its contribution. 13 | 14 | 2. Grant of Rights 15 | (A) Copyright Grant- Subject to the terms of this license, including the license 16 | conditions and limitations in section 3, each contributor grants you a non-exclusive, 17 | worldwide, royalty-free copyright license to reproduce its contribution, prepare 18 | derivative works of its contribution, and distribute its contribution or any derivative 19 | works that you create. 20 | (B) Patent Grant- Subject to the terms of this license, including the license 21 | conditions and limitations in section 3, each contributor grants you a non-exclusive, 22 | worldwide, royalty-free license under its licensed patents to make, have made, 23 | use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the 24 | software or derivative works of the contribution in the software. 25 | 26 | 3. Conditions and Limitations 27 | (A) No Trademark License- This license does not grant you rights to use any 28 | contributors' name, logo, or trademarks. 29 | (B) If you bring a patent claim against any contributor over patents that you claim 30 | are infringed by the software, your patent license from such contributor to the 31 | software ends automatically. 32 | (C) If you distribute any portion of the software, you must retain all copyright, 33 | patent, trademark, and attribution notices that are present in the software. 34 | (D) If you distribute any portion of the software in source code form, you may do 35 | so only under this license by including a complete copy of this license with your 36 | distribution. If you distribute any portion of the software in compiled or object code 37 | form, you may only do so under a license that complies with this license. 38 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors 39 | give no express warranties, guarantees or conditions. You may have additional 40 | consumer rights under your local laws which this license cannot change. 41 | To the extent permitted under your local laws, the contributors exclude the 42 | implied warranties of merchantability, fitness for a particular purpose and non- 43 | infringement. 44 | */ 45 | 46 | uniform samplerBuffer u_MerlSampler; 47 | 48 | const int BRDF_SAMPLING_RES_THETA_H = 90; 49 | const int BRDF_SAMPLING_RES_THETA_D = 90; 50 | const int BRDF_SAMPLING_RES_PHI_D = 360; 51 | const float RED_SCALE = (1.0/1500.0); 52 | const float GREEN_SCALE = (1.15/1500.0); 53 | const float BLUE_SCALE = (1.66/1500.0); 54 | #ifndef M_PI 55 | #define M_PI 3.1415926535897932384626433832795 56 | #endif 57 | 58 | 59 | // Lookup phi_diff index 60 | int phi_diff_index(float phi_diff) 61 | { 62 | // Because of reciprocity, the BRDF is unchanged under 63 | // phi_diff -> phi_diff + M_PI 64 | if (phi_diff < 0.0) 65 | phi_diff += M_PI; 66 | 67 | // In: phi_diff in [0 .. pi] 68 | // Out: tmp in [0 .. 179] 69 | return clamp(int(phi_diff * (1.0/M_PI * (BRDF_SAMPLING_RES_PHI_D / 2))), 0, BRDF_SAMPLING_RES_PHI_D / 2 - 1); 70 | } 71 | 72 | 73 | // Lookup theta_half index 74 | // This is a non-linear mapping! 75 | // In: [0 .. pi/2] 76 | // Out: [0 .. 89] 77 | int theta_half_index(float theta_half) 78 | { 79 | if (theta_half <= 0.0) 80 | return 0; 81 | 82 | return clamp(int(sqrt(theta_half * (2.0/M_PI)) * BRDF_SAMPLING_RES_THETA_H), 0, BRDF_SAMPLING_RES_THETA_H-1); 83 | } 84 | 85 | 86 | // Lookup theta_diff index 87 | // In: [0 .. pi/2] 88 | // Out: [0 .. 89] 89 | int theta_diff_index(float theta_diff) 90 | { 91 | return clamp(int(theta_diff * (2.0/M_PI * BRDF_SAMPLING_RES_THETA_D)), 0, BRDF_SAMPLING_RES_THETA_D - 1); 92 | } 93 | 94 | 95 | vec3 BRDF( vec3 toLight, vec3 toViewer, vec3 normal, vec3 tangent, vec3 bitangent ) 96 | { 97 | vec3 H = normalize(toLight + toViewer); 98 | float theta_H = acos(clamp(dot(normal, H), 0, 1)); 99 | float theta_diff = acos(clamp(dot(H, toLight), 0, 1)); 100 | float phi_diff=0; 101 | 102 | if (dot(normal, toLight) <= 0.0) return vec3(0); 103 | if (dot(normal, toViewer) <= 0.0) return vec3(0); 104 | 105 | if (theta_diff < 1e-3) { 106 | // phi_diff indeterminate, use phi_half instead 107 | phi_diff = atan(clamp(-dot(toLight, bitangent), -1, 1), clamp(dot(toLight, tangent), -1, 1)); 108 | } 109 | else if (theta_H > 1e-3) { 110 | // use Gram-Schmidt orthonormalization to find diff basis vectors 111 | vec3 u = -normalize(normal - dot(normal,H) * H); 112 | vec3 v = cross(H, u); 113 | phi_diff = atan(clamp(dot(toLight,v), -1, 1), clamp(dot(toLight,u), -1, 1)); 114 | } 115 | else theta_H = 0; 116 | 117 | // Find index. 118 | // Note that phi_half is ignored, since isotropic BRDFs are assumed 119 | int ind = phi_diff_index(phi_diff) + 120 | theta_diff_index(theta_diff) * BRDF_SAMPLING_RES_PHI_D / 2 + 121 | theta_half_index(theta_H) * BRDF_SAMPLING_RES_PHI_D / 2 * 122 | BRDF_SAMPLING_RES_THETA_D; 123 | 124 | int redIndex = ind; 125 | int greenIndex = ind + BRDF_SAMPLING_RES_THETA_H*BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D/2; 126 | int blueIndex = ind + BRDF_SAMPLING_RES_THETA_H*BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D; 127 | 128 | #if 1 129 | return vec3( 130 | texelFetch(u_MerlSampler, redIndex).r * RED_SCALE, 131 | texelFetch(u_MerlSampler, greenIndex).r * GREEN_SCALE, 132 | texelFetch(u_MerlSampler, blueIndex).r * BLUE_SCALE 133 | ); 134 | #endif 135 | } 136 | 137 | #undef M_PI 138 | 139 | -------------------------------------------------------------------------------- /demo-merl/shaders/ggx.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | /* ggx.glsl - public domain GLSL library 3 | by Jonathan Dupuy 4 | 5 | This file provides utility functions for GGX BRDFs, which are used 6 | in the sphere light shading technique described in my paper 7 | "A Spherical Cap Preserving Parameterization for Spherical Distributions". 8 | */ 9 | 10 | // Evaluate GGX BRDF (brdf times cosine) 11 | float ggx_evalp(vec3 wi, vec3 wo, float alpha, out float pdf); 12 | 13 | // Importance sample a visible microfacet normal from direction wi 14 | // Note: the algorithm I use is an improvement over Eric Heit'z 15 | // algorithm. It relies on Shirley's concentric mapping and produces less 16 | // distortion. 17 | vec3 ggx_sample(vec2 u, vec3 wi, float alpha); 18 | 19 | // 20 | // 21 | //// end header file /////////////////////////////////////////////////////////// 22 | 23 | // ***************************************************************************** 24 | /** 25 | * GGX Functions 26 | * 27 | */ 28 | 29 | #define PI 3.141592654 30 | 31 | // ----------------------------------------------------------------------------- 32 | // Evaluation 33 | float ggx_evalp(vec3 wi, vec3 wo, float alpha, out float pdf) 34 | { 35 | if (wo.z > 0.0 && wi.z > 0.0) { 36 | vec3 wh = normalize(wi + wo); 37 | vec3 wh_xform = vec3(wh.xy / alpha, wh.z); 38 | vec3 wi_xform = vec3(wi.xy * alpha, wi.z); 39 | vec3 wo_xform = vec3(wo.xy * alpha, wo.z); 40 | float wh_xform_mag = length(wh_xform); 41 | float wi_xform_mag = length(wi_xform); 42 | float wo_xform_mag = length(wo_xform); 43 | wh_xform/= wh_xform_mag; // normalize 44 | wi_xform/= wi_xform_mag; // normalize 45 | wo_xform/= wo_xform_mag; // normalize 46 | float sigma_i = 0.5 + 0.5 * wi_xform.z; 47 | float sigma_o = 0.5 + 0.5 * wo_xform.z; 48 | float Gi = clamp(wi.z, 0.0, 1.0) / (sigma_i * wi_xform_mag); 49 | float Go = clamp(wo.z, 0.0, 1.0) / (sigma_o * wo_xform_mag); 50 | float J = alpha * alpha * wh_xform_mag * wh_xform_mag * wh_xform_mag; 51 | float Dvis = clamp(dot(wo_xform, wh_xform), 0.0, 1.0) / (sigma_o * PI * J); 52 | float Gcond = Gi / (Gi + Go - Gi * Go); 53 | float cos_theta_d = dot(wh, wo); 54 | 55 | pdf = (Dvis / (cos_theta_d * 4.0)); 56 | return pdf * Gcond; 57 | } 58 | pdf = 0.0; 59 | return 0.0; 60 | } 61 | 62 | // ----------------------------------------------------------------------------- 63 | // uniform to concentric disk 64 | vec2 ggx__u2_to_d2(vec2 u) 65 | { 66 | /* Concentric map code with less branching (by Dave Cline), see 67 | http://psgraphics.blogspot.ch/2011/01/improved-code-for-concentric-map.html */ 68 | float r1 = 2 * u.x - 1; 69 | float r2 = 2 * u.y - 1; 70 | float phi, r; 71 | 72 | if (r1 == 0 && r2 == 0) { 73 | r = phi = 0; 74 | } else if (r1 * r1 > r2 * r2) { 75 | r = r1; 76 | phi = (PI / 4) * (r2 / r1); 77 | } else { 78 | r = r2; 79 | phi = (PI / 2) - (r1 / r2) * (PI / 4); 80 | } 81 | 82 | return r * vec2(cos(phi), sin(phi)); 83 | } 84 | 85 | // ----------------------------------------------------------------------------- 86 | // uniform to half a concentric disk 87 | vec2 ggx__u2_to_hd2(vec2 u) 88 | { 89 | vec2 v = vec2((1 + u.x) / 2, u.y); 90 | return ggx__u2_to_d2(v); 91 | } 92 | 93 | // ----------------------------------------------------------------------------- 94 | // uniform to microfacet normal projected onto concentric disk 95 | vec2 ggx__u2_to_md2(vec2 u, float zi) 96 | { 97 | float a = 1.0f / (1.0f + zi); 98 | 99 | if (u.x > a) { 100 | float xu = (u.x - a) / (1.0f - a); // remap to [0, 1] 101 | 102 | return vec2(zi, 1) * ggx__u2_to_hd2(vec2(xu, u.y)); 103 | } else { 104 | float xu = (u.x - a) / a; // remap to [-1, 0] 105 | 106 | return ggx__u2_to_hd2(vec2(xu, u.y)); 107 | } 108 | } 109 | 110 | // ----------------------------------------------------------------------------- 111 | // concentric disk to microfacet normal 112 | vec3 ggx__d2_to_h2(vec2 d, float zi, float z_i) 113 | { 114 | vec3 z = vec3(z_i, 0, zi); 115 | vec3 y = vec3(0, 1, 0); 116 | vec3 x = vec3(zi, 0, -z_i); // cross(z, y) 117 | float tmp = clamp(1 - dot(d, d), 0.0, 1.0); 118 | vec3 wm = x * d.x + y * d.y + z * sqrt(tmp); 119 | 120 | return vec3(wm.x, wm.y, clamp(wm.z, 0.0, 1.0)); 121 | } 122 | 123 | // ----------------------------------------------------------------------------- 124 | vec3 ggx__u2_to_h2_std_radial(vec2 u, float zi, float z_i) 125 | { 126 | return ggx__d2_to_h2(ggx__u2_to_md2(u, zi), zi, z_i); 127 | } 128 | 129 | // ----------------------------------------------------------------------------- 130 | // standard GGX variate exploiting rotational symmetry 131 | vec3 ggx__u2_to_h2_std(vec2 u, vec3 wi) 132 | { 133 | float zi = wi.z; 134 | float z_i = sqrt(wi.x * wi.x + wi.y * wi.y); 135 | vec3 wm = ggx__u2_to_h2_std_radial(u, zi, z_i); 136 | 137 | // rotate for non-normal incidence 138 | if (z_i > 0) { 139 | float nrm = 1 / z_i; 140 | float c = wi.x * nrm; 141 | float s = wi.y * nrm; 142 | float x = c * wm.x - s * wm.y; 143 | float y = s * wm.x + c * wm.y; 144 | 145 | wm = vec3(x, y, wm.z); 146 | } 147 | 148 | return wm; 149 | } 150 | 151 | // ----------------------------------------------------------------------------- 152 | // warp the domain to match the standard GGX distribution 153 | // (note: works with anisotropic roughness) 154 | vec3 ggx__u2_to_h2(vec2 u, vec3 wi, float r1, float r2) 155 | { 156 | vec3 wi_std = normalize(vec3(r1, r2, 1) * wi); 157 | vec3 wm_std = ggx__u2_to_h2_std(u, wi_std); 158 | vec3 wm = normalize(vec3(r1, r2, 1) * wm_std); 159 | 160 | return wm; 161 | } 162 | 163 | // ----------------------------------------------------------------------------- 164 | // importance sample: map the unit square to the hemisphere 165 | vec3 ggx_sample(vec2 u, vec3 wi, float alpha) 166 | { 167 | return ggx__u2_to_h2(u, wi, alpha, alpha); 168 | } 169 | 170 | #undef PI 171 | 172 | -------------------------------------------------------------------------------- /demo-merl/shaders/npf.glsl: -------------------------------------------------------------------------------- 1 | uniform sampler2D u_NpfSampler; 2 | 3 | const float M_PI = 3.14159265359; 4 | const float M_PI_2 = 1.57079632679; 5 | 6 | // decode the simple HDR format in the PNG 7 | vec3 uberTextureLookup( vec2 uv ) { 8 | vec4 color = texture( u_NpfSampler, uv ); 9 | return color.xyz; 10 | } 11 | 12 | bool isNan(float val) { 13 | return (val <= 0.0 || 0.0 <= val) ? false : true; 14 | } 15 | 16 | bool isInf(float val) { 17 | return (val >= 9999.99990 || val <= -9999.99990); 18 | } 19 | 20 | float dotProduct(vec3 dir1, vec3 dir2) 21 | { 22 | float dot = dot(dir1, dir2); 23 | return clamp(dot, 1e-5, 1.0); 24 | } 25 | 26 | vec3 lookupInterpolatedG1(int n, float theta) 27 | { 28 | float fb = theta / M_PI_2 * 90.0 ; 29 | // find bin centers 30 | float f0 = floor(fb-0.5) + 0.5 ; 31 | float f1 = f0 + 1.0 ; 32 | // find bin indexes t0 and t1 33 | float t0 = floor(f0) ; 34 | float t1 = floor(f1) ; 35 | // ignores the first bin 36 | t0 = max(1.0, t0); 37 | t1 = max(2.0, t1); 38 | // find theta at bin centers 39 | float theta0 = (t0+0.5) * M_PI_2 / 90.0; 40 | float theta1 = (t1+0.5) * M_PI_2 / 90.0; 41 | //find the weights 42 | float w0 = 1.0 - (fb - f0) ; 43 | float w1 = 1.0 - w0 ; // 1.0 - (f1 - fb) 44 | // sample 45 | vec3 G1 = vec3(0.0); 46 | vec2 uv; 47 | uv.y = float(n)/256.; 48 | if (t1 > 90.0-1.0) 49 | { 50 | uv.x = (t0+2.+90.)/512.; 51 | G1 = uberTextureLookup(uv) * w0 ; 52 | } 53 | else 54 | { 55 | uv.x = (t0+2.+90.)/512.; 56 | vec3 g0 = uberTextureLookup(uv); 57 | uv.x = (t1+2.+90.)/512.; 58 | vec3 g1 = uberTextureLookup(uv); 59 | G1 = g0 * w0 + g1 * w1; 60 | } 61 | return G1; 62 | } 63 | 64 | // material index goes from 0 to 99 65 | // each texture row contains: rhoD rhoS D[90] G1[90] F[90] 66 | vec3 getBRDF(int n, vec3 dirIn, vec3 dirOut, vec3 dirNormal) { 67 | if (dirIn.z < 0 || dirOut.z < 0) 68 | return vec3(0); 69 | 70 | vec3 dirH = normalize(dirIn + dirOut); 71 | float thetaH = acos(dot(dirH, dirNormal)); 72 | float thetaD = acos(dot(dirIn, dirH)); 73 | float thetaI = acos(dot(dirIn, dirNormal)); 74 | float thetaO = acos(dot(dirOut, dirNormal)); 75 | 76 | // BRDF texture has a fixed size of 512x256 77 | vec2 uv; 78 | uv.y = float(n)/256.; 79 | 80 | uv.x = 0.0/512.; 81 | vec3 rhoD = uberTextureLookup(uv); 82 | 83 | uv.x = 1.0/512.; 84 | vec3 rhoS = uberTextureLookup(uv); 85 | 86 | int iD = int(clamp(sqrt(thetaH / M_PI_2 87 | * 90.0 * 90.0), 0.0, 89.0)); 88 | uv.x = float(iD+2)/512.; 89 | vec3 D = uberTextureLookup(uv); 90 | 91 | vec3 G1I = lookupInterpolatedG1(n, thetaI); 92 | 93 | vec3 G1O = lookupInterpolatedG1(n, thetaO); 94 | 95 | int iF = int(clamp(thetaD / M_PI_2 * 90.0, 0.0, 89.0)); 96 | uv.x = float(iF+2+90+90)/512.; 97 | vec3 F = uberTextureLookup(uv); 98 | 99 | vec3 BRDF = (rhoD) + (rhoS) * D * F * 100 | (G1I / cos(thetaI)) * (G1O / cos(thetaO)); 101 | 102 | if(isInf(length(BRDF)) || isNan(length(BRDF))) 103 | { 104 | return vec3(0.0); 105 | } 106 | 107 | return BRDF; 108 | } 109 | -------------------------------------------------------------------------------- /demo-merl/shaders/pivot.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | /* pivot.glsl - public domain GLSL library 3 | by Jonathan Dupuy 4 | 5 | This file provides utility functions for the sphere light 6 | shading technique described in my paper "A Spherical Cap Preserving 7 | Parameterization for Spherical Distributions".. 8 | */ 9 | 10 | // Spherical Cap 11 | struct cap { 12 | vec3 dir; // direction 13 | float z; // cos of the aperture angle 14 | }; 15 | 16 | // Sphere 17 | struct sphere { 18 | vec3 pos; // center 19 | float r; // radius 20 | }; 21 | 22 | // Mappings 23 | vec3 u2_to_cap(vec2 u, cap c); 24 | vec3 u2_to_cos(vec2 u); 25 | vec3 u2_to_s2(vec2 u); 26 | vec3 u2_to_h2(vec2 u); 27 | vec3 u2_to_ps2(vec2 u, vec3 r_p); 28 | vec3 u2_to_ph2(vec2 u, vec3 r_p); 29 | vec3 u2_to_pcap(vec2 u, cap c, vec3 r_p); 30 | vec3 r3_to_pr3(vec3 r, vec3 r_p); 31 | vec3 s2_to_ps2(vec3 r, vec3 r_p); 32 | cap cap_to_pcap(cap c, vec3 r_p); 33 | 34 | // PDFs 35 | float pdf_cap(vec3 wk, cap c); 36 | float pdf_cos(vec3 wk); 37 | float pdf_s2(vec3 wk); 38 | float pdf_h2(vec3 wk); 39 | float pdf_ps2(vec3 wk, vec3 r_p); 40 | float pdf_pcap(vec3 wk, cap c, vec3 r_p); 41 | float pdf_pcap_fast(vec3 wk, cap c_std, vec3 r_p); 42 | 43 | // solid angles 44 | float cap_solidangle(cap c); 45 | float cap_solidangle(cap c1, cap c2); 46 | 47 | // Approximate BRDF shading 48 | float GGXSphereLightingPivotApprox(sphere s, vec3 wo, vec3 pivot); 49 | 50 | // 51 | // 52 | //// end header file /////////////////////////////////////////////////////////// 53 | 54 | 55 | // Frisvad's method to build an orthonomal basis around a direction w 56 | void basis(vec3 w, out vec3 t1, out vec3 t2) 57 | { 58 | if (w.z < -0.9999999) { 59 | t1 = vec3( 0, -1, 0); 60 | t2 = vec3(-1, 0, 0); 61 | } else { 62 | const float a = 1.0 / (1.0 + w.z); 63 | const float b = -w.x * w.y * a; 64 | t1 = vec3(1.0 - w.x * w.x * a, b, -w.x); 65 | t2 = vec3(b, 1.0 - w.y * w.y * a, -w.y); 66 | } 67 | } 68 | 69 | #define TWOPI 6.283185307 70 | 71 | float cap_solidangle(cap c) 72 | { 73 | return TWOPI - TWOPI * c.z; 74 | } 75 | 76 | // Based on Oat and Sander's 2008 technique 77 | float cap_solidangle(cap c1, cap c2) 78 | { 79 | float r1 = acos(c1.z); 80 | float r2 = acos(c2.z); 81 | float rd = acos(dot(c1.dir, c2.dir)); 82 | float fArea = 0.0; 83 | 84 | if (rd <= max(r1, r2) - min(r1, r2)) { 85 | // One cap in completely inside the other 86 | fArea = TWOPI - TWOPI * max(c1.z, c2.z); 87 | } else if (rd >= r1 + r2) { 88 | // No intersection exists 89 | fArea = 0; 90 | } else { 91 | float fDiff = abs(r1 - r2); 92 | float den = r1 + r2 - fDiff; 93 | float x = 1.0 - clamp((rd - fDiff) / den, 0.0, 1.0); 94 | fArea = smoothstep(0.0, 1.0, x); 95 | fArea*= TWOPI - TWOPI * max(c1.z, c2.z); 96 | } 97 | 98 | return fArea; 99 | } 100 | 101 | 102 | float GGXSphereLightingPivotApprox(sphere s, vec3 wo, vec3 pivot) 103 | { 104 | // compute the spherical cap produced by the sphere 105 | float tmp = clamp(s.r * s.r / dot(s.pos, s.pos), 0.0, 1.0); 106 | cap c = cap(normalize(s.pos), sqrt(1.0 - tmp)); 107 | 108 | // integrate 109 | cap c1 = cap_to_pcap(c, pivot); 110 | cap c2 = cap_to_pcap(cap(vec3(0, 0, 1), 0.0), pivot); 111 | float res = cap_solidangle(c1, c2) * /*1/4pi*/0.079577472; 112 | return clamp(res, 0.0, 1.0); 113 | } 114 | 115 | // ----------------------------------------------------------------------------- 116 | // sample warps 117 | 118 | /* Sphere */ 119 | vec3 u2_to_s2(vec2 u) 120 | { 121 | float z = 2.0 * u.x - 1.0; // in [-1, 1) 122 | float sin_theta = sqrt(1.0 - z * z); 123 | float phi = TWOPI * u.y; // in [0, 2pi) 124 | float x = sin_theta * cos(phi); 125 | float y = sin_theta * sin(phi); 126 | 127 | return vec3(x, y, z); 128 | } 129 | 130 | /* Hemisphere */ 131 | vec3 u2_to_h2(vec2 u) 132 | { 133 | float z = u.x; // in [0, 1) 134 | float sin_theta = sqrt(1.0 - z * z); 135 | float phi = TWOPI * u.y; // in [0, 2pi) 136 | float x = sin_theta * cos(phi); 137 | float y = sin_theta * sin(phi); 138 | 139 | return vec3(x, y, z); 140 | } 141 | 142 | /* Spherical Cap */ 143 | vec3 u2_to_cap(vec2 u, cap c) 144 | { 145 | // generate the sample in the basis aligned with the cap 146 | float z = (1.0 - c.z) * u.x + c.z; // in [cap_cos, 1) 147 | float sin_theta = sqrt(1.0 - z * z); 148 | float phi = TWOPI * u.y; // in [0, 2pi) 149 | float x = sin_theta * cos(phi); 150 | float y = sin_theta * sin(phi); 151 | 152 | // compute basis vectors 153 | vec3 t1, t2; 154 | basis(c.dir, t1, t2); 155 | mat3 xf = mat3(t1, t2, c.dir); 156 | 157 | // warp the sample in the proper basis 158 | return normalize(xf * vec3(x, y, z)); 159 | } 160 | 161 | /* Disk */ 162 | vec2 u2_to_disk(vec2 u) 163 | { 164 | float r = sqrt(u.x); // in [0, 1) 165 | float phi = TWOPI * u.y; // in [0, 2pi) 166 | return r * vec2(cos(phi), sin(phi)); 167 | } 168 | 169 | /* Clamped Cosine */ 170 | vec3 u2_to_cos(vec2 u) 171 | { 172 | // project a disk sample back to the hemisphere 173 | vec2 d = u2_to_disk(u); 174 | float z = sqrt(1.0 - dot(d, d)); 175 | return vec3(d, z); 176 | } 177 | 178 | /* Pivot 3D Transformation */ 179 | vec3 r3_to_pr3(vec3 r, vec3 r_p) 180 | { 181 | vec3 tmp = r - r_p; 182 | vec3 cp1 = cross(r, r_p); 183 | vec3 cp2 = cross(tmp, cp1); 184 | float dp = dot(r, r_p) - 1.f; 185 | float qf = dp * dp + dot(cp1, cp1); 186 | 187 | return ((dp * tmp - cp2) / qf); 188 | } 189 | vec3 s2_to_ps2(vec3 wk, vec3 r_p) 190 | { 191 | return r3_to_pr3(wk, r_p); 192 | } 193 | 194 | /* Pivot Transformed Sphere Sample */ 195 | vec3 u2_to_ps2(vec2 u, vec3 r_p) 196 | { 197 | vec3 std = u2_to_s2(u); 198 | return s2_to_ps2(std, r_p); 199 | } 200 | 201 | /* Pivot Transformed Hemisphere Sample */ 202 | vec3 u2_to_ph2(vec2 u, vec3 r_p) 203 | { 204 | vec3 std = u2_to_h2(u); 205 | return s2_to_ps2(std, r_p); 206 | } 207 | 208 | /* Pivot Transformed Cap Sample */ 209 | vec3 u2_to_pcap(vec2 u, cap c, vec3 r_p) 210 | { 211 | vec3 std = u2_to_cap(u, c); 212 | return s2_to_ps2(std, r_p); 213 | } 214 | 215 | /* Pivot 2D Transformation */ 216 | vec2 r2_to_pr2(vec2 r, float r_p) 217 | { 218 | vec2 tmp1 = vec2(r.x - r_p, r.y); 219 | vec2 tmp2 = r_p * r - vec2(1, 0); 220 | float x = dot(tmp1, tmp2); 221 | float y = tmp1.y * tmp2.x - tmp1.x * tmp2.y; 222 | float qf = dot(tmp2, tmp2); 223 | 224 | return (vec2(x, y) / qf); 225 | } 226 | 227 | /* Pivot Transformed Cap */ 228 | cap cap_to_pcap(cap c, vec3 r_p) 229 | { 230 | // extract pivot length and direction 231 | float pivot_mag = length(r_p); 232 | // special case: the pivot is at the origin 233 | if (pivot_mag < 0.001) 234 | return cap(-c.dir, c.z); 235 | vec3 pivot_dir = r_p / pivot_mag; 236 | 237 | // 2D cap dir 238 | float cos_phi = dot(c.dir, pivot_dir); 239 | float sin_phi = sqrt(1.0 - cos_phi * cos_phi); 240 | 241 | // 2D basis = (pivotDir, PivotOrthogonalDirection) 242 | vec3 pivot_ortho_dir; 243 | if (abs(cos_phi) < 0.9999) { 244 | pivot_ortho_dir = (c.dir - cos_phi * pivot_dir) / sin_phi; 245 | } else { 246 | pivot_ortho_dir = vec3(0, 0, 0); 247 | } 248 | 249 | // compute cap 2D end points 250 | float cap_sin = sqrt(1.0 - c.z * c.z); 251 | float a1 = cos_phi * c.z; 252 | float a2 = sin_phi * cap_sin; 253 | float a3 = sin_phi * c.z; 254 | float a4 = cos_phi * cap_sin; 255 | vec2 dir1 = vec2(a1 + a2, a3 - a4); 256 | vec2 dir2 = vec2(a1 - a2, a3 + a4); 257 | 258 | // project in 2D 259 | vec2 dir1_xf = r2_to_pr2(dir1, pivot_mag); 260 | vec2 dir2_xf = r2_to_pr2(dir2, pivot_mag); 261 | 262 | // compute the cap 2D direction 263 | float area = dir1_xf.x * dir2_xf.y - dir1_xf.y * dir2_xf.x; 264 | float s = area > 0.0 ? 1.0 : -1.0; 265 | vec2 dir_xf = s * normalize(dir1_xf + dir2_xf); 266 | 267 | // compute the 3D cap parameters 268 | vec3 cap_dir = dir_xf.x * pivot_dir + dir_xf.y * pivot_ortho_dir; 269 | float cap_cos = dot(dir_xf, dir1_xf); 270 | 271 | return cap(cap_dir, cap_cos); 272 | } 273 | 274 | 275 | // ----------------------------------------------------------------------------- 276 | // PDFs 277 | float pdf_cap(vec3 wk, cap c) 278 | { 279 | // make sure the sample lies in the the cap 280 | if (dot(wk, c.dir) >= c.z) { 281 | return 1.0 / cap_solidangle(c); 282 | } 283 | return 0.0; 284 | } 285 | 286 | float pdf_cos(vec3 wk) 287 | { 288 | return clamp(wk.z, 0.0, 1.0) * /* 1/pi */0.318309886; 289 | } 290 | 291 | float pdf_s2(vec3 wk) 292 | { 293 | return /* 1/4pi */ 0.079577472; 294 | } 295 | 296 | float pdf_h2(vec3 wk) 297 | { 298 | return wk.z > 0.0 ?/* 1/2pi */ 0.159154943 : 0.0; 299 | } 300 | 301 | float pivot_jacobian(vec3 wk, vec3 r_p) 302 | { 303 | float num = 1.0 - dot(r_p, r_p); 304 | vec3 tmp = wk - r_p; 305 | float den = dot(tmp, tmp); 306 | 307 | return (num * num) / (den * den); 308 | } 309 | 310 | float pdf_ps2(vec3 wk, vec3 r_p) 311 | { 312 | float std = pdf_s2(s2_to_ps2(wk, r_p)); 313 | float J = pivot_jacobian(wk, r_p); 314 | return std * J; 315 | } 316 | 317 | float pdf_pcap_fast(vec3 wk, cap c_std, vec3 r_p) 318 | { 319 | float std = pdf_cap(s2_to_ps2(wk, r_p), c_std); 320 | float J = pivot_jacobian(wk, r_p); 321 | return std * J; 322 | } 323 | 324 | float pdf_pcap(vec3 wk, cap c, vec3 r_p) 325 | { 326 | return pdf_pcap_fast(wk, cap_to_pcap(c, r_p), r_p); 327 | } 328 | 329 | #undef TWOPI 330 | 331 | 332 | -------------------------------------------------------------------------------- /demo-merl/shaders/sphere.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | uniform int u_SamplesPerPass; 8 | 9 | 10 | struct Transform { 11 | mat4 modelView; 12 | mat4 projection; 13 | mat4 modelViewProjection; 14 | mat4 viewInv; 15 | }; 16 | 17 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 18 | uniform Transforms { 19 | Transform u_Transform; 20 | }; 21 | 22 | layout(std140, binding = BUFFER_BINDING_RANDOM) 23 | uniform Random { 24 | vec4 value[64]; 25 | } u_Random; 26 | 27 | vec4 rand(int idx) { return u_Random.value[idx]; } 28 | float hash(vec2 p) 29 | { 30 | float h = dot(p, vec2(127.1, 311.7)); 31 | return fract(sin(h) * 43758.5453123); 32 | } 33 | 34 | uniform sampler2D u_EnvmapSampler; 35 | uniform float u_Alpha; 36 | uniform int u_MerlId = 0; 37 | 38 | // ============================================================================ 39 | 40 | vec3 evalEnvmap(vec3 dir) 41 | { 42 | float u1 = atan(dir.x, dir.y) / M_PI * 0.5 + 0.5; 43 | float u2 = 1.0 - acos(dir.z) / M_PI; 44 | return textureLod(u_EnvmapSampler, vec2(u1, u2), 0.0).rgb; 45 | } 46 | 47 | vec3 evalBrdf(vec3 wi, vec3 wo) 48 | { 49 | float c = clamp(wi.z, 0.0, 1.0); 50 | #if BRDF_NPF 51 | return getBRDF(u_MerlId, wi, wo, vec3(0, 0, 1)) * c / M_PI; 52 | #elif BRDF_MERL 53 | return BRDF(wi, wo, vec3(0, 0, 1), vec3(1, 0, 0), vec3(0, 1, 0)) * c; 54 | #else 55 | return vec3(c / 3.14159265359); 56 | #endif 57 | } 58 | 59 | // ============================================================================ 60 | 61 | // ***************************************************************************** 62 | /** 63 | * Vertex Shader 64 | * 65 | * The shader outputs attributes relevant for shading in view space. 66 | */ 67 | #ifdef VERTEX_SHADER 68 | layout(location = 0) in vec4 i_Position; 69 | layout(location = 1) in vec4 i_TexCoord; 70 | layout(location = 2) in vec4 i_Tangent1; 71 | layout(location = 3) in vec4 i_Tangent2; 72 | layout(location = 0) out vec4 o_Position; 73 | layout(location = 1) out vec4 o_TexCoord; 74 | layout(location = 2) out vec4 o_Tangent1; 75 | layout(location = 3) out vec4 o_Tangent2; 76 | layout(location = 4) flat out int o_SphereId; 77 | 78 | void main(void) 79 | { 80 | o_Position = u_Transform.modelView * i_Position; 81 | o_TexCoord = i_TexCoord; 82 | o_Tangent1 = u_Transform.modelView * i_Tangent1; 83 | o_Tangent2 = u_Transform.modelView * i_Tangent2; 84 | o_SphereId = gl_InstanceID; 85 | 86 | gl_Position = u_Transform.modelViewProjection * i_Position; 87 | } 88 | #endif // VERTEX_SHADER 89 | 90 | // ***************************************************************************** 91 | /** 92 | * Fragment Shader 93 | * 94 | */ 95 | #ifdef FRAGMENT_SHADER 96 | layout(location = 0) in vec4 i_Position; 97 | layout(location = 1) in vec4 i_TexCoord; 98 | layout(location = 2) in vec4 i_Tangent1; 99 | layout(location = 3) in vec4 i_Tangent2; 100 | layout(location = 4) flat in int i_SphereId; 101 | layout(location = 0) out vec4 o_FragColor; 102 | 103 | void main(void) 104 | { 105 | // extract attributes 106 | vec3 wx = normalize(i_Tangent1.xyz); 107 | vec3 wy = normalize(i_Tangent2.xyz); 108 | vec3 wn = normalize(cross(wx, wy)); 109 | vec3 wo = normalize(-i_Position.xyz); 110 | mat3 tgInv = mat3(wy, wx, wn); 111 | mat3 tg = transpose(mat3(wx, wy, wn)); 112 | 113 | // express data in tangent space 114 | wo = tg * wo; 115 | wn = vec3(0, 0, 1); 116 | 117 | // initialize emitted and outgoing radiance 118 | vec3 Lo = vec3(0); 119 | 120 | // ----------------------------------------------------------------------------- 121 | /** 122 | * Shading with Importance Sampling 123 | * 124 | */ 125 | #if (SHADE_MC_GGX || SHADE_MC_COS) 126 | // loop over all samples 127 | for (int j = 0; j < u_SamplesPerPass; ++j) { 128 | // compute a uniform sample 129 | float h1 = hash(gl_FragCoord.xy); 130 | float h2 = hash(gl_FragCoord.yx); 131 | vec2 u2 = mod(vec2(h1, h2) + rand(j).xy, vec2(1.0)); 132 | #if SHADE_MC_COS 133 | vec3 wi = u2_to_cos(u2); 134 | float pdf = pdf_cos(wi); 135 | #elif SHADE_MC_GGX 136 | vec3 wm = ggx_sample(u2, wo, u_Alpha); 137 | vec3 wi = 2.0 * dot(wm, wo) * wm - wo; 138 | float pdf; 139 | ggx_evalp(wi, wo, u_Alpha, pdf); 140 | #endif 141 | float pdf_dummy; 142 | 143 | vec3 frp = evalBrdf(wi, wo); 144 | vec4 tmp = vec4(transpose(tg) * wi, 0); 145 | vec3 wiWorld = normalize( (u_Transform.viewInv * tmp).xyz ); 146 | vec3 Li = evalEnvmap(wiWorld); 147 | 148 | if (pdf > 0.0) 149 | Lo+= Li * frp / pdf; 150 | } 151 | 152 | o_FragColor = vec4(Lo, u_SamplesPerPass); 153 | 154 | // ----------------------------------------------------------------------------- 155 | /** 156 | * Shading with MIS 157 | * 158 | */ 159 | #elif SHADE_MC_MIS 160 | // loop over all samples 161 | for (int j = 0; j < u_SamplesPerPass; ++j) { 162 | // compute a uniform sample 163 | float h1 = hash(gl_FragCoord.xy); 164 | float h2 = hash(gl_FragCoord.yx); 165 | vec2 u2 = mod(vec2(h1, h2) + rand(j).xy, vec2(1.0)); 166 | 167 | // importance sample the GGX Approx 168 | if (true) { 169 | vec3 wm = ggx_sample(u2, wo, u_Alpha); 170 | vec3 wi = 2.0 * wm * dot(wo, wm) - wo; 171 | float pdf1; 172 | ggx_evalp(wi, wo, u_Alpha, pdf1); 173 | vec3 frp = evalBrdf(wi, wo); 174 | vec4 tmp = vec4(transpose(tg) * wi, 0); 175 | vec3 wiWorld = normalize( (u_Transform.viewInv * tmp).xyz ); 176 | vec3 Li = evalEnvmap(wiWorld); 177 | 178 | // raytrace the sphere light 179 | if (pdf1 > 0.0) { 180 | float pdf2 = pdf_cos(wi); 181 | float misWeight = pdf1 * pdf1; 182 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 183 | 184 | Lo+= Li * frp / pdf1 * misWeight / misNrm; 185 | } 186 | } 187 | 188 | // importance sample Cos 189 | if (true) { 190 | vec3 wi = u2_to_cos(u2); 191 | float pdf2 = pdf_cos(wi); 192 | vec3 frp = evalBrdf(wi, wo); 193 | vec4 tmp = vec4(transpose(tg) * wi, 0); 194 | vec3 wiWorld = normalize( (u_Transform.viewInv * tmp).xyz ); 195 | vec3 Li = evalEnvmap(wiWorld); 196 | 197 | if (pdf2 > 0.0) { 198 | float pdf1; 199 | ggx_evalp(wi, wo, u_Alpha, pdf1); 200 | float misWeight = pdf2 * pdf2; 201 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 202 | 203 | Lo+= Li * frp / pdf2 * misWeight / misNrm; 204 | } 205 | } 206 | } 207 | 208 | o_FragColor = vec4(Lo, u_SamplesPerPass); 209 | 210 | // ----------------------------------------------------------------------------- 211 | /** 212 | * Debug Shading 213 | * 214 | * Do whatever you like in here. 215 | */ 216 | #elif SHADE_DEBUG 217 | vec3 wi = normalize(tg * vec3(0, 0, 1)); 218 | Lo = getBRDF(0, wi, wo, vec3(0, 0, 1)) * u_SamplesPerPass; 219 | 220 | vec3 wx2 = normalize((u_Transform.viewInv * i_Tangent1).xyz); 221 | vec3 wy2 = normalize((u_Transform.viewInv * i_Tangent2).xyz); 222 | wn = normalize(cross(wy2, wx2)); 223 | 224 | Lo = evalEnvmap(wn) * u_SamplesPerPass; 225 | Lo = BRDF(wi, wo, vec3(0, 0, 1), vec3(1, 0, 0), vec3(0, 1, 0)) * u_SamplesPerPass; 226 | float dummy; 227 | Lo = vec3(u_SamplesPerPass) * ggx_evalp(wo, wo, 1.0, dummy); 228 | o_FragColor = vec4(clamp(wy2, 0.0, 1.0), u_SamplesPerPass); 229 | o_FragColor = vec4(Lo, u_SamplesPerPass); 230 | // ----------------------------------------------------------------------------- 231 | #endif // SHADE 232 | 233 | } 234 | #endif // FRAGMENT_SHADER 235 | 236 | 237 | -------------------------------------------------------------------------------- /demo-merl/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | uniform float u_Exposure; 2 | uniform float u_Gamma; 3 | uniform vec3 u_Viewport; 4 | 5 | #if MSAA_FACTOR 6 | uniform sampler2DMS u_FramebufferSampler; 7 | #else 8 | uniform sampler2D u_FramebufferSampler; 9 | #endif 10 | 11 | // ------------------------------------------------------------------------------------------------- 12 | /** 13 | * Vertex Shader 14 | * 15 | * This vertex shader draws a fullscreen quad 16 | */ 17 | #ifdef VERTEX_SHADER 18 | layout(location = 0) out vec2 o_TexCoord; 19 | 20 | void main(void) 21 | { 22 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 23 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 24 | } 25 | #endif 26 | 27 | // ------------------------------------------------------------------------------------------------- 28 | /** 29 | * Fragment Shader 30 | * 31 | * This fragment shader post-processes the scene framebuffer by applying 32 | * tone mapping, gamma correction and image scaling. 33 | */ 34 | #ifdef FRAGMENT_SHADER 35 | layout(location = 0) in vec2 i_TexCoord; 36 | layout(location = 0) out vec4 o_FragColor; 37 | 38 | void main(void) 39 | { 40 | vec4 color = vec4(0); 41 | ivec2 P = ivec2(gl_FragCoord.xy); 42 | 43 | // get framebuffer data 44 | #if MSAA_FACTOR 45 | for (int i = 0; i < MSAA_FACTOR; ++i) { 46 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 47 | if (c.a > 0.0) color+= c / c.a; // normalize by number of samples 48 | } 49 | color/= vec4(MSAA_FACTOR); 50 | #else 51 | color = texelFetch(u_FramebufferSampler, P, 0); 52 | if (color.a > 0.0) color.rgb/= color.a; 53 | #endif 54 | 55 | // make fragments store positive values 56 | if (any(lessThan(color.rgb, vec3(0)))) { 57 | o_FragColor = vec4(1, 0, 0, 1); 58 | return; 59 | } 60 | 61 | // exposure 62 | color.rgb*= exp2(u_Exposure); 63 | 64 | // gamma 65 | color.rgb = pow(color.rgb, vec3(1.0 / u_Gamma)); 66 | 67 | // final color 68 | o_FragColor = vec4(color.rgb, 1.0); 69 | 70 | // make sure the fragments store real values 71 | if (any(isnan(color.rgb))) 72 | o_FragColor = vec4(1, 0, 0, 1); 73 | 74 | // o_FragColor = vec4(i_TexCoord, 0, 1); 75 | } 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /imgui/imgui_impl.h: -------------------------------------------------------------------------------- 1 | // ImGui GLFW binding with OpenGL3 + shaders 2 | // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 3 | // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) 4 | 5 | // Implemented features: 6 | // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. 7 | // [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 8 | 9 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 10 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 11 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 12 | // https://github.com/ocornut/imgui 13 | 14 | #ifndef IMGUI_IMPL_H 15 | #define IMGUI_IMPL_H 16 | struct GLFWwindow; 17 | 18 | IMGUI_API bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks, const char* glsl_version = NULL); 19 | IMGUI_API void ImGui_ImplGlfwGL3_Shutdown(); 20 | IMGUI_API void ImGui_ImplGlfwGL3_NewFrame(); 21 | IMGUI_API void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data); 22 | 23 | // Use if you want to reset your rendering device without losing ImGui state. 24 | IMGUI_API void ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); 25 | IMGUI_API bool ImGui_ImplGlfwGL3_CreateDeviceObjects(); 26 | 27 | // GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) 28 | // Provided here if you want to chain callbacks. 29 | // You can also handle inputs yourself and use those as a reference. 30 | IMGUI_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 31 | IMGUI_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 32 | IMGUI_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 33 | IMGUI_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); 34 | #endif //IMGUI_IMPL_H 35 | -------------------------------------------------------------------------------- /plot-brdf/README.md: -------------------------------------------------------------------------------- 1 | ## BRDF Plot Tool 2 | 3 | This is a BRDF Plotting tool. 4 | 5 | ![alt text](preview.png "Preview") 6 | -------------------------------------------------------------------------------- /plot-brdf/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/opengl-framework/547aed6affae10f3d86fc3080815bc28de79391f/plot-brdf/preview.png -------------------------------------------------------------------------------- /plot-brdf/shaders/background.glsl: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // Uniforms 3 | // -------------------------------------------------- 4 | uniform vec3 u_ClearColor; 5 | 6 | struct Transform { 7 | mat4 modelView; 8 | mat4 projection; 9 | mat4 modelViewProjection; 10 | mat4 viewInv; 11 | }; 12 | 13 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 14 | uniform Transforms { 15 | Transform u_Transform; 16 | }; 17 | 18 | // -------------------------------------------------- 19 | // Vertex shader 20 | // -------------------------------------------------- 21 | #ifdef VERTEX_SHADER 22 | layout(location = 0) out vec3 o_TexCoord; 23 | void main() { 24 | // draw a full screen quad in modelview space 25 | vec2 p = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1) * 2.0 - 1.0; 26 | vec3 e = vec3(-900.0, p * 1e5); 27 | 28 | gl_Position = u_Transform.projection * vec4(e, 1); 29 | gl_Position.z = 0.99999 * gl_Position.w; // make sure the cubemap is visible 30 | o_TexCoord = vec3(u_Transform.viewInv * vec4(normalize(e), 0)); 31 | } 32 | #endif 33 | 34 | // -------------------------------------------------- 35 | // Fragment shader 36 | // -------------------------------------------------- 37 | #ifdef FRAGMENT_SHADER 38 | layout(location = 0) in vec3 i_TexCoord; 39 | layout(location = 0) out vec4 o_FragColor; 40 | 41 | void main() { 42 | o_FragColor = vec4(1); 43 | } 44 | #endif 45 | 46 | 47 | -------------------------------------------------------------------------------- /plot-brdf/shaders/brdf_merl.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Disney Enterprises, Inc. All rights reserved. 3 | 4 | This license governs use of the accompanying software. If you use the software, you 5 | accept this license. If you do not accept the license, do not use the software. 6 | 7 | 1. Definitions 8 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have 9 | the same meaning here as under U.S. copyright law. A "contribution" is the original 10 | software, or any additions or changes to the software. A "contributor" is any person 11 | that distributes its contribution under this license. "Licensed patents" are a 12 | contributor's patent claims that read directly on its contribution. 13 | 14 | 2. Grant of Rights 15 | (A) Copyright Grant- Subject to the terms of this license, including the license 16 | conditions and limitations in section 3, each contributor grants you a non-exclusive, 17 | worldwide, royalty-free copyright license to reproduce its contribution, prepare 18 | derivative works of its contribution, and distribute its contribution or any derivative 19 | works that you create. 20 | (B) Patent Grant- Subject to the terms of this license, including the license 21 | conditions and limitations in section 3, each contributor grants you a non-exclusive, 22 | worldwide, royalty-free license under its licensed patents to make, have made, 23 | use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the 24 | software or derivative works of the contribution in the software. 25 | 26 | 3. Conditions and Limitations 27 | (A) No Trademark License- This license does not grant you rights to use any 28 | contributors' name, logo, or trademarks. 29 | (B) If you bring a patent claim against any contributor over patents that you claim 30 | are infringed by the software, your patent license from such contributor to the 31 | software ends automatically. 32 | (C) If you distribute any portion of the software, you must retain all copyright, 33 | patent, trademark, and attribution notices that are present in the software. 34 | (D) If you distribute any portion of the software in source code form, you may do 35 | so only under this license by including a complete copy of this license with your 36 | distribution. If you distribute any portion of the software in compiled or object code 37 | form, you may only do so under a license that complies with this license. 38 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors 39 | give no express warranties, guarantees or conditions. You may have additional 40 | consumer rights under your local laws which this license cannot change. 41 | To the extent permitted under your local laws, the contributors exclude the 42 | implied warranties of merchantability, fitness for a particular purpose and non- 43 | infringement. 44 | */ 45 | 46 | uniform samplerBuffer u_MerlSampler; 47 | 48 | const int BRDF_SAMPLING_RES_THETA_H = 90; 49 | const int BRDF_SAMPLING_RES_THETA_D = 90; 50 | const int BRDF_SAMPLING_RES_PHI_D = 360; 51 | const float RED_SCALE = (1.0/1500.0); 52 | const float GREEN_SCALE = (1.15/1500.0); 53 | const float BLUE_SCALE = (1.66/1500.0); 54 | #ifndef M_PI 55 | #define M_PI 3.1415926535897932384626433832795 56 | #endif 57 | 58 | 59 | // Lookup phi_diff index 60 | int phi_diff_index(float phi_diff) 61 | { 62 | // Because of reciprocity, the BRDF is unchanged under 63 | // phi_diff -> phi_diff + M_PI 64 | if (phi_diff < 0.0) 65 | phi_diff += M_PI; 66 | 67 | // In: phi_diff in [0 .. pi] 68 | // Out: tmp in [0 .. 179] 69 | return clamp(int(phi_diff * (1.0/M_PI * (BRDF_SAMPLING_RES_PHI_D / 2))), 0, BRDF_SAMPLING_RES_PHI_D / 2 - 1); 70 | } 71 | 72 | 73 | // Lookup theta_half index 74 | // This is a non-linear mapping! 75 | // In: [0 .. pi/2] 76 | // Out: [0 .. 89] 77 | int theta_half_index(float theta_half) 78 | { 79 | if (theta_half <= 0.0) 80 | return 0; 81 | 82 | return clamp(int(sqrt(theta_half * (2.0/M_PI)) * BRDF_SAMPLING_RES_THETA_H), 0, BRDF_SAMPLING_RES_THETA_H-1); 83 | } 84 | 85 | 86 | // Lookup theta_diff index 87 | // In: [0 .. pi/2] 88 | // Out: [0 .. 89] 89 | int theta_diff_index(float theta_diff) 90 | { 91 | return clamp(int(theta_diff * (2.0/M_PI * BRDF_SAMPLING_RES_THETA_D)), 0, BRDF_SAMPLING_RES_THETA_D - 1); 92 | } 93 | 94 | 95 | vec3 BRDF( vec3 toLight, vec3 toViewer, vec3 normal, vec3 tangent, vec3 bitangent ) 96 | { 97 | vec3 H = normalize(toLight + toViewer); 98 | float theta_H = acos(clamp(dot(normal, H), 0, 1)); 99 | float theta_diff = acos(clamp(dot(H, toLight), 0, 1)); 100 | float phi_diff=0; 101 | 102 | if (dot(normal, toLight) <= 0.0) return vec3(0); 103 | if (dot(normal, toViewer) <= 0.0) return vec3(0); 104 | 105 | if (theta_diff < 1e-3) { 106 | // phi_diff indeterminate, use phi_half instead 107 | phi_diff = atan(clamp(-dot(toLight, bitangent), -1, 1), clamp(dot(toLight, tangent), -1, 1)); 108 | } 109 | else if (theta_H > 1e-3) { 110 | // use Gram-Schmidt orthonormalization to find diff basis vectors 111 | vec3 u = -normalize(normal - dot(normal,H) * H); 112 | vec3 v = cross(H, u); 113 | phi_diff = atan(clamp(dot(toLight,v), -1, 1), clamp(dot(toLight,u), -1, 1)); 114 | } 115 | else theta_H = 0; 116 | 117 | // Find index. 118 | // Note that phi_half is ignored, since isotropic BRDFs are assumed 119 | int ind = phi_diff_index(phi_diff) + 120 | theta_diff_index(theta_diff) * BRDF_SAMPLING_RES_PHI_D / 2 + 121 | theta_half_index(theta_H) * BRDF_SAMPLING_RES_PHI_D / 2 * 122 | BRDF_SAMPLING_RES_THETA_D; 123 | 124 | int redIndex = ind; 125 | int greenIndex = ind + BRDF_SAMPLING_RES_THETA_H*BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D/2; 126 | int blueIndex = ind + BRDF_SAMPLING_RES_THETA_H*BRDF_SAMPLING_RES_THETA_D*BRDF_SAMPLING_RES_PHI_D; 127 | 128 | #if 1 129 | return vec3( 130 | texelFetch(u_MerlSampler, redIndex).r * RED_SCALE, 131 | texelFetch(u_MerlSampler, greenIndex).r * GREEN_SCALE, 132 | texelFetch(u_MerlSampler, blueIndex).r * BLUE_SCALE 133 | ) * toLight.z; 134 | #endif 135 | } 136 | 137 | #undef M_PI 138 | 139 | -------------------------------------------------------------------------------- /plot-brdf/shaders/ggx.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | /* ggx.glsl - public domain GLSL library 3 | by Jonathan Dupuy 4 | 5 | This file provides utility functions for GGX BRDFs, which are used 6 | in the sphere light shading technique described in my paper 7 | "A Spherical Cap Preserving Parameterization for Spherical Distributions". 8 | */ 9 | 10 | // Evaluate GGX BRDF (brdf times cosine) 11 | float ggx_evalp(vec3 wi, vec3 wo, float alpha, out float pdf); 12 | 13 | // Importance sample a visible microfacet normal from direction wi 14 | // Note: the algorithm I use is an improvement over Eric Heit'z 15 | // algorithm. It relies on Shirley's concentric mapping and produces less 16 | // distortion. 17 | vec3 ggx_sample(vec2 u, vec3 wi, float alpha); 18 | 19 | // 20 | // 21 | //// end header file /////////////////////////////////////////////////////////// 22 | 23 | // ***************************************************************************** 24 | /** 25 | * GGX Functions 26 | * 27 | */ 28 | 29 | #define PI 3.141592654 30 | #define e0 vec3(0, 0, 1) 31 | 32 | float sat(float x) {return clamp(x, 0.0f, 1.0f);} 33 | vec3 g2(vec3 r, vec3 rp) { 34 | vec3 tmp = r - rp; 35 | vec3 cp1 = cross(r, rp); 36 | vec3 cp2 = cross(tmp, cp1); 37 | float dp = 1.0f - dot(r, rp); 38 | float sc = dp * dp + dot(cp1, cp1); 39 | 40 | return (dp * tmp + cp2) / sc; 41 | } 42 | vec3 g3(vec3 r) 43 | { 44 | float z = sqrt(sat(1.0f - dot(r.xy, r.xy))); 45 | 46 | return vec3(r.xy, z); 47 | } 48 | vec3 g3inv(vec3 w) 49 | { 50 | return vec3(w.xy, 0.0f); 51 | } 52 | float Jacobian(vec3 r, vec3 rp) 53 | { 54 | float num = 1.0f - dot(rp, rp); 55 | vec3 cp1 = cross(r, rp); 56 | float dp = 1.0f - dot(r, rp); 57 | float den = dp * dp + dot(cp1, cp1); 58 | 59 | return (num * num) / (den * den); 60 | } 61 | 62 | // ----------------------------------------------------------------------------- 63 | // Evaluation 64 | float ggx_evalp(vec3 wi, vec3 wo, float alpha, out float pdf) 65 | { 66 | if (wo.z > 0.0 && wi.z > 0.0) { 67 | #if 0 68 | vec3 wh = normalize(wi + wo); 69 | vec3 wh_xform = vec3(wh.xy / alpha, wh.z); 70 | vec3 wi_xform = vec3(wi.xy * alpha, wi.z); 71 | vec3 wo_xform = vec3(wo.xy * alpha, wo.z); 72 | float wh_xform_mag = length(wh_xform); 73 | float wi_xform_mag = length(wi_xform); 74 | float wo_xform_mag = length(wo_xform); 75 | wh_xform/= wh_xform_mag; // normalize 76 | wi_xform/= wi_xform_mag; // normalize 77 | wo_xform/= wo_xform_mag; // normalize 78 | float sigma_i = 0.5 + 0.5 * wi_xform.z; 79 | float sigma_o = 0.5 + 0.5 * wo_xform.z; 80 | float Gi = clamp(wi.z, 0.0, 1.0) / (sigma_i * wi_xform_mag); 81 | float Go = clamp(wo.z, 0.0, 1.0) / (sigma_o * wo_xform_mag); 82 | float J = alpha * alpha * wh_xform_mag * wh_xform_mag * wh_xform_mag; 83 | float Dvis = clamp(dot(wo_xform, wh_xform), 0.0, 1.0) / (sigma_o * PI * J); 84 | float Gcond = Gi / (Gi + Go - Gi * Go); 85 | float cos_theta_d = dot(wh, wo); 86 | 87 | pdf = (Dvis / (cos_theta_d * 4.0)); 88 | return pdf * Gcond; 89 | #else 90 | vec3 rp = g3inv(wi); 91 | vec3 r = g3inv(wo); 92 | 93 | return Jacobian(r, -rp) * wi.z / PI; 94 | 95 | #endif 96 | } 97 | pdf = 0.0; 98 | return 0.0; 99 | } 100 | 101 | // ----------------------------------------------------------------------------- 102 | // uniform to concentric disk 103 | vec2 ggx__u2_to_d2(vec2 u) 104 | { 105 | /* Concentric map code with less branching (by Dave Cline), see 106 | http://psgraphics.blogspot.ch/2011/01/improved-code-for-concentric-map.html */ 107 | float r1 = 2 * u.x - 1; 108 | float r2 = 2 * u.y - 1; 109 | float phi, r; 110 | 111 | if (r1 == 0 && r2 == 0) { 112 | r = phi = 0; 113 | } else if (r1 * r1 > r2 * r2) { 114 | r = r1; 115 | phi = (PI / 4) * (r2 / r1); 116 | } else { 117 | r = r2; 118 | phi = (PI / 2) - (r1 / r2) * (PI / 4); 119 | } 120 | 121 | return r * vec2(cos(phi), sin(phi)); 122 | } 123 | 124 | // ----------------------------------------------------------------------------- 125 | // uniform to half a concentric disk 126 | vec2 ggx__u2_to_hd2(vec2 u) 127 | { 128 | vec2 v = vec2((1 + u.x) / 2, u.y); 129 | return ggx__u2_to_d2(v); 130 | } 131 | 132 | // ----------------------------------------------------------------------------- 133 | // uniform to microfacet normal projected onto concentric disk 134 | vec2 ggx__u2_to_md2(vec2 u, float zi) 135 | { 136 | float a = 1.0f / (1.0f + zi); 137 | 138 | if (u.x > a) { 139 | float xu = (u.x - a) / (1.0f - a); // remap to [0, 1] 140 | 141 | return vec2(zi, 1) * ggx__u2_to_hd2(vec2(xu, u.y)); 142 | } else { 143 | float xu = (u.x - a) / a; // remap to [-1, 0] 144 | 145 | return ggx__u2_to_hd2(vec2(xu, u.y)); 146 | } 147 | } 148 | 149 | // ----------------------------------------------------------------------------- 150 | // concentric disk to microfacet normal 151 | vec3 ggx__d2_to_h2(vec2 d, float zi, float z_i) 152 | { 153 | vec3 z = vec3(z_i, 0, zi); 154 | vec3 y = vec3(0, 1, 0); 155 | vec3 x = vec3(zi, 0, -z_i); // cross(z, y) 156 | float tmp = clamp(1 - dot(d, d), 0.0, 1.0); 157 | vec3 wm = x * d.x + y * d.y + z * sqrt(tmp); 158 | 159 | return vec3(wm.x, wm.y, clamp(wm.z, 0.0, 1.0)); 160 | } 161 | 162 | // ----------------------------------------------------------------------------- 163 | vec3 ggx__u2_to_h2_std_radial(vec2 u, float zi, float z_i) 164 | { 165 | return ggx__d2_to_h2(ggx__u2_to_md2(u, zi), zi, z_i); 166 | } 167 | 168 | // ----------------------------------------------------------------------------- 169 | // standard GGX variate exploiting rotational symmetry 170 | vec3 ggx__u2_to_h2_std(vec2 u, vec3 wi) 171 | { 172 | float zi = wi.z; 173 | float z_i = sqrt(wi.x * wi.x + wi.y * wi.y); 174 | vec3 wm = ggx__u2_to_h2_std_radial(u, zi, z_i); 175 | 176 | // rotate for non-normal incidence 177 | if (z_i > 0) { 178 | float nrm = 1 / z_i; 179 | float c = wi.x * nrm; 180 | float s = wi.y * nrm; 181 | float x = c * wm.x - s * wm.y; 182 | float y = s * wm.x + c * wm.y; 183 | 184 | wm = vec3(x, y, wm.z); 185 | } 186 | 187 | return wm; 188 | } 189 | 190 | // ----------------------------------------------------------------------------- 191 | // warp the domain to match the standard GGX distribution 192 | // (note: works with anisotropic roughness) 193 | vec3 ggx__u2_to_h2(vec2 u, vec3 wi, float r1, float r2) 194 | { 195 | vec3 wi_std = normalize(vec3(r1, r2, 1) * wi); 196 | vec3 wm_std = ggx__u2_to_h2_std(u, wi_std); 197 | vec3 wm = normalize(vec3(r1, r2, 1) * wm_std); 198 | 199 | return wm; 200 | } 201 | 202 | vec3 sampleGGXVNDF(vec3 Ve, float alpha_x, float alpha_y, float U1, float U2) 203 | { 204 | // Section 3.2: transforming the view direction to the hemisphere configuration 205 | vec3 Vh = normalize(vec3(alpha_x * Ve.x, alpha_y * Ve.y, Ve.z)); 206 | // Section 4.1: orthonormal basis (with special case if cross product is zero) 207 | float lensq = Vh.x * Vh.x + Vh.y * Vh.y; 208 | vec3 T1 = lensq > 0 ? vec3(-Vh.y, Vh.x, 0) * inversesqrt(lensq) : vec3(1,0,0); 209 | vec3 T2 = cross(Vh, T1); 210 | // Section 4.2: parameterization of the projected area 211 | float r = sqrt(U1); 212 | float phi = 2.0 * PI * U2; 213 | float t1 = r * cos(phi); 214 | float t2 = r * sin(phi); 215 | float s = 0.5 * (1.0 + Vh.z); 216 | t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; 217 | // Section 4.3: reprojection onto hemisphere 218 | vec3 Nh = t1*T1 + t2*T2 + sqrt(max(0.0, 1.0 - t1*t1 - t2*t2))*Vh; 219 | // Section 3.4: transforming the normal back to the ellipsoid configuration 220 | vec3 Ne = normalize(vec3(alpha_x * Nh.x, alpha_y * Nh.y, max(0.0, Nh.z))); 221 | return Ne; 222 | } 223 | 224 | // ----------------------------------------------------------------------------- 225 | // importance sample: map the unit square to the hemisphere 226 | vec3 ggx_sample(vec2 u, vec3 wi, float alpha) 227 | { 228 | return sampleGGXVNDF(wi, alpha, alpha, u.x, u.y); 229 | return ggx__u2_to_h2(u, wi, alpha, alpha); 230 | } 231 | 232 | #undef PI 233 | 234 | -------------------------------------------------------------------------------- /plot-brdf/shaders/parametric.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | 8 | uniform float u_Exposure; 9 | uniform float u_Gamma; 10 | 11 | uniform float u_Alpha; 12 | uniform vec3 u_Dir; 13 | uniform vec4 u_Color; 14 | uniform sampler1D u_CmapSampler; 15 | 16 | struct Transform { 17 | mat4 modelView; 18 | mat4 projection; 19 | mat4 modelViewProjection; 20 | mat4 viewInv; 21 | }; 22 | 23 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 24 | uniform TransformBuffer { 25 | Transform u_Transform; 26 | }; 27 | 28 | // ============================================================================ 29 | 30 | vec3 evalBrdf(vec3 wi, vec3 wo) 31 | { 32 | float c = clamp(wi.z, 0.0, 1.0); 33 | #if BRDF_MERL 34 | return BRDF(wi, wo, vec3(0, 0, 1), vec3(1, 0, 0), vec3(0, 1, 0)); 35 | #else 36 | float pdf; 37 | return vec3(ggx_evalp(wi, wo, u_Alpha, pdf)) / wi.z; 38 | #endif 39 | } 40 | 41 | float sRGB(float linear) { 42 | if (linear > 1.0) { 43 | return 1.0; 44 | } else if (linear < 0.0) { 45 | return 0.0; 46 | } else if (linear < 0.0031308) { 47 | return 12.92 * linear; 48 | } else { 49 | return 1.055 * pow(linear, 0.41666) - 0.055; 50 | } 51 | } 52 | vec3 tonemap(vec3 x) { 53 | vec3 tmp = exp2(u_Exposure) * x; 54 | return vec3(sRGB(tmp.x), sRGB(tmp.y), sRGB(tmp.z)); 55 | } 56 | 57 | 58 | // ***************************************************************************** 59 | /** 60 | * Vertex Shader 61 | * 62 | * The shader outputs attributes relevant for shading in view space. 63 | */ 64 | #ifdef VERTEX_SHADER 65 | layout(location = 0) out vec2 o_TexCoord; 66 | 67 | void main(void) 68 | { 69 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 70 | gl_Position.xy = 2 * o_TexCoord - 1; 71 | gl_Position.zw = vec2(-0.95, 1); 72 | } 73 | #endif // VERTEX_SHADER 74 | 75 | // ***************************************************************************** 76 | /** 77 | * Fragment Shader 78 | * 79 | */ 80 | #ifdef FRAGMENT_SHADER 81 | layout(location = 0) in vec2 i_TexCoord; 82 | layout(location = 0) out vec4 o_FragColor; 83 | 84 | #define M_PI 3.14159 85 | 86 | void main(void) 87 | { 88 | float alpha = u_Alpha; 89 | float pdf = 0.0, dummy; 90 | vec3 wi = u_Dir; 91 | vec2 u = i_TexCoord;//floor(i_TexCoord * 32.0) / 32.0; 92 | vec3 wo = vec3(0, 0, 1); 93 | 94 | #if SCHEME_GGX 95 | vec3 wm = ggx_sample(u, wi, alpha * 0.65); 96 | 97 | wo = 2.0 * dot(wi, wm) * wm - wi; 98 | #else 99 | const float pi = 3.14159; 100 | float tm = u.x * u.x * pi / 2; 101 | float pm = u.y * pi * 2; 102 | float x = sin(tm) * cos(pm); 103 | float y = sin(tm) * sin(pm); 104 | float z = cos(tm); 105 | vec3 wm = vec3(x, y, z); 106 | 107 | wo = 2.0 * dot(wi, wm) * wm - wi; 108 | #endif 109 | 110 | #if SHADE_BRDF 111 | vec3 fr = evalBrdf(wi, wo); 112 | o_FragColor = vec4(u_Color.a * tonemap(fr), u_Color.a); 113 | 114 | #elif SHADE_CMAP 115 | vec3 wr = 2 * wi.z * vec3(0, 0, 1) - wi; 116 | vec3 fr = evalBrdf(wi, wo) / (evalBrdf(wi, wr)); 117 | float avg = dot(fr, vec3(1.0 / 3.0)); 118 | vec3 rgb = texture(u_CmapSampler, avg).rgb; 119 | //if (avg == 0) rgb*= 0.0; 120 | o_FragColor = vec4(rgb, u_Color.a); 121 | 122 | #elif SHADE_COLOR 123 | o_FragColor = u_Color; 124 | 125 | #else 126 | o_FragColor = vec4(1, 0, 0, 1); 127 | #endif 128 | } 129 | #endif // FRAGMENT_SHADER 130 | 131 | 132 | -------------------------------------------------------------------------------- /plot-brdf/shaders/samples.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | 8 | uniform vec3 u_Dir; 9 | uniform float u_Alpha; 10 | uniform float u_PointScale = 96.0 * 1.5; //48.0; 11 | 12 | struct Transform { 13 | mat4 modelView; 14 | mat4 projection; 15 | mat4 modelViewProjection; 16 | mat4 viewInv; 17 | }; 18 | 19 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 20 | uniform TransformBuffer { 21 | Transform u_Transform; 22 | }; 23 | 24 | // ***************************************************************************** 25 | /** 26 | * Vertex Shader 27 | * 28 | */ 29 | #ifdef VERTEX_SHADER 30 | //layout(location = 0) in vec2 i_Position; 31 | layout(location = 0) out vec3 o_Position; 32 | layout(location = 1) out vec3 o_Normal; 33 | 34 | void main(void) 35 | { 36 | vec2 u = vec2(gl_VertexID / 16, gl_VertexID % 16) / 15.0; 37 | vec3 wi = u_Dir; 38 | vec3 wo = vec3(0, 0, 1); 39 | 40 | #if SCHEME_GGX 41 | vec3 wm = ggx_sample(u, wi, u_Alpha); 42 | 43 | wo = 2.0 * dot(wi, wm) * wm - wi; 44 | #else 45 | const float pi = 3.14159; 46 | float tm = u.x * u.x * pi / 2; 47 | float pm = u.y * pi * 2; 48 | float x = sin(tm) * cos(pm); 49 | float y = sin(tm) * sin(pm); 50 | float z = cos(tm); 51 | vec3 wm = vec3(x, y, z); 52 | 53 | wo = 2.0 * dot(wi, wm) * wm - wi; 54 | #endif 55 | 56 | o_Position = (u_Transform.modelView * vec4(wo, 1)).xyz; 57 | o_Normal = (u_Transform.modelView * vec4(wo, 0)).xyz; 58 | gl_Position = u_Transform.modelViewProjection * vec4(wo, 1); 59 | gl_PointSize = u_PointScale / (1.0 + 4.0 * length(o_Position)); 60 | } 61 | #endif // VERTEX_SHADER 62 | 63 | 64 | // ***************************************************************************** 65 | /** 66 | * Fragment Shader 67 | * 68 | */ 69 | #ifdef FRAGMENT_SHADER 70 | layout(location = 0) in vec3 i_Position; 71 | layout(location = 1) in vec3 i_Normal; 72 | 73 | layout(location = 0) out vec4 o_FragColor; 74 | 75 | void main(void) 76 | { 77 | vec3 wo = normalize(i_Position); 78 | vec3 wn = normalize(i_Normal); 79 | vec2 P = vec2(gl_PointCoord.x, 1.0 - gl_PointCoord.y); 80 | vec2 p = 2.0 * P - 1.0; 81 | float r = dot(p, p); 82 | vec4 wnw = u_Transform.viewInv * vec4(wn, 0); 83 | if (r > 1.0 || wnw.z < 0.0) 84 | discard; 85 | 86 | float alpha = dot(wn, wo) > 0.1 ? 1.0 : 0.0; 87 | 88 | o_FragColor = vec4(mix(vec3(0.2), vec3(0.7), alpha), 1); 89 | } 90 | #endif // FRAGMENT_SHADER 91 | 92 | 93 | -------------------------------------------------------------------------------- /plot-brdf/shaders/sphere.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | uniform int u_SamplesPerPass; 8 | 9 | 10 | struct Transform { 11 | mat4 modelView; 12 | mat4 projection; 13 | mat4 modelViewProjection; 14 | mat4 viewInv; 15 | }; 16 | 17 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 18 | uniform Transforms { 19 | Transform u_Transform; 20 | }; 21 | 22 | layout(std140, binding = BUFFER_BINDING_RANDOM) 23 | uniform Random { 24 | vec4 value[64]; 25 | } u_Random; 26 | 27 | vec4 rand(int idx) { return u_Random.value[idx]; } 28 | float hash(vec2 p) 29 | { 30 | float h = dot(p, vec2(127.1, 311.7)); 31 | return fract(sin(h) * 43758.5453123); 32 | } 33 | 34 | uniform float u_Exposure; 35 | uniform float u_Gamma; 36 | 37 | uniform float u_Alpha; 38 | uniform vec3 u_Dir; 39 | uniform vec4 u_Color; 40 | uniform sampler1D u_CmapSampler; 41 | 42 | // ============================================================================ 43 | 44 | vec3 evalBrdf(vec3 wi, vec3 wo) 45 | { 46 | float c = clamp(wi.z, 0.0, 1.0); 47 | #if BRDF_MERL 48 | return BRDF(wo, wi, vec3(0, 0, 1), vec3(1, 0, 0), vec3(0, 1, 0)); 49 | #else 50 | float pdf; 51 | return vec3(ggx_evalp(wo, wi, u_Alpha, pdf)); 52 | #endif 53 | } 54 | 55 | float sRGB(float linear) { 56 | if (linear > 1.0) { 57 | return 1.0; 58 | } else if (linear < 0.0) { 59 | return 0.0; 60 | } else if (linear < 0.0031308) { 61 | return 12.92 * linear; 62 | } else { 63 | return 1.055 * pow(linear, 0.41666) - 0.055; 64 | } 65 | } 66 | vec3 tonemap(vec3 x) { 67 | vec3 tmp = exp2(u_Exposure) * x; 68 | return vec3(sRGB(tmp.x), sRGB(tmp.y), sRGB(tmp.z)); 69 | } 70 | 71 | 72 | 73 | // ============================================================================ 74 | 75 | // ***************************************************************************** 76 | /** 77 | * Vertex Shader 78 | * 79 | * The shader outputs attributes relevant for shading in view space. 80 | */ 81 | #ifdef VERTEX_SHADER 82 | layout(location = 0) in vec4 i_Position; 83 | layout(location = 1) in vec4 i_TexCoord; 84 | layout(location = 2) in vec4 i_Tangent1; 85 | layout(location = 3) in vec4 i_Tangent2; 86 | layout(location = 0) out vec4 o_Position; 87 | layout(location = 1) out vec4 o_TexCoord; 88 | layout(location = 2) out vec4 o_Tangent1; 89 | layout(location = 3) out vec4 o_Tangent2; 90 | 91 | void main(void) 92 | { 93 | o_Position = u_Transform.viewInv * u_Transform.modelView * i_Position; 94 | o_TexCoord = i_TexCoord; 95 | o_Tangent1 = u_Transform.viewInv * u_Transform.modelView * i_Tangent1; 96 | o_Tangent2 = u_Transform.viewInv * u_Transform.modelView * i_Tangent2; 97 | 98 | gl_Position = u_Transform.modelViewProjection * i_Position; 99 | } 100 | #endif // VERTEX_SHADER 101 | 102 | // ***************************************************************************** 103 | /** 104 | * Fragment Shader 105 | * 106 | */ 107 | #ifdef FRAGMENT_SHADER 108 | layout(location = 0) in vec4 i_Position; 109 | layout(location = 1) in vec4 i_TexCoord; 110 | layout(location = 2) in vec4 i_Tangent1; 111 | layout(location = 3) in vec4 i_Tangent2; 112 | layout(location = 0) out vec4 o_FragColor; 113 | 114 | void main(void) 115 | { 116 | vec3 wi = u_Dir; 117 | vec3 wo = normalize(i_Position.xyz); 118 | // ----------------------------------------------------------------------------- 119 | /** 120 | * Shading with Importance Sampling 121 | * 122 | */ 123 | #if SHADE_BRDF 124 | vec3 fr = evalBrdf(wi, wo); 125 | 126 | o_FragColor = vec4(u_Color.a * tonemap(fr), u_Color.a); 127 | 128 | // ----------------------------------------------------------------------------- 129 | /** 130 | * Color Shading 131 | * 132 | * Outputs a flat color 133 | */ 134 | #elif SHADE_CMAP 135 | vec3 wr = 2 * wi.z * vec3(0, 0, 1) - wi; 136 | vec3 fr = evalBrdf(wi, wo) / (evalBrdf(wi, wr)); 137 | float avg = dot(fr, vec3(1.0 / 3.0)); 138 | vec3 rgb = texture(u_CmapSampler, avg).rgb; 139 | 140 | o_FragColor = vec4(rgb, u_Color.a); 141 | 142 | 143 | // ----------------------------------------------------------------------------- 144 | /** 145 | * Color Shading 146 | * 147 | * Outputs a flat color 148 | */ 149 | #elif SHADE_COLOR 150 | o_FragColor = u_Color; 151 | 152 | 153 | // ----------------------------------------------------------------------------- 154 | /** 155 | * Debug Shading 156 | * 157 | * Do whatever you like in here. 158 | */ 159 | #elif SHADE_DEBUG 160 | #if 0 161 | float h = hash(gl_FragCoord.xy); 162 | float u = mod(h + rand(0).x, 1.0); 163 | if (u > 0.5) 164 | o_FragColor = vec4(vec3(1, 0, 1), 0.5); 165 | else 166 | o_FragColor = vec4(vec3(0, 1, 0), 0.5); 167 | #else 168 | o_FragColor = u_Color; 169 | #endif 170 | 171 | // ----------------------------------------------------------------------------- 172 | /** 173 | * Default Shading: flat red 174 | * 175 | * 176 | */ 177 | #else 178 | o_FragColor = vec4(1, 0, 0, 1); 179 | 180 | 181 | // ----------------------------------------------------------------------------- 182 | #endif // SHADE 183 | 184 | } 185 | #endif // FRAGMENT_SHADER 186 | 187 | 188 | -------------------------------------------------------------------------------- /plot-brdf/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | uniform vec3 u_Viewport; 2 | 3 | #if MSAA_FACTOR 4 | uniform sampler2DMS u_FramebufferSampler; 5 | #else 6 | uniform sampler2D u_FramebufferSampler; 7 | #endif 8 | 9 | // ------------------------------------------------------------------------------------------------- 10 | /** 11 | * Vertex Shader 12 | * 13 | * This vertex shader draws a fullscreen quad 14 | */ 15 | #ifdef VERTEX_SHADER 16 | layout(location = 0) out vec2 o_TexCoord; 17 | 18 | void main(void) 19 | { 20 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 21 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 22 | } 23 | #endif 24 | 25 | // ------------------------------------------------------------------------------------------------- 26 | /** 27 | * Fragment Shader 28 | * 29 | * This fragment shader post-processes the scene framebuffer by applying 30 | * tone mapping, gamma correction and image scaling. 31 | */ 32 | #ifdef FRAGMENT_SHADER 33 | layout(location = 0) in vec2 i_TexCoord; 34 | layout(location = 0) out vec4 o_FragColor; 35 | 36 | void main(void) 37 | { 38 | vec4 color = vec4(0); 39 | ivec2 P = ivec2(gl_FragCoord.xy); 40 | 41 | // get framebuffer data 42 | #if MSAA_FACTOR 43 | for (int i = 0; i < MSAA_FACTOR; ++i) { 44 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 45 | color+= c; 46 | } 47 | color/= vec4(MSAA_FACTOR); 48 | #else 49 | color = texelFetch(u_FramebufferSampler, P, 0); 50 | #endif 51 | if (color.a > 1.0) color.rgb/= (color.a - 1.0); 52 | 53 | // make fragments store positive values 54 | if (any(lessThan(color.rgb, vec3(0)))) { 55 | o_FragColor = vec4(1, 0, 0, 1); 56 | return; 57 | } 58 | 59 | // final color 60 | o_FragColor = vec4(color.rgb, 1.0); 61 | 62 | // make sure the fragments store real values 63 | if (any(isnan(color.rgb))) 64 | o_FragColor = vec4(1, 0, 0, 1); 65 | 66 | //o_FragColor = vec4(o_FragColor.aaa / 2.0, 1); 67 | } 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /plot-brdf/shaders/wi_angle.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | 8 | uniform vec3 u_Dir; 9 | 10 | struct Transform { 11 | mat4 modelView; 12 | mat4 projection; 13 | mat4 modelViewProjection; 14 | mat4 viewInv; 15 | }; 16 | 17 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 18 | uniform TransformBuffer { 19 | Transform u_Transform; 20 | }; 21 | 22 | // ***************************************************************************** 23 | /** 24 | * Vertex Shader 25 | * 26 | * The shader outputs attributes relevant for shading in view space. 27 | */ 28 | #ifdef VERTEX_SHADER 29 | layout(location = 0) in vec4 i_Position; 30 | 31 | void main(void) 32 | { 33 | int cnt = VERTEX_CNT; 34 | vec3 position = vec3(0); 35 | 36 | if (gl_VertexID < cnt - 1) { 37 | vec2 azimuthal = normalize(u_Dir.xy); 38 | float u = float(gl_VertexID) / float(cnt - 2); 39 | float cosTheta = cos(acos(u_Dir.z) * u); 40 | float sinTheta = sqrt(1.0 - cosTheta * cosTheta); 41 | vec2 p = vec2(sinTheta, 0); 42 | mat2 R = mat2(azimuthal.x, azimuthal.y, 43 | -azimuthal.y, azimuthal.x); 44 | 45 | position = vec3(R * p, cosTheta); 46 | } 47 | 48 | gl_Position = u_Transform.modelViewProjection 49 | * vec4(position * 1.001, 1); 50 | } 51 | #endif // VERTEX_SHADER 52 | 53 | // ***************************************************************************** 54 | /** 55 | * Fragment Shader 56 | * 57 | */ 58 | #ifdef FRAGMENT_SHADER 59 | layout(location = 0) out vec4 o_FragColor; 60 | 61 | void main(void) 62 | { 63 | const vec3 blue = vec3(60.0/255.0, 128.0/255.0, 1.0); 64 | o_FragColor = vec4(vec3(0), 1); 65 | } 66 | #endif // FRAGMENT_SHADER 67 | 68 | 69 | -------------------------------------------------------------------------------- /plot-brdf/shaders/wi_dir.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | 8 | uniform vec3 u_Dir; 9 | 10 | struct Transform { 11 | mat4 modelView; 12 | mat4 projection; 13 | mat4 modelViewProjection; 14 | mat4 viewInv; 15 | }; 16 | 17 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 18 | uniform TransformBuffer { 19 | Transform u_Transform; 20 | }; 21 | 22 | // ***************************************************************************** 23 | /** 24 | * Vertex Shader 25 | * 26 | * The shader outputs attributes relevant for shading in view space. 27 | */ 28 | #ifdef VERTEX_SHADER 29 | void main(void) 30 | { 31 | vec3 p = gl_VertexID * u_Dir * 1.5; 32 | gl_Position = u_Transform.modelViewProjection * vec4(p, 1); 33 | } 34 | #endif // VERTEX_SHADER 35 | 36 | // ***************************************************************************** 37 | /** 38 | * Fragment Shader 39 | * 40 | */ 41 | #ifdef FRAGMENT_SHADER 42 | layout(location = 0) out vec4 o_FragColor; 43 | 44 | void main(void) 45 | { 46 | o_FragColor = vec4(vec3(0), 1); 47 | } 48 | #endif // FRAGMENT_SHADER 49 | 50 | 51 | -------------------------------------------------------------------------------- /plot-brdf/shaders/wire.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | // ***************************************************************************** 3 | /** 4 | * Uniforms 5 | * 6 | */ 7 | uniform int u_InstanceCount; 8 | 9 | struct Transform { 10 | mat4 modelView; 11 | mat4 projection; 12 | mat4 modelViewProjection; 13 | mat4 viewInv; 14 | }; 15 | 16 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 17 | uniform TransformBuffer { 18 | Transform u_Transform; 19 | }; 20 | 21 | // ***************************************************************************** 22 | /** 23 | * Vertex Shader 24 | * 25 | * The shader outputs attributes relevant for shading in view space. 26 | */ 27 | #ifdef VERTEX_SHADER 28 | layout(location = 0) in vec4 i_Position; 29 | layout(location = 0) out vec4 o_Position; 30 | layout(location = 1) out vec4 o_Normal; 31 | 32 | void main(void) 33 | { 34 | vec3 position; 35 | if (gl_InstanceID < u_InstanceCount) { 36 | // elevation slices 37 | float u = float(gl_InstanceID + 1) / float(u_InstanceCount + 1); 38 | float theta = u * 3.14159; 39 | float sinTheta = sin(theta); 40 | float cosTheta = cos(theta); 41 | 42 | position = vec3(sinTheta * i_Position.xy, cosTheta); 43 | } else { 44 | // azimuthal slices 45 | int instanceID = gl_InstanceID - u_InstanceCount; 46 | int instanceCnt = u_InstanceCount; 47 | float u = float(instanceID) / float(instanceCnt); 48 | float phi = 3.14159 * u; 49 | float cosPhi = cos(phi); 50 | float sinPhi = sin(phi); 51 | mat2 r = mat2(cosPhi, -sinPhi, sinPhi, cosPhi); 52 | 53 | position = vec3(r * i_Position.xz, i_Position.y); 54 | } 55 | 56 | vec3 normal = position; 57 | o_Position = u_Transform.modelView * vec4(normalize(position), 1); 58 | o_Normal = u_Transform.modelView * vec4(normalize(normal), 0); 59 | 60 | gl_Position = u_Transform.modelViewProjection * vec4(position, 1); 61 | } 62 | #endif // VERTEX_SHADER 63 | 64 | // ***************************************************************************** 65 | /** 66 | * Fragment Shader 67 | * 68 | */ 69 | #ifdef FRAGMENT_SHADER 70 | layout(location = 0) in vec4 i_Position; 71 | layout(location = 1) in vec4 i_Normal; 72 | layout(location = 0) out vec4 o_FragColor; 73 | 74 | void main(void) 75 | { 76 | vec3 wo = normalize(i_Position.xyz); 77 | vec3 wn = normalize(i_Normal.xyz); 78 | vec4 wnw = u_Transform.viewInv * vec4(wn, 0); 79 | if (wnw.z < 0.0) 80 | discard; 81 | float alpha = dot(wn, wo) > 0.1 ? 1.0 : 0.5; 82 | o_FragColor = vec4(mix(vec3(0.5), vec3(0.9), alpha), 1); 83 | } 84 | #endif // FRAGMENT_SHADER 85 | 86 | 87 | -------------------------------------------------------------------------------- /plot-brdf/vidgen.py: -------------------------------------------------------------------------------- 1 | import sys,os 2 | import numpy as np 3 | 4 | fps = 60 5 | dur = 5 6 | cnt = dur * fps 7 | 8 | gen_wi = False 9 | gen_wo = False 10 | gen_ggx_cmap = False 11 | gen_ggx_samples_merl = False 12 | gen_ggx_samples_ggx = False 13 | gen_parametric_merl = True 14 | gen_parametric_ggx = True 15 | 16 | def smoothstep(u): 17 | return 3 * u**2 - 2 * u**3 18 | 19 | def smootherstep(u): 20 | return 6 * u**5 - 15 * u**4 + 10 * u**3 21 | 22 | # render and export images 23 | for i in range(cnt): 24 | u = float(i) / float(cnt - 1) 25 | # render the angle 26 | tmp = np.sin(smootherstep(u) * 2.0 * np.pi) 27 | ang = 45 + tmp * 30 28 | alpha = 0.3 + tmp * 0.25 29 | 30 | def renderToFrame(prefix): 31 | bmpFile = 'capture_00_000000000.bmp' 32 | frameName = prefix + '_' + format(i, '09') 33 | frameBmpName = frameName + '.bmp' 34 | cmd = 'mv ' + bmpFile + ' ' + frameBmpName 35 | os.system(cmd) 36 | cmd = 'mogrify -format png ' + frameBmpName + ' && rm ' + frameBmpName 37 | os.system(cmd) 38 | 39 | if (gen_wi): 40 | cmd = \ 41 | './plot-brdf --frame-limit 1 --no-hud --hidden --record \ 42 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-samples \ 43 | --dir ' + str(ang) + ' 255 --color 0.1 0.5 0.1 0.25' 44 | os.system(cmd) 45 | renderToFrame('wi') 46 | 47 | if (gen_wo): 48 | cmd = \ 49 | './plot-brdf --frame-limit 1 --no-hud --hidden --record \ 50 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-wi-helper \ 51 | --dir ' + str(ang) + ' 255 --color 0.0 0.2 0.7 0.25' 52 | os.system(cmd) 53 | renderToFrame('wo') 54 | 55 | if (gen_ggx_cmap): 56 | cmd = \ 57 | './plot-brdf --frame-limit 1 --no-hud --hidden --record \ 58 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-wi-helper \ 59 | --disable-sphere-samples --dir ' + str(ang) + ' 255 --color 0.1 0.5 0.1 0.7 \ 60 | --shading-cmap --alpha 0.3 --cmap ../plot-brdf/cmaps/divergent.png' 61 | os.system(cmd) 62 | renderToFrame('ggx_cmap') 63 | 64 | if (gen_ggx_samples_merl): 65 | cmd = \ 66 | './plot-brdf --frame-limit 1 --no-hud --hidden --record --scheme-merl \ 67 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-wi-helper \ 68 | --enable-sphere-samples --dir 45 255 --color 0.1 0.5 0.1 0.7 \ 69 | --shading-cmap --alpha ' + str(alpha) + ' --cmap ../plot-brdf/cmaps/divergent.png' 70 | os.system(cmd) 71 | renderToFrame('ggx_samples_merl') 72 | 73 | if (gen_ggx_samples_ggx): 74 | cmd = \ 75 | './plot-brdf --frame-limit 1 --no-hud --hidden --record --scheme-ggx \ 76 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-wi-helper \ 77 | --enable-sphere-samples --dir 45 255 --color 0.1 0.5 0.1 0.7 \ 78 | --shading-cmap --alpha ' + str(alpha) + ' --cmap ../plot-brdf/cmaps/divergent.png' 79 | os.system(cmd) 80 | renderToFrame('ggx_samples_ggx') 81 | 82 | if (gen_parametric_merl): 83 | cmd = \ 84 | './plot-brdf --frame-limit 1 --no-hud --hidden --record --scheme-merl \ 85 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-wi-helper \ 86 | --enable-parametric --dir 45 255 --color 0.1 0.5 0.1 0.7 \ 87 | --shading-cmap --alpha ' + str(alpha) + ' --cmap ../plot-brdf/cmaps/divergent.png' 88 | os.system(cmd) 89 | renderToFrame('parametric_merl') 90 | 91 | if (gen_parametric_ggx): 92 | cmd = \ 93 | './plot-brdf --frame-limit 1 --no-hud --hidden --record --scheme-ggx \ 94 | --shader-dir ../plot-brdf/shaders/ --disable-sphere-wi-helper \ 95 | --enable-parametric --dir 45 255 --color 0.1 0.5 0.1 0.7 \ 96 | --shading-cmap --alpha ' + str(alpha) + ' --cmap ../plot-brdf/cmaps/divergent.png' 97 | os.system(cmd) 98 | renderToFrame('parametric_ggx') 99 | 100 | if (gen_wi): 101 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i wi_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_wi.mp4' 102 | os.system(cmd) 103 | if (gen_wo): 104 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i wo_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_wo.mp4' 105 | os.system(cmd) 106 | if (gen_ggx_cmap): 107 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i ggx_cmap_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_ggx_cmap.mp4' 108 | os.system(cmd) 109 | if (gen_ggx_samples_merl): 110 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i ggx_samples_merl_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_ggx_samples_merl.mp4' 111 | os.system(cmd) 112 | if (gen_ggx_samples_ggx): 113 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i ggx_samples_ggx_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_ggx_samples_ggx.mp4' 114 | os.system(cmd) 115 | if (gen_parametric_merl): 116 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i parametric_merl_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_parametric_merl.mp4' 117 | os.system(cmd) 118 | if (gen_parametric_ggx): 119 | cmd = 'avconv -y -r ' + str(fps) + ' -f image2 -i parametric_ggx_%09d.png -c:v libx264 -crf 20 -pix_fmt yuv420p video_parametric_ggx.mp4' 120 | os.system(cmd) 121 | 122 | --------------------------------------------------------------------------------