diff --git a/beautifulScene.cpp b/beautifulScene.cpp index bf561b5..cd32eef 100644 --- a/beautifulScene.cpp +++ b/beautifulScene.cpp @@ -23,45 +23,57 @@ #include #include #include +#include "scene/fastscene.h" +#include "shader/toneshader.h" -int main() -{ +int main() { SimpleScene scene; scene.setEnvironmentMap(std::make_shared("../data/clear_blue_sky.jpg")); // scene.setEnvironmentMap(std::make_shared("../data/TychoSkymapII.t5_04096x02048.png")); - scene.setBackgroundColor(Color(0.1,0.1,0.1)); + scene.setBackgroundColor(Color(0.1, 0.1, 0.1)); + + // Light + auto mainLight = std::make_shared(Vector3d(-6.0f, -0.5f, 2.0f), 2.0f, + Color(1, 1, 1));//Color(1, 0.79f, 0.62f)); + scene.add(mainLight); + scene.add(std::make_shared(0.1f)); // Set up the camera PerspectiveCamera camera; - camera.setFovAngle(90.0f); - camera.setPosition(Vector3d(0.0f, -2.0f, -5.0f)); - camera.setForwardDirection(Vector3d(1.0f, 0.1f, 0.2f)); + camera.setFovAngle(70.0f); + camera.setPosition(Vector3d(0.0f, 1.0f, 0.0f)); + camera.setForwardDirection(Vector3d(1.0f, 0.0f, 0.0f)); // Final camera Position - // camera.setPosition(Vector3d(0.0f, -4.9f, 0.0f)); - // camera.setForwardDirection(Vector3d(1.0f, 0.2f, 0.0f)); +// camera.setPosition(Vector3d(0.0f, -4.9f, 0.0f)); +// camera.setForwardDirection(Vector3d(1.0f, 0.2f, 0.0f)); camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f)); // Shader auto gold = std::make_shared(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 0.08f, 0.58f), 1.2f, 0.2f); + auto green = std::make_shared(mainLight, Color(1, 1, 1), Color(0.1f, 0.6f, 0.1f), Color(0,0,0)); auto orange = std::make_shared(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f); - auto white = std::make_shared(Color(0.9f, 0.9f, 0.9f)); - auto fiona_color = std::make_shared(Color(0.4f, 1.0f, 0.f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f); + auto white = std::make_shared(Color(0.9f, 0.9f, 0.9f)); + auto fiona_color = std::make_shared(Color(0.4f, 1.0f, 0.f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, + 25.0f); auto mirror = std::make_shared(); auto glass = std::make_shared(1.31f, 1.0f); // BRDF - auto gold_metallic_paint2 = std::make_shared("../data/BRDF/gold-metallic-paint2.binary", Color(7.0f, 7.0f, 7.0f)); - auto alumina_oxide = std::make_shared("../data/BRDF/alumina-oxide.binary", Color(7.0f, 7.0f, 7.0f)); + auto gold_metallic_paint2 = std::make_shared("../data/BRDF/gold-metallic-paint2.binary", + Color(2.0f, 5.0f, 7.0f)); + auto alumina_oxide = std::make_shared("../data/BRDF/alumina-oxide.binary", Color(7.0f, 0.0f, 5.0f)); + auto chrome_steel = std::make_shared("../data/BRDF/chrome-steel.binary", Color(0.0f, 7.0f, 2.0f)); // Make Objects auto house = std::make_shared(alumina_oxide); auto temple = std::make_shared(gold_metallic_paint2); - house->loadObj("../data/NewObjects/house/objBuilding.obj", Vector3d(1.0f, 1.0f, 1.0f), Vector3d(20.0f, -6.0f, 10.0f)); + house->loadObj("../data/NewObjects/house/objBuilding.obj", Vector3d(1.0f, 1.0f, 1.0f), + Vector3d(20.0f, -6.0f, 10.0f)); temple->loadObj("../data/NewObjects/Random/Temple.obj", Vector3d(0.1f, 0.1f, 0.1f), Vector3d(30.0f, -6.0f, -10.0f)); @@ -74,7 +86,10 @@ int main() // Add clouds auto cloudSettings = CloudSettings(); - cloudSettings.scale = 16.0f; + cloudSettings.seed = 42; + cloudSettings.lightAbsorptionTowardsLight = 0.6f; + cloudSettings.lightAbsorptionThroughCloud = 1.0f; + cloudSettings.densityOffset = -0.65f; auto cloudShader = std::make_shared(cloudSettings); scene.add(std::make_shared(Vector3d(30.0f, 10.0f, 0.0f), Vector3d(75.0f, 10.0f, 75.0f), cloudShader)); @@ -82,22 +97,15 @@ int main() // Insert Objects - scene.add(house); - scene.add(temple); - scene.add(std::make_shared(Vector3d(3.0f, -2.0f, -5.0f), 0.5f, mirror)); - - - - // Light - auto mainLight = std::make_shared(Vector3d(-10.0f, -0.5f, -1.0f), 2.0f, Color(1,1,1));//Color(1, 0.79f, 0.62f)); - scene.add(mainLight); - scene.add(std::make_shared(0.1f)); - + //scene.add(house); + //scene.add(temple); + //scene.add(std::make_shared(Vector3d(3.0f, -2.0f, -5.0f), 0.5f, mirror)); + scene.add(std::make_shared(Vector3d(6.0f, -2.0f, -1.0f), 5.0f, green)); // Render SimpleRenderer rendererTest; - int width = 1920; - Texture imageSceneToTest = rendererTest.renderImage(scene, camera, width, width/16 * 9); + int width = 512; + Texture imageSceneToTest = rendererTest.renderImage(scene, camera, width, width / 16 * 9); // initialize renderer: aperture = lens thickness, secondaryRayCount = how many rays per pixel are created // focalLength = the area which is in focus @@ -114,17 +122,17 @@ int main() */ // save images - // imageSceneToTest.save("result.png"); - // image.save("result.png"); - // image.save("resultWithBloom"); + imageSceneToTest.save("result1.png"); +// image.save("result.png"); +// image.save("resultWithBloom"); CImg image = imageSceneToTest.getImage(); CImg img_8bit(image.width(), image.height(), 1, 3); - cimg_forXYC(image,x,y,c) { - img_8bit(x,y,c) = (unsigned char)std::round(image(x, y, c) * 255); - } + cimg_forXYC(image, x, y, c) { + img_8bit(x, y, c) = (unsigned char) std::round(image(x, y, c) * 255); + } - CImgDisplay disp(img_8bit, "My Rendered Image",0, false, false); + CImgDisplay disp(img_8bit, "My Rendered Image", 0, false, false); while (!disp.is_closed()) { disp.wait(); disp.display(img_8bit); diff --git a/common/noise/cloudnoise.cpp b/common/noise/cloudnoise.cpp index 9469ff2..b98f72c 100644 --- a/common/noise/cloudnoise.cpp +++ b/common/noise/cloudnoise.cpp @@ -1,21 +1,50 @@ +#include +#include +#include #include "cloudnoise.h" -#include "worleynoise.h" -#include "perlinnoise.h" -CloudNoise::CloudNoise(int size, unsigned int seed) : Noise(size) +CloudNoise::CloudNoise(int size, unsigned int seed) : Noise(size), worleyNoise1(WorleyNoise(std::min(LOWRES_SIZE, size), 3, seed)), + worleyNoise3(WorleyNoise(size, 15, seed)), + perlinNoise1(PerlinNoise(std::min(LOWRES_SIZE, size), 3, seed)), + perlinNoise2(PerlinNoise(size, 15, seed)) { - int minSize = std::min(32, size); - - // Some worley noises - WorleyNoise worleyNoise1(minSize, 3, seed); - WorleyNoise worleyNoise3(size, 15, seed); - - // Some perlin noises - PerlinNoise perlinNoise1(minSize, 3, seed); - PerlinNoise perlinNoise2(size, 15, seed); + auto start = std::chrono::high_resolution_clock::now(); // Generate the noise - for (int x = 0; x < size; x++) + int const nThreads = (int) std::thread::hardware_concurrency() - 1; + int threadSize = std::floor((float) size / (float) nThreads); + int remaining = size - nThreads * threadSize; + std::vector threads; + for (int n = 0; n < nThreads; n++) + { + threads.emplace_back(runCloudNoiseInThread, n * threadSize, threadSize, this); + } + + renderNoiseThread(nThreads * threadSize, remaining); + + // Rejoin the threads + for (int t = 0; t < nThreads; ++t) + { + threads[t].join(); + } + + // Duration of computation + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(stop - start); + + std::cout << "Finished computing Cloud noise for size " << size << " in " + << duration.count() << " seconds" << std::endl; +} + + +void CloudNoise::runCloudNoiseInThread(int xOffset, int xSize, CloudNoise *noise) +{ + noise->renderNoiseThread(xOffset, xSize); +} + +void CloudNoise::renderNoiseThread(int xOffset, int xSize) +{ + for (int x = xOffset; x < xOffset + xSize; x++) { for (int y = 0; y < size; y++) { @@ -37,4 +66,4 @@ CloudNoise::CloudNoise(int size, unsigned int seed) : Noise(size) } } } -} +} \ No newline at end of file diff --git a/common/noise/cloudnoise.h b/common/noise/cloudnoise.h index aa4d60d..47879b0 100644 --- a/common/noise/cloudnoise.h +++ b/common/noise/cloudnoise.h @@ -3,6 +3,10 @@ #include "noise.h" +#include "worleynoise.h" +#include "perlinnoise.h" + +int const LOWRES_SIZE = 32; class CloudNoise : public Noise { @@ -12,7 +16,20 @@ public: * @param size * @param seed 0 for random seed */ - CloudNoise(int size, unsigned int seed = 0); + explicit CloudNoise(int size, unsigned int seed = 0); + + void renderNoiseThread(int xOffset, int xSize); + +private: + // Some worley noises + WorleyNoise worleyNoise1; + WorleyNoise worleyNoise3; + + // Some perlin noises + PerlinNoise perlinNoise1; + PerlinNoise perlinNoise2; + + static void runCloudNoiseInThread(int xOffset, int xSize, CloudNoise *noise); }; diff --git a/common/noise/perlinnoise.cpp b/common/noise/perlinnoise.cpp index a675bd1..4dbc880 100644 --- a/common/noise/perlinnoise.cpp +++ b/common/noise/perlinnoise.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include "perlinnoise.h" Vector3d PerlinNoise::randomGradient() @@ -72,6 +74,8 @@ PerlinNoise::PerlinNoise(int size, int gridSize, unsigned int seed) : Noise(size void PerlinNoise::generateNoise() { + auto start = std::chrono::high_resolution_clock::now(); + // Generate gradients gradients.clear(); gradients.resize(pow(gridSize, 3)); @@ -81,15 +85,21 @@ void PerlinNoise::generateNoise() } // Generate each pixel - for (int x = 0; x < size; x++) + int const nThreads = (int) std::thread::hardware_concurrency() - 1; + int threadSize = std::floor((float) size / (float) nThreads); + int remaining = size - nThreads * threadSize; + std::vector threads; + for (int n = 0; n < nThreads; n++) { - for (int y = 0; y < size; y++) - { - for (int z = 0; z < size; z++) - { - setNoise(x, y, z, getGradientValue(x, y, z)); - } - } + threads.emplace_back(runPerlinNoiseInThread, n * threadSize, threadSize, this); + } + + renderNoiseThread(nThreads * threadSize, remaining); + + // Rejoin the threads + for (int t = 0; t < nThreads; ++t) + { + threads[t].join(); } // Normalize cloudNoise map to [0, 1] @@ -100,6 +110,32 @@ void PerlinNoise::generateNoise() { value = (value - min) / (max - min); }); + + // Duration of computation + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(stop - start); + + std::cout << "Finished computing Perlin noise for size " << size << " and grid size " << gridSize << " in " + << duration.count() << " seconds" << std::endl; +} + +void PerlinNoise::runPerlinNoiseInThread(int xOffset, int xSize, PerlinNoise *noise) +{ + noise->renderNoiseThread(xOffset, xSize); +} + +void PerlinNoise::renderNoiseThread(int xOffset, int xSize) +{ + for (int x = xOffset; x < xOffset + xSize; x++) + { + for (int y = 0; y < size; y++) + { + for (int z = 0; z < size; z++) + { + setNoise(x, y, z, getGradientValue(x, y, z)); + } + } + } } float PerlinNoise::getCornerValue(Vector3d position, Vector3d corner) diff --git a/common/noise/perlinnoise.h b/common/noise/perlinnoise.h index 9a4837c..4976b12 100644 --- a/common/noise/perlinnoise.h +++ b/common/noise/perlinnoise.h @@ -16,6 +16,7 @@ public: */ PerlinNoise(int size, int gridSize, unsigned int seed = 0); + void renderNoiseThread(int xOffset, int xSize); private: void generateNoise(); std::normal_distribution distribution; @@ -29,6 +30,9 @@ private: float getGradientValue(int x, int y, int z); float getCornerValue(Vector3d position, Vector3d corner); + + static void runPerlinNoiseInThread(int xOffset, int xSize, PerlinNoise *noise); + }; diff --git a/common/noise/worleynoise.cpp b/common/noise/worleynoise.cpp index 3d65749..dde1593 100644 --- a/common/noise/worleynoise.cpp +++ b/common/noise/worleynoise.cpp @@ -2,9 +2,15 @@ #include #include #include +#include #include "worleynoise.h" #include "common/vector3d.h" +void WorleyNoise::runWorleyNoiseInThread(int xOffset, int xSize, WorleyNoise *noise) +{ + noise->renderNoiseThread(xOffset, xSize); +} + void WorleyNoise::generateNoise() { auto start = std::chrono::high_resolution_clock::now(); @@ -36,7 +42,43 @@ void WorleyNoise::generateNoise() noiseMap.clear(); noiseMap.resize(size * size * size); - for (int x = 0; x < size; x++) + int const nThreads = (int) std::thread::hardware_concurrency() - 1; + int threadSize = std::floor((float) size / (float) nThreads); + int remaining = size - nThreads * threadSize; + std::vector threads; + for (int n = 0; n < nThreads; n++) + { + threads.emplace_back(runWorleyNoiseInThread, n * threadSize, threadSize, this); + } + + renderNoiseThread(nThreads * threadSize, remaining); + + // Rejoin the threads + for (int t = 0; t < nThreads; ++t) + { + threads[t].join(); + } + + // Normalize cloudNoise map to [0, 1] + float min = *std::min_element(noiseMap.begin(), noiseMap.end()); + float max = *std::max_element(noiseMap.begin(), noiseMap.end()); + + std::for_each(noiseMap.begin(), noiseMap.end(), [min, max](float &value) + { + value = (value - min) / (max - min); + }); + + // Duration of computation + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(stop - start); + + std::cout << "Finished computing Worley noise for size " << size << " and " << numberOfPoints << " points in " + << duration.count() << " seconds" << std::endl; +} + +void WorleyNoise::renderNoiseThread(int xOffset, int xSize) +{ + for (int x = xOffset; x < xOffset + xSize; x++) { for (int y = 0; y < size; y++) { @@ -48,22 +90,6 @@ void WorleyNoise::generateNoise() } } } - - // Normalize getNoise map to [0, 1] - float min = *std::min_element(noiseMap.begin(), noiseMap.end()); - float max = *std::max_element(noiseMap.begin(), noiseMap.end()); - - for (int i = 0; i < noiseMap.size(); i++) - { - noiseMap[i] = (noiseMap[i] - min) / (max - min); - } - - // Duration of computation - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(stop - start); - - std::cout << "Finished computing Worley noise for size " << size << " and " << numberOfPoints << " points in " - << duration.count() << " seconds" << std::endl; } WorleyNoise::WorleyNoise(int size, int numberOfPoints, unsigned int seed) : numberOfPoints(numberOfPoints), Noise(size) diff --git a/common/noise/worleynoise.h b/common/noise/worleynoise.h index 3a37390..4994e5e 100644 --- a/common/noise/worleynoise.h +++ b/common/noise/worleynoise.h @@ -18,6 +18,8 @@ public: */ WorleyNoise(int size, int numberOfPoints, unsigned int seed = 0); + void renderNoiseThread(int xOffset, int xSize); + protected: int numberOfPoints; std::vector points; // 3D-Array, each cell represents a subcell. There are numberOfPoints^3 subcells. @@ -32,6 +34,8 @@ protected: void generateNoise(); std::vector getSubcellPoints(Vector3d point); + + static void runWorleyNoiseInThread(int xOffset, int xSize, WorleyNoise *noise); }; diff --git a/fancy1.cpp b/fancy1.cpp index f55e46e..9bcd97e 100644 --- a/fancy1.cpp +++ b/fancy1.cpp @@ -50,9 +50,9 @@ int main() // scene.add(std::make_shared(Vector3d(9.0f, 3.0f, 12.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader1)); // Add floor -// auto floorShader = std::make_shared(Color(0.9f, 0.9f, 0.9f)); -// scene.add(std::make_shared(Vector3d(0.0f, 0.0f, 0.0f), Vector3d(0.0f, 1.0f, 0.0f), -// floorShader)); + auto floorShader = std::make_shared(Color(0.9f, 0.9f, 0.9f)); + scene.add(std::make_shared(Vector3d(0.0f, 0.0f, 0.0f), Vector3d(0.0f, 1.0f, 0.0f), + floorShader)); // Add box for volume shader auto cloudSettings = CloudSettings(); @@ -72,7 +72,7 @@ int main() // Render the scene SuperRenderer sr; sr.setSuperSamplingFactor(1); - sr.renderImage(scene, camera, 512, 512).save("result.png"); + sr.renderImage(scene, camera, 2048, 2048).save("result.png"); return 0; } diff --git a/shader/cloudshader.cpp b/shader/cloudshader.cpp index dd8a11a..6358d1d 100644 --- a/shader/cloudshader.cpp +++ b/shader/cloudshader.cpp @@ -69,7 +69,7 @@ bool CloudShader::isTransparent() const } CloudShader::CloudShader(const CloudSettings &settings) : settings(settings), - cloudNoise(CloudNoise(NOISE_SIZE, settings.seed)) + cloudNoise(CloudNoise(settings.noiseSize, settings.seed)) { cloudNoise.invert = true; } diff --git a/shader/cloudshader.h b/shader/cloudshader.h index a85d394..4c549e0 100644 --- a/shader/cloudshader.h +++ b/shader/cloudshader.h @@ -7,11 +7,11 @@ #include "primitive/primitive.h" #include "common/noise/worleynoise.h" -int const NOISE_SIZE = 64; float const TRANSMITTANCE_BREAK = 0.0001f; // If transmittance goes below this limit, the cloud is considered opaque struct CloudSettings { + int noiseSize = 512; // 64 unsigned int seed = 0; // 0 for random seed float densitySteps = .2f; // .2f float scale = 30; // 30