├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── src ├── gui.cpp ├── gui.h ├── index.cpp ├── index.h ├── main.cpp ├── model.cpp ├── model.h ├── pool.h ├── program.cpp ├── program.h ├── sphere.cpp ├── sphere.h ├── stl.cpp ├── stl.h ├── triangle.cpp ├── triangle.h ├── util.cpp └── util.h └── tracer ├── .gitignore ├── Makefile └── src ├── camera.cpp ├── camera.h ├── colormap.cpp ├── colormap.h ├── config.h ├── embree.cpp ├── embree.h ├── hit.cpp ├── hit.h ├── image.cpp ├── image.h ├── main.cpp ├── material.h ├── microfacet.h ├── onb.h ├── ray.h ├── sphere.cpp ├── sphere.h ├── stl.cpp ├── stl.h ├── texture.h ├── util.h └── vec3.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /bin 3 | /build 4 | /main 5 | *.stl 6 | 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019 Michael Fogleman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #### PROJECT SETTINGS #### 2 | # The name of the executable to be created 3 | BIN_NAME := main 4 | # Compiler used 5 | C ?= g++ 6 | # Extension of source files used in the project 7 | SRC_EXT = cpp 8 | # Path to the source directory, relative to the makefile 9 | SRC_PATH = src 10 | # General compiler flags 11 | COMPILE_FLAGS = -std=c++14 -flto -O3 -Wall -Wextra -Wno-sign-compare -march=native 12 | # Additional release-specific flags 13 | RCOMPILE_FLAGS = -D NDEBUG 14 | # Additional debug-specific flags 15 | DCOMPILE_FLAGS = -D DEBUG 16 | # Add additional include paths 17 | INCLUDES = -I $(SRC_PATH) 18 | # General linker settings 19 | LINK_FLAGS = -flto -O3 -lpthread -lglfw -framework OpenGL 20 | # Additional release-specific linker settings 21 | RLINK_FLAGS = 22 | # Additional debug-specific linker settings 23 | DLINK_FLAGS = 24 | # Destination directory, like a jail or mounted system 25 | DESTDIR = / 26 | # Install path (bin/ is appended automatically) 27 | INSTALL_PREFIX = usr/local 28 | #### END PROJECT SETTINGS #### 29 | 30 | # Generally should not need to edit below this line 31 | 32 | # Shell used in this makefile 33 | # bash is used for 'echo -en' 34 | SHELL = /bin/bash 35 | # Clear built-in rules 36 | .SUFFIXES: 37 | # Programs for installation 38 | INSTALL = install 39 | INSTALL_PROGRAM = $(INSTALL) 40 | INSTALL_DATA = $(INSTALL) -m 644 41 | 42 | # Verbose option, to output compile and link commands 43 | export V := false 44 | export CMD_PREFIX := @ 45 | ifeq ($(V),true) 46 | CMD_PREFIX := 47 | endif 48 | 49 | # Combine compiler and linker flags 50 | release: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS) 51 | release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS) 52 | debug: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS) 53 | debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS) 54 | 55 | # Build and output paths 56 | release: export BUILD_PATH := build/release 57 | release: export BIN_PATH := bin/release 58 | debug: export BUILD_PATH := build/debug 59 | debug: export BIN_PATH := bin/debug 60 | install: export BIN_PATH := bin/release 61 | 62 | # Find all source files in the source directory, sorted by most 63 | # recently modified 64 | SOURCES = $(shell find $(SRC_PATH)/ -name '*.$(SRC_EXT)' \ 65 | | sort -k 1nr | cut -f2-) 66 | # fallback in case the above fails 67 | rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \ 68 | $(filter $(subst *,%,$2), $d)) 69 | ifeq ($(SOURCES),) 70 | SOURCES := $(call rwildcard, $(SRC_PATH)/, *.$(SRC_EXT)) 71 | endif 72 | 73 | # Set the object file names, with the source directory stripped 74 | # from the path, and the build path prepended in its place 75 | OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o) 76 | # Set the dependency files that will be used to add header dependencies 77 | DEPS = $(OBJECTS:.o=.d) 78 | 79 | # Macros for timing compilation 80 | TIME_FILE = $(dir $@).$(notdir $@)_time 81 | START_TIME = date '+%s' > $(TIME_FILE) 82 | END_TIME = read st < $(TIME_FILE) ; \ 83 | $(RM) $(TIME_FILE) ; \ 84 | st=$$((`date '+%s'` - $$st - 86400)) ; \ 85 | echo `date -u -d @$$st '+%H:%M:%S'` 86 | 87 | # Version macros 88 | # Comment/remove this section to remove versioning 89 | USE_VERSION := false 90 | # If this isn't a git repo or the repo has no tags, git describe will return non-zero 91 | ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0) 92 | USE_VERSION := true 93 | VERSION := $(shell git describe --tags --long --dirty --always | \ 94 | sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g') 95 | VERSION_MAJOR := $(word 1, $(VERSION)) 96 | VERSION_MINOR := $(word 2, $(VERSION)) 97 | VERSION_PATCH := $(word 3, $(VERSION)) 98 | VERSION_REVISION := $(word 4, $(VERSION)) 99 | VERSION_HASH := $(word 5, $(VERSION)) 100 | VERSION_STRING := \ 101 | "$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)" 102 | override CFLAGS := $(CFLAGS) \ 103 | -D VERSION_MAJOR=$(VERSION_MAJOR) \ 104 | -D VERSION_MINOR=$(VERSION_MINOR) \ 105 | -D VERSION_PATCH=$(VERSION_PATCH) \ 106 | -D VERSION_REVISION=$(VERSION_REVISION) \ 107 | -D VERSION_HASH=\"$(VERSION_HASH)\" 108 | endif 109 | 110 | # Standard, non-optimized release build 111 | .PHONY: release 112 | release: dirs 113 | ifeq ($(USE_VERSION), true) 114 | @echo "Beginning release build v$(VERSION_STRING)" 115 | else 116 | @echo "Beginning release build" 117 | endif 118 | @$(MAKE) all --no-print-directory 119 | 120 | # Debug build for gdb debugging 121 | .PHONY: debug 122 | debug: dirs 123 | ifeq ($(USE_VERSION), true) 124 | @echo "Beginning debug build v$(VERSION_STRING)" 125 | else 126 | @echo "Beginning debug build" 127 | endif 128 | @$(MAKE) all --no-print-directory 129 | 130 | # Create the directories used in the build 131 | .PHONY: dirs 132 | dirs: 133 | @echo "Creating directories" 134 | @mkdir -p $(dir $(OBJECTS)) 135 | @mkdir -p $(BIN_PATH) 136 | 137 | # Installs to the set path 138 | .PHONY: install 139 | install: 140 | @echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin" 141 | @$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin 142 | 143 | # Uninstalls the program 144 | .PHONY: uninstall 145 | uninstall: 146 | @echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)" 147 | @$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME) 148 | 149 | # Removes all build files 150 | .PHONY: clean 151 | clean: 152 | @echo "Deleting $(BIN_NAME) symlink" 153 | @$(RM) $(BIN_NAME) 154 | @echo "Deleting directories" 155 | @$(RM) -r build 156 | @$(RM) -r bin 157 | 158 | # Main rule, checks the executable and symlinks to the output 159 | all: $(BIN_PATH)/$(BIN_NAME) 160 | @echo "Making symlink: $(BIN_NAME) -> $<" 161 | @$(RM) $(BIN_NAME) 162 | @ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME) 163 | 164 | # Link the executable 165 | $(BIN_PATH)/$(BIN_NAME): $(OBJECTS) 166 | @echo "Linking: $@" 167 | $(CMD_PREFIX)$(C) $(OBJECTS) $(LDFLAGS) -o $@ 168 | 169 | # Add dependency files, if they exist 170 | -include $(DEPS) 171 | 172 | # Source file rules 173 | # After the first compilation they will be joined with the rules from the 174 | # dependency files to provide header dependencies 175 | $(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT) 176 | @echo "Compiling: $< -> $@" 177 | $(CMD_PREFIX)$(C) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@ 178 | 179 | .PHONY: run 180 | run: release 181 | time ./$(BIN_NAME) 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cellular Forms 2 | 3 | An implementation of Andy Lomas' [Cellular Forms](http://www.andylomas.com/extra/andylomas_paper_cellular_forms_aisb50.pdf). 4 | 5 | ### Dependencies 6 | 7 | - [boost](https://www.boost.org/) 8 | - [glfw](https://www.glfw.org/) 9 | - [glm](https://glm.g-truc.net/) 10 | 11 | ### Usage 12 | 13 | $ make 14 | $ ./main 15 | 16 | ![Example](https://www.michaelfogleman.com/static/cellular-forms/2.png) 17 | -------------------------------------------------------------------------------- /src/gui.cpp: -------------------------------------------------------------------------------- 1 | #define GL_SILENCE_DEPRECATION 2 | #define GLM_ENABLE_EXPERIMENTAL 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "gui.h" 14 | #include "pool.h" 15 | #include "program.h" 16 | #include "stl.h" 17 | 18 | const int ITERATIONS_PER_FRAME = 10; 19 | 20 | const std::string VertexSource = R"( 21 | #version 120 22 | 23 | uniform mat4 matrix; 24 | 25 | attribute vec4 position; 26 | attribute vec3 normal; 27 | attribute float value; 28 | 29 | varying vec3 ec_pos; 30 | varying vec3 ec_normal; 31 | varying float ec_value; 32 | 33 | void main() { 34 | gl_Position = matrix * position; 35 | ec_pos = vec3(gl_Position); 36 | ec_normal = normal; 37 | ec_value = value; 38 | } 39 | )"; 40 | 41 | const std::string FragmentSource = R"( 42 | #version 120 43 | 44 | varying vec3 ec_pos; 45 | varying vec3 ec_normal; 46 | varying float ec_value; 47 | 48 | const vec3 light_direction0 = normalize(vec3(0.5, -2, 1)); 49 | const vec3 light_direction1 = normalize(vec3(-0.5, -1, 1)); 50 | const vec3 color1 = vec3(0.59, 0.93, 0.54); 51 | const vec3 color0 = color1 * 0.1;//, 0.15, 0.11); 52 | 53 | void main() { 54 | vec3 normal = ec_normal; 55 | normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos))); 56 | float diffuse0 = max(0, dot(normal, light_direction0)); 57 | float diffuse1 = max(0, dot(normal, light_direction1)); 58 | float diffuse = diffuse0 * 0.75 + diffuse1 * 0.25; 59 | vec3 valueColor = vec3(ec_value * 0.8 + 0.2); 60 | valueColor = color1; 61 | vec3 color = mix(color0, valueColor, diffuse); 62 | gl_FragColor = vec4(color, 1); 63 | } 64 | )"; 65 | 66 | void RunGUI(Model &model) { 67 | auto startTime = std::chrono::steady_clock::now(); 68 | std::chrono::duration elapsed; 69 | 70 | ThreadPool pool; 71 | 72 | for (int i = 0; i < 100; i++) { 73 | model.Update(pool, false); 74 | } 75 | 76 | if (!glfwInit()) { 77 | return; 78 | } 79 | 80 | glfwWindowHint(GLFW_SAMPLES, 4); 81 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 82 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); 83 | GLFWwindow *window = glfwCreateWindow( 84 | 1600, 1200, "Cellular Forms", NULL, NULL); 85 | if (!window) { 86 | glfwTerminate(); 87 | return; 88 | } 89 | 90 | glfwMakeContextCurrent(window); 91 | 92 | glEnable(GL_DEPTH_TEST); 93 | glEnable(GL_CULL_FACE); 94 | glCullFace(GL_BACK); 95 | glClearColor((float)0x2a/255, (float)0x2c/255, (float)0x2b/255, 1); 96 | 97 | Program program(VertexSource, FragmentSource); 98 | 99 | const auto positionAttrib = program.GetAttribLocation("position"); 100 | const auto normalAttrib = program.GetAttribLocation("normal"); 101 | const auto valueAttrib = program.GetAttribLocation("value"); 102 | const auto matrixUniform = program.GetUniformLocation("matrix"); 103 | 104 | GLuint arrayBuffer; 105 | GLuint elementBuffer; 106 | glGenBuffers(1, &arrayBuffer); 107 | glGenBuffers(1, &elementBuffer); 108 | 109 | glm::vec3 targetMin, targetMax; 110 | model.Bounds(targetMin, targetMax); 111 | glm::vec3 currentMin = targetMin; 112 | glm::vec3 currentMax = targetMax; 113 | const glm::vec3 minSize(glm::distance(targetMin, targetMax) * 5); 114 | 115 | const auto getModelTransform = [&]() { 116 | glm::vec3 min, max; 117 | model.Bounds(min, max); 118 | targetMin = glm::min(targetMin, min); 119 | targetMax = glm::max(targetMax, max); 120 | currentMin += (targetMin - currentMin) * 0.01f; 121 | currentMax += (targetMax - currentMax) * 0.01f; 122 | currentMin = glm::min(currentMin, -minSize); 123 | currentMax = glm::max(currentMax, minSize); 124 | const glm::vec3 size = currentMax - currentMin; 125 | const glm::vec3 center = (currentMin + currentMax) / 2.0f; 126 | const float scale = glm::compMin(glm::vec3(2) / size); 127 | const glm::mat4 modelTransform = 128 | glm::scale(glm::mat4(1.0f), glm::vec3(scale)) * 129 | glm::translate(glm::mat4(1.0f), -center); 130 | return modelTransform; 131 | }; 132 | 133 | std::vector vertexAttributes; 134 | std::vector indexes; 135 | 136 | const auto updateBuffers = [&]() { 137 | vertexAttributes.resize(0); 138 | model.VertexAttributes(vertexAttributes); 139 | 140 | indexes.resize(0); 141 | model.TriangleIndexes(indexes); 142 | 143 | glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); 144 | glBufferData( 145 | GL_ARRAY_BUFFER, 146 | vertexAttributes.size() * sizeof(vertexAttributes.front()), 147 | vertexAttributes.data(), 148 | GL_DYNAMIC_DRAW); 149 | glBindBuffer(GL_ARRAY_BUFFER, 0); 150 | 151 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); 152 | glBufferData( 153 | GL_ELEMENT_ARRAY_BUFFER, 154 | indexes.size() * sizeof(indexes.front()), 155 | indexes.data(), 156 | GL_DYNAMIC_DRAW); 157 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 158 | }; 159 | 160 | while (!glfwWindowShouldClose(window)) { 161 | elapsed = std::chrono::steady_clock::now() - startTime; 162 | 163 | for (int i = 0; i < ITERATIONS_PER_FRAME; i++) { 164 | model.Update(pool); 165 | } 166 | 167 | updateBuffers(); 168 | 169 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 170 | 171 | program.Use(); 172 | 173 | int w, h; 174 | glfwGetWindowSize(window, &w, &h); 175 | const float aspect = (float)w / (float)h; 176 | const float angle = 0;//elapsed.count() * 3; 177 | glm::mat4 rotation = glm::rotate( 178 | glm::mat4(1.0f), glm::radians(angle), glm::vec3(0, 0, 1)); 179 | glm::mat4 projection = glm::perspective( 180 | glm::radians(25.f), aspect, 1.f, 1000.f); 181 | glm::vec3 eye(0, -5, 0); 182 | glm::vec3 center(0, 0, 0); 183 | glm::vec3 up(0, 0, 1); 184 | glm::mat4 lookAt = glm::lookAt(eye, center, up); 185 | glm::mat4 matrix = projection * lookAt * rotation * getModelTransform(); 186 | glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, glm::value_ptr(matrix)); 187 | 188 | glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); 189 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); 190 | glEnableVertexAttribArray(positionAttrib); 191 | glEnableVertexAttribArray(normalAttrib); 192 | glEnableVertexAttribArray(valueAttrib); 193 | glVertexAttribPointer(positionAttrib, 3, GL_FLOAT, false, 28, 0); 194 | glVertexAttribPointer(normalAttrib, 3, GL_FLOAT, false, 28, (void *)12); 195 | glVertexAttribPointer(valueAttrib, 1, GL_FLOAT, false, 28, (void *)24); 196 | glDrawElements(GL_TRIANGLES, indexes.size() * 3, GL_UNSIGNED_INT, 0); 197 | glDisableVertexAttribArray(positionAttrib); 198 | glDisableVertexAttribArray(normalAttrib); 199 | glDisableVertexAttribArray(valueAttrib); 200 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 201 | glBindBuffer(GL_ARRAY_BUFFER, 0); 202 | 203 | glfwSwapBuffers(window); 204 | glfwPollEvents(); 205 | } 206 | 207 | glfwTerminate(); 208 | } 209 | -------------------------------------------------------------------------------- /src/gui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "model.h" 4 | 5 | void RunGUI(Model &model); 6 | -------------------------------------------------------------------------------- /src/index.cpp: -------------------------------------------------------------------------------- 1 | #include "index.h" 2 | 3 | #include 4 | 5 | #include "util.h" 6 | 7 | #define DEBUG_INDEX 0 8 | 9 | Index::Index(const float cellSize) : 10 | m_CellSize(cellSize), 11 | m_Start(-25, -25, -25), 12 | m_Size(51, 51, 51), 13 | m_Cells(m_Size.x * m_Size.y * m_Size.z), 14 | m_Locks(1024) 15 | { 16 | } 17 | 18 | void Index::Ensure(const glm::vec3 &min, const glm::vec3 &max) { 19 | const auto k0 = KeyForPoint(min); 20 | const auto k1 = KeyForPoint(max); 21 | if (glm::all(glm::greaterThanEqual(k0, m_Start)) && 22 | glm::all(glm::lessThan(k1, m_Start + m_Size))) 23 | { 24 | return; 25 | } 26 | const glm::ivec3 padding = (k1 - k0 + 1) / 4; 27 | const glm::ivec3 newStart = glm::min(m_Start, k0 - padding); 28 | const glm::ivec3 newEnd = glm::max(m_Start + m_Size - 1, k1 + padding); 29 | const glm::ivec3 newSize = newEnd - newStart + 1; 30 | // printf(" %d x %d x %d = %d\n", 31 | // newSize.x, newSize.y, newSize.z, newSize.x * newSize.y * newSize.z); 32 | std::vector> newCells(newSize.x * newSize.y * newSize.z); 33 | for (int i = 0; i < m_Cells.size(); i++) { 34 | const int x = m_Start.x + i % m_Size.x; 35 | const int y = m_Start.y + (i / m_Size.x) % m_Size.y; 36 | const int z = m_Start.z + i / (m_Size.x * m_Size.y); 37 | const auto d = glm::ivec3(x, y, z) - newStart; 38 | const int j = d.x + (d.y * newSize.x) + (d.z * newSize.x * newSize.y); 39 | newCells[j] = m_Cells[i]; 40 | } 41 | m_Start = newStart; 42 | m_Size = newSize; 43 | m_Cells = newCells; 44 | } 45 | 46 | glm::ivec3 Index::KeyForPoint(const glm::vec3 &point) const { 47 | const int x = std::roundf(point.x / m_CellSize); 48 | const int y = std::roundf(point.y / m_CellSize); 49 | const int z = std::roundf(point.z / m_CellSize); 50 | return glm::ivec3(x, y, z); 51 | } 52 | 53 | int Index::IndexForKey(const glm::ivec3 &key) const { 54 | const auto d = key - m_Start; 55 | return d.x + (d.y * m_Size.x) + (d.z * m_Size.x * m_Size.y); 56 | } 57 | 58 | const std::vector &Index::Nearby(const glm::vec3 &point) const { 59 | return m_Cells[IndexForKey(KeyForPoint(point))]; 60 | } 61 | 62 | void Index::Add(const glm::vec3 &point, const int id) { 63 | const glm::ivec3 key = KeyForPoint(point); 64 | const auto k0 = key - 1; 65 | const auto k1 = key + 1; 66 | for (int x = k0.x; x <= k1.x; x++) { 67 | for (int y = k0.y; y <= k1.y; y++) { 68 | for (int z = k0.z; z <= k1.z; z++) { 69 | auto &ids = m_Cells[IndexForKey(glm::ivec3(x, y, z))]; 70 | #if DEBUG_INDEX 71 | const auto it = std::find(ids.begin(), ids.end(), id); 72 | if (it != ids.end()) { 73 | Panic("id already present in Add"); 74 | } 75 | #endif 76 | ids.push_back(id); 77 | } 78 | } 79 | } 80 | } 81 | 82 | void Index::Remove(const glm::vec3 &point, const int id) { 83 | const glm::ivec3 key = KeyForPoint(point); 84 | const auto k0 = key - 1; 85 | const auto k1 = key + 1; 86 | for (int x = k0.x; x <= k1.x; x++) { 87 | for (int y = k0.y; y <= k1.y; y++) { 88 | for (int z = k0.z; z <= k1.z; z++) { 89 | auto &ids = m_Cells[IndexForKey(glm::ivec3(x, y, z))]; 90 | const auto it = std::find(ids.begin(), ids.end(), id); 91 | #if DEBUG_INDEX 92 | if (it == ids.end()) { 93 | Panic("id not found in Remove"); 94 | } 95 | #endif 96 | std::swap(*it, ids.back()); 97 | ids.pop_back(); 98 | } 99 | } 100 | } 101 | } 102 | 103 | bool Index::Update(const glm::vec3 &p0, const glm::vec3 &p1, const int id) { 104 | const auto key0 = KeyForPoint(p0); 105 | const auto key1 = KeyForPoint(p1); 106 | 107 | if (key0 == key1) { 108 | return false; 109 | } 110 | 111 | const auto k00 = key0 - 1; 112 | const auto k01 = key0 + 1; 113 | const auto k10 = key1 - 1; 114 | const auto k11 = key1 + 1; 115 | 116 | const auto in0 = [&k00, &k01](const int x, const int y, const int z) { 117 | return 118 | x >= k00.x && x <= k01.x && 119 | y >= k00.y && y <= k01.y && 120 | z >= k00.z && z <= k01.z; 121 | }; 122 | 123 | const auto in1 = [&k10, &k11](const int x, const int y, const int z) { 124 | return 125 | x >= k10.x && x <= k11.x && 126 | y >= k10.y && y <= k11.y && 127 | z >= k10.z && z <= k11.z; 128 | }; 129 | 130 | // remove if in key0 and not in key1 131 | for (int x = k00.x; x <= k01.x; x++) { 132 | for (int y = k00.y; y <= k01.y; y++) { 133 | for (int z = k00.z; z <= k01.z; z++) { 134 | if (in1(x, y, z)) { 135 | continue; 136 | } 137 | auto &ids = m_Cells[IndexForKey(glm::ivec3(x, y, z))]; 138 | std::lock_guard guard( 139 | m_Locks[(x + y + z) % m_Locks.size()]); 140 | const auto it = std::find(ids.begin(), ids.end(), id); 141 | #if DEBUG_INDEX 142 | if (it == ids.end()) { 143 | Panic("id not found in Remove"); 144 | } 145 | #endif 146 | std::swap(*it, ids.back()); 147 | ids.pop_back(); 148 | } 149 | } 150 | } 151 | 152 | // add if in key1 and not in key0 153 | for (int x = k10.x; x <= k11.x; x++) { 154 | for (int y = k10.y; y <= k11.y; y++) { 155 | for (int z = k10.z; z <= k11.z; z++) { 156 | if (in0(x, y, z)) { 157 | continue; 158 | } 159 | auto &ids = m_Cells[IndexForKey(glm::ivec3(x, y, z))]; 160 | std::lock_guard guard( 161 | m_Locks[(x + y + z) % m_Locks.size()]); 162 | #if DEBUG_INDEX 163 | const auto it = std::find(ids.begin(), ids.end(), id); 164 | if (it != ids.end()) { 165 | Panic("id already present in Add"); 166 | } 167 | #endif 168 | ids.push_back(id); 169 | } 170 | } 171 | } 172 | 173 | return true; 174 | } 175 | -------------------------------------------------------------------------------- /src/index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GLM_ENABLE_EXPERIMENTAL 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class Index { 10 | public: 11 | Index(const float cellSize); 12 | 13 | void Ensure(const glm::vec3 &min, const glm::vec3 &max); 14 | 15 | glm::ivec3 KeyForPoint(const glm::vec3 &point) const; 16 | 17 | int IndexForKey(const glm::ivec3 &key) const; 18 | 19 | const std::vector &Nearby(const glm::vec3 &point) const; 20 | 21 | void Add(const glm::vec3 &point, const int id); 22 | 23 | void Remove(const glm::vec3 &point, const int id); 24 | 25 | bool Update(const glm::vec3 &p0, const glm::vec3 &p1, const int id); 26 | 27 | private: 28 | float m_CellSize; 29 | glm::ivec3 m_Start; 30 | glm::ivec3 m_Size; 31 | std::vector> m_Cells; 32 | std::vector m_Locks; 33 | }; 34 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gui.h" 4 | #include "model.h" 5 | #include "pool.h" 6 | #include "sphere.h" 7 | #include "stl.h" 8 | #include "util.h" 9 | 10 | void RunForever(Model &model) { 11 | const auto startTime = std::chrono::steady_clock::now(); 12 | ThreadPool pool; 13 | int iterations = 0; 14 | while (1) { 15 | model.Update(pool); 16 | iterations++; 17 | if (iterations % 1000 == 0) { 18 | char filename[1024]; 19 | sprintf(filename, "out%08d.stl", iterations); 20 | SaveBinarySTL(filename, model.Triangulate()); 21 | const std::chrono::duration elapsed = 22 | std::chrono::steady_clock::now() - startTime; 23 | std::cout 24 | << iterations << " " 25 | << elapsed.count() << " " 26 | << model.Positions().size() 27 | << std::endl; 28 | } 29 | } 30 | } 31 | 32 | int main() { 33 | const auto triangles = SphereTriangles(1); 34 | // const auto triangles = LoadBinarySTL(argv[1]); 35 | 36 | const float averageEdgeLength = [&triangles]() { 37 | float sum = 0; 38 | for (const auto &t : triangles) { 39 | sum += glm::distance(t.A(), t.B()); 40 | sum += glm::distance(t.B(), t.C()); 41 | sum += glm::distance(t.C(), t.A()); 42 | } 43 | return sum / (triangles.size() * 3); 44 | }(); 45 | 46 | float SplitThreshold = 1000; 47 | float LinkRestLength = averageEdgeLength; 48 | float RadiusOfInfluence = Random(LinkRestLength, LinkRestLength * 5); 49 | float RepulsionFactor = Random(0, 0.1); 50 | float SpringFactor = Random(0, 0.1); 51 | float PlanarFactor = Random(0, 0.1); 52 | float BulgeFactor = Random(0, 0.1); 53 | 54 | std::cout << "SplitThreshold = " << SplitThreshold << std::endl; 55 | std::cout << "LinkRestLength = " << LinkRestLength << std::endl; 56 | std::cout << "RadiusOfInfluence = " << RadiusOfInfluence << std::endl; 57 | std::cout << "RepulsionFactor = " << RepulsionFactor << std::endl; 58 | std::cout << "SpringFactor = " << SpringFactor << std::endl; 59 | std::cout << "PlanarFactor = " << PlanarFactor << std::endl; 60 | std::cout << "BulgeFactor = " << BulgeFactor << std::endl; 61 | std::cout << std::endl; 62 | 63 | Model model( 64 | triangles, 65 | SplitThreshold, LinkRestLength, RadiusOfInfluence, 66 | RepulsionFactor, SpringFactor, PlanarFactor, BulgeFactor); 67 | 68 | RunGUI(model); 69 | // RunForever(model); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/model.cpp: -------------------------------------------------------------------------------- 1 | #include "model.h" 2 | 3 | #define GLM_ENABLE_EXPERIMENTAL 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util.h" 12 | 13 | Model::Model( 14 | const std::vector &triangles, 15 | const float splitThreshold, 16 | const float linkRestLength, 17 | const float radiusOfInfluence, 18 | const float repulsionFactor, 19 | const float springFactor, 20 | const float planarFactor, 21 | const float bulgeFactor) : 22 | m_SplitThreshold(splitThreshold), 23 | m_LinkRestLength(linkRestLength), 24 | m_RadiusOfInfluence(radiusOfInfluence), 25 | m_RepulsionFactor(repulsionFactor), 26 | m_SpringFactor(springFactor), 27 | m_PlanarFactor(planarFactor), 28 | m_BulgeFactor(bulgeFactor), 29 | m_Index(radiusOfInfluence * 1.2) 30 | { 31 | // find unique vertices and create cells 32 | std::unordered_map indexes; 33 | std::unordered_map> vertexTriangles; 34 | for (int i = 0; i < triangles.size(); i++) { 35 | const Triangle &t = triangles[i]; 36 | for (const auto &v : {t.A(), t.B(), t.C()}) { 37 | vertexTriangles[v].push_back(i); 38 | if (indexes.find(v) == indexes.end()) { 39 | indexes[v] = m_Positions.size(); 40 | // create new cell 41 | m_Positions.push_back(v); 42 | m_Normals.emplace_back(0); 43 | m_Food.push_back(0); 44 | m_Links.emplace_back(); 45 | } 46 | } 47 | } 48 | 49 | // sort triangles into CCW order for each vertex and create links 50 | for (auto &it : vertexTriangles) { 51 | const auto &point = it.first; 52 | auto &tris = it.second; 53 | for (int i0 = 1; i0 < tris.size(); i0++) { 54 | const glm::vec3 &prev = triangles[tris[i0-1]].VertexBefore(point); 55 | for (int i1 = i0; i1 < tris.size(); i1++) { 56 | if (triangles[tris[i1]].VertexAfter(point) == prev) { 57 | std::swap(tris[i0], tris[i1]); 58 | break; 59 | } 60 | } 61 | } 62 | const int i = indexes[point]; 63 | for (const int j : tris) { 64 | const int k = indexes[triangles[j].VertexAfter(point)]; 65 | m_Links[i].push_back(k); 66 | } 67 | } 68 | 69 | // build index and compute normals 70 | Ensure(); 71 | for (int i = 0; i < m_Positions.size(); i++) { 72 | m_Index.Add(m_Positions[i], i); 73 | m_Normals[i] = CellNormal(i); 74 | } 75 | } 76 | 77 | void Model::Bounds(glm::vec3 &min, glm::vec3 &max) const { 78 | min = m_Positions[0]; 79 | max = m_Positions[0]; 80 | for (const auto &p : m_Positions) { 81 | min = glm::min(min, p); 82 | max = glm::max(max, p); 83 | } 84 | } 85 | 86 | void Model::Ensure() { 87 | glm::vec3 min, max; 88 | Bounds(min, max); 89 | const float padding = std::max(m_LinkRestLength, m_RadiusOfInfluence) * 10; 90 | min -= padding; 91 | max += padding; 92 | m_Index.Ensure(min, max); 93 | } 94 | 95 | void Model::UpdateBatch(const int wi, const int wn) { 96 | const float roi2 = m_RadiusOfInfluence * m_RadiusOfInfluence; 97 | const float link2 = m_LinkRestLength * m_LinkRestLength; 98 | 99 | for (int i = wi; i < m_Positions.size(); i += wn) { 100 | // get cell position, normal, and links 101 | const glm::vec3 P = m_Positions[i]; 102 | const glm::vec3 N = CellNormal(i); 103 | const auto &links = m_Links[i]; 104 | 105 | // accumulate 106 | glm::vec3 repulsionVector(0); 107 | glm::vec3 springTarget(0); 108 | glm::vec3 planarTarget(0); 109 | float bulgeDistance = 0; 110 | for (const int j : links) { 111 | const glm::vec3 &L = m_Positions[j]; 112 | const glm::vec3 D = L - P; 113 | const glm::vec3 Dn = glm::normalize(D); 114 | springTarget += L - Dn * m_LinkRestLength; 115 | planarTarget += L; 116 | const float length2 = glm::length2(D); 117 | if (length2 < link2) { 118 | const float dot = glm::dot(D, N); 119 | bulgeDistance += std::sqrt( 120 | link2 - glm::dot(D, D) + dot * dot) + dot; 121 | } 122 | if (length2 < roi2) { 123 | // linked cells will be repulsed in the repulsion step below 124 | // so, here we add in the opposite to counteract it for 125 | // performance reasons 126 | const float m = (roi2 - length2) / roi2; 127 | repulsionVector += Dn * m; 128 | } 129 | } 130 | 131 | // average 132 | const float m = 1.f / static_cast(links.size()); 133 | springTarget *= m; 134 | planarTarget *= m; 135 | bulgeDistance *= m; 136 | 137 | // repulsion 138 | for (const int j : m_Index.Nearby(P)) { 139 | if (j == i) { 140 | continue; 141 | } 142 | const glm::vec3 &L = m_Positions[j]; 143 | const glm::vec3 D = P - L; 144 | const float d2 = glm::length2(D); 145 | if (d2 < roi2) { 146 | const float m = (roi2 - d2) / roi2; 147 | repulsionVector += glm::normalize(D) * m; 148 | } 149 | } 150 | 151 | // results 152 | m_NewNormals[i] = N; 153 | m_NewPositions[i] = P + 154 | m_SpringFactor * (springTarget - P) + 155 | m_PlanarFactor * (planarTarget - P) + 156 | (m_BulgeFactor * bulgeDistance) * N + 157 | m_RepulsionFactor * repulsionVector; 158 | 159 | // m_Food[i] += 1 / std::sqrt(std::abs(P.z) + 1); 160 | // m_Food[i] += N.z; 161 | // m_Food[i] += Random(0, 1); 162 | // m_Food[i] += Random(0, 1) / (std::abs(P.y) + 1); 163 | // m_Food[i] += glm::length(repulsionVector); 164 | // m_Food[i] += Random(0, 1); 165 | // m_Food[i] = food + std::pow(std::max(0.f, N.z), 2); 166 | // m_Food[i] = food + N.z + 0.1; 167 | // m_Food[i] += std::pow(N.z, 2); 168 | // m_Food[i] = std::max(0.f, m_Food[i]); 169 | } 170 | } 171 | 172 | void Model::Update(ThreadPool &pool, const bool split) { 173 | Ensure(); 174 | 175 | m_NewPositions.resize(m_Positions.size()); 176 | m_NewNormals.resize(m_Normals.size()); 177 | 178 | const int wn = pool.NumThreads(); 179 | std::vector> results(wn); 180 | 181 | auto done = Timed("run workers"); 182 | for (int wi = 0; wi < wn; wi++) { 183 | results[wi] = pool.Add([this, wi, wn]() { 184 | UpdateBatch(wi, wn); 185 | }); 186 | } 187 | for (int wi = 0; wi < wn; wi++) { 188 | results[wi].get(); 189 | } 190 | done(); 191 | 192 | // compute mean position change 193 | glm::vec3 sum(0); 194 | for (int i = 0; i < m_Positions.size(); i++) { 195 | sum += m_NewPositions[i] - m_Positions[i]; 196 | } 197 | const glm::vec3 offset = -sum / (float)m_Positions.size(); 198 | for (int i = 0; i < m_Positions.size(); i++) { 199 | m_NewPositions[i] += offset; 200 | } 201 | 202 | done = Timed("update index"); 203 | for (int wi = 0; wi < wn; wi++) { 204 | results[wi] = pool.Add([this, wi, wn]() { 205 | for (int i = wi; i < m_Positions.size(); i += wn) { 206 | m_Index.Update(m_Positions[i], m_NewPositions[i], i); 207 | } 208 | }); 209 | } 210 | for (int wi = 0; wi < wn; wi++) { 211 | results[wi].get(); 212 | } 213 | done(); 214 | 215 | // commit 216 | done = Timed("commit"); 217 | m_Positions = m_NewPositions; 218 | m_Normals = m_NewNormals; 219 | done(); 220 | 221 | // split 222 | if (split) { 223 | done = Timed("split"); 224 | for (int i = 0; i < m_Food.size(); i++) { 225 | m_Food[i] += Random(0, 1); 226 | if (m_Food[i] > m_SplitThreshold) { 227 | Split(i); 228 | } 229 | } 230 | done(); 231 | } 232 | } 233 | 234 | glm::vec3 Model::CellNormal(const int index) const { 235 | const auto &links = m_Links[index]; 236 | const glm::vec3 p0 = m_Positions[index]; 237 | glm::vec3 p1 = m_Positions[links.back()]; 238 | glm::vec3 N(0); 239 | for (const int i : links) { 240 | const glm::vec3 p2 = m_Positions[i]; 241 | N += glm::triangleNormal(p0, p1, p2); 242 | p1 = p2; 243 | } 244 | return glm::normalize(N); 245 | } 246 | 247 | void Model::Split(const int parentIndex) { 248 | 249 | const auto changeLink = [this]( 250 | const int i, const int from, const int to) 251 | { 252 | auto &links = m_Links[i]; 253 | const auto it = std::find(links.begin(), links.end(), from); 254 | // if (it == links.end()) { 255 | // Panic("index not found in ChangeLink"); 256 | // } 257 | *it = to; 258 | }; 259 | 260 | const auto insertLinkBefore = [this]( 261 | const int i, const int before, const int link) 262 | { 263 | auto &links = m_Links[i]; 264 | const auto it = std::find(links.begin(), links.end(), before); 265 | // if (it == links.end()) { 266 | // Panic("index not found in InsertLinkAfter"); 267 | // } 268 | links.insert(it, link); 269 | }; 270 | 271 | const auto insertLinkAfter = [this]( 272 | const int i, const int after, const int link) 273 | { 274 | auto &links = m_Links[i]; 275 | const auto it = std::find(links.begin(), links.end(), after); 276 | // if (it == links.end()) { 277 | // Panic("index not found in InsertLinkAfter"); 278 | // } 279 | links.insert(it + 1, link); 280 | }; 281 | 282 | // create the child in the same spot as the parent for now 283 | const int childIndex = m_Links.size(); 284 | m_Positions.push_back(m_Positions[parentIndex]); 285 | m_Normals.emplace_back(m_Normals[parentIndex]); 286 | m_Food.push_back(0); 287 | m_Links.emplace_back(); 288 | 289 | // choose "plane of cleavage" 290 | const auto links = m_Links[parentIndex]; 291 | const int n = links.size(); 292 | const int i0 = [&]() { 293 | float bestDistance = 1e9; 294 | int bestIndex = 0; 295 | for (int i = 0; i < n; i++) { 296 | const int j = (i + n / 2) % n; 297 | const auto p0 = m_Positions[links[i]]; 298 | const auto p1 = m_Positions[links[j]]; 299 | const float d = glm::distance(p0, p1); 300 | if (d < bestDistance) { 301 | bestDistance = d; 302 | bestIndex = i; 303 | } 304 | } 305 | return bestIndex; 306 | }(); 307 | const int i1 = i0 + n / 2; 308 | 309 | // update parent links 310 | auto &parentLinks = m_Links[parentIndex]; 311 | parentLinks.resize(0); 312 | for (int i = i0; i <= i1; i++) { 313 | parentLinks.push_back(links[i % n]); 314 | } 315 | parentLinks.push_back(childIndex); 316 | 317 | // update child links 318 | auto &childLinks = m_Links[childIndex]; 319 | for (int i = i1; i <= i0 + n; i++) { 320 | childLinks.push_back(links[i % n]); 321 | } 322 | childLinks.push_back(parentIndex); 323 | 324 | // update neighbor links 325 | insertLinkAfter(links[i0 % n], parentIndex, childIndex); 326 | insertLinkBefore(links[i1 % n], parentIndex, childIndex); 327 | for (int i = i1 + 1; i <= i0 + n - 1; i++) { 328 | changeLink(links[i % n], parentIndex, childIndex); 329 | } 330 | 331 | // compute new parent position 332 | glm::vec3 newParentPosition(m_Positions[parentIndex]); 333 | for (const int j : parentLinks) { 334 | newParentPosition += m_Positions[j]; 335 | } 336 | newParentPosition /= parentLinks.size() + 1; 337 | 338 | // compute new child position 339 | glm::vec3 newChildPosition(m_Positions[childIndex]); 340 | for (const int j : childLinks) { 341 | newChildPosition += m_Positions[j]; 342 | } 343 | newChildPosition /= childLinks.size() + 1; 344 | 345 | // update positions, normals, and index 346 | m_Index.Update(m_Positions[parentIndex], newParentPosition, parentIndex); 347 | m_Index.Add(newChildPosition, childIndex); 348 | m_Positions[parentIndex] = newParentPosition; 349 | m_Positions[childIndex] = newChildPosition; 350 | m_Normals[parentIndex] = CellNormal(parentIndex); 351 | m_Normals[childIndex] = CellNormal(childIndex); 352 | 353 | // reset parent's food level 354 | m_Food[parentIndex] = 0; 355 | } 356 | 357 | std::vector Model::Triangulate() const { 358 | std::vector indexes; 359 | TriangleIndexes(indexes); 360 | std::vector triangles; 361 | triangles.reserve(indexes.size()); 362 | for (const auto &i : indexes) { 363 | triangles.emplace_back( 364 | m_Positions[i.x], m_Positions[i.y], m_Positions[i.z]); 365 | } 366 | return triangles; 367 | } 368 | 369 | void Model::TriangleIndexes(std::vector &result) const { 370 | for (int i = 0; i < m_Positions.size(); i++) { 371 | const auto &links = m_Links[i]; 372 | for (int j = 0; j < links.size(); j++) { 373 | const int k = (j + 1) % links.size(); 374 | const int link0 = links[j]; 375 | const int link1 = links[k]; 376 | if (i < link0 && i < link1) { 377 | result.emplace_back(i, link0, link1); 378 | } 379 | } 380 | } 381 | } 382 | 383 | void Model::VertexAttributes(std::vector &result) const { 384 | for (int i = 0; i < m_Positions.size(); i++) { 385 | const auto &p = m_Positions[i]; 386 | const auto &n = m_Normals[i]; 387 | const float value = m_Food[i] / m_SplitThreshold; 388 | // const float value = i / (float)(m_Positions.size() - 1); 389 | result.push_back(p.x); 390 | result.push_back(p.y); 391 | result.push_back(p.z); 392 | result.push_back(n.x); 393 | result.push_back(n.y); 394 | result.push_back(n.z); 395 | result.push_back(value); 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /src/model.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "index.h" 7 | #include "pool.h" 8 | #include "triangle.h" 9 | 10 | class Model { 11 | public: 12 | Model( 13 | const std::vector &triangles, 14 | const float splitThreshold, 15 | const float linkRestLength, 16 | const float radiusOfInfluence, 17 | const float repulsionFactor, 18 | const float springFactor, 19 | const float planarFactor, 20 | const float bulgeFactor); 21 | 22 | // getter methods 23 | const std::vector &Positions() const { return m_Positions; } 24 | const std::vector &Normals() const { return m_Normals; } 25 | const std::vector &Food() const { return m_Food; } 26 | const std::vector> &Links() const { return m_Links; } 27 | float SplitThreshold() const { return m_SplitThreshold; } 28 | float LinkRestLength() const { return m_LinkRestLength; } 29 | float RadiusOfInfluence() const { return m_RadiusOfInfluence; } 30 | float RepulsionFactor() const { return m_RepulsionFactor; } 31 | float SpringFactor() const { return m_SpringFactor; } 32 | float PlanarFactor() const { return m_PlanarFactor; } 33 | float BulgeFactor() const { return m_BulgeFactor; } 34 | 35 | // Bounds computes the min / max bounds of all cells 36 | void Bounds(glm::vec3 &min, glm::vec3 &max) const; 37 | 38 | // Update runs one iteration of simulation using the provided thread pool 39 | void Update(ThreadPool &pool, const bool split = true); 40 | 41 | std::vector Triangulate() const; 42 | 43 | void TriangleIndexes(std::vector &result) const; 44 | 45 | void VertexAttributes(std::vector &result) const; 46 | 47 | private: 48 | void Ensure(); 49 | 50 | void UpdateBatch(const int wi, const int wn); 51 | 52 | glm::vec3 CellNormal(const int index) const; 53 | 54 | void Split(const int i); 55 | 56 | // amount of food required for a cell to split 57 | float m_SplitThreshold; 58 | 59 | // preferred distance between linked cells 60 | float m_LinkRestLength; 61 | 62 | // distance at which non-linked cells will repel each other 63 | float m_RadiusOfInfluence; 64 | 65 | // weights 66 | float m_RepulsionFactor; 67 | float m_SpringFactor; 68 | float m_PlanarFactor; 69 | float m_BulgeFactor; 70 | 71 | // position of each cell 72 | std::vector m_Positions; 73 | 74 | // normal of each cell 75 | std::vector m_Normals; 76 | 77 | // food level of each cell 78 | std::vector m_Food; 79 | 80 | // list of indexes of linked cells 81 | std::vector> m_Links; 82 | 83 | // spatial hash index 84 | Index m_Index; 85 | 86 | // buffers 87 | std::vector m_NewPositions; 88 | std::vector m_NewNormals; 89 | }; 90 | -------------------------------------------------------------------------------- /src/pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class ThreadPool { 12 | public: 13 | ThreadPool(int numThreads = std::thread::hardware_concurrency()) : 14 | m_Stop(false) 15 | { 16 | for (int i = 0; i < numThreads; i++) { 17 | m_Threads.emplace_back([this]() { 18 | while (1) { 19 | std::function task; 20 | 21 | { 22 | std::unique_lock lock(m_Mutex); 23 | m_Condition.wait(lock, [this]() { 24 | return m_Stop || !m_Queue.empty(); 25 | }); 26 | if (m_Stop && m_Queue.empty()) { 27 | return; 28 | } 29 | task = std::move(m_Queue.front()); 30 | m_Queue.pop(); 31 | } 32 | 33 | task(); 34 | } 35 | }); 36 | } 37 | } 38 | 39 | ~ThreadPool() { 40 | { 41 | std::lock_guard guard(m_Mutex); 42 | m_Stop = true; 43 | } 44 | m_Condition.notify_all(); 45 | for (auto &thread: m_Threads) { 46 | thread.join(); 47 | } 48 | } 49 | 50 | int NumThreads() const { 51 | return m_Threads.size(); 52 | } 53 | 54 | template 55 | auto Add(F&& f, Args&&... args) 56 | -> std::future::type> 57 | { 58 | using ReturnType = typename std::result_of::type; 59 | auto task = std::make_shared>( 60 | std::bind(std::forward(f), std::forward(args)...)); 61 | std::future result = task->get_future(); 62 | 63 | { 64 | std::lock_guard guard(m_Mutex); 65 | if (!m_Stop) { 66 | m_Queue.emplace([task]() { 67 | (*task)(); 68 | }); 69 | } 70 | } 71 | 72 | m_Condition.notify_one(); 73 | return result; 74 | } 75 | 76 | private: 77 | std::vector m_Threads; 78 | std::queue> m_Queue; 79 | std::mutex m_Mutex; 80 | std::condition_variable m_Condition; 81 | bool m_Stop; 82 | }; 83 | -------------------------------------------------------------------------------- /src/program.cpp: -------------------------------------------------------------------------------- 1 | #include "program.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace { 7 | 8 | GLuint compileShader(GLenum shaderType, std::string shaderSource) { 9 | const GLuint shader = glCreateShader(shaderType); 10 | const GLchar *source = shaderSource.c_str(); 11 | glShaderSource(shader, 1, &source, 0); 12 | glCompileShader(shader); 13 | GLint isCompiled; 14 | glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); 15 | if(!isCompiled) { 16 | GLint maxLength; 17 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); 18 | std::vector infoLog(maxLength); 19 | glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]); 20 | glDeleteShader(shader); 21 | throw std::runtime_error(std::string(infoLog.begin(), infoLog.end())); 22 | } 23 | return shader; 24 | } 25 | 26 | GLuint compileProgram(std::string vertexSource, std::string fragmentSource) { 27 | const GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource); 28 | const GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource); 29 | const GLuint program = glCreateProgram(); 30 | glAttachShader(program, vertexShader); 31 | glAttachShader(program, fragmentShader); 32 | glLinkProgram(program); 33 | GLint isLinked; 34 | glGetProgramiv(program, GL_LINK_STATUS, &isLinked); 35 | if (!isLinked) { 36 | GLint maxLength; 37 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); 38 | std::vector infoLog(maxLength); 39 | glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); 40 | glDeleteProgram(program); 41 | glDeleteShader(vertexShader); 42 | glDeleteShader(fragmentShader); 43 | throw std::runtime_error(std::string(infoLog.begin(), infoLog.end())); 44 | } 45 | glDetachShader(program, vertexShader); 46 | glDetachShader(program, fragmentShader); 47 | glDeleteShader(vertexShader); 48 | glDeleteShader(fragmentShader); 49 | return program; 50 | } 51 | 52 | } 53 | 54 | Program::Program(std::string vertexSource, std::string fragmentSource) : 55 | m_Program(compileProgram(vertexSource, fragmentSource)) 56 | {} 57 | 58 | void Program::Use() const { 59 | glUseProgram(m_Program); 60 | } 61 | 62 | GLint Program::GetUniformLocation(std::string name) const { 63 | return glGetUniformLocation(m_Program, name.c_str()); 64 | } 65 | 66 | GLint Program::GetAttribLocation(std::string name) const { 67 | return glGetAttribLocation(m_Program, name.c_str()); 68 | } 69 | -------------------------------------------------------------------------------- /src/program.h: -------------------------------------------------------------------------------- 1 | #define GL_SILENCE_DEPRECATION 2 | 3 | #include 4 | #include 5 | 6 | class Program { 7 | public: 8 | Program(std::string vsrc, std::string fsrc); 9 | void Use() const; 10 | GLint GetUniformLocation(std::string name) const; 11 | GLint GetAttribLocation(std::string name) const; 12 | private: 13 | GLuint m_Program; 14 | }; 15 | -------------------------------------------------------------------------------- /src/sphere.cpp: -------------------------------------------------------------------------------- 1 | #include "sphere.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | std::vector IcosahedronTriangles() { 8 | const float a = 0.8506507174597755; 9 | const float b = 0.5257312591858783; 10 | 11 | const auto vertices = std::vector{ 12 | {-a, -b, 0}, {-a, b, 0}, {-b, 0, -a}, {-b, 0, a}, 13 | { 0, -a, -b}, { 0, -a, b}, { 0, a, -b}, { 0, a, b}, 14 | { b, 0, -a}, { b, 0, a}, { a, -b, 0}, { a, b, 0}, 15 | }; 16 | 17 | const auto indices = std::vector{ 18 | { 0, 3, 1}, { 1, 3, 7}, { 2, 0, 1}, { 2, 1, 6}, 19 | { 4, 0, 2}, { 4, 5, 0}, { 5, 3, 0}, { 6, 1, 7}, 20 | { 6, 7, 11}, { 7, 3, 9}, { 8, 2, 6}, { 8, 4, 2}, 21 | { 8, 6, 11}, { 8, 10, 4}, { 8, 11, 10}, { 9, 3, 5}, 22 | {10, 5, 4}, {10, 9, 5}, {11, 7, 9}, {11, 9, 10}, 23 | }; 24 | 25 | std::vector triangles; 26 | triangles.reserve(indices.size()); 27 | for (const auto &i : indices) { 28 | triangles.emplace_back( 29 | vertices[i.x], 30 | vertices[i.y], 31 | vertices[i.z]); 32 | } 33 | return triangles; 34 | } 35 | 36 | std::vector SphereTriangles(const int detail) { 37 | std::vector triangles; 38 | triangles.reserve(20 * std::pow(4, detail)); 39 | 40 | std::function helper; 43 | 44 | helper = [&helper, &triangles]( 45 | const int detail, 46 | const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) 47 | { 48 | if (detail == 0) { 49 | triangles.emplace_back(v1, v2, v3); 50 | return; 51 | } 52 | const glm::vec3 v12 = glm::normalize((v1 + v2) / 2.0f); 53 | const glm::vec3 v13 = glm::normalize((v1 + v3) / 2.0f); 54 | const glm::vec3 v23 = glm::normalize((v2 + v3) / 2.0f); 55 | helper(detail - 1, v1, v12, v13); 56 | helper(detail - 1, v2, v23, v12); 57 | helper(detail - 1, v3, v13, v23); 58 | helper(detail - 1, v12, v23, v13); 59 | }; 60 | 61 | for (const auto &t : IcosahedronTriangles()) { 62 | helper(detail, t.A(), t.B(), t.C()); 63 | } 64 | 65 | return triangles; 66 | } 67 | -------------------------------------------------------------------------------- /src/sphere.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "triangle.h" 6 | 7 | std::vector IcosahedronTriangles(); 8 | std::vector SphereTriangles(const int detail); 9 | -------------------------------------------------------------------------------- /src/stl.cpp: -------------------------------------------------------------------------------- 1 | #include "stl.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace boost::interprocess; 8 | 9 | std::vector LoadBinarySTL(std::string path) { 10 | file_mapping fm(path.c_str(), read_only); 11 | mapped_region mr(fm, read_only); 12 | uint8_t *src = (uint8_t *)mr.get_address(); 13 | const int numBytes = mr.get_size(); 14 | const int numTriangles = std::max(0, (numBytes - 84) / 50); 15 | const int numVertices = numTriangles * 3; 16 | std::vector positions(numVertices); 17 | auto dst = positions.data(); 18 | src += 96; 19 | for (int i = 0; i < numTriangles; i++) { 20 | memcpy(dst, src, 36); 21 | src += 50; 22 | dst += 3; 23 | } 24 | std::vector triangles; 25 | triangles.reserve(numTriangles); 26 | for (int i = 0; i < positions.size(); i += 3) { 27 | triangles.emplace_back( 28 | positions[i+0], 29 | positions[i+1], 30 | positions[i+2]); 31 | } 32 | return triangles; 33 | } 34 | 35 | void SaveBinarySTL(std::string path, const std::vector &triangles) { 36 | const uint64_t numBytes = uint64_t(triangles.size()) * 50 + 84; 37 | 38 | { 39 | file_mapping::remove(path.c_str()); 40 | std::filebuf fbuf; 41 | fbuf.open(path.c_str(), 42 | std::ios_base::in | std::ios_base::out | std::ios_base::trunc | 43 | std::ios_base::binary); 44 | fbuf.pubseekoff(numBytes - 1, std::ios_base::beg); 45 | fbuf.sputc(0); 46 | } 47 | 48 | file_mapping fm(path.c_str(), read_write); 49 | mapped_region mr(fm, read_write); 50 | uint8_t *dst = (uint8_t *)mr.get_address(); 51 | 52 | const uint32_t count = triangles.size(); 53 | memcpy(dst + 80, &count, 4); 54 | 55 | for (uint32_t i = 0; i < triangles.size(); i++) { 56 | const Triangle &t = triangles[i]; 57 | const glm::vec3 normal = t.Normal(); 58 | const uint64_t idx = 84 + i * 50; 59 | memcpy(dst + idx + 0, &normal, 12); 60 | memcpy(dst + idx + 12, &t.A(), 12); 61 | memcpy(dst + idx + 24, &t.B(), 12); 62 | memcpy(dst + idx + 36, &t.C(), 12); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/stl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "triangle.h" 8 | 9 | std::vector LoadBinarySTL(std::string path); 10 | 11 | void SaveBinarySTL(std::string path, const std::vector &triangles); 12 | -------------------------------------------------------------------------------- /src/triangle.cpp: -------------------------------------------------------------------------------- 1 | #include "triangle.h" 2 | 3 | #define GLM_ENABLE_EXPERIMENTAL 4 | 5 | #include 6 | 7 | #include "util.h" 8 | 9 | glm::vec3 Triangle::Normal() const { 10 | return glm::triangleNormal(m_A, m_B, m_C); 11 | } 12 | 13 | const glm::vec3 &Triangle::VertexAfter(const glm::vec3 &point) const { 14 | if (m_A == point) { 15 | return m_B; 16 | } else if (m_B == point) { 17 | return m_C; 18 | } else if (m_C == point) { 19 | return m_A; 20 | } else { 21 | Panic("point not found in VertexAfter"); 22 | return m_A; 23 | } 24 | } 25 | 26 | const glm::vec3 &Triangle::VertexBefore(const glm::vec3 &point) const { 27 | if (m_A == point) { 28 | return m_C; 29 | } else if (m_B == point) { 30 | return m_A; 31 | } else if (m_C == point) { 32 | return m_B; 33 | } else { 34 | Panic("point not found in VertexBefore"); 35 | return m_A; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/triangle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Triangle { 7 | public: 8 | Triangle(const glm::vec3 &a, const glm::vec3 &b, const glm::vec3 &c) : 9 | m_A(a), m_B(b), m_C(c) {} 10 | 11 | const glm::vec3 &A() const { 12 | return m_A; 13 | } 14 | 15 | const glm::vec3 &B() const { 16 | return m_B; 17 | } 18 | 19 | const glm::vec3 &C() const { 20 | return m_C; 21 | } 22 | 23 | glm::vec3 Normal() const; 24 | 25 | const glm::vec3 &VertexAfter(const glm::vec3 &point) const; 26 | 27 | const glm::vec3 &VertexBefore(const glm::vec3 &point) const; 28 | 29 | private: 30 | glm::vec3 m_A; 31 | glm::vec3 m_B; 32 | glm::vec3 m_C; 33 | }; 34 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void Panic(const std::string &message) { 9 | std::cerr << message << std::endl; 10 | std::exit(1); 11 | } 12 | 13 | std::function Timed(const std::string &message) { 14 | const auto startTime = std::chrono::steady_clock::now(); 15 | return [message, startTime]() { 16 | return; 17 | const std::chrono::duration elapsed = 18 | std::chrono::steady_clock::now() - startTime; 19 | const double millis = elapsed.count() * 1000; 20 | std::cerr << message << ": " << millis << "ms" << std::endl; 21 | }; 22 | } 23 | 24 | double Random(const double lo, const double hi) { 25 | // static thread_local std::mt19937 gen(0); 26 | static thread_local std::mt19937 gen(std::chrono::high_resolution_clock::now().time_since_epoch().count()); 27 | std::uniform_real_distribution dist(lo, hi); 28 | return dist(gen); 29 | } 30 | 31 | int RandomIntN(const int n) { 32 | // static thread_local std::mt19937 gen(0); 33 | static thread_local std::mt19937 gen(std::chrono::high_resolution_clock::now().time_since_epoch().count()); 34 | std::uniform_int_distribution dist(0, n - 1); 35 | return dist(gen); 36 | } 37 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void Panic(const std::string &message); 7 | 8 | std::function Timed(const std::string &message); 9 | 10 | double Random(const double lo, const double hi); 11 | 12 | int RandomIntN(const int n); 13 | -------------------------------------------------------------------------------- /tracer/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /build 3 | /main 4 | /*.png 5 | /*.ppm 6 | 7 | -------------------------------------------------------------------------------- /tracer/Makefile: -------------------------------------------------------------------------------- 1 | #### PROJECT SETTINGS #### 2 | # The name of the executable to be created 3 | BIN_NAME := main 4 | # Compiler used 5 | C ?= g++ 6 | # Extension of source files used in the project 7 | SRC_EXT = cpp 8 | # Path to the source directory, relative to the makefile 9 | SRC_PATH = src 10 | # General compiler flags 11 | COMPILE_FLAGS = -std=c++14 -flto -O3 -Wall -Wextra -Wshadow -pedantic -Wno-sign-compare -Wno-unused-parameter -march=native 12 | # Additional release-specific flags 13 | RCOMPILE_FLAGS = -D NDEBUG 14 | # Additional debug-specific flags 15 | DCOMPILE_FLAGS = -D DEBUG 16 | # Add additional include paths 17 | INCLUDES = -I $(SRC_PATH) 18 | # General linker settings 19 | LINK_FLAGS = -flto -O3 -lembree3 20 | # Additional release-specific linker settings 21 | RLINK_FLAGS = 22 | # Additional debug-specific linker settings 23 | DLINK_FLAGS = 24 | # Destination directory, like a jail or mounted system 25 | DESTDIR = / 26 | # Install path (bin/ is appended automatically) 27 | INSTALL_PREFIX = usr/local 28 | #### END PROJECT SETTINGS #### 29 | 30 | # Generally should not need to edit below this line 31 | 32 | # Shell used in this makefile 33 | # bash is used for 'echo -en' 34 | SHELL = /bin/bash 35 | # Clear built-in rules 36 | .SUFFIXES: 37 | # Programs for installation 38 | INSTALL = install 39 | INSTALL_PROGRAM = $(INSTALL) 40 | INSTALL_DATA = $(INSTALL) -m 644 41 | 42 | # Verbose option, to output compile and link commands 43 | export V := false 44 | export CMD_PREFIX := @ 45 | ifeq ($(V),true) 46 | CMD_PREFIX := 47 | endif 48 | 49 | # Combine compiler and linker flags 50 | release: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS) 51 | release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS) 52 | debug: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS) 53 | debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS) 54 | 55 | # Build and output paths 56 | release: export BUILD_PATH := build/release 57 | release: export BIN_PATH := bin/release 58 | debug: export BUILD_PATH := build/debug 59 | debug: export BIN_PATH := bin/debug 60 | install: export BIN_PATH := bin/release 61 | 62 | # Find all source files in the source directory, sorted by most 63 | # recently modified 64 | SOURCES = $(shell find $(SRC_PATH)/ -name '*.$(SRC_EXT)' \ 65 | | sort -k 1nr | cut -f2-) 66 | # fallback in case the above fails 67 | rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \ 68 | $(filter $(subst *,%,$2), $d)) 69 | ifeq ($(SOURCES),) 70 | SOURCES := $(call rwildcard, $(SRC_PATH)/, *.$(SRC_EXT)) 71 | endif 72 | 73 | # Set the object file names, with the source directory stripped 74 | # from the path, and the build path prepended in its place 75 | OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o) 76 | # Set the dependency files that will be used to add header dependencies 77 | DEPS = $(OBJECTS:.o=.d) 78 | 79 | # Macros for timing compilation 80 | TIME_FILE = $(dir $@).$(notdir $@)_time 81 | START_TIME = date '+%s' > $(TIME_FILE) 82 | END_TIME = read st < $(TIME_FILE) ; \ 83 | $(RM) $(TIME_FILE) ; \ 84 | st=$$((`date '+%s'` - $$st - 86400)) ; \ 85 | echo `date -u -d @$$st '+%H:%M:%S'` 86 | 87 | # Version macros 88 | # Comment/remove this section to remove versioning 89 | USE_VERSION := false 90 | # If this isn't a git repo or the repo has no tags, git describe will return non-zero 91 | ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0) 92 | USE_VERSION := true 93 | VERSION := $(shell git describe --tags --long --dirty --always | \ 94 | sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g') 95 | VERSION_MAJOR := $(word 1, $(VERSION)) 96 | VERSION_MINOR := $(word 2, $(VERSION)) 97 | VERSION_PATCH := $(word 3, $(VERSION)) 98 | VERSION_REVISION := $(word 4, $(VERSION)) 99 | VERSION_HASH := $(word 5, $(VERSION)) 100 | VERSION_STRING := \ 101 | "$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)" 102 | override CFLAGS := $(CFLAGS) \ 103 | -D VERSION_MAJOR=$(VERSION_MAJOR) \ 104 | -D VERSION_MINOR=$(VERSION_MINOR) \ 105 | -D VERSION_PATCH=$(VERSION_PATCH) \ 106 | -D VERSION_REVISION=$(VERSION_REVISION) \ 107 | -D VERSION_HASH=\"$(VERSION_HASH)\" 108 | endif 109 | 110 | # Standard, non-optimized release build 111 | .PHONY: release 112 | release: dirs 113 | ifeq ($(USE_VERSION), true) 114 | @echo "Beginning release build v$(VERSION_STRING)" 115 | else 116 | @echo "Beginning release build" 117 | endif 118 | @$(MAKE) all --no-print-directory 119 | 120 | # Debug build for gdb debugging 121 | .PHONY: debug 122 | debug: dirs 123 | ifeq ($(USE_VERSION), true) 124 | @echo "Beginning debug build v$(VERSION_STRING)" 125 | else 126 | @echo "Beginning debug build" 127 | endif 128 | @$(MAKE) all --no-print-directory 129 | 130 | # Create the directories used in the build 131 | .PHONY: dirs 132 | dirs: 133 | @echo "Creating directories" 134 | @mkdir -p $(dir $(OBJECTS)) 135 | @mkdir -p $(BIN_PATH) 136 | 137 | # Installs to the set path 138 | .PHONY: install 139 | install: 140 | @echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin" 141 | @$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin 142 | 143 | # Uninstalls the program 144 | .PHONY: uninstall 145 | uninstall: 146 | @echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)" 147 | @$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME) 148 | 149 | # Removes all build files 150 | .PHONY: clean 151 | clean: 152 | @echo "Deleting $(BIN_NAME) symlink" 153 | @$(RM) $(BIN_NAME) 154 | @echo "Deleting directories" 155 | @$(RM) -r build 156 | @$(RM) -r bin 157 | 158 | # Main rule, checks the executable and symlinks to the output 159 | all: $(BIN_PATH)/$(BIN_NAME) 160 | @echo "Making symlink: $(BIN_NAME) -> $<" 161 | @$(RM) $(BIN_NAME) 162 | @ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME) 163 | 164 | # Link the executable 165 | $(BIN_PATH)/$(BIN_NAME): $(OBJECTS) 166 | @echo "Linking: $@" 167 | $(CMD_PREFIX)$(C) $(OBJECTS) $(LDFLAGS) -o $@ 168 | 169 | # Add dependency files, if they exist 170 | -include $(DEPS) 171 | 172 | # Source file rules 173 | # After the first compilation they will be joined with the rules from the 174 | # dependency files to provide header dependencies 175 | $(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT) 176 | @echo "Compiling: $< -> $@" 177 | $(CMD_PREFIX)$(C) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@ 178 | 179 | .PHONY: run 180 | run: release 181 | time ./$(BIN_NAME) 182 | -------------------------------------------------------------------------------- /tracer/src/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | 3 | #include 4 | 5 | #include "util.h" 6 | 7 | Camera::Camera( 8 | const Vec3 &eye, 9 | const Vec3 ¢er, 10 | const Vec3 &up, 11 | const real fovy, 12 | const real aspect, 13 | const real aperture, 14 | const real focusDistance) 15 | { 16 | const real d = focusDistance; 17 | const real theta = fovy * M_PI / 180; 18 | const real halfHeight = std::tan(theta / 2); 19 | const real halfWidth = halfHeight * aspect; 20 | m_W = Normalized(eye - center); 21 | m_U = Normalized(Cross(up, m_W)); 22 | m_V = Cross(m_W, m_U); 23 | m_LowerLeft = eye - halfWidth * d * m_U - halfHeight * d * m_V - d * m_W; 24 | m_Horizontal = 2 * halfWidth * d * m_U; 25 | m_Vertical = 2 * halfHeight * d * m_V; 26 | m_Origin = eye; 27 | m_Aperture = aperture; 28 | } 29 | 30 | Ray Camera::MakeRay(const real u, const real v) const { 31 | const Vec3 rd = RandomInUnitDisk() * (m_Aperture / 2); 32 | const Vec3 offset = m_U * rd.X() + m_V * rd.Y(); 33 | const Vec3 dir = Normalized( 34 | m_LowerLeft + m_Horizontal * u + m_Vertical * v - m_Origin - offset); 35 | return Ray(m_Origin + offset, dir); 36 | } 37 | -------------------------------------------------------------------------------- /tracer/src/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ray.h" 4 | #include "vec3.h" 5 | 6 | class Camera { 7 | public: 8 | Camera( 9 | const Vec3 &eye, 10 | const Vec3 ¢er, 11 | const Vec3 &up, 12 | const real fovy, 13 | const real aspect, 14 | const real aperture, 15 | const real focusDistance); 16 | 17 | Ray MakeRay(const real u, const real v) const; 18 | 19 | private: 20 | Vec3 m_Origin; 21 | Vec3 m_LowerLeft; 22 | Vec3 m_Horizontal; 23 | Vec3 m_Vertical; 24 | Vec3 m_U; 25 | Vec3 m_V; 26 | Vec3 m_W; 27 | real m_Aperture; 28 | }; 29 | -------------------------------------------------------------------------------- /tracer/src/colormap.cpp: -------------------------------------------------------------------------------- 1 | #include "colormap.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | 8 | Colormap::Colormap(const std::string &data) { 9 | for (int i = 0; i < data.size(); i += 6) { 10 | std::stringstream ss; 11 | ss << std::hex << data.substr(i, 6); 12 | int x; 13 | ss >> x; 14 | m_Colors.push_back(HexColor(x)); 15 | } 16 | } 17 | 18 | Vec3 Colormap::At(const double t) const { 19 | const int n = m_Colors.size(); 20 | 21 | if (t <= 0) { 22 | return m_Colors.front(); 23 | } 24 | if (t >= 1) { 25 | return m_Colors.back(); 26 | } 27 | 28 | const int i = t * (n - 1); 29 | const double s = 1.0 / (n - 1); 30 | const double p = (t - i * s) / s; 31 | 32 | const Vec3 c0 = m_Colors[i]; 33 | const Vec3 c1 = m_Colors[i+1]; 34 | 35 | if (p <= 0) { 36 | return c0; 37 | } 38 | if (p >= 1) { 39 | return c1; 40 | } 41 | 42 | const double r = Clamp(c0.R() + (c1.R() - c0.R()) * p, 0, 1); 43 | const double g = Clamp(c0.G() + (c1.G() - c0.G()) * p, 0, 1); 44 | const double b = Clamp(c0.B() + (c1.B() - c0.B()) * p, 0, 1); 45 | 46 | return Vec3(r, g, b); 47 | } 48 | -------------------------------------------------------------------------------- /tracer/src/colormap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vec3.h" 7 | 8 | class Colormap { 9 | public: 10 | Colormap(const std::string &data); 11 | Vec3 At(const double t) const; 12 | private: 13 | std::vector m_Colors; 14 | }; 15 | 16 | static Colormap Viridis("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"); 17 | static Colormap Magma("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf"); 18 | static Colormap Inferno("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4"); 19 | static Colormap Plasma("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"); 20 | static Colormap Grayscale("000000ffffff"); 21 | static Colormap Spectral("d24252f98e60fde091ffffc3e6f49e9bd4973988ba"); 22 | static Colormap Blues("f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b"); 23 | static Colormap Viget("113c54175b761d78972395b85fa693a2b771eac853eaaa47ec8c3bee6e30d6502bbd3226"); 24 | -------------------------------------------------------------------------------- /tracer/src/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define real double 4 | #define inf 1e9 5 | #define eps 1e-4 6 | -------------------------------------------------------------------------------- /tracer/src/embree.cpp: -------------------------------------------------------------------------------- 1 | #include "embree.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "colormap.h" 8 | #include "stl.h" 9 | 10 | const RTCDevice device = rtcNewDevice(NULL); 11 | 12 | struct EmbreeVertex { 13 | float x, y, z, a; 14 | }; 15 | 16 | struct EmbreeTriangle { 17 | int v0, v1, v2; 18 | }; 19 | 20 | EmbreeMesh::EmbreeMesh(std::string path, const P_Material &material) : 21 | m_Material(material) 22 | { 23 | // load stl 24 | std::vector points = LoadBinarySTL(path); 25 | 26 | // compute bounding box 27 | Vec3 min = points[0]; 28 | Vec3 max = points[0]; 29 | for (const auto &v : points) { 30 | min = Min(min, v); 31 | max = Max(max, v); 32 | } 33 | 34 | // transform points 35 | const Vec3 size = max - min; 36 | const Vec3 center = min + size / 2; 37 | const real scale = 1 / size.MaxComponent(); 38 | for (int i = 0; i < points.size(); i++) { 39 | points[i] = (points[i] - center) * scale; 40 | // points[i] = Vec3(points[i].X(), points[i].Y(), points[i].Z()); 41 | } 42 | 43 | const int numTriangles = points.size() / 3; 44 | 45 | // store triangles 46 | m_Triangles.resize(numTriangles); 47 | for (int i = 0; i < numTriangles; i++) { 48 | m_Triangles[i].V1 = points[i*3+0]; 49 | m_Triangles[i].V2 = points[i*3+1]; 50 | m_Triangles[i].V3 = points[i*3+2]; 51 | const Vec3 n = m_Triangles[i].Normal(); 52 | m_Triangles[i].N1 = n; 53 | m_Triangles[i].N2 = n; 54 | m_Triangles[i].N3 = n; 55 | } 56 | 57 | // smooth normals 58 | std::unordered_map map; 59 | for (const auto &t : m_Triangles) { 60 | const Vec3 n = t.Normal(); 61 | map[t.V1] = map[t.V1] + n; 62 | map[t.V2] = map[t.V2] + n; 63 | map[t.V3] = map[t.V3] + n; 64 | } 65 | 66 | for (const auto &pair : map) { 67 | map[pair.first] = Normalized(pair.second); 68 | } 69 | 70 | for (auto &t : m_Triangles) { 71 | t.N1 = map[t.V1]; 72 | t.N2 = map[t.V2]; 73 | t.N3 = map[t.V3]; 74 | } 75 | 76 | m_Scene = rtcNewScene(device); 77 | RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE); 78 | 79 | EmbreeVertex *vertices = (EmbreeVertex *)rtcSetNewGeometryBuffer( 80 | geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(EmbreeVertex), points.size()); 81 | for (int i = 0; i < points.size(); i++) { 82 | vertices[i] = EmbreeVertex{ 83 | static_cast(points[i].X()), 84 | static_cast(points[i].Y()), 85 | static_cast(points[i].Z()), 86 | 0}; 87 | } 88 | 89 | EmbreeTriangle *triangles = (EmbreeTriangle *)rtcSetNewGeometryBuffer( 90 | geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(EmbreeTriangle), numTriangles); 91 | for (int i = 0; i < numTriangles; i++) { 92 | triangles[i] = EmbreeTriangle{i*3+0, i*3+1, i*3+2}; 93 | } 94 | 95 | rtcCommitGeometry(geom); 96 | rtcAttachGeometry(m_Scene, geom); 97 | rtcReleaseGeometry(geom); 98 | rtcCommitScene(m_Scene); 99 | } 100 | 101 | bool EmbreeMesh::Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const { 102 | RTCIntersectContext context; 103 | rtcInitIntersectContext(&context); 104 | 105 | const Vec3 &org = ray.Origin(); 106 | const Vec3 &dir = ray.Direction(); 107 | 108 | RTCRayHit r; 109 | r.ray.org_x = org.X(); r.ray.org_y = org.Y(); r.ray.org_z = org.Z(); 110 | r.ray.dir_x = dir.X(); r.ray.dir_y = dir.Y(); r.ray.dir_z = dir.Z(); 111 | r.ray.tnear = tmin; 112 | r.ray.tfar = tmax; 113 | r.ray.mask = -1; 114 | r.ray.flags = 0; 115 | r.ray.time = 0; 116 | r.ray.id = 0; 117 | 118 | r.hit.geomID = RTC_INVALID_GEOMETRY_ID; 119 | r.hit.primID = RTC_INVALID_GEOMETRY_ID; 120 | 121 | rtcIntersect1(m_Scene, &context, &r); 122 | 123 | if (r.hit.primID == RTC_INVALID_GEOMETRY_ID) { 124 | return false; 125 | } 126 | 127 | const float x = r.ray.org_x + r.ray.dir_x * r.ray.tfar; 128 | const float y = r.ray.org_y + r.ray.dir_y * r.ray.tfar; 129 | const float z = r.ray.org_z + r.ray.dir_z * r.ray.tfar; 130 | 131 | hit.T = r.ray.tfar; 132 | hit.Position = Vec3(x, y, z); 133 | hit.Normal = m_Triangles[r.hit.primID].Normal(hit.Position); 134 | hit.Material = m_Material; 135 | return true; 136 | } 137 | 138 | typedef struct { 139 | float X; 140 | float Y; 141 | float Z; 142 | float R; 143 | } Sphere; 144 | 145 | EmbreeSpheres::EmbreeSpheres(std::string path, const P_Material &material) : 146 | m_Material(material) 147 | { 148 | std::vector points = LoadBinarySTL(path); 149 | 150 | std::unordered_map> linkLengths; 151 | for (int i = 0; i < points.size(); i += 3) { 152 | const auto &a = points[i+0]; 153 | const auto &b = points[i+1]; 154 | const auto &c = points[i+2]; 155 | const float ab = (a - b).Length(); 156 | const float ac = (a - c).Length(); 157 | const float bc = (b - c).Length(); 158 | linkLengths[a].push_back(ab); 159 | linkLengths[a].push_back(ac); 160 | linkLengths[b].push_back(ab); 161 | linkLengths[b].push_back(bc); 162 | linkLengths[c].push_back(ac); 163 | linkLengths[c].push_back(bc); 164 | } 165 | 166 | std::unordered_map radius; 167 | for (const auto &it : linkLengths) { 168 | const auto &p = it.first; 169 | const auto &lengths = it.second; 170 | const float mean = std::accumulate( 171 | lengths.begin(), lengths.end(), 0.f) / lengths.size(); 172 | radius[p] = mean * 0.5f; 173 | } 174 | 175 | std::vector spheres; 176 | for (const auto &it : radius) { 177 | const auto &p = it.first; 178 | const float x = p.X(); 179 | const float y = p.Y(); 180 | const float z = p.Z(); 181 | const float r = it.second; 182 | if (x > 0) { 183 | // continue; 184 | } 185 | spheres.push_back(Sphere{x, y, z, r}); 186 | } 187 | 188 | std::sort(spheres.begin(), spheres.end(), [](const auto &a, const auto &b) { 189 | return a.R < b.R; 190 | }); 191 | 192 | const float minRadius = spheres[spheres.size() * 1 / 1000].R; 193 | const float maxRadius = spheres[spheres.size() * 999 / 1000].R; 194 | 195 | // float minRadius = spheres[0].R; 196 | // float maxRadius = spheres[0].R; 197 | // for (const auto &s : spheres) { 198 | // minRadius = std::min(minRadius, s.R); 199 | // maxRadius = std::max(maxRadius, s.R); 200 | // } 201 | 202 | for (const auto &s : spheres) { 203 | const float t = (s.R - minRadius) / (maxRadius - minRadius); 204 | m_Materials.push_back(std::make_shared( 205 | std::make_shared(Viridis.At(t)))); 206 | } 207 | 208 | // compute bounding box 209 | Vec3 min = points[0]; 210 | Vec3 max = points[0]; 211 | for (const auto &p : points) { 212 | min = Min(min, p); 213 | max = Max(max, p); 214 | } 215 | 216 | // transform spheres 217 | const Vec3 size = max - min; 218 | const Vec3 center = min + size / 2; 219 | const real scale = 1 / size.MaxComponent(); 220 | for (int i = 0; i < spheres.size(); i++) { 221 | Vec3 v(spheres[i].X, spheres[i].Y, spheres[i].Z); 222 | v = (v - center) * scale; 223 | spheres[i] = Sphere{ 224 | static_cast(v.X()), 225 | static_cast(v.Y()), 226 | static_cast(v.Z()), 227 | static_cast(spheres[i].R * scale)}; 228 | } 229 | 230 | m_Scene = rtcNewScene(device); 231 | RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_SPHERE_POINT); 232 | Sphere *buf = (Sphere *)rtcSetNewGeometryBuffer( 233 | geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4, sizeof(Sphere), spheres.size()); 234 | for (int i = 0; i < spheres.size(); i++) { 235 | buf[i] = spheres[i]; 236 | } 237 | 238 | rtcCommitGeometry(geom); 239 | unsigned int geomID = rtcAttachGeometry(m_Scene, geom); 240 | rtcReleaseGeometry(geom); 241 | rtcCommitScene(m_Scene); 242 | 243 | std::cout << geomID << " " << spheres.size() << std::endl; 244 | } 245 | 246 | bool EmbreeSpheres::Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const { 247 | RTCIntersectContext context; 248 | rtcInitIntersectContext(&context); 249 | 250 | const Vec3 &org = ray.Origin(); 251 | const Vec3 &dir = ray.Direction(); 252 | 253 | RTCRayHit r; 254 | r.ray.org_x = org.X(); r.ray.org_y = org.Y(); r.ray.org_z = org.Z(); 255 | r.ray.dir_x = dir.X(); r.ray.dir_y = dir.Y(); r.ray.dir_z = dir.Z(); 256 | r.ray.tnear = tmin; 257 | r.ray.tfar = tmax; 258 | r.ray.mask = -1; 259 | r.ray.flags = 0; 260 | r.ray.time = 0; 261 | r.ray.id = 0; 262 | 263 | r.hit.geomID = RTC_INVALID_GEOMETRY_ID; 264 | r.hit.primID = RTC_INVALID_GEOMETRY_ID; 265 | 266 | rtcIntersect1(m_Scene, &context, &r); 267 | 268 | if (r.hit.primID == RTC_INVALID_GEOMETRY_ID) { 269 | return false; 270 | } 271 | 272 | const float x = r.ray.org_x + r.ray.dir_x * r.ray.tfar; 273 | const float y = r.ray.org_y + r.ray.dir_y * r.ray.tfar; 274 | const float z = r.ray.org_z + r.ray.dir_z * r.ray.tfar; 275 | 276 | hit.T = r.ray.tfar; 277 | hit.Position = Vec3(x, y, z); 278 | hit.Normal = Normalized(Vec3(r.hit.Ng_x, r.hit.Ng_y, r.hit.Ng_z)); 279 | hit.Material = m_Materials[r.hit.primID]; 280 | // hit.Material = m_Material; 281 | return true; 282 | } 283 | -------------------------------------------------------------------------------- /tracer/src/embree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "hit.h" 8 | #include "material.h" 9 | 10 | class Triangle { 11 | public: 12 | Vec3 V1, V2, V3; 13 | Vec3 N1, N2, N3; 14 | 15 | Vec3 Normal() const { 16 | return TriangleNormal(V1, V2, V3); 17 | } 18 | 19 | Vec3 Normal(const Vec3 &p) const { 20 | const Vec3 b = Barycentric(p); 21 | return Normalized(b.X() * N1 + b.Y() * N2 + b.Z() * N3); 22 | } 23 | 24 | Vec3 Barycentric(const Vec3 &p) const { 25 | const Vec3 v0 = V2 - V1; 26 | const Vec3 v1 = V3 - V1; 27 | const Vec3 v2 = p - V1; 28 | const real d00 = Dot(v0, v0); 29 | const real d01 = Dot(v0, v1); 30 | const real d11 = Dot(v1, v1); 31 | const real d20 = Dot(v2, v0); 32 | const real d21 = Dot(v2, v1); 33 | const real d = d00*d11 - d01*d01; 34 | const real v = (d11*d20 - d01*d21) / d; 35 | const real w = (d00*d21 - d01*d20) / d; 36 | const real u = 1 - v - w; 37 | return Vec3(u, v, w); 38 | } 39 | }; 40 | 41 | class EmbreeMesh : public Hittable { 42 | public: 43 | EmbreeMesh(std::string path, const P_Material &material); 44 | virtual bool Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const; 45 | private: 46 | RTCScene m_Scene; 47 | std::vector m_Triangles; 48 | P_Material m_Material; 49 | }; 50 | 51 | class EmbreeSpheres : public Hittable { 52 | public: 53 | EmbreeSpheres(std::string path, const P_Material &material); 54 | virtual bool Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const; 55 | private: 56 | RTCScene m_Scene; 57 | P_Material m_Material; 58 | std::vector m_Materials; 59 | }; 60 | -------------------------------------------------------------------------------- /tracer/src/hit.cpp: -------------------------------------------------------------------------------- 1 | #include "hit.h" 2 | 3 | void HittableList::Add(const P_Hittable &item) { 4 | m_Items.push_back(item); 5 | } 6 | 7 | void HittableList::AddLight(const P_Hittable &item) { 8 | m_Lights.push_back(item); 9 | } 10 | 11 | bool HittableList::Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const { 12 | bool result = false; 13 | real closest = tmax; 14 | for (const auto &item : m_Items) { 15 | HitInfo temp; 16 | if (item->Hit(ray, tmin, closest, temp)) { 17 | result = true; 18 | closest = temp.T; 19 | hit = temp; 20 | } 21 | } 22 | return result; 23 | } 24 | -------------------------------------------------------------------------------- /tracer/src/hit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "material.h" 7 | #include "ray.h" 8 | #include "vec3.h" 9 | 10 | struct HitInfo { 11 | real T; 12 | Vec3 Position; 13 | Vec3 Normal; 14 | P_Material Material; 15 | }; 16 | 17 | class Hittable { 18 | public: 19 | virtual bool Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const = 0; 20 | 21 | virtual Ray RandomRay(const Vec3 &o) const { 22 | return Ray(); 23 | } 24 | 25 | virtual real Pdf(const Ray &ray) const { 26 | return 0; 27 | } 28 | 29 | virtual ~Hittable() {} 30 | }; 31 | 32 | typedef std::shared_ptr P_Hittable; 33 | 34 | class HittableList : public Hittable { 35 | public: 36 | void Add(const P_Hittable &item); 37 | void AddLight(const P_Hittable &item); 38 | 39 | const std::vector &Lights() const { 40 | return m_Lights; 41 | } 42 | 43 | virtual bool Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const; 44 | private: 45 | std::vector m_Items; 46 | std::vector m_Lights; 47 | }; 48 | -------------------------------------------------------------------------------- /tracer/src/image.cpp: -------------------------------------------------------------------------------- 1 | #include "image.h" 2 | 3 | #include 4 | #include 5 | 6 | Image::Image(int width, int height) : 7 | m_Width(width), m_Height(height) 8 | { 9 | m_Data.resize(width * height); 10 | } 11 | 12 | void Image::SavePPM(const std::string &path, const real divider) const { 13 | const real depth = 65535; 14 | const real multiplier = 1 / divider; 15 | const real exponent = 1 / 2.2; 16 | std::ofstream out(path); 17 | out << "P3\n"; 18 | out << m_Width << " " << m_Height << "\n"; 19 | out << depth << "\n"; 20 | int i = 0; 21 | for (int y = 0; y < m_Height; y++) { 22 | for (int x = 0; x < m_Width; x++) { 23 | const Vec3 &c = Pow(m_Data[i++] * multiplier, exponent); 24 | const int r = std::min(c.R() * depth, depth); 25 | const int g = std::min(c.G() * depth, depth); 26 | const int b = std::min(c.B() * depth, depth); 27 | out << r << " " << g << " " << b << "\n"; 28 | } 29 | } 30 | out.close(); 31 | } 32 | 33 | void Image::SaveDepthPPM(const std::string &path, const real divider, const real min, const real max) const { 34 | const real depth = 65535; 35 | const real multiplier = 1 / divider; 36 | std::ofstream out(path); 37 | out << "P3\n"; 38 | out << m_Width << " " << m_Height << "\n"; 39 | out << depth << "\n"; 40 | int i = 0; 41 | for (int y = 0; y < m_Height; y++) { 42 | for (int x = 0; x < m_Width; x++) { 43 | const Vec3 &c = (m_Data[i++] * multiplier - min) / (max - min); 44 | const int r = std::min(c.R() * depth, depth); 45 | const int g = std::min(c.G() * depth, depth); 46 | const int b = std::min(c.B() * depth, depth); 47 | out << r << " " << g << " " << b << "\n"; 48 | } 49 | } 50 | out.close(); 51 | } 52 | -------------------------------------------------------------------------------- /tracer/src/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vec3.h" 7 | 8 | class Image { 9 | public: 10 | Image(int width, int height); 11 | 12 | int Width() const { 13 | return m_Width; 14 | } 15 | 16 | int Height() const { 17 | return m_Height; 18 | } 19 | 20 | Vec3 Min() const { 21 | Vec3 min = m_Data[0]; 22 | for (const auto &c : m_Data) { 23 | min = ::Min(min, c); 24 | } 25 | return min; 26 | } 27 | 28 | Vec3 Max() const { 29 | Vec3 max = m_Data[0]; 30 | for (const auto &c : m_Data) { 31 | max = ::Max(max, c); 32 | } 33 | return max; 34 | } 35 | 36 | const Vec3 &Get(int x, int y) const { 37 | return m_Data[y * m_Width + x]; 38 | } 39 | 40 | void Set(int x, int y, const Vec3 &c) { 41 | m_Data[y * m_Width + x] = c; 42 | } 43 | 44 | void Add(int x, int y, const Vec3 &c) { 45 | const int i = y * m_Width + x; 46 | m_Data[i] = m_Data[i] + c; 47 | } 48 | 49 | void SavePPM(const std::string &path, const real divider = 1) const; 50 | 51 | void SaveDepthPPM(const std::string &path, const real divider, const real min, const real max) const; 52 | 53 | private: 54 | int m_Width; 55 | int m_Height; 56 | std::vector m_Data; 57 | }; 58 | -------------------------------------------------------------------------------- /tracer/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "camera.h" 9 | #include "embree.h" 10 | #include "hit.h" 11 | #include "image.h" 12 | #include "material.h" 13 | #include "onb.h" 14 | #include "ray.h" 15 | #include "sphere.h" 16 | #include "util.h" 17 | #include "vec3.h" 18 | 19 | const int w = 1600; 20 | const int h = 1600; 21 | const int ns = 16; 22 | const int wn = 4; 23 | 24 | Vec3 background(const Ray &ray) { 25 | return Vec3(0); 26 | } 27 | 28 | Vec3 iterative(const HittableList &world, const Ray &cameraRay) { 29 | const int minBounces = 4; 30 | const int maxBounces = 8; 31 | 32 | Vec3 color(0, 0, 0); 33 | Vec3 throughput(1, 1, 1); 34 | bool specular = true; 35 | Ray ray(cameraRay); 36 | 37 | const P_Hittable light = world.Lights()[0]; 38 | 39 | for (int bounces = 0; bounces < maxBounces; bounces++) { 40 | HitInfo hit; 41 | if (!world.Hit(ray, eps, inf, hit)) { 42 | color = color + throughput * background(ray); 43 | break; 44 | } 45 | 46 | const Vec3 emitted = hit.Material->Emitted(0, 0, hit.Position); 47 | if (emitted.MaxComponent() > 0) { 48 | if (specular && Dot(hit.Normal, ray.Direction()) < 0) { 49 | color = color + throughput * emitted; 50 | } 51 | break; 52 | } 53 | 54 | const ONB onb(hit.Normal); 55 | const Vec3 p(hit.Position); 56 | const Vec3 wo(onb.WorldToLocal(Normalized(-ray.Direction()))); 57 | 58 | Vec3 wi; 59 | real pdf; 60 | const Vec3 a = hit.Material->Sample_f(p, wo, wi, pdf, specular); 61 | 62 | // direct lighting 63 | if (!specular) { 64 | const Ray lightRay = light->RandomRay(p); 65 | HitInfo lightHit; 66 | if (world.Hit(lightRay, eps, inf, lightHit)) { 67 | const Vec3 Li = lightHit.Material->Emitted(0, 0, lightHit.Position); 68 | if (Li.MaxComponent() > 0 && Dot(lightHit.Normal, lightRay.Direction()) < 0) { 69 | const real lightPdf = light->Pdf(lightRay); 70 | const Vec3 lwi = onb.WorldToLocal(lightRay.Direction()); 71 | const Vec3 direct = hit.Material->f(p, wo, lwi) * Li / lightPdf; 72 | color = color + throughput * direct * std::abs(lwi.Z()); 73 | } 74 | } 75 | } 76 | 77 | // Vec3 a; 78 | // Vec3 wi; 79 | // real pdf; 80 | // if (Random() < 0.5) { 81 | // // use light pdf 82 | // const Ray lightRay = light->RandomRay(p); 83 | // pdf = light->Pdf(lightRay); 84 | // wi = onb.WorldToLocal(lightRay.Direction()); 85 | // a = hit.Material->f(p, wo, wi); 86 | // pdf = (pdf + hit.Material->Pdf(wo, wi)) / 2; 87 | // specular = false; 88 | // } else { 89 | // // use material pdf 90 | // a = hit.Material->Sample_f(p, wo, wi, pdf, specular); 91 | // pdf = (pdf + light->Pdf(Ray(p, onb.LocalToWorld(wi)))) / 2; 92 | // } 93 | 94 | if (specular) { 95 | throughput = throughput * a; 96 | } else { 97 | if (pdf < eps) { 98 | break; 99 | } 100 | throughput = throughput * a * std::abs(wi.Z()) / pdf; 101 | } 102 | 103 | ray = Ray(p, onb.LocalToWorld(wi)); 104 | 105 | if (bounces >= minBounces) { 106 | const real prob = throughput.MaxComponent(); 107 | if (Random() > prob) { 108 | break; 109 | } 110 | throughput = throughput / prob; 111 | } 112 | } 113 | 114 | return color; 115 | } 116 | 117 | int main(int argc, char **argv) { 118 | _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); 119 | _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); 120 | 121 | HittableList world; 122 | 123 | const auto material = std::make_shared( 124 | std::make_shared(HexColor(0x808080))); 125 | const auto geom = std::make_shared(argv[1], material); 126 | 127 | world.Add(geom); 128 | 129 | const auto light = std::make_shared( 130 | std::make_shared(Kelvin(5000) * 30)); 131 | const auto L = std::make_shared(Vec3(3, 1, 4), 1, light); 132 | world.Add(L); 133 | world.AddLight(L); 134 | 135 | // const auto floorMaterial = std::make_shared( 136 | // std::make_shared(HexColor(0xFFFFFF))); 137 | // const auto floor = std::make_shared(Vec3(0, 0, -1000), 1000, floorMaterial); 138 | // world.Add(floor); 139 | 140 | const Vec3 eye(3, 0, 0); 141 | const Vec3 center(0, 0, 0); 142 | const Vec3 up(0, 0, 1); 143 | const real fovy = 25; 144 | const real aspect = real(w) / real(h); 145 | const real aperture = 0.005; 146 | const real focusDistance = (eye - center).Length(); 147 | const Camera camera(eye, center, up, fovy, aspect, aperture, focusDistance); 148 | 149 | Image im(w, h); 150 | for (int frame = 1; ; frame++) { 151 | std::vector threads; 152 | for (int wi = 0; wi < wn; wi++) { 153 | threads.push_back(std::thread([&](int i) { 154 | _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); 155 | _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); 156 | for (int y = 0; y < h; y++) { 157 | if (y % wn != i) { 158 | continue; 159 | } 160 | for (int x = 0; x < w; x++) { 161 | Vec3 c; 162 | for (int s = 0; s < ns; s++) { 163 | const real u = (x + Random()) / w; 164 | const real v = (y + Random()) / h; 165 | const Ray ray = camera.MakeRay(u, 1 - v); 166 | c = c + iterative(world, ray); 167 | } 168 | im.Add(x, y, c); 169 | } 170 | } 171 | }, wi)); 172 | } 173 | for (int wi = 0; wi < wn; wi++) { 174 | threads[wi].join(); 175 | } 176 | im.SavePPM("out.ppm", ns * frame); 177 | std::cout << (frame * ns) << std::endl; 178 | } 179 | return 0; 180 | } 181 | -------------------------------------------------------------------------------- /tracer/src/material.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "microfacet.h" 8 | #include "ray.h" 9 | #include "texture.h" 10 | #include "util.h" 11 | #include "vec3.h" 12 | 13 | class Material { 14 | public: 15 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const = 0; 16 | 17 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 18 | wi = CosineSampleHemisphere(); 19 | if (wo.Z() < 0) { 20 | wi = Vec3(wi.X(), wi.Y(), -wi.Z()); 21 | } 22 | pdf = Pdf(wo, wi); 23 | specular = false; 24 | return f(p, wo, wi); 25 | } 26 | 27 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 28 | if (wo.Z() * wi.Z() <= 0) { 29 | return 0; 30 | } 31 | return std::abs(wi.Z()) / M_PI; 32 | } 33 | 34 | virtual Vec3 Emitted(const real u, const real v, const Vec3 &p) const { 35 | return Vec3(); 36 | } 37 | 38 | virtual ~Material() {} 39 | }; 40 | 41 | typedef std::shared_ptr P_Material; 42 | 43 | inline real G(const Vec3 &wo, const Vec3 &wi, const Vec3 &wh) { 44 | const real NdotWh = AbsCosTheta(wh); 45 | const real NdotWo = AbsCosTheta(wo); 46 | const real NdotWi = AbsCosTheta(wi); 47 | const real WOdotWh = std::abs(Dot(wo, wh)); 48 | return std::min(real(1), std::min( 49 | 2 * NdotWh * NdotWo / WOdotWh, 50 | 2 * NdotWh * NdotWi / WOdotWh)); 51 | } 52 | 53 | class FresnelBlend : public Material { 54 | public: 55 | FresnelBlend(const P_Texture &Rd, const P_Texture &Rs, const P_MicrofacetDistribution &distribution) : 56 | m_Rd(Rd), m_Rs(Rs), m_Distribution(distribution) {} 57 | 58 | Vec3 SchlickFresnel(const Vec3 &rs, const real costheta) const { 59 | return rs + std::pow(1 - costheta, 5) * (Vec3(1) - rs); 60 | } 61 | 62 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 63 | const Vec3 rd = m_Rd->Sample(0, 0, p); 64 | const Vec3 rs = m_Rs->Sample(0, 0, p); 65 | const Vec3 diffuse = (28 / (23 * M_PI)) * rd * 66 | (Vec3(1) - rs) * 67 | (1 - std::pow(1 - 0.5 * AbsCosTheta(wi), 5)) * 68 | (1 - std::pow(1 - 0.5 * AbsCosTheta(wo), 5)); 69 | const Vec3 wh = Normalized(wi + wo); 70 | const Vec3 specular = m_Distribution->D(wh) / 71 | (4 * std::abs(Dot(wi, wh)) * std::max(AbsCosTheta(wi), AbsCosTheta(wo))) * 72 | SchlickFresnel(rs, Dot(wi, wh)); 73 | return diffuse + specular; 74 | } 75 | 76 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 77 | if (!SameHemisphere(wo, wi)) { 78 | return 0; 79 | } 80 | return 0.5 * (AbsCosTheta(wi) / M_PI + m_Distribution->Pdf(wo, wi)); 81 | } 82 | 83 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 84 | if (Random() < 0.5) { 85 | wi = CosineSampleHemisphere(); 86 | if (wo.Z() < 0) { 87 | wi = Vec3(wi.X(), wi.Y(), -wi.Z()); 88 | } 89 | } else { 90 | m_Distribution->Sample_f(p, wo, wi, pdf); 91 | if (!SameHemisphere(wo, wi)) { 92 | return Vec3(); 93 | } 94 | } 95 | pdf = Pdf(wo, wi); 96 | specular = false; 97 | return f(p, wo, wi); 98 | } 99 | 100 | private: 101 | P_Texture m_Rd; 102 | P_Texture m_Rs; 103 | P_MicrofacetDistribution m_Distribution; 104 | }; 105 | 106 | class Microfacet : public Material { 107 | public: 108 | Microfacet(const P_Texture &albedo, const P_MicrofacetDistribution &distribution, const real eta) : 109 | m_Albedo(albedo), m_Distribution(distribution), m_Eta(eta) {} 110 | 111 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 112 | const real cosThetaO = AbsCosTheta(wo); 113 | const real cosThetaI = AbsCosTheta(wi); 114 | if (cosThetaO == 0 || cosThetaI == 0) { 115 | return Vec3(); 116 | } 117 | const Vec3 wh = Normalized(wi + wo); 118 | const real cosThetaH = Dot(wi, wh); 119 | const real F = Schlick(cosThetaH, m_Eta); 120 | const Vec3 R = m_Albedo->Sample(0, 0, p); 121 | return R * m_Distribution->D(wh) * G(wo, wi, wh) * F / (4 * cosThetaI * cosThetaO); 122 | } 123 | 124 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 125 | if (!SameHemisphere(wo, wi)) { 126 | return 0; 127 | } 128 | return m_Distribution->Pdf(wo, wi); 129 | } 130 | 131 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 132 | m_Distribution->Sample_f(p, wo, wi, pdf); 133 | if (!SameHemisphere(wo, wi)) { 134 | return Vec3(); 135 | } 136 | specular = false; 137 | return f(p, wo, wi); 138 | } 139 | 140 | private: 141 | P_Texture m_Albedo; 142 | P_MicrofacetDistribution m_Distribution; 143 | real m_Eta; 144 | }; 145 | 146 | class Metal : public Material { 147 | public: 148 | Metal(const P_Texture &albedo) : 149 | m_Albedo(albedo) {} 150 | 151 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 152 | return Vec3(0); 153 | } 154 | 155 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 156 | return 0; 157 | } 158 | 159 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 160 | wi = Vec3(-wo.X(), -wo.Y(), wo.Z()); 161 | pdf = 1; 162 | specular = true; 163 | return m_Albedo->Sample(0, 0, p); 164 | } 165 | 166 | private: 167 | P_Texture m_Albedo; 168 | }; 169 | 170 | class Dielectric : public Material { 171 | public: 172 | Dielectric(const P_Texture &albedo, const real eta) : 173 | m_Albedo(albedo), m_Eta(eta) {} 174 | 175 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 176 | return Vec3(0); 177 | } 178 | 179 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 180 | return 0; 181 | } 182 | 183 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 184 | Vec3 outwardNormal; 185 | real ratio; 186 | if (wo.Z() < 0) { 187 | outwardNormal = Vec3(0, 0, -1); 188 | ratio = m_Eta; 189 | } else { 190 | outwardNormal = Vec3(0, 0, 1); 191 | ratio = 1 / m_Eta; 192 | } 193 | 194 | Vec3 refracted; 195 | real reflectProbability; 196 | if (Refract(-wo, outwardNormal, ratio, refracted)) { 197 | reflectProbability = Schlick(AbsCosTheta(wo), m_Eta); 198 | } else { 199 | reflectProbability = 1; 200 | } 201 | 202 | if (Random() < reflectProbability) { 203 | wi = Vec3(-wo.X(), -wo.Y(), wo.Z()); 204 | } else { 205 | wi = refracted; 206 | } 207 | 208 | pdf = 1; 209 | specular = true; 210 | return m_Albedo->Sample(0, 0, p); 211 | } 212 | 213 | private: 214 | P_Texture m_Albedo; 215 | real m_Eta; 216 | }; 217 | 218 | class SpecularReflection : public Material { 219 | public: 220 | SpecularReflection(const P_Texture &albedo, const real eta) : 221 | m_Albedo(albedo), m_Eta(eta) {} 222 | 223 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 224 | return Vec3(); 225 | } 226 | 227 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 228 | wi = Vec3(-wo.X(), -wo.Y(), wo.Z()); 229 | pdf = 1; 230 | specular = true; 231 | const real fr = Schlick(AbsCosTheta(wo), m_Eta); 232 | return m_Albedo->Sample(0, 0, p) * fr; 233 | } 234 | 235 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 236 | return 0; 237 | } 238 | 239 | private: 240 | P_Texture m_Albedo; 241 | real m_Eta; 242 | }; 243 | 244 | class Lambertian : public Material { 245 | public: 246 | Lambertian(const P_Texture &albedo) : 247 | m_Albedo(albedo) {} 248 | 249 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 250 | return m_Albedo->Sample(0, 0, p) / M_PI; 251 | } 252 | 253 | private: 254 | P_Texture m_Albedo; 255 | }; 256 | 257 | class OrenNayar : public Material { 258 | public: 259 | OrenNayar(const P_Texture &albedo, const real sigma_degrees) : 260 | m_Albedo(albedo) 261 | { 262 | const real sigma = sigma_degrees * M_PI / 180; 263 | const real sigma2 = sigma * sigma; 264 | m_A = 1 - (sigma2 / (2 * (sigma2 + 0.33))); 265 | m_B = 0.45 * sigma2 / (sigma2 + 0.09); 266 | } 267 | 268 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 269 | const real sinthetai = SinTheta(wi); 270 | const real sinthetao = SinTheta(wo); 271 | real maxcos = 0; 272 | if (sinthetai > eps && sinthetao > eps) { 273 | const real sinphii = SinPhi(wi); 274 | const real cosphii = CosPhi(wi); 275 | const real sinphio = SinPhi(wo); 276 | const real cosphio = CosPhi(wo); 277 | const real dcos = cosphii * cosphio + sinphii * sinphio; 278 | maxcos = std::max(real(0), dcos); 279 | } 280 | real sinalpha, tanbeta; 281 | if (AbsCosTheta(wi) > AbsCosTheta(wo)) { 282 | sinalpha = sinthetao; 283 | tanbeta = sinthetai / AbsCosTheta(wi); 284 | } else { 285 | sinalpha = sinthetai; 286 | tanbeta = sinthetao / AbsCosTheta(wo); 287 | } 288 | return m_Albedo->Sample(0, 0, p) * (m_A + m_B * maxcos * sinalpha * tanbeta) / M_PI; 289 | } 290 | 291 | private: 292 | P_Texture m_Albedo; 293 | real m_A, m_B; 294 | }; 295 | 296 | class DiffuseLight : public Material { 297 | public: 298 | DiffuseLight(const P_Texture &emit) : 299 | m_Emit(emit) {} 300 | 301 | virtual Vec3 Emitted(const real u, const real v, const Vec3 &p) const { 302 | return m_Emit->Sample(u, v, p); 303 | } 304 | 305 | virtual Vec3 Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf, bool &specular) const { 306 | pdf = 0; 307 | specular = false; 308 | return Vec3(); 309 | } 310 | 311 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 312 | return 0; 313 | } 314 | 315 | virtual Vec3 f(const Vec3 &p, const Vec3 &wo, const Vec3 &wi) const { 316 | return Vec3(); 317 | } 318 | 319 | private: 320 | P_Texture m_Emit; 321 | }; 322 | -------------------------------------------------------------------------------- /tracer/src/microfacet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | 8 | class MicrofacetDistribution { 9 | public: 10 | virtual real D(const Vec3 &wh) const = 0; 11 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const = 0; 12 | virtual void Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf) const = 0; 13 | virtual ~MicrofacetDistribution() {} 14 | }; 15 | 16 | typedef std::shared_ptr P_MicrofacetDistribution; 17 | 18 | class BlinnDistribution : public MicrofacetDistribution { 19 | public: 20 | BlinnDistribution(const real exponent) : 21 | m_Exponent(exponent) {} 22 | 23 | virtual real D(const Vec3 &wh) const { 24 | return (m_Exponent + 2) * std::pow(AbsCosTheta(wh), m_Exponent) / M_PI; 25 | } 26 | 27 | virtual real Pdf(const Vec3 &wo, const Vec3 &wi) const { 28 | const Vec3 wh = Normalized(wo + wi); 29 | if (Dot(wo, wh) <= 0) { 30 | return 0; 31 | } 32 | const real costheta = AbsCosTheta(wh); 33 | return ((m_Exponent + 1) * std::pow(costheta, m_Exponent)) / (2 * M_PI * 4 * Dot(wo, wh)); 34 | } 35 | 36 | virtual void Sample_f(const Vec3 &p, const Vec3 &wo, Vec3 &wi, real &pdf) const { 37 | const real costheta = std::pow(Random(), 1 / (m_Exponent + 1)); 38 | const real sintheta = std::sqrt(std::max(real(0), 1 - costheta * costheta)); 39 | const real phi = Random() * 2 * M_PI; 40 | Vec3 wh = Vec3(sintheta * std::cos(phi), sintheta * std::sin(phi), costheta); 41 | if (wh.Z() * wo.Z() < 0) { 42 | wh = -wh; 43 | } 44 | wi = Normalized(-wo + 2 * Dot(wo, wh) * wh); 45 | pdf = Pdf(wo, wi); 46 | } 47 | 48 | private: 49 | real m_Exponent; 50 | }; 51 | -------------------------------------------------------------------------------- /tracer/src/onb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vec3.h" 4 | 5 | const real INV_SQRT_3 = 1 / std::sqrt(3); 6 | 7 | class ONB { 8 | public: 9 | ONB(const Vec3 &n) { 10 | const Vec3 normal = Normalized(n); 11 | Vec3 majorAxis; 12 | if (std::abs(normal.X()) < INV_SQRT_3) { 13 | majorAxis = Vec3(1, 0, 0); 14 | } else if (std::abs(normal.Y()) < INV_SQRT_3) { 15 | majorAxis = Vec3(0, 1, 0); 16 | } else { 17 | majorAxis = Vec3(0, 0, 1); 18 | } 19 | m_S = Normalized(Cross(normal, majorAxis)); 20 | m_T = Cross(normal, m_S); 21 | m_N = normal; 22 | } 23 | 24 | Vec3 WorldToLocal(const Vec3 &v) const { 25 | return Vec3(Dot(v, m_S), Dot(v, m_T), Dot(v, m_N)); 26 | } 27 | 28 | Vec3 LocalToWorld(const Vec3 &v) const { 29 | const real x = m_S.X() * v.X() + m_T.X() * v.Y() + m_N.X() * v.Z(); 30 | const real y = m_S.Y() * v.X() + m_T.Y() * v.Y() + m_N.Y() * v.Z(); 31 | const real z = m_S.Z() * v.X() + m_T.Z() * v.Y() + m_N.Z() * v.Z(); 32 | return Vec3(x, y, z); 33 | } 34 | 35 | private: 36 | Vec3 m_S; 37 | Vec3 m_T; 38 | Vec3 m_N; 39 | }; 40 | -------------------------------------------------------------------------------- /tracer/src/ray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vec3.h" 4 | 5 | class Ray { 6 | public: 7 | Ray() {} 8 | 9 | Ray(const Vec3 &origin, const Vec3 &direction) : 10 | m_Origin(origin), m_Direction(direction) {} 11 | 12 | const Vec3 &Origin() const { 13 | return m_Origin; 14 | } 15 | 16 | const Vec3 &Direction() const { 17 | return m_Direction; 18 | } 19 | 20 | Vec3 At(const real t) const { 21 | return m_Origin + t * m_Direction; 22 | } 23 | 24 | private: 25 | Vec3 m_Origin; 26 | Vec3 m_Direction; 27 | }; 28 | -------------------------------------------------------------------------------- /tracer/src/sphere.cpp: -------------------------------------------------------------------------------- 1 | #include "sphere.h" 2 | 3 | #include 4 | 5 | #include "onb.h" 6 | #include "util.h" 7 | 8 | bool Sphere::Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const { 9 | const Vec3 oc = ray.Origin() - m_Center; 10 | const real a = Dot(ray.Direction(), ray.Direction()); 11 | const real b = Dot(oc, ray.Direction()); 12 | const real c = Dot(oc, oc) - m_Radius * m_Radius; 13 | const real d = b * b - a * c; 14 | if (d > 0) { 15 | real t = (-b - std::sqrt(b * b - a * c)) / a; 16 | if (t < tmax && t > tmin) { 17 | hit.T = t; 18 | hit.Position = ray.At(t); 19 | hit.Normal = (hit.Position - m_Center) / m_Radius; 20 | hit.Material = m_Material; 21 | return true; 22 | } 23 | t = (-b + std::sqrt(b * b - a * c)) / a; 24 | if (t < tmax && t > tmin) { 25 | hit.T = t; 26 | hit.Position = ray.At(t); 27 | hit.Normal = (hit.Position - m_Center) / m_Radius; 28 | hit.Material = m_Material; 29 | return true; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | Ray Sphere::RandomRay(const Vec3 &o) const { 36 | const Vec3 dir = m_Center - o; 37 | const ONB onb(dir); 38 | const Vec3 p = m_Center + onb.LocalToWorld(RandomInUnitDisk() * m_Radius); 39 | return Ray(o, Normalized(p - o)); 40 | } 41 | 42 | real Sphere::Pdf(const Ray &ray) const { 43 | HitInfo hit; 44 | if (!Hit(ray, eps, inf, hit)) { 45 | return 0; 46 | } 47 | const real costhetamax = std::sqrt(1 - m_Radius * m_Radius / (m_Center - ray.Origin()).LengthSquared()); 48 | const real solidangle = 2 * M_PI * (1 - costhetamax); 49 | return 1 / solidangle; 50 | } 51 | -------------------------------------------------------------------------------- /tracer/src/sphere.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "hit.h" 4 | #include "ray.h" 5 | #include "vec3.h" 6 | 7 | class Sphere : public Hittable { 8 | public: 9 | Sphere(const Vec3 ¢er, const real radius, const P_Material &material) : 10 | m_Center(center), m_Radius(radius), m_Material(material) {} 11 | 12 | virtual bool Hit(const Ray &ray, const real tmin, const real tmax, HitInfo &hit) const; 13 | 14 | virtual Ray RandomRay(const Vec3 &o) const; 15 | 16 | virtual real Pdf(const Ray &ray) const; 17 | 18 | private: 19 | Vec3 m_Center; 20 | real m_Radius; 21 | P_Material m_Material; 22 | }; 23 | -------------------------------------------------------------------------------- /tracer/src/stl.cpp: -------------------------------------------------------------------------------- 1 | #include "stl.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace boost::interprocess; 7 | 8 | std::vector LoadBinarySTL(std::string path) { 9 | file_mapping fm(path.c_str(), read_only); 10 | mapped_region mr(fm, read_only); 11 | uint8_t *src = (uint8_t *)mr.get_address(); 12 | const int numBytes = mr.get_size(); 13 | const int numTriangles = std::max(0, (numBytes - 84) / 50); 14 | const int numVertices = numTriangles * 3; 15 | std::vector result(numVertices); 16 | src += 96; 17 | for (int i = 0; i < numTriangles; i++) { 18 | const float *p = (float *)src; 19 | result[i*3+0] = Vec3(p[0], p[1], p[2]); 20 | result[i*3+1] = Vec3(p[3], p[4], p[5]); 21 | result[i*3+2] = Vec3(p[6], p[7], p[8]); 22 | src += 50; 23 | } 24 | return result; 25 | } 26 | -------------------------------------------------------------------------------- /tracer/src/stl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vec3.h" 7 | 8 | std::vector LoadBinarySTL(std::string path); 9 | -------------------------------------------------------------------------------- /tracer/src/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "vec3.h" 6 | 7 | class Texture { 8 | public: 9 | virtual Vec3 Sample(const real u, const real v, const Vec3 &p) const = 0; 10 | virtual ~Texture() {} 11 | }; 12 | 13 | typedef std::shared_ptr P_Texture; 14 | 15 | class SolidTexture : public Texture { 16 | public: 17 | SolidTexture(const Vec3 &color) : 18 | m_Color(color) {} 19 | 20 | virtual Vec3 Sample(const real u, const real v, const Vec3 &p) const { 21 | return m_Color; 22 | } 23 | 24 | private: 25 | Vec3 m_Color; 26 | }; 27 | 28 | class CheckeredTexture : public Texture { 29 | public: 30 | CheckeredTexture(const P_Texture &a, const P_Texture &b, const real size) : 31 | m_A(a), m_B(b), m_Frequency(2 * M_PI / size) {} 32 | 33 | virtual Vec3 Sample(const real u, const real v, const Vec3 &p) const { 34 | const real f = m_Frequency; 35 | const real s = std::sin(f * p.X()) * std::sin(f * p.Y()) * std::sin(f * p.Z()); 36 | if (s < 0) { 37 | return m_A->Sample(u, v, p); 38 | } else { 39 | return m_B->Sample(u, v, p); 40 | } 41 | } 42 | 43 | private: 44 | P_Texture m_A; 45 | P_Texture m_B; 46 | real m_Frequency; 47 | }; 48 | 49 | class GridTexture : public Texture { 50 | public: 51 | GridTexture(const P_Texture &a, const P_Texture &b, const real lineSpacing, const real lineWidth) : 52 | m_A(a), m_B(b), m_LineSpacing(lineSpacing), m_LineWidth(lineWidth) {} 53 | 54 | virtual Vec3 Sample(const real u, const real v, const Vec3 &p) const { 55 | const Vec3 q = Abs(Fract(p / m_LineSpacing - 0.5) - 0.5); 56 | const real line = std::min(q.X(), q.Z()); 57 | if (line > m_LineWidth / 2) { 58 | return m_A->Sample(u, v, p); 59 | } else { 60 | return m_B->Sample(u, v, p); 61 | } 62 | } 63 | 64 | private: 65 | P_Texture m_A; 66 | P_Texture m_B; 67 | real m_LineSpacing; 68 | real m_LineWidth; 69 | }; 70 | -------------------------------------------------------------------------------- /tracer/src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "vec3.h" 8 | 9 | inline real Random() { 10 | static thread_local std::mt19937 gen( 11 | std::chrono::high_resolution_clock::now().time_since_epoch().count()); 12 | std::uniform_real_distribution dist(real(0), real(1)); 13 | return dist(gen); 14 | } 15 | 16 | inline Vec3 RandomInUnitSphere() { 17 | while (true) { 18 | const Vec3 p = Vec3( 19 | Random() * 2 - 1, 20 | Random() * 2 - 1, 21 | Random() * 2 - 1); 22 | if (p.LengthSquared() < 1) { 23 | return p; 24 | } 25 | } 26 | } 27 | 28 | inline Vec3 RandomInUnitDisk() { 29 | while (true) { 30 | const Vec3 p = Vec3( 31 | Random() * 2 - 1, 32 | Random() * 2 - 1, 33 | 0); 34 | if (p.LengthSquared() < 1) { 35 | return p; 36 | } 37 | } 38 | } 39 | 40 | inline Vec3 CosineSampleHemisphere() { 41 | const Vec3 d = RandomInUnitDisk(); 42 | const real z = std::sqrt(std::max(real(0), 1 - d.X() * d.X() - d.Y() * d.Y())); 43 | return Vec3(d.X(), d.Y(), z); 44 | } 45 | 46 | inline Vec3 Reflect(const Vec3 &v, const Vec3 &n) { 47 | return v - 2 * Dot(v, n) * n; 48 | } 49 | 50 | inline bool Refract(const Vec3 &v, const Vec3 &n, const real ratio, Vec3 &refracted) { 51 | const Vec3 uv = Normalized(v); 52 | const real dt = Dot(uv, n); 53 | const real discriminant = 1 - ratio * ratio * (1 - dt * dt); 54 | if (discriminant <= 0) { 55 | return false; 56 | } 57 | refracted = ratio * (uv - n * dt) - n * std::sqrt(discriminant); 58 | return true; 59 | } 60 | 61 | inline real Schlick(const real cosine, const real index) { 62 | real r0 = (1 - index) / (1 + index); 63 | r0 = r0 * r0; 64 | return r0 + (1 - r0) * std::pow((1 - cosine), 5); 65 | } 66 | 67 | inline Vec3 Kelvin(const real K) { 68 | real red, green, blue; 69 | // red 70 | if (K >= 6600) { 71 | const real a = 351.97690566805693; 72 | const real b = 0.114206453784165; 73 | const real c = -40.25366309332127; 74 | const real x = K/100 - 55; 75 | red = a + b*x + c*std::log(x); 76 | } else { 77 | red = 255; 78 | } 79 | // green 80 | if (K >= 6600) { 81 | const real a = 325.4494125711974; 82 | const real b = 0.07943456536662342; 83 | const real c = -28.0852963507957; 84 | const real x = K/100 - 50; 85 | green = a + b*x + c*std::log(x); 86 | } else if (K >= 1000) { 87 | const real a = -155.25485562709179; 88 | const real b = -0.44596950469579133; 89 | const real c = 104.49216199393888; 90 | const real x = K/100 - 2; 91 | green = a + b*x + c*std::log(x); 92 | } else { 93 | green = 0; 94 | } 95 | // blue 96 | if (K >= 6600) { 97 | blue = 255; 98 | } else if (K >= 2000) { 99 | const real a = -254.76935184120902; 100 | const real b = 0.8274096064007395; 101 | const real c = 115.67994401066147; 102 | const real x = K/100 - 10; 103 | blue = a + b*x + c*std::log(x); 104 | } else { 105 | blue = 0; 106 | } 107 | red = std::min(real(1), red / 255); 108 | green = std::min(real(1), green / 255); 109 | blue = std::min(real(1), blue / 255); 110 | return Vec3(red, green, blue); 111 | } 112 | 113 | inline real Clamp(const real value, const real lo, const real hi) { 114 | if (value <= lo) { 115 | return lo; 116 | } 117 | if (value >= hi) { 118 | return hi; 119 | } 120 | return value; 121 | } 122 | 123 | inline real CosTheta(const Vec3 &w) { 124 | return w.Z(); 125 | } 126 | 127 | inline real AbsCosTheta(const Vec3 &w) { 128 | return std::abs(w.Z()); 129 | } 130 | 131 | inline real SinTheta2(const Vec3 &w) { 132 | return std::max(real(0), 1 - CosTheta(w) * CosTheta(w)); 133 | } 134 | 135 | inline real SinTheta(const Vec3 &w) { 136 | return std::sqrt(SinTheta2(w)); 137 | } 138 | 139 | inline real CosPhi(const Vec3 &w) { 140 | const real sintheta = SinTheta(w); 141 | if (sintheta == 0) { 142 | return 1; 143 | } 144 | return Clamp(w.X() / sintheta, -1, 1); 145 | } 146 | 147 | inline real SinPhi(const Vec3 &w) { 148 | const real sintheta = SinTheta(w); 149 | if (sintheta == 0) { 150 | return 0; 151 | } 152 | return Clamp(w.Y() / sintheta, -1, 1); 153 | } 154 | 155 | inline bool SameHemisphere(const Vec3 &a, const Vec3 &b) { 156 | return a.Z() * b.Z() > 0; 157 | } 158 | -------------------------------------------------------------------------------- /tracer/src/vec3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | class Vec3 { 10 | public: 11 | Vec3() : 12 | m_X(0), m_Y(0), m_Z(0) {} 13 | 14 | Vec3(real x) : 15 | m_X(x), m_Y(x), m_Z(x) {} 16 | 17 | Vec3(real x, real y, real z) : 18 | m_X(x), m_Y(y), m_Z(z) {} 19 | 20 | real X() const { return m_X; } 21 | real Y() const { return m_Y; } 22 | real Z() const { return m_Z; } 23 | 24 | real R() const { return m_X; } 25 | real G() const { return m_Y; } 26 | real B() const { return m_Z; } 27 | 28 | real Length() const { 29 | return std::sqrt(m_X * m_X + m_Y * m_Y + m_Z * m_Z); 30 | } 31 | 32 | real LengthSquared() const { 33 | return m_X * m_X + m_Y * m_Y + m_Z * m_Z; 34 | } 35 | 36 | Vec3 Normalized() const { 37 | const real length = Length(); 38 | return Vec3(m_X / length, m_Y / length, m_Z / length); 39 | } 40 | 41 | real MinComponent() const { 42 | return std::min(m_X, std::min(m_Y, m_Z)); 43 | } 44 | 45 | real MaxComponent() const { 46 | return std::max(m_X, std::max(m_Y, m_Z)); 47 | } 48 | 49 | bool operator==(const Vec3 &other) const { 50 | return m_X == other.m_X && m_Y == other.m_Y && m_Z == other.m_Z; 51 | } 52 | 53 | private: 54 | real m_X, m_Y, m_Z; 55 | }; 56 | 57 | inline std::ostream &operator<<(std::ostream &os, const Vec3 &v) { 58 | return os << "Vec3(" << v.X() << ", " << v.Y() << ", " << v.Z() << ")"; 59 | } 60 | 61 | namespace std { 62 | template <> 63 | struct hash { 64 | std::size_t operator()(const Vec3 &v) const { 65 | const auto h = std::hash(); 66 | return h(v.X()) ^ h(v.Y()) ^ h(v.Z()); 67 | } 68 | }; 69 | 70 | } 71 | 72 | // scalar 73 | inline Vec3 operator+(const Vec3 &v, const real t) { 74 | return Vec3(v.X() + t, v.Y() + t, v.Z() + t); 75 | } 76 | 77 | inline Vec3 operator-(const Vec3 &v, const real t) { 78 | return Vec3(v.X() - t, v.Y() - t, v.Z() - t); 79 | } 80 | 81 | inline Vec3 operator*(const real t, const Vec3 &v) { 82 | return Vec3(v.X() * t, v.Y() * t, v.Z() * t); 83 | } 84 | 85 | inline Vec3 operator*(const Vec3 &v, const real t) { 86 | return Vec3(v.X() * t, v.Y() * t, v.Z() * t); 87 | } 88 | 89 | inline Vec3 operator/(const Vec3 &v, const real t) { 90 | return Vec3(v.X() / t, v.Y() / t, v.Z() / t); 91 | } 92 | 93 | // vector 94 | inline Vec3 operator-(const Vec3 &a) { 95 | return Vec3(-a.X(), -a.Y(), -a.Z()); 96 | } 97 | 98 | inline Vec3 operator+(const Vec3 &a, const Vec3 &b) { 99 | return Vec3(a.X() + b.X(), a.Y() + b.Y(), a.Z() + b.Z()); 100 | } 101 | 102 | inline Vec3 operator-(const Vec3 &a, const Vec3 &b) { 103 | return Vec3(a.X() - b.X(), a.Y() - b.Y(), a.Z() - b.Z()); 104 | } 105 | 106 | inline Vec3 operator*(const Vec3 &a, const Vec3 &b) { 107 | return Vec3(a.X() * b.X(), a.Y() * b.Y(), a.Z() * b.Z()); 108 | } 109 | 110 | inline Vec3 operator/(const Vec3 &a, const Vec3 &b) { 111 | return Vec3(a.X() / b.X(), a.Y() / b.Y(), a.Z() / b.Z()); 112 | } 113 | 114 | // functions 115 | inline Vec3 Normalized(const Vec3 &v) { 116 | return v.Normalized(); 117 | } 118 | 119 | inline real Dot(const Vec3 &a, const Vec3 &b) { 120 | return a.X() * b.X() + a.Y() * b.Y() + a.Z() * b.Z(); 121 | } 122 | 123 | inline Vec3 Cross(const Vec3 &a, const Vec3 &b) { 124 | const real x = a.Y() * b.Z() - a.Z() * b.Y(); 125 | const real y = a.Z() * b.X() - a.X() * b.Z(); 126 | const real z = a.X() * b.Y() - a.Y() * b.X(); 127 | return Vec3(x, y, z); 128 | } 129 | 130 | inline Vec3 Pow(const Vec3 &v, const real a) { 131 | return Vec3(std::pow(v.X(), a), std::pow(v.Y(), a), std::pow(v.Z(), a)); 132 | } 133 | 134 | inline Vec3 Floor(const Vec3 &v) { 135 | return Vec3(std::floor(v.X()), std::floor(v.Y()), std::floor(v.Z())); 136 | } 137 | 138 | inline Vec3 Fract(const Vec3 &v) { 139 | return v - Floor(v); 140 | const real x = std::fmod(v.X(), 1); 141 | const real y = std::fmod(v.Y(), 1); 142 | const real z = std::fmod(v.Z(), 1); 143 | return Vec3(x, y, z); 144 | } 145 | 146 | inline Vec3 TriangleNormal(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3) { 147 | return Normalized(Cross(v2 - v1, v3 - v1)); 148 | } 149 | 150 | inline Vec3 Abs(const Vec3 &v) { 151 | return Vec3(std::abs(v.X()), std::abs(v.Y()), std::abs(v.Z())); 152 | } 153 | 154 | inline Vec3 Min(const Vec3 &a, const Vec3 &b) { 155 | return Vec3(std::min(a.X(), b.X()), std::min(a.Y(), b.Y()), std::min(a.Z(), b.Z())); 156 | } 157 | 158 | inline Vec3 Max(const Vec3 &a, const Vec3 &b) { 159 | return Vec3(std::max(a.X(), b.X()), std::max(a.Y(), b.Y()), std::max(a.Z(), b.Z())); 160 | } 161 | 162 | inline Vec3 HexColor(const int hex) { 163 | const real r = real((hex >> 16) & 0xff) / 255; 164 | const real g = real((hex >> 8) & 0xff) / 255; 165 | const real b = real((hex >> 0) & 0xff) / 255; 166 | return Pow(Vec3(r, g, b), 2.2); 167 | } 168 | --------------------------------------------------------------------------------