140 Commits

Author SHA1 Message Date
Manoël Trapier
0e188a0594 Update build badge from travis to github action.
(free for OSS project) Travis is dead!
2022-02-28 11:43:31 +00:00
Godzil
c1f41cb6f6 Trying to fix github actions. 2022-02-28 11:37:41 +00:00
Godzil
50143076ab Fix a typo in README 2022-02-28 11:29:44 +00:00
Manoël Trapier
c33cdabdec Add some actions! 2022-02-28 11:04:51 +00:00
Godzil
ad16643111 Googletest...
This "master" to "main" is already none-sense, but renaming the principal branch on an existing repo to break everything.
Thanks a lot.
2022-02-28 10:55:52 +00:00
Godzil
45f5f8098e Force cmake 3.11 because of changes in add_executable
Remove Ubuntu 18.04 arm target as it use an old cmake.
2021-03-10 15:22:26 +00:00
Godzil
28b619c566 Try to change (again) the travis file. May work this time? 2021-03-10 14:08:08 +00:00
Godzil
4f512405b2 Travis works, but always is unpredictable at time.. 2021-03-10 12:26:52 +00:00
Godzil
fe80488c5e Update travis file to more up to date practice for Mac OS X build using homebrew 2021-03-10 12:17:28 +00:00
Godzil
f8f4d7b21a Add sample output from the dragon scene 2021-03-10 11:58:09 +00:00
Godzil
5651570c2b Slight changes in octree/bvh 2020-03-17 00:44:58 +00:00
Godzil
71c236abb0 Better to fill the function I suppose. 2020-03-17 00:31:30 +00:00
Godzil
c0fc061834 Don't set the focal/aperture on the constructor and use a dedicated method for that. 2020-03-17 00:28:56 +00:00
Godzil
61ce9d3543 Add possibility to play with focal length and aperture to the camera. Not enabled by default. 2020-03-17 00:23:16 +00:00
Godzil
9849c16f66 Couple of small optimisation
Add option to build for gprof
Do not build by default with the renderstat (they have a reasonable impact on performances)
Separated created ray and castedray in the stats
Trying to force some simple function to be inlined
2020-03-13 18:22:35 +00:00
Godzil
aacd4f6c9e Make BVH and Octree to not flatten the world before parsing
It may help a lot on some scene that already use groups.

So each exiting group have their own BVH or Octree in.

It does help a lot on already organised scene like the Christmas balls.
2020-03-13 01:07:28 +00:00
Godzil
5b6b627a43 Small but effective optimisations.
Allow inlining the function helps a bit!
2020-03-13 01:04:56 +00:00
Manoël Trapier
4f2e327533 Update for a proper testing size and don’t get a timeout 2020-03-12 19:39:32 +00:00
Godzil
c858b4dcde A new scene and some optimisations. 2020-03-12 17:45:29 +00:00
Godzil
7a43a98816 Adding BVH optimisation. 2020-03-12 07:21:44 +00:00
Godzil
47e31714f3 That file was supposed to be there XD 2020-03-12 00:14:50 +00:00
Godzil
424d58c59b Octree Optimiser seems to work.
Well.. There are a couple of weird things happening, need to investigate, but it's not that easy when scene start to be complicated :/
2020-03-12 00:14:00 +00:00
Godzil
5996e38e6e Fix my own stupidity.. 2020-03-12 00:11:48 +00:00
Godzil
b00bb75189 Huge speed up by changing how Intersect are shared. 2020-03-12 00:11:26 +00:00
Godzil
0aa949c60b Continue working work optimiser. 2020-03-11 19:35:08 +00:00
Godzil
420203e95d Add a tool to debug the normal vector in objfiles by rendering them. 2020-03-11 16:59:02 +00:00
Godzil
c667ce26d7 Fix a bug in objfile where OBJ file were not the parent of the base group. 2020-03-11 16:57:49 +00:00
Godzil
13cc2c0ff9 Add more useful dump info for shapes 2020-03-11 16:56:38 +00:00
Godzil
1c4018e47a Cosmetics. 2020-03-11 16:56:03 +00:00
Godzil
083a5b600f typo 2020-03-11 16:54:46 +00:00
Godzil
3a2d21b787 Starting to work on the World Optimiser sequence. 2020-03-11 09:09:30 +00:00
Godzil
441d758845 Add locking mechanism to prevent updating transform/parent 2020-03-10 13:55:27 +00:00
Godzil
5da0c10182 Started working on "world optimiser" base class.
The idea is to reorganise objets to take use of the them to prevent most object to be intersect if they are not likely to be on the ray path.
2020-03-10 09:18:15 +00:00
Godzil
b89f9ec331 Add a way to remove shapes from groups 2020-03-10 09:16:39 +00:00
Godzil
add3d7c861 A bit of cleanup 2020-03-10 09:15:38 +00:00
Godzil
4e241a1871 Trying to make the dump a bit more usefull and slightly less cluttered 2020-03-09 17:41:11 +00:00
Godzil
15a861802a OBJFile now use group instead of it own list.
Change the group function to find group by name, and now name group when needed, and change tests to use group name instead of number.

Also now the default group is the base group, not a separate group.

Bonus: Add a destructor to cleanup memory
Added a function to get base group. OBJFile will still behave as a valid shape, but now we can skip it in the generated world by adding the base group instead.

Code is now cleaner.
2020-03-09 16:03:27 +00:00
Godzil
66a8b98aeb Add missing code \o/ 2020-03-09 15:59:52 +00:00
Godzil
0f945b69cc World default group now have a name!
Fancy!
2020-03-09 15:58:59 +00:00
Godzil
314da11005 Add support for naming groups 2020-03-09 15:58:32 +00:00
Godzil
8437ab8753 Move ShapeType into the Shape object. 2020-03-09 15:57:23 +00:00
Godzil
d514219ae6 Due to the way material are working now, we need to set by hand (for now) that the material is set. 2020-03-09 14:17:08 +00:00
Godzil
efa47f28ca Fix some potential buffer overflow issues.
Not critical, but better to avoid them!
2020-03-09 14:16:05 +00:00
Godzil
e653855556 Change World to use a base group instead of storing all the world object.
Makes the code simpler and avoid some potential issues. Add to change a bit some test to make them pass properly.
2020-03-09 13:47:42 +00:00
Godzil
1510de3b36 Change CSG to derive the intersect function and do the calculation there instead of the local one.
Also use the bounding box to reject ray that don't it that CSG element.
2020-03-09 13:45:57 +00:00
Godzil
8550d4068f Correct how filterIntersections work, seems c++ don't like how thing were done. 2020-03-09 13:44:10 +00:00
Godzil
cd93b67274 Revert some debug changes that should have not been commited. 2020-03-09 13:42:21 +00:00
Godzil
18965fe1bd They say it is better to do this way. 2020-03-06 21:59:16 +00:00
Godzil
57eff4830e Sample scene for CSG \o/ 2020-03-06 21:55:32 +00:00
Godzil
b5ee92c544 And CSG! \o/
Still working on a nice scene for it.
2020-03-06 19:00:31 +00:00
Godzil
e57b5715e8 Smooth triangles! And support for them in the OBJ File parser.
Also add an interesting tea party scene!
2020-03-06 15:07:26 +00:00
Godzil
73012b6dd1 We need to run testMyRays in the test folder as it now depends on some files that need to be in the work directory. 2020-03-06 10:13:04 +00:00
Godzil
2488cc6319 Seems long ago I forgot to update/correct the local boudingbox calculation for triangles.... 2020-03-06 09:39:33 +00:00
Godzil
518ac260e1 OBJFile parser seems to work.
Can render a simple scene using it.

TODO: Need add way to set material to childs.
2020-03-06 09:38:39 +00:00
Godzil
2725b5f657 Go a bit deeper in parsing the OBJ file. Now we parse each lines into token, and it's now ready to try to execute them. 2020-03-06 01:29:26 +00:00
Godzil
c17bfadc76 Started working on parsing OBJ Files.
Why string manipulation is so tedious in C/++ :(
2020-03-05 17:46:40 +00:00
Godzil
6bef6a1b77 over 9000 years of shame on me.
What where I thinking when I wrote that?!
2020-03-05 16:28:49 +00:00
Godzil
e33cf0d5f0 Maybe need to unlink Python2 before?! 2020-03-05 16:15:58 +00:00
Godzil
c41cbaeea8 Update readme 2020-03-05 16:01:47 +00:00
Godzil
3a8600d274 Add travis command to install ImageMagick on Mac OS X VMs 2020-03-05 15:58:03 +00:00
Godzil
f8aa5cc920 Fix a "bug" in UV Image introduced while debugging 2020-03-05 15:15:20 +00:00
Godzil
30db4d7ca1 Add support to load JPEG files along with PNGs. 2020-03-05 15:14:31 +00:00
Godzil
317148a37d Lua include is only added if lua support is enabled.
Add NanoJPEG dependency to the main library
2020-03-05 15:13:40 +00:00
Godzil
5c6a5afef6 Add support for NanoJPEG to load jpeg files 2020-03-05 15:12:35 +00:00
Godzil
90e79576a2 Correct the MD5 sum 2020-03-05 15:11:34 +00:00
Godzil
a31ae5b2ef Add download and copy of the earth texture map. 2020-03-05 15:09:26 +00:00
Godzil
2ebc177d9d Header update/corrections 2020-03-05 15:06:16 +00:00
Godzil
6a2c5a77ae Trying to implement image mapping.
Something not working yet.
2020-03-05 09:48:37 +00:00
Godzil
d6ae062f7f Add checkered cube render 2020-03-05 00:24:02 +00:00
Godzil
3ebe403de0 Continue working on Cube mapping 2020-03-05 00:10:39 +00:00
Godzil
f5685a45e1 Add AlignCheck UV Pattern 2020-03-04 16:42:43 +00:00
Godzil
7209244f48 Add cylindrical mapping! 2020-03-04 16:16:35 +00:00
Godzil
1b6c14691b fmod is not mathematically valid. Replace it by a correct one. 2020-03-04 16:15:49 +00:00
Godzil
107b612130 Added planar mapping 2020-03-04 13:35:09 +00:00
Godzil
5bbd036fc5 Add spherical mapping render to the README. 2020-03-04 13:23:22 +00:00
Godzil
1ceabe7e62 Change CMake files to simplify them.
Also disable dorayme.cpp for now as it if wrongly taken in the rayonnement library.
2020-03-04 13:21:39 +00:00
Godzil
3e37b5ca44 Add spherical mapping for UV Textures. 2020-03-04 13:20:15 +00:00
Godzil
5c10d65c8d Started working on 2D patterns. 2020-03-04 09:32:11 +00:00
Manoël Trapier
83c12dbd83 Update README.md 2020-03-03 00:12:45 +00:00
Godzil
3cfbb604b0 Fix missing includes. 2020-03-02 23:34:44 +00:00
Godzil
36962275f6 Disable the second jittered test as I can't find a way to make them consistant (they fail depends on combination of OS/Compiler because of the way compiler work..) 2020-03-02 23:29:06 +00:00
Godzil
a1c53fc9cc Commited that file with incorrect size for automated test. 2020-03-02 23:16:17 +00:00
Godzil
0be3236a03 Fix how the Lua lib is build to remove that annoying tmpnam warning. 2020-03-02 18:05:33 +00:00
Godzil
344c36cd78 Remove frand from that scene. 2020-03-02 17:44:17 +00:00
Godzil
478b1f0af1 Disable a test as it is not consistent between compilers. 2020-03-02 17:41:04 +00:00
Godzil
1cebcd4f8b Fix my own stupidity. 2020-03-02 16:49:10 +00:00
Godzil
aab9df0802 Add support for Lua in world, and create the Lua Pattern (pattern can be defined with a lua function) 2020-03-02 16:30:24 +00:00
Godzil
d05a0fb4d0 Update ch11 reflection scene to render the 3 different view 2020-03-02 14:09:30 +00:00
Godzil
21749695b6 Add jitter to area light and example render of it. 2020-03-02 14:03:31 +00:00
Godzil
1fbe682572 Update readme 2020-03-02 08:46:30 +00:00
Godzil
ace7d53571 Clearing up some memory to prevent stupid issues
Preparing for some optimisations. (absolutely need to reduce the ammount of allocations done.)
2020-03-02 08:24:09 +00:00
Godzil
0ac44c3539 Use area light and make a proper canvas size to run the size.
This test is eating memory like crazy. Need to see why.
2020-02-28 18:36:57 +00:00
Godzil
c4b680789e Starting working on area lights. 2020-02-28 18:35:45 +00:00
Godzil
53f66b554b Move renderstat function into the CPP file and add atomic to the variables. 2020-02-28 18:34:42 +00:00
Godzil
307c125eba More bounding boxes 2020-02-28 18:33:59 +00:00
Godzil
b4ae737b40 Continuing working on dumping the world 2020-02-28 09:29:09 +00:00
Godzil
8ceb68fdff Add dependencies to Lua. 2020-02-28 00:05:14 +00:00
Godzil
c369d2fe2d Start working on dumping the world (to a JSON file) for debug purposes. 2020-02-27 18:03:08 +00:00
Godzil
2926166ce6 Add Lua dependencies.
Lua is going to be used for both scene description (it will also provide a YAML importer) and some future expansion in the code. Expect some fun surprises!

(just playing with lua on the main app for now)
2020-02-27 17:24:40 +00:00
Godzil
e61382a129 Doh, this was suppose to be the other way around: add to the bound IF it have finit bounds! 2020-02-27 17:22:47 +00:00
Godzil
a6f0422bd1 Add renderstat to get some info about rendering. 2020-02-27 17:20:55 +00:00
Godzil
a4ddfddbf3 Found the problem with openmp.
X here need to be declared as private, else each thread are sharing the same variable which... well.... don't work well .. :/
2020-02-26 16:09:28 +00:00
Godzil
f1849cdbc1 Pow is way too slow. 2020-02-26 16:08:34 +00:00
Manoël Trapier
9174424d91 Update README.md 2020-02-26 02:29:10 +00:00
Godzil
0369bee306 It is christmas time! 2020-02-26 02:24:04 +00:00
Godzil
ed347e304d Quick (and dirty) change to be able to use OpenMP for rendering. 2020-02-26 02:18:54 +00:00
Godzil
7a96d42874 Add bounding box calculation to triangle..
Could be helpful XD
2020-02-26 02:17:57 +00:00
Godzil
1c00077949 Prepare code to be able to get material from some form of a "group leader". 2020-02-26 00:32:14 +00:00
Godzil
5e4cfb84e6 Fix an issue with groups, and add Chapter 14 example! 2020-02-26 00:30:09 +00:00
Godzil
aded6bb943 Triangles!!! 2020-02-25 18:42:45 +00:00
Godzil
2ea4abdce7 Boundingboxes should be ready.
Next step (later) would be to properly use them other than group to lower the number of intersection calculation per ray.
2020-02-25 18:03:12 +00:00
Godzil
831a096281 Continue working on bounding boxes. 2020-02-25 09:20:38 +00:00
Godzil
3011544e8f Started working on boundingboxes. 2020-02-24 18:03:25 +00:00
Godzil
d1965caf8d Add an option to run coverage locally 2020-02-24 17:26:36 +00:00
Godzil
7bbe5e843b Group should work now. 2020-02-24 17:25:54 +00:00
Godzil
7c794f0496 Working on groups 2020-02-24 09:25:52 +00:00
Godzil
80f59efa43 Add another hardcoded scene. Also made a test file for hw3 that should cover all the commands. 2020-02-23 19:37:47 +00:00
Godzil
f226664fe3 And cones ! 2020-02-23 02:31:30 +00:00
Godzil
0650ac7b44 There were a small copy mistake in ch12 test file. Update the render output 2020-02-22 23:01:06 +00:00
Godzil
d87bbb184e And now we have cylinders! 2020-02-22 22:58:57 +00:00
Godzil
b9bacd3ac9 Don't really understand why this code is marked as not being tested where it should. 2020-02-22 18:51:03 +00:00
Godzil
1d685de8fd Trying to identify why they say these lines are not tested 2020-02-22 18:29:47 +00:00
Godzil
9c35cfc4f3 Trying to fix coverage. 2020-02-22 18:21:30 +00:00
Godzil
56095169eb Add a test for hw3render 2020-02-22 18:00:07 +00:00
Godzil
60db274214 Trye to talk a bit more in the readme XD 2020-02-22 17:45:45 +00:00
Godzil
566be9bcf6 Add missing image 2020-02-22 17:40:48 +00:00
Godzil
dac74007ea Add sample from Chapter 12 :) 2020-02-22 17:39:25 +00:00
Godzil
b251b632ac Add a parameter for shapes to not drop shadow. 2020-02-22 17:38:25 +00:00
Godzil
81e323fdf4 Added CUBES! 2020-02-22 17:30:15 +00:00
Godzil
c9021974f6 Add a world generator based on another raytracer file format I made in the past and add a crude tool to run it.
it does not render properly, there are some major differences between both engine especially in the material definition. Will need more work, but is not urgent.
2020-02-22 15:16:25 +00:00
Godzil
4d4c4a7453 Add preliminary support for material emissivity.
Not yet sure it work as I intended.
2020-02-22 15:14:09 +00:00
Godzil
935c8ebff7 Add support for multiple lights 2020-02-22 15:12:06 +00:00
Godzil
4cdf7a4264 Correct default canvas size for ch11_test 2020-02-22 01:29:42 +00:00
Godzil
51a6bbebb9 Refraction is fully there, with magic fresnel! 2020-02-22 01:27:48 +00:00
Godzil
e45dbad59e Added some proper test scenes for chapter 11. 2020-02-22 00:50:55 +00:00
Godzil
3db0aaaeac Refraction seems to work. Still need to do a nice scene. 2020-02-21 22:50:12 +00:00
Godzil
df52cb36db Working on refraction & transparency.
Lots of work left to do!
2020-02-21 18:59:14 +00:00
Godzil
89dd74fa7c Finally! We have reflections! 2020-02-21 17:39:45 +00:00
Godzil
7337ae4837 Finally! We have reflections! 2020-02-21 17:21:06 +00:00
Godzil
9fffb68026 Remove nanogui dependencies for now. If the need of a gui come, will add back but for now it just add unnecessary checkout time 2020-02-21 12:08:54 +00:00
158 changed files with 23597 additions and 513 deletions

44
.github/workflows/cmake.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: CMake
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.allow_failure }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-latest ]
allow_failure: [false]
# include:
# - os: macos-latest
# allow_failure: true
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Test round 1
working-directory: ${{github.workspace}}/build
run: make test
- name: Test round 2
working-directory: ${{github.workspace}}/build/tests
run: make test

3
.gitmodules vendored
View File

@@ -1,9 +1,6 @@
[submodule "external/googletest"]
path = external/googletest
url = https://github.com/google/googletest.git
[submodule "external/nanogui"]
path = external/nanogui
url = https://github.com/Godzil/nanogui.git
[submodule "external/glfw"]
path = external/glfw
url = https://github.com/glfw/glfw.git

View File

@@ -1,17 +1,13 @@
dist: bionic
language: c
addons:
apt:
packages:
- lcov
os:
- linux
- osx
compiler:
- clang
- gcc
- imagemagick
homebrew:
packages:
- imagemagick
script:
- mkdir build
@@ -19,12 +15,61 @@ script:
- cmake ..
- make
- make test
- ./tests/testMyRays
- cd tests
- ./testMyRays
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python@2 ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install imagemagick ; fi
jobs:
include:
- os: linux
dist: bionic
arch: amd64
compiler: gcc
- os: linux
dist: bionic
arch: amd64
compiler: clang
- os: linux
dist: focal
arch: amd64
compiler: gcc
- os: linux
dist: focal
arch: amd64
compiler: clang
- os: osx
compiler: clang
osx_image: xcode12.2
- os: linux
dist: focal
arch: arm64
compiler: gcc
- os: osx
compiler: clang
osx_image: xcode10.3
- os: osx
compiler: clang
osx_image: xcode11.6
- os: osx
compiler: clang
osx_image: xcode12
- stage: "Coverage"
os: linux
dist: bionic
arch: amd64
compiler: gcc
script:
- mkdir coverage
@@ -33,4 +78,10 @@ jobs:
- cmake --build .
- cmake --build . --target coveralls
after_success:
- bash <(curl -s https://codecov.io/bash)
- bash <(curl -s https://codecov.io/bash)
allow_failures:
- os: linux
arch: arm64
- os: osx

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.11)
include(ExternalProject)
@@ -10,18 +10,88 @@ option(COVERALLS "Generate coveralls data" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/external/coveralls-cmake/cmake)
option(PACKAGE_TESTS "Build the tests" ON)
option(ENABLE_COVERAGE "Build for code coverage" OFF)
option(SHOW_STATS "Show rendering stat" OFF)
if (SHOW_STATS)
add_compile_options(-DRENDER_STATS)
endif()
option(USE_GPROF "Enable profiling" OFF)
if (USE_GPROF)
add_compile_options(-pg)
add_link_options(-pg)
endif()
option(USE_LUA "Enable the use of Lua" ON)
if (USE_LUA)
add_compile_options(-DENABLE_LUA_SUPPORT)
endif()
if (ENABLE_COVERAGE AND COVERALLS)
message(FATAL_ERROR "You can't enable both ENABLE_COVERAGE and COVERALLS at the same time")
endif()
if (COVERALLS)
include(Coveralls)
coveralls_turn_on_coverage()
endif()
if (ENABLE_COVERAGE)
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang" OR
"${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
message("Building with LLVM Code Coverage Tools")
set(CMAKE_CXX_FLAGS "-fprofile-instr-generate -fcoverage-mapping")
elseif(CMAKE_COMPILER_IS_GNUCXX)
message("Building with lcov Code Coverage Tools")
set(CMAKE_CXX_FLAGS "--coverage")
else()
message(FATAL_ERROR "Compiler ${CMAKE_C_COMPILER_ID} is not supported for code coverage")
endif()
endif()
# LodePNG don't make a .a or .so, so let's build a library here
add_library(LodePNG STATIC)
set(LODEPNG_INCLUDE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/external/lodepng)
target_sources(LodePNG PRIVATE external/lodepng/lodepng.cpp external/lodepng/lodepng.h)
if (USE_LUA)
if (CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
set(LUA_MAKE_TARGET linux)
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
set(LUA_MAKE_TARGET macosx)
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
set(LUA_MAKE_TARGET posix)
else()
set(LUA_MAKE_TARGET posix)
endif()
message("-- Lua: Building Lua for ${LUA_MAKE_TARGET}")
ExternalProject_Add(LuaCore
URL "https://www.lua.org/ftp/lua-5.3.5.tar.gz"
URL_HASH SHA1=112eb10ff04d1b4c9898e121d6bdf54a81482447
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/lua
CONFIGURE_COMMAND ""
BUILD_IN_SOURCE True
BUILD_COMMAND make ${LUA_MAKE_TARGET}
INSTALL_COMMAND ""
)
set(LUA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/lua/src")
set(LUA_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/external/lua/src/liblua.a" -ldl)
endif()
# NanoJPEG
file(DOWNLOAD
http://svn.emphy.de/nanojpeg/trunk/nanojpeg/nanojpeg.c
${CMAKE_CURRENT_SOURCE_DIR}/external/nanojpeg/nanojpeg.c
EXPECTED_HASH MD5=03ce304a71ae0ad1c43663fb386cc233
)
add_library(NanoJPEG STATIC)
set(NANOJPEG_INCLUDE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/external/nanojpeg)
target_sources(NanoJPEG PRIVATE external/nanojpeg/nanojpeg.c)
# Main app
add_subdirectory(source)

View File

@@ -1,4 +1,4 @@
[![codecov](https://codecov.io/gh/Godzil/DoRayMe/branch/master/graph/badge.svg)](https://codecov.io/gh/Godzil/DoRayMe) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/66339747e4a843719cba29cf5e31ff90)](https://www.codacy.com/manual/Godzil/DoRayMe?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=Godzil/DoRayMe&amp;utm_campaign=Badge_Grade) [![Coverage Status](https://coveralls.io/repos/github/Godzil/DoRayMe/badge.svg?branch=master)](https://coveralls.io/github/Godzil/DoRayMe?branch=master) [![Build Status](https://travis-ci.org/Godzil/DoRayMe.svg?branch=master)](https://travis-ci.org/Godzil/DoRayMe)
[![codecov](https://codecov.io/gh/Godzil/DoRayMe/branch/master/graph/badge.svg)](https://codecov.io/gh/Godzil/DoRayMe) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/66339747e4a843719cba29cf5e31ff90)](https://www.codacy.com/manual/Godzil/DoRayMe?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=Godzil/DoRayMe&amp;utm_campaign=Badge_Grade) [![Coverage Status](https://coveralls.io/repos/github/Godzil/DoRayMe/badge.svg?branch=master)](https://coveralls.io/github/Godzil/DoRayMe?branch=master) [![CMake](https://github.com/Godzil/DoRayMe/actions/workflows/cmake.yml/badge.svg)](https://github.com/Godzil/DoRayMe/actions/workflows/cmake.yml)
DoRayMe
=======
@@ -6,34 +6,97 @@ DoRayMe
A Quick and dirty raytracer.
This raytracer is made following the book "[The Ray Tracer Challenge](https://pragprog.com/book/jbtracer/the-ray-tracer-challenge)" by Jamis Buck.
This raytracer is made following the book
"[The Ray Tracer Challenge](https://pragprog.com/book/jbtracer/the-ray-tracer-challenge)" by Jamis Buck.
It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lodepng) to output PNG file.
It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lodepng) to output PNG file and use them
as texture, also use [NanoJPEG](https://keyj.emphy.de/nanojpeg/) to use jpeg file as texture, and can use use
[Lua 5.3](https://www.lua.org/) for 3D pattern definition and more to come on the Lua side later..
Examples outputs
----------------
From chapter 05:
#### From chapter 05 - Sphere intersections:
![Chapter 5 rendering test](output/ch5_test.png)
From Chapter 06:
#### From Chapter 06 - Phong shading:
![Chapter 6 rendering test](output/ch6_test.png)
From Chapter 07:
#### From Chapter 07 - World / Camera / Scenes:
![Chapter 7 rendering test](output/ch7_test.png)
From Chapter 08:
#### From Chapter 08 - Shadows:
![Chapter 8 rendering test](output/ch8_test.png)
From Chapter 09:
#### From Chapter 09 - Planes:
![Chapter 9 rendering test](output/ch9_test.png)
From Chapter 10:
#### From Chapter 10 - Patterns:
![Chapter 10 rendering test](output/ch10_test.png)
![Chapter 10 rendering test](output/ch10_test.png)
#### From Chapter 11 - Reflections, Transparency & Refractions:
![Chapter 11 reflections rendering test](output/ch11_reflection.png)
###### Bonus: Zooming on a reflective ball:
![Chapter 11 zooming on a ball](output/ch11_zooming_on_reflective_ball.png)
###### Zooming on a reflection on that ball:
![Chapter 11 zooming on a reflection](output/ch11_reflection_on_ball.png)
![Chapter 11 refraction rendering test](output/ch11_refraction.png)
![Chapter 11 rendering test](output/ch11_test.png)
#### From Chapter 12 - Cubes:
![Chapter 12 rendering test](output/ch12_test.png)
#### From Chapter 13 - Cylinders:
![Chapter 13 rendering test](output/ch13_test.png)
###### Bonus:
![Chapter 13 cone test](output/ch13_cone.png)
#### From Chapter 14 - Groups & Bounding boxes:
![Chapter 14 rendering test](output/ch14_test.png)
#### From Chapter 15 - Triangles, Wavefrom OBJ files - Smooth trianges:
![Chapter 15 Triangles and teapots](output/ch15_teapot_objfile.png)
#### From Chapter 16 - Constructive Solid Geomety:
![Chapter 16 CSG](output/ch16_test.png)
#### Bonus (from the forum):
[Merry Christmas](https://forum.raytracerchallenge.com/thread/16/merry-christmas-scene-description)
![Merry Christmas](output/christmasball.png)
(about 1min render time using OpenMP on a 2.6Ghz Core i7 3720QM)
#### Bonus chapter - Soft shadow / Area light
###### Without jitter:
![Area light without jitter](output/arealight_test_nojitter.png)
###### With jitter:
![Area light witht jitter](output/arealight_test.png)
#### Bonus chapter - Texture mapping
###### Spherical mapping:
![Spherical mapping](output/uvmap_checkeredsphere.png)
###### Planar mapping:
![Planar mapping](output/uvmap_checkeredplane.png)
###### Cylindrical mapping:
![Cylindrical mapping](output/uvmap_checkeredcylinder.png)
###### Aligncheck plane:
![Aligncheck plane](output/uvmap_aligncheckplane.png)
###### Cubical mapping:
![Cubical mapping](output/uvmap_checkeredcube.png)
###### Image mapping:
![Image mapping](output/uvmap_earth.png)
###### Skybox:
![Skybox](output/uvmap_skybox.png)
###### Large OBJ file:
![Dragon](output/dragon_scene.png)

1
external/nanogui vendored

Submodule external/nanogui deleted from 16bc6b1d3a

494
external/teapot-low.obj vendored Normal file
View File

@@ -0,0 +1,494 @@
#
# object Teapot001
#
v 7.0000 0.0000 12.0000
v 4.9700 -4.9700 12.0000
v 4.9811 -4.9811 12.4922
v 7.0156 0.0000 12.4922
v 5.3250 -5.3250 12.0000
v 7.5000 0.0000 12.0000
v 0.0000 -7.0000 12.0000
v 0.0000 -7.0156 12.4922
v 0.0000 -7.5000 12.0000
v -5.1387 -4.9700 12.0000
v -5.0022 -4.9811 12.4922
v -5.3250 -5.3250 12.0000
v -7.0000 0.0000 12.0000
v -7.0156 0.0000 12.4922
v -7.5000 0.0000 12.0000
v -4.9700 4.9700 12.0000
v -4.9811 4.9811 12.4922
v -5.3250 5.3250 12.0000
v 0.0000 7.0000 12.0000
v 0.0000 7.0156 12.4922
v 0.0000 7.5000 12.0000
v 4.9700 4.9700 12.0000
v 4.9811 4.9811 12.4922
v 5.3250 5.3250 12.0000
v 6.5453 -6.5453 8.1094
v 9.2188 0.0000 8.1094
v 7.1000 -7.1000 4.5000
v 10.0000 0.0000 4.5000
v 0.0000 -9.2188 8.1094
v 0.0000 -10.0000 4.5000
v -6.5453 -6.5453 8.1094
v -7.1000 -7.1000 4.5000
v -9.2188 0.0000 8.1094
v -10.0000 0.0000 4.5000
v -6.5453 6.5453 8.1094
v -7.1000 7.1000 4.5000
v 0.0000 9.2188 8.1094
v 0.0000 10.0000 4.5000
v 6.5453 6.5453 8.1094
v 7.1000 7.1000 4.5000
v 6.2125 -6.2125 1.9219
v 8.7500 0.0000 1.9219
v 5.3250 -5.3250 0.7500
v 7.5000 0.0000 0.7500
v 0.0000 -8.7500 1.9219
v 0.0000 -7.5000 0.7500
v -6.2125 -6.2125 1.9219
v -5.3250 -5.3250 0.7500
v -8.7500 0.0000 1.9219
v -7.5000 0.0000 0.7500
v -6.2125 6.2125 1.9219
v -5.3250 5.3250 0.7500
v 0.0000 8.7500 1.9219
v 0.0000 7.5000 0.7500
v 6.2125 6.2125 1.9219
v 5.3250 5.3250 0.7500
v 4.5595 -4.5595 0.2344
v 6.4219 0.0000 0.2344
v 0.0000 0.0000 0.0000
v 0.0000 -6.4219 0.2344
v -4.5595 -4.5595 0.2344
v -6.4219 0.0000 0.2344
v -4.5595 4.5595 0.2344
v 0.0000 6.4219 0.2344
v 4.5595 4.5595 0.2344
v -8.0000 0.0000 10.1250
v -7.7500 -1.1250 10.6875
v -12.5938 -1.1250 10.4766
v -12.0625 0.0000 9.9844
v -14.2500 -1.1250 9.0000
v -13.5000 0.0000 9.0000
v -7.5000 0.0000 11.2500
v -13.1250 0.0000 10.9688
v -15.0000 0.0000 9.0000
v -7.7500 1.1250 10.6875
v -12.5938 1.1250 10.4766
v -14.2500 1.1250 9.0000
v -13.1719 -1.1250 6.2695
v -12.6875 0.0000 6.7500
v -9.7500 -1.1250 3.7500
v -13.6563 0.0000 5.7891
v -9.5000 0.0000 3.0000
v -13.1719 1.1250 6.2695
v -9.7500 1.1250 3.7500
v 8.5000 0.0000 7.1250
v 8.5000 -2.4750 5.0625
v 12.6875 -1.7062 8.1094
v 11.9375 0.0000 9.0000
v 15.0000 -0.9375 12.0000
v 13.5000 0.0000 12.0000
v 8.5000 0.0000 3.0000
v 13.4375 0.0000 7.2187
v 16.5000 0.0000 12.0000
v 8.5000 2.4750 5.0625
v 12.6875 1.7062 8.1094
v 15.0000 0.9375 12.0000
v 15.6328 -0.7500 12.3340
v 14.1250 0.0000 12.2813
v 15.0000 -0.5625 12.0000
v 14.0000 0.0000 12.0000
v 17.1406 0.0000 12.3867
v 16.0000 0.0000 12.0000
v 15.6328 0.7500 12.3340
v 15.0000 0.5625 12.0000
v 1.1552 -1.1552 14.9063
v 1.6250 0.0000 14.9063
v 0.0000 0.0000 15.7500
v 0.7100 -0.7100 13.5000
v 1.0000 0.0000 13.5000
v 0.0000 -1.6250 14.9063
v 0.0000 -1.0000 13.5000
v -1.1552 -1.1552 14.9063
v -0.7100 -0.7100 13.5000
v -1.6250 0.0000 14.9063
v -1.0000 0.0000 13.5000
v -1.1552 1.1552 14.9063
v -0.7100 0.7100 13.5000
v 0.0000 1.6250 14.9063
v 0.0000 1.0000 13.5000
v 1.1552 1.1552 14.9063
v 0.7100 0.7100 13.5000
v 2.9288 -2.9288 12.7500
v 4.1250 0.0000 12.7500
v 4.6150 -4.6150 12.0000
v 6.5000 0.0000 12.0000
v 0.0000 -4.1250 12.7500
v 0.0000 -6.5000 12.0000
v -2.9288 -2.9288 12.7500
v -4.6150 -4.6150 12.0000
v -4.1250 0.0000 12.7500
v -6.5000 0.0000 12.0000
v -2.9288 2.9288 12.7500
v -4.6150 4.6150 12.0000
v 0.0000 4.1250 12.7500
v 0.0000 6.5000 12.0000
v 2.9288 2.9288 12.7500
v 4.6150 4.6150 12.0000
# 137 vertices
vn -0.9995 -0.0000 0.0317
vn -0.7067 0.7067 0.0319
vn -0.0966 0.0966 0.9906
vn -0.1416 0.0000 0.9899
vn 0.5936 -0.5936 0.5435
vn 0.8400 0.0000 0.5425
vn -0.0010 0.9996 0.0283
vn -0.0008 0.1421 0.9899
vn 0.0000 -0.8400 0.5425
vn 0.7268 0.6636 -0.1773
vn 0.0816 0.2165 0.9729
vn -0.5949 -0.5971 0.5381
vn 0.9994 -0.0148 0.0317
vn 0.1496 -0.0134 0.9886
vn -0.8403 0.0004 0.5422
vn 0.7067 -0.7067 0.0319
vn 0.0966 -0.0966 0.9906
vn -0.5936 0.5936 0.5435
vn 0.0000 -0.9995 0.0317
vn -0.0000 -0.1416 0.9899
vn -0.0000 0.8400 0.5425
vn -0.7067 -0.7067 0.0319
vn -0.0966 -0.0966 0.9906
vn 0.5936 0.5936 0.5435
vn 0.6738 -0.6738 0.3034
vn 0.9532 -0.0000 0.3025
vn 0.7028 -0.7028 -0.1107
vn 0.9939 -0.0000 -0.1105
vn -0.0000 -0.9532 0.3025
vn -0.0000 -0.9939 -0.1105
vn -0.6738 -0.6738 0.3034
vn -0.7028 -0.7028 -0.1107
vn -0.9532 0.0000 0.3025
vn -0.9939 0.0000 -0.1105
vn -0.6738 0.6738 0.3034
vn -0.7028 0.7028 -0.1107
vn 0.0000 0.9532 0.3025
vn 0.0000 0.9939 -0.1105
vn 0.6738 0.6738 0.3034
vn 0.7028 0.7028 -0.1107
vn 0.5792 -0.5792 -0.5735
vn 0.8198 0.0000 -0.5726
vn 0.4157 -0.4157 -0.8089
vn 0.5888 -0.0000 -0.8083
vn 0.0000 -0.8198 -0.5726
vn -0.0000 -0.5888 -0.8083
vn -0.5792 -0.5792 -0.5735
vn -0.4157 -0.4157 -0.8089
vn -0.8198 -0.0000 -0.5726
vn -0.5888 0.0000 -0.8083
vn -0.5792 0.5792 -0.5735
vn -0.4157 0.4157 -0.8089
vn -0.0000 0.8198 -0.5726
vn 0.0000 0.5888 -0.8083
vn 0.5792 0.5792 -0.5735
vn 0.4157 0.4157 -0.8089
vn 0.2016 -0.2016 -0.9585
vn 0.2850 -0.0000 -0.9585
vn 0.0000 -0.0000 -1.0000
vn -0.0000 -0.2850 -0.9585
vn -0.2016 -0.2016 -0.9585
vn -0.2850 0.0000 -0.9585
vn -0.2016 0.2016 -0.9585
vn 0.0000 0.2850 -0.9585
vn 0.2016 0.2016 -0.9585
vn 0.0384 0.0031 -0.9993
vn -0.0182 -0.9619 0.2727
vn -0.0190 -0.9786 0.2047
vn 0.2817 0.0145 -0.9594
vn -0.2938 -0.9475 0.1264
vn 0.9324 0.0422 -0.3590
vn -0.0473 -0.0015 0.9989
vn -0.4420 -0.0127 0.8969
vn -0.9859 -0.0106 0.1669
vn -0.0177 0.9631 0.2685
vn -0.0097 0.9839 0.1786
vn -0.2735 0.9565 0.1013
vn -0.1217 -0.9875 -0.0998
vn 0.8176 0.0138 0.5756
vn -0.3352 -0.7946 -0.5061
vn 0.6216 0.0294 0.7828
vn -0.7747 -0.0079 -0.6322
vn -0.5711 -0.0076 -0.8208
vn -0.1055 0.9904 -0.0889
vn -0.3009 0.8200 -0.4869
vn -0.4862 0.0074 0.8738
vn 0.3271 -0.9145 -0.2382
vn 0.1595 -0.9869 0.0246
vn -0.6970 -0.0236 0.7167
vn -0.0062 -0.9245 0.3812
vn -0.7234 -0.0562 0.6881
vn 0.6538 0.0025 -0.7567
vn 0.7677 0.0173 -0.6406
vn 0.6465 0.0447 -0.7616
vn 0.3456 0.9087 -0.2343
vn 0.1845 0.9828 0.0081
vn 0.0506 0.9476 0.3154
vn 0.2319 -0.5821 0.7793
vn 0.0415 -0.0704 0.9967
vn 0.3158 0.9477 -0.0454
vn 0.9011 -0.0135 -0.4334
vn 0.9533 0.0371 0.2997
vn -0.3219 0.0032 0.9468
vn 0.3655 0.5783 0.7294
vn 0.3394 -0.9333 -0.1174
vn 0.6774 -0.6773 0.2871
vn 0.9576 -0.0001 0.2882
vn 0.0000 0.0000 1.0000
vn 0.5955 -0.5952 0.5396
vn 0.8436 -0.0002 0.5370
vn -0.0001 -0.9576 0.2882
vn -0.0002 -0.8436 0.5370
vn -0.6773 -0.6774 0.2871
vn -0.5952 -0.5955 0.5396
vn -0.9576 0.0001 0.2882
vn -0.8436 0.0002 0.5370
vn -0.6774 0.6773 0.2871
vn -0.5955 0.5952 0.5396
vn 0.0001 0.9576 0.2882
vn 0.0002 0.8436 0.5370
vn 0.6773 0.6774 0.2871
vn 0.5952 0.5955 0.5396
vn 0.1942 -0.1942 0.9616
vn 0.2754 0.0000 0.9613
vn 0.2121 -0.2121 0.9539
vn 0.3011 0.0000 0.9536
vn 0.0000 -0.2754 0.9613
vn 0.0000 -0.3011 0.9536
vn -0.1942 -0.1942 0.9616
vn -0.2121 -0.2121 0.9539
vn -0.2754 -0.0000 0.9613
vn -0.3011 -0.0000 0.9536
vn -0.1942 0.1942 0.9616
vn -0.2121 0.2121 0.9539
vn -0.0000 0.2754 0.9613
vn -0.0000 0.3011 0.9536
vn 0.1942 0.1942 0.9616
vn 0.2121 0.2121 0.9539
# 138 vertex normals
vt 2.0000 2.0000 0.0000
vt 1.5000 2.0000 0.0000
vt 1.5000 1.9500 0.0000
vt 2.0000 1.9500 0.0000
vt 1.5000 1.9000 0.0000
vt 2.0000 1.9000 0.0000
vt 1.0000 2.0000 0.0000
vt 1.0000 1.9500 0.0000
vt 1.0000 1.9000 0.0000
vt 0.5000 2.0000 0.0000
vt 0.5000 1.9500 0.0000
vt 0.5000 1.9000 0.0000
vt 0.0000 2.0000 0.0000
vt 0.0000 1.9500 0.0000
vt 0.0000 1.9000 0.0000
vt 1.5000 1.4500 0.0000
vt 2.0000 1.4500 0.0000
vt 1.5000 1.0000 0.0000
vt 2.0000 1.0000 0.0000
vt 1.0000 1.4500 0.0000
vt 1.0000 1.0000 0.0000
vt 0.5000 1.4500 0.0000
vt 0.5000 1.0000 0.0000
vt 0.0000 1.4500 0.0000
vt 0.0000 1.0000 0.0000
vt 1.5000 0.7000 0.0000
vt 2.0000 0.7000 0.0000
vt 1.5000 0.4000 0.0000
vt 2.0000 0.4000 0.0000
vt 1.0000 0.7000 0.0000
vt 1.0000 0.4000 0.0000
vt 0.5000 0.7000 0.0000
vt 0.5000 0.4000 0.0000
vt 0.0000 0.7000 0.0000
vt 0.0000 0.4000 0.0000
vt 1.5000 0.2000 0.0000
vt 2.0000 0.2000 0.0000
vt 1.5000 0.0000 0.0000
vt 1.0000 0.2000 0.0000
vt 1.0000 0.0000 0.0000
vt 0.5000 0.2000 0.0000
vt 0.5000 0.0000 0.0000
vt 0.0000 0.2000 0.0000
vt 0.0000 0.0000 0.0000
vt 0.7500 1.0000 0.0000
vt 0.7500 0.7500 0.0000
vt 1.0000 0.7500 0.0000
vt 0.7500 0.5000 0.0000
vt 1.0000 0.5000 0.0000
vt 0.5000 0.7500 0.0000
vt 0.5000 0.5000 0.0000
vt 0.2500 1.0000 0.0000
vt 0.2500 0.7500 0.0000
vt 0.2500 0.5000 0.0000
vt 0.0000 0.7500 0.0000
vt 0.0000 0.5000 0.0000
vt 0.7500 0.2500 0.0000
vt 1.0000 0.2500 0.0000
vt 0.7500 0.0000 0.0000
vt 0.5000 0.2500 0.0000
vt 0.2500 0.2500 0.0000
vt 0.2500 0.0000 0.0000
vt 0.0000 0.2500 0.0000
vt 0.7500 0.4500 0.0000
vt 0.5000 0.4500 0.0000
vt 0.7500 0.9000 0.0000
vt 0.5000 0.9000 0.0000
vt 1.0000 0.4500 0.0000
vt 1.0000 0.9000 0.0000
vt 0.2500 0.4500 0.0000
vt 0.0000 0.4500 0.0000
vt 0.2500 0.9000 0.0000
vt 0.0000 0.9000 0.0000
vt 0.7500 0.9500 0.0000
vt 0.5000 0.9500 0.0000
vt 1.0000 0.9500 0.0000
vt 0.2500 0.9500 0.0000
vt 0.0000 0.9500 0.0000
# 78 texture coords
g Teapot001
f 1/1/1 2/2/2 3/3/3 4/4/4
f 4/4/4 3/3/3 5/5/5 6/6/6
f 2/2/2 7/7/7 8/8/8 3/3/3
f 3/3/3 8/8/8 9/9/9 5/5/5
f 7/7/7 10/10/10 11/11/11 8/8/8
f 8/8/8 11/11/11 12/12/12 9/9/9
f 10/10/10 13/13/13 14/14/14 11/11/11
f 11/11/11 14/14/14 15/15/15 12/12/12
f 13/1/13 16/2/16 17/3/17 14/4/14
f 14/4/14 17/3/17 18/5/18 15/6/15
f 16/2/16 19/7/19 20/8/20 17/3/17
f 17/3/17 20/8/20 21/9/21 18/5/18
f 19/7/19 22/10/22 23/11/23 20/8/20
f 20/8/20 23/11/23 24/12/24 21/9/21
f 22/10/22 1/13/1 4/14/4 23/11/23
f 23/11/23 4/14/4 6/15/6 24/12/24
f 6/6/6 5/5/5 25/16/25 26/17/26
f 26/17/26 25/16/25 27/18/27 28/19/28
f 5/5/5 9/9/9 29/20/29 25/16/25
f 25/16/25 29/20/29 30/21/30 27/18/27
f 9/9/9 12/12/12 31/22/31 29/20/29
f 29/20/29 31/22/31 32/23/32 30/21/30
f 12/12/12 15/15/15 33/24/33 31/22/31
f 31/22/31 33/24/33 34/25/34 32/23/32
f 15/6/15 18/5/18 35/16/35 33/17/33
f 33/17/33 35/16/35 36/18/36 34/19/34
f 18/5/18 21/9/21 37/20/37 35/16/35
f 35/16/35 37/20/37 38/21/38 36/18/36
f 21/9/21 24/12/24 39/22/39 37/20/37
f 37/20/37 39/22/39 40/23/40 38/21/38
f 24/12/24 6/15/6 26/24/26 39/22/39
f 39/22/39 26/24/26 28/25/28 40/23/40
f 28/19/28 27/18/27 41/26/41 42/27/42
f 42/27/42 41/26/41 43/28/43 44/29/44
f 27/18/27 30/21/30 45/30/45 41/26/41
f 41/26/41 45/30/45 46/31/46 43/28/43
f 30/21/30 32/23/32 47/32/47 45/30/45
f 45/30/45 47/32/47 48/33/48 46/31/46
f 32/23/32 34/25/34 49/34/49 47/32/47
f 47/32/47 49/34/49 50/35/50 48/33/48
f 34/19/34 36/18/36 51/26/51 49/27/49
f 49/27/49 51/26/51 52/28/52 50/29/50
f 36/18/36 38/21/38 53/30/53 51/26/51
f 51/26/51 53/30/53 54/31/54 52/28/52
f 38/21/38 40/23/40 55/32/55 53/30/53
f 53/30/53 55/32/55 56/33/56 54/31/54
f 40/23/40 28/25/28 42/34/42 55/32/55
f 55/32/55 42/34/42 44/35/44 56/33/56
f 44/29/44 43/28/43 57/36/57 58/37/58
f 58/37/58 57/36/57 59/38/59
f 43/28/43 46/31/46 60/39/60 57/36/57
f 57/36/57 60/39/60 59/40/59
f 46/31/46 48/33/48 61/41/61 60/39/60
f 60/39/60 61/41/61 59/42/59
f 48/33/48 50/35/50 62/43/62 61/41/61
f 61/41/61 62/43/62 59/44/59
f 50/29/50 52/28/52 63/36/63 62/37/62
f 62/37/62 63/36/63 59/38/59
f 52/28/52 54/31/54 64/39/64 63/36/63
f 63/36/63 64/39/64 59/40/59
f 54/31/54 56/33/56 65/41/65 64/39/64
f 64/39/64 65/41/65 59/42/59
f 56/33/56 44/35/44 58/43/58 65/41/65
f 65/41/65 58/43/58 59/44/59
f 66/21/66 67/45/67 68/46/68 69/47/69
f 69/47/69 68/46/68 70/48/70 71/49/71
f 67/45/67 72/23/72 73/50/73 68/46/68
f 68/46/68 73/50/73 74/51/74 70/48/70
f 72/23/72 75/52/75 76/53/76 73/50/73
f 73/50/73 76/53/76 77/54/77 74/51/74
f 75/52/75 66/25/66 69/55/69 76/53/76
f 76/53/76 69/55/69 71/56/71 77/54/77
f 71/49/71 70/48/70 78/57/78 79/58/79
f 79/58/79 78/57/78 80/59/80 34/40/81
f 70/48/70 74/51/74 81/60/82 78/57/78
f 78/57/78 81/60/82 82/42/83 80/59/80
f 74/51/74 77/54/77 83/61/84 81/60/82
f 81/60/82 83/61/84 84/62/85 82/42/83
f 77/54/77 71/56/71 79/63/79 83/61/84
f 83/61/84 79/63/79 34/44/81 84/62/85
f 85/42/86 86/59/87 87/64/88 88/65/89
f 88/65/89 87/64/88 89/66/90 90/67/91
f 86/59/87 91/40/92 92/68/93 87/64/88
f 87/64/88 92/68/93 93/69/94 89/66/90
f 91/44/92 94/62/95 95/70/96 92/71/93
f 92/71/93 95/70/96 96/72/97 93/73/94
f 94/62/95 85/42/86 88/65/89 95/70/96
f 95/70/96 88/65/89 90/67/91 96/72/97
f 90/67/91 89/66/90 97/74/98 98/75/99
f 98/75/99 97/74/98 99/45/100 100/23/101
f 89/66/90 93/69/94 101/76/102 97/74/98
f 97/74/98 101/76/102 102/21/103 99/45/100
f 93/73/94 96/72/97 103/77/104 101/78/102
f 101/78/102 103/77/104 104/52/105 102/25/103
f 96/72/97 90/67/91 98/75/99 103/77/104
f 103/77/104 98/75/99 100/23/101 104/52/105
f 105/48/106 106/49/107 107/21/108
f 106/49/107 105/48/106 108/59/109 109/40/110
f 110/51/111 105/48/106 107/45/108
f 105/48/106 110/51/111 111/42/112 108/59/109
f 112/54/113 110/51/111 107/23/108
f 110/51/111 112/54/113 113/62/114 111/42/112
f 114/56/115 112/54/113 107/52/108
f 112/54/113 114/56/115 115/44/116 113/62/114
f 116/48/117 114/49/115 107/21/108
f 114/49/115 116/48/117 117/59/118 115/40/116
f 118/51/119 116/48/117 107/45/108
f 116/48/117 118/51/119 119/42/120 117/59/118
f 120/54/121 118/51/119 107/23/108
f 118/51/119 120/54/121 121/62/122 119/42/120
f 106/56/107 120/54/121 107/52/108
f 120/54/121 106/56/107 109/44/110 121/62/122
f 109/21/110 108/45/109 122/48/123 123/49/124
f 123/49/124 122/48/123 124/59/125 125/40/126
f 108/45/109 111/23/112 126/51/127 122/48/123
f 122/48/123 126/51/127 127/42/128 124/59/125
f 111/23/112 113/52/114 128/54/129 126/51/127
f 126/51/127 128/54/129 129/62/130 127/42/128
f 113/52/114 115/25/116 130/56/131 128/54/129
f 128/54/129 130/56/131 131/44/132 129/62/130
f 115/21/116 117/45/118 132/48/133 130/49/131
f 130/49/131 132/48/133 133/59/134 131/40/132
f 117/45/118 119/23/120 134/51/135 132/48/133
f 132/48/133 134/51/135 135/42/136 133/59/134
f 119/23/120 121/52/122 136/54/137 134/51/135
f 134/51/135 136/54/137 137/62/138 135/42/136
f 121/52/122 109/25/110 123/56/124 136/54/137
f 136/54/137 123/56/124 125/44/126 137/62/138
# 112 polygons - 16 triangles

274
external/teapot-lowtri.obj vendored Normal file
View File

@@ -0,0 +1,274 @@
#
# object Teapot001
#
v 7.0000 0.0000 12.0000
v 4.9700 -4.9700 12.0000
v 4.9811 -4.9811 12.4922
v 7.0156 0.0000 12.4922
v 5.3250 -5.3250 12.0000
v 7.5000 0.0000 12.0000
v 0.0000 -7.0000 12.0000
v 0.0000 -7.0156 12.4922
v 0.0000 -7.5000 12.0000
v -5.1387 -4.9700 12.0000
v -5.0022 -4.9811 12.4922
v -5.3250 -5.3250 12.0000
v -7.0000 0.0000 12.0000
v -7.0156 0.0000 12.4922
v -7.5000 0.0000 12.0000
v -4.9700 4.9700 12.0000
v -4.9811 4.9811 12.4922
v -5.3250 5.3250 12.0000
v 0.0000 7.0000 12.0000
v 0.0000 7.0156 12.4922
v 0.0000 7.5000 12.0000
v 4.9700 4.9700 12.0000
v 4.9811 4.9811 12.4922
v 5.3250 5.3250 12.0000
v 6.5453 -6.5453 8.1094
v 9.2188 0.0000 8.1094
v 7.1000 -7.1000 4.5000
v 10.0000 0.0000 4.5000
v 0.0000 -9.2188 8.1094
v 0.0000 -10.0000 4.5000
v -6.5453 -6.5453 8.1094
v -7.1000 -7.1000 4.5000
v -9.2188 0.0000 8.1094
v -10.0000 0.0000 4.5000
v -6.5453 6.5453 8.1094
v -7.1000 7.1000 4.5000
v 0.0000 9.2188 8.1094
v 0.0000 10.0000 4.5000
v 6.5453 6.5453 8.1094
v 7.1000 7.1000 4.5000
v 6.2125 -6.2125 1.9219
v 8.7500 0.0000 1.9219
v 5.3250 -5.3250 0.7500
v 7.5000 0.0000 0.7500
v 0.0000 -8.7500 1.9219
v 0.0000 -7.5000 0.7500
v -6.2125 -6.2125 1.9219
v -5.3250 -5.3250 0.7500
v -8.7500 0.0000 1.9219
v -7.5000 0.0000 0.7500
v -6.2125 6.2125 1.9219
v -5.3250 5.3250 0.7500
v 0.0000 8.7500 1.9219
v 0.0000 7.5000 0.7500
v 6.2125 6.2125 1.9219
v 5.3250 5.3250 0.7500
v 4.5595 -4.5595 0.2344
v 6.4219 0.0000 0.2344
v 0.0000 0.0000 0.0000
v 0.0000 -6.4219 0.2344
v -4.5595 -4.5595 0.2344
v -6.4219 0.0000 0.2344
v -4.5595 4.5595 0.2344
v 0.0000 6.4219 0.2344
v 4.5595 4.5595 0.2344
v -8.0000 0.0000 10.1250
v -7.7500 -1.1250 10.6875
v -12.5938 -1.1250 10.4766
v -12.0625 0.0000 9.9844
v -14.2500 -1.1250 9.0000
v -13.5000 0.0000 9.0000
v -7.5000 0.0000 11.2500
v -13.1250 0.0000 10.9688
v -15.0000 0.0000 9.0000
v -7.7500 1.1250 10.6875
v -12.5938 1.1250 10.4766
v -14.2500 1.1250 9.0000
v -13.1719 -1.1250 6.2695
v -12.6875 0.0000 6.7500
v -9.7500 -1.1250 3.7500
v -13.6563 0.0000 5.7891
v -9.5000 0.0000 3.0000
v -13.1719 1.1250 6.2695
v -9.7500 1.1250 3.7500
v 8.5000 0.0000 7.1250
v 8.5000 -2.4750 5.0625
v 12.6875 -1.7062 8.1094
v 11.9375 0.0000 9.0000
v 15.0000 -0.9375 12.0000
v 13.5000 0.0000 12.0000
v 8.5000 0.0000 3.0000
v 13.4375 0.0000 7.2187
v 16.5000 0.0000 12.0000
v 8.5000 2.4750 5.0625
v 12.6875 1.7062 8.1094
v 15.0000 0.9375 12.0000
v 15.6328 -0.7500 12.3340
v 14.1250 0.0000 12.2813
v 15.0000 -0.5625 12.0000
v 14.0000 0.0000 12.0000
v 17.1406 0.0000 12.3867
v 16.0000 0.0000 12.0000
v 15.6328 0.7500 12.3340
v 15.0000 0.5625 12.0000
v 1.1552 -1.1552 14.9063
v 1.6250 0.0000 14.9063
v 0.0000 0.0000 15.7500
v 0.7100 -0.7100 13.5000
v 1.0000 0.0000 13.5000
v 0.0000 -1.6250 14.9063
v 0.0000 -1.0000 13.5000
v -1.1552 -1.1552 14.9063
v -0.7100 -0.7100 13.5000
v -1.6250 0.0000 14.9063
v -1.0000 0.0000 13.5000
v -1.1552 1.1552 14.9063
v -0.7100 0.7100 13.5000
v 0.0000 1.6250 14.9063
v 0.0000 1.0000 13.5000
v 1.1552 1.1552 14.9063
v 0.7100 0.7100 13.5000
v 2.9288 -2.9288 12.7500
v 4.1250 0.0000 12.7500
v 4.6150 -4.6150 12.0000
v 6.5000 0.0000 12.0000
v 0.0000 -4.1250 12.7500
v 0.0000 -6.5000 12.0000
v -2.9288 -2.9288 12.7500
v -4.6150 -4.6150 12.0000
v -4.1250 0.0000 12.7500
v -6.5000 0.0000 12.0000
v -2.9288 2.9288 12.7500
v -4.6150 4.6150 12.0000
v 0.0000 4.1250 12.7500
v 0.0000 6.5000 12.0000
v 2.9288 2.9288 12.7500
v 4.6150 4.6150 12.0000
# 137 vertices
g Teapot001
f 1 2 3 4
f 4 3 5 6
f 2 7 8 3
f 3 8 9 5
f 7 10 11 8
f 8 11 12 9
f 10 13 14 11
f 11 14 15 12
f 13 16 17 14
f 14 17 18 15
f 16 19 20 17
f 17 20 21 18
f 19 22 23 20
f 20 23 24 21
f 22 1 4 23
f 23 4 6 24
f 6 5 25 26
f 26 25 27 28
f 5 9 29 25
f 25 29 30 27
f 9 12 31 29
f 29 31 32 30
f 12 15 33 31
f 31 33 34 32
f 15 18 35 33
f 33 35 36 34
f 18 21 37 35
f 35 37 38 36
f 21 24 39 37
f 37 39 40 38
f 24 6 26 39
f 39 26 28 40
f 28 27 41 42
f 42 41 43 44
f 27 30 45 41
f 41 45 46 43
f 30 32 47 45
f 45 47 48 46
f 32 34 49 47
f 47 49 50 48
f 34 36 51 49
f 49 51 52 50
f 36 38 53 51
f 51 53 54 52
f 38 40 55 53
f 53 55 56 54
f 40 28 42 55
f 55 42 44 56
f 44 43 57 58
f 58 57 59
f 43 46 60 57
f 57 60 59
f 46 48 61 60
f 60 61 59
f 48 50 62 61
f 61 62 59
f 50 52 63 62
f 62 63 59
f 52 54 64 63
f 63 64 59
f 54 56 65 64
f 64 65 59
f 56 44 58 65
f 65 58 59
f 66 67 68 69
f 69 68 70 71
f 67 72 73 68
f 68 73 74 70
f 72 75 76 73
f 73 76 77 74
f 75 66 69 76
f 76 69 71 77
f 71 70 78 79
f 79 78 80 34
f 70 74 81 78
f 78 81 82 80
f 74 77 83 81
f 81 83 84 82
f 77 71 79 83
f 83 79 34 84
f 85 86 87 88
f 88 87 89 90
f 86 91 92 87
f 87 92 93 89
f 91 94 95 92
f 92 95 96 93
f 94 85 88 95
f 95 88 90 96
f 90 89 97 98
f 98 97 99 100
f 89 93 101 97
f 97 101 102 99
f 93 96 103 101
f 101 103 104 102
f 96 90 98 103
f 103 98 100 104
f 105 106 107
f 106 105 108 109
f 110 105 107
f 105 110 111 108
f 112 110 107
f 110 112 113 111
f 114 112 107
f 112 114 115 113
f 116 114 107
f 114 116 117 115
f 118 116 107
f 116 118 119 117
f 120 118 107
f 118 120 121 119
f 106 120 107
f 120 106 109 121
f 109 108 122 123
f 123 122 124 125
f 108 111 126 122
f 122 126 127 124
f 111 113 128 126
f 126 128 129 127
f 113 115 130 128
f 128 130 131 129
f 115 117 132 130
f 130 132 133 131
f 117 119 134 132
f 132 134 135 133
f 119 121 136 134
f 134 136 137 135
f 121 109 123 136
f 136 123 125 137
# 112 polygons - 16 triangles

11284
external/teapot.obj vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
output/arealight_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
output/ch11_reflection.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 KiB

BIN
output/ch11_refraction.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

BIN
output/ch11_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
output/ch12_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
output/ch13_cone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
output/ch13_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
output/ch14_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
output/ch16_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
output/christmasball.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 KiB

BIN
output/dragon_scene.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
output/uvmap_earth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
output/uvmap_skybox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

685
samplescenes/orrery.yml Normal file
View File

@@ -0,0 +1,685 @@
# ======================================================
# orrery.yml
#
# This file describes the title image for the "Texture
# Mapping" bonus chapter at:
#
# http://www.raytracerchallenge.com/bonus/texture-mapping.html
#
# It requires several additional resources, provided as a
# separate download. The resources were found on the following
# sites:
#
# * https://www.bittbox.com/freebies/free-hi-resolution-wood-textures
# : the wooden texture for the table
# * https://astrogeology.usgs.gov/search/map/Mercury/Messenger/Global/Mercury_MESSENGER_MDIS_Basemap_LOI_Mosaic_Global_166m
# : the map of Mercury
# * http://planetpixelemporium.com/planets.html
# : maps of Earth, Mars, Jupiter, Saturn, Uranus, and Neptune
# * https://hdrihaven.com/hdri/?c=indoor&h=artist_workshop
# : the "artist workshop" environment map
#
# by Jamis Buck <jamis@jamisbuck.org>
# ======================================================
- add: camera
width: 800
height: 400
field-of-view: 1.2
from: [ 2, 4, -10]
to: [ -1, -1, 0 ]
up: [ 0, 1, 0 ]
# The scene as shown in the bonus chapter is rendered using an area light,
# precisely as described in the "Rendering soft shadows" bonus chapter,
# here: http://www.raytracerchallenge.com/bonus/area-light.html
#
# if you haven't implemented area lights, you can replace this with a point
# light located at [0, 2.5, -10].
- add: light
corner: [-5, 0, -10 ]
uvec: [ 10, 0, 0 ]
vvec: [ 0, 5, 0 ]
usteps: 10
vsteps: 5
jitter: true
intensity: [ 1, 1, 1 ]
# -------------------------------------------
# some common textures
# -------------------------------------------
- define: GOLD
value:
color: [ 1, 0.8, 0.1 ]
ambient: 0.1
diffuse: 0.6
specular: 0.3
shininess: 15
- define: SILVER
value:
color: [ 1, 1, 1 ]
ambient: 0.1
diffuse: 0.7
specular: 0.3
shininess: 15
# -----------------------------------------------
# CSG definition for the gears used to construct
# the orrery.
#
# NOTCH is a helper object used to create the
# teeth for the gears.
#
# GEAR is the actual gear object itself.
# -----------------------------------------------
- define: NOTCH
value:
add: csg
operation: difference
left:
type: cube
transform:
- [ scale, 1, 0.25, 1 ]
- [ translate, 1, 0, 1 ]
- [ rotate-y, 0.7854 ]
- [ scale, 1, 1, 0.1 ]
right:
type: cylinder
min: -0.26
max: 0.26
closed: true
transform:
- [ scale, 0.8, 1, 0.8 ]
- define: GEAR
value:
add: csg
operation: difference
left:
type: cylinder
min: -0.025
max: 0.025
closed: true
right:
type: group
children:
# center hole
- add: cylinder
min: -0.06
max: 0.06
closed: true
transform:
- [ scale, 0.1, 1, 0.1 ]
# crescents
- add: csg
operation: difference
left:
type: cylinder
min: -0.06
max: 0.06
closed: true
transform:
- [ scale, 0.7, 1, 0.7 ]
right:
type: cube
transform:
- [ scale, 1, 0.1, 0.2 ]
# teeth
- add: NOTCH
- add: NOTCH
transform:
- [ rotate-y, 0.31415 ]
- add: NOTCH
transform:
- [ rotate-y, 0.6283 ]
- add: NOTCH
transform:
- [ rotate-y, 0.94245 ]
- add: NOTCH
transform:
- [ rotate-y, 1.2566 ]
- add: NOTCH
transform:
- [ rotate-y, 1.57075 ]
- add: NOTCH
transform:
- [ rotate-y, 1.8849 ]
- add: NOTCH
transform:
- [ rotate-y, 2.19905 ]
- add: NOTCH
transform:
- [ rotate-y, 2.5132 ]
- add: NOTCH
transform:
- [ rotate-y, 2.82735 ]
- add: NOTCH
transform:
- [ rotate-y, 3.1415 ]
- add: NOTCH
transform:
- [ rotate-y, -0.31415 ]
- add: NOTCH
transform:
- [ rotate-y, -0.6283 ]
- add: NOTCH
transform:
- [ rotate-y, -0.94245 ]
- add: NOTCH
transform:
- [ rotate-y, -1.2566 ]
- add: NOTCH
transform:
- [ rotate-y, -1.57075 ]
- add: NOTCH
transform:
- [ rotate-y, -1.8849 ]
- add: NOTCH
transform:
- [ rotate-y, -2.19905 ]
- add: NOTCH
transform:
- [ rotate-y, -2.5132 ]
- add: NOTCH
transform:
- [ rotate-y, -2.82735 ]
# mechanism: top plate
- add: csg
operation: difference
material: GOLD
transform:
- [ rotate-y, -1 ]
left:
type: cylinder
min: -1.51
max: -1.5
closed: true
right:
type: group
children:
- add: cylinder
min: -1.52
max: -1.49
closed: true
transform:
- [ scale, 0.1, 1, 0.1 ]
- add: csg
operation: difference
left:
type: cylinder
min: -1.52
max: -1.49
closed: true
transform:
- [ scale, 0.75, 1, 0.75 ]
right:
type: cube
transform:
- [ scale, 1, 0.1, 0.2 ]
- [ translate, 0, -1.5, 0 ]
# mechanism: gear
- add: GEAR
material: SILVER
transform:
- [ scale, 0.5, 0.5, 0.5 ]
- [ translate, 0.4, -1.45, -0.4 ]
# mechanism: gear
- add: GEAR
material: SILVER
transform:
- [ rotate-y, 0.8 ]
- [ scale, 0.4, 0.4, 0.4 ]
- [ translate, -0.4, -1.45, 0.2 ]
# sun
- add: group
children:
- add: sphere
shadow: false
material:
color: [1, 1, 0]
ambient: 0.1
diffuse: 0.6
specular: 0 # count on the skybox reflection being the specular highlight
reflective: 0.2
- add: group
material: GOLD
children:
- add: cylinder
min: -4
max: -0.5
transform:
- [ scale, 0.025, 1, 0.025 ]
# base
- add: sphere
transform:
- [ translate, 0, -4, 0 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: checkers
width: 16
height: 8
colors:
- [ 0, 0, 0 ]
- [ 0.5, 0.5, 0.5 ]
diffuse: 0.6
specular: 0 # count on the skybox reflection being the specular highlight
ambient: 0.1
reflective: 0.2
# table
- add: cube
transform:
- [ scale, 5, 0.1, 5 ]
- [ translate, 0, -4, 0 ]
material:
diffuse: 0.9
ambient: 0.1
specular: 0
pattern:
type: map
mapping: planar
uv_pattern:
type: image
file: res/wood.ppm
transform:
- [ scale, 0.5, 0.5, 0.5 ]
# mechanism: gear-plate between top & mercury
- add: GEAR
material: SILVER
transform:
- [ rotate-y, -0.4 ]
- [ scale, 0.9, 0.9, 0.9 ]
- [ translate, 0, -1.75, 0 ]
# mercury
- add: group
transform:
- [ translate, 2, 0, 0 ]
- [ rotate-y, 0.7 ]
children:
- add: sphere
transform:
- [ scale, 0.25, 0.25, 0.25 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/mercury-small.ppm
- add: group
material: GOLD
children:
- add: cylinder
min: -2
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2, 0 ]
- add: cylinder
min: 0
max: 2
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2, 0 ]
# mechanism: gear-plate between mercury & venus
- add: GEAR
material: SILVER
transform:
- [ rotate-y, 1.3 ]
- [ translate, 0, -2.05, 0 ]
# venus
- add: group
transform:
- [ translate, 3, 0, 0 ]
- [ rotate-y, 0.3 ]
children:
- add: sphere
transform:
- [ scale, 0.25, 0.25, 0.25 ]
material:
color: [ 1, 1, 0.8 ]
- add: group
material: GOLD
children:
- add: cylinder
min: -2.1
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.1, 0 ]
- add: cylinder
min: 0
max: 3
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.1, 0 ]
# mechanism: gear-plate between venus & earth
- add: GEAR
material: SILVER
transform:
- [ scale, 0.9, 0.9, 0.9 ]
- [ rotate-y, -2.2 ]
- [ translate, 0, -2.15, 0 ]
# earth
- add: group
transform:
- [ translate, 4, 0, 0 ]
- [ rotate-y, 2 ]
children:
- add: sphere
transform:
- [ scale, 0.25, 0.25, 0.25 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/earthmap-small.ppm
- add: group
material: GOLD
children:
- add: cylinder
min: -2.2
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.2, 0 ]
- add: cylinder
min: 0
max: 4
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.2, 0 ]
# mechanism: gear-plate between earth & mars
- add: GEAR
material: SILVER
transform:
- [ scale, 0.8, 0.8, 0.8 ]
- [ rotate-y, 1.7 ]
- [ translate, 0, -2.25, 0 ]
# mars
- add: group
transform:
- [ translate, 5, 0, 0 ]
- [ rotate-y, -2 ]
children:
- add: sphere
transform:
- [ scale, 0.25, 0.25, 0.25 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/mars-small.ppm
- add: group
material: GOLD
children:
- add: cylinder
min: -2.3
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.3, 0 ]
- add: cylinder
min: 0
max: 5
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.3, 0 ]
# mechanism: gear-plate between mars & jupiter
- add: GEAR
material: SILVER
transform:
- [ rotate-y, -0.9 ]
- [ translate, 0, -2.35, 0 ]
# jupiter
- add: group
transform:
- [ translate, 6.5, 0, 0 ]
- [ rotate-y, -0.75 ]
children:
- add: sphere
transform:
- [ scale, 0.67, 0.67, 0.67 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/jupitermap-small.ppm
- add: group
material: GOLD
children:
- add: cylinder
min: -2.4
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.4, 0 ]
- add: cylinder
min: 0
max: 6.5
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.4, 0 ]
# mechanism: gear-plate between jupiter & saturn
- add: GEAR
material: SILVER
transform:
- [ scale, 0.95, 0.95, 0.95 ]
- [ rotate-y, -1.1 ]
- [ translate, 0, -2.45, 0 ]
# saturn
- add: group
transform:
- [ translate, 8, 0, 0 ]
- [ rotate-y, -2.5 ]
children:
- add: sphere
transform:
- [ scale, 0.5, 0.5, 0.5 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/saturnmap-small.ppm
# rings
- add: csg
operation: difference
transform:
- [ rotate-z, 0.2 ]
material:
pattern:
type: rings
colors:
- [ 1, 1, 0.5 ]
- [ 1, 1, 0 ]
transform:
- [ scale, 0.05, 1, 0.05 ]
left:
type: cylinder
min: -0.01
max: 0.01
closed: true
transform:
- [ scale, 1.2, 1, 1.2 ]
right:
type: cylinder
min: -0.02
max: 0.02
closed: true
transform:
- [ scale, 0.75, 1, 0.75 ]
- add: group
material: GOLD
children:
- add: cylinder
min: -2.5
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.5, 0 ]
- add: cylinder
min: 0
max: 8
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.5, 0 ]
# mechanism: gear-plate between saturn & uranus
- add: GEAR
material: SILVER
transform:
- [ scale, 0.9, 0.9, 0.9 ]
- [ rotate-y, 1 ]
- [ translate, 0, -2.55, 0 ]
# uranus
- add: group
transform:
- [ translate, 9, 0, 0 ]
- [ rotate-y, -3 ]
children:
- add: sphere
transform:
- [ scale, 0.4, 0.4, 0.4 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/uranusmap-small.ppm
- add: group
material: GOLD
children:
- add: cylinder
min: -2.6
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.6, 0 ]
- add: cylinder
min: 0
max: 9
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.6, 0 ]
# mechanism: gear-plate between uranus & neptune
- add: GEAR
material: SILVER
transform:
- [ rotate-y, -1 ]
- [ translate, 0, -2.65, 0 ]
# neptune
- add: group
transform:
- [ translate, 10, 0, 0 ]
- [ rotate-y, -1.25 ]
children:
- add: sphere
transform:
- [ scale, 0.4, 0.4, 0.4 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/neptunemap-small.ppm
- add: group
material: GOLD
children:
- add: cylinder
min: -2.7
max: 0
transform:
- [ scale, 0.025, 1, 0.025 ]
- add: sphere
transform:
- [ scale, 0.025, 0.025, 0.025 ]
- [ translate, 0, -2.7, 0 ]
- add: cylinder
min: 0
max: 10
transform:
- [ scale, 0.025, 1, 0.025 ]
- [ rotate-z, 1.5708 ]
- [ translate, 0, -2.7, 0 ]
# outer sphere as the surrounding environment
- add: sphere
transform:
- [ scale, 1000, 1000, 1000 ]
material:
pattern:
type: map
mapping: spherical
uv_pattern:
type: image
file: res/artist_workshop.ppm
transform:
- [ rotate-y, -2.7 ]
diffuse: 0
specular: 0
ambient: 1

View File

@@ -1,21 +1,42 @@
# To simplify testing, the app is build in two passes,
option(USE_OPENMP "Build using OpenMP" OFF)
if (USE_OPENMP)
find_package(OpenMP REQUIRED)
endif()
# First most is build as a library
add_library(rayonnement STATIC)
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/pattern/*.h)
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/pattern/*.h
${CMAKE_CURRENT_SOURCE_DIR}/uvpattern/*.h)
file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/worldoptimiser/*.cpp)
target_include_directories(rayonnement PUBLIC include pattern)
target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES})
target_link_libraries(rayonnement LodePNG)
# Second we build the main executable
add_executable(dorayme main.cpp)
target_include_directories(rayonnement PUBLIC include ${LODEPNG_INCLUDE_FOLDER})
target_link_libraries(dorayme rayonnement)
if (USE_LUA)
add_dependencies(rayonnement LuaCore)
target_link_libraries(rayonnement ${LUA_LIBRARIES})
target_include_directories(rayonnement PUBLIC ${LUA_INCLUDE_DIR})
endif()
target_include_directories(rayonnement PUBLIC include ${LODEPNG_INCLUDE_FOLDER} ${NANOJPEG_INCLUDE_FOLDER})
target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES})
target_link_libraries(rayonnement LodePNG NanoJPEG)
if (USE_OPENMP)
target_link_libraries(rayonnement OpenMP::OpenMP_CXX)
endif()
# The main executable
#add_executable(dorayme main.cpp)
#add_dependencies(dorayme LuaCore)
#target_link_libraries(dorayme rayonnement ${LUA_LIBRARIES})
if (COVERALLS)
set(COVERAGE_SRCS ${RAY_HEADERS} ${RAY_SOURCES} ${COVERAGE_SRCS} PARENT_SCOPE)

View File

@@ -12,10 +12,14 @@
#include <ray.h>
#include <camera.h>
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize), horizontalSize(hsize), fieldOfView(fov)
#include <stdio.h>
#include <renderstat.h>
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize),
horizontalSize(hsize), fieldOfView(fov), focalDistance(1), apertureSize(0), rayCount(1)
{
double aspectRatio = (double)hsize / (double)vsize;
double halfView = tan(fov / 2.0);
double halfView = tan(fov / 2.0) * this->focalDistance;
if (aspectRatio >= 1)
{
@@ -31,7 +35,6 @@ Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize)
this->pixelSize = (this->halfWidth * 2) / this->horizontalSize;
this->setTransform(Matrix4().identity());
}
void Camera::setTransform(Matrix transform)
@@ -40,7 +43,7 @@ void Camera::setTransform(Matrix transform)
this->inverseTransform = transform.inverse();
}
Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY, double horzOffset, double vertOffset)
{
double xOffset = ((double)pixelX + 0.5) * this->pixelSize;
double yOffset = ((double)pixelY + 0.5) * this->pixelSize;
@@ -48,27 +51,52 @@ Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
double worldX = this->halfWidth - xOffset;
double worldY = this->halfHeight - yOffset;
Tuple pixel = this->inverseTransform * Point(worldX, worldY, -1);
Tuple origin = this->inverseTransform * Point(0, 0, 0);
Tuple pixel = this->inverseTransform * Point(worldX, worldY, -this->focalDistance);
Tuple origin = this->inverseTransform * Point(horzOffset, vertOffset, 0);
Tuple direction = (pixel - origin).normalise();
stats.addCastedRay();
return Ray(origin, direction);
}
Canvas Camera::render(World world)
Canvas Camera::render(World world, uint32_t depth)
{
uint32_t x, y;
Canvas image = Canvas(this->horizontalSize, this->verticalSize);
for(y = 0; y < this->verticalSize; y++)
#pragma omp parallel default(shared) private(x, y) shared(image, stats)
{
for(x = 0; x < this->horizontalSize; x++)
#pragma omp for schedule(dynamic, 5)
for (y = 0 ; y < this->verticalSize ; y++)
{
Ray r = this->rayForPixel(x, y);
Tuple colour = world.colourAt(r);
image.putPixel(x, y, colour);
for (x = 0 ; x < this->horizontalSize ; x++)
{
Tuple colour;
if (this->apertureSize > 0)
{
int i;
for (i = 0 ; i < this->rayCount ; i++)
{
double horz = frandclip(-this->apertureSize/2, this->apertureSize / 2);
double vert = frandclip(-this->apertureSize/2, this->apertureSize / 2);
Ray r = this->rayForPixel(x, y, horz, vert);
colour = colour + world.colourAt(r, depth);
}
colour = colour / this->rayCount;
}
else
{
Ray r = this->rayForPixel(x, y);
colour = world.colourAt(r, depth);
}
stats.addPixel();
image.putPixel(x, y, colour);
}
}
}
stats.printStats();
return image;
}

View File

@@ -9,6 +9,16 @@
#include <canvas.h>
#include <lodepng.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* NanoJPEG is a bit interesting to include as a header */
extern "C" {
#define _NJ_INCLUDE_HEADER_ONLY
#include <nanojpeg.c>
#undef _NJ_INCLUDE_HEADER_ONLY
}
#define BPP (24)
#define BytePP (BPP / 8)
@@ -40,6 +50,80 @@ Canvas::Canvas(const Canvas *b)
memcpy(this->bitmap, b->bitmap, 4 * b->width * b->height);
}
Canvas::Canvas(const char *imgfile)
{
/* Try to determine the file type in a really lazy way */
const char *fileExt = strrchr(imgfile, '.');
this->bitmap = NULL;
if (fileExt == NULL)
{
printf("ERROR: Invalid file name '%s' - Can't determine the file format\n", imgfile);
}
else if (strncasecmp(fileExt, ".png", strlen(fileExt)) == 0)
{
uint32_t ret = lodepng_decode24_file(&this->bitmap, &this->width, &this->height, imgfile);
if (ret)
{
printf("ERROR: %s\n", lodepng_error_text(ret));
}
}
else if ( (strncasecmp(fileExt, ".jpg", strlen(fileExt)) == 0) || (strncasecmp(fileExt, ".jpeg", strlen(fileExt)) == 0) )
{
FILE *fp;
char *fileBuff;
size_t fileSize;
fp = fopen(imgfile, "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fileBuff = (char *)calloc(fileSize, 1);
fseek(fp, 0, SEEK_SET);
fileSize = fread(fileBuff, 1, fileSize, fp);
fclose(fp);
njInit();
if (!njDecode(fileBuff, fileSize))
{
this->width = njGetWidth();
this->height = njGetHeight();
/* Need to do a local copy */
this->bitmap = (uint8_t *)calloc(1, njGetImageSize());
memcpy(this->bitmap, njGetImage(), njGetImageSize());
}
else
{
printf("ERROR: Error while decoding the file '%s'\n", imgfile);
}
free(fileBuff);
njDone();
}
else
{
printf("ERROR: Can't open/find the file '%s'.\n", imgfile);
}
}
else
{
printf("ERROR: File extention '%s' is not a recognized one.\n", fileExt);
}
if (this->bitmap == NULL)
{
printf("ABORT: Opening image file '%s' failed\n", imgfile);
exit(-1);
}
this->stride = BytePP * width;
}
Canvas::~Canvas()
{
if (this->bitmap != nullptr)
@@ -48,12 +132,12 @@ Canvas::~Canvas()
}
}
void Canvas::putPixel(uint32_t x, uint32_t y, Tuple colour)
void Canvas::putPixel(uint32_t x, uint32_t y, Tuple c)
{
uint32_t offset = y * this->stride + x * BytePP;
this->bitmap[offset + 0] = MAX(MIN(colour.x * 255, 255), 0);
this->bitmap[offset + 1] = MAX(MIN(colour.y * 255, 255), 0);
this->bitmap[offset + 2] = MAX(MIN(colour.z * 255, 255), 0);
this->bitmap[offset + 0] = MAX(MIN(c.x * 255, 255), 0);
this->bitmap[offset + 1] = MAX(MIN(c.y * 255, 255), 0);
this->bitmap[offset + 2] = MAX(MIN(c.z * 255, 255), 0);
}
Colour Canvas::getPixel(uint32_t x, uint32_t y)
@@ -67,4 +151,4 @@ bool Canvas::SaveAsPNG(const char *filename)
uint32_t ret = lodepng_encode24_file(filename, this->bitmap, this->width, this->height);
return ret == 0;
}
}

View File

@@ -0,0 +1,134 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Bounding box header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_BOUNDINGBOX_H
#define DORAYME_BOUNDINGBOX_H
#include <renderstat.h>
#include <stdio.h>
struct BoundingBox
{
private:
bool isReset;
public:
Tuple min;
Tuple max;
BoundingBox() : min(INFINITY, INFINITY, INFINITY, 1.0), max(-INFINITY, -INFINITY, -INFINITY, 1.0), isReset(true) { };
BoundingBox(Tuple min, Tuple max) : min(min), max(max), isReset(false) { };
void operator|(const BoundingBox &b) {
isReset = false;
if (this->min.x > b.min.x) { this->min.x = b.min.x; }
if (this->min.y > b.min.y) { this->min.y = b.min.y; }
if (this->min.z > b.min.z) { this->min.z = b.min.z; }
if (this->max.x < b.max.x) { this->max.x = b.max.x; }
if (this->max.y < b.max.y) { this->max.y = b.max.y; }
if (this->max.z < b.max.z) { this->max.z = b.max.z; }
}
void operator|(const Tuple &b) {
isReset = false;
if (this->min.x > b.x) { this->min.x = b.x; }
if (this->min.y > b.y) { this->min.y = b.y; }
if (this->min.z > b.z) { this->min.z = b.z; }
if (this->max.x < b.x) { this->max.x = b.x; }
if (this->max.y < b.y) { this->max.y = b.y; }
if (this->max.z < b.z) { this->max.z = b.z; }
}
bool haveFiniteBounds() { return this->min.isRepresentable() && this->max.isRepresentable(); };
bool fitsIn(const BoundingBox &other) {
bool fits = true;
if (this->min.x > other.min.x) { fits = false; }
if (this->min.y > other.min.y) { fits = false; }
if (this->min.z > other.min.z) { fits = false; }
if (this->max.x < other.max.x) { fits = false; }
if (this->max.y < other.max.y) { fits = false; }
if (this->max.z < other.max.z) { fits = false; }
return fits;
}
void checkAxis(double axeOrigin, double axeDirection, double xMin, double xMax, double *axeMin, double *axeMax)
{
double tMinNumerator = (xMin - axeOrigin);
double tMaxNumerator = (xMax - axeOrigin);
if (fabs(axeDirection) >= getEpsilon())
{
*axeMin = tMinNumerator / axeDirection;
*axeMax = tMaxNumerator / axeDirection;
}
else
{
*axeMin = tMinNumerator * INFINITY;
*axeMax = tMaxNumerator * INFINITY;
}
if (*axeMin > *axeMax)
{
double swap = *axeMax;
*axeMax = *axeMin;
*axeMin = swap;
}
}
void reset()
{
this->isReset = true;
min.x = min.y = min.z = INFINITY;
max.x = max.y = max.z = -INFINITY;
}
bool isEmpty() { return this->isReset; };
bool intesectMe(Ray r) {
double xtMin, xtMax, ytMin, ytMax, ztMin, ztMax;
double tMin, tMax;
this->checkAxis(r.origin.x, r.direction.x, this->min.x, this->max.x, &xtMin, &xtMax);
this->checkAxis(r.origin.y, r.direction.y, this->min.y, this->max.y, &ytMin, &ytMax);
this->checkAxis(r.origin.z, r.direction.z, this->min.z, this->max.z, &ztMin, &ztMax);
tMin = max3(xtMin, ytMin, ztMin);
tMax = min3(xtMax, ytMax, ztMax);
if (tMin <= tMax)
{
return true;
}
stats.addDiscardedIntersect();
return false;
}
void dumpMe(FILE *fp)
{
Tuple t = this->min;
fprintf(fp, "\"min\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->max;
fprintf(fp, "\"max\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
}
};
#endif /* DORAYME_BOUNDINGBOX_H */

View File

@@ -21,6 +21,10 @@ private:
double halfWidth;
double halfHeight;
public:
double focalDistance;
double apertureSize;
uint32_t rayCount;
uint32_t verticalSize;
uint32_t horizontalSize;
double fieldOfView;
@@ -30,9 +34,14 @@ public:
public:
Camera(uint32_t hsize, uint32_t vsize, double fov);
void setFocal(double focal, double aperture, uint32_t rayCount) {
this->focalDistance = focal;
this->apertureSize = aperture;
this->rayCount = rayCount;
}
void setTransform(Matrix transform);
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY);
Canvas render(World w);
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY, double horzOffset = 0, double vertOffset = 0);
Canvas render(World w, uint32_t depth = 5);
};
#endif /* DORAYME_CAMERA_H */

View File

@@ -23,6 +23,8 @@ public:
Canvas(uint32_t width, uint32_t height);
Canvas(const Canvas *c);
Canvas(const Canvas &c);
Canvas(const char *imgfile);
~Canvas();
void putPixel(uint32_t x, uint32_t y, Tuple c);

39
source/include/cone.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cone header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CONE_H
#define DORAYME_CONE_H
#include <shape.h>
#include <ray.h>
#include <intersect.h>
#include <renderstat.h>
#include <stdio.h>
class Cone : public Shape {
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
bool checkCap(Ray r, double t, double y);
void intersectCaps(Ray r, Intersect &xs);
public:
bool isClosed;
double minCap;
double maxCap;
Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(Shape::CONE) { stats.addCone(); };
BoundingBox getLocalBounds();
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
void dumpMe(FILE *fp);
};
#endif /* DORAYME_CONE_H */

56
source/include/csg.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* DoRayMe - a quick and dirty Raytracer
* CSG header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CSG_H
#define DORAYME_CSG_H
#include <shape.h>
class CSG : public Shape
{
public:
enum OperationType
{
UNION,
DIFFERENCE,
INTERSECTION
};
protected:
Shape *left;
Shape *right;
OperationType operation;
BoundingBox bounds;
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
BoundingBox getLocalBounds();
bool intersectionAllowed(bool leftHit, bool inLeft, bool inRight);
void filterIntersections(Intersect &xs, Intersect &ret);
void updateBoundingBox();
BoundingBox getBounds();
public:
CSG(OperationType operation, Shape *left, Shape *right);
void intersect(Ray &r, Intersect &xs);
bool includes(Shape *b);
void updateTransform();
void lock();
void dumpMe(FILE *fp);
};
#endif /* DORAYME_CSG_H */

32
source/include/cube.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cube header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CUBE_H
#define DORAYME_CUBE_H
#include <shape.h>
#include <ray.h>
#include <intersect.h>
#include <renderstat.h>
#include <stdio.h>
class Cube : public Shape {
protected:
void checkAxis(double axeOrigin, double axeDirection, double *axeMin, double *axeMax);
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Cube() : Shape(Shape::CUBE) { stats.addCube(); };
void dumpMe(FILE *fp);
};
#endif /* DORAYME_CUBE_H */

41
source/include/cylinder.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cylinder header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CYLINDER_H
#define DORAYME_CYLINDER_H
#include <shape.h>
#include <ray.h>
#include <intersect.h>
#include <renderstat.h>
#include <stdio.h>
class Cylinder : public Shape {
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
bool checkCap(Ray r, double t);
void intersectCaps(Ray r, Intersect &xs);
public:
bool isClosed;
double minCap;
double maxCap;
Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(Shape::CYLINDER) { stats.addCylinder(); };
BoundingBox getLocalBounds();
bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); };
void dumpMe(FILE *fp);
};
#endif //DORAYME_CYLINDER_H

69
source/include/group.h Normal file
View File

@@ -0,0 +1,69 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Group header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_GROUP_H
#define DORAYME_GROUP_H
#include <shape.h>
#include <stdio.h>
/* TODO: Add a way to force(?) material from group to be applied on childs */
class Group : public Shape
{
private:
uint32_t allocatedObjectCount;
Shape* *objectList;
uint32_t objectCount;
uint32_t allocatedUnboxableObjectCount;
Shape* *unboxableObjectList;
uint32_t unboxableObjectCount;
char name[32 + 1];
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
BoundingBox bounds;
public:
bool isEmpty();
void addObject(Shape *s);
void removeObject(Shape *s);
Shape *operator[](const int p) { return this->getObject(p); };
Shape *getObject(const int p) { return this->objectList[p]; };
Shape *getUnboxable(const int p) { return this->unboxableObjectList[p]; };
void intersect(Ray &r, Intersect &xs);
BoundingBox getLocalBounds();
BoundingBox getBounds();
void updateBoundingBox();
void updateTransform();
bool includes(Shape *b);
uint32_t getObjectCount() { return this->objectCount; };
uint32_t getUnboxableCount() { return this->unboxableObjectCount; };
Group(const char *name = nullptr);
void lock();
void setBounds(BoundingBox &bb) { this->bounds | bb; };
const char *getName() { return this->name; };
void dumpMe(FILE * fp);
};
#endif /* DORAYME_GROUP_H */

View File

@@ -21,6 +21,7 @@ private:
public:
Intersect();
~Intersect();
void reset();
void add(Intersection i);
int count() { return this->num; };
Intersection operator[](const int p) { return *this->list[p]; }

View File

@@ -11,20 +11,35 @@
#include <stdlib.h>
#include <ray.h>
#include <material.h>
#include <renderstat.h>
class Shape;
class Intersect;
struct Computation
{
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP, bool inside) :
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside), overHitPoint(overHitP) { };
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP,
bool inside, Tuple reflectV = Vector(0, 0, 0), double n1 = 1.0, double n2 = 1.0,
Tuple underHitP = Point(0, 0, 0), Material *objMat = nullptr) :
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2), material(objMat) { };
double schlick();
Shape *object;
double t;
Tuple hitPoint;
Tuple overHitPoint;
Tuple underHitPoint;
Tuple eyeVector;
Tuple normalVector;
Tuple reflectVector;
Material *material;
double n1;
double n2;
bool inside;
};
@@ -34,12 +49,13 @@ class Intersection
public:
double t;
Shape *object;
double u, v;
public:
Intersection(double t, Shape *object) : t(t), object(object) { };
Intersection(double t, Shape *object, double u = NAN, double v = NAN) : t(t), object(object), u(u), v(v) { stats.addIntersection(); };
bool nothing() { return (this->object == nullptr); };
Computation prepareComputation(Ray r);
Computation prepareComputation(Ray r, Intersect *xs = nullptr);
bool operator==(const Intersection &b) const { return ((this->t == b.t) && (this->object == b.object)); };
};

View File

@@ -11,10 +11,17 @@
#include <tuple.h>
#include <colour.h>
#include <renderstat.h>
#include <sequence.h>
#include <stdio.h>
class World;
enum LightType
{
POINT_LIGHT = 0,
AREA_LIGHT,
};
class Light
@@ -24,13 +31,37 @@ public:
Tuple position;
LightType type;
/* For area light */
Tuple corner;
Tuple uVec;
Tuple vVec;
uint32_t samples;
uint32_t uSteps;
uint32_t vSteps;
bool jitter;
Sequence jitterBy;
public:
Light(LightType type = POINT_LIGHT, Tuple position=Point(0, 0, 0),
Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity) { };
Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity)
{ stats.addLight(); };
Light(LightType type, Tuple corner, Tuple fullUVec, uint32_t uSteps, Tuple fullVVec, uint32_t vSteps,
Colour intensity, bool jitter = false): type(type), corner(corner), uVec(fullUVec / uSteps), uSteps(uSteps),
vVec(fullVVec / vSteps), vSteps(vSteps), intensity(intensity), jitter(jitter)
{
this->samples = this->vSteps * this->uSteps;
this->position = this->corner + ((fullUVec + fullVVec) / 2);
stats.addLight();
};
double intensityAt(World &w, Tuple point);
bool operator==(const Light &b) const { return this->intensity == b.intensity &&
this->position == b.position &&
this->type == b.type; };
Tuple pointOnLight(uint32_t u, uint32_t v);
void dumpMe(FILE *fp);
};
#endif /* DORAYME_LIGHT_H */

120
source/include/list.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* DoRayMe - a quick and dirty Raytracer
* List header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_LIST_H
#define DORAYME_LIST_H
#include <shape.h>
struct ChainList
{
Shape *shape;
ChainList *next;
};
class List
{
private:
ChainList *head;
ChainList *tail;
uint32_t count;
public:
List() : head(nullptr), tail(nullptr), count(0) { };
~List()
{
ChainList *p = this->head;
if (p == nullptr) { return; }
/* clear up the list */
do
{
ChainList *next = p->next;
free(p);
p = next;
}
while(p != nullptr);
}
Shape *last()
{
ChainList *p = this->tail;
if (p == nullptr) { return nullptr; }
return p->shape;
}
void remove(Shape *s)
{
ChainList *p = this->head;
if (p == nullptr) { return; }
if ((p->next == nullptr) && (p->shape == s))
{
/* First element */
this->tail = nullptr;
free(this->head);
this->head = nullptr;
this->count = 0;
return;
}
while(p->next != nullptr)
{
if (p->next->shape == s)
{
ChainList *found = p->next;
p->next = p->next->next;
free(found);
if (p->next == NULL) { this->tail = p; }
this->count --;
return;
}
p = p->next;
}
}
void append(Shape *s)
{
ChainList *theNew = (ChainList *)calloc(1, sizeof(ChainList));
theNew->shape = s;
ChainList *p = this->tail;
this->tail = theNew;
if (p != nullptr) { p->next = theNew; }
else { this->head = theNew; } /* If the tail is empty, it mean the list IS empty. */
this->count ++;
}
bool isEmpty()
{
return (this->count == 0);
}
bool doesInclude(Shape *s)
{
ChainList *p = this->head;
while(p != nullptr)
{
if (p->shape == s) { return true; }
p = p->next;
}
return false;
}
};
#endif //DORAYME_LIST_H

View File

@@ -13,6 +13,7 @@
#include <colour.h>
#include <pattern.h>
#include <light.h>
#include <stdio.h>
class Shape;
@@ -24,19 +25,33 @@ public:
double diffuse;
double specular;
double shininess;
double reflective;
double transparency;
double emissive;
double refractiveIndex;
Pattern *pattern;
public:
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200), pattern(nullptr) {};
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200),
reflective(0.0), transparency(0.0), emissive(0), refractiveIndex(1.0), pattern(nullptr) {};
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow = false);
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject,
double lightIntensity = 1.0);
bool operator==(const Material &b) const { return double_equal(this->ambient, b.ambient) &&
double_equal(this->diffuse, b.diffuse) &&
double_equal(this->specular, b.specular) &&
double_equal(this->shininess, b.shininess) &&
double_equal(this->reflective, b.reflective) &&
double_equal(this->transparency, b.transparency) &&
double_equal(this->emissive, b.emissive) &&
double_equal(this->refractiveIndex, b.refractiveIndex) &&
(this->colour == b.colour); };
bool operator!=(const Material &b) const { return !(*this == b); };
void dumpMe(FILE *fp);
};
#endif /* DORAYME_MATERIAL_H */

View File

@@ -18,4 +18,15 @@ bool double_equal(double a, double b);
double deg_to_rad(double deg);
double min3(double a, double b, double c);
double max3(double a, double b, double c);
double frand();
double frandclip(double min, double max);
static double modulo(double a, double b)
{
return a - floor(a/b) * b;
}
#endif /* DORAYME_MATH_HELPER_H */

View File

@@ -19,6 +19,8 @@
#undef minor
#endif
#define FastGet4(_x, _y) (this->data[4 * (_x) + (_y)])
class Matrix
{
protected:
@@ -46,7 +48,12 @@ public:
bool isInvertible() { return this->determinant() != 0; }
Matrix operator*(const Matrix &b) const;
Tuple operator*(const Tuple &b) const;
Tuple operator*(const Tuple &b) const {
return Tuple(b.x * FastGet4(0, 0) + b.y * FastGet4(0, 1) + b.z * FastGet4(0, 2) + b.w * FastGet4(0, 3),
b.x * FastGet4(1, 0) + b.y * FastGet4(1, 1) + b.z * FastGet4(1, 2) + b.w * FastGet4(1, 3),
b.x * FastGet4(2, 0) + b.y * FastGet4(2, 1) + b.z * FastGet4(2, 2) + b.w * FastGet4(2, 3),
b.x * FastGet4(3, 0) + b.y * FastGet4(3, 1) + b.z * FastGet4(3, 2) + b.w * FastGet4(3, 3));
}
};
class Matrix4: public Matrix

80
source/include/objfile.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* DoRayMe - a quick and dirty Raytracer
* OBJ File header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_OBJFILE_H
#define DORAYME_OBJFILE_H
#include <stdint.h>
#include <tuple.h>
#include <shape.h>
#include <group.h>
#include <renderstat.h>
class OBJFile : public Shape
{
private:
Group *baseGroup;
Group *currentGroup;
uint32_t allocatedVertexCount;
Point* *vertexList;
uint32_t vertexCount;
uint32_t allocatedVertexNormalCount;
Vector* *vertexNormalList;
uint32_t vertexNormalCount;
private:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
/* Some stats */
uint32_t ignoredLines;
protected:
void addGroup(Group *group);
void addVertex(Point *vertex);
void addVertexNormal(Vector *vertexNormal);
void parseLine(char *line, uint32_t currentLine);
int execLine(int argc, char *argv[], uint32_t currentLine);
BoundingBox bounds;
public:
OBJFile();
OBJFile(const char *filepath);
~OBJFile();
int parseOBJFile(const char *content);
/* OBJ file expect the first vertice to be 1 and not 0 */
Point vertices(uint32_t i) { return *this->vertexList[i - 1]; };
Vector verticesNormal(uint32_t i) { return *this->vertexNormalList[i - 1]; };
Group *groups(const char *groupName);
void intersect(Ray &r, Intersect &xs);
BoundingBox getLocalBounds();
BoundingBox getBounds();
Shape *getBaseGroup() { return this->baseGroup; };
bool includes(Shape *b);
void updateBoundingBox();
void updateTransform();
void lock();
void dumpMe(FILE * fp);
};
#define OBJ_DEFAULT_GROUP "_DefaultObjGroup_"
#endif /* DORAYME_OBJFILE_H */

View File

@@ -12,6 +12,7 @@
#include <colour.h>
#include <tuple.h>
#include <matrix.h>
#include <stdio.h>
class Shape;
@@ -28,6 +29,7 @@ public:
Pattern(Colour a, Colour b);
virtual Colour patternAt(Tuple point) = 0;
virtual void dumpMe(FILE *fp);
void setTransform(Matrix transform);
Colour patternAtObject(Shape *object, Tuple point);

View File

@@ -9,14 +9,18 @@
#ifndef DORAYME_PLANE_H
#define DORAYME_PLANE_H
#include <renderstat.h>
class Plane : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Plane() : Shape(SHAPE_PLANE) { };
Plane() : Shape(Shape::PLANE) { stats.addPlane(); };
BoundingBox getLocalBounds();
bool haveFiniteBounds() { return false; };
};
#endif //DORAYME_PLANE_H

View File

@@ -10,6 +10,7 @@
#define DORAYME_RAY_H
#include <tuple.h>
#include <renderstat.h>
class Ray
{
@@ -17,7 +18,7 @@ public:
Tuple direction;
Tuple origin;
Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { };
Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { stats.addRay(); };
Tuple position(double t) { return this->origin + this->direction * t; };
};

107
source/include/renderstat.h Normal file
View File

@@ -0,0 +1,107 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render statistics header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_RENDERSTAT_H
#define DORAYME_RENDERSTAT_H
#include <stdint.h>
class RenderStats
{
private:
uint64_t coneCount; /* Total number of cones */
uint64_t cylinderCount; /* Total number of cylinder */
uint64_t cubeCount; /* Total number of cubes */
uint64_t groupCount; /* Total number of groups */
uint64_t lightCount; /* Total number of light */
uint64_t planeCount; /* Total number of plane */
uint64_t sphereCount; /* Total number of sphere */
uint64_t triangleCount; /* Total number of triangle */
uint64_t smoothTriangleCount; /* Total number of smooth triangle */
uint64_t objfileCount; /* Total number of OBJ File */
uint64_t csgCount; /* Total number of CSG */
uint64_t pixelCount; /* Total number of rendered pixels */
uint64_t rayCount; /* Total number of rays object created */
uint64_t rayCasted; /* Total number of rays actually casted */
uint64_t lightRayEmitedCount; /* Total number of ray launched for light tests */
uint64_t reflectionRayCount; /* Total number of reflection ray launched */
uint64_t refractedRayCount; /* Total number of refracted ray launched */
uint64_t intersectCount; /* Total number of intersect object created */
uint64_t intersectionCount; /* Total number of intersection for all casted rays, including light and reflections */
uint64_t reallocCallCount; /* Total number of time realloc being called */
uint64_t mallocCallCount; /* Total number of time malloc/calloc being called */
uint64_t discardedIntersectCount; /* Number of time a bounding box check said "no need to test me" */
uint64_t maxDepthAttained; /* Report the lowest depth attained during ray recursion */
uint64_t maxIntersectOnARay; /* Biggest intersect done */
public:
RenderStats() : coneCount(0), cylinderCount(0), cubeCount(0), groupCount(0), lightCount(0), planeCount(0), sphereCount(0), triangleCount(0),
pixelCount(0), rayCount(0), lightRayEmitedCount(0), reflectionRayCount(0), refractedRayCount(0),
intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0), smoothTriangleCount(0),
discardedIntersectCount(0), maxDepthAttained(UINT64_MAX), maxIntersectOnARay(0), objfileCount(0),
csgCount(0), rayCasted(0) {};
#ifdef RENDER_STATS
void addCone();
void addCylinder();
void addCube();
void addGroup();
void addLight();
void addPlane();
void addSphere();
void addCsg();
void addOBJFile();
void addTriangle();
void addSmoothTriangle();
void printStats();
void addPixel();
void addRay();
void addCastedRay();
void addLightRay();
void addReflectRay();
void addRefractRay();
void addIntersection();
void addDiscardedIntersect();
void setMaxDepth(uint32_t depth);
void addIntersect();
void addMalloc();
void addRealloc();
void setMaxIntersect(uint32_t count);
#else
static void addCone() {};
static void addCylinder() {};
static void addCube() {};
static void addGroup() {};
static void addLight() {};
static void addPlane() {};
static void addSphere() {};
static void addTriangle() {};
static void addSmoothTriangle() {};
static void printStats() {};
static void addPixel() {};
static void addRay() {};
static void addCastedRay() {};
static void addLightRay() {};
static void addReflectRay() {};
static void addRefractRay() {};
static void addIntersection() {};
static void addDiscardedIntersect() {};
static void setMaxDepth(uint32_t depth) {};
static void addIntersect() {};
static void addMalloc() {};
static void addRealloc() {};
static void setMaxIntersect(uint32_t count) {};
static void addOBJFile() {};
static void addCsg() {};
#endif
};
extern RenderStats stats;
#endif /* DORAYME_RENDERSTAT_H */

27
source/include/sequence.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Sequence header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_SEQUENCE_H
#define DORAYME_SEQUENCE_H
#include <stdint.h>
class Sequence
{
private:
double *list;
uint32_t listPos;
uint32_t listSize;
public:
Sequence();
Sequence(double *list, uint32_t listSize);
double next();
};
#endif /* DORAYME_SEQUENCE_H */

View File

@@ -11,44 +11,96 @@
class Shape;
#include <stdio.h>
#include <ray.h>
#include <tuple.h>
#include <matrix.h>
#include <intersect.h>
#include <material.h>
enum ShapeType
{
SHAPE_NONE,
SHAPE_SPHERE,
SHAPE_PLANE,
};
#include <boundingbox.h>
/* Base class for all object that can be presented in the world */
class Shape
{
private:
ShapeType type;
public:
enum ShapeType
{
NONE,
SPHERE,
PLANE,
CUBE,
CYLINDER,
CONE,
GROUP,
TRIANGLE,
OBJFILE,
SMOOTHTRIANGLE,
CSG,
};
private:
virtual Intersect localIntersect(Ray r) = 0;
virtual Tuple localNormalAt(Tuple point) = 0;
protected:
ShapeType type;
Matrix localTransformMatrix;
bool locked;
uint64_t objectId;
protected:
virtual void localIntersect(Ray r, Intersect &xs) = 0;
virtual Tuple localNormalAt(Tuple point, Intersection *hit) = 0;
static uint64_t newObjectId();
public:
Matrix transformMatrix;
Matrix inverseTransform;
Matrix transposedInverseTransform;
Material material;
bool dropShadow;
Shape *parent;
bool materialSet;
public:
Shape(ShapeType = SHAPE_NONE);
Shape(ShapeType = Shape::NONE);
Intersect intersect(Ray r);
Tuple normalAt(Tuple point);
ShapeType getType() { return this->type; };
virtual void intersect(Ray &r, Intersect &xs) { this->localIntersect(this->invTransform(r), xs); };
Tuple normalAt(Tuple point, Intersection *hit = nullptr);
uint64_t getObjectId() { return this->objectId; };
void setObjectId(uint64_t oid) { this->objectId = oid; };
/* Bounding box points are always world value */
virtual BoundingBox getLocalBounds();
virtual BoundingBox getBounds();
virtual bool haveFiniteBounds() { return true; };
virtual void updateTransform();
virtual bool includes(Shape *b) { return this == b; };
virtual void dumpMe(FILE *fp);
/* When an object is locked, the matrix transformation and bounding box can't be updated. This is
* usefull to move object between group without changing the real hierarchy between them.
* It will also not change the parent member.
* This is supposed to be used only before a render is going to start so we can optimise the
* way the object are stored to prefer lots of un-needed intersections.
*/
virtual void lock() { this->locked = true; };
Tuple worldToObject(Tuple point) { return this->inverseTransform * point; };
Tuple objectToWorld(Tuple point) { return this->transformMatrix * point; };
Tuple normalToWorld(Tuple normalVector);
void setParent(Shape *parent) { if (!this->locked) { this->parent = parent; };};
void setTransform(Matrix transform);
void setMaterial(Material material) { this->material = material; };
Ray transform(Ray r) { return Ray(this->transformMatrix * r.origin, this->transformMatrix * r.direction); };
Ray invTransform(Ray r) { return Ray(this->inverseTransform * r.origin, this->inverseTransform * r.direction); };
void setMaterial(Material material) { this->material = material; this->materialSet = true; };
Material *getMaterial();
Ray transform(Ray &r) { return Ray(this->transformMatrix * r.origin, this->transformMatrix * r.direction); };
Ray invTransform(Ray &r) { return Ray(this->inverseTransform * r.origin, this->inverseTransform * r.direction); };
bool operator==(const Shape &b) const { return this->material == b.material &&
this->type == b.type &&

View File

@@ -0,0 +1,30 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Smooth Triangle header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_SMOOTHTRIANGLE_H
#define DORAYME_SMOOTHTRIANGLE_H
#include <triangle.h>
class SmoothTriangle : public Triangle
{
public:
Vector n1;
Vector n2;
Vector n3;
protected:
Tuple localNormalAt(Tuple point, Intersection *hit);
public:
SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3);
void dumpMe(FILE *fp);
};
#endif /* DORAYME_SMOOTHTRIANGLE_H */

View File

@@ -12,16 +12,28 @@
#include <shape.h>
#include <ray.h>
#include <intersect.h>
#include <renderstat.h>
#include <stdio.h>
class Sphere : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Sphere() : Shape(SHAPE_SPHERE) { };
Sphere() : Shape(Shape::SPHERE) { stats.addSphere(); };
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
void dumpMe(FILE *fp);
};
/* Mostly for test purposes */
class GlassSphere : public Sphere
{
public:
GlassSphere() : Sphere() { this->material.transparency = 1.0; this->material.refractiveIndex = 1.5; };
};
#endif /* DORAYME_SPHERE_H */

View File

@@ -16,8 +16,8 @@
class TestShape : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Ray localRay;

34
source/include/triangle.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Triangle header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_TRIANGLE_H
#define DORAYME_TRIANGLE_H
#include <shape.h>
#include <stdio.h>
class Triangle : public Shape
{
protected:
void localIntersect(Ray r, Intersect &xs);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Tuple p1, p2, p3;
Tuple e1, e2;
Tuple normal;
public:
Triangle(Point p1, Point p2, Point p3);
BoundingBox getLocalBounds();
void dumpMe(FILE *fp);
};
#endif /* DORAYME_TRIANGLE_H */

View File

@@ -17,6 +17,7 @@ public:
double x, y, z, w;
public:
Tuple() : x(0), y(0), z(0), w(0.0) {};
Tuple(double x, double y, double z) : x(x), y(y), z(z), w(0.0) {};
Tuple(double x, double y, double z, double w) : x(x), y(y), z(z), w(w) {};
bool isPoint() { return (this->w == 1.0); };
@@ -26,6 +27,7 @@ public:
double_equal(this->y, b.y) &&
double_equal(this->z, b.z) &&
double_equal(this->w, b.w); };
bool operator!=(const Tuple &b) const { return !(*this == b); };
Tuple operator+(const Tuple &b) const { return Tuple(this->x + b.x, this->y + b.y,
this->z + b.z, this->w + b.w); };
@@ -37,23 +39,37 @@ public:
this->z * b, this->w * b); };
Tuple operator/(const double &b) const { return Tuple(this->x / b, this->y / b,
this->z / b, this->w / b); };
bool isRepresentable();
void set(double nX, double nY, double nZ) { this->x = nX; this->y = nY; this->z = nZ; };
double magnitude();
Tuple normalise();
double dot(const Tuple &b);
Tuple cross(const Tuple &b) const;
double dot(const Tuple &b) {
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
}
Tuple cross(const Tuple &b) const {
return Tuple(this->y * b.z - this->z * b.y,
this->z * b.x - this->x * b.z,
this->x * b.y - this->y * b.x,
0);
}
Tuple reflect(const Tuple &normal);
};
class Point: public Tuple
{
public:
Point() : Tuple(0, 0, 0, 1.0) {};
Point(double x, double y, double z) : Tuple(x, y, z, 1.0) {};
};
class Vector: public Tuple
{
public:
Vector() : Tuple(0, 0, 0, 0.0) {};
Vector(double x, double y, double z) : Tuple(x, y, z, 0.0) {};
};

View File

@@ -0,0 +1,28 @@
/*
* DoRayMe - a quick and dirty Raytracer
* UV Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_UV_PATTERN_H
#define DORAYME_UV_PATTERN_H
#include <colour.h>
class UVPattern
{
public:
Colour a;
Colour b;
double width;
double height;
UVPattern(double width, double height, Colour a, Colour b) : a(a), b(b),
width(width), height(height) {};
virtual Colour uvPatternAt(double u, double v) = 0;
};
#endif /* DORAYME_UV_PATTERN_H */

View File

@@ -14,19 +14,30 @@
#include <shape.h>
#include <intersect.h>
#include <ray.h>
#include <stdio.h>
#include <group.h>
#include <worldoptimiser.h>
#ifdef ENABLE_LUA_SUPPORT
extern "C" {
#include <lua.h>
}
#endif
class World
{
public:
uint32_t objectCount;
uint32_t lightCount;
private:
uint32_t allocatedObjectCount;
uint32_t allocatedLightCount;
Light* *lightList;
Shape* *objectList;
Group worldGroup;
#ifdef ENABLE_LUA_SUPPORT
lua_State *L;
#endif
public:
World();
@@ -39,14 +50,24 @@ public:
bool lightIsIn(Light &l);
bool objectIsIn(Shape &s);
Shape *getObject(int i) { return this->objectList[i]; };
Shape *getObject(int i) { return this->worldGroup[i]; };
Light *getLight(int i) { return this->lightList[i]; };
Tuple shadeHit(Computation comps);;
Tuple colourAt(Ray r);
bool isShadowed(Tuple point);
uint32_t getObjectCount() { return this->worldGroup.getObjectCount(); };
uint32_t getLightCount() { return this->lightCount; };
Intersect intersect(Ray r);
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
Tuple colourAt(Ray r, uint32_t depthCount = 4);
bool isShadowed(Tuple point, Tuple lightPosition);
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
Colour refractedColour(Computation comps, uint32_t depthCount = 4);
void intersect(Ray &r, Intersect &xs);
void finalise(WorldOptimiser &opt);
void dumpMe(FILE *fp);
};
#endif /* DORAYME_WORLD_H */

View File

@@ -10,6 +10,7 @@
#define DORAYME_WORLDBUILDER_H
#include <world.h>
#include <camera.h>
/* Let's keep a single header for now, will see later */
@@ -22,7 +23,30 @@ public:
/* Not implemented yet */
class Hw3File : public World
{
private:
Matrix transformStack[50];
uint32_t transStackCount;
public:
double currentAmbient;
double currentShininess;
double currentSpecular;
double currentDiffuse;
double currentEmission;
double currentReflective;
double currentTransparency;
double currentRefIndex;
Colour currentColour;
Matrix cam;
double camFoV;
public:
Matrix getTransformMatrix();
void popTransformMatrix();
void pushTransformMatrix();
void applyTransformMatrix(Matrix t);
Hw3File(const char *filename);
};

View File

@@ -0,0 +1,51 @@
/*
* DoRayMe - a quick and dirty Raytracer
* World optimiser header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_WORLDOPTIMISER_H
#define DORAYME_WORLDOPTIMISER_H
#include <group.h>
/* World Optimiser subclasses will created move objects around to try to optimise the raytrace of the world, to
* have as least as possible object to intersect per ray.
* This class is abstract to we can implement different type and change at runtime or build time
*/
class WorldOptimiser
{
protected:
Group *root;
void moveInfiniteObjects(Shape *s = nullptr);
void moveAllObjects(Shape *s = nullptr);
public:
void setRoot(Group *root) { this->root = root; };
virtual void run() = 0;
};
class NoWorldOptimisation : public WorldOptimiser
{
public:
void run() {};
};
class BVHOptimisation : public WorldOptimiser
{
private:
void makeTree(Group *leaf, int depth = 0);
public:
void run();
};
class OctreeOptimisation : public WorldOptimiser
{
private:
void makeTree(Group *leaf, int depth = 0);
public:
void run();
};
#endif /* DORAYME_WORLDOPTIMISER_H */

View File

@@ -11,19 +11,54 @@
#include <intersect.h>
#include <float.h>
#include <renderstat.h>
#define MIN_ALLOC (2)
/* TODO: Memory allocation, even if using standard calloc/realloc have a huge impact on performances. need to find a way
* to reuse the intersect object without reallocating from scratch all the time. We use a lot of Intersect objects as
* there is at least 2 per ray (one for Ray intersect object, one object per light)
*/
Intersect::Intersect()
{
this->allocated = MIN_ALLOC;
this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
this->num = 0;
if (this->list != nullptr)
{
stats.addMalloc();
stats.addIntersect();
this->num = 0;
}
else
{
printf("ABORT: Allocation error [%s]!\n", __FUNCTION__);
exit(-1);
}
}
Intersect::~Intersect()
{
int i;
for(i = 0; i < this->num; i++)
{
if (this->list[i] != nullptr)
{
delete this->list[i];
this->list[i] = nullptr;
}
}
/* Free stuff */
if (this->list != nullptr)
{
free(this->list);
this->list = nullptr;
}
}
void Intersect::reset()
{
this->num = 0;
}
void Intersect::add(Intersection i)
@@ -34,9 +69,13 @@ void Intersect::add(Intersection i)
if ((this->num + 1) > this->allocated)
{
this->allocated *= 2;
stats.addRealloc();
this->list = (Intersection **)realloc(this->list, sizeof(Intersection *) * this->allocated);
}
this->list[this->num++] = new Intersection(i.t, i.object);
this->list[this->num++] = new Intersection(i.t, i.object, i.u, i.v);
stats.setMaxIntersect(this->num);
/* Now sort.. */
for(j = 1; j < (this->num); j++)

View File

@@ -8,11 +8,52 @@
*/
#include <intersection.h>
#include <shape.h>
#include <list.h>
Computation Intersection::prepareComputation(Ray r)
double Computation::schlick()
{
/* Find the cos of the angle betzeen the eye and normal vector */
double cos = this->eyeVector.dot(this->normalVector);
double r0;
/* Total internal reflection can only occur when n1 > n2 */
if (this->n1 > this->n2)
{
double n, sin2_t;
n = this->n1 / this->n2;
sin2_t = (n * n) * (1.0 - (cos * cos));
if (sin2_t > 1.0)
{
return 1.0;
}
/* Compute the cos of theta */
cos = sqrt(1.0 - sin2_t);
}
r0 = ((this->n1 - this->n2) / (this->n1 + this->n2));
r0 = r0 * r0;
return r0 + (1 - r0) * ((1 - cos)*(1 - cos)*(1 - cos)*(1 - cos)*(1 - cos));
};
Computation Intersection::prepareComputation(Ray r, Intersect *xs)
{
double n1 = 1.0;
double n2 = 1.0;
Tuple hitP = r.position(this->t);
Tuple normalV = this->object->normalAt(hitP);
Tuple normalV;
if (xs != nullptr)
{
Intersection hit = xs->hit();
normalV = this->object->normalAt(hitP, &hit);
}
else
{
normalV = this->object->normalAt(hitP, nullptr);
}
Tuple eyeV = -r.direction;
bool inside = false;
@@ -23,6 +64,49 @@ Computation Intersection::prepareComputation(Ray r)
}
Tuple overHitP = hitP + normalV * getEpsilon();
Tuple underHitP = hitP - normalV * getEpsilon();
Tuple reflectV = r.direction.reflect(normalV);
/* If the hit object is not transparent, there is no need to do that. I think .*/
if ((xs != nullptr) && (xs->hit().object->getMaterial()->transparency > 0))
{
List containers;
int j;
for (j = 0 ; j < xs->count() ; j++)
{
Intersection i = ( *xs )[j];
if (*this == i)
{
if (!containers.isEmpty())
{
n1 = containers.last()->getMaterial()->refractiveIndex;
}
}
if (containers.doesInclude(i.object))
{
containers.remove(i.object);
}
else
{
containers.append(i.object);
}
if (*this == i)
{
if (!containers.isEmpty())
{
n2 = containers.last()->getMaterial()->refractiveIndex;
}
/* End the loop */
break;
}
}
}
Material *m = this->object->getMaterial();
return Computation(this->object,
this->t,
@@ -30,5 +114,10 @@ Computation Intersection::prepareComputation(Ray r)
eyeV,
normalV,
overHitP,
inside);
}
inside,
reflectV,
n1,
n2,
underHitP,
m);
}

View File

@@ -8,10 +8,45 @@
*
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
static int l_sin (lua_State *L) {
double d = luaL_checknumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
#if 0
int main(int argc, char *argv[])
{
printf("Hello !\n");
char buff[256];
int error;
lua_State *L = luaL_newstate(); /* opens Lua */
luaL_openlibs(L); /* opens the basic library */
lua_pushcfunction(L, l_sin);
lua_setglobal(L, "mysin");
printf("[0]>");
while (fgets(buff, sizeof(buff), stdin) != NULL)
{
error = luaL_loadstring(L, buff) || lua_pcall(L, 0, LUA_MULTRET, 0);
if (error)
{
fprintf(stderr, "%s\n", lua_tostring(L, -1));
lua_pop(L, 1); /* pop error message from the stack */
}
printf("[%d]>", lua_gettop(L));
}
lua_close(L);
return 0;
}
}
#endif

View File

@@ -7,6 +7,7 @@
*
*/
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <math_helper.h>
@@ -25,10 +26,51 @@ double getEpsilon()
bool double_equal(double a, double b)
{
if (isinf(a) && isinf(b))
return true;
return fabs(a - b) < current_precision;
}
double deg_to_rad(double deg)
{
return deg * M_PI / 180.;
}
double min3(double a, double b, double c)
{
if (a <= b)
{
if (c < a) return c;
return a;
}
if (b <= a)
{
if (c < b) return c;
}
return b;
}
double max3(double a, double b, double c)
{
if (a >= b)
{
if (c > a) return c;
return a;
}
if (b >= a)
{
if (c > b) return c;
}
return b;
}
double frand()
{
return rand() / ((double) RAND_MAX);
}
double frandclip(double min, double max)
{
return (frand() * (max - min)) + min;
}

View File

@@ -101,14 +101,18 @@ Matrix Matrix::operator*(const Matrix &b) const
return ret;
}
//#define FastGet4(_x, _y) (this->data[4 * (_x) + (_y)])
/* TODO: Check if we can optimise this function. It is called a lot */
/*
Tuple Matrix::operator*(const Tuple &b) const
{
return Tuple(b.x * this->get(0, 0) + b.y * this->get(0, 1) + b.z * this->get(0, 2) + b.w * this->get(0, 3),
b.x * this->get(1, 0) + b.y * this->get(1, 1) + b.z * this->get(1, 2) + b.w * this->get(1, 3),
b.x * this->get(2, 0) + b.y * this->get(2, 1) + b.z * this->get(2, 2) + b.w * this->get(2, 3),
b.x * this->get(3, 0) + b.y * this->get(3, 1) + b.z * this->get(3, 2) + b.w * this->get(3, 3));
return Tuple(b.x * FastGet4(0, 0) + b.y * FastGet4(0, 1) + b.z * FastGet4(0, 2) + b.w * FastGet4(0, 3),
b.x * FastGet4(1, 0) + b.y * FastGet4(1, 1) + b.z * FastGet4(1, 2) + b.w * FastGet4(1, 3),
b.x * FastGet4(2, 0) + b.y * FastGet4(2, 1) + b.z * FastGet4(2, 2) + b.w * FastGet4(2, 3),
b.x * FastGet4(3, 0) + b.y * FastGet4(3, 1) + b.z * FastGet4(3, 2) + b.w * FastGet4(3, 3));
}
*/
Matrix Matrix::identity()
{
int i;
@@ -123,6 +127,7 @@ Matrix Matrix::transpose()
{
int x, y;
Matrix ret = Matrix(this->size);
for (y = 0 ; y < this->size ; y++)
{
for (x = 0 ; x < this->size ; x++)
@@ -138,6 +143,7 @@ Matrix Matrix::submatrix(int row, int column)
int i, j;
int x = 0, y = 0;
Matrix ret = Matrix(this->size - 1);
for (i = 0 ; i < this->size ; i++)
{
if (i == row)

View File

@@ -9,6 +9,7 @@
#include <pattern.h>
#include <shape.h>
#include <stdio.h>
Pattern::Pattern(Colour a, Colour b): a(a), b(b)
{
@@ -18,7 +19,7 @@ Pattern::Pattern(Colour a, Colour b): a(a), b(b)
Colour Pattern::patternAtObject(Shape *object, Tuple worldPoint)
{
Tuple objectPoint = object->inverseTransform * worldPoint;
Tuple objectPoint = object->worldToObject(worldPoint);
Tuple patternPoint = this->inverseTransform * objectPoint;
return this->patternAt(patternPoint);
@@ -28,4 +29,10 @@ void Pattern::setTransform(Matrix transform)
{
this->transformMatrix = transform;
this->inverseTransform = transform.inverse();
}
void Pattern::dumpMe(FILE *fp)
{
fprintf(fp, "\"Colour A\": {\"red\": %f, \"green\": %f, \"blue\": %f},\n", this->a.x, this->a.y, this->a.z);
fprintf(fp, "\"Colour B\": {\"red\": %f, \"green\": %f, \"blue\": %f},\n", this->b.x, this->b.y, this->b.z);
}

View File

@@ -9,6 +9,8 @@
#ifndef DORAYME_CHECKERSPATTERN_H
#define DORAYME_CHECKERSPATTERN_H
#include <stdio.h>
class CheckersPattern : public Pattern
{
public:
@@ -18,8 +20,14 @@ public:
{
double value = floor(point.x) + floor(point.y) + floor(point.z);
return (fmod(value, 2) == 0)?this->a:this->b;
return (modulo(value, 2) == 0)?this->a:this->b;
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"Checkers\",\n");
Pattern::dumpMe(fp);
}
};
#endif /* DORAYME_CHECKERSPATTERN_H */

View File

@@ -10,6 +10,7 @@
#define DORAYME_GRADIENTPATTERN_H
#include <pattern.h>
#include <stdio.h>
class GradientPattern : public Pattern
{
@@ -25,6 +26,12 @@ public:
return Colour(ret.x, ret.y, ret.z);
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"Gradient\",\n");
Pattern::dumpMe(fp);
}
};
#endif /* DORAYME_GRADIENTPATTERN_H */

107
source/pattern/luapattern.h Normal file
View File

@@ -0,0 +1,107 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Lua based Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_LUAPATTERN_H
#define DORAYME_LUAPATTERN_H
#include <pattern.h>
#include <stdio.h>
#include <string.h>
#ifndef ENABLE_LUA_SUPPORT
#error Cannot use the Lua Pattern generator with no Lua support disabled!
#endif
extern "C" {
#include <lua.h>
}
class LuaPattern : public Pattern
{
private:
lua_State *L;
char funcName[50];
public:
LuaPattern(Colour a, Colour b) : Pattern(a, b), L(nullptr) { };
void setLua(lua_State *L) {
this->L = L;
};
void setLuaFunctionName(const char *name) {
strncpy(this->funcName, name, 50);
}
Colour patternAt(Tuple point)
{
int isnum;
double r, g, b;
lua_getglobal(this->L, this->funcName);
lua_pushnumber(this->L, point.x);
lua_pushnumber(this->L, point.y);
lua_pushnumber(this->L, point.z);
lua_createtable(L, 3, 0);
lua_pushnumber(L, this->a.x);
lua_setfield(L, -2, "r");
lua_pushnumber(L, this->a.y);
lua_setfield(L, -2, "g");
lua_pushnumber(L, this->a.z);
lua_setfield(L, -2, "b");
lua_createtable(L, 3, 0);
lua_pushnumber(L, this->b.x);
lua_setfield(L, -2, "r");
lua_pushnumber(L, this->b.y);
lua_setfield(L, -2, "g");
lua_pushnumber(L, this->b.z);
lua_setfield(L, -2, "b");
if (lua_pcall(this->L, 5, 3, 0) != LUA_OK)
{
printf("Error running the Lua function '%s': %s\n", this->funcName,
lua_tostring(this->L, -1));
return Colour(0, 0, 0);
}
r = lua_tonumberx(this->L, -3, &isnum);
if (!isnum)
{
printf("Error: function '%s' must return numbers\n", this->funcName);
lua_pop(this->L, 1);
return Colour(0, 0, 0);
}
g = lua_tonumberx(this->L, -2, &isnum);
if (!isnum)
{
printf("Error: function '%s' must return numbers\n", this->funcName);
lua_pop(this->L, 1);
return Colour(0, 0, 0);
}
b = lua_tonumberx(this->L, -1, &isnum);
if (!isnum)
{
printf("Error: function '%s' must return numbers\n", this->funcName);
lua_pop(this->L, 1);
return Colour(0, 0, 0);
}
lua_pop(this->L, 1);
return Colour(r, g, b);
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"Lua\",\n");
Pattern::dumpMe(fp);
}
};
#endif /* DORAYME_LUAPATTERN_H */

View File

@@ -22,7 +22,12 @@ public:
double value = floor(sqrt(squared));
return (fmod(value, 2) == 0)?this->a:this->b;
return (modulo(value, 2) == 0)?this->a:this->b;
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"Ring\"\n");
Pattern::dumpMe(fp);
}
};

View File

@@ -21,12 +21,17 @@ public:
Colour patternAt(Tuple point)
{
if (fmod(floor(point.x), 2) == 0)
if (modulo(floor(point.x), 2) == 0)
{
return this->a;
}
return this->b;
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"Strip\",\n");
Pattern::dumpMe(fp);
}
};
#endif /* DORAYME_STRIPPATTERN_H */

View File

@@ -22,6 +22,11 @@ public:
{
return Colour(point.x, point.y, point.z);
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"Test\",\n");
Pattern::dumpMe(fp);
}
};
#endif /* DORAYME_TESTPATTERN_H */

198
source/pattern/texturemap.h Normal file
View File

@@ -0,0 +1,198 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Texture Map header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_TEXTUREMAP_H
#define DORAYME_TEXTUREMAP_H
#include <math.h>
#include <tuple.h>
#include <uv_pattern.h>
#include <colour.h>
enum TextureMapType
{
SPHERICAL_MAP,
PLANAR_MAP,
CYLINDRICAL_MAP,
CUBIC_MAP
};
class TextureMap : public Pattern
{
private:
TextureMapType type;
UVPattern *pattern;
UVPattern *frontPat, *leftPat, *rightPat, *backPat, *upPat, *downPat;
public:
TextureMap(TextureMapType type, UVPattern *pattern) : Pattern(Colour(0, 0, 0), Colour(0, 0, 0)),
type(type), pattern(pattern) { };
static void sphericalMap(Tuple point, double &u, double &v) {
/* First compute the azimuthal angle
* -π < theta <= π
* angle increases clockwise as viewed from above,
* which is opposite of what we want, but we'll fix it later.
*/
double theta = atan2(point.x, point.z);
/* vec is the vector pointing from the sphere's origin (the world origin)
* to the point, which will also happen to be exactly equal to the sphere's
* radius.
*/
Tuple vec = Vector(point.x, point.y, point.z);
double radius = vec.magnitude();
/* Let's compute the polar angle
* 0 <= phi <= π
*/
double phi = acos(point.y / radius);
/* -0.5 < raw_u <= 0.5 */
double raw_u = theta / (2 * M_PI);
/* 0 <= u < 1
* here's also where we fix the direction of u. Subtract it from 1,
* so that it increases counterclockwise as viewed from above.
*/
u = 1 - (raw_u + 0.5);
/* We want v to be 0 at the south pole of the sphere,
* and 1 at the north pole, so we have to "flip it over"
* by subtracting it from 1.
*/
v = 1 - phi / M_PI;
}
static void planarMap(Tuple point, double &u, double &v) {
u = modulo(point.x, 1.0);
v = modulo(point.z, 1.0);
}
static void cylindricalMap(Tuple point, double &u, double &v) {
/* Let's get the azimuthal angle, same as with the spherical mapping */
double theta = atan2(point.x , point.z);
double raw_u = theta / (2 * M_PI);
u = 1 - (raw_u + 0.5);
v = modulo(point.y, 1.0);
}
enum CubeFaces {
CUBE_LEFT,
CUBE_RIGHT,
CUBE_FRONT,
CUBE_BACK,
CUBE_UP,
CUBE_DOWN,
};
static CubeFaces faceFromPoint(Tuple point) {
double abs_x = fabs(point.x);
double abs_y = fabs(point.y);
double abs_z = fabs(point.z);
double coord = max3(abs_x, abs_y, abs_z);
if (coord == point.x) { return CUBE_RIGHT; }
if (coord == -point.x) { return CUBE_LEFT; }
if (coord == point.y) { return CUBE_UP; }
if (coord == -point.y) { return CUBE_DOWN; }
if (coord == point.z) { return CUBE_FRONT; }
return CUBE_BACK;
}
static void cubeUBFront(Tuple point, double &u, double &v) {
u = modulo(point.x + 1, 2.0) / 2.0;
v = modulo(point.y + 1, 2.0) / 2.0;
}
static void cubeUBBack(Tuple point, double &u, double &v) {
u = modulo(1 - point.x, 2.0) / 2.0;
v = modulo(point.y + 1, 2.0) / 2.0;
}
static void cubeUBLeft(Tuple point, double &u, double &v) {
u = modulo(point.z + 1, 2.0) / 2.0;
v = modulo(point.y + 1, 2.0) / 2.0;
}
static void cubeUBRight(Tuple point, double &u, double &v) {
u = modulo(1 - point.z, 2.0) / 2.0;
v = modulo(point.y + 1, 2.0) / 2.0;
}
static void cubeUBUp(Tuple point, double &u, double &v) {
u = modulo(point.x + 1, 2.0) / 2.0;
v = modulo(1 - point.z, 2.0) / 2.0;
}
static void cubeUBDown(Tuple point, double &u, double &v) {
u = modulo(point.x + 1, 2.0) / 2.0;
v = modulo(point.z + 1, 2.0) / 2.0;
}
void setCubePattern(UVPattern *front, UVPattern *left, UVPattern *right,
UVPattern *back, UVPattern *up, UVPattern *down)
{
this->frontPat = front;
this->leftPat = left;
this->rightPat = right;
this->backPat = back;
this->upPat = up;
this->downPat = down;
}
Colour patternAt(Tuple point)
{
double u,v;
if (this->type == CUBIC_MAP)
{
CubeFaces face = TextureMap::faceFromPoint(point);
UVPattern *facePat;
switch(face)
{
default:
case CUBE_LEFT: facePat = this->leftPat; TextureMap::cubeUBLeft(point, u, v); break;
case CUBE_RIGHT: facePat = this->rightPat; TextureMap::cubeUBRight(point, u, v); break;
case CUBE_FRONT: facePat = this->frontPat; TextureMap::cubeUBFront(point, u, v); break;
case CUBE_BACK: facePat = this->backPat; TextureMap::cubeUBBack(point, u, v); break;
case CUBE_UP: facePat = this->upPat; TextureMap::cubeUBUp(point, u, v); break;
case CUBE_DOWN: facePat = this->downPat; TextureMap::cubeUBDown(point, u, v); break;
}
return facePat->uvPatternAt(u, v);
}
else
{
switch (this->type)
{
default:
case SPHERICAL_MAP:
this->sphericalMap(point, u, v);
break;
case PLANAR_MAP:
this->planarMap(point, u, v);
break;
case CYLINDRICAL_MAP:
this->cylindricalMap(point, u, v);
break;
}
return this->pattern->uvPatternAt(u, v);
}
}
void dumpMe(FILE *fp) {
fprintf(fp, "\"Type\": \"TextureMap\",\n");
Pattern::dumpMe(fp);
}
};
#endif /* DORAYME_TEXTUREMAP_H */

202
source/renderstat.cpp Normal file
View File

@@ -0,0 +1,202 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render statistics implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <renderstat.h>
#include <stdio.h>
RenderStats stats;
#ifdef RENDER_STATS
void RenderStats::addCone()
{
#pragma omp atomic
this->coneCount++;
};
void RenderStats::addCylinder()
{
#pragma omp atomic
this->cylinderCount++;
};
void RenderStats::addCube()
{
#pragma omp atomic
this->cubeCount++;
};
void RenderStats::addGroup()
{
#pragma omp atomic
this->groupCount++;
};
void RenderStats::addLight()
{
#pragma omp atomic
this->lightCount++;
};
void RenderStats::addPlane()
{
#pragma omp atomic
this->planeCount++;
};
void RenderStats::addSphere()
{
#pragma omp atomic
this->sphereCount++;
};
void RenderStats::addTriangle()
{
#pragma omp atomic
this->triangleCount++;
};
void RenderStats::addSmoothTriangle()
{
#pragma omp atomic
this->smoothTriangleCount++;
};
void RenderStats::addOBJFile()
{
#pragma omp atomic
this->objfileCount++;
};
void RenderStats::addPixel()
{
#pragma omp atomic
this->pixelCount++;
};
void RenderStats::addRay()
{
#pragma omp atomic
this->rayCount++;
};
void RenderStats::addCastedRay()
{
#pragma omp atomic
this->rayCasted++;
};
void RenderStats::addLightRay()
{
this->addCastedRay();
#pragma omp atomic
this->lightRayEmitedCount++;
};
void RenderStats::addReflectRay()
{
this->addCastedRay();
#pragma omp atomic
this->reflectionRayCount++;
};
void RenderStats::addRefractRay()
{
this->addCastedRay();
#pragma omp atomic
this->refractedRayCount++;
};
void RenderStats::addIntersect()
{
#pragma omp atomic
this->intersectCount++;
};
void RenderStats::addIntersection()
{
#pragma omp atomic
this->intersectionCount++;
};
void RenderStats::addMalloc()
{
#pragma omp atomic
this->mallocCallCount++;
};
void RenderStats::addRealloc()
{
#pragma omp atomic
this->reallocCallCount++;
};
void RenderStats::addDiscardedIntersect()
{
#pragma omp atomic
this->discardedIntersectCount++;
};
void RenderStats::addCsg()
{
#pragma omp atomic
this->csgCount++;
};
void RenderStats::setMaxDepth(uint32_t depth)
{
if (this->maxDepthAttained > depth)
{
this->maxDepthAttained = depth;
}
};
void RenderStats::setMaxIntersect(uint32_t count)
{
if (this->maxIntersectOnARay < count)
{
this->maxIntersectOnARay = count;
}
};
void RenderStats::printStats()
{
printf("Rendering statistics:\n");
printf("Cones : %lld\n", this->coneCount);
printf("Cubes : %lld\n", this->cubeCount);
printf("Cylinders : %lld\n", this->cylinderCount);
printf("Groups : %lld\n", this->groupCount);
printf("Lights : %lld\n", this->lightCount);
printf("Planes : %lld\n", this->planeCount);
printf("Spheres : %lld\n", this->sphereCount);
printf("Triangles : %lld\n", this->triangleCount);
printf("Smooth Triangles : %lld\n", this->smoothTriangleCount);
printf("OBJ File : %lld\n", this->objfileCount);
printf("CSG : %lld\n", this->csgCount);
printf("==================================================\n");
printf("Pixel rendered : %lld\n", this->pixelCount);
printf("Ray created : %lld\n", this->rayCount);
printf("Ray casted : %lld\n", this->rayCasted);
printf("Light Ray casted : %lld\n", this->lightRayEmitedCount);
printf("Reflection ray casted : %lld\n", this->reflectionRayCount);
printf("Refraction ray casted : %lld\n", this->refractedRayCount);
printf("Intersect object created: %lld\n", this->intersectCount);
printf("Intersection created : %lld\n", this->intersectionCount);
printf("Malloc called : %lld\n", this->mallocCallCount);
printf("Realloc called : %lld\n", this->reallocCallCount);
printf("Bounding box missed : %lld\n", this->discardedIntersectCount);
printf("Min depth attained : %lld\n", this->maxDepthAttained);
printf("Max intersect count : %lld\n", this->maxIntersectOnARay);
printf("==================================================\n");
};
#endif

33
source/sequence.cpp Normal file
View File

@@ -0,0 +1,33 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Sequence implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <sequence.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <math_helper.h>
Sequence::Sequence() : list(nullptr), listPos(0), listSize(0) {
/* Need to bootstrap rand here */
srand(time(NULL));
}
Sequence::Sequence(double *list, uint32_t listSize) : list(list), listPos(0), listSize(listSize) { };
double Sequence::next() {
if (this->listSize == 0)
{
return frand();
}
else
{
uint32_t pos = this->listPos;
this->listPos = (this->listPos + 1) % this->listSize;
return this->list[pos];
}
}

146
source/shapes/cone.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cone implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <cone.h>
#include <math_helper.h>
bool Cone::checkCap(Ray r, double t, double y)
{
/* Helping function to reduce duplication.
* Checks to see if the intersection ot t is within a radius
* of 1 (the radius of our Cone from the y axis
*/
double x = r.origin.x + t * r.direction.x;
double z = r.origin.z + t * r.direction.z;
return (x * x + z * z) <= fabs(y);
}
void Cone::intersectCaps(Ray r, Intersect &xs)
{
/* Caps only mattter is the Cone is closed, and might possibly be
* intersected by the ray
*/
if ((this->isClosed) && (fabs(r.direction.y) > getEpsilon()))
{
double t;
/* Check for an intersection with the lower end cap by intersecting
* the ray with the plan at y = this->minCap
*/
t = (this->minCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t, this->minCap))
{
xs.add(Intersection(t, this));
}
/* Check for an intersection with the upper end cap by intersecting
* the ray with the plan at y = this->maxCap
*/
t = (this->maxCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t, this->maxCap))
{
xs.add(Intersection(t, this));
}
}
}
void Cone::localIntersect(Ray r, Intersect &xs)
{
double A = (r.direction.x * r.direction.x) -
(r.direction.y * r.direction.y) +
(r.direction.z * r.direction.z);
double B = (2 * r.origin.x * r.direction.x) -
(2 * r.origin.y * r.direction.y) +
(2 * r.origin.z * r.direction.z);
double C = (r.origin.x * r.origin.x) -
(r.origin.y * r.origin.y) +
(r.origin.z * r.origin.z);
if ((fabs(A) <= getEpsilon()) && (fabs(B) >= getEpsilon()))
{
double t = -C / (2*B);
xs.add(Intersection(t, this));
}
else if (fabs(A) >= getEpsilon())
{
double disc = (B * B) - 4 * A * C;
if (disc >= 0)
{
double t0 = (-B - sqrt(disc)) / (2 * A);
double t1 = (-B + sqrt(disc)) / (2 * A);
double y0 = r.origin.y + t0 * r.direction.y;
if ((this->minCap < y0) && (y0 < this->maxCap))
{
xs.add(Intersection(t0, this));
}
double y1 = r.origin.y + t1 * r.direction.y;
if ((this->minCap < y1) && (y1 < this->maxCap))
{
xs.add(Intersection(t1, this));
}
}
}
this->intersectCaps(r, xs);
}
Tuple Cone::localNormalAt(Tuple point, Intersection *hit)
{
/* Compute the square of the distance from the Y axis */
double dist = point.x * point.x + point.z * point.z;
if ((dist < 1) && (point.y >= (this->maxCap - getEpsilon())))
{
return Vector(0, 1, 0);
}
if ((dist < 1) && (point.y <= this->minCap + getEpsilon()))
{
return Vector(0, -1, 0);
}
double y = sqrt(point.x * point.x + point.z * point.z);
if (point.y > 0)
{
y = -y;
}
return Vector(point.x, y, point.z);
}
BoundingBox Cone::getLocalBounds()
{
BoundingBox ret;
double a = fabs(this->minCap);
double b = fabs(this->maxCap);
double limit = (a > b)?a:b;
ret | Point(-limit, this->minCap, -limit);
ret | Point(limit, this->maxCap, limit);
return ret;
}
void Cone::dumpMe(FILE *fp)
{
fprintf(fp, "\"Type\": \"Cylinder\",\n");
Tuple t = this->transformMatrix * Point(0, 0, 0);
fprintf(fp, "\"pseudocenter\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->transformMatrix * Point(0, this->minCap, 0);
fprintf(fp, "\"min\": %f, \n", t.y);
t = this->transformMatrix * Point(1, this->maxCap, 1);
fprintf(fp, "\"max\": %f, \n", t.y);
Shape::dumpMe(fp);
}

146
source/shapes/csg.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Constructive Solid Geometry (CSG) implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <csg.h>
#include <math_helper.h>
CSG::CSG(OperationType operation, Shape *left, Shape *right) : Shape(Shape::CSG), operation(operation), left(left), right(right)
{
stats.addCsg();
this->left->setParent(this);
this->right->setParent(this);
this->bounds | this->left->getBounds();
this->bounds | this->right->getBounds();
}
void CSG::localIntersect(Ray r, Intersect &xs)
{
this->intersect(r, xs);
}
void CSG::intersect(Ray &r, Intersect &xs)
{
int i;
Intersect tmp = Intersect();
if (this->bounds.intesectMe(r))
{
this->left->intersect(r, tmp);
this->right->intersect(r, tmp);
this->filterIntersections(tmp, xs);
}
}
Tuple CSG::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(1, 0, 0);
}
BoundingBox CSG::getLocalBounds()
{
return this->bounds;
}
BoundingBox CSG::getBounds()
{
if (this->bounds.isEmpty()) { this->updateBoundingBox(); }
return this->bounds;
}
void CSG::updateBoundingBox()
{
this->bounds.reset();
this->bounds | this->left->getBounds();
this->bounds | this->right->getBounds();
}
void CSG::updateTransform()
{
Shape::updateTransform();
this->left->updateTransform();
this->right->updateTransform();
/* Once the full stack being notified of the changes, let's update the
* bounding box
*/
this->updateBoundingBox();
}
bool CSG::includes(Shape *b)
{
if (this->left->includes(b)) { return true; }
if (this->right->includes(b)) { return true; }
if (this == b) { return true; }
return false;
}
bool CSG::intersectionAllowed(bool leftHit, bool inLeft, bool inRight)
{
switch(this->operation)
{
case CSG::UNION: return (leftHit && !inRight) || (!leftHit && !inLeft);
case CSG::INTERSECTION: return (!leftHit && inLeft) || (leftHit && inRight);
case CSG::DIFFERENCE: return (leftHit && !inRight) || (!leftHit && inLeft);
}
return false;
}
void CSG::filterIntersections(Intersect &xs, Intersect &ret)
{
bool inl = false;
bool inr = false;
int i;
for(i = 0; i < xs.count(); i++)
{
bool lhit = this->left->includes(xs[i].object);
if (this->intersectionAllowed(lhit, inl, inr))
{
ret.add(xs[i]);
}
if (lhit)
{
inl = !inl;
}
else
{
inr = !inr;
}
}
}
void CSG::lock()
{
Shape::lock();
if(this->left)
{
this->left->lock();
}
if(this->right)
{
this->right->lock();
}
}
void CSG::dumpMe(FILE *fp)
{
}

84
source/shapes/cube.cpp Normal file
View File

@@ -0,0 +1,84 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cube implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <cube.h>
#include <math_helper.h>
void Cube::checkAxis(double axeOrigin, double axeDirection, double *axeMin, double *axeMax)
{
double tMinNumerator = (-1 - axeOrigin);
double tMaxNumerator = (1 - axeOrigin);
if (fabs(axeDirection) >= getEpsilon())
{
*axeMin = tMinNumerator / axeDirection;
*axeMax = tMaxNumerator / axeDirection;
}
else
{
*axeMin = tMinNumerator * INFINITY;
*axeMax = tMaxNumerator * INFINITY;
}
if (*axeMin > *axeMax)
{
double swap = *axeMax;
*axeMax = *axeMin;
*axeMin = swap;
}
}
void Cube::localIntersect(Ray r, Intersect &xs)
{
double xtMin, xtMax, ytMin, ytMax, ztMin, ztMax;
double tMin, tMax;
this->checkAxis(r.origin.x, r.direction.x, &xtMin, &xtMax);
this->checkAxis(r.origin.y, r.direction.y, &ytMin, &ytMax);
this->checkAxis(r.origin.z, r.direction.z, &ztMin, &ztMax);
tMin = max3(xtMin, ytMin, ztMin);
tMax = min3(xtMax, ytMax, ztMax);
if (tMin <= tMax)
{
xs.add(Intersection(tMin, this));
xs.add(Intersection(tMax, this));
}
}
Tuple Cube::localNormalAt(Tuple point, Intersection *hit)
{
double maxC = max3(fabs(point.x), fabs(point.y), fabs(point.z));
if (maxC == fabs(point.x))
{
return Vector(point.x, 0, 0);
}
else if (maxC == fabs(point.y))
{
return Vector(0, point.y, 0);
}
return Vector(0, 0, point.z);
}
void Cube::dumpMe(FILE *fp)
{
fprintf(fp, "\"Type\": \"Cube\",\n");
Tuple t = this->transformMatrix * Point(0, 0, 0);
fprintf(fp, "\"center\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->transformMatrix * Point(1, 1, 1);
fprintf(fp, "\"corner\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
Shape::dumpMe(fp);
}

128
source/shapes/cylinder.cpp Normal file
View File

@@ -0,0 +1,128 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cylinder implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <cylinder.h>
#include <math_helper.h>
bool Cylinder::checkCap(Ray r, double t)
{
/* Helping function to reduce duplication.
* Checks to see if the intersection ot t is within a radius
* of 1 (the radius of our cylinder from the y axis
*/
double x = r.origin.x + t * r.direction.x;
double z = r.origin.z + t * r.direction.z;
return (x * x + z * z) <= 1;
}
void Cylinder::intersectCaps(Ray r, Intersect &xs)
{
/* Caps only matter if the cylinder is closed, and might possibly be
* intersected by the ray
*/
if ((this->isClosed) && (fabs(r.direction.y) > getEpsilon()))
{
double t;
/* Check for an intersection with the lower end cap by intersecting
* the ray with the plan at y = this->minCap
*/
t = (this->minCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t))
{
xs.add(Intersection(t, this));
}
/* Check for an intersection with the upper end cap by intersecting
* the ray with the plan at y = this->maxCap
*/
t = (this->maxCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t))
{
xs.add(Intersection(t, this));
}
}
}
void Cylinder::localIntersect(Ray r, Intersect &xs)
{
double A = r.direction.x * r.direction.x + r.direction.z * r.direction.z;
/* Ray is parallel to the Y axis */
if (A >= getEpsilon())
{
double B = 2 * r.origin.x * r.direction.x +
2 * r.origin.z * r.direction.z;
double C = r.origin.x * r.origin.x + r.origin.z * r.origin.z - 1;
double disc = B * B - 4 * A * C;
if (disc >= 0)
{
double t0 = (-B - sqrt(disc)) / (2 * A);
double t1 = (-B + sqrt(disc)) / (2 * A);
double y0 = r.origin.y + t0 * r.direction.y;
if ((this->minCap < y0) && (y0 < this->maxCap))
{
xs.add(Intersection(t0, this));
}
double y1 = r.origin.y + t1 * r.direction.y;
if ((this->minCap < y1) && (y1 < this->maxCap))
{
xs.add(Intersection(t1, this));
}
}
}
this->intersectCaps(r, xs);
}
Tuple Cylinder::localNormalAt(Tuple point, Intersection *hit)
{
/* Compute the square of the distance from the Y axis */
double dist = point.x * point.x + point.z * point.z;
if ((dist < 1) && (point.y >= (this->maxCap - getEpsilon())))
{
return Vector(0, 1, 0);
}
if ((dist < 1) && (point.y <= this->minCap + getEpsilon()))
{
return Vector(0, -1, 0);
}
return Vector(point.x, 0, point.z);
}
BoundingBox Cylinder::getLocalBounds()
{
BoundingBox ret;
ret | Point(-1, this->minCap, -1);
ret | Point(1, this->maxCap, 1);
return ret;
}
void Cylinder::dumpMe(FILE *fp)
{
fprintf(fp, "\"Type\": \"Cylinder\",\n");
Tuple t = this->transformMatrix * Point(0, 0, 0);
fprintf(fp, "\"pseudocenter\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->transformMatrix * Point(0, this->minCap, 0);
fprintf(fp, "\"min\": %f, \n", t.y);
t = this->transformMatrix * Point(1, this->maxCap, 1);
fprintf(fp, "\"max\": %f, \n", t.y);
Shape::dumpMe(fp);
}

279
source/shapes/group.cpp Normal file
View File

@@ -0,0 +1,279 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Group implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <group.h>
#include <math_helper.h>
#include <renderstat.h>
#include <string.h>
#define MIN_ALLOC (2)
Group::Group(const char *name) : Shape(Shape::GROUP)
{
stats.addGroup();
this->allocatedObjectCount = MIN_ALLOC;
this->objectList = (Shape **)calloc(sizeof(Shape **), MIN_ALLOC);
this->objectCount = 0;
this->allocatedUnboxableObjectCount = MIN_ALLOC;
this->unboxableObjectList = (Shape **)calloc(sizeof(Shape **), MIN_ALLOC);
this->unboxableObjectCount = 0;
if (name != nullptr)
{
strncpy(this->name, name, 32);
}
else
{
strncpy(this->name, "untitled", 32);
}
}
void Group::intersect(Ray &r, Intersect &xs)
{
int i, j;
if (this->objectCount > 0)
{
if (this->bounds.intesectMe(r))
{
for (i = 0 ; i < this->objectCount ; i++)
{
this->objectList[i]->intersect(r, xs);
}
}
}
/* We are force to do them all the time */
if (this->unboxableObjectCount > 0)
{
for(i = 0; i < this->unboxableObjectCount; i++)
{
this->unboxableObjectList[i]->intersect(r, xs);
}
}
}
bool Group::includes(Shape *b)
{
if (this->objectCount > 0)
{
int i;
for (i = 0 ; i < this->objectCount ; i++)
{
if (this->objectList[i] == b)
{
return true;
}
}
}
if (this->unboxableObjectCount > 0)
{
int i;
for(i = 0; i < this->unboxableObjectCount; i++)
{
if (this->unboxableObjectList[i] == b)
{
return true;
}
}
}
return false;
}
void Group::localIntersect(Ray r, Intersect &xs)
{
this->intersect(r, xs);
}
Tuple Group::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(1, 0, 0);
}
/* ONLY INSERT SHAPES THAT ARE NOT GOING TO CHANGE ELSE..! */
void Group::addObject(Shape *s)
{
if (s->haveFiniteBounds())
{
if ((this->objectCount + 1) > this->allocatedObjectCount)
{
this->allocatedObjectCount *= 2;
this->objectList = (Shape **)realloc(this->objectList, sizeof(Shape **) * this->allocatedObjectCount);
}
s->setParent(this);
s->updateTransform();
this->objectList[this->objectCount++] = s;
this->bounds | s->getBounds();
}
else
{
if ((this->unboxableObjectCount + 1) > this->allocatedUnboxableObjectCount)
{
this->allocatedUnboxableObjectCount *= 2;
this->unboxableObjectList = (Shape **)realloc(this->unboxableObjectList, sizeof(Shape **) * this->allocatedUnboxableObjectCount);
}
s->setParent(this);
s->updateTransform();
this->unboxableObjectList[this->unboxableObjectCount++] = s;
}
}
void Group::removeObject(Shape *s)
{
int i;
if (s->haveFiniteBounds())
{
for (i = 0; i < this->objectCount; i++)
{
if (this->objectList[i] == s)
{
this->objectCount --;
this->objectList[i] = this->objectList[this->objectCount];
this->objectList[this->objectCount] = nullptr;
break;
}
}
}
else
{
for (i = 0; i < this->unboxableObjectCount; i++)
{
if (this->unboxableObjectList[i] == s)
{
this->unboxableObjectCount --;
this->unboxableObjectList[i] = this->unboxableObjectList[this->unboxableObjectCount];
this->unboxableObjectList[this->unboxableObjectCount] = nullptr;
break;
}
}
}
}
bool Group::isEmpty()
{
return (this->objectCount == 0) && (this->unboxableObjectCount == 0);
}
BoundingBox Group::getLocalBounds()
{
return this->bounds;
}
BoundingBox Group::getBounds()
{
if (this->bounds.isEmpty()) { this->updateBoundingBox(); }
return this->bounds;
}
void Group::updateBoundingBox()
{
this->bounds.reset();
if (this->objectCount > 0)
{
int i;
for(i = 0; i < this->objectCount; i++)
{
if (this->objectList[i]->haveFiniteBounds())
{
BoundingBox objB = this->objectList[i]->getBounds();
this->bounds | objB;
}
}
}
}
void Group::updateTransform()
{
int i;
Shape::updateTransform();
if (this->objectCount > 0)
{
for (i = 0 ; i < this->objectCount ; i++)
{
this->objectList[i]->updateTransform();
}
}
if (this->unboxableObjectCount > 0)
{
for(i = 0; i < this->unboxableObjectCount; i++)
{
this->unboxableObjectList[i]->updateTransform();
}
}
/* Once the full stack being notified of the changes, let's update the
* bounding box
*/
this->updateBoundingBox();
}
void Group::dumpMe(FILE *fp)
{
int i;
fprintf(fp, "\"Type\": \"Group\",\n");
fprintf(fp, "\"Name\": \"%s\",\n", this->name);
if (this->objectCount > 0)
{
fprintf(fp, "\"Objects\": {\n");
for(i = 0; i < this->objectCount; i++)
{
fprintf(fp, "\"%d\": {\n", i);
this->objectList[i]->dumpMe(fp);
fprintf(fp, "},\n");
}
fprintf(fp, "},\n");
}
if (this->unboxableObjectCount > 0)
{
fprintf(fp, "\"UnboxableObjects\": {\n");
for(i = 0; i < this->objectCount; i++)
{
fprintf(fp, "\"%d\": {\n", i);
this->objectList[i]->dumpMe(fp);
fprintf(fp, "},\n");
}
fprintf(fp, "},\n");
}
Shape::dumpMe(fp);
}
void Group::lock()
{
Shape::lock();
/* Now notify included object they have to lock */
int i;
if (this->objectCount > 0)
{
for (i = 0 ; i < this->objectCount ; i++)
{
this->objectList[i]->lock();
}
}
if (this->unboxableObjectCount > 0)
{
for(i = 0; i < this->unboxableObjectCount; i++)
{
this->unboxableObjectList[i]->lock();
}
}
}

View File

@@ -6,3 +6,51 @@
* Copyright (c) 2020 986-Studio.
*
*/
#include <stdio.h>
#include <light.h>
#include <world.h>
void Light::dumpMe(FILE *fp)
{
fprintf(fp, "\"Colour\": {\"red\": %f, \"green\": %f, \"blue\": %f},\n",
this->intensity.x, this->intensity.y, this->intensity.z);
fprintf(fp, "\"Position\": {\"x\": %f, \"y\": %f, \"z\":%f},\n",
this->position.x, this->position.y, this->position.z);
fprintf(fp, "\"Type\": \"PointLight\",\n");
}
double Light::intensityAt(World &w, Tuple point)
{
switch(this->type)
{
case POINT_LIGHT:
default:
return (w.isShadowed(point, this->position))?0.0:1.0;
case AREA_LIGHT:
double total = 0.0;
uint32_t v, u;
for(v = 0; v < this->vSteps; v++)
{
for(u = 0; u < this->uSteps; u++)
{
if (!w.isShadowed(point, this->pointOnLight(u, v)))
{
total = total + 1.0;
}
}
}
return total / this->samples;
}
}
Tuple Light::pointOnLight(uint32_t u, uint32_t v)
{
if (this->jitter)
{
return this->corner +
this->uVec * (u + this->jitterBy.next()) +
this->vVec * (v + this->jitterBy.next());
}
return this->corner + this->uVec * (u + 0.5) + this->vVec * (v + 0.5);
}

View File

@@ -11,7 +11,8 @@
#include <colour.h>
#include <shape.h>
Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow)
Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject,
double lightIntensity)
{
Colour pointColor = this->colour;
@@ -25,6 +26,7 @@ Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple norma
Tuple effectiveColour = pointColor * light.intensity;
Tuple ambientColour = Colour(0, 0, 0);
Tuple emissiveColour = Colour(0, 0, 0);
Tuple diffuseColour = Colour(0, 0, 0);
Tuple specularColour = Colour(0, 0, 0);
Tuple finalColour = Colour(0, 0, 0);
@@ -33,34 +35,56 @@ Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple norma
ambientColour = effectiveColour * this->ambient;
if (!inShadow)
{
lightDotNormal = lightVector.dot(normalVector);
emissiveColour = pointColor * this->emissive;
if (lightDotNormal < 0)
lightDotNormal = lightVector.dot(normalVector);
if (lightDotNormal < 0)
{
diffuseColour = Colour(0, 0, 0);
specularColour = Colour(0, 0, 0);
}
else
{
diffuseColour = effectiveColour * this->diffuse * lightDotNormal;
reflectVector = -lightVector.reflect(normalVector);
reflectDotEye = reflectVector.dot(eyeVector);
if (reflectDotEye < 0)
{
diffuseColour = Colour(0, 0, 0);
specularColour = Colour(0, 0, 0);
}
else
{
diffuseColour = effectiveColour * this->diffuse * lightDotNormal;
reflectVector = -lightVector.reflect(normalVector);
reflectDotEye = reflectVector.dot(eyeVector);
if (reflectDotEye < 0)
{
specularColour = Colour(0, 0, 0);
}
else
{
double factor = pow(reflectDotEye, this->shininess);
specularColour = light.intensity * this->specular * factor;
}
double factor = pow(reflectDotEye, this->shininess);
specularColour = light.intensity * this->specular * factor;
}
}
finalColour = ambientColour + diffuseColour + specularColour;
diffuseColour = diffuseColour * lightIntensity;
specularColour = specularColour * lightIntensity;
finalColour = emissiveColour + ambientColour + diffuseColour + specularColour;
return Colour(finalColour.x, finalColour.y, finalColour.z);
}
void Material::dumpMe(FILE *fp)
{
fprintf(fp, "\"Colour\": {\"red\": %f, \"green\": %f, \"blue\": %f},\n", this->colour.x, this->colour.y, this->colour.z);
fprintf(fp, "\"Ambient\": %f,\n", this->ambient);
fprintf(fp, "\"Diffuse\": %f,\n", this->diffuse);
fprintf(fp, "\"Specular\": %f,\n", this->specular);
fprintf(fp, "\"Shininess\": %f,\n", this->shininess);
fprintf(fp, "\"Reflective\": %f,\n", this->reflective);
fprintf(fp, "\"Transparency\": %f,\n", this->transparency);
fprintf(fp, "\"Emissive\": %f,\n", this->emissive);
fprintf(fp, "\"RefractiveIndex\": %f,\n", this->refractiveIndex);
if (this->pattern)
{
fprintf(fp, "\"Pattern\": {\n");
this->pattern->dumpMe(fp);
fprintf(fp, "},\n");
}
}

558
source/shapes/objfile.cpp Normal file
View File

@@ -0,0 +1,558 @@
/*
* DoRayMe - a quick and dirty Raytracer
* OBJ File implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <objfile.h>
#include <math_helper.h>
#include <group.h>
#include <triangle.h>
#include <sphere.h>
#include <smoothtriangle.h>
#include <transformation.h>
#include <cone.h>
#include <cylinder.h>
#define MIN_ALLOC (2)
//#define DEBUG_NORMAL
OBJFile::OBJFile() : Shape(Shape::OBJFILE), ignoredLines(0)
{
stats.addOBJFile();
this->allocatedVertexCount = MIN_ALLOC;
this->vertexList = (Point **)calloc(sizeof(Point **), MIN_ALLOC);
this->vertexCount = 0;
this->allocatedVertexNormalCount = MIN_ALLOC;
this->vertexNormalList = (Vector **)calloc(sizeof(Vector **), MIN_ALLOC);
this->vertexNormalCount = 0;
/* The base group */
this->baseGroup = new Group(OBJ_DEFAULT_GROUP);
this->currentGroup = this->baseGroup;
this->baseGroup->parent = this;
};
OBJFile::~OBJFile()
{
int i;
if (vertexCount > 0)
{
for(i = 0; i < vertexCount; i++)
{
delete this->vertexList[i];
this->vertexList[i] = nullptr;
}
}
free(this->vertexList);
this->vertexList = nullptr;
if (vertexNormalCount > 0)
{
for(i = 0; i < vertexNormalCount; i++)
{
delete this->vertexNormalList[i];
this->vertexNormalList[i] = nullptr;
}
}
free(this->vertexNormalList);
this->vertexNormalList = nullptr;
/* It is not our responsibility to clear the group object as this object may be destroyed before the
* render is done
*/
}
OBJFile::OBJFile(const char *filepath) : OBJFile()
{
FILE *fp;
size_t fileSize;
char *fileBuff;
fp = fopen(filepath, "rt");
if (fp)
{
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
/* Add one byte to the size to make sure it is null terminated */
fileBuff = (char *)calloc(fileSize + 1, 1);
fseek(fp, 0, SEEK_SET);
fileSize = fread(fileBuff, 1, fileSize, fp);
fclose(fp);
this->parseOBJFile(fileBuff);
free(fileBuff);
}
else
{
printf("ERROR: Can't open/find the file '%s'.\n", filepath);
}
}
void OBJFile::addGroup(Group *group)
{
this->baseGroup->addObject(group);
group->setParent(this);
group->updateTransform();
this->bounds | group->getBounds();
this->currentGroup = group;
}
void OBJFile::addVertex(Point *vertex)
{
if ((this->vertexCount + 1) > this->allocatedVertexCount)
{
this->allocatedVertexCount *= 2;
this->vertexList = (Point **)realloc(this->vertexList, sizeof(Point **) * this->allocatedVertexCount);
}
this->vertexList[this->vertexCount++] = vertex;
}
void OBJFile::addVertexNormal(Vector *vertexNormal)
{
if ((this->vertexNormalCount + 1) > this->allocatedVertexNormalCount)
{
this->allocatedVertexNormalCount *= 2;
this->vertexNormalList = (Vector **)realloc(this->vertexNormalList, sizeof(Vector **) * this->allocatedVertexNormalCount);
}
this->vertexNormalList[this->vertexNormalCount++] = vertexNormal;
}
Group *OBJFile::groups(const char *groupName)
{
if (strncmp(groupName, this->baseGroup->getName(), strlen(groupName)) == 0)
{
return this->baseGroup;
}
int i;
for(i = 0; i < this->baseGroup->getObjectCount(); i++)
{
Shape *cur = (*this->baseGroup)[i];
if (cur->getType() == Shape::GROUP)
{
Group *curGrp = (Group *)cur;
if (strncmp(groupName, curGrp->getName(), strlen(groupName)) == 0)
{
return curGrp;
}
}
}
/* Not found */
return nullptr;
}
void OBJFile::intersect(Ray &r, Intersect &xs)
{
this->baseGroup->intersect(r, xs);
}
bool OBJFile::includes(Shape *b)
{
return this->baseGroup->includes(b);
}
void OBJFile::localIntersect(Ray r, Intersect &xs)
{
this->intersect(r, xs);
}
Tuple OBJFile::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(0, 1, 0);
}
BoundingBox OBJFile::getLocalBounds()
{
return this->bounds;
}
BoundingBox OBJFile::getBounds()
{
if (this->bounds.isEmpty()) { this->updateBoundingBox(); }
return this->bounds;
}
void OBJFile::updateBoundingBox()
{
int i;
this->bounds.reset();
this->bounds | this->baseGroup->getBounds();
}
void OBJFile::updateTransform()
{
int i;
Shape::updateTransform();
this->baseGroup->updateTransform();
/* Once the full stack being notified of the changes, let's update the
* bounding box
*/
this->updateBoundingBox();
}
void OBJFile::dumpMe(FILE * fp)
{
int i;
fprintf(fp, "\"Type\": \"OBJFile\",\n");
fprintf(fp, "\"Objects\": {\n");
this->baseGroup->dumpMe(fp);
fprintf(fp, "},\n");
fprintf(fp, "\"Vertices\": {\n");
for(i = 1; i < this->vertexCount + 1; i++)
{
fprintf(fp, "\"v[%d]\": { \"x\": %f, \"y\": %f, \"z\": %f },\n", i,
this->vertices(i).x, this->vertices(i).y, this->vertices(i).z);
}
fprintf(fp, "},\n");
fprintf(fp, "\"NormalVertices\": {\n");
for(i = 1; i < this->vertexNormalCount + 1; i++)
{
fprintf(fp, "\"vn[%d]\": { \"x\": %f, \"y\": %f, \"z\": %f },\n", i,
this->verticesNormal(i).x, this->verticesNormal(i).y, this->verticesNormal(i).z);
}
fprintf(fp, "},\n");
Shape::dumpMe(fp);
}
#define MAX_LINE_LENGTH (512)
/* Here start the fun! */
int OBJFile::parseOBJFile(const char *content)
{
/* I don't think we will handle lines of more than 512 characters... */
char lineBuff[MAX_LINE_LENGTH];
uint32_t currentLineNum = 1;
uint32_t totalLength = strlen(content);
/* Need to process line by line */
const char *bufferPos = content;
const char *lineNewline;
while(*bufferPos != '\0')
{
uint32_t lineLength;
lineNewline = strchr(bufferPos, '\n');
if (lineNewline == nullptr)
{
/* We are on the last line */
lineLength = strlen(bufferPos);
}
else
{
lineLength = (lineNewline - bufferPos);
}
if (lineLength >= MAX_LINE_LENGTH)
{
printf("ERROR: Line %d is too long! (%d)\n", currentLineNum, lineLength);
return -1;
}
memset(lineBuff, 0, MAX_LINE_LENGTH);
strncpy(lineBuff, bufferPos, lineLength);
this->parseLine(lineBuff, currentLineNum);
bufferPos += lineLength + 1;
if ((bufferPos - content) >= totalLength)
{
/* We are past the length of the buffer, don't need to continue */
break;
}
currentLineNum++;
}
return 0;
}
#define MAX_ARGS (15)
/* Parse the line into a couple ofr argc/argv using space as argument separator */
void OBJFile::parseLine(char *line, uint32_t currentLine)
{
char *argv[MAX_ARGS];
uint32_t argc = 0;
char *buffer = line;
uint32_t lineLength = strlen(line);
uint32_t linePos = 0;
/* First argument */
argv[argc++] = line;
while(linePos < lineLength)
{
char *next = strchr(buffer, ' ');
if (next != nullptr)
{
*next = '\0';
linePos = next - line;
buffer = next + 1;
/* Skip empty strings as it mean multiple spaces */
if (strlen(buffer) > 0)
{
argv[argc++] = buffer;
}
}
else
{
linePos = lineLength;
}
}
if (this->execLine(argc, argv, currentLine))
{
this->ignoredLines++;
}
}
static int parseFaceVertex(char *buf, uint32_t &v, uint32_t &vt, uint32_t &vn)
{
uint32_t bufPos = 0;
uint32_t lineLength = strlen(buf);
vt = INT32_MAX;
vn = INT32_MAX;
int ret = 0;
int token = 0;
while(bufPos < lineLength)
{
char *next = strchr(buf, '/');
if (next != nullptr)
{
*next = '\0';
bufPos = next - buf;
}
else
{
bufPos = lineLength;
}
if (strlen(buf) > 0)
{
switch(token)
{
case 0: v = atol(buf); break;
case 1: vt = atol(buf); break;
case 2: vn = atol(buf); break;
default: printf("ERROR: Too many entry for a face vertice!"); ret = 1;
}
}
buf = next + 1;
token++;
}
return ret;
}
#ifdef DEBUG_NORMAL
Shape *makeVector(Point pos, Vector verNorm, Colour c, double scale = 1)
{
Group *ret = new Group("Vector");
Sphere *sp = new Sphere();
Colour c2 = c;
sp->material.colour = c2;
sp->material.ambient = 1;
sp->material.refractiveIndex = 0;
sp->material.reflective = 0;
sp->material.specular = 0;
sp->materialSet = true;
sp->setTransform(translation(pos.x, pos.y, pos.z) * scaling(0.1, 0.1, 0.1));
ret->addObject(sp);
double theta = atan2(verNorm.x, verNorm.z);
double radius = verNorm.magnitude();
double phi = acos(verNorm.y / radius);
sp = new Sphere();
c2 = c;
c2.x /=3; c2.y /=3; c2.y /=3;
sp->material.colour = c2;
sp->material.ambient = 1;
sp->material.refractiveIndex = 0;
sp->material.transparency = 0;
sp->material.specular = 0;
sp->materialSet = true;
sp->setTransform(translation(pos.x, pos.y, pos.z) * translation(verNorm.x, verNorm.y, verNorm.z) * scaling(0.1, 0.1, 0.1));
ret->addObject(sp);
c2 = c;
c2.x /=2; c2.y /=3; c2.y /=2;
Cone *cn = new Cone();
cn->minCap = 0;
cn->maxCap = 1;
cn->material.colour = c2;
cn->material.ambient = 1;
cn->material.refractiveIndex = 0;
cn->material.reflective = 0;
cn->material.specular = 0;
cn->materialSet = true;
cn->setTransform(translation(pos.x, pos.y, pos.z) * rotationY(theta) * rotationX(phi) * scaling(0.1, radius, 0.1));
ret->addObject(cn);
return ret;
}
#endif
/* Actually execute the line */
int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
{
int ret = 1;
if (strncmp(argv[0], "v", 2) == 0)
{
/* Vertice entry */
if (argc != 4)
{
printf("ERROR: Malformed file at line %d: Vertices expect 3 parameters!\n", currentLine);
}
else
{
this->addVertex(new Point(atof(argv[1]), atof(argv[2]), atof(argv[3])));
ret = 0;
}
}
else if (strncmp(argv[0], "vn", 3) == 0)
{
/* Vertice Normal entry */
if (argc != 4)
{
printf("ERROR: Malformed file at line %d: Vertices normal expect 3 parameters!\n", currentLine);
}
else
{
this->addVertexNormal(new Vector(atof(argv[1]), atof(argv[2]), atof(argv[3])));
ret = 0;
}
}
else if (strncmp(argv[0], "f", 2) == 0)
{
/* Faces entry */
int i;
uint32_t v[MAX_ARGS], vt[MAX_ARGS], vn[MAX_ARGS];
for(i = 1; i < argc; i++)
{
parseFaceVertex(argv[i], v[i], vt[i], vn[i]);
}
if (argc == 4)
{
Shape *t;
if (vn[1] == INT32_MAX)
{
t = new Triangle(this->vertices(v[1]),
this->vertices(v[2]),
this->vertices(v[3]));
}
else
{
#ifdef DEBUG_NORMAL
this->currentGroup->addObject(makeVector(this->vertices(v[1]),
this->verticesNormal(vn[1]),
Colour(1, 0, 1)));
this->currentGroup->addObject(makeVector(this->vertices(v[2]),
this->verticesNormal(vn[2]),
Colour(0.5, 0, 0.5)));
this->currentGroup->addObject(makeVector(this->vertices(v[3]),
this->verticesNormal(vn[3]),
Colour(0.5, 0, 1)));
#endif
t = new SmoothTriangle(this->vertices(v[1]),
this->vertices(v[2]),
this->vertices(v[3]),
this->verticesNormal(vn[1]),
this->verticesNormal(vn[2]),
this->verticesNormal(vn[3]));
}
/* Set the object id to the OBJ one */
t->setObjectId(this->getObjectId());
this->currentGroup->addObject(t);
ret = 0;
}
else if (argc > 4)
{
#ifdef DEBUG_NORMAL
if (vn[1] != INT32_MAX)
{
for(i = 2; i < (argc); i++)
{
this->currentGroup->addObject(makeVector(this->vertices(v[i]),
this->verticesNormal(vn[i]),
Colour(1, 1, 0)));
}
this->currentGroup->addObject(makeVector(this->vertices(v[1]),
this->verticesNormal(vn[1]),
Colour(0, 1, 0)));
}
#endif
for(i = 2; i < (argc - 1); i++)
{
Shape *t;
if (vn[1] == INT32_MAX)
{
t = new Triangle(this->vertices(v[1]),
this->vertices(v[i]),
this->vertices(v[i + 1]));
}
else
{
t = new SmoothTriangle(this->vertices(v[1]),
this->vertices(v[i]),
this->vertices(v[i + 1]),
this->verticesNormal(vn[1]),
this->verticesNormal(vn[i]),
this->verticesNormal(vn[i + 1]));
}
t->setObjectId(this->getObjectId());
this->currentGroup->addObject(t);
}
ret = 0;
}
else
{
printf("ERROR: Malformed file at line %d: Too few/many parameters!\n", currentLine);
}
}
else if (strncmp(argv[0], "g", 2) == 0)
{
if (argc == 2)
{
this->addGroup(new Group(argv[1]));
}
else
{
printf("ERROR: Malformed file at line %d: Too few/many parameters!\n", currentLine);
}
}
return ret;
}
void OBJFile::lock()
{
Shape::lock();
this->baseGroup->lock();
}

View File

@@ -12,25 +12,33 @@
#include <plane.h>
#include <math_helper.h>
Intersect Plane::localIntersect(Ray r)
void Plane::localIntersect(Ray r, Intersect &xs)
{
double t;
Intersect ret = Intersect();
if (fabs(r.direction.y) < getEpsilon())
{
/* With a direction == 0, the ray can't intersect the plane */
return ret;
}
else
{
t = -r.origin.y / r.direction.y;
t = -r.origin.y / r.direction.y;
ret.add(Intersection(t, this));
return ret;
xs.add(Intersection(t, this));
}
}
Tuple Plane::localNormalAt(Tuple point)
Tuple Plane::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(0, 1, 0);
}
BoundingBox Plane::getLocalBounds()
{
BoundingBox ret;
ret | Point(-INFINITY, 0-getEpsilon(), -INFINITY);
ret | Point(INFINITY, 0+getEpsilon(), INFINITY);
return ret;
}

View File

@@ -15,32 +15,122 @@
Shape::Shape(ShapeType type)
{
this->objectId = Shape::newObjectId();
this->locked = false;
this->parent = nullptr;
this->dropShadow = true;
this->type = type;
this->transformMatrix = Matrix4().identity();
this->inverseTransform = this->transformMatrix.inverse();
this->localTransformMatrix = Matrix4().identity();
this->materialSet = false;
this->updateTransform();
}
Intersect Shape::intersect(Ray r)
uint64_t Shape::newObjectId()
{
return this->localIntersect(this->invTransform(r));
};
static uint64_t id = 0;
uint64_t ret;
Tuple Shape::normalAt(Tuple point)
ret = id++;
return ret;
}
Tuple Shape::normalToWorld(Tuple normalVector)
{
Tuple local_point = this->inverseTransform * point;
Tuple local_normal = this->localNormalAt(local_point);
Tuple world_normal = this->inverseTransform.transpose() * local_normal;
Tuple world_normal = this->transposedInverseTransform * normalVector;
/* W may get wrong, so hack it. This is perfectly normal as we are using a 4x4 matrix instead of a 3x3 */
world_normal.w = 0;
return world_normal.normalise();
};
Tuple Shape::normalAt(Tuple point, Intersection *hit)
{
Tuple local_point = this->worldToObject(point);
Tuple local_normal = this->localNormalAt(local_point, hit);
Tuple world_normal = this->normalToWorld(local_normal);
return world_normal;
}
void Shape::updateTransform()
{
if (this->locked) return;
this->transformMatrix = this->localTransformMatrix;
if (this->parent != nullptr)
{
this->transformMatrix = this->parent->transformMatrix * this->transformMatrix;
}
this->inverseTransform = this->transformMatrix.inverse();
this->transposedInverseTransform = this->inverseTransform.transpose();
}
void Shape::setTransform(Matrix transform)
{
this->transformMatrix = transform;
this->inverseTransform = transform.inverse();
if (this->locked) return;
this->localTransformMatrix = transform;
this->updateTransform();
}
BoundingBox Shape::getLocalBounds()
{
return BoundingBox(Point(-1, -1, -1), Point(1,1,1));
}
BoundingBox Shape::getBounds()
{
BoundingBox ret;
BoundingBox me = this->getLocalBounds();
ret | this->objectToWorld(Point(me.min.x, me.min.y, me.min.z));
ret | this->objectToWorld(Point(me.min.x, me.min.y, me.max.z));
ret | this->objectToWorld(Point(me.min.x, me.max.y, me.min.z));
ret | this->objectToWorld(Point(me.max.x, me.min.y, me.min.z));
ret | this->objectToWorld(Point(me.max.x, me.max.y, me.min.z));
ret | this->objectToWorld(Point(me.max.x, me.min.y, me.max.z));
ret | this->objectToWorld(Point(me.min.x, me.max.y, me.max.z));
ret | this->objectToWorld(Point(me.max.x, me.max.y, me.max.z));
return ret;
}
Material *Shape::getMaterial()
{
Shape *s = this;
while((!s->materialSet) && (s->parent != nullptr))
{
s = s->parent;
}
return &s->material;
}
void Shape::dumpMe(FILE *fp)
{
if (this->materialSet)
{
fprintf(fp, "\"Material\": {\n");
this->material.dumpMe(fp);
fprintf(fp, "},\n");
}
fprintf(fp, "\"DropShadow\": %d,\n", this->dropShadow);
fprintf(fp, "\"Locked\": %d,\n", this->locked);
fprintf(fp, "\"MaterialSet\": %d,\n", this->materialSet);
if (this->haveFiniteBounds())
{
fprintf(fp, "\"BoundingBox\": {\n");
this->getBounds().dumpMe(fp);
fprintf(fp, "},\n");
}
fprintf(fp, "\"id\": %ld,\n", this->getObjectId());
if (this->parent)
{
fprintf(fp, "\"parentId\": %ld,\n", this->parent->getObjectId());
}
}

View File

@@ -0,0 +1,42 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Smooth Triangle implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <ray.h>
#include <shape.h>
#include <triangle.h>
#include <smoothtriangle.h>
#include <math_helper.h>
#include <renderstat.h>
SmoothTriangle::SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : Triangle(p1, p2, p3),
n1(n1), n2(n2), n3(n3)
{
this->type = Shape::SMOOTHTRIANGLE;
stats.addSmoothTriangle();
}
Tuple SmoothTriangle::localNormalAt(Tuple point, Intersection *hit)
{
return (this->n2 * hit->u +
this->n3 * hit->v +
this->n1 * (1 - hit->u - hit->v)).normalise();
}
void SmoothTriangle::dumpMe(FILE *fp)
{
Tuple t = this->n1;
fprintf(fp, "\"n1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->n2;
fprintf(fp, "\"n2\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->n3;
fprintf(fp, "\"n3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
Triangle::dumpMe(fp);
}

View File

@@ -13,9 +13,8 @@
#include <tuple.h>
#include <intersect.h>
Intersect Sphere::localIntersect(Ray r)
void Sphere::localIntersect(Ray r, Intersect &xs)
{
Intersect ret;
double a, b, c, discriminant;
Tuple sphere_to_ray = r.origin - Point(0, 0, 0);
@@ -28,14 +27,24 @@ Intersect Sphere::localIntersect(Ray r)
if (discriminant >= 0)
{
ret.add(Intersection((-b - sqrt(discriminant)) / (2 * a), this));
ret.add(Intersection((-b + sqrt(discriminant)) / (2 * a), this));
xs.add(Intersection((-b - sqrt(discriminant)) / (2 * a), this));
xs.add(Intersection((-b + sqrt(discriminant)) / (2 * a), this));
}
return ret;
}
Tuple Sphere::localNormalAt(Tuple point)
Tuple Sphere::localNormalAt(Tuple point, Intersection *hit)
{
return (point - Point(0, 0, 0)).normalise();
}
void Sphere::dumpMe(FILE *fp)
{
fprintf(fp, "\"Type\": \"Sphere\",\n");
Tuple t = this->transformMatrix * Point(0, 0, 0);
fprintf(fp, "\"center\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->transformMatrix * Point(1, 1, 1);
fprintf(fp, "\"radius\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
Shape::dumpMe(fp);
}

View File

@@ -13,13 +13,12 @@ TestShape::TestShape() : localRay(Point(0, 0, 0), Vector(0, 0, 0))
{
}
Intersect TestShape::localIntersect(Ray r)
void TestShape::localIntersect(Ray r, Intersect &xs)
{
this->localRay = r;
return Intersect();
}
Tuple TestShape::localNormalAt(Tuple point)
Tuple TestShape::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(point.x, point.y, point.z);
}

View File

@@ -0,0 +1,95 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Triangle implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <ray.h>
#include <shape.h>
#include <triangle.h>
#include <math_helper.h>
#include <renderstat.h>
Triangle::Triangle(Point p1, Point p2, Point p3) : Shape(Shape::TRIANGLE), p1(p1), p2(p2), p3(p3)
{
stats.addTriangle();
this->e1 = p2 - p1;
this->e2 = p3 - p1;
this->normal = e2.cross(e1).normalise();
}
void Triangle::localIntersect(Ray r, Intersect &xs)
{
Tuple dirCrossE2 = r.direction.cross(this->e2);
double determinant = this->e1.dot(dirCrossE2);
if (fabs(determinant) < getEpsilon())
{
return;
}
double f = 1.0 / determinant;
Tuple p1ToOrigin = r.origin - this->p1;
Tuple originCrossE1 = p1ToOrigin.cross(this->e1);
double u = f * p1ToOrigin.dot(dirCrossE2);
double v = f * r.direction.dot(originCrossE1);
if ((u < 0) || (u > 1))
{
return;
}
if ((v < 0) || ((u + v) > 1))
{
return;
}
double t = f * this->e2.dot(originCrossE1);
xs.add(Intersection(t, this, u, v));
}
Tuple Triangle::localNormalAt(Tuple point, Intersection *hit)
{
return this->normal;
}
BoundingBox Triangle::getLocalBounds()
{
BoundingBox ret;
ret | p1;
ret | p2;
ret | p3;
return ret;
}
void Triangle::dumpMe(FILE *fp)
{
fprintf(fp, "\"Type\": \"Triangle\",\n");
/* World points*/
Tuple t = this->transformMatrix * this->p1;
fprintf(fp, "\"p1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->transformMatrix * this->p2;
fprintf(fp, "\"p2\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->transformMatrix * this->p3;
fprintf(fp, "\"p3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
/* Local points */
t = this->p1;
fprintf(fp, "\"lp1\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->p2;
fprintf(fp, "\"lp2\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
t = this->p3;
fprintf(fp, "\"lp3\": { \"x\": %f, \"y\": %f, \"z\": %f}, \n",
t.x, t.y, t.z);
Shape::dumpMe(fp);
}

View File

@@ -19,9 +19,14 @@ double Tuple::magnitude()
Tuple Tuple::normalise()
{
double mag = this->magnitude();
if (mag == 0)
{
return Tuple(0, 0, 0, 0);
}
return Tuple(this->x / mag, this->y / mag, this->z / mag, this->w / mag);
}
/*
double Tuple::dot(const Tuple &b)
{
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
@@ -34,8 +39,14 @@ Tuple Tuple::cross(const Tuple &b) const
this->x * b.y - this->y * b.x,
0);
}
*/
Tuple Tuple::reflect(const Tuple &normal)
{
return *this - normal * 2 * this->dot(normal);
}
bool Tuple::isRepresentable()
{
return !(isnan(this->x) || isnan(this->y) || isnan(this->z) ||
isinf(this->x) || isinf(this->y) || isinf(this->z));
}

View File

@@ -0,0 +1,39 @@
/*
* DoRayMe - a quick and dirty Raytracer
* UV Align Check test pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_UV_ALIGNCHECK_H
#define DORAYME_UV_ALIGNCHECK_H
class UVAlignCheck : public UVPattern
{
public:
Colour ul, ur, bl, br;
UVAlignCheck(Colour main, Colour ul, Colour ur, Colour bl, Colour br) : UVPattern(1, 1, main, main),
ul(ul), ur(ur), bl(bl), br(br) {};
Colour uvPatternAt(double u, double v) {
/* Remember that v=0 is at the bottom, v=1 at the top */
if (v > 0.8)
{
if (u < 0.2) { return this->ul; }
if (u > 0.8) { return this->ur; }
}
else if (v < 0.2)
{
if (u < 0.2) { return this->bl; }
if (u > 0.8) { return this->br; }
}
/* main is stored in A or B */
return this->a;
};
};
#endif /* DORAYME_UV_ALIGNCHECK_H */

View File

@@ -0,0 +1,32 @@
/*
* DoRayMe - a quick and dirty Raytracer
* UV Checkers header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_UV_CHECKERS_H
#define DORAYME_UV_CHECKERS_H
#include <uv_pattern.h>
#include <math.h>
class UVCheckers : public UVPattern
{
public:
UVCheckers(double width, double height, Colour a, Colour b) : UVPattern(width, height, a, b) {};
Colour uvPatternAt(double u, double v) {
double u2 = floor(u * this->width);
double v2 = floor(v * this->height);
if (modulo((u2 + v2), 2) == 0)
{
return this->a;
}
return this->b;
};
};
#endif /* DORAYME_UV_CHECKERS_H */

View File

@@ -0,0 +1,43 @@
/*
* DoRayMe - a quick and dirty Raytracer
* UV Image pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_UV_IMAGE_H
#define DORAYME_UV_IMAGE_H
#include <stdint.h>
#include <uv_pattern.h>
#include <canvas.h>
#include <tuple.h>
class UVImage : public UVPattern
{
public:
Canvas *image;
UVImage(Canvas *image) : UVPattern(1, 1, Colour(0, 0, 0), Colour(0, 0, 0)),
image(image) {};
UVImage(const char *filepath) : UVPattern(1, 1, Colour(0, 0, 0), Colour(0, 0, 0)) {
this->image = new Canvas(filepath);
this->width = this->image->width;
this->height = this->image->height;
};
Colour uvPatternAt(double u, double v) {
v = 1 - v;
double x = u * (this->image->width - 1);
double y = v * (this->image->height - 1);
Colour ret = this->image->getPixel(round(x), round(y));
return ret;
};
};
#endif /* DORAYME_UV_IMAGE_H */

Some files were not shown because too many files have changed in this diff Show More