diff --git a/projects/saf_r/CMakeLists.txt b/projects/saf_r/CMakeLists.txt index 2ad7029157765ee429ee6458b3d808433f6315d5..61ede8ae5a5cedac78ff5781aec20973854a3df7 100644 --- a/projects/saf_r/CMakeLists.txt +++ b/projects/saf_r/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(saf_r src/main.cpp "src/safrScene.hpp" - "src/safrScene.cpp") + ) # this should fix the execution path to load local files from the project (for MSVC) if(MSVC) diff --git a/projects/saf_r/shaders/raytracing.comp b/projects/saf_r/shaders/raytracing.comp index db7a1852b1f2b867eb28a1ff9a0c3d20ae054d21..fde08ae51b6c68dc556d57cab10e7c09a7e293d1 100644 --- a/projects/saf_r/shaders/raytracing.comp +++ b/projects/saf_r/shaders/raytracing.comp @@ -6,6 +6,7 @@ const float hitBias = 0.0001; // used to offset hits to avoid self intersectio layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; +//structs of materials, lights, spheres and intersection for use in compute shader struct Material { vec3 albedo; vec3 diffuse_color; @@ -23,6 +24,15 @@ struct Sphere{ Material material; }; +struct Intersection{ + bool hit; + vec3 pos; + vec3 N; + Material material; +}; + + +//incoming data layout(std430, binding = 0) coherent buffer lights{ Light inLights[]; }; @@ -39,6 +49,14 @@ layout( push_constant ) uniform constants{ int sphereCount; }; +/* +* the ray_intersect function checks, if a ray from the raytracer passes through the sphere, hits the sphere or passes by the the sphere +* @param vec3: origin of ray +* @param vec3: direction of ray +* @param float: distance of the ray to the sphere (out because there are no references in shaders) +* @return bool: if ray interesects sphere or not (out because there are no references in shaders) +*/ + bool ray_intersect(const vec3 origin, const vec3 dir, out float t0, const int id){ vec3 L = inSpheres[id].center - origin; float tca = dot(L, dir); @@ -58,19 +76,20 @@ bool ray_intersect(const vec3 origin, const vec3 dir, out float t0, const int id return true; } -struct Intersection{ - bool hit; - vec3 pos; - vec3 N; - Material material; -}; +/* +* @param vec3: Origin of the ray +* @param vec3: direction of the ray +* @return: Intersection struct with hit(bool) position, normal and material of sphere +*/ Intersection sceneIntersect(const vec3 rayOrigin, const vec3 rayDirection) { + //distance if spheres will be rendered float min_d = 100000; // lets start with something big Intersection intersection; intersection.hit = false; + //go over every sphere, check if sphere is hit by ray, save if hit is near enough into intersection struct for (int i = 0; i < sphereCount; i++) { float d; if (ray_intersect(rayOrigin, rayDirection, d, i)) { @@ -88,32 +107,45 @@ Intersection sceneIntersect(const vec3 rayOrigin, const vec3 rayDirection) { return intersection; } +/* +* @param vec3: Hit Position +* @param vec3: direction of ray +* @param vec3: N(ormal) +* @return vec3: new Hit position depending on hitBias (used to offset hits to avoid self intersection) +*/ vec3 biasHitPosition(vec3 hitPos, vec3 rayDirection, vec3 N){ - // return hitPos + N * hitBias; // works as long as no refraction/transmission is used and camera is outside sphere return hitPos + sign(dot(rayDirection, N)) * N * hitBias; } +/* +* @param Intersection: struct with all the data of the intersection +* @param vec3: ??? +* @param float: material albedo of the intersection +* @return colour/shadows of sphere with illumination +*/ vec3 computeHitLighting(Intersection intersection, vec3 V, out float outReflectionThroughput){ float lightIntensityDiffuse = 0; float lightIntensitySpecular = 0; + //iterate over every light source to compute sphere colours/shadows for (int i = 0; i < lightCount; i++) { - + + //compute normal + distance between light and intersection vec3 L = normalize(inLights[i].position - intersection.pos); float d = distance(inLights[i].position, intersection.pos); + //compute shadows vec3 shadowOrigin = biasHitPosition(intersection.pos, L, intersection.N); - Intersection shadowIntersection = sceneIntersect(shadowOrigin, L); - bool isShadowed = false; - if(shadowIntersection.hit) + if(shadowIntersection.hit){ isShadowed = distance(shadowIntersection.pos, shadowOrigin) < d; - - if(isShadowed) - continue; - + } + if(isShadowed){ + continue; + } + lightIntensityDiffuse += inLights[i].intensity * max(0.f, dot(L, intersection.N)); lightIntensitySpecular += pow(max(0.f, dot(reflect(V, intersection.N), L)), intersection.material.specular_exponent) * inLights[i].intensity; } @@ -122,6 +154,13 @@ vec3 computeHitLighting(Intersection intersection, vec3 V, out float outReflecti return intersection.material.diffuse_color * lightIntensityDiffuse * intersection.material.albedo[0] + lightIntensitySpecular * intersection.material.albedo[1]; } +/* +* @param vec3: initial origin of ray +* @param vec3: initial direction of ray +* @param int: max depth o ray reflection +* @return s +*/ + vec3 castRay(const vec3 initialOrigin, const vec3 initialDirection, int max_depth) { vec3 skyColor = vec3(0.2, 0.7, 0.8); @@ -130,27 +169,30 @@ vec3 castRay(const vec3 initialOrigin, const vec3 initialDirection, int max_dept float reflectionThroughput = 1; vec3 color = vec3(0); - + + //iterate to max depth of reflections for(int i = 0; i < max_depth; i++){ Intersection intersection = sceneIntersect(rayOrigin, rayDirection); - + vec3 hitColor; float hitReflectionThroughput; - + if(intersection.hit){ hitColor = computeHitLighting(intersection, rayDirection, hitReflectionThroughput); - } - else{ + }else{ hitColor = skyColor; } - + color += hitColor * reflectionThroughput; reflectionThroughput *= hitReflectionThroughput; - - if(!intersection.hit) + + //if there is no intersection of a ray with a sphere, break out of the loop + if(!intersection.hit){ break; + } + //compute new direction and origin of the reflected ray rayDirection = normalize(reflect(rayDirection, intersection.N)); rayOrigin = biasHitPosition(intersection.pos, rayDirection, intersection.N); } @@ -158,6 +200,10 @@ vec3 castRay(const vec3 initialOrigin, const vec3 initialDirection, int max_dept return color; } +/* +* @param ivec2: coordinates of the camera +* @return vec3: camera stuff what it sees or something????? +*/ vec3 computeDirection(ivec2 coord){ ivec2 outImageRes = imageSize(outImage); @@ -172,14 +218,6 @@ vec3 computeDirection(ivec2 coord){ float x = ndc.x * tanFovHalf * aspectRatio; float y = -ndc.y * tanFovHalf; - // z component must be chosen so vector length equals 1 - // setting z=1 and normalizing shortens x and y, changing the fov - // the final length must satisfy: - // 1 = |v| <=> - // 1 = sqrt(x^2 + y^2 + z^2) <=> - // 1 = x^2 + y^2 + z^2 <=> - // 1 - x^2 - y^2 = z^2 <=> - // sqrt(1 - x^2 - y^2) = z vec3 directionViewSpace = vec3(x, y, sqrt(1 - x*x - y*y)); vec3 directionWorldSpace = mat3(viewToWorld) * directionViewSpace; return directionWorldSpace; diff --git a/projects/saf_r/shaders/shader.frag b/projects/saf_r/shaders/shader.frag index 2403928a386ab981dc6f511500e00482df6b3276..64b45eb26b9831f6504e69882018e46120615615 100644 --- a/projects/saf_r/shaders/shader.frag +++ b/projects/saf_r/shaders/shader.frag @@ -6,10 +6,8 @@ layout(location = 1) in vec2 texCoord; layout(location = 0) out vec3 outColor; -layout(set=0, binding=0) uniform texture2D tex; layout(set=0, binding=1) uniform sampler textureSampler; void main() { outColor = fragColor; - outColor = texture(sampler2D(tex, textureSampler), texCoord).rgb; } \ No newline at end of file diff --git a/projects/saf_r/src/main.cpp b/projects/saf_r/src/main.cpp index 6078bf5ce2b9de3223c4db42c565b5ba46bd619c..572b844570eaca77c118627945a9ca8f7be0b1f4 100644 --- a/projects/saf_r/src/main.cpp +++ b/projects/saf_r/src/main.cpp @@ -15,6 +15,7 @@ int main(int argc, const char** argv) { const char* applicationName = "SAF_R"; + //window creation const int windowWidth = 800; const int windowHeight = 600; vkcv::Window window = vkcv::Window::create( @@ -44,14 +45,31 @@ int main(int argc, const char** argv) { std::string shaderPathCompute = "shaders/raytracing.comp"; + //creating the shader programs vkcv::ShaderProgram safrShaderProgram; vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram computeShaderProgram{}; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/shader.vert"), + [&safrShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + safrShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/shader.frag"), + [&safrShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + safrShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::COMPUTE, shaderPathCompute, [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { computeShaderProgram.addShader(shaderStage, path); }); + //create DescriptorSets (...) for every Shader + const vkcv::DescriptorBindings& descriptorBindings = safrShaderProgram.getReflectedDescriptors().at(0); + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + const vkcv::DescriptorBindings& computeDescriptorBindings = computeShaderProgram.getReflectedDescriptors().at(0); vkcv::DescriptorSetLayoutHandle computeDescriptorSetLayout = core.createDescriptorSetLayout(computeDescriptorBindings); @@ -65,21 +83,9 @@ int main(int argc, const char** argv) { } const vkcv::VertexLayout computeLayout(computeBindings); - - compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/shader.vert"), - [&safrShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - safrShaderProgram.addShader(shaderStage, path); - }); - - compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/shader.frag"), - [&safrShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - safrShaderProgram.addShader(shaderStage, path); - }); - - const vkcv::DescriptorBindings& descriptorBindings = safrShaderProgram.getReflectedDescriptors().at(0); - vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings); - vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); - vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + /* + * create the scene + */ //materials for the spheres std::vector<safrScene::Material> materials; @@ -102,14 +108,6 @@ int main(int argc, const char** argv) { lights.push_back(safrScene::Light(glm::vec3(-20, 20, 20), 1.5)); lights.push_back(safrScene::Light(glm::vec3(30, 50, -25), 1.8)); lights.push_back(safrScene::Light(glm::vec3(30, 20, 30), 1.7)); - //create the raytracer image for rendering - safrScene scene; - vkcv::asset::Texture texData = scene.render(spheres, lights); - - vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Unorm, texData.width, texData.height); - texture.fill(texData.data.data()); - texture.generateMipChainImmediate(); - texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); vkcv::SamplerHandle sampler = core.createSampler( vkcv::SamplerFilterType::LINEAR, @@ -119,7 +117,6 @@ int main(int argc, const char** argv) { ); - //create Buffer for compute shader vkcv::Buffer<safrScene::Light> lightsBuffer = core.createBuffer<safrScene::Light>( vkcv::BufferType::STORAGE, @@ -133,16 +130,12 @@ int main(int argc, const char** argv) { ); sphereBuffer.fill(spheres); - vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; - setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; - core.writeDescriptorSet(descriptorSet, setWrites); - vkcv::DescriptorWrites computeWrites; computeWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0,lightsBuffer.getHandle()), vkcv::BufferDescriptorWrite(1,sphereBuffer.getHandle())}; core.writeDescriptorSet(computeDescriptorSet, computeWrites); + const auto& context = core.getContext(); auto safrIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3, vkcv::BufferMemoryType::DEVICE_LOCAL); @@ -164,7 +157,7 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } - + //create the render pipeline + compute pipeline const vkcv::PipelineConfig safrPipelineDefinition{ safrShaderProgram, (uint32_t)windowWidth, @@ -192,8 +185,7 @@ int main(int argc, const char** argv) { vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); vkcv::DrawcallInfo drawcall(renderMesh, { descriptorUsage }, 1); - //const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - + //create the camera vkcv::camera::CameraManager cameraManager(window); uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); @@ -213,31 +205,36 @@ int main(int argc, const char** argv) { continue; } + //configure timer auto end = std::chrono::system_clock::now(); auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); start = end; time += 0.000001f * static_cast<float>(deltatime.count()); + //adjust light position lights[0].position.x += std::cos(time * 3.0f) * 2.5f; lights[1].position.z += std::cos(time * 2.5f) * 3.0f; lights[2].position.y += std::cos(time * 1.5f) * 4.0f; lightsBuffer.fill(lights); + //update camera cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); glm::mat4 proj = cameraManager.getActiveCamera().getProjection(); + //create pushconstants for render vkcv::PushConstants pushConstants(sizeof(glm::mat4) * 2); pushConstants.appendDrawcall(std::array<glm::mat4, 2>{ mvp, proj }); auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + //configure the outImage for compute shader (render into the swapchain image) computeWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, swapchainInput)}; core.writeDescriptorSet(computeDescriptorSet, computeWrites); - core.prepareImageForStorage (cmdStream, swapchainInput); + //fill pushconstants for compute shader struct RaytracingPushConstantData { glm::mat4 viewToWorld; int32_t lightCount; @@ -252,6 +249,7 @@ int main(int argc, const char** argv) { vkcv::PushConstants pushConstantsCompute(sizeof(RaytracingPushConstantData)); pushConstantsCompute.appendDrawcall(raytracingPushData); + //dispatch compute shader uint32_t computeDispatchCount[3] = {static_cast<uint32_t> (std::ceil( swapchainWidth/16.f)), static_cast<uint32_t> (std::ceil(swapchainHeight/16.f)), 1 }; // Anzahl workgroups diff --git a/projects/saf_r/src/safrScene.cpp b/projects/saf_r/src/safrScene.cpp deleted file mode 100644 index 18b6be310db52b44b50e1c5184d567460e1ca32d..0000000000000000000000000000000000000000 --- a/projects/saf_r/src/safrScene.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "safrScene.hpp" - - -glm::vec3 safrScene::reflect(const glm::vec3& dir, const glm::vec3& hit_center) { - return dir - hit_center * 2.f * (glm::dot(dir, hit_center)); -} - -bool safrScene::sceneIntersect(const glm::vec3& orig, const glm::vec3& dir, const std::vector<safrScene::Sphere>& spheres, - glm::vec3& hit, glm::vec3& hit_center, safrScene::Material& material) { - float spheres_dist = std::numeric_limits<float>::max(); - for (size_t i = 0; i < spheres.size(); i++) { - float dist_i; - if (spheres[i].ray_intersect(orig, dir, dist_i) && dist_i < spheres_dist) { - spheres_dist = dist_i; - hit = orig + dir * dist_i; - hit_center = glm::normalize(hit - spheres[i].center); - material = spheres[i].material; - } - } - return spheres_dist < 1000; -} - -glm::vec3 safrScene::castRay(const glm::vec3& orig, const glm::vec3& dir, const std::vector<safrScene::Sphere>& spheres, - const std::vector<safrScene::Light>& lights, size_t depth = 0) { - glm::vec3 point, hit_center; - safrScene::Material material; - - //return background color if a max recursive depth is reached - if (depth > 4 || !sceneIntersect(orig, dir, spheres, point, hit_center, material)) { - return glm::vec3(0.2, 0.7, 0.8); - } - - //compute recursive directions and origins of rays and then call the function - glm::vec3 reflect_dir = glm::normalize(reflect(dir, hit_center)); - glm::vec3 reflect_orig = (glm::dot(reflect_dir, hit_center) < 0) ? point - hit_center * static_cast<float>(1e-3) : - point + hit_center * static_cast<float>(1e-3); // offset the original point to avoid occlusion by the object itself - glm::vec3 reflect_color = castRay(reflect_orig, reflect_dir, spheres, lights, depth + 1); - - //compute shadows and other light properties for the returned ray color - float diffuse_light_intensity = 0, specular_light_intensity = 0; - for (size_t i = 0; i < lights.size(); i++) { - glm::vec3 light_dir = glm::normalize(lights[i].position - point); - float light_distance = glm::distance(lights[i].position, point); - - glm::vec3 shadow_orig = (glm::dot(light_dir, hit_center) < 0) ? point - hit_center * static_cast<float>(1e-3) : - point + hit_center * static_cast<float>(1e-3); // checking if the point lies in the shadow of the lights[i] - glm::vec3 shadow_pt, shadow_hit_center; - safrScene::Material tmpmaterial; - if (sceneIntersect(shadow_orig, light_dir, spheres, shadow_pt, shadow_hit_center, tmpmaterial) - && glm::distance(shadow_pt, shadow_orig) < light_distance) - continue; - diffuse_light_intensity += lights[i].intensity * std::max(0.f, glm::dot(light_dir, hit_center)); - specular_light_intensity += powf(std::max(0.f, glm::dot(reflect(light_dir, hit_center), dir)), material.specular_exponent) * lights[i].intensity; - } - return material.diffuse_color * diffuse_light_intensity * material.albedo[0] + - glm::vec3(1., 1., 1.) * specular_light_intensity * material.albedo[1] + reflect_color * material.albedo[2]; -} - -vkcv::asset::Texture safrScene::render(const std::vector<safrScene::Sphere>& spheres, const std::vector<safrScene::Light>& lights) { - //constants for the image data - const int width = 800; - const int height = 600; - const int fov = M_PI / 2.; - - //compute image format for the framebuffer and compute the ray colors for the image - std::vector<glm::vec3> framebuffer(width * height); -#pragma omp parallel for - for (size_t j = 0; j < height; j++) { - for (size_t i = 0; i < width; i++) { - //framebuffer[i + j * width] = glm::vec3(j / float(height), i / float(width), 0); - float x = (2 * (i + 0.5f) / (float)width - 1) * tan(fov / 2.f) * width / (float)height; - float y = -(2 * (j + 0.5f) / (float)height - 1) * tan(fov / 2.f); - glm::vec3 dir = glm::normalize(glm::vec3(x, y, -1)); - framebuffer[i + j * width] = castRay(glm::vec3(0, 0, 0), dir, spheres, lights); - } - } - - std::vector<uint8_t> data; - for (size_t i = 0; i < height * width; ++i) { - glm::vec3& c = framebuffer[i]; - float max = std::max(c[0], std::max(c[1], c[2])); - if (max > 1) c = c * (1.f / max); - data.push_back(static_cast<uint8_t>(255.f * framebuffer[i].x)); - data.push_back(static_cast<uint8_t>(255.f * framebuffer[i].y)); - data.push_back(static_cast<uint8_t>(255.f * framebuffer[i].z)); - data.push_back(static_cast<uint8_t>(255.f)); - } - - vkcv::asset::Texture textureData; - - textureData.width = width; - textureData.height = height; - textureData.channels = 4; - - textureData.data.resize(textureData.width * textureData.height * textureData.channels); - memcpy(textureData.data.data(), data.data(), textureData.data.size()); - return textureData; -} \ No newline at end of file diff --git a/projects/saf_r/src/safrScene.hpp b/projects/saf_r/src/safrScene.hpp index 09fdf5e53d33a103d50d4666f797529164161728..a8141efd5859bb2f30f064e2b83aac0e610972bc 100644 --- a/projects/saf_r/src/safrScene.hpp +++ b/projects/saf_r/src/safrScene.hpp @@ -36,12 +36,6 @@ public: /* * the sphere is defined by it's center, the radius and the material - * - * the ray_intersect function checks, if a ray from the raytracer passes through the sphere, hits the sphere or passes by the the sphere - * @param vec3: origin of ray - * @param vec3: direction of ray - * @param float: distance of the ray to the sphere - * @return bool: if ray interesects sphere or not */ struct Sphere { glm::vec3 center; @@ -50,56 +44,7 @@ public: Sphere(const glm::vec3& c, const float& r, const Material& m) : center(c), radius(r), material(m) {} - bool ray_intersect(const glm::vec3& origin, const glm::vec3& dir, float& t0) const { - glm::vec3 L = center - origin; - float tca = glm::dot(L, dir); - float d2 = glm::dot(L, L) - tca * tca; - if (d2 > radius * radius) return false; - float thc = sqrtf(radius * radius - d2); - t0 = tca - thc; - float t1 = tca + thc; - if (t0 < 0) t0 = t1; - if (t0 < 0) return false; - return true; - } }; - /* - * @param vector: all spheres in the scene - * @param vector: all light sources in the scene - * @return TextureData: texture data for the buffers - */ - vkcv::asset::Texture render(const std::vector<Sphere>& spheres, const std::vector<Light>& lights); - -private: - /* - * @param vec3 dir: direction of the ray - * @param vec3 hit_center: normalized vector between hit on the sphere and center of the sphere - * @return vec3: returns reflected vector for the new direction of the ray - */ - glm::vec3 reflect(const glm::vec3& dir, const glm::vec3& hit_center); - - /* - * @param orig: Origin of the ray - * @param dir: direction of the ray - * @param vector: vector of all spheres in the scene - * @param vec3 hit: returns the vector from the origin of the ray to the closest sphere - * @param vec3 N: normalizes the vector from the origin of the ray to the closest sphere center - * @param Material: returns the material of the closest sphere - * @return: closest sphere distance if it's < 1000 - */ - bool sceneIntersect(const glm::vec3& orig, const glm::vec3& dir, const std::vector<Sphere>& spheres, - glm::vec3& hit, glm::vec3& hit_center, Material& material); - - /* - * @param vec3 orig: origin of the ray - * @param vec3 dir: direction of the ray - * @param vector: all spheres in the scene - * @param vector: all light sources of the scene - * @param depth = 0: initial recrusive depth - * @return color of the pixel depending on material and light - */ - glm::vec3 castRay(const glm::vec3& orig, const glm::vec3& dir, const std::vector<Sphere>& spheres, - const std::vector<Light>& lights, size_t depth); }; \ No newline at end of file