Compare commits

...

50 commits

Author SHA1 Message Date
4994a23a8a Some adjustments 2023-01-29 22:07:21 +01:00
159e9ca843 Merge remote-tracking branch 'origin/master' 2023-01-29 21:50:08 +01:00
8988af2e0f Some beautiful scenes 2023-01-29 21:50:01 +01:00
m.gaedke
2d996602a4 Merge remote-tracking branch 'origin/master' 2023-01-29 18:17:55 +01:00
m.gaedke
e4305190ea Scene for Bloom effect and some variable adjustments 2023-01-29 18:17:28 +01:00
Tobias
a99159dc53 BeautifulScene prepared. 2023-01-29 17:02:37 +01:00
4baf2901bb Parallel noise processing for all noise and fixed previous errors 2023-01-29 16:27:15 +01:00
a4d7ccc1a5 Parallel noise processing 2023-01-29 15:48:17 +01:00
9b07411ebd Removes unused CMake Code 2023-01-29 12:21:30 +01:00
9837920c38 Merge remote-tracking branch 'origin/master' 2023-01-29 12:19:30 +01:00
9608281164 Adds seed to noise and CloudSettings 2023-01-29 12:19:19 +01:00
m.gaedke
ed47dc2b9b some beautiful scenes, dont render DOFScene it takes 8hours ahaha we need gpu acceleration 2023-01-29 12:10:57 +01:00
ffaaa7e814 Alternative parameters for clouds. Defaults in comments 2023-01-29 04:44:49 +01:00
dd92e85861 I am a cloud god now 2023-01-29 04:25:14 +01:00
m.gaedke
133f8444d2 scene to view our effects 2023-01-27 13:49:07 +01:00
m.gaedke
6159650a00 putting homework files in one directory to clean up repository 2023-01-27 10:46:20 +01:00
c1bae8a43c Merge branch 'partial-occlusion-lighting' 2023-01-27 05:37:17 +01:00
9b228bff61 SOme adjustments 2023-01-27 05:36:27 +01:00
81e9a162be Reset some values and a somewhat decent result 2023-01-27 05:27:42 +01:00
79446c289b Clouds still bad 2023-01-27 05:22:54 +01:00
d7c093944d Fixed sunlight not interacting with clouds 2023-01-27 05:22:43 +01:00
m.gaedke
128581d469 changed simplerenderer so that using the post processing is happening in main and not in any random methods 2023-01-26 23:17:02 +01:00
m.gaedke
ce1fe4dd9a Merge branch 'Bloomshader'
Adding DOFRenderer and postProcessing bloom effect to master
2023-01-26 23:00:20 +01:00
m.gaedke
f5347bb07a Field of View renderer finally working pretty good + some restructioring of code 2023-01-26 22:56:23 +01:00
3f3c89b0d0 Performance improvement 2023-01-26 20:32:40 +01:00
ec59f251c7 Removes wrong intial value for density 2023-01-26 20:30:59 +01:00
cc5b66e338 Fixed shadow not being fully transparent 2023-01-26 20:14:45 +01:00
8a1ec659a2 Clouds now interacting with lights, but especially sunlight is still buggy 2023-01-26 05:04:30 +01:00
m.gaedke
f24011f642 DepthofField Function added in this branch still runs into segFault 2023-01-25 21:03:21 +01:00
m.gaedke
0d4814c901 Bloom shader finished 2023-01-25 10:20:27 +01:00
arvid schröder
9dd5878f31 fixed nullptr 2023-01-25 00:07:44 +01:00
da1f885e9a Trying to add volumetric light to clouds 2023-01-24 22:55:48 +01:00
5db502d77a Adds concept of distance to illumination 2023-01-24 22:03:33 +01:00
17fd35fe76 Adds rich transparency for shaders and implements if for light calculations 2023-01-24 21:11:30 +01:00
dcae587b8c Added light loss to refraction shader 2023-01-24 20:23:16 +01:00
3b76969ff2 Colored refraction shader 2023-01-24 19:59:13 +01:00
7ec5a8a5f4 Some experiments for Cloud lighting 2023-01-24 19:38:12 +01:00
m.gaedke
9a8d6bcccd Bloom shader as post-processing 2023-01-24 13:32:19 +01:00
d7d38944ea Implements SunLight 2023-01-24 11:31:09 +01:00
e5575c03f5 Some more adjustments to clouds 2023-01-24 11:18:03 +01:00
0c7071524a Fixed wrong text 2023-01-24 08:53:38 +01:00
66959c3599 Proper white color for clouds 2023-01-24 08:39:13 +01:00
0eef1f8e6e Cloud noise adjustments 2023-01-24 08:35:10 +01:00
a90441c255 Updated fancy scene 2023-01-24 07:54:33 +01:00
53a4a6d1f0 Fixes optimization issue and scaling inconsistency 2023-01-24 07:52:55 +01:00
98c3f97eea Huge optimization 2023-01-24 07:04:58 +01:00
1dd7d845d9 Specific cloud noise 2023-01-24 06:44:20 +01:00
3b644c8d29 Oh boi, more noise! 2023-01-24 06:26:24 +01:00
720aafbb9a Here I go starting my noise adventure. And initial version of fancy scene 2023-01-24 05:22:40 +01:00
a374c31938 Added fun depth shader 2023-01-23 04:09:48 +01:00
59 changed files with 12461 additions and 132 deletions

2
.gitignore vendored
View file

@ -7,6 +7,8 @@ build/
/data/* /data/*
!/data/README.txt !/data/README.txt
!/data/fireplace !/data/fireplace
!/data/FancyPlane
!/data/Bus
!/data/subdiv !/data/subdiv
!/data/parallax !/data/parallax
!/data/fractal.obj !/data/fractal.obj

View file

@ -17,32 +17,45 @@ endif()
# which source files to use # which source files to use
file(GLOB common_src "common/*.cpp") file(GLOB common_src "common/*.cpp")
file(GLOB noise_src "common/noise/*.cpp")
file(GLOB camera_src "camera/*.cpp") file(GLOB camera_src "camera/*.cpp")
file(GLOB light_src "light/*.cpp") file(GLOB light_src "light/*.cpp")
file(GLOB primitive_src "primitive/*.cpp") file(GLOB primitive_src "primitive/*.cpp")
file(GLOB renderer_src "renderer/*.cpp") file(GLOB renderer_src "renderer/*.cpp")
file(GLOB scene_src "scene/*.cpp") file(GLOB scene_src "scene/*.cpp")
file(GLOB shader_src "shader/*.cpp") file(GLOB shader_src "shader/*.cpp")
file(GLOB post_processing_src "post_processing/*.cpp")
file(GLOB homework_src "homeworkMains/*.cpp")
# The tracey library # The tracey library
add_library(tracey STATIC ${common_src} ${camera_src} ${light_src} add_library(tracey STATIC ${common_src} ${noise_src} ${camera_src} ${light_src}
${primitive_src} ${renderer_src} ${scene_src} ${shader_src} light/ambientlight.cpp light/ambientlight.h light/spotlight.cpp light/spotlight.h) ${primitive_src} ${renderer_src} ${scene_src} ${shader_src}
${post_processing_src} ${homework_src})
if(NOT WIN32) if(NOT WIN32)
target_link_libraries(tracey ${CMAKE_THREAD_LIBS_INIT} ${X11_LIBRARIES}) target_link_libraries(tracey ${CMAKE_THREAD_LIBS_INIT} ${X11_LIBRARIES})
endif() endif()
# Executables # Executables
add_executable(tracey_ex1 ex1.cpp) add_executable(beautifulScene beautifulScene.cpp)
target_link_libraries(beautifulScene tracey)
add_executable(beautifulSceneEpic beautifulSceneEpic.cpp)
target_link_libraries(beautifulSceneEpic tracey)
add_executable(beautifulSceneDark beautifulSceneDark.cpp)
target_link_libraries(beautifulSceneDark tracey)
add_executable(tracey_ex1 homeworkMains/ex1.cpp)
target_link_libraries(tracey_ex1 tracey) target_link_libraries(tracey_ex1 tracey)
add_executable(tracey_ex2 ex2.cpp) add_executable(tracey_ex2 homeworkMains/ex2.cpp)
target_link_libraries(tracey_ex2 tracey) target_link_libraries(tracey_ex2 tracey)
add_executable(tracey_ex3_1 ex3.cpp) add_executable(tracey_ex3_1 homeworkMains/ex3.cpp)
target_link_libraries(tracey_ex3_1 tracey) target_link_libraries(tracey_ex3_1 tracey)
target_compile_definitions(tracey_ex3_1 PUBLIC SITUATION=1) target_compile_definitions(tracey_ex3_1 PUBLIC SITUATION=1)
add_executable(tracey_ex3_2 ex3.cpp) add_executable(tracey_ex3_2 homeworkMains/ex3.cpp)
target_link_libraries(tracey_ex3_2 tracey) target_link_libraries(tracey_ex3_2 tracey)
target_compile_definitions(tracey_ex3_2 PUBLIC SITUATION=2) target_compile_definitions(tracey_ex3_2 PUBLIC SITUATION=2)
if("${CMAKE_CURRENT_LIST_DIR}/light/ambientlight.cpp" IN_LIST light_src) if("${CMAKE_CURRENT_LIST_DIR}/light/ambientlight.cpp" IN_LIST light_src)
@ -55,19 +68,32 @@ if("${CMAKE_CURRENT_LIST_DIR}/primitive/objmodel.cpp" IN_LIST primitive_src)
add_definitions(-DOBJMODEL_FOUND) add_definitions(-DOBJMODEL_FOUND)
endif() endif()
add_executable(tracey_ex4 ex4.cpp) add_executable(tracey_ex4 homeworkMains/ex4.cpp)
target_link_libraries(tracey_ex4 tracey) target_link_libraries(tracey_ex4 tracey)
add_executable(tracey_ex5 ex5.cpp) add_executable(tracey_ex5 homeworkMains/ex5.cpp)
target_link_libraries(tracey_ex5 tracey) target_link_libraries(tracey_ex5 tracey)
add_executable(tracey_ex6 ex6.cpp) add_executable(tracey_ex6 homeworkMains/ex6.cpp)
target_link_libraries(tracey_ex6 tracey) target_link_libraries(tracey_ex6 tracey)
if("${CMAKE_CURRENT_LIST_DIR}/renderer/superrenderer.cpp" IN_LIST renderer_src) if("${CMAKE_CURRENT_LIST_DIR}/renderer/superrenderer.cpp" IN_LIST renderer_src)
add_definitions(-DSUPERRENDERER_FOUND) add_definitions(-DSUPERRENDERER_FOUND)
endif() endif()
add_executable(fancy1 fancy1.cpp)
target_link_libraries(fancy1 tracey)
add_executable(bloom bloom.cpp)
target_link_libraries(bloom tracey)
add_executable(DOFScene DOFScene.cpp)
target_link_libraries(DOFScene tracey)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")

123
DOFScene.cpp Normal file
View file

@ -0,0 +1,123 @@
#include <iostream>
#include <string>
#include <shader/refractionshader.h>
#include "camera/perspectivecamera.h"
#include "renderer/simplerenderer.h"
#include "scene/simplescene.h"
#include "primitive/box.h"
#include "primitive/infiniteplane.h"
#include "primitive/objmodel.h"
#include "primitive/sphere.h"
#include "shader/brdfshader.h"
#include "shader/lambertshader.h"
#include "shader/mirrorshader.h"
#include "shader/phongshader.h"
#include "shader/cooktorranceshader.h"
#include "renderer/depthoffieldrenderer.h"
#include "light/ambientlight.h"
#include "light/pointlight.h"
#include "light/spotlight.h"
#include "post_processing/bloom.h"
int main() {
// Let's create a simple scene...
SimpleScene scene;
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, 0.0f, -10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.0f, 1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Add shaders
auto mirror = std::make_shared<MirrorShader>();
auto white = std::make_shared<LambertShader>(Color(0.9f, 0.9f, 0.9f));
auto red = std::make_shared<LambertShader>(Color(1.0f, 0.3f, 0.2f));
auto blue = std::make_shared<LambertShader>(Color(0.2f, 0.3f, 1.0f));
auto orange = std::make_shared<PhongShader>(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f);
auto gold= std::make_shared<CookTorranceShader>(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 1.0f, 0.0f), 1.2f, 0.2f);
auto blueMetallic = std::make_shared<BrdfShader>("../data/blue-metallic-paint.binary", Color(7.0f, 7.0f, 7.0f));
auto darkRed = std::make_shared<BrdfShader>("../data/dark-red-paint.binary", Color(7.0f, 7.0f, 7.0f));
auto glass = std::make_shared<RefractionShader>(1.31f, 1.0f);
// Set up the walls
// ---------------------------------------------------------------------------
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, +5.0f), Vector3d(0.0f, 0.0f, -1.0f), mirror));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, -5.0f), Vector3d(0.0f, 0.0f, +1.0f), mirror));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, +5.0f, 0.0f), Vector3d(0.0f, -1.0f, 0.0f), white));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, -5.0f, 0.0f), Vector3d(0.0f, +1.0f, 0.0f), white));
scene.add(std::make_shared<InfinitePlane>(Vector3d(+5.0f, 0.0f, 0.0f), Vector3d(-1.0f, 0.0f, 0.0f), blue));
scene.add(std::make_shared<InfinitePlane>(Vector3d(-5.0f, 0.0f, 0.0f), Vector3d(+1.0f, 0.0f, 0.0f), red));
scene.add(std::make_shared<Sphere>(Vector3d(-5.0f, 0.0f, 0.0f), 1.0f, blueMetallic));
scene.add(std::make_shared<Sphere>(Vector3d(-2.0f, 5.0f, 0.0f), 1.0f, orange));
scene.add(std::make_shared<Sphere>(Vector3d(2.0f, 4.0f, 0.0f), 1.0f, orange));
scene.add(std::make_shared<Sphere>(Vector3d(3.0f, 4.0f, -2.0f), 1.0f, glass));
scene.add(std::make_shared<Sphere>(Vector3d(2.0f, 3.0f, 4.0f), 1.0f, orange));
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, 3.0f, 2.0f), 1.0f, orange));
scene.add(std::make_shared<Sphere>(Vector3d(2.0f, 1.0f, 0.0f), 1.0f, mirror));
scene.add(std::make_shared<Sphere>(Vector3d(3.0f, -3.0f, 0.0f), 1.0f, darkRed));
scene.add(std::make_shared<Sphere>(Vector3d(5.0f, 0.0f, 0.0f), 1.0f, darkRed));
scene.add(std::make_shared<Sphere>(Vector3d(-5.0f, -3.3f, -4.0f), 1.0f, mirror));
// Add the teapot
auto ship = std::make_shared<ObjModel>(gold);
ship->loadObj("../data/NewObjects/Random/Ship.obj", Vector3d(0.07f, 0.07f, 0.07f), Vector3d(-1.0f, -5.0f, 0.0f));
scene.add(ship);
// Add ambient light
scene.add(std::make_shared<AmbientLight>(0.15f));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, -4.0f), 7.0f));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 2.5f, -4.0f), 7.0f));
// Render the scene
// SimpleRenderer renderer;
// renderer.renderImage(scene, camera, 1024, 1024).save("result.png");
// initialize renderer: aperture = lens thickness, secondaryRayCount = how many rays per pixel are created
// focalLength = the area which is in focus
DOFRenderer renderer(0.2, 50, 10.0f);
// Use DOFRenderer to raytrace !!! careful more pixels lead to insane rendering times
int width = 600;
Texture image = renderer.renderImage(scene, camera, width, width / 16 * 9);
// Use post-processing Bloom effect
// Bloom bloomEffect = Bloom(image.getImage());
// Texture imageWithBloom = image;
// imageWithBloom.setTexture(bloomEffect.bloom(0.55f, 5, 10.0f, 0.06f));
// save images
image.save("result.png");
CImg<float> CImgOfImage = image.getImage();
CImg<unsigned char> img_8bit(image.width(), image.height(), 1, 3);
cimg_forXYC(CImgOfImage,x,y,c) {
img_8bit(x,y,c) = (unsigned char)std::round(CImgOfImage(x, y, c) * 255);
}
CImgDisplay disp(img_8bit, "My Rendered Image",0, false, false);
while (!disp.is_closed()) {
disp.wait();
disp.display(img_8bit);
if (disp.is_resized()) {
disp.resize();
}
}
// image.save("resultWithBloom");
return 0;
}

145
beautifulScene.cpp Normal file
View file

@ -0,0 +1,145 @@
#include <scene/simplescene.h>
#include <camera/perspectivecamera.h>
#include <shader/materialshader.h>
#include <primitive/sphere.h>
#include <light/ambientlight.h>
#include <light/pointlight.h>
#include <renderer/depthoffieldrenderer.h>
#include <post_processing/bloom.h>
#include <primitive/objmodel.h>
#include <shader/lambertshader.h>
#include <renderer/simplerenderer.h>
#include <shader/brdfshader.h>
#include <thread>
//#include <conio.h>
#include <shader/cooktorranceshader.h>
#include <shader/phongshader.h>
#include <primitive/infiniteplane.h>
#include <light/spotlight.h>
#include <shader/cloudshader.h>
#include <shader/mirrorshader.h>
#include <shader/refractionshader.h>
#include <primitive/triangle.h>
#include <shader/simpleshadowshader.h>
#include <light/sunlight.h>
#include "scene/fastscene.h"
#include "shader/toneshader.h"
#include "renderer/superrenderer.h"
int main() {
SimpleScene scene;
scene.setEnvironmentMap(std::make_shared<Texture>("data/clear_blue_sky.jpg"));
// scene.setEnvironmentMap(std::make_shared<Texture>("data/TychoSkymapII.t5_04096x02048.png"));
scene.setBackgroundColor(Color(0.1, 0.1, 0.1));
// Light
auto mainLight = std::make_shared<SunLight>(Vector3d(.5f, -.7f, .5f), 2.0f,
Color(1, 1, 1));//Color(1, 0.79f, 0.62f));
scene.add(mainLight);
scene.add(std::make_shared<AmbientLight>(.1f));
// Set up the camera
PerspectiveCamera camera;
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.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Shader
auto church = std::make_shared<SimpleShadowShader>(Color(0.6f, 0.4f, 0.2f));
auto mirror = std::make_shared<MirrorShader>();
auto glass = std::make_shared<RefractionShader>(1.31f, 1.0f);
// Make Objects
auto house = std::make_shared<ObjModel>(church);
// auto temple = std::make_shared<ObjModel>(glass);
house->loadObj("data/NewObjects/house/objBuilding.obj", Vector3d(1.0f, 1.0f, 1.0f) * 0.7f,
Vector3d(43.0f, 1.5f, -9.0f));
// temple->loadObj("data/NewObjects/Random/Temple.obj", Vector3d(0.1f, 0.1f, 0.1f), Vector3d(30.0f, -6.0f, -10.0f));
// Setup ground and sky
// Add floor
// scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, -5.0f, 0.0f), Vector3d(0.0f, 1.0f, 0.0f), church));
// Add clouds
auto cloudSettings = CloudSettings();
cloudSettings.seed = 42;
cloudSettings.lightAbsorptionTowardsLight = 0.2f;
cloudSettings.lightAbsorptionThroughCloud = 1.9f;
cloudSettings.densityOffset = -0.65f;
cloudSettings.shadowIntensity = 0.4f;
auto cloudShader = std::make_shared<CloudShader>(cloudSettings);
scene.add(std::make_shared<Box>(Vector3d(0.0f, 15.0f, 0.0f), Vector3d(200.0f, 10.0f, 300.0f), cloudShader));
// Insert Objects
scene.add(house);
//scene.add(temple);
//scene.add(std::make_shared<Sphere>(Vector3d(3.0f, -2.0f, -5.0f), 0.5f, mirror));
float f = 0.7;
scene.add(std::make_shared<Sphere>(Vector3d(10.0f, -6.5f, 5.5f), 5.0f, std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(15.0f, -21.0f, -9.0f), 20.0f, std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(23.0f, -12.0f, 5.0f), 10.0f, std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(30.0f, -15.0f, 19.0f), 15.0f, std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(45.0f, -38.0f, -9.0f), 40.0f, std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(52.0f, -28.0f, 20.0f), 30.0f, std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
// Render
SuperRenderer rendererTest;
rendererTest.setSuperSamplingFactor(1);
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
// DOFRenderer renderer(0.2, 100, 70.0f);
// Use DOFRenderer to raytrace !!! careful more pixels lead to insane rendering times
// Texture image = renderer.renderImage(scene, camera, 1920, 1080);
// Use post-processing Bloom effect
/*
Bloom bloomEffect = Bloom(image.getImage());
Texture imageWithBloom = image;
imageWithBloom.setTexture(bloomEffect.bloom(0.55f, 5, 10.0f, 0.06f));
*/
// save images
imageSceneToTest.save("result.png");
// image.save("result.png");
// image.save("resultWithBloom");
// CImg<float> image = imageSceneToTest.getImage();
// CImg<unsigned char> 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);
// }
//
// CImgDisplay disp(img_8bit, "My Rendered Image", 0, false, false);
// while (!disp.is_closed()) {
// disp.wait();
// disp.display(img_8bit);
// if (disp.is_resized()) {
// disp.resize();
// }
// }
return 0;
}

152
beautifulSceneDark.cpp Normal file
View file

@ -0,0 +1,152 @@
#include <scene/simplescene.h>
#include <camera/perspectivecamera.h>
#include <shader/materialshader.h>
#include <primitive/sphere.h>
#include <light/ambientlight.h>
#include <light/pointlight.h>
#include <renderer/depthoffieldrenderer.h>
#include <post_processing/bloom.h>
#include <primitive/objmodel.h>
#include <shader/lambertshader.h>
#include <renderer/simplerenderer.h>
#include <shader/brdfshader.h>
#include <thread>
//#include <conio.h>
#include <shader/cooktorranceshader.h>
#include <shader/phongshader.h>
#include <primitive/infiniteplane.h>
#include <light/spotlight.h>
#include <shader/cloudshader.h>
#include <shader/mirrorshader.h>
#include <shader/refractionshader.h>
#include <primitive/triangle.h>
#include <shader/simpleshadowshader.h>
#include <light/sunlight.h>
#include "scene/fastscene.h"
#include "shader/toneshader.h"
#include "renderer/superrenderer.h"
int main()
{
SimpleScene scene;
scene.setEnvironmentMap(std::make_shared<Texture>("data/clear_red_sky.jpg"));
// scene.setEnvironmentMap(std::make_shared<Texture>("data/TychoSkymapII.t5_04096x02048.png"));
scene.setBackgroundColor(Color(0.2, 0.1, 0.1) * .5f);
// Light
auto mainLight = std::make_shared<SunLight>(Vector3d(-.9f, -.7f, .5f), 2.0f,
Color(1, 0.79f, 0.62f));
scene.add(mainLight);
// scene.add(std::make_shared<AmbientLight>(.1f));
// Set up the camera
PerspectiveCamera camera;
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.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Shader
auto church = std::make_shared<SimpleShadowShader>(Color(0.6f, 0.4f, 0.2f));
auto mirror = std::make_shared<MirrorShader>();
auto glass = std::make_shared<RefractionShader>(1.31f, 1.0f);
// Make Objects
auto house = std::make_shared<ObjModel>(church);
// auto temple = std::make_shared<ObjModel>(glass);
house->loadObj("data/NewObjects/house/objBuilding.obj", Vector3d(1.0f, 1.0f, 1.0f) * 0.7f,
Vector3d(43.0f, 1.5f, -9.0f));
// temple->loadObj("data/NewObjects/Random/Temple.obj", Vector3d(0.1f, 0.1f, 0.1f), Vector3d(30.0f, -6.0f, -10.0f));
// Setup ground and sky
// Add floor
// scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, -5.0f, 0.0f), Vector3d(0.0f, 1.0f, 0.0f), church));
// Add clouds
auto cloudSettings = CloudSettings();
cloudSettings.seed = 42;
cloudSettings.lightAbsorptionTowardsLight = 0.2f;
cloudSettings.lightAbsorptionThroughCloud = 1.9f;
cloudSettings.densityOffset = -0.65f;
cloudSettings.shadowIntensity = 0.4f;
auto cloudShader = std::make_shared<CloudShader>(cloudSettings);
scene.add(std::make_shared<Box>(Vector3d(0.0f, 15.0f, 0.0f), Vector3d(200.0f, 10.0f, 300.0f), cloudShader));
// Insert Objects
scene.add(house);
//scene.add(temple);
//scene.add(std::make_shared<Sphere>(Vector3d(3.0f, -2.0f, -5.0f), 0.5f, mirror));
float f = 0.5;
scene.add(std::make_shared<Sphere>(Vector3d(10.0f, -6.5f, 5.5f), 5.0f,
std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(15.0f, -21.0f, -9.0f), 20.0f,
std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(23.0f, -12.0f, 5.0f), 10.0f,
std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(30.0f, -15.0f, 19.0f), 15.0f,
std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(45.0f, -38.0f, -9.0f), 40.0f,
std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
f *= 0.9f;
scene.add(std::make_shared<Sphere>(Vector3d(52.0f, -28.0f, 20.0f), 30.0f,
std::make_shared<SimpleShadowShader>(Color(0.1f, 0.6f, 0.1f) * f)));
// Render
SuperRenderer rendererTest;
rendererTest.setSuperSamplingFactor(1);
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
// DOFRenderer renderer(0.2, 100, 70.0f);
// Use DOFRenderer to raytrace !!! careful more pixels lead to insane rendering times
// Texture image = renderer.renderImage(scene, camera, 1920, 1080);
// Use post-processing Bloom effect
/*
Bloom bloomEffect = Bloom(image.getImage());
Texture imageWithBloom = image;
imageWithBloom.setTexture(bloomEffect.bloom(0.55f, 5, 10.0f, 0.06f));
*/
// save images
imageSceneToTest.save("result.png");
// image.save("result.png");
// image.save("resultWithBloom");
// CImg<float> image = imageSceneToTest.getImage();
// CImg<unsigned char> 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);
// }
//
// CImgDisplay disp(img_8bit, "My Rendered Image", 0, false, false);
// while (!disp.is_closed()) {
// disp.wait();
// disp.display(img_8bit);
// if (disp.is_resized()) {
// disp.resize();
// }
// }
return 0;
}

88
beautifulSceneEpic.cpp Normal file
View file

@ -0,0 +1,88 @@
#include <scene/simplescene.h>
#include <camera/perspectivecamera.h>
#include <shader/materialshader.h>
#include <primitive/sphere.h>
#include <light/ambientlight.h>
#include <light/pointlight.h>
#include <renderer/depthoffieldrenderer.h>
#include <post_processing/bloom.h>
#include <primitive/objmodel.h>
#include <shader/lambertshader.h>
#include <renderer/simplerenderer.h>
#include <shader/brdfshader.h>
#include <thread>
//#include <conio.h>
#include <shader/cooktorranceshader.h>
#include <shader/phongshader.h>
#include <primitive/infiniteplane.h>
#include <light/spotlight.h>
#include <shader/cloudshader.h>
#include <shader/mirrorshader.h>
#include <shader/refractionshader.h>
#include <primitive/triangle.h>
#include <shader/simpleshadowshader.h>
#include <light/sunlight.h>
#include "scene/fastscene.h"
#include "shader/toneshader.h"
#include "renderer/superrenderer.h"
int main()
{
FastScene scene;
scene.setEnvironmentMap(std::make_shared<Texture>("data/clear_blue_sky.jpg"));
// Light
// Alternative directio Vector3d(0, -.5f, -.5f)
auto mainLight = std::make_shared<SunLight>(Vector3d(0, -0.09f, 1), 2.0f, Color(1, 1, 1));
scene.add(mainLight);
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(100.0f);
camera.setPosition(Vector3d(0, .5f, .5f) * 2);
camera.setForwardDirection(Vector3d(0, .5f, .5f));
camera.setUpDirection(Vector3d(0, .5f, -.5f));
// Shader
Vector3d planePosition = Vector3d(0, 3, 4);
auto plane = std::make_shared<ObjModel>(
std::make_shared<ToneShader>(mainLight, Color(1, 1, 1), Color(1, 0.5f, 0.5f), Color(1, 0.5f, 0.5f) * 0.5f));
plane->loadObj("data/FancyPlane/Plane.obj", Vector3d(1.0f, 1.0f, 1.0f) * 0.005f, planePosition);
scene.add(plane);
// Add clouds
auto cloudSettings = CloudSettings();
cloudSettings.seed = 10;
cloudSettings.scale = 40;
cloudSettings.noiseSize = 512;
cloudSettings.lightAbsorptionTowardsLight = 0.05f;
cloudSettings.lightAbsorptionThroughCloud = 1.9f;
cloudSettings.densityOffset = -0.61f;
auto cloudShader = std::make_shared<CloudShader>(cloudSettings);
scene.add(std::make_shared<Box>(Vector3d(0.0f, 15.0f, 30.0f), Vector3d(100.0f, 15.0f, 70.0f), cloudShader));
scene.buildTree();
// Render
// SuperRenderer rendererTest;
// rendererTest.setSuperSamplingFactor(1);
// int width = 512;
// Texture image = rendererTest.renderImage(scene, camera, width, width / 16 * 9);
// initialize renderer: aperture = lens thickness, secondaryRayCount = how many rays per pixel are created
float focalLength = length(camera.getPosition() - planePosition + Vector3d(0, 0, 2.6f));
DOFRenderer renderer(0.02, 100, focalLength);
float imageScalar = 1;
Texture image = renderer.renderImage(scene, camera, 1920 * imageScalar, 1080 * imageScalar);
image.save("result.png");
// Use post-processing Bloom effect
Bloom bloomEffect = Bloom(image.getImage(), 0.88f, image);
image.setTexture(bloomEffect.bloom(50, 20.0f, 0.5f));
image.save("resultWithBloom.png");
return 0;
}

60
bloom.cpp Normal file
View file

@ -0,0 +1,60 @@
#include <iostream>
#include <string>
#include <post_processing/bloom.h>
#include <shader/phongshader.h>
#include <shader/simpleshadowshader.h>
#include <light/pointlight.h>
#include "camera/perspectivecamera.h"
#include "renderer/simplerenderer.h"
#include "scene/simplescene.h"
#include "primitive/box.h"
#include "primitive/infiniteplane.h"
#include "primitive/sphere.h"
#include "primitive/triangle.h"
int main() {
SimpleScene scene;
scene.setBackgroundColor(Color(0, 0, 0));
// Add shaders
auto red = std::make_shared<SimpleShadowShader>(Color(1.0f, 0.3f, 0.2f));
auto gray = std::make_shared<SimpleShadowShader>(Color(0.78f, 0.78f, 0.78f));
auto blue = std::make_shared<SimpleShadowShader>(Color(0.2f, 0.3f, 1.0f));
auto orange = std::make_shared<PhongShader>(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f);
scene.add(std::make_shared<Box>(Vector3d(5.0f, -5.0f, 0.0f), Vector3d(4.0f, 4.0f, 4.0f), red));
scene.add(std::make_shared<Box>(Vector3d(-5.0f, -3.0f, 1.0f), Vector3d(1.0f, 6.0f, 1.0f), blue));
scene.add(std::make_shared<Box>(Vector3d(-3.5f, 4.0f, -2.0f), Vector3d(2.0f, 2.0f, 2.0f), orange));
scene.add(std::make_shared<Sphere>(Vector3d(2.0f, 4.0f, 0.0f), 1.0f, orange));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 0.0f, -11.0f), 100.0f));
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, 0.0f, -10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.0f, 1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Render the scene
SimpleRenderer renderer;
Texture img = renderer.renderImage(scene, camera, 512, 512);
img.save("beforeBloom.png");
Bloom bloom = Bloom(img.getImage(), 0.88f, img);
img.setTexture(bloom.bloom(100, 300.0f, 1.5f));
img.save("afterBloom.png");
return 0;
}

View file

@ -12,6 +12,12 @@ public:
// Camera functions // Camera functions
virtual Ray createRay(float x, float y) const = 0; virtual Ray createRay(float x, float y) const = 0;
// Setter methods
virtual Vector3d getPosition() const = 0;
virtual Vector3d getRightDirection() const = 0;
virtual Vector3d getUpDirection() const = 0;
}; };
#endif #endif

View file

@ -13,11 +13,6 @@ public:
// Set // Set
void setPosition(Vector3d const &position) { this->position = position; } void setPosition(Vector3d const &position) { this->position = position; }
void setForwardDirection(Vector3d const &forwardDirection) { void setForwardDirection(Vector3d const &forwardDirection) {
// IMPLEMENT ME
// Set up a left-handed coordinate system,
// in which the camera looks along the positive z-Axis
// Set up a left-handed coordinate system,
// in which the camera looks along the positive z-Axis
std::tie(this->forwardDirection, this->upDirection, this->rightDirection) = orthoNormalized(forwardDirection, this->upDirection, crossProduct(this->upDirection, forwardDirection)); std::tie(this->forwardDirection, this->upDirection, this->rightDirection) = orthoNormalized(forwardDirection, this->upDirection, crossProduct(this->upDirection, forwardDirection));
} }
void setUpDirection(Vector3d const &upDirection) { void setUpDirection(Vector3d const &upDirection) {
@ -36,6 +31,12 @@ public:
// Camera functions // Camera functions
Ray createRay(float x, float y) const override; Ray createRay(float x, float y) const override;
//Getter
Vector3d getPosition() const override { return position; }
Vector3d getRightDirection() const override{ return rightDirection; }
Vector3d getUpDirection() const override { return upDirection; }
protected: protected:
Vector3d position; Vector3d position;
Vector3d forwardDirection; Vector3d forwardDirection;

View file

@ -0,0 +1,69 @@
#include <chrono>
#include <iostream>
#include <thread>
#include "cloudnoise.h"
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))
{
auto start = std::chrono::high_resolution_clock::now();
// Generate the noise
int const nThreads = (int) std::thread::hardware_concurrency() - 1;
int threadSize = std::floor((float) size / (float) nThreads);
int remaining = size - nThreads * threadSize;
std::vector<std::thread> 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<std::chrono::seconds>(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++)
{
for (int z = 0; z < size; z++)
{
float sx = x / (float) size;
float sy = y / (float) size;
float sz = z / (float) size;
float worley1 = worleyNoise1.getNoise(sx, sy, sz);
float worley3 = worleyNoise3.getNoise(sx, sy, sz);
float perlin1 = perlinNoise1.getNoise(sx, sy, sz);
float perlin2 = perlinNoise2.getNoise(sx, sy, sz);
float noise = worley1 * 0.4f + worley3 * 0.12f + perlin1 * 0.36f + perlin2 * 0.12;
setNoise(x, y, z, noise);
}
}
}
}

36
common/noise/cloudnoise.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef CG1_TRACER_CLOUDNOISE_H
#define CG1_TRACER_CLOUDNOISE_H
#include "noise.h"
#include "worleynoise.h"
#include "perlinnoise.h"
int const LOWRES_SIZE = 32;
class CloudNoise : public Noise
{
public:
/**
*
* @param size
* @param seed 0 for random seed
*/
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);
};
#endif //CG1_TRACER_CLOUDNOISE_H

117
common/noise/noise.cpp Normal file
View file

@ -0,0 +1,117 @@
#include <cmath>
#include "noise.h"
float Noise::getNoise(Vector3d point) const
{
return getNoise(point.x, point.y, point.z);
}
void Noise::generateNoiseMap(int size)
{
noiseMap.clear();
noiseMap.resize(size * size * size);
}
void Noise::setNoise(int x, int y, int z, float value)
{
long int index = x + y * (long int) size + z * size * (long int) size;
noiseMap[index] = value;
}
float Noise::getNoise(float x, float y, float z) const
{
// Scale to [0, size]
x *= size;
y *= size;
z *= size;
// Get corner points of the cube
int x_min = (int) fitToNoise(floor(x));
int y_min = (int) fitToNoise(floor(y));
int z_min = (int) fitToNoise(floor(z));
int x_max = fitToNoise(x_min + 1);
int y_max = fitToNoise(y_min + 1);
int z_max = fitToNoise(z_min + 1);
// Get the getNoise values at the corner points
float n000 = getCalculatedNoise(x_min, y_min, z_min);
float n001 = getCalculatedNoise(x_min, y_min, z_max);
float n010 = getCalculatedNoise(x_min, y_max, z_min);
float n011 = getCalculatedNoise(x_min, y_max, z_max);
float n100 = getCalculatedNoise(x_max, y_min, z_min);
float n101 = getCalculatedNoise(x_max, y_min, z_max);
float n110 = getCalculatedNoise(x_max, y_max, z_min);
float n111 = getCalculatedNoise(x_max, y_max, z_max);
// Get fractions
float fx = x - floor(x);
float fy = y - floor(y);
float fz = z - floor(z);
// Interpolate
float nx00 = interpolate(n000, n100, fx);
float nx01 = interpolate(n001, n101, fx);
float nx10 = interpolate(n010, n110, fx);
float nx11 = interpolate(n011, n111, fx);
float nxy0 = interpolate(nx00, nx10, fy);
float nxy1 = interpolate(nx01, nx11, fy);
float noise = interpolate(nxy0, nxy1, fz);
return noise;
}
float Noise::getCalculatedNoise(int x, int y, int z) const
{
long int index = x + y * (long int) size + z * size * (long int) size;
float noise = noiseMap[index];
if (invert)
{
noise = 1.0f - noise;
}
return noise;
}
Noise::Noise(int size)
{
this->size = size;
generateNoiseMap(size);
}
/* Function to linearly interpolate between a0 and a1
* Weight w should be in the range [0.0, 1.0]
*/
float Noise::interpolate(float a0, float a1, float w) const
{
if (0.0 > w) return a0;
if (1.0 < w) return a1;
/* // You may want clamping by inserting:
* if (0.0 > w) return a0;
* if (1.0 < w) return a1;
*/
return (a1 - a0) * ((w * (w * 6.0 - 15.0) + 10.0) * w * w * w) + a0;
return (a1 - a0) * (3.0 - w * 2.0) * w * w + a0;
return (a1 - a0) * w + a0;
/* // Use this cubic interpolation [[Smoothstep]] instead, for a smooth appearance:
* return (a1 - a0) * (3.0 - w * 2.0) * w * w + a0;
*
* // Use [[Smootherstep]] for an even smoother result with a second derivative equal to zero on boundaries:
* return (a1 - a0) * ((w * (w * 6.0 - 15.0) + 10.0) * w * w * w) + a0;
*/
}
float Noise::fitToNoise(float point) const
{
float remainingValue = fmod(point, size);
if (remainingValue < 0)
{
remainingValue += size;
}
return remainingValue;
}

33
common/noise/noise.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef CG1_TRACER_NOISE_H
#define CG1_TRACER_NOISE_H
#include <vector>
#include "common/vector3d.h"
class Vector3d;
class Noise
{
public:
Noise(int size);
float getNoise(Vector3d point) const; // Parameters in [0,1], Output in [0,1]
float getNoise(float x, float y, float z) const; // Parameters in [0,1], Output in [0,1]
bool invert = false;
protected:
std::vector<float> noiseMap; // Noise map
int size; // Size of the cloudNoise map
void generateNoiseMap(int size);
void setNoise(int x, int y, int z, float value); // Parameters in [0,size]
float getCalculatedNoise(int x, int y, int z) const;
float interpolate(float a0, float a1, float w) const;
float fitToNoise(float point) const;
};
#endif //CG1_TRACER_NOISE_H

View file

@ -0,0 +1,149 @@
#include <random>
#include <algorithm>
#include <thread>
#include <iostream>
#include "perlinnoise.h"
Vector3d PerlinNoise::randomGradient()
{
Vector3d v;
v.x = distribution(generator);
v.y = distribution(generator);
v.z = distribution(generator);
return normalized(v);
}
float PerlinNoise::getGradientValue(int x, int y, int z)
{
Vector3d position = Vector3d(x, y, z);
// Translate position to grid
position /= (float) size;
position *= (float) gridSize;
// Get the grid coordinates
int x_min = floor(position.x);
int y_min = floor(position.y);
int z_min = floor(position.z);
// Get the grid coordinates of the next grid point
int x_max = x_min + 1;
int y_max = y_min + 1;
int z_max = z_min + 1;
// Get the fractional part of the position
Vector3d delta = position - Vector3d(x_min, y_min, z_min);
// Interpolate the gradient values
float n000 = getCornerValue(position, Vector3d(x_min, y_min, z_min));
float n100 = getCornerValue(position, Vector3d(x_max, y_min, z_min));
float n010 = getCornerValue(position, Vector3d(x_min, y_max, z_min));
float n110 = getCornerValue(position, Vector3d(x_max, y_max, z_min));
float n001 = getCornerValue(position, Vector3d(x_min, y_min, z_max));
float n101 = getCornerValue(position, Vector3d(x_max, y_min, z_max));
float n011 = getCornerValue(position, Vector3d(x_min, y_max, z_max));
float n111 = getCornerValue(position, Vector3d(x_max, y_max, z_max));
// Smooth the interpolation
float ix0 = interpolate(n000, n100, delta.x);
float ix1 = interpolate(n010, n110, delta.x);
float ix2 = interpolate(n001, n101, delta.x);
float ix3 = interpolate(n011, n111, delta.x);
float iy0 = interpolate(ix0, ix1, delta.y);
float iy1 = interpolate(ix2, ix3, delta.y);
float noise = interpolate(iy0, iy1, delta.z);
return noise;
}
PerlinNoise::PerlinNoise(int size, int gridSize, unsigned int seed) : Noise(size), gridSize(gridSize)
{
// Init random
if (seed == 0)
{
std::random_device rd;
seed = rd();
}
this->generator = std::mt19937(seed);
this->distribution = std::normal_distribution<float>(0.0f, 1.0f);
generateNoise();
}
void PerlinNoise::generateNoise()
{
auto start = std::chrono::high_resolution_clock::now();
// Generate gradients
gradients.clear();
gradients.resize(pow(gridSize, 3));
for (int i = 0; i < gradients.size(); i++)
{
gradients[i] = randomGradient();
}
// Generate each pixel
int const nThreads = (int) std::thread::hardware_concurrency() - 1;
int threadSize = std::floor((float) size / (float) nThreads);
int remaining = size - nThreads * threadSize;
std::vector<std::thread> threads;
for (int n = 0; n < nThreads; n++)
{
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]
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<std::chrono::seconds>(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)
{
int x = (int) corner.x % gridSize;
int y = (int) corner.y % gridSize;
int z = (int) corner.z % gridSize;
Vector3d gradient = gradients[x + y * gridSize + z * gridSize * gridSize];
return dotProduct(gradient, position - corner);
}

View file

@ -0,0 +1,39 @@
#ifndef CG1_TRACER_PERLINNOISE_H
#define CG1_TRACER_PERLINNOISE_H
#include "noise.h"
// Stolen from https://en.wikipedia.org/wiki/Perlin_noise
class PerlinNoise : public Noise
{
public:
/**
*
* @param size
* @param gridSize
* @param seed 0 for random seed
*/
PerlinNoise(int size, int gridSize, unsigned int seed = 0);
void renderNoiseThread(int xOffset, int xSize);
private:
void generateNoise();
std::normal_distribution<float> distribution;
std::mt19937 generator;
int gridSize;
std::vector<Vector3d> gradients;
Vector3d randomGradient();
float getGradientValue(int x, int y, int z);
float getCornerValue(Vector3d position, Vector3d corner);
static void runPerlinNoiseInThread(int xOffset, int xSize, PerlinNoise *noise);
};
#endif //CG1_TRACER_PERLINNOISE_H

View file

@ -0,0 +1,189 @@
#include <chrono>
#include <iostream>
#include <algorithm>
#include <set>
#include <thread>
#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();
// Generate random points
points.clear();
points.reserve(pow(numberOfPoints, 3));
for (int x = 0; x < numberOfPoints; x++)
{
for (int y = 0; y < numberOfPoints; y++)
{
for (int z = 0; z < numberOfPoints; z++)
{
Vector3d point = getRandomPoint();
// Scale and translate into subcell
point /= (float) numberOfPoints;
Vector3d offset = Vector3d(x, y, z);
offset /= (float) numberOfPoints;
point += offset;
points[x + y * numberOfPoints + z * numberOfPoints * numberOfPoints] = point;
}
}
}
// Generate getNoise map
noiseMap.clear();
noiseMap.resize(size * size * size);
int const nThreads = (int) std::thread::hardware_concurrency() - 1;
int threadSize = std::floor((float) size / (float) nThreads);
int remaining = size - nThreads * threadSize;
std::vector<std::thread> 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<std::chrono::seconds>(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++)
{
for (int z = 0; z < size; z++)
{
Vector3d point = Vector3d(x, y, z);
point /= (float) size;
setNoise(x, y, z, distanceToClosestPoint(point));
}
}
}
}
WorleyNoise::WorleyNoise(int size, int numberOfPoints, unsigned int seed) : numberOfPoints(numberOfPoints), Noise(size)
{
// Init random
if (seed == 0)
{
std::random_device rd;
seed = rd();
}
this->generator = std::mt19937(seed);
this->distribution = std::uniform_real_distribution<float>(0.0f, 1.0f);
generateNoise();
}
Vector3d WorleyNoise::getRandomPoint()
{
return Vector3d(distribution(generator), distribution(generator), distribution(generator));
}
float WorleyNoise::distanceToClosestPoint(Vector3d point)
{
std::vector<Vector3d> closePoints = getSubcellPoints(point);
// Iterate over all subcells
float minDistance = INFINITY;
for (Vector3d p: closePoints)
{
float distance = length(p - point);
if (distance < minDistance)
{
minDistance = distance;
}
}
return minDistance;
}
std::vector<Vector3d> WorleyNoise::getSubcellPoints(Vector3d point)
{
std::vector<Vector3d> closePoints;
point *= numberOfPoints;
point.x = std::floor(point.x);
point.y = std::floor(point.y);
point.z = std::floor(point.z);
// Iterate over all subcells
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
for (int z = -1; z <= 1; z++)
{
Vector3d offset = Vector3d(x, y, z);
Vector3d subcell = point + offset;
Vector3d cellOffsets = Vector3d(0, 0, 0);
if (subcell.x < 0)
{
cellOffsets.x = -1;
}
if (subcell.y < 0)
{
cellOffsets.y = -1;
}
if (subcell.z < 0)
{
cellOffsets.z = -1;
}
if (subcell.x >= numberOfPoints)
{
cellOffsets.x = 1;
}
if (subcell.y >= numberOfPoints)
{
cellOffsets.y = 1;
}
if (subcell.z >= numberOfPoints)
{
cellOffsets.z = 1;
}
// Wrap around
subcell.x = std::fmod(subcell.x + numberOfPoints, numberOfPoints);
subcell.y = std::fmod(subcell.y + numberOfPoints, numberOfPoints);
subcell.z = std::fmod(subcell.z + numberOfPoints, numberOfPoints);
// Get points in subcell
int index = subcell.x + subcell.y * numberOfPoints + subcell.z * numberOfPoints * numberOfPoints;
closePoints.push_back(points[index] + cellOffsets);
}
}
}
return closePoints;
}

View file

@ -0,0 +1,42 @@
#ifndef CG1_TRACER_WORLEYNOISE_H
#define CG1_TRACER_WORLEYNOISE_H
#include <random>
#include "common/vector3d.h"
#include "noise.h"
// Based on: https://www.youtube.com/watch?v=4QOcCGI6xOU
class WorleyNoise : public Noise
{
public:
/**
*
* @param size
* @param numberOfPoints
* @param seed 0 for random seed
*/
WorleyNoise(int size, int numberOfPoints, unsigned int seed = 0);
void renderNoiseThread(int xOffset, int xSize);
protected:
int numberOfPoints;
std::vector<Vector3d> points; // 3D-Array, each cell represents a subcell. There are numberOfPoints^3 subcells.
std::uniform_real_distribution<float> distribution;
std::mt19937 generator;
Vector3d getRandomPoint();
float distanceToClosestPoint(Vector3d point);
void generateNoise();
std::vector<Vector3d> getSubcellPoints(Vector3d point);
static void runWorleyNoiseInThread(int xOffset, int xSize, WorleyNoise *noise);
};
#endif //CG1_TRACER_WORLEYNOISE_H

View file

@ -3,6 +3,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <utility>
Texture::Texture(int width, int height) { this->resize(width, height); } Texture::Texture(int width, int height) { this->resize(width, height); }
@ -83,3 +84,12 @@ Color Texture::color(float u, float v, bool interpolate) const {
Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const { Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const {
return color(surfacePosition.u, surfacePosition.v, interpolate); return color(surfacePosition.u, surfacePosition.v, interpolate);
} }
CImg<float>& Texture::getImage() {
return image_;
}
void Texture::setTexture(CImg<float> image){
image_ = std::move(image);
}

View file

@ -9,30 +9,34 @@ using namespace cimg_library;
class Texture { class Texture {
public: public:
// Constructor // Constructor
Texture(int width, int height); Texture(int width, int height);
Texture(char const *fileName); Texture(char const *fileName);
// Image functions // Image functions
inline void resize(int width, int height) { this->image_.resize(width, height, 1, 3); } inline void resize(int width, int height) { this->image_.resize(width, height, 1, 3); }
bool load(char const *fileName); bool load(char const *fileName);
bool save(char const *fileName) const; bool save(char const *fileName) const;
// Get // Get
inline bool isNull() const { return this->image_.is_empty(); } inline bool isNull() const { return this->image_.is_empty(); }
inline int width() const { return this->image_.width(); } inline int width() const { return this->image_.width(); }
inline int height() const { return this->image_.height(); } inline int height() const { return this->image_.height(); }
Color getPixelAt(int x, int y) const; Color getPixelAt(int x, int y) const;
// Set // Set
void setPixelAt(int x, int y, Color const &color); void setPixelAt(int x, int y, Color const &color);
void setTexture(CImg<float> image);
// Color functions
Color color(float u, float v, bool interpolate = true) const; // Color functions
Color color(Vector2d const &surfacePosition, bool interpolate = true) const; Color color(float u, float v, bool interpolate = true) const;
Color color(Vector2d const &surfacePosition, bool interpolate = true) const;
CImg<float>& getImage();
private: private:
CImg<float> image_; CImg<float> image_;
}; };
#endif #endif

View file

@ -0,0 +1,14 @@
# Materials file generated by ZModeler 2.2.6 (Build 981).
newmtl Default_Material
Ka 0.20 0.20 0.20
Kd 0.80 0.80 0.80
Ks 1.00 1.00 1.00
illum 4
newmtl Default_Material
Ka 0.20 0.20 0.20
Kd 0.80 0.80 0.80
Ks 1.00 1.00 1.00
illum 4
map_Kd t07-11m_d.png

7441
data/Bus/source/T07-11M.obj Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

2667
data/FancyPlane/Plane.obj Normal file

File diff suppressed because it is too large Load diff

78
fancy1.cpp Normal file
View file

@ -0,0 +1,78 @@
#include <iostream>
#include <string>
#include "camera/perspectivecamera.h"
#include "scene/fastscene.h"
#ifdef SUPERRENDERER_FOUND
#include "renderer/superrenderer.h"
#endif
#include "light/ambientlight.h"
#include "light/pointlight.h"
#include "shader/toneshader.h"
#include "primitive/infiniteplane.h"
#include "shader/simpleshadowshader.h"
#include "primitive/box.h"
#include "shader/cloudshader.h"
#include "common/noise/perlinnoise.h"
#include "shader/noiseshader.h"
#include "common/noise/cloudnoise.h"
#include "light/sunlight.h"
#include "scene/simplescene.h"
#include "shader/refractionshader.h"
int main()
{
FastScene scene;
scene.setBackgroundColor(Color(0.2588f, 0.5098f, 0.9568f) * 1.0f);
// scene.setBackgroundColor(Color(1, 0.79f, 0.62f) * 0.8f);
// Add lights
auto mainLight = std::make_shared<SunLight>(Vector3d(-1.0f, -0.5f, -1.0f), 1.0f, Color(1,1,1));
scene.add(mainLight);
// scene.add(std::make_shared<AmbientLight>(0.3f));
// auto light = std::make_shared<PointLight>(Vector3d(25.0f, 10.0f, 25.0f), 100.0f);
// scene.add(light);
// Add the bus
// auto busShader = std::make_shared<ToneShader>(mainLight);
// scene.addObj("data/Bus/source/T07-11M.obj", Vector3d(1.0f, 1.0f, 1.0f), Vector3d(15.0f, 0.0f, 10.0f),
// busShader);
// Refraction boxes
// auto boxShader = std::make_shared<RefractionShader>(1.05f, 1, Color(1,1,0), 0.7f);
// scene.add(std::make_shared<Box>(Vector3d(5.0f, 3.0f, 10.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader));
// auto boxShader1 = std::make_shared<RefractionShader>(1.05f, 1, Color(0,1,1), 0.7f);
// scene.add(std::make_shared<Box>(Vector3d(9.0f, 3.0f, 12.0f), Vector3d(3.0f, 3.0f, 3.0f), boxShader1));
// Add floor
auto floorShader = std::make_shared<SimpleShadowShader>(Color(0.9f, 0.9f, 0.9f));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, 0.0f), Vector3d(0.0f, 1.0f, 0.0f),
floorShader));
// Add box for volume shader
auto cloudSettings = CloudSettings();
auto cloudShader = std::make_shared<CloudShader>(cloudSettings);
scene.add(std::make_shared<Box>(Vector3d(20.0f, 15.0f, 20.0f), Vector3d(70.0f, 10.0f, 70.0f), cloudShader));
// build the tree
scene.buildTree();
// Set up the camera
PerspectiveCamera camera;
camera.setPosition(Vector3d(0.0f, 3.0f, 0.0f));
camera.setForwardDirection(normalized(Vector3d(1.0f, 0.15f, 1.0f)));
camera.setUpDirection(normalized(Vector3d(0.0f, 1.0f, 0.0f)));
camera.setFovAngle(90.0f);
// Render the scene
SuperRenderer sr;
sr.setSuperSamplingFactor(1);
sr.renderImage(scene, camera, 2048, 2048).save("result.png");
return 0;
}

View file

@ -18,7 +18,7 @@
int main() { int main() {
// Let's create a simple cornell box scene... // Let's create a simple cornell box scene...
SimpleScene scene; SimpleScene scene;
scene.setEnvironmentMap(std::make_shared<Texture>("data/lion_env.png")); scene.setEnvironmentMap(std::make_shared<Texture>("../data/lion_env.png"));
auto mirror = std::make_shared<MirrorShader>(); auto mirror = std::make_shared<MirrorShader>();
auto glass = std::make_shared<RefractionShader>(1.31f, 1.0f); auto glass = std::make_shared<RefractionShader>(1.31f, 1.0f);

View file

@ -11,20 +11,29 @@
#include "primitive/sphere.h" #include "primitive/sphere.h"
#include "shader/brdfshader.h" #include "shader/brdfshader.h"
#include "shader/lambertshader.h" #include "shader/lambertshader.h"
#include "shader/mirrorshader.h" #include "shader/mirrorshader.h"
#include "shader/phongshader.h" #include "shader/phongshader.h"
#include "shader/cooktorranceshader.h" #include "shader/cooktorranceshader.h"
#include "renderer/depthoffieldrenderer.h"
#include "light/ambientlight.h" #include "light/ambientlight.h"
#include "light/pointlight.h" #include "light/pointlight.h"
#include "light/spotlight.h" #include "light/spotlight.h"
#include "post_processing/bloom.h"
int main() { int main() {
// Let's create a simple scene... // Let's create a simple scene...
SimpleScene scene; SimpleScene scene;
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, 0.0f, -10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.0f, 1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Add shaders // Add shaders
auto mirror = std::make_shared<MirrorShader>(); auto mirror = std::make_shared<MirrorShader>();
auto white = std::make_shared<LambertShader>(Color(0.9f, 0.9f, 0.9f)); auto white = std::make_shared<LambertShader>(Color(0.9f, 0.9f, 0.9f));
@ -32,11 +41,14 @@ int main() {
auto blue = std::make_shared<LambertShader>(Color(0.2f, 0.3f, 1.0f)); auto blue = std::make_shared<LambertShader>(Color(0.2f, 0.3f, 1.0f));
auto orange = std::make_shared<PhongShader>(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f); auto orange = std::make_shared<PhongShader>(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f);
auto gold= std::make_shared<CookTorranceShader>(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 1.0f, 0.0f), 1.2f, 0.2f); auto gold= std::make_shared<CookTorranceShader>(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 1.0f, 0.0f), 1.2f, 0.2f);
auto blueMetallic = std::make_shared<BrdfShader>("data/blue-metallic-paint.binary", Color(7.0f, 7.0f, 7.0f)); auto blueMetallic = std::make_shared<BrdfShader>("../data/blue-metallic-paint.binary", Color(7.0f, 7.0f, 7.0f));
auto darkRed = std::make_shared<BrdfShader>("data/dark-red-paint.binary", Color(7.0f, 7.0f, 7.0f)); auto darkRed = std::make_shared<BrdfShader>("../data/dark-red-paint.binary", Color(7.0f, 7.0f, 7.0f));
// Set up the walls // Set up the walls
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, +5.0f), Vector3d(0.0f, 0.0f, -1.0f), mirror)); scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, +5.0f), Vector3d(0.0f, 0.0f, -1.0f), mirror));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, -5.0f), Vector3d(0.0f, 0.0f, +1.0f), mirror)); scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, -5.0f), Vector3d(0.0f, 0.0f, +1.0f), mirror));
@ -48,27 +60,36 @@ int main() {
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, 0.0f, 0.0f), 1.0f, blueMetallic)); scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, 0.0f, 0.0f), 1.0f, blueMetallic));
scene.add(std::make_shared<Sphere>(Vector3d(0.0f, 2.0f, 0.0f), 1.0f, orange)); scene.add(std::make_shared<Sphere>(Vector3d(0.0f, 2.0f, 0.0f), 1.0f, orange));
scene.add(std::make_shared<Sphere>(Vector3d(3.0f, 0.0f, 0.0f), 1.0f, darkRed)); scene.add(std::make_shared<Sphere>(Vector3d(3.0f, 0.0f, 0.0f), 1.0f, darkRed));
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, -3.3f, -4.0f), 1.0f, mirror));
// Add the teapot // Add the teapot
auto teapot = std::make_shared<ObjModel>(gold); auto teapot = std::make_shared<ObjModel>(gold);
teapot->loadObj("data/teapot.obj", Vector3d(3.0f, 3.0f, 3.0f), Vector3d(0.0f, -5.0f, 0.0f)); teapot->loadObj("../data/teapot.obj", Vector3d(3.0f, 3.0f, 3.0f), Vector3d(0.0f, -5.0f, 0.0f));
scene.add(teapot); scene.add(teapot);
// Add ambient light // Add ambient light
scene.add(std::make_shared<AmbientLight>(0.15f)); scene.add(std::make_shared<AmbientLight>(0.15f));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, -4.0f), 15.0f)); //scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, -4.0f), 15.0f));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, 4.0f), 15.0f)); scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 2.5f, -4.0f), 7.0f));
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, 0.0f, -10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.0f, 1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Render the scene // Render the scene
SimpleRenderer renderer; // SimpleRenderer renderer;
renderer.renderImage(scene, camera, 512, 512).save("result.png"); // renderer.renderImage(scene, camera, 1024, 1024).save("result.png");
// initialize renderer: aperture = lens thickness, secondaryRayCount = how many rays per pixel are created
// focalLength = the area which is in focus
DOFRenderer renderer(0.2, 100, 10.0f);
// Use DOFRenderer to raytrace !!! careful more pixels lead to insane rendering times
Texture image = renderer.renderImage(scene, camera, 1024, 1024);
// save images
image.save("result.png");
return 0; return 0;
} }

View file

@ -1,5 +1,5 @@
#include "light/ambientlight.h" #include "light/ambientlight.h"
Light::Illumination AmbientLight::illuminate(Scene const &scene, Ray const &ray) const { Light::Illumination AmbientLight::illuminate(Scene const &scene, Ray const &ray) const {
return {this->color * this->intensity, -ray.normal}; return {this->color * this->intensity, -ray.normal, 0};
} }

View file

@ -13,6 +13,7 @@ public:
struct Illumination { struct Illumination {
Color color; Color color;
Vector3d direction; Vector3d direction;
float distance;
}; };
// Constructor / Destructor // Constructor / Destructor

View file

@ -12,6 +12,7 @@ Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) c
// Precompute the distance from the light source // Precompute the distance from the light source
float const distance = length(target - this->position); float const distance = length(target - this->position);
illum.distance = distance;
// Define a secondary ray from the surface point to the light source. // Define a secondary ray from the surface point to the light source.
Ray lightRay; Ray lightRay;
@ -20,8 +21,10 @@ Light::Illumination PointLight::illuminate(Scene const &scene, Ray const &ray) c
lightRay.length = distance - LGT_EPS; lightRay.length = distance - LGT_EPS;
// If the target is not in shadow... // If the target is not in shadow...
if (!scene.findOcclusion(lightRay)) if (!scene.findOcclusion(lightRay)) {
// ... compute the attenuation and light color // ... compute the attenuation and light color
illum.color = 1.0f / (distance * distance) * this->color * this->intensity; Color rayTransparency = scene.getTransparency(lightRay, distance);
illum.color = 1.0f / (distance * distance) * this->color * this->intensity * rayTransparency;
}
return illum; return illum;
} }

View file

@ -13,6 +13,7 @@ Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) co
// Precompute the distance from the light source // Precompute the distance from the light source
float const distance = length(target - this->position); float const distance = length(target - this->position);
illum.distance = distance;
// Define a secondary ray from the surface point to the light source // Define a secondary ray from the surface point to the light source
Ray lightRay; Ray lightRay;
@ -28,7 +29,8 @@ Light::Illumination SpotLight::illuminate(Scene const &scene, Ray const &ray) co
// ... and not in shadow ... // ... and not in shadow ...
if (!scene.findOcclusion(lightRay)) { if (!scene.findOcclusion(lightRay)) {
// ... compute the attenuation and light color ... // ... compute the attenuation and light color ...
illum.color = 1.0f / (distance * distance) * this->color * this->intensity; Color rayTransparency = scene.getTransparency(lightRay, distance);
illum.color = 1.0f / (distance * distance) * this->color * this->intensity * rayTransparency;
// ... then compute the falloff towards the edge of the cone // ... then compute the falloff towards the edge of the cone
if (this->alphaMin < alpha) if (this->alphaMin < alpha)
illum.color *= 1.0f - (alpha - this->alphaMin) / (this->alphaMax - this->alphaMin); illum.color *= 1.0f - (alpha - this->alphaMin) / (this->alphaMax - this->alphaMin);

36
light/sunlight.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "sunlight.h"
SunLight::SunLight(Vector3d const &direction, float intensity, Color const &color) : Light(intensity, color),
direction(normalized(direction))
{}
Light::Illumination SunLight::illuminate(Scene const &scene, Ray const &ray) const
{
Vector3d const target = ray.origin + (ray.length - LGT_EPS) * ray.direction;
// Illumination object
Illumination illum;
illum.direction = this->direction;
illum.distance = INFINITY;
// Define a secondary ray from the surfa ce point to the light source.
Ray lightRay;
lightRay.origin = target;
lightRay.direction = -illum.direction;
lightRay.length = INFINITY;
// If the target is not in shadow...
if (!scene.findOcclusion(lightRay))
{
// Look at angleIntensity of light
float angleIntensity = 1;
// Only if normal is relevant, calculate angleIntensity
if (ray.normal != Vector3d(0, 0, 0))
angleIntensity = dotProduct(-ray.normal, this->direction); // 0 if light is behind surface, 1 if light is in front of surface
Color rayTransparency = scene.getTransparency(lightRay, INFINITY);
illum.color = this->color * this->intensity * angleIntensity * rayTransparency;
}
return illum;
}

20
light/sunlight.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef CG1_TRACER_SUNLIGHT_H
#define CG1_TRACER_SUNLIGHT_H
#include "scene/scene.h"
#include "light.h"
class SunLight : public Light
{
public:
SunLight(Vector3d const &direction, float intensity, Color const &color = Color(1, 1, 1));
// Light functions
Illumination illuminate(Scene const &scene, Ray const &ray) const override;
protected:
Vector3d direction;
};
#endif //CG1_TRACER_SUNLIGHT_H

75
post_processing/bloom.cpp Normal file
View file

@ -0,0 +1,75 @@
#include <iostream>
#include "bloom.h"
Bloom::Bloom(CImg<float> &image, float threshold, Texture& thresholdImg) : image(image), threshold(threshold) {}
CImg<float> Bloom::bloom(int kernelSize, float sigma, float intensity) {
CImg<float> brightPixels = image.get_threshold(threshold);
// Apply gaussian blur to bright pixels
CImg<float> kernel = computeGaussianKernel(kernelSize, sigma);
CImg<float> blurred = convolution(image, kernel);
blurred *= intensity;
// Add blurred image back to original image
cimg_forXYC(image, x, y, c) {
float value = image(x, y, 0, c) + blurred(x, y, 0, c);
image(x, y, 0, c) = (value > 1.0f) ? 1.0f : value;
}
return image;
}
// Function to compute Gaussian kernel
CImg<float> Bloom::computeGaussianKernel(int kernelSize, float sigma) {
// Create kernel
CImg<float> kernel(kernelSize, kernelSize, 1, 1);
// Compute Gaussian kernel
float sum = 0.0f;
int i, j;
for (i = 0; i < kernelSize; i++) {
for (j = 0; j < kernelSize; j++) {
kernel(i, j) = exp(-0.5f * (pow((i - kernelSize / 2.f) / sigma, 2.f) +
pow((j - kernelSize / 2.f) / sigma, 2.f))) / (2 * M_PI * sigma * sigma);
sum += kernel(i, j);
}
}
// Normalize kernel
kernel /= sum;
return kernel;
}
// Function to perform convolution
CImg<float> Bloom::convolution(CImg<float> &img, CImg<float> &kernel) {
int kernelSize = kernel.width();
int imgRows = img.height();
int imgCols = img.width();
CImg<float> result(imgCols, imgRows, 1, 3);
float sum;
int i, j, m, n;
int kernelRadius = kernelSize / 2;
// Perform convolution
cimg_forXYC(img, i, j, c) {
sum = 0;
cimg_forY(kernel, m) {
cimg_forX(kernel, n) {
int x = i + n - kernelRadius;
int y = j + m - kernelRadius;
if (x >= 0 && x < imgCols && y >= 0 && y < imgRows) {
sum += img(x, y, 0, c) * kernel(n, m);
}
}
}
result(i, j, 0, c) = sum;
}
return result;
}

25
post_processing/bloom.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef CG1_TRACER_BLOOM_H
#define CG1_TRACER_BLOOM_H
#include "common/texture.h"
#include "common/vector3d.h"
class Bloom {
public:
Bloom(CImg<float>& image, float threshold, Texture& thresholdImg);
CImg<float> bloom(int kernelSize, float sigma, float intensity);
private:
CImg<float> convolution(CImg<float> &img, CImg<float> &kernel);
CImg<float> computeGaussianKernel(int kernelSize, float sigma);
CImg<float> image;
float threshold;
};
#endif //CG1_TRACER_BLOOM_H

View file

@ -0,0 +1,116 @@
#include <iostream>
#include <thread>
#include <chrono>
#include "depthoffieldrenderer.h"
#include <iomanip>
DOFRenderer::DOFRenderer(float _aperture, int _secondaryRayCount, float _focalLength) : aperture(_aperture),
numSamples(_secondaryRayCount), focalLength(_focalLength) {}
std::random_device DOFRenderer::rd;
std::mt19937 DOFRenderer::gen(DOFRenderer::rd());
Color DOFRenderer::sample(const Ray &ray, const Scene& scene, const Camera& camera) const {
std::uniform_real_distribution<float> dis(-1.0, 1.0);
// calculate the point of focus
Vector3d focusPoint = ray.origin + ray.direction * focalLength;
// Final color
Color finalColor;
// Calculate all secondary Rays
for (int i = 0; i < numSamples; i++) {
// create a random point on the aperture
Vector3d rnd = Vector3d(dis(gen), dis(gen), 0);
Vector3d apertureOffset = aperture * (rnd.x * camera.getRightDirection() + rnd.y * camera.getUpDirection());
// create the new ray with the offset point
Vector3d dofRayOrigin = ray.origin + apertureOffset;
Vector3d dofRayDirection = normalized(focusPoint - dofRayOrigin);
Ray dofRay(dofRayOrigin, dofRayDirection);
// get Color of the new Ray
finalColor += scene.traceRay(dofRay);
}
// trace the new ray and return the color
return finalColor /= float(numSamples);;
}
void DOFRenderer::renderThread(const Scene *scene, Camera const *camera, Texture *image, const DOFRenderer *renderer, int width, int widthStep, int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k, int const stepSize) {
float const aspectRatio = static_cast<float>(height) / width;
for (int y = heightOffset; y < height; y += heightStep) {
for (int x = widthOffset; x < width; x += widthStep) {
Ray ray = camera->createRay((static_cast<float>(x) / width * 2 - 1), -(static_cast<float>(y) / height * 2 - 1) * aspectRatio);
// Trace rays with DOF
image->setPixelAt(x, y, clamped(renderer->sample(ray, *scene, *camera)));
// Super hacky progress bar!
if (++*k % stepSize == 0) {
std::cout << "=" << std::flush;
}
}
}
}
Texture DOFRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height);
// Setup timer
std::chrono::steady_clock::time_point start, stop;
// Reset Ray counting
Ray::resetRayCount();
// Super-hacky progress bar!
std::cout << "(SimpleRenderer): Begin rendering..." << std::endl;
std::cout << "| 0%";
int const barSize = 50;
int const stepSize = (width * height) / barSize;
for (int i = 0; i < barSize - 3 - 5; ++i)
std::cout << " ";
std::cout << "100% |" << std::endl << "|";
std::atomic<int> k(0);
/* Start timer */ start = std::chrono::steady_clock::now();
// Spawn a thread for every logical processor -1, calling the renderThread function
int const nThreads = std::thread::hardware_concurrency();
std::vector<std::thread> threads;
for (int t = 0; t < nThreads - 1; ++t) {
threads.emplace_back(renderThread, &scene, &camera, &image, this, width, nThreads, t, height, 1, 0, &k, stepSize);
}
// Call the renderThread function yourself
renderThread(&scene, &camera, &image, this, width, nThreads, nThreads - 1, height, 1, 0, &k, stepSize);
// Rejoin the threads
for (int t = 0; t < nThreads - 1; ++t) {
threads[t].join();
}
// Stop timer
stop = std::chrono::steady_clock::now();
std::cout << "| Done!" << std::endl;
// Calculate the Time taken in seconds
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(stop - start).count();
std::cout << "Time: " << seconds << "s" << std::endl;
// Get the number of seconds per ray
int rays = Ray::getRayCount();
std::cout << "Paths: " << rays << std::endl;
std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl;
return image;
}

View file

@ -0,0 +1,35 @@
#ifndef DEPTHOFFIELDSHADER_H
#define DEPTHOFFIELDSHADER_H
#include "camera/camera.h"
#include <random>
#include "renderer/renderer.h"
#include "scene/simplescene.h"
class DOFRenderer : public Renderer {
static void renderThread(const Scene *scene, const Camera *camera, Texture *image, const DOFRenderer *renderer, int width, int widthStep,
int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k,
const int stepSize);
public:
// Constructor
DOFRenderer(float _aperture, int _secondaryRayCount, float _focalLength);
~DOFRenderer() override = default;
//Render Functions
Texture renderImage(Scene const &scene, Camera const &camera, int width, int height) override;
// DOF sampler
Color sample(const Ray &ray, const Scene& scene, const Camera& camera) const;
private:
float aperture, focalLength;
int numSamples;
static std::random_device rd;
static std::mt19937 gen;
};
#endif

View file

@ -73,6 +73,5 @@ Texture SimpleRenderer::renderImage(Scene const &scene, Camera const &camera, in
std::cout << "Paths: " << rays << std::endl; std::cout << "Paths: " << rays << std::endl;
std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl; std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl;
return image; return image;
} }

View file

@ -3,11 +3,12 @@
#include <atomic> #include <atomic>
#include "renderer/renderer.h" #include "renderer/renderer.h"
#include <renderer/depthoffieldrenderer.h>
class SimpleRenderer : public Renderer { class SimpleRenderer : public Renderer {
static void renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, static void renderThread(const Scene *scene, const Camera *camera, Texture *image, int width, int widthStep,
int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k, int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k,
int const stepSize); const int stepSize);
public: public:
// Constructor / Destructor // Constructor / Destructor

View file

@ -221,3 +221,25 @@ std::unique_ptr<Node> FastScene::build(Vector3d const &minimumBounds, Vector3d c
node->child[1] = this->build(minimumSplit, maximumBounds, rightPrimitives, depth); node->child[1] = this->build(minimumSplit, maximumBounds, rightPrimitives, depth);
return node; return node;
} }
Color FastScene::getTransparency(Ray &ray, float maxDistance) const
{
// TODO: Not taking advantage of the tree structure!
ray.length = maxDistance;
ray.primitive = nullptr;
Color transparency(1, 1, 1);
for (auto i: this->primitives())
{
Ray r = ray;
if (i->intersect(r) && r.length < maxDistance && i->shader()->isTransparent())
{
Color t = i->shader()->transparency(*this, r, maxDistance);
transparency.r *= t.r;
transparency.g *= t.g;
transparency.b *= t.b;
}
}
return transparency;
}

View file

@ -28,6 +28,7 @@ public:
// Raytracing functions // Raytracing functions
bool findIntersection(Ray &ray) const override; bool findIntersection(Ray &ray) const override;
bool findOcclusion(Ray &ray) const override; bool findOcclusion(Ray &ray) const override;
Color getTransparency(Ray &ray, float maxDistance) const override;
int countNodeIntersections(const Ray &ray) const; int countNodeIntersections(const Ray &ray) const;
// Setup functions // Setup functions

View file

@ -41,6 +41,7 @@ public:
Color traceRay(Ray &ray) const; Color traceRay(Ray &ray) const;
virtual bool findIntersection(Ray &ray) const = 0; virtual bool findIntersection(Ray &ray) const = 0;
virtual bool findOcclusion(Ray &ray) const = 0; virtual bool findOcclusion(Ray &ray) const = 0;
virtual Color getTransparency(Ray &ray, float maxDistance) const = 0;
protected: protected:
Color backgroundColor; Color backgroundColor;

View file

@ -2,16 +2,38 @@
#include "primitive/primitive.h" #include "primitive/primitive.h"
#include "shader/shader.h" #include "shader/shader.h"
bool SimpleScene::findIntersection(Ray &ray) const { bool SimpleScene::findIntersection(Ray &ray) const
bool hit = false; {
for (auto i : this->primitives()) bool hit = false;
hit |= i->intersect(ray); for (auto i: this->primitives())
return hit; hit |= i->intersect(ray);
return hit;
} }
bool SimpleScene::findOcclusion(Ray &ray) const { bool SimpleScene::findOcclusion(Ray &ray) const
for (auto i : this->primitives()) {
if (i->intersect(ray) && !i->shader()->isTransparent()) for (auto i: this->primitives())
return true; if (i->intersect(ray) && !i->shader()->isTransparent())
return false; return true;
return false;
}
Color SimpleScene::getTransparency(Ray &ray, float maxDistance) const
{
ray.length = maxDistance;
ray.primitive = nullptr;
Color transparency(1, 1, 1);
for (auto i: this->primitives())
{
Ray r = ray;
if (i->intersect(r) && r.length < maxDistance && i->shader()->isTransparent())
{
Color t = i->shader()->transparency(*this, r, maxDistance);
transparency.r *= t.r;
transparency.g *= t.g;
transparency.b *= t.b;
}
}
return transparency;
} }

View file

@ -9,6 +9,7 @@ public:
// Raytracing functions // Raytracing functions
bool findIntersection(Ray &ray) const override; bool findIntersection(Ray &ray) const override;
bool findOcclusion(Ray &ray) const override; bool findOcclusion(Ray &ray) const override;
Color getTransparency(Ray &ray, float maxDistance) const override;
}; };
#endif #endif

View file

@ -8,42 +8,42 @@ BrdfShader::BrdfShader(char const *fileName, Color const &scale)
: scale(scale), brdf(std::make_unique<BRDFRead>(fileName)) {} : scale(scale), brdf(std::make_unique<BRDFRead>(fileName)) {}
Color BrdfShader::shade(Scene const &scene, Ray const &ray) const { Color BrdfShader::shade(Scene const &scene, Ray const &ray) const {
// Calculate theta and phi // Calculate theta and phi
float thetaIn = std::acos(dotProduct(-ray.normal, ray.direction)); float thetaIn = std::acos(dotProduct(-ray.normal, ray.direction));
float phiIn = 0.0f; float phiIn = 0.0f;
// Derive local coordinate system // Derive local coordinate system
Vector3d const x = crossProduct(-ray.direction, ray.normal); Vector3d const x = crossProduct(-ray.direction, ray.normal);
Vector3d const y = crossProduct(ray.normal, x); Vector3d const y = crossProduct(ray.normal, x);
// Accumulate the light over all light sources // Accumulate the light over all light sources
Color illuminationColor; Color illuminationColor;
for (const auto &light : scene.lights()) { for (const auto &light : scene.lights()) {
Light::Illumination illum; Light::Illumination illum;
illum = light->illuminate(scene, ray); illum = light->illuminate(scene, ray);
// Diffuse term // Diffuse term
float const cosine = dotProduct(-illum.direction, ray.normal); float const cosine = dotProduct(-illum.direction, ray.normal);
if (cosine > 0) { if (cosine > 0) {
Color color; Color color;
// Avoid numeric instability // Avoid numeric instability
if (cosine < 1) { if (cosine < 1) {
float const thetaOut = std::acos(cosine); float const thetaOut = std::acos(cosine);
// Project outgoing vector into local coordinate system // Project outgoing vector into local coordinate system
Vector3d const c = crossProduct(-illum.direction, ray.normal); Vector3d const c = crossProduct(-illum.direction, ray.normal);
float const phiOut = std::atan2(dotProduct(c, y), dotProduct(c, x)); float const phiOut = std::atan2(dotProduct(c, y), dotProduct(c, x));
color = Color(brdf->lookupBrdfValues(thetaIn, phiIn, thetaOut, phiOut)); color = Color(brdf->lookupBrdfValues(thetaIn, phiIn, thetaOut, phiOut));
} else { } else {
color = Color(brdf->lookupBrdfValues(thetaIn, phiIn, 0, 0)); color = Color(brdf->lookupBrdfValues(thetaIn, phiIn, 0, 0));
} }
// Calculate colors // Calculate colors
Color const diffuseColor = scale * color * cosine; Color const diffuseColor = scale * color * cosine;
illuminationColor += diffuseColor * illum.color; illuminationColor += diffuseColor * illum.color;
}
} }
} return illuminationColor;
return illuminationColor;
} }

259
shader/cloudshader.cpp Normal file
View file

@ -0,0 +1,259 @@
#include <cmath>
#include <iostream>
#include "cloudshader.h"
#include "common/noise/cloudnoise.h"
Color CloudShader::shade(const Scene &scene, const Ray &ray) const
{
Vector3d hitPoint = ray.origin + ray.direction * ray.length; // Potentially add epsilon
// Collect getNoise through the cloud
float cloudLength = 0.0f; // Length of cloud in ray direction
// Get background color behind cloud and information about the clouds length
Ray cloudRay = ray;
cloudRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction;
cloudRay.length = INFINITY;
cloudRay.primitive = nullptr;
// Get out of cloud primitive first
if (ray.primitive->intersect(cloudRay))
{
// Get length
cloudLength = cloudRay.length;
// Prepare ray for background color
cloudRay.setRemainingBounces(cloudRay.getRemainingBounces() + 1);
cloudRay.origin = cloudRay.origin + (cloudRay.length + REFR_EPS) * cloudRay.direction;
cloudRay.length = INFINITY;
cloudRay.primitive = nullptr;
}
Color background = scene.traceRay(cloudRay);
if (cloudLength == 0.0f) return background; // No cloud or at edge
// Calculate step length
float stepLength = settings.densitySteps;
int noiseSamples = std::floor(cloudLength / stepLength);
// Step through cloud
float transmittance = 1.0f;
Color cloudColor = Color(0, 0, 0);
for (int i = 0; i < noiseSamples; ++i)
{
// Get sample point
Vector3d lengthDirection = (float) i * stepLength * ray.direction;
Vector3d samplePoint = hitPoint + lengthDirection;
// Get data at point
float sampleDensity = getCloudDensity(samplePoint, ray.primitive) * stepLength;
if (sampleDensity <= 0)
{
continue;
}
cloudColor += lightMarch(scene, samplePoint, lengthDirection, ray.primitive) * sampleDensity * transmittance;
transmittance *= calcBeer(sampleDensity * settings.lightAbsorptionThroughCloud);
if (transmittance < TRANSMITTANCE_BREAK) break;
}
return background * transmittance + cloudColor;
}
bool CloudShader::isTransparent() const
{
return true;
}
CloudShader::CloudShader(const CloudSettings &settings) : settings(settings),
cloudNoise(CloudNoise(settings.noiseSize, settings.seed))
{
cloudNoise.invert = true;
}
float CloudShader::getCloudDensity(Vector3d point, Primitive const *primitive) const
{
//! Requires the unscaled point
float edgeDensity = getEdgeDensity(point, primitive);
point /= settings.scale;
float density = cloudNoise.getNoise(point);
// Threshold
// TODO: Smooth out!
density = std::max(0.0f, density + settings.densityOffset) * settings.densityIntensity;
return density * edgeDensity;
}
float CloudShader::getEdgeDensity(const Vector3d &point, const Primitive *primitive) const
{
if (primitive == nullptr)
{
return 1;
}
Vector3d size = Vector3d(0, 0, 0);
size.x = primitive->maximumBounds(0) - primitive->minimumBounds(0);
size.y = primitive->maximumBounds(1) - primitive->minimumBounds(1);
size.z = primitive->maximumBounds(2) - primitive->minimumBounds(2);
Vector3d center = Vector3d(0, 0, 0);
center.x = primitive->maximumBounds(0) + primitive->minimumBounds(0);
center.y = primitive->maximumBounds(1) + primitive->minimumBounds(1);
center.z = primitive->maximumBounds(2) + primitive->minimumBounds(2);
center /= 2;
Vector3d distance = point - center;
distance.x = std::abs(distance.x);
distance.y = std::abs(distance.y);
distance.z = std::abs(distance.z);
Vector3d distanceFromEdge = size / 2 - distance;
distanceFromEdge.x = std::max(0.0f, distanceFromEdge.x);
distanceFromEdge.y = std::max(0.0f, distanceFromEdge.y);
distanceFromEdge.z = std::max(0.0f, distanceFromEdge.z);
float fallOff = std::min(distanceFromEdge.x, std::min(distanceFromEdge.y, distanceFromEdge.z)) /
settings.edgeFadeOffDistance;
return std::min(1.0f, fallOff);
}
Color CloudShader::lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance,
const Primitive *cloudObject) const
{
Color cloudColor = Color(0, 0, 0);
// For alle lights
for (const auto &light: scene.lights())
{
Ray ray = Ray(currentInCloudPosition - lengthDistance, normalized(lengthDistance));
ray.length = length(lengthDistance);
ray.primitive = cloudObject;
auto illumination = light->illuminate(scene, ray);
// Handle ambient lights
if (illumination.distance == 0.0f)
{
cloudColor += illumination.color;
continue;
}
// Light ray from object to light
Ray lightRay;
lightRay.origin = currentInCloudPosition;
lightRay.direction = -illumination.direction;
lightRay.primitive = cloudObject;
lightRay.length = 0; // Starting in cloud itself
float density = this->rayDensity(lightRay, illumination.distance);
// Proper light calculation
float transmittance = calcBeer(density * settings.lightAbsorptionTowardsLight);
transmittance *= phase(normalized(lengthDistance), lightRay.direction);
cloudColor += transmittance * illumination.color;
}
float darknessFactor = settings.darknessThreshold +
(1.0f - settings.darknessThreshold);
return cloudColor * darknessFactor;
}
Color CloudShader::transparency(const Scene &scene, const Ray &ray, float maxLength) const
{
float density = rayDensity(ray, maxLength);
float transmittance = calcBeer(density * settings.shadowLightAbsorption);
transmittance = 1 - (1 - transmittance) * settings.shadowIntensity;
return Color(1, 1, 1) * transmittance;
}
float CloudShader::rayDensity(const Ray &ray, float maxLength) const
{
Vector3d startPoint = ray.origin + ray.direction * (ray.length + REFR_EPS);
// Determine length of cloud
Ray cloudRay;
cloudRay.origin = startPoint;
cloudRay.length = INFINITY;
cloudRay.direction = ray.direction;
cloudRay.primitive = nullptr;
// Get out of cloud primitive first
if (ray.primitive != nullptr && !ray.primitive->intersect(cloudRay) || cloudRay.length == INFINITY ||
cloudRay.length <= 0)
{
// Assume ray started in cloud
cloudRay.length = ray.length;
cloudRay.primitive = ray.primitive;
}
float cloudLength = std::min(cloudRay.length, maxLength - ray.length);
// Calculate step length
float stepLength = settings.densitySteps;
int noiseSamples = std::floor(cloudLength / stepLength);
// Step through cloud
float density = 0.0f;
for (int i = 0; i < noiseSamples; ++i)
{
// Get sample point
Vector3d samplePoint = startPoint + (float) i * stepLength * ray.direction;
// Get data at point
density += getCloudDensity(samplePoint, ray.primitive) * stepLength;
}
// If there is length left, check if it is in the cloud recursively
if (cloudLength < maxLength - ray.length)
{
Vector3d endPoint = startPoint + (cloudLength + REFR_EPS) * ray.direction;
Ray recursiveRay = cloudRay;
recursiveRay.origin = endPoint;
recursiveRay.length = 0;
recursiveRay.primitive = nullptr;
if (ray.primitive != nullptr && ray.primitive->intersect(recursiveRay) && recursiveRay.length > 0)
{
density += rayDensity(recursiveRay, maxLength - (cloudLength + REFR_EPS));
}
}
return density;
}
float CloudShader::phase(Vector3d visualRay, Vector3d illuminationRay) const
{
// The angle between the visual and illumination rays
float cosTheta = dotProduct(visualRay, illuminationRay);
// The Dual-Lob Henyey-Greenstein function
float blend = .5;
float phaseBlend = calcDualHenyeyGreenstein(cosTheta, settings.phaseA) * (1 - blend) +
calcDualHenyeyGreenstein(cosTheta, -settings.phaseB) * blend;
// Clamp the result to the range [0, 1]
phaseBlend = std::max(std::min(phaseBlend, 1.0f), 0.0f);
return settings.phaseOffset + phaseBlend * settings.phaseIntensity;
}
float CloudShader::calcBeer(float d)
{
float beer = std::exp(-d);
return beer;
}
float CloudShader::calcDualHenyeyGreenstein(float cosTheta, float g)
{
float g2 = g * g;
return (1 - g2) / (4 * 3.1415f * std::pow(1 + g2 - 2 * g * (cosTheta), 1.5f));
}

67
shader/cloudshader.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef CG1_TRACER_CLOUDSHADER_H
#define CG1_TRACER_CLOUDSHADER_H
#include "scene/scene.h"
#include "shader.h"
#include "light/light.h"
#include "primitive/primitive.h"
#include "common/noise/worleynoise.h"
float const TRANSMITTANCE_BREAK = 0.0001f; // If transmittance goes below this limit, the cloud is considered opaque
struct CloudSettings
{
int noiseSize = 128; // 64
unsigned int seed = 0; // 0 for random seed
float densitySteps = .2f; // .2f
float scale = 30; // 30
float densityOffset = -.55f; // -.55f
float densityIntensity = 5.0f; // 5.0f
float darknessThreshold = .1f; // .1f
float shadowIntensity = .6f; // .6f
float shadowLightAbsorption = 1; // 1
float lightAbsorptionTowardsLight = 0.1f; // .3f // How Bright should the clouds be? Lower is brighter
float lightAbsorptionThroughCloud = .8f; // .8f // How dark should the background be where the cloud is? Higher values mean darker background
float phaseA = .8f; // .5f
float phaseB = .4f; // .3f
float phaseOffset = .8f; // .8f
float phaseIntensity = .1f; // 1.0f
float edgeFadeOffDistance = .5f; // .5f
};
class CloudShader : public Shader
{
public:
CloudShader(CloudSettings const &settings = CloudSettings());
// Shader functions
Color shade(Scene const &scene, Ray const &ray) const;
Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override;
private:
CloudSettings settings;
bool isTransparent() const;
Noise cloudNoise;
float getCloudDensity(Vector3d point, const Primitive *primitive = nullptr) const;
Color lightMarch(const Scene &scene, Vector3d currentInCloudPosition, Vector3d lengthDistance,
const Primitive *cloudObject) const;
float rayDensity(const Ray &ray, float maxLength) const;
float phase(Vector3d visualRay, Vector3d illuminationRay) const;
static float calcDualHenyeyGreenstein(float cosTheta, float g) ;
static float calcBeer(float d) ;
float getEdgeDensity(const Vector3d &point, const Primitive *primitive) const;
};
#endif //CG1_TRACER_CLOUDSHADER_H

12
shader/depthshader.cpp Normal file
View file

@ -0,0 +1,12 @@
#include "depthshader.h"
Color DepthShader::shade(const Scene &scene, const Ray &ray) const
{
float brightness = exp(-ray.length * 0.1f);
return brightness * nearColor + (1 - brightness) * farColor;
}
DepthShader::DepthShader(const Color &nearColor, const Color &farColor) : nearColor(nearColor), farColor(farColor)
{
}

21
shader/depthshader.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef CG1_TRACER_DEPTHSHADER_H
#define CG1_TRACER_DEPTHSHADER_H
#include "shader/shader.h"
class DepthShader : public Shader {
public:
DepthShader(Color const &nearColor = Color(1, 1, 1), Color const &farColor = Color(0, 0, 0));
// Shader functions
Color shade(Scene const &scene, Ray const &ray) const override;
private:
Color nearColor;
Color farColor;
};
#endif //CG1_TRACER_DEPTHSHADER_H

15
shader/noiseshader.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "noiseshader.h"
NoiseShader::NoiseShader(std::shared_ptr<Noise> noise, float scale, Vector3d offset)
{
this->noise = noise;
this->scale = scale;
this->offset = offset;
}
Color NoiseShader::shade(const Scene &scene, const Ray &ray) const
{
Vector3d point = ray.origin + ray.direction * ray.length;
float noiseValue = noise->getNoise(point / scale + offset);
return Color(1, 1, 1) * noiseValue;
}

25
shader/noiseshader.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef CG1_TRACER_NOISESHADER_H
#define CG1_TRACER_NOISESHADER_H
#include <memory>
#include "shader.h"
#include "common/noise/noise.h"
class NoiseShader : public Shader
{
public:
// Constructor
NoiseShader(std::shared_ptr<Noise> noise, float scale = 1, Vector3d offset = Vector3d(0, 0, 0));
// Shader functions
Color shade(Scene const &scene, Ray const &ray) const override;
private:
float scale;
Vector3d offset;
std::shared_ptr<Noise> noise;
};
#endif //CG1_TRACER_NOISESHADER_H

View file

@ -1,20 +1,29 @@
#include "scene/scene.h" #include "scene/scene.h"
#include "shader/refractionshader.h" #include "shader/refractionshader.h"
RefractionShader::RefractionShader(float indexInside, float indexOutside) : indexInside(indexInside), indexOutside(indexOutside) {} RefractionShader::RefractionShader(float indexInside, float indexOutside, Color const &objectColor, float lightLoss)
: indexInside(
indexInside), indexOutside(indexOutside), objectColor(objectColor), lightLoss(lightLoss)
{}
Color RefractionShader::shade(Scene const &scene, Ray const &ray) const
{
// Circumvent getting environment map color into the mix
if (ray.getRemainingBounces() <= 0)
{
return Color(0.0f, 0.0f, 0.0f);
}
Color RefractionShader::shade(Scene const &scene, Ray const &ray) const {
// Circumvent getting environment map color into the mix
if (ray.getRemainingBounces() > 0) {
// Get the normal of the primitive which was hit // Get the normal of the primitive which was hit
Vector3d normalVector = ray.normal; Vector3d normalVector = ray.normal;
// Calculate the index of refraction // Calculate the index of refraction
float refractiveIndex = indexOutside / indexInside; float refractiveIndex = indexOutside / indexInside;
// What if we are already inside the object? // What if we are already inside the object?
if (dotProduct(normalVector, ray.direction) > 0) { if (dotProduct(normalVector, ray.direction) > 0)
normalVector = -normalVector; {
refractiveIndex = indexInside / indexOutside; normalVector = -normalVector;
refractiveIndex = indexInside / indexOutside;
} }
// Using the notation from the lecture // Using the notation from the lecture
@ -30,22 +39,51 @@ Color RefractionShader::shade(Scene const &scene, Ray const &ray) const {
refractionRay.primitive = nullptr; refractionRay.primitive = nullptr;
// Check whether it is a refraction. // Check whether it is a refraction.
if (dotProduct(t, normalVector) <= 0.0) { if (dotProduct(t, normalVector) <= 0.0)
refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction; {
refractionRay.direction = normalized(t); refractionRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction;
} else { // Otherwise, it is a total reflection. refractionRay.direction = normalized(t);
refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; } else
// Next we get the reflection vector { // Otherwise, it is a total reflection.
Vector3d const reflectionVector = ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector; refractionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction;
// Next we get the reflection vector
Vector3d const reflectionVector =
ray.direction - 2.0f * dotProduct(normalVector, ray.direction) * normalVector;
// Change the ray direction and origin // Change the ray direction and origin
refractionRay.direction = normalized(reflectionVector); refractionRay.direction = normalized(reflectionVector);
} }
// Send out a new refracted ray into the scene // Send out a new refracted ray into the scene
return scene.traceRay(refractionRay); Color hitColor = scene.traceRay(refractionRay);
} float lightRemaining = 1;
return Color(0.0f, 0.0f, 0.0f); if (ray.primitive == refractionRay.primitive) lightRemaining = remainingLightIntensity(refractionRay.length);
return hitColor * objectColor * lightRemaining;
} }
bool RefractionShader::isTransparent() const { return true; } float RefractionShader::remainingLightIntensity(float distanceThroughObject) const
{
return 1 - lightLoss + exp(-distanceThroughObject / 10) * lightLoss;
}
bool RefractionShader::isTransparent() const
{ return true; }
Color RefractionShader::transparency(const Scene &scene, const Ray &ray, float maxLength) const
{
// Determine length through the object
Ray lengthRay = ray;
// Reset the ray
lengthRay.length = INFINITY;
lengthRay.primitive = nullptr;
lengthRay.origin = ray.origin + (ray.length + REFR_EPS) * ray.direction;
scene.traceRay(lengthRay);
float transparencyDistance = std::min(maxLength - ray.length, lengthRay.length);
float lightRemaining = 1;
if (ray.primitive == lengthRay.primitive) lightRemaining = remainingLightIntensity(transparencyDistance);
return objectColor * lightRemaining;
}

View file

@ -3,19 +3,26 @@
#include "shader/shader.h" #include "shader/shader.h"
class RefractionShader : public Shader { class RefractionShader : public Shader
{
public: public:
// Constructor // Constructor
RefractionShader(float indexInside, float indexOutside); RefractionShader(float indexInside, float indexOutside, Color const &objectColor = Color(1, 1, 1), float lightLoss = 0);
// Shader functions // Shader functions
Color shade(Scene const &scene, Ray const &ray) const override; Color shade(Scene const &scene, Ray const &ray) const override;
bool isTransparent() const override;
bool isTransparent() const override;
Color transparency(const Scene &scene, const Ray &ray, float maxLength) const override;
private: private:
float indexInside; float indexInside;
float indexOutside; float indexOutside;
float lightLoss;
Color objectColor;
float remainingLightIntensity(float distanceThroughObject) const;
}; };
#endif #endif

View file

@ -7,17 +7,30 @@
// Forward declarations // Forward declarations
class Scene; class Scene;
class Shader { class Shader
{
public: public:
// Constructor / Desctructor // Constructor / Desctructor
Shader() = default; Shader() = default;
virtual ~Shader() = default;
// Get virtual ~Shader() = default;
virtual bool isTransparent() const { return false; }
// Shader functions // Get
virtual Color shade(Scene const &scene, Ray const &ray) const = 0; virtual bool isTransparent() const
{ return false; }
/**
* Especially used for lighting calculations.
* @brief Returns the light let through the shader in opposite direction of the given ray.
* @param ray Origin and direction of the desired path through the object. Origin might be inside or outside the object. Length of the ray is upper bound for the destination point, and might not go through the object completely.
* @param maxLength Maximum length of the ray. If the ray through the object is longer than this, it is cut off.
* @return 0 if the shader is opaque, 1 if the shader is transparent, for each color channel.
*/
virtual Color transparency(const Scene &scene, const Ray &ray, float maxLength) const
{ return isTransparent() ? Color(1, 1, 1) : Color(0, 0, 0); }
// Shader functions
virtual Color shade(Scene const &scene, Ray const &ray) const = 0;
}; };
#endif #endif