Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dac74007ea | ||
|
|
b251b632ac | ||
|
|
81e323fdf4 | ||
|
|
c9021974f6 | ||
|
|
4d4c4a7453 | ||
|
|
935c8ebff7 | ||
|
|
4cdf7a4264 | ||
|
|
51a6bbebb9 | ||
|
|
e45dbad59e | ||
|
|
3db0aaaeac | ||
|
|
df52cb36db | ||
|
|
89dd74fa7c | ||
|
|
7337ae4837 | ||
|
|
9fffb68026 | ||
|
|
7687581e83 | ||
|
|
75cf59cc1a | ||
|
|
9d0db6a635 | ||
|
|
66c1582a5f | ||
|
|
2a8fe61388 | ||
|
|
f8c60da05e | ||
|
|
de315d06f9 | ||
|
|
cf5597ad6d | ||
|
|
5198888df6 | ||
|
|
10ae695f01 | ||
|
|
d4fae2dbe2 | ||
|
|
daef0c078f | ||
|
|
a477b137e7 | ||
|
|
ba1ae34855 | ||
|
|
bc047cd89e | ||
|
|
863bb2a34b | ||
|
|
14c3044acf | ||
|
|
4b4d2b7819 | ||
|
|
9e1f448e0f | ||
|
|
457f5d04e4 | ||
|
|
999419dfe1 | ||
|
|
be6b472472 | ||
|
|
aa078f4d46 | ||
|
|
dbaa6eea2c | ||
|
|
a82b67faa4 | ||
|
|
efe46e2864 | ||
|
|
60d639f3a7 | ||
|
|
ddaefafa1a | ||
|
|
9f764018d3 | ||
|
|
6200e5ed56 | ||
|
|
be245523c9 | ||
|
|
11a00a6e74 | ||
|
|
af96d52c5a | ||
|
|
5a4f9f4dc4 | ||
|
|
df4ec9794a | ||
|
|
db9f2c0203 | ||
|
|
88fcd48481 | ||
|
|
5e295c06b2 | ||
|
|
4dd7a3af39 | ||
|
|
1aab8f6619 | ||
|
|
27ec4d5567 | ||
|
|
a64b1288a5 | ||
|
|
77907499a4 | ||
|
|
cf00e62c5a | ||
|
|
5086a5c82f | ||
|
|
b84ed7496b | ||
|
|
b4bd8bd4b7 | ||
|
|
c1e7496d21 | ||
|
|
674831b370 | ||
|
|
8cc2272b50 | ||
|
|
2e2d8c143c | ||
|
|
a8ca88640b | ||
|
|
9bdfb26f7e | ||
|
|
5ebed12f4f | ||
|
|
73d60fb7e4 | ||
|
|
4a9379c0b2 | ||
|
|
aeb4669162 | ||
|
|
a8194169c5 |
6
.gitmodules
vendored
@@ -1,12 +1,12 @@
|
||||
[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
|
||||
[submodule "external/lodepng"]
|
||||
path = external/lodepng
|
||||
url = https://github.com/lvandeve/lodepng
|
||||
[submodule "external/coveralls-cmake"]
|
||||
path = external/coveralls-cmake
|
||||
url = https://github.com/Godzil/coveralls-cmake.git
|
||||
|
||||
36
.travis.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
dist: bionic
|
||||
language: c
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lcov
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- make
|
||||
- make test
|
||||
- ./tests/testMyRays
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: "Coverage"
|
||||
os: linux
|
||||
compiler: gcc
|
||||
script:
|
||||
- mkdir coverage
|
||||
- cd coverage
|
||||
- cmake .. -DCOVERALLS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
- cmake --build .
|
||||
- cmake --build . --target coveralls
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
@@ -6,19 +6,36 @@ project(DoRayMe)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
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)
|
||||
|
||||
if (COVERALLS)
|
||||
include(Coveralls)
|
||||
coveralls_turn_on_coverage()
|
||||
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)
|
||||
|
||||
|
||||
# Main app
|
||||
add_subdirectory(source)
|
||||
|
||||
option(PACKAGE_TESTS "Build the tests" ON)
|
||||
if(PACKAGE_TESTS)
|
||||
if(PACKAGE_TESTS OR COVERALLS)
|
||||
enable_testing()
|
||||
include(GoogleTest)
|
||||
add_subdirectory("${PROJECT_SOURCE_DIR}/external/googletest" "external/googletest")
|
||||
add_subdirectory("${PROJECT_SOURCE_DIR}/external/googletest" "external/googletest")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (COVERALLS)
|
||||
# Create the coveralls target.
|
||||
coveralls_setup(
|
||||
"${COVERAGE_SRCS}" # The source files.
|
||||
ON # If we should upload.
|
||||
) # (Optional) Alternate project cmake module path.
|
||||
endif()
|
||||
340
COPYING
Normal file
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
14
LICENSE
Normal file
@@ -0,0 +1,14 @@
|
||||
Copyright (C) 2020 Manoel <Godzil> Trapier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
51
README.md
@@ -0,0 +1,51 @@
|
||||
[](https://codecov.io/gh/Godzil/DoRayMe) [](https://www.codacy.com/manual/Godzil/DoRayMe?utm_source=github.com&utm_medium=referral&utm_content=Godzil/DoRayMe&utm_campaign=Badge_Grade) [](https://coveralls.io/github/Godzil/DoRayMe?branch=master) [](https://travis-ci.org/Godzil/DoRayMe)
|
||||
|
||||
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.
|
||||
|
||||
It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lodepng) to output PNG file.
|
||||
|
||||
|
||||
Examples outputs
|
||||
----------------
|
||||
|
||||
From chapter 05:
|
||||
|
||||

|
||||
|
||||
From Chapter 06:
|
||||
|
||||

|
||||
|
||||
From Chapter 07:
|
||||
|
||||

|
||||
|
||||
From Chapter 08:
|
||||
|
||||

|
||||
|
||||
From Chapter 09:
|
||||
|
||||

|
||||
|
||||
From Chapter 10:
|
||||
|
||||

|
||||
|
||||
From Chapter 11:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
From Chapter 12:
|
||||
|
||||

|
||||
1
external/coveralls-cmake
vendored
Submodule
1
external/nanogui
vendored
BIN
output/ch10_test.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
output/ch11_reflection.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
output/ch11_refraction.png
Normal file
|
After Width: | Height: | Size: 242 KiB |
BIN
output/ch11_test.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
output/ch5_test.png
Normal file
|
After Width: | Height: | Size: 273 B |
BIN
output/ch6_test.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
output/ch7_test.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
output/ch8_test.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
output/ch9_test.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
@@ -3,17 +3,20 @@
|
||||
# First most is build as a library
|
||||
add_library(rayonnement STATIC)
|
||||
|
||||
set(RAY_HEADERS include/tuple.h include/math_helper.h include/colour.h include/canvas.h
|
||||
include/matrix.h include/transformation.h include/intersect.h include/intersection.h
|
||||
include/object.h include/ray.h include/sphere.h)
|
||||
set(RAY_SOURCES tuple.cpp math_helper.cpp colour.cpp canvas.cpp matrix.cpp transformation.cpp intersect.cpp
|
||||
objects/object.cpp objects/ray.cpp objects/sphere.cpp)
|
||||
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/pattern/*.h)
|
||||
|
||||
target_include_directories(rayonnement PUBLIC include)
|
||||
file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.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)
|
||||
target_link_libraries(dorayme rayonnement)
|
||||
|
||||
if (COVERALLS)
|
||||
set(COVERAGE_SRCS ${RAY_HEADERS} ${RAY_SOURCES} ${COVERAGE_SRCS} PARENT_SCOPE)
|
||||
endif()
|
||||
74
source/camera.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Camera implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <matrix.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <ray.h>
|
||||
#include <camera.h>
|
||||
|
||||
Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize), horizontalSize(hsize), fieldOfView(fov)
|
||||
{
|
||||
double aspectRatio = (double)hsize / (double)vsize;
|
||||
double halfView = tan(fov / 2.0);
|
||||
|
||||
if (aspectRatio >= 1)
|
||||
{
|
||||
this->halfWidth = halfView;
|
||||
this->halfHeight = halfView / aspectRatio;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->halfWidth = halfView * aspectRatio;
|
||||
this->halfHeight = halfView;
|
||||
}
|
||||
|
||||
this->pixelSize = (this->halfWidth * 2) / this->horizontalSize;
|
||||
|
||||
this->setTransform(Matrix4().identity());
|
||||
|
||||
}
|
||||
|
||||
void Camera::setTransform(Matrix transform)
|
||||
{
|
||||
this->transformMatrix = transform;
|
||||
this->inverseTransform = transform.inverse();
|
||||
}
|
||||
|
||||
Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
|
||||
{
|
||||
double xOffset = ((double)pixelX + 0.5) * this->pixelSize;
|
||||
double yOffset = ((double)pixelY + 0.5) * this->pixelSize;
|
||||
|
||||
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 direction = (pixel - origin).normalise();
|
||||
|
||||
return Ray(origin, direction);
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
for(x = 0; x < this->horizontalSize; x++)
|
||||
{
|
||||
Ray r = this->rayForPixel(x, y);
|
||||
Tuple colour = world.colourAt(r, depth);
|
||||
image.putPixel(x, y, colour);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
@@ -22,6 +22,24 @@ Canvas::Canvas(uint32_t width, uint32_t height) : width(width), height(height)
|
||||
this->stride = BytePP * width;
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Canvas &b)
|
||||
{
|
||||
this->width = b.width;
|
||||
this->height = b.height;
|
||||
this->stride = b.stride;
|
||||
this->bitmap = (uint8_t *)calloc(4, b.width * b.height);
|
||||
memcpy(this->bitmap, b.bitmap, 4 * b.width * b.height);
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Canvas *b)
|
||||
{
|
||||
this->width = b->width;
|
||||
this->height = b->height;
|
||||
this->stride = b->stride;
|
||||
this->bitmap = (uint8_t *)calloc(4, b->width * b->height);
|
||||
memcpy(this->bitmap, b->bitmap, 4 * b->width * b->height);
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
if (this->bitmap != nullptr)
|
||||
@@ -30,28 +48,23 @@ Canvas::~Canvas()
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::put_pixel(uint32_t x, uint32_t y, Colour c)
|
||||
void Canvas::putPixel(uint32_t x, uint32_t y, Tuple colour)
|
||||
{
|
||||
uint32_t offset = y * this->stride + x * BytePP;
|
||||
this->bitmap[offset + 0] = MAX(MIN(c.red() * 255, 255), 0);
|
||||
this->bitmap[offset + 1] = MAX(MIN(c.green() * 255, 255), 0);
|
||||
this->bitmap[offset + 2] = MAX(MIN(c.blue() * 255, 255), 0);
|
||||
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);
|
||||
}
|
||||
|
||||
Colour Canvas::get_pixel(uint32_t x, uint32_t y)
|
||||
Colour Canvas::getPixel(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t offset = y * this->stride + x * BytePP;
|
||||
return Colour(this->bitmap[offset + 0] / 255, this->bitmap[offset + 1] / 255, this->bitmap[offset + 2] / 255);
|
||||
return Colour(this->bitmap[offset + 0] / 255.0, this->bitmap[offset + 1] / 255.0, this->bitmap[offset + 2] / 255.0);
|
||||
}
|
||||
|
||||
bool Canvas::SaveAsPNG(const char *filename)
|
||||
{
|
||||
uint32_t ret = lodepng_encode24_file(filename, this->bitmap, this->width, this->height);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
printf("lodepng_encode_file returned %d!\n", ret);
|
||||
}
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
}
|
||||
|
||||
38
source/include/camera.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Camera header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_CAMERA_H
|
||||
#define DORAYME_CAMERA_H
|
||||
|
||||
#include <matrix.h>
|
||||
#include <stdint.h>
|
||||
#include <ray.h>
|
||||
#include <canvas.h>
|
||||
#include <world.h>
|
||||
|
||||
class Camera
|
||||
{
|
||||
private:
|
||||
double halfWidth;
|
||||
double halfHeight;
|
||||
public:
|
||||
uint32_t verticalSize;
|
||||
uint32_t horizontalSize;
|
||||
double fieldOfView;
|
||||
double pixelSize;
|
||||
Matrix transformMatrix;
|
||||
Matrix inverseTransform;
|
||||
|
||||
public:
|
||||
Camera(uint32_t hsize, uint32_t vsize, double fov);
|
||||
void setTransform(Matrix transform);
|
||||
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY);
|
||||
Canvas render(World w, uint32_t depth = 5);
|
||||
};
|
||||
|
||||
#endif /* DORAYME_CAMERA_H */
|
||||
@@ -21,10 +21,12 @@ public:
|
||||
uint32_t width, height;
|
||||
|
||||
Canvas(uint32_t width, uint32_t height);
|
||||
Canvas(const Canvas *c);
|
||||
Canvas(const Canvas &c);
|
||||
~Canvas();
|
||||
|
||||
void put_pixel(uint32_t x, uint32_t y, Colour c);
|
||||
Colour get_pixel(uint32_t x, uint32_t y);
|
||||
void putPixel(uint32_t x, uint32_t y, Tuple c);
|
||||
Colour getPixel(uint32_t x, uint32_t y);
|
||||
|
||||
bool SaveAsPNG(const char *filename);
|
||||
};
|
||||
|
||||
28
source/include/cube.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
class Cube : public Shape {
|
||||
private:
|
||||
void checkAxis(double axeOrigine, double axeDirection, double *axeMin, double *axeMax);
|
||||
|
||||
Intersect localIntersect(Ray r);
|
||||
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Cube() : Shape(SHAPE_CUBE) {};
|
||||
};
|
||||
|
||||
#endif /* DORAYME_CUBE_H */
|
||||
@@ -15,15 +15,16 @@
|
||||
class Intersect
|
||||
{
|
||||
private:
|
||||
Intersection *list;
|
||||
Intersection **list;
|
||||
uint32_t num;
|
||||
uint32_t allocated;
|
||||
public:
|
||||
Intersect();
|
||||
~Intersect();
|
||||
void add(Intersection i);
|
||||
int count() { return this->num; };
|
||||
Intersection operator[](const int p) { return this->list[p]; }
|
||||
Intersection operator[](const int p) { return *this->list[p]; }
|
||||
Intersection hit();
|
||||
};
|
||||
|
||||
#endif //DORAYME_INTERSECT_H
|
||||
#endif /* DORAYME_INTERSECT_H */
|
||||
|
||||
@@ -10,20 +10,73 @@
|
||||
#define DORAYME_INTERSECTION_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ray.h>
|
||||
|
||||
class Object;
|
||||
class Shape;
|
||||
class Intersect;
|
||||
|
||||
struct Computation
|
||||
{
|
||||
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)) :
|
||||
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
|
||||
overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2) { };
|
||||
|
||||
double 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));
|
||||
};
|
||||
|
||||
Shape *object;
|
||||
double t;
|
||||
Tuple hitPoint;
|
||||
Tuple overHitPoint;
|
||||
Tuple underHitPoint;
|
||||
Tuple eyeVector;
|
||||
Tuple normalVector;
|
||||
Tuple reflectVector;
|
||||
|
||||
double n1;
|
||||
double n2;
|
||||
|
||||
bool inside;
|
||||
};
|
||||
|
||||
class Intersection
|
||||
{
|
||||
public:
|
||||
double t;
|
||||
Object *object;
|
||||
Shape *object;
|
||||
|
||||
public:
|
||||
Intersection(double t, Object *object) : t(t), object(object) { };
|
||||
Intersection(double t, Shape *object) : t(t), object(object) { };
|
||||
bool nothing() { return (this->object == nullptr); };
|
||||
|
||||
Computation prepareComputation(Ray r, Intersect *xs = nullptr);
|
||||
|
||||
bool operator==(const Intersection &b) const { return ((this->t == b.t) && (this->object == b.object)); };
|
||||
};
|
||||
|
||||
#endif //DORAYME_INTERSECTION_H
|
||||
#endif /* DORAYME_INTERSECTION_H */
|
||||
|
||||
36
source/include/light.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Light header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_LIGHT_H
|
||||
#define DORAYME_LIGHT_H
|
||||
|
||||
#include <tuple.h>
|
||||
#include <colour.h>
|
||||
|
||||
enum LightType
|
||||
{
|
||||
POINT_LIGHT = 0,
|
||||
};
|
||||
|
||||
class Light
|
||||
{
|
||||
public:
|
||||
Colour intensity;
|
||||
Tuple position;
|
||||
LightType type;
|
||||
|
||||
public:
|
||||
Light(LightType type = POINT_LIGHT, Tuple position=Point(0, 0, 0),
|
||||
Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity) { };
|
||||
|
||||
bool operator==(const Light &b) const { return this->intensity == b.intensity &&
|
||||
this->position == b.position &&
|
||||
this->type == b.type; };
|
||||
};
|
||||
|
||||
#endif /* DORAYME_LIGHT_H */
|
||||
112
source/include/list.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 */
|
||||
}
|
||||
|
||||
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
|
||||
52
source/include/material.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Material header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_MATERIAL_H
|
||||
#define DORAYME_MATERIAL_H
|
||||
|
||||
#include <tuple.h>
|
||||
#include <colour.h>
|
||||
#include <pattern.h>
|
||||
#include <light.h>
|
||||
|
||||
class Shape;
|
||||
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
Colour colour;
|
||||
double ambient;
|
||||
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),
|
||||
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);
|
||||
|
||||
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); };
|
||||
};
|
||||
|
||||
|
||||
#endif /* DORAYME_MATERIAL_H */
|
||||
@@ -13,8 +13,12 @@
|
||||
#include <math.h>
|
||||
|
||||
void set_equal_precision(double v);
|
||||
double getEpsilon();
|
||||
bool double_equal(double a, double b);
|
||||
|
||||
double deg_to_rad(double deg);
|
||||
|
||||
#endif //DORAYME_MATH_HELPER_H
|
||||
double min3(double a, double b, double c);
|
||||
double max3(double a, double b, double c);
|
||||
|
||||
#endif /* DORAYME_MATH_HELPER_H */
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
|
||||
#include <tuple.h>
|
||||
|
||||
/* Some **** linux distro seems to define "minor" as a macro
|
||||
* and wreak havoc.
|
||||
* Let's make sure we are clean here
|
||||
*/
|
||||
#ifdef minor
|
||||
#undef minor
|
||||
#endif
|
||||
|
||||
class Matrix
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Object header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_OBJECT_H
|
||||
#define DORAYME_OBJECT_H
|
||||
|
||||
class Object;
|
||||
|
||||
#include <ray.h>
|
||||
#include <tuple.h>
|
||||
#include <matrix.h>
|
||||
#include <intersect.h>
|
||||
|
||||
/* Base class for all object that can be presented in the world */
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
Matrix transformMatrix;
|
||||
Matrix inverseTransform;
|
||||
|
||||
public:
|
||||
Object();
|
||||
|
||||
virtual Intersect intersect(Ray r);
|
||||
void setTransform(Matrix transform);
|
||||
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); };
|
||||
};
|
||||
|
||||
#endif //DORAYME_OBJECT_H
|
||||
36
source/include/pattern.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Pattern header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_PATTERN_H
|
||||
#define DORAYME_PATTERN_H
|
||||
|
||||
#include <colour.h>
|
||||
#include <tuple.h>
|
||||
#include <matrix.h>
|
||||
|
||||
class Shape;
|
||||
|
||||
class Pattern
|
||||
{
|
||||
public:
|
||||
Colour a;
|
||||
Colour b;
|
||||
|
||||
Matrix transformMatrix;
|
||||
Matrix inverseTransform;
|
||||
|
||||
public:
|
||||
Pattern(Colour a, Colour b);
|
||||
|
||||
virtual Colour patternAt(Tuple point) = 0;
|
||||
|
||||
void setTransform(Matrix transform);
|
||||
Colour patternAtObject(Shape *object, Tuple point);
|
||||
};
|
||||
|
||||
#endif /* DORAYME_PATTERN_H */
|
||||
22
source/include/plane.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Plane header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_PLANE_H
|
||||
#define DORAYME_PLANE_H
|
||||
|
||||
class Plane : public Shape
|
||||
{
|
||||
private:
|
||||
Intersect localIntersect(Ray r);
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Plane() : Shape(SHAPE_PLANE) { };
|
||||
};
|
||||
|
||||
#endif //DORAYME_PLANE_H
|
||||
@@ -22,4 +22,4 @@ public:
|
||||
Tuple position(double t) { return this->origin + this->direction * t; };
|
||||
};
|
||||
|
||||
#endif //DORAYME_RAY_H
|
||||
#endif /* DORAYME_RAY_H */
|
||||
|
||||
62
source/include/shape.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Object header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_SHAPE_H
|
||||
#define DORAYME_SHAPE_H
|
||||
|
||||
class Shape;
|
||||
|
||||
#include <ray.h>
|
||||
#include <tuple.h>
|
||||
#include <matrix.h>
|
||||
#include <intersect.h>
|
||||
#include <material.h>
|
||||
|
||||
enum ShapeType
|
||||
{
|
||||
SHAPE_NONE,
|
||||
SHAPE_SPHERE,
|
||||
SHAPE_PLANE,
|
||||
SHAPE_CUBE,
|
||||
SHAPE_CONE,
|
||||
};
|
||||
|
||||
/* Base class for all object that can be presented in the world */
|
||||
class Shape
|
||||
{
|
||||
private:
|
||||
ShapeType type;
|
||||
|
||||
private:
|
||||
virtual Intersect localIntersect(Ray r) = 0;
|
||||
virtual Tuple localNormalAt(Tuple point) = 0;
|
||||
|
||||
public:
|
||||
Matrix transformMatrix;
|
||||
Matrix inverseTransform;
|
||||
Material material;
|
||||
bool dropShadow;
|
||||
|
||||
public:
|
||||
Shape(ShapeType = SHAPE_NONE);
|
||||
|
||||
Intersect intersect(Ray r);
|
||||
Tuple normalAt(Tuple point);
|
||||
|
||||
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); };
|
||||
|
||||
bool operator==(const Shape &b) const { return this->material == b.material &&
|
||||
this->type == b.type &&
|
||||
this->transformMatrix == b.transformMatrix; };
|
||||
|
||||
};
|
||||
|
||||
#endif /* DORAYME_SHAPE_H */
|
||||
@@ -9,15 +9,26 @@
|
||||
#ifndef DORAYME_SPHERE_H
|
||||
#define DORAYME_SPHERE_H
|
||||
|
||||
#include <object.h>
|
||||
#include <shape.h>
|
||||
#include <ray.h>
|
||||
#include <intersect.h>
|
||||
|
||||
class Sphere : public Object
|
||||
class Sphere : public Shape
|
||||
{
|
||||
private:
|
||||
Intersect localIntersect(Ray r);
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Sphere() : Shape(SHAPE_SPHERE) { };
|
||||
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
|
||||
virtual Intersect intersect(Ray r);
|
||||
};
|
||||
|
||||
#endif //DORAYME_SPHERE_H
|
||||
/* 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 */
|
||||
|
||||
28
source/include/testshape.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Test shape header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_TESTSHAPE_H
|
||||
#define DORAYME_TESTSHAPE_H
|
||||
|
||||
#include <shape.h>
|
||||
#include <ray.h>
|
||||
#include <tuple.h>
|
||||
|
||||
class TestShape : public Shape
|
||||
{
|
||||
private:
|
||||
Intersect localIntersect(Ray r);
|
||||
Tuple localNormalAt(Tuple point);
|
||||
|
||||
public:
|
||||
Ray localRay;
|
||||
|
||||
TestShape();
|
||||
};
|
||||
|
||||
#endif //DORAYME_TESTSHAPE_H
|
||||
@@ -15,10 +15,12 @@ Matrix translation(double x, double y, double z);
|
||||
|
||||
Matrix scaling(double x, double y, double z);
|
||||
|
||||
Matrix rotation_x(double angle);
|
||||
Matrix rotation_y(double angle);
|
||||
Matrix rotation_z(double angle);
|
||||
Matrix rotationX(double angle);
|
||||
Matrix rotationY(double angle);
|
||||
Matrix rotationZ(double angle);
|
||||
|
||||
Matrix shearing(double Xy, double Xx, double Yx, double Yz, double Zx, double Zy);
|
||||
|
||||
Matrix viewTransform(Tuple from, Tuple to, Tuple up);
|
||||
|
||||
#endif /* DORAYME_TRANSFORMATION_H */
|
||||
@@ -41,8 +41,8 @@ public:
|
||||
double magnitude();
|
||||
Tuple normalise();
|
||||
double dot(const Tuple &b);
|
||||
|
||||
Tuple cross(const Tuple &b) const;
|
||||
Tuple reflect(const Tuple &normal);
|
||||
};
|
||||
|
||||
class Point: public Tuple
|
||||
@@ -57,4 +57,4 @@ public:
|
||||
Vector(double x, double y, double z) : Tuple(x, y, z, 0.0) {};
|
||||
};
|
||||
|
||||
#endif /*DORAYME_TUPLE_H*/
|
||||
#endif /* DORAYME_TUPLE_H */
|
||||
|
||||
56
source/include/world.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* World header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_WORLD_H
|
||||
#define DORAYME_WORLD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <light.h>
|
||||
#include <shape.h>
|
||||
#include <intersect.h>
|
||||
#include <ray.h>
|
||||
|
||||
class World
|
||||
{
|
||||
public:
|
||||
uint32_t objectCount;
|
||||
uint32_t lightCount;
|
||||
|
||||
private:
|
||||
uint32_t allocatedObjectCount;
|
||||
uint32_t allocatedLightCount;
|
||||
|
||||
Light* *lightList;
|
||||
Shape* *objectList;
|
||||
|
||||
public:
|
||||
World();
|
||||
~World();
|
||||
|
||||
void addObject(Shape *s);
|
||||
void addLight(Light *l);
|
||||
|
||||
/* Some debug things */
|
||||
bool lightIsIn(Light &l);
|
||||
bool objectIsIn(Shape &s);
|
||||
|
||||
Shape *getObject(int i) { return this->objectList[i]; };
|
||||
Light *getLight(int i) { return this->lightList[i]; };
|
||||
|
||||
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
|
||||
Tuple colourAt(Ray r, uint32_t depthCount = 4);
|
||||
bool isShadowed(Tuple point, uint32_t light = 0);
|
||||
|
||||
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
|
||||
Colour refractedColour(Computation comps, uint32_t depthCount = 4);
|
||||
|
||||
Intersect intersect(Ray r);
|
||||
|
||||
};
|
||||
|
||||
#endif /* DORAYME_WORLD_H */
|
||||
53
source/include/worldbuilder.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Worldbuilder header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_WORLDBUILDER_H
|
||||
#define DORAYME_WORLDBUILDER_H
|
||||
|
||||
#include <world.h>
|
||||
#include <camera.h>
|
||||
|
||||
/* Let's keep a single header for now, will see later */
|
||||
|
||||
class DefaultWorld : public World
|
||||
{
|
||||
public:
|
||||
DefaultWorld();
|
||||
};
|
||||
|
||||
/* 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);
|
||||
};
|
||||
|
||||
#endif /* DORAYME_WORLDBUILDER_H */
|
||||
@@ -17,38 +17,50 @@
|
||||
Intersect::Intersect()
|
||||
{
|
||||
this->allocated = MIN_ALLOC;
|
||||
this->list = (Intersection *)calloc(sizeof(Object *), MIN_ALLOC);
|
||||
this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
|
||||
this->num = 0;
|
||||
}
|
||||
|
||||
Intersect::~Intersect()
|
||||
{
|
||||
/* Free stuff */
|
||||
}
|
||||
|
||||
void Intersect::add(Intersection i)
|
||||
{
|
||||
if ((this->num + 1) < this->allocated)
|
||||
Intersection *x;
|
||||
int j, k;
|
||||
|
||||
if ((this->num + 1) > this->allocated)
|
||||
{
|
||||
this->allocated *= 2;
|
||||
this->list = (Intersection *)realloc(this->list, sizeof(Object *) * this->allocated);
|
||||
this->list = (Intersection **)realloc(this->list, sizeof(Intersection *) * this->allocated);
|
||||
}
|
||||
this->list[this->num++] = new Intersection(i.t, i.object);
|
||||
|
||||
/* Now sort.. */
|
||||
for(j = 1; j < (this->num); j++)
|
||||
{
|
||||
x = this->list[j];
|
||||
k = j;
|
||||
while( (k > 0) && (this->list[k - 1]->t) > x->t )
|
||||
{
|
||||
this->list[k] = this->list[k - 1];
|
||||
k--;
|
||||
}
|
||||
this->list[k] = x;
|
||||
}
|
||||
this->list[this->num++] = i;
|
||||
}
|
||||
|
||||
Intersection Intersect::hit()
|
||||
{
|
||||
int i;
|
||||
double minHit = DBL_MAX;
|
||||
uint32_t curHit = -1;
|
||||
|
||||
for(i = 0; i < this->num; i++)
|
||||
{
|
||||
if ((this->list[i].t >= 0) && (this->list[i].t < minHit))
|
||||
{
|
||||
curHit = i;
|
||||
minHit = this->list[i].t;
|
||||
}
|
||||
if (this->list[i]->t >= 0)
|
||||
return *this->list[i];
|
||||
}
|
||||
|
||||
if (curHit == -1)
|
||||
{
|
||||
return Intersection(0, nullptr);
|
||||
}
|
||||
|
||||
return this->list[curHit];
|
||||
return Intersection(0, nullptr);
|
||||
}
|
||||
82
source/intersection.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Intersection implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <intersection.h>
|
||||
#include <shape.h>
|
||||
#include <list.h>
|
||||
|
||||
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 eyeV = -r.direction;
|
||||
bool inside = false;
|
||||
|
||||
if (normalV.dot(eyeV) < 0)
|
||||
{
|
||||
inside = true;
|
||||
normalV = -normalV;
|
||||
}
|
||||
|
||||
Tuple overHitP = hitP + normalV * getEpsilon();
|
||||
Tuple underHitP = hitP - normalV * getEpsilon();
|
||||
Tuple reflectV = r.direction.reflect(normalV);
|
||||
|
||||
if (xs != nullptr)
|
||||
{
|
||||
List containers;
|
||||
int j, k;
|
||||
|
||||
for(j = 0; j < xs->count(); j++)
|
||||
{
|
||||
Intersection i = (*xs)[j];
|
||||
if (*this == i)
|
||||
{
|
||||
if (!containers.isEmpty())
|
||||
{
|
||||
n1 = containers.last()->material.refractiveIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (containers.doesInclude(i.object))
|
||||
{
|
||||
containers.remove(i.object);
|
||||
}
|
||||
else
|
||||
{
|
||||
containers.append(i.object);
|
||||
}
|
||||
|
||||
if (*this == i)
|
||||
{
|
||||
if (!containers.isEmpty())
|
||||
{
|
||||
n2 = containers.last()->material.refractiveIndex;
|
||||
}
|
||||
|
||||
/* End the loop */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Computation(this->object,
|
||||
this->t,
|
||||
hitP,
|
||||
eyeV,
|
||||
normalV,
|
||||
overHitP,
|
||||
inside,
|
||||
reflectV,
|
||||
n1,
|
||||
n2,
|
||||
underHitP);
|
||||
}
|
||||
@@ -18,6 +18,11 @@ void set_equal_precision(double v)
|
||||
current_precision = v;
|
||||
}
|
||||
|
||||
double getEpsilon()
|
||||
{
|
||||
return current_precision;
|
||||
}
|
||||
|
||||
bool double_equal(double a, double b)
|
||||
{
|
||||
return fabs(a - b) < current_precision;
|
||||
@@ -26,4 +31,42 @@ bool double_equal(double a, double b)
|
||||
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;
|
||||
}
|
||||
if (c <= a)
|
||||
{
|
||||
if (b < c) return b;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (c >= a)
|
||||
{
|
||||
if (b > c) return b;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Object implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ray.h>
|
||||
#include <object.h>
|
||||
#include <matrix.h>
|
||||
#include <tuple.h>
|
||||
#include <intersect.h>
|
||||
|
||||
Object::Object()
|
||||
{
|
||||
this->transformMatrix = Matrix4().identity();
|
||||
this->inverseTransform = this->transformMatrix.inverse();
|
||||
}
|
||||
|
||||
Intersect Object::intersect(Ray r)
|
||||
{
|
||||
return Intersect();
|
||||
};
|
||||
|
||||
void Object::setTransform(Matrix transform)
|
||||
{
|
||||
this->transformMatrix = transform;
|
||||
this->inverseTransform = transform.inverse();
|
||||
}
|
||||
31
source/pattern.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Pattern implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <pattern.h>
|
||||
#include <shape.h>
|
||||
|
||||
Pattern::Pattern(Colour a, Colour b): a(a), b(b)
|
||||
{
|
||||
this->transformMatrix = Matrix4().identity();
|
||||
this->inverseTransform = this->transformMatrix.inverse();
|
||||
};
|
||||
|
||||
Colour Pattern::patternAtObject(Shape *object, Tuple worldPoint)
|
||||
{
|
||||
Tuple objectPoint = object->inverseTransform * worldPoint;
|
||||
Tuple patternPoint = this->inverseTransform * objectPoint;
|
||||
|
||||
return this->patternAt(patternPoint);
|
||||
}
|
||||
|
||||
void Pattern::setTransform(Matrix transform)
|
||||
{
|
||||
this->transformMatrix = transform;
|
||||
this->inverseTransform = transform.inverse();
|
||||
}
|
||||
25
source/pattern/checkerspattern.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Checkers Pattern header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_CHECKERSPATTERN_H
|
||||
#define DORAYME_CHECKERSPATTERN_H
|
||||
|
||||
class CheckersPattern : public Pattern
|
||||
{
|
||||
public:
|
||||
CheckersPattern(Colour a, Colour b) : Pattern(a, b) { };
|
||||
|
||||
Colour patternAt(Tuple point)
|
||||
{
|
||||
double value = floor(point.x) + floor(point.y) + floor(point.z);
|
||||
|
||||
return (fmod(value, 2) == 0)?this->a:this->b;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DORAYME_CHECKERSPATTERN_H */
|
||||
30
source/pattern/gradientpattern.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Gradient Pattern header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_GRADIENTPATTERN_H
|
||||
#define DORAYME_GRADIENTPATTERN_H
|
||||
|
||||
#include <pattern.h>
|
||||
|
||||
class GradientPattern : public Pattern
|
||||
{
|
||||
public:
|
||||
GradientPattern(Colour a, Colour b) : Pattern(a, b) { };
|
||||
|
||||
Colour patternAt(Tuple point)
|
||||
{
|
||||
Tuple distance = this->b - this->a;
|
||||
double fraction = point.x - floor(point.x);
|
||||
|
||||
Tuple ret = this->a + distance * fraction;
|
||||
|
||||
return Colour(ret.x, ret.y, ret.z);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DORAYME_GRADIENTPATTERN_H */
|
||||
30
source/pattern/ringpattern.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Ring Pattern header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_RINGSUPPORT_H
|
||||
#define DORAYME_RINGSUPPORT_H
|
||||
|
||||
#include <pattern.h>
|
||||
|
||||
class RingPattern : public Pattern
|
||||
{
|
||||
public:
|
||||
RingPattern(Colour a, Colour b) : Pattern(a, b) { };
|
||||
|
||||
Colour patternAt(Tuple point)
|
||||
{
|
||||
double squared = (point.x * point.x) + (point.z * point.z);
|
||||
|
||||
double value = floor(sqrt(squared));
|
||||
|
||||
return (fmod(value, 2) == 0)?this->a:this->b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* DORAYME_RINGSUPPORT_H */
|
||||
32
source/pattern/strippattern.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Strip Pattern header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DORAYME_STRIPPATTERN_H
|
||||
#define DORAYME_STRIPPATTERN_H
|
||||
|
||||
#include <pattern.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
class StripPattern : public Pattern
|
||||
{
|
||||
public:
|
||||
StripPattern(Colour a, Colour b) : Pattern(a, b) { };
|
||||
|
||||
Colour patternAt(Tuple point)
|
||||
{
|
||||
if (fmod(floor(point.x), 2) == 0)
|
||||
{
|
||||
return this->a;
|
||||
}
|
||||
return this->b;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DORAYME_STRIPPATTERN_H */
|
||||
27
source/pattern/testpattern.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Strip Pattern header
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_TESTPATTERN_H
|
||||
#define DORAYME_TESTPATTERN_H
|
||||
|
||||
#include <pattern.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
class TestPattern : public Pattern
|
||||
{
|
||||
public:
|
||||
TestPattern() : Pattern(Colour(0, 0, 0), Colour(1, 1, 1)) { };
|
||||
|
||||
Colour patternAt(Tuple point)
|
||||
{
|
||||
return Colour(point.x, point.y, point.z);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DORAYME_TESTPATTERN_H */
|
||||
76
source/shapes/cube.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
Intersect Cube::localIntersect(Ray r)
|
||||
{
|
||||
Intersect ret;
|
||||
|
||||
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)
|
||||
{
|
||||
ret.add(Intersection(tMin, this));
|
||||
ret.add(Intersection(tMax, this));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Tuple Cube::localNormalAt(Tuple point)
|
||||
{
|
||||
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);
|
||||
}
|
||||
8
source/shapes/light.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Light implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
69
source/shapes/material.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Material implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <tuple.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <shape.h>
|
||||
|
||||
Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow)
|
||||
{
|
||||
Colour pointColor = this->colour;
|
||||
|
||||
if (this->pattern != nullptr)
|
||||
{
|
||||
pointColor = this->pattern->patternAtObject(hitObject, point);
|
||||
}
|
||||
|
||||
Tuple lightVector = (light.position - point).normalise();
|
||||
Tuple reflectVector = Tuple(0, 0, 0, 0);
|
||||
|
||||
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);
|
||||
|
||||
double lightDotNormal, reflectDotEye;
|
||||
|
||||
ambientColour = effectiveColour * this->ambient;
|
||||
|
||||
emissiveColour = pointColor * this->emissive;
|
||||
|
||||
if (!inShadow)
|
||||
{
|
||||
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)
|
||||
{
|
||||
specularColour = Colour(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
double factor = pow(reflectDotEye, this->shininess);
|
||||
specularColour = light.intensity * this->specular * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
finalColour = emissiveColour + ambientColour + diffuseColour + specularColour;
|
||||
|
||||
return Colour(finalColour.x, finalColour.y, finalColour.z);
|
||||
}
|
||||
36
source/shapes/plane.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Plane implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <tuple.h>
|
||||
#include <ray.h>
|
||||
#include <shape.h>
|
||||
#include <plane.h>
|
||||
#include <math_helper.h>
|
||||
|
||||
Intersect Plane::localIntersect(Ray r)
|
||||
{
|
||||
double t;
|
||||
Intersect ret = Intersect();
|
||||
|
||||
if (fabs(r.direction.y) < getEpsilon())
|
||||
{
|
||||
/* With a direction == 0, the ray can't intersect the plane */
|
||||
return ret;
|
||||
}
|
||||
|
||||
t = -r.origin.y / r.direction.y;
|
||||
|
||||
ret.add(Intersection(t, this));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Tuple Plane::localNormalAt(Tuple point)
|
||||
{
|
||||
return Vector(0, 1, 0);
|
||||
}
|
||||
47
source/shapes/shape.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Object implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ray.h>
|
||||
#include <shape.h>
|
||||
#include <matrix.h>
|
||||
#include <tuple.h>
|
||||
#include <intersect.h>
|
||||
|
||||
Shape::Shape(ShapeType type)
|
||||
{
|
||||
this->dropShadow = true;
|
||||
this->type = type;
|
||||
this->transformMatrix = Matrix4().identity();
|
||||
this->inverseTransform = this->transformMatrix.inverse();
|
||||
}
|
||||
|
||||
Intersect Shape::intersect(Ray r)
|
||||
{
|
||||
return this->localIntersect(this->invTransform(r));
|
||||
};
|
||||
|
||||
Tuple Shape::normalAt(Tuple point)
|
||||
{
|
||||
Tuple local_point = this->inverseTransform * point;
|
||||
|
||||
Tuple local_normal = this->localNormalAt(local_point);
|
||||
|
||||
Tuple world_normal = this->inverseTransform.transpose() * local_normal;
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
void Shape::setTransform(Matrix transform)
|
||||
{
|
||||
this->transformMatrix = transform;
|
||||
this->inverseTransform = transform.inverse();
|
||||
}
|
||||
@@ -13,17 +13,15 @@
|
||||
#include <tuple.h>
|
||||
#include <intersect.h>
|
||||
|
||||
Intersect Sphere::intersect(Ray r)
|
||||
Intersect Sphere::localIntersect(Ray r)
|
||||
{
|
||||
Intersect ret;
|
||||
double a, b, c, discriminant;
|
||||
|
||||
Ray transRay = this->invTransform(r);
|
||||
Tuple sphere_to_ray = r.origin - Point(0, 0, 0);
|
||||
|
||||
Tuple sphere_to_ray = transRay.origin - Point(0, 0, 0);
|
||||
|
||||
a = transRay.direction.dot(transRay.direction);
|
||||
b = 2 * transRay.direction.dot(sphere_to_ray);
|
||||
a = r.direction.dot(r.direction);
|
||||
b = 2 * r.direction.dot(sphere_to_ray);
|
||||
c = sphere_to_ray.dot(sphere_to_ray) - 1;
|
||||
|
||||
discriminant = b * b - 4 * a * c;
|
||||
@@ -35,4 +33,9 @@ Intersect Sphere::intersect(Ray r)
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Tuple Sphere::localNormalAt(Tuple point)
|
||||
{
|
||||
return (point - Point(0, 0, 0)).normalise();
|
||||
}
|
||||
25
source/shapes/testshape.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Test shape implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <shape.h>
|
||||
#include <testshape.h>
|
||||
|
||||
TestShape::TestShape() : localRay(Point(0, 0, 0), Vector(0, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
Intersect TestShape::localIntersect(Ray r)
|
||||
{
|
||||
this->localRay = r;
|
||||
return Intersect();
|
||||
}
|
||||
|
||||
Tuple TestShape::localNormalAt(Tuple point)
|
||||
{
|
||||
return Vector(point.x, point.y, point.z);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ Matrix scaling(double x, double y, double z)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix rotation_x(double angle)
|
||||
Matrix rotationX(double angle)
|
||||
{
|
||||
Matrix ret = Matrix4().identity();
|
||||
|
||||
@@ -45,7 +45,7 @@ Matrix rotation_x(double angle)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix rotation_y(double angle)
|
||||
Matrix rotationY(double angle)
|
||||
{
|
||||
Matrix ret = Matrix4().identity();
|
||||
|
||||
@@ -57,7 +57,7 @@ Matrix rotation_y(double angle)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix rotation_z(double angle)
|
||||
Matrix rotationZ(double angle)
|
||||
{
|
||||
Matrix ret = Matrix4().identity();
|
||||
|
||||
@@ -81,4 +81,19 @@ Matrix shearing(double Xy, double Xz, double Yx, double Yz, double Zx, double Zy
|
||||
ret.set(2, 1, Zy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix viewTransform(Tuple from, Tuple to, Tuple up)
|
||||
{
|
||||
Tuple forward = (to - from).normalise();
|
||||
Tuple left = forward.cross(up.normalise());
|
||||
Tuple true_up = left.cross(forward);
|
||||
|
||||
double orientationValues[] = { left.x, left.y, left.z, 0,
|
||||
true_up.x, true_up.y, true_up.z, 0,
|
||||
-forward.x, -forward.y, -forward.z, 0,
|
||||
0, 0, 0, 1 };
|
||||
Matrix orientation = Matrix4(orientationValues);
|
||||
|
||||
return orientation * translation(-from.x, -from.y, -from.z);
|
||||
}
|
||||
@@ -33,4 +33,9 @@ Tuple Tuple::cross(const Tuple &b) const
|
||||
this->z * b.x - this->x * b.z,
|
||||
this->x * b.y - this->y * b.x,
|
||||
0);
|
||||
}
|
||||
|
||||
Tuple Tuple::reflect(const Tuple &normal)
|
||||
{
|
||||
return *this - normal * 2 * this->dot(normal);
|
||||
}
|
||||
195
source/world.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* World implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <shape.h>
|
||||
|
||||
#define MIN_ALLOC (2)
|
||||
|
||||
World::World() : objectCount(0), lightCount(0)
|
||||
{
|
||||
this->allocatedLightCount = MIN_ALLOC;
|
||||
this->lightList = (Light **)calloc(sizeof(Light *), MIN_ALLOC);
|
||||
this->lightCount = 0;
|
||||
|
||||
this->allocatedObjectCount = MIN_ALLOC;
|
||||
this->objectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC);
|
||||
this->objectCount = 0;
|
||||
};
|
||||
|
||||
World::~World()
|
||||
{
|
||||
/* We need to do some cleanup... */
|
||||
}
|
||||
|
||||
void World::addObject(Shape *s)
|
||||
{
|
||||
if ((this->objectCount + 1) > this->allocatedObjectCount)
|
||||
{
|
||||
this->allocatedObjectCount *= 2;
|
||||
this->objectList = (Shape **)realloc(this->objectList, sizeof(Shape **) * this->allocatedObjectCount);
|
||||
}
|
||||
this->objectList[this->objectCount++] = s;
|
||||
}
|
||||
|
||||
void World::addLight(Light *l)
|
||||
{
|
||||
if ((this->lightCount + 1) > this->allocatedLightCount)
|
||||
{
|
||||
this->allocatedLightCount *= 2;
|
||||
this->lightList = (Light **)realloc(this->lightList, sizeof(Light **) * this->allocatedLightCount);
|
||||
}
|
||||
this->lightList[this->lightCount++] = l;
|
||||
}
|
||||
|
||||
bool World::lightIsIn(Light &l)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < this->lightCount; i++)
|
||||
{
|
||||
if (*this->lightList[i] == l)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool World::objectIsIn(Shape &s)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < this->objectCount; i++)
|
||||
{
|
||||
if (*this->objectList[i] == s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Intersect World::intersect(Ray r)
|
||||
{
|
||||
Intersect ret;
|
||||
int i, j;
|
||||
|
||||
for(i = 0; i < this->objectCount; i++)
|
||||
{
|
||||
Intersect xs = this->objectList[i]->intersect(r);
|
||||
|
||||
for(j = 0; j < xs.count(); j++)
|
||||
{
|
||||
ret.add(xs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Tuple World::shadeHit(Computation comps, uint32_t depthCount)
|
||||
{
|
||||
uint32_t lightIndex;
|
||||
|
||||
Tuple surface = Colour(0, 0, 0);
|
||||
|
||||
for(lightIndex = 0; lightIndex < this->lightCount; lightIndex++)
|
||||
{
|
||||
bool isThereAnObstacle = this->isShadowed(comps.overHitPoint, lightIndex);
|
||||
|
||||
surface = surface + comps.object->material.lighting(*this->lightList[lightIndex], comps.overHitPoint, comps.eyeVector,
|
||||
comps.normalVector, comps.object, isThereAnObstacle);
|
||||
}
|
||||
Tuple reflected = this->reflectColour(comps, depthCount);
|
||||
Tuple refracted = this->refractedColour(comps, depthCount);
|
||||
|
||||
if ((comps.object->material.reflective > 0) && (comps.object->material.transparency > 0))
|
||||
{
|
||||
double reflectance = comps.schlick();
|
||||
|
||||
return surface + reflected * reflectance + refracted * (1 - reflectance);
|
||||
}
|
||||
|
||||
return surface + reflected + refracted;
|
||||
}
|
||||
|
||||
Tuple World::colourAt(Ray r, uint32_t depthCount)
|
||||
{
|
||||
Intersect allHits = this->intersect(r);
|
||||
Intersection hit = allHits.hit();
|
||||
|
||||
if (hit.nothing())
|
||||
{
|
||||
return Colour(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->shadeHit(hit.prepareComputation(r, &allHits), depthCount);
|
||||
}
|
||||
}
|
||||
|
||||
bool World::isShadowed(Tuple point, uint32_t light)
|
||||
{
|
||||
Tuple v = this->lightList[light]->position - point;
|
||||
double distance = v.magnitude();
|
||||
Tuple direction = v.normalise();
|
||||
|
||||
Ray r = Ray(point, direction);
|
||||
Intersect xs = this->intersect(r);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < xs.count(); i++)
|
||||
{
|
||||
Intersection h = xs[i];
|
||||
|
||||
if (h.t < 0) continue;
|
||||
|
||||
if ((h.object->dropShadow == true) && (h.t < distance))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Colour World::reflectColour(Computation comps, uint32_t depthCount)
|
||||
{
|
||||
if ((depthCount == 0) || (comps.object->material.reflective == 0))
|
||||
{
|
||||
return Colour(0, 0, 0);
|
||||
}
|
||||
|
||||
/* So it is reflective, even just a bit. Let'sr reflect the ray! */
|
||||
Ray reflectedRay = Ray(comps.overHitPoint, comps.reflectVector);
|
||||
|
||||
Tuple hitColour = this->colourAt(reflectedRay, depthCount - 1);
|
||||
hitColour = hitColour * comps.object->material.reflective;
|
||||
|
||||
return Colour(hitColour.x, hitColour.y, hitColour.z);
|
||||
}
|
||||
|
||||
Colour World::refractedColour(Computation comps, uint32_t depthCount)
|
||||
{
|
||||
double nRatio = comps.n1 / comps.n2;
|
||||
double cos_i = comps.eyeVector.dot(comps.normalVector);
|
||||
double sin2_t = (nRatio*nRatio) * (1 - cos_i * cos_i);
|
||||
|
||||
if ((sin2_t > 1 ) || (depthCount == 0) || (comps.object->material.transparency == 0))
|
||||
{
|
||||
return Colour(0, 0, 0);
|
||||
}
|
||||
|
||||
double cos_t = sqrt(1.0 - sin2_t);
|
||||
Tuple direction = comps.normalVector * (nRatio * cos_i - cos_t) - comps.eyeVector * nRatio;
|
||||
|
||||
Ray refractedRay = Ray(comps.underHitPoint, direction);
|
||||
|
||||
Tuple hitColour = this->colourAt(refractedRay, depthCount - 1) * comps.object->material.transparency;
|
||||
|
||||
return Colour(hitColour.x, hitColour.y, hitColour.z);
|
||||
}
|
||||
34
source/worldbuilder/default_world.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Default World builder implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <worldbuilder.h>
|
||||
#include <world.h>
|
||||
|
||||
#include <sphere.h>
|
||||
#include <light.h>
|
||||
#include <material.h>
|
||||
#include <transformation.h>
|
||||
|
||||
DefaultWorld::DefaultWorld()
|
||||
{
|
||||
Light *l = new Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
Sphere *s1 = new Sphere();
|
||||
Sphere *s2 = new Sphere();
|
||||
Material s1Mat = Material();
|
||||
s1Mat.colour = Colour(0.8, 1.0, 0.6);
|
||||
s1Mat.diffuse = 0.7;
|
||||
s1Mat.specular = 0.2;
|
||||
s1->setMaterial(s1Mat);
|
||||
|
||||
s2->setTransform(scaling(0.5, 0.5,0.5));
|
||||
|
||||
this->addLight(l);
|
||||
|
||||
this->addObject(s1);
|
||||
this->addObject(s2);
|
||||
}
|
||||
346
source/worldbuilder/hw3file.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Hw3file implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
/* Don't build for now */
|
||||
|
||||
/* This file is parsing a text format from another raytracer I made in the past. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <worldbuilder.h>
|
||||
#include <sphere.h>
|
||||
#include <matrix.h>
|
||||
#include <transformation.h>
|
||||
|
||||
#define IS_CMD(_cmd) (strncmp(buffer, _cmd, sizeof(_cmd)) == 0)
|
||||
|
||||
typedef void (*cmdFunc)(Hw3File *w, uint32_t curLine, double argv[15]);
|
||||
|
||||
typedef struct cmd_def
|
||||
{
|
||||
const char *name;
|
||||
int8_t numparam; /* -1 == one string, else >0 = number of argv */
|
||||
cmdFunc f;
|
||||
} cmd_def;
|
||||
|
||||
static void cmdCamera (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->cam = viewTransform(Point(argv[0], argv[1], argv[2]),
|
||||
Point(argv[3], argv[4], argv[5]),
|
||||
Vector(argv[6], argv[7], argv[8]));
|
||||
|
||||
w->camFoV = deg_to_rad(argv[9]);
|
||||
}
|
||||
|
||||
/* 0: X, 1: Y, 2: Z
|
||||
* 3: Radius
|
||||
*/
|
||||
static void cmdSphere (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
printf("Adding a sphere...\n");
|
||||
/* Instanciate a sphere */
|
||||
Sphere *shape = new Sphere();
|
||||
|
||||
Matrix pos = translation(argv[0], argv[1], argv[2]) * scaling(argv[3], argv[3], argv[3]);
|
||||
|
||||
shape->setTransform(w->getTransformMatrix() * pos);
|
||||
|
||||
shape->material.ambient = w->currentAmbient;
|
||||
shape->material.reflective = w->currentReflective;
|
||||
shape->material.shininess = w->currentShininess;
|
||||
shape->material.specular = w->currentSpecular;
|
||||
shape->material.diffuse = w->currentDiffuse;
|
||||
shape->material.colour = w->currentColour;
|
||||
shape->material.transparency = w->currentTransparency;
|
||||
shape->material.refractiveIndex = w->currentRefIndex;
|
||||
|
||||
w->addObject(shape);
|
||||
}
|
||||
|
||||
static void cmdCube (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
}
|
||||
|
||||
static void cmdTrans (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
Matrix m = translation(argv[0], argv[1], argv[2]);
|
||||
w->applyTransformMatrix(m);
|
||||
}
|
||||
|
||||
static void cmdRotate (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
Matrix m = Matrix4().identity();
|
||||
if (argv[2] != 0)
|
||||
{
|
||||
m = m * rotationZ(argv[3]);
|
||||
}
|
||||
if (argv[1] != 0)
|
||||
{
|
||||
m = m * rotationY(argv[3]);
|
||||
}
|
||||
if (argv[0] != 0)
|
||||
{
|
||||
m = m * rotationX(argv[3]);
|
||||
}
|
||||
|
||||
w->applyTransformMatrix(m);
|
||||
}
|
||||
|
||||
static void cmdScale (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
Matrix m = scaling(argv[0], argv[1], argv[2]);
|
||||
|
||||
w->applyTransformMatrix(m);
|
||||
}
|
||||
|
||||
static void cmdPushT (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->pushTransformMatrix();
|
||||
}
|
||||
|
||||
static void cmdPopT (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->popTransformMatrix();
|
||||
}
|
||||
|
||||
static void cmdLPoint (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
/* create point light */
|
||||
Light *l = new Light(POINT_LIGHT, Point(argv[0], argv[1], argv[2]), Colour(argv[3], argv[4], argv[5]));
|
||||
|
||||
w->addLight(l);
|
||||
}
|
||||
|
||||
static void cmdAmbient (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
//w->currentAmbient = (argv[0] + argv[1] + argv[2]) / 3;
|
||||
w->currentColour = Colour(argv[0], argv[1], argv[2]);
|
||||
}
|
||||
|
||||
static void cmdColour (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->currentColour = Colour(argv[0], argv[1], argv[2]);
|
||||
}
|
||||
|
||||
static void cmdDiffuse (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->currentDiffuse = (argv[0] + argv[1] + argv[2]) / 3;
|
||||
}
|
||||
|
||||
static void cmdSpecular (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->currentSpecular = (argv[0] + argv[1] + argv[2]) / 3;
|
||||
}
|
||||
|
||||
static void cmdShine (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->currentReflective = argv[0];
|
||||
}
|
||||
|
||||
static void cmdEmission (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
w->currentEmission = (argv[0] + argv[1] + argv[2]) / 3;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void cmdLDire (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
/* create directional light */
|
||||
light *cur = new light();
|
||||
|
||||
cur->type = LIGHT_DIRECTIONAL;
|
||||
cur->attenuation = sc->attenuation;
|
||||
|
||||
cur->position.set(argv[0], argv[1], argv[2]);
|
||||
cur->lightcolor.set(argv[3], argv[4], argv[5]);
|
||||
|
||||
sc->l[sc->l_count] = cur;
|
||||
sc->l_count++;
|
||||
}
|
||||
|
||||
static void cmdAtten (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
sc->attenuation = Vector(argv[0], argv[1], argv[2]);
|
||||
}
|
||||
|
||||
static void cmdMaxVerts (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
sc->vertexCount = (uint32_t) argv[0];
|
||||
sc->vertex = new point[sc->vertexCount];
|
||||
sc->curVertex = 0;
|
||||
}
|
||||
|
||||
static void cmdMaxVertN (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
/* ignore for now */
|
||||
}
|
||||
|
||||
static void cmdVertex (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
sc->vertex[sc->curVertex].set(argv[0], argv[1], argv[2]);
|
||||
sc->curVertex++;
|
||||
}
|
||||
|
||||
static void cmdVertexN (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
/* ignore for now */
|
||||
}
|
||||
|
||||
static void cmdTri (Hw3File *w, uint32_t curLine, double argv[15])
|
||||
{
|
||||
/* arg are vertex numbers */
|
||||
triangle *tr = new triangle(sc->vertex[(int)argv[0]], sc->vertex[(int)argv[1]], sc->vertex[(int)argv[2]], sc->getMatrix());
|
||||
tr->ambient = sc->ambient;
|
||||
tr->diffuse = sc->diffuse;
|
||||
tr->specular = sc->specular;
|
||||
tr->emission = sc->emission;
|
||||
tr->shininess = sc->shininess;
|
||||
tr->sourceLine = curLine;
|
||||
sc->o[sc->o_count] = tr;
|
||||
sc->o_count++;
|
||||
}
|
||||
#endif
|
||||
|
||||
static cmd_def commandList[] =
|
||||
{
|
||||
/* Camera */
|
||||
{"camera", 10, cmdCamera},
|
||||
/* Geometry */
|
||||
{"sphere", 4, cmdSphere},
|
||||
//{ "maxverts", 1, cmdMaxVerts },
|
||||
//{ "maxvertnorms", 1, cmdMaxVertN },
|
||||
//{ "vertex", 3, cmdVertex },
|
||||
//{ "vertexnormal", 6, cmdVertexN },
|
||||
//{ "tri", 3, cmdTri },
|
||||
//{ "trinormal", 3, cmdTriN },
|
||||
//{ "cube", 1, cmdCube },
|
||||
/* transformation */
|
||||
{"translate", 3, cmdTrans},
|
||||
{"rotate", 4, cmdRotate},
|
||||
{"scale", 3, cmdScale},
|
||||
{"pushTransform", 0, cmdPushT},
|
||||
{"popTransform", 0, cmdPopT},
|
||||
/* Lights */
|
||||
//{ "directional", 6, cmdLDire },
|
||||
{"point", 6, cmdLPoint},
|
||||
//{ "attenuation", 3, cmdAtten },
|
||||
/* Materials */
|
||||
{"color", 3, cmdColour},
|
||||
{"ambient", 3, cmdAmbient},
|
||||
{"diffuse", 3, cmdDiffuse},
|
||||
{"specular", 3, cmdSpecular},
|
||||
{"shininess", 1, cmdShine},
|
||||
{"emission", 3, cmdEmission},
|
||||
};
|
||||
#define CMD_COUNT (sizeof(commandList) / sizeof(cmd_def))
|
||||
|
||||
|
||||
Hw3File::Hw3File(const char *filename) : transStackCount(0), currentColour(Colour(1, 1, 1)),
|
||||
currentEmission(0), currentShininess(200), currentAmbient(0.1),
|
||||
currentDiffuse(0.9), currentSpecular(0.9), camFoV(0)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
this->popTransformMatrix();
|
||||
|
||||
fp = fopen(filename, "rt");
|
||||
if (fp != NULL)
|
||||
{
|
||||
char buffer[512];
|
||||
int line = 0;
|
||||
while(!feof(fp))
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
line++;
|
||||
|
||||
memset(buffer, 0, 512);
|
||||
fgets(buffer, 512, fp);
|
||||
|
||||
if ((buffer[0] == '#') || (strlen(buffer) < 2))
|
||||
{
|
||||
continue; /* Ingore empty line or commented lines */
|
||||
}
|
||||
|
||||
for (i = 0; i < CMD_COUNT; i++)
|
||||
{
|
||||
if (strncmp(buffer, commandList[i].name, strlen(commandList[i].name)) == 0)
|
||||
{
|
||||
char first[512];
|
||||
double value[15];
|
||||
if (commandList[i].numparam != 0)
|
||||
{
|
||||
size_t j;
|
||||
int k = 0, l = 0;
|
||||
char buff[512];
|
||||
for(j = strlen(commandList[i].name); j < strlen(buffer); j++)
|
||||
{
|
||||
if (!isspace(buffer[j]))
|
||||
{
|
||||
buff[l++] = buffer[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
buff[l] = 0;
|
||||
l = 0;
|
||||
if (k == 0)
|
||||
{
|
||||
strcpy(first, buff);
|
||||
}
|
||||
if (strlen(buff) > 0)
|
||||
{
|
||||
value[k++] = atof(buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k != abs(commandList[i].numparam))
|
||||
{
|
||||
printf("line %d malformed: given %d parameter, expected %d\n%s", line, k, abs(commandList[i].numparam), buffer);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
commandList[i].f(this, line, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix Hw3File::getTransformMatrix()
|
||||
{
|
||||
return this->transformStack[this->transStackCount];
|
||||
}
|
||||
|
||||
void Hw3File::popTransformMatrix()
|
||||
{
|
||||
if (this->transStackCount == 0)
|
||||
{
|
||||
this->transformStack[0] = Matrix4().identity();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->transformStack[this->transStackCount] = Matrix4().identity();
|
||||
this->transStackCount --;
|
||||
}
|
||||
}
|
||||
|
||||
void Hw3File::pushTransformMatrix()
|
||||
{
|
||||
this->transStackCount ++;
|
||||
this->transformStack[this->transStackCount] = this->transformStack[this->transStackCount - 1];
|
||||
}
|
||||
|
||||
void Hw3File::applyTransformMatrix(Matrix t)
|
||||
{
|
||||
this->transformStack[this->transStackCount] = this->transformStack[this->transStackCount] * t;
|
||||
}
|
||||
@@ -3,8 +3,9 @@ project(DoRayTested)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp
|
||||
intersect_test.cpp sphere_test.cpp)
|
||||
set(TESTS_SRC math_test.cpp tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp
|
||||
ray_test.cpp intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
|
||||
shape_test.cpp plane_test.cpp pattern_test.cpp cube_test.cpp)
|
||||
|
||||
add_executable(testMyRays)
|
||||
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||
@@ -18,7 +19,62 @@ gtest_discover_tests(testMyRays
|
||||
)
|
||||
|
||||
|
||||
add_executable(hw3render)
|
||||
target_include_directories(hw3render PUBLIC ../source/include)
|
||||
target_sources(hw3render PRIVATE hw3render.cpp)
|
||||
target_link_libraries(hw3render rayonnement)
|
||||
|
||||
add_executable(ch5_test)
|
||||
target_include_directories(ch5_test PUBLIC ../source/include)
|
||||
target_sources(ch5_test PRIVATE ch5_test.cpp)
|
||||
target_link_libraries(ch5_test rayonnement)
|
||||
target_link_libraries(ch5_test rayonnement)
|
||||
|
||||
add_executable(ch6_test)
|
||||
target_include_directories(ch6_test PUBLIC ../source/include)
|
||||
target_sources(ch6_test PRIVATE ch6_test.cpp)
|
||||
target_link_libraries(ch6_test rayonnement)
|
||||
|
||||
add_executable(ch7_test)
|
||||
target_include_directories(ch7_test PUBLIC ../source/include)
|
||||
target_sources(ch7_test PRIVATE ch7_test.cpp)
|
||||
target_link_libraries(ch7_test rayonnement)
|
||||
|
||||
add_executable(ch9_test)
|
||||
target_include_directories(ch9_test PUBLIC ../source/include)
|
||||
target_sources(ch9_test PRIVATE ch9_test.cpp)
|
||||
target_link_libraries(ch9_test rayonnement)
|
||||
|
||||
add_executable(ch10_test)
|
||||
target_include_directories(ch10_test PUBLIC ../source/include)
|
||||
target_sources(ch10_test PRIVATE ch10_test.cpp)
|
||||
target_link_libraries(ch10_test rayonnement)
|
||||
|
||||
add_executable(ch11_reflection)
|
||||
target_include_directories(ch11_reflection PUBLIC ../source/include)
|
||||
target_sources(ch11_reflection PRIVATE ch11_reflection.cpp)
|
||||
target_link_libraries(ch11_reflection rayonnement)
|
||||
|
||||
add_executable(ch11_refraction)
|
||||
target_include_directories(ch11_refraction PUBLIC ../source/include)
|
||||
target_sources(ch11_refraction PRIVATE ch11_refraction.cpp)
|
||||
target_link_libraries(ch11_refraction rayonnement)
|
||||
|
||||
add_executable(ch11_test)
|
||||
target_include_directories(ch11_test PUBLIC ../source/include)
|
||||
target_sources(ch11_test PRIVATE ch11_test.cpp)
|
||||
target_link_libraries(ch11_test rayonnement)
|
||||
|
||||
add_executable(ch12_test)
|
||||
target_include_directories(ch12_test PUBLIC ../source/include)
|
||||
target_sources(ch12_test PRIVATE ch12_test.cpp)
|
||||
target_link_libraries(ch12_test rayonnement)
|
||||
|
||||
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
|
||||
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
|
||||
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
|
||||
add_test(NAME Chapter09_Test COMMAND $<TARGET_FILE:ch9_test>)
|
||||
add_test(NAME Chapter10_Test COMMAND $<TARGET_FILE:ch10_test>)
|
||||
add_test(NAME Chapter11_Reflection COMMAND $<TARGET_FILE:ch11_reflection>)
|
||||
add_test(NAME Chapter11_Refraction COMMAND $<TARGET_FILE:ch11_refraction>)
|
||||
add_test(NAME Chapter11_Test COMMAND $<TARGET_FILE:ch11_test>)
|
||||
add_test(NAME Chapter12_Test COMMAND $<TARGET_FILE:ch12_test>)
|
||||
111
tests/camera_test.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Camera unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <camera.h>
|
||||
#include <math.h>
|
||||
#include <math_helper.h>
|
||||
#include <matrix.h>
|
||||
#include <tuple.h>
|
||||
#include <ray.h>
|
||||
#include <world.h>
|
||||
#include <canvas.h>
|
||||
#include <colour.h>
|
||||
#include <worldbuilder.h>
|
||||
#include <transformation.h>
|
||||
#include <stdint.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(CameraTest, Constructing_a_camera)
|
||||
{
|
||||
uint32_t hsize = 160;
|
||||
uint32_t vsize = 120;
|
||||
double field_of_view = M_PI / 2;
|
||||
|
||||
Camera c = Camera(hsize, vsize, field_of_view);
|
||||
|
||||
ASSERT_EQ(c.horizontalSize, 160);
|
||||
ASSERT_EQ(c.verticalSize, 120);
|
||||
ASSERT_TRUE(double_equal(c.fieldOfView, M_PI / 2));
|
||||
ASSERT_EQ(c.transformMatrix, Matrix4().identity());
|
||||
}
|
||||
|
||||
TEST(CameraTest, Pixel_size_for_a_horizontal_canvas)
|
||||
{
|
||||
Camera c = Camera(200, 125, M_PI / 2);
|
||||
|
||||
ASSERT_TRUE(double_equal(c.pixelSize, 0.01));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Pixel_size_for_a_vertical_canvas)
|
||||
{
|
||||
Camera c = Camera(125, 200, M_PI / 2);
|
||||
|
||||
ASSERT_TRUE(double_equal(c.pixelSize, 0.01));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Constructing_a_ray_through_the_center_of_the_canvas)
|
||||
{
|
||||
Camera c = Camera(201, 101, M_PI / 2);
|
||||
Ray r = c.rayForPixel(100, 50);
|
||||
|
||||
ASSERT_EQ(r.origin, Point(0, 0, 0));
|
||||
ASSERT_EQ(r.direction, Vector(0, 0, -1));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Constructing_a_ray_through_a_corner_of_the_canvas)
|
||||
{
|
||||
Camera c = Camera(201, 101, M_PI / 2);
|
||||
Ray r = c.rayForPixel(0, 0);
|
||||
|
||||
ASSERT_EQ(r.origin, Point(0, 0, 0));
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(r.direction, Vector(0.66519, 0.33259, -0.66851));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(CameraTest, Constructing_a_ray_when_the_camera_is_transformed)
|
||||
{
|
||||
Camera c = Camera(201, 101, M_PI / 2);
|
||||
c.setTransform(rotationY(M_PI / 4) * translation(0, -2, 5));
|
||||
Ray r = c.rayForPixel(100, 50);
|
||||
|
||||
ASSERT_EQ(r.origin, Point(0, 2, -5));
|
||||
ASSERT_EQ(r.direction, Vector(sqrt(2)/2, 0, -sqrt(2)/2));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Rendering_a_world_with_a_camera)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Camera c = Camera(11, 11, M_PI / 2);
|
||||
|
||||
Tuple from = Point(0, 0, -5);
|
||||
Tuple to = Point(0, 0, 0);
|
||||
Tuple up = Vector(0, 1, 0);
|
||||
|
||||
c.setTransform(viewTransform(from, to, up));
|
||||
|
||||
Canvas image = c.render(w);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
/* We need to lower a lot as Canvas is not keeping the
|
||||
* floating point value, but a value between 0 and 255 per channel,
|
||||
* as it is storing the actual frame buffer, so there is a more big different
|
||||
* between the value.
|
||||
*/
|
||||
set_equal_precision(0.005);
|
||||
|
||||
Colour col = image.getPixel(5, 5);
|
||||
|
||||
ASSERT_EQ(col, Colour(0.38066, 0.47583, 0.2855));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ TEST(CanvasTest, Creating_a_canvas)
|
||||
{
|
||||
for(x = 0; x < 10; x++)
|
||||
{
|
||||
ASSERT_EQ(c.get_pixel(x, y), Colour(0, 0, 0));
|
||||
ASSERT_EQ(c.getPixel(x, y), Colour(0, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ TEST(CanvasTest, Test_Writing_pixels_to_a_canvas_Test)
|
||||
Canvas c = Canvas(10, 20);
|
||||
Colour red = Colour(1, 0, 0);
|
||||
|
||||
c.put_pixel(2, 3, red);
|
||||
c.putPixel(2, 3, red);
|
||||
|
||||
ASSERT_EQ(c.get_pixel(2, 3), red);
|
||||
ASSERT_EQ(c.getPixel(2, 3), red);
|
||||
|
||||
}
|
||||
|
||||
@@ -44,10 +44,28 @@ TEST(CanvasTest, Save_a_PNG_file)
|
||||
Colour c2 = Colour(0, 0.5, 0);
|
||||
Colour c3 = Colour(-0.5, 0, 1);
|
||||
|
||||
c.put_pixel(0, 0, c1);
|
||||
c.put_pixel(2, 1, c2);
|
||||
c.put_pixel(4, 2, c3);
|
||||
c.putPixel(0, 0, c1);
|
||||
c.putPixel(2, 1, c2);
|
||||
c.putPixel(4, 2, c3);
|
||||
|
||||
ASSERT_TRUE(c.SaveAsPNG("Save_a_PNG_file.png"));
|
||||
|
||||
}
|
||||
|
||||
TEST(CanvasTest, Create_a_canvas_from_another_using_reference)
|
||||
{
|
||||
Canvas c = Canvas(100, 100);
|
||||
|
||||
Canvas copy = Canvas(c);
|
||||
|
||||
ASSERT_EQ(c.width, copy.width);
|
||||
}
|
||||
|
||||
TEST(CanvasTest, Create_a_canvas_from_another_using_pointer)
|
||||
{
|
||||
Canvas c = Canvas(100, 100);
|
||||
|
||||
Canvas copy = Canvas(&c);
|
||||
|
||||
ASSERT_EQ(c.width, copy.width);
|
||||
}
|
||||
94
tests/ch10_test.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for chapter 10
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <ringpattern.h>
|
||||
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
/* First we need to construct the world */
|
||||
Plane floor = Plane();
|
||||
floor.material.specular = 0;
|
||||
floor.material.pattern = new RingPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
|
||||
|
||||
Plane wall = Plane();
|
||||
wall.material.specular = 0;
|
||||
wall.material.pattern = new StripPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
|
||||
wall.material.pattern->setTransform(translation(0, 0, 1) * rotationY(M_PI/4));
|
||||
wall.setTransform(translation(0, 0, 5) * rotationX(M_PI/2));
|
||||
|
||||
Sphere middle = Sphere();
|
||||
middle.setTransform(translation(-0.7, 1, 0.6));
|
||||
middle.material.diffuse = 0.7;
|
||||
middle.material.specular = 0.3;
|
||||
middle.material.pattern = new StripPattern(Colour(0.1, 1, 0.5), Colour(0, 0.2, 0.2));
|
||||
middle.material.pattern->setTransform((rotationZ(M_PI/4) * rotationY(M_PI/5) * scaling(0.2, 0.2, 0.2)));
|
||||
|
||||
Sphere right = Sphere();
|
||||
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
|
||||
right.material.diffuse = 0.7;
|
||||
right.material.specular = 0.3;
|
||||
right.material.pattern = new StripPattern(Colour(0.5, 1, 0.1), Colour(0, 0, 0));
|
||||
right.material.pattern->setTransform((scaling(0.1, 0.1, 0.1)));
|
||||
|
||||
Sphere left = Sphere();
|
||||
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
|
||||
left.material.diffuse = 0.7;
|
||||
left.material.specular = 0.3;
|
||||
left.material.pattern = new GradientPattern(Colour(1, 0.8, 0.1), Colour(0.1, 0.1, 1));
|
||||
left.material.pattern->setTransform(translation(1.5, 0, 0) * scaling(2.1, 2, 2) * rotationY(-M_PI/4));
|
||||
|
||||
Sphere fourth = Sphere();
|
||||
fourth.setTransform(translation(.5, 0.25, 0.4) * scaling(0.3, 0.3, 0.3));
|
||||
fourth.material.diffuse = 0.7;
|
||||
fourth.material.specular = 0.3;
|
||||
fourth.material.pattern = new CheckersPattern(Colour(0.1, 0.8, 0.1), Colour(0.8, 1, 0.8));
|
||||
fourth.material.pattern->setTransform( scaling(0.2, 0.2, 0.2));
|
||||
|
||||
World w = World();
|
||||
|
||||
w.addObject(&floor);
|
||||
w.addObject(&wall);
|
||||
w.addObject(&middle);
|
||||
w.addObject(&left);
|
||||
w.addObject(&right);
|
||||
w.addObject(&fourth);
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(100, 50, M_PI / 3);
|
||||
camera.setTransform(viewTransform(Point(0, 1.5, -5),
|
||||
Point(0, 1, 0),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch10_test.png");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
117
tests/ch11_reflection.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for reflection in chapter 11.
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <ringpattern.h>
|
||||
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
/* First we need to construct the world */
|
||||
Plane floor = Plane();
|
||||
floor.material.specular = 0;
|
||||
floor.material.pattern = new RingPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
|
||||
floor.material.reflective = 0.1;
|
||||
|
||||
Plane wall = Plane();
|
||||
wall.material.specular = 0;
|
||||
wall.material.pattern = new StripPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
|
||||
wall.material.pattern->setTransform(translation(0, 0, 1) * rotationY(M_PI/4));
|
||||
wall.setTransform(translation(0, 0, 5) * rotationX(M_PI/2));
|
||||
|
||||
Sphere middle = Sphere();
|
||||
middle.setTransform(translation(-0.7, 1, 0.6));
|
||||
middle.material.diffuse = 0.7;
|
||||
middle.material.specular = 0.3;
|
||||
middle.material.pattern = new StripPattern(Colour(0.1, 1, 0.5), Colour(0, 0.2, 0.2));
|
||||
middle.material.pattern->setTransform((rotationZ(M_PI/4) * rotationY(M_PI/5) * scaling(0.2, 0.2, 0.2)));
|
||||
|
||||
Sphere right = Sphere();
|
||||
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
|
||||
right.material.diffuse = 0.7;
|
||||
right.material.specular = 0.3;
|
||||
right.material.pattern = new StripPattern(Colour(0.5, 1, 0.1), Colour(0, 0, 0));
|
||||
right.material.pattern->setTransform((scaling(0.1, 0.1, 0.1)));
|
||||
right.material.reflective = 0.1;
|
||||
|
||||
Sphere left = Sphere();
|
||||
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
|
||||
left.material.diffuse = 0.7;
|
||||
left.material.specular = 0.3;
|
||||
left.material.pattern = new GradientPattern(Colour(1, 0.8, 0.1), Colour(0.1, 0.1, 1));
|
||||
left.material.pattern->setTransform(translation(1.5, 0, 0) * scaling(2.1, 2, 2) * rotationY(-M_PI/4));
|
||||
|
||||
Sphere fourth = Sphere();
|
||||
fourth.setTransform(translation(.5, 0.25, 0.4) * scaling(0.3, 0.3, 0.3));
|
||||
fourth.material.diffuse = 0.7;
|
||||
fourth.material.specular = 0.3;
|
||||
fourth.material.pattern = new CheckersPattern(Colour(0.1, 0.8, 0.1), Colour(0.8, 1, 0.8));
|
||||
fourth.material.pattern->setTransform( scaling(0.2, 0.2, 0.2));
|
||||
fourth.material.reflective = 0.4;
|
||||
|
||||
World w = World();
|
||||
|
||||
w.addObject(&floor);
|
||||
w.addObject(&wall);
|
||||
w.addObject(&middle);
|
||||
w.addObject(&left);
|
||||
w.addObject(&right);
|
||||
w.addObject(&fourth);
|
||||
|
||||
/* Add some more reflective spheres */
|
||||
Sphere ref1 = Sphere();
|
||||
ref1.setTransform(translation(1, 1, .4) * scaling(0.2, 0.2, 0.2));
|
||||
ref1.material.reflective = 1;
|
||||
ref1.material.colour = Colour(0.3, 0.7, 0.6);
|
||||
w.addObject(&ref1);
|
||||
|
||||
Sphere ref2 = Sphere();
|
||||
ref2.setTransform(translation(1.5, 2, -.8) * scaling(0.2, 0.2, 0.2));
|
||||
ref2.material.reflective = 1;
|
||||
ref2.material.specular = 0.5;
|
||||
ref2.material.colour = Colour(0.3, 0.3, 0.3);
|
||||
w.addObject(&ref2);
|
||||
|
||||
Sphere ref3 = Sphere();
|
||||
ref3.setTransform(translation(-2, 1.678, .4) * scaling(0.4, 0.4, 0.4));
|
||||
ref3.material.reflective = 1;
|
||||
ref3.material.specular = 0.5;
|
||||
w.addObject(&ref3);
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(100, 50, M_PI / 3);
|
||||
camera.setTransform(viewTransform(Point(0, 1.5, -5),
|
||||
Point(0, 1, 0),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch11_reflection.png");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
71
tests/ch11_refraction.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for reflection in chapter 11.
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <ringpattern.h>
|
||||
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
World w = World();
|
||||
|
||||
/* First we need to construct the world */
|
||||
Plane floor = Plane();
|
||||
floor.material.pattern = new CheckersPattern(Colour(1, 1, 1), Colour(0, 0, 0));
|
||||
floor.setTransform(translation(0, -10, 0));
|
||||
w.addObject(&floor);
|
||||
|
||||
|
||||
Sphere glassBall = Sphere();
|
||||
glassBall.material.shininess = 300;
|
||||
glassBall.material.transparency = 1;
|
||||
glassBall.material.reflective = 1;
|
||||
glassBall.material.refractiveIndex = 1.52;
|
||||
glassBall.material.diffuse = 0.1;
|
||||
w.addObject(&glassBall);
|
||||
|
||||
Sphere airBall = Sphere();
|
||||
airBall.setTransform(scaling(0.5, 0.5, 0.5));
|
||||
airBall.material.shininess = 300;
|
||||
airBall.material.transparency = 1;
|
||||
airBall.material.reflective = 1;
|
||||
airBall.material.refractiveIndex = 1.0009;
|
||||
airBall.material.diffuse = 0.1;
|
||||
w.addObject(&airBall);
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(20, 10, 0), Colour(0.7, 0.7, 0.7));
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(100, 100, M_PI / 3);
|
||||
|
||||
camera.setTransform(viewTransform(Point(0, 2.5, 0),
|
||||
Point(0, 0, 0),
|
||||
Vector(1, 0, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch11_refraction.png");
|
||||
|
||||
return 0;
|
||||
}
|
||||
160
tests/ch11_test.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for reflection in chapter 11.
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <ringpattern.h>
|
||||
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
World w = World();
|
||||
|
||||
Material wallMaterial = Material();
|
||||
wallMaterial.pattern = new StripPattern(Colour(0.45, 0.45, 0.45), Colour(0.55, 0.55, 0.55));
|
||||
wallMaterial.pattern->setTransform( scaling(0.25, 0.25, 0.25) * rotationY(1.5708));
|
||||
wallMaterial.ambient = 0;
|
||||
wallMaterial.diffuse = 0.4;
|
||||
wallMaterial.specular = 0;
|
||||
wallMaterial.reflective = 0.3;
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* The flood */
|
||||
Plane floor = Plane();
|
||||
floor.setTransform(rotationY(0.31415));
|
||||
floor.material.pattern = new CheckersPattern(Colour(0.35, 0.35, 0.35), Colour(0.65, 0.65, 0.65));
|
||||
floor.material.specular = 0;
|
||||
floor.material.reflective = 0.4;
|
||||
w.addObject(&floor);
|
||||
|
||||
/* the ceiling */
|
||||
Plane ceiling = Plane();
|
||||
ceiling.setTransform(translation(0, 5, 0));
|
||||
ceiling.material.colour = Colour(0.8, 0.8, 0.8);
|
||||
ceiling.material.ambient = 0.3;
|
||||
ceiling.material.specular = 0;
|
||||
w.addObject(&ceiling);
|
||||
|
||||
/* West wall */
|
||||
Plane westWall = Plane();
|
||||
westWall.setTransform( translation(-5, 0, 0) * rotationZ(1.5708) * rotationY(1.5708));
|
||||
westWall.setMaterial(wallMaterial);
|
||||
w.addObject(&westWall);
|
||||
|
||||
/* east wall */
|
||||
Plane eastWall = Plane();
|
||||
eastWall.setTransform( translation(5, 0, 0) * rotationZ(1.5708) * rotationY(1.5708));
|
||||
eastWall.setMaterial(wallMaterial);
|
||||
w.addObject(&eastWall);
|
||||
|
||||
/* north wall */
|
||||
Plane northWall = Plane();
|
||||
northWall.setTransform( translation(0, 0, 5) * rotationX(1.5708));
|
||||
northWall.setMaterial(wallMaterial);
|
||||
w.addObject(&northWall);
|
||||
|
||||
/* south wall */
|
||||
Plane southWall = Plane();
|
||||
southWall.setTransform( translation(0, 0, -5) * rotationX(1.5708));
|
||||
southWall.setMaterial(wallMaterial);
|
||||
w.addObject(&southWall);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* Background balls */
|
||||
Sphere bg1 = Sphere();
|
||||
bg1.setTransform(translation(4.6, 0.4, 1) * scaling(0.4, 0.4, 0.4));
|
||||
bg1.material.colour = Colour(0.8, 0.5, 0.3);
|
||||
bg1.material.shininess = 50;
|
||||
w.addObject(&bg1);
|
||||
|
||||
Sphere bg2 = Sphere();
|
||||
bg2.setTransform(translation(4.7, 0.3, 0.4) * scaling(0.3, 0.3, 0.3));
|
||||
bg2.material.colour = Colour(0.9, 0.4, 0.5);
|
||||
bg2.material.shininess = 50;
|
||||
w.addObject(&bg2);
|
||||
|
||||
Sphere bg3 = Sphere();
|
||||
bg3.setTransform(translation(-1, 0.5, 4.5) * scaling(0.5, 0.5, 0.5));
|
||||
bg3.material.colour = Colour(0.4, 0.9, 0.6);
|
||||
bg3.material.shininess = 50;
|
||||
w.addObject(&bg3);
|
||||
|
||||
Sphere bg4 = Sphere();
|
||||
bg4.setTransform(translation(-1.7, 0.3, 4.7) * scaling(0.3, 0.3, 0.3));
|
||||
bg4.material.colour = Colour(0.4, 0.6, 0.9);
|
||||
bg4.material.shininess = 50;
|
||||
w.addObject(&bg4);
|
||||
|
||||
/* Forground balls */
|
||||
|
||||
/* Red sphere */
|
||||
Sphere redBall = Sphere();
|
||||
redBall.setTransform(translation(-0.6, 1, 0.6));
|
||||
redBall.material.colour = Colour(1, 0.3, 0.2);
|
||||
redBall.material.shininess = 5;
|
||||
redBall.material.specular = 0.4;
|
||||
w.addObject(&redBall);
|
||||
|
||||
/* blue glass ball */
|
||||
Sphere blueGlassBall = Sphere();
|
||||
blueGlassBall.setTransform(translation(0.6, 0.7, -0.6) * scaling(0.7, 0.7, 0.7));
|
||||
blueGlassBall.material.colour = Colour(0, 0, 0.2);
|
||||
blueGlassBall.material.ambient = 0;
|
||||
blueGlassBall.material.diffuse = 0.4;
|
||||
blueGlassBall.material.specular = 0.9;
|
||||
blueGlassBall.material.shininess = 300;
|
||||
blueGlassBall.material.transparency = 0.9;
|
||||
blueGlassBall.material.refractiveIndex = 1.5;
|
||||
w.addObject(&blueGlassBall);
|
||||
|
||||
/* green glass ball */
|
||||
Sphere greenGlassBall = Sphere();
|
||||
greenGlassBall.setTransform(translation(-0.7, 0.5, -0.8) * scaling(0.5, 0.5, 0.5));
|
||||
greenGlassBall.material.colour = Colour(0, 0.2, 0);
|
||||
greenGlassBall.material.ambient = 0;
|
||||
greenGlassBall.material.diffuse = 0.4;
|
||||
greenGlassBall.material.specular = 0.9;
|
||||
greenGlassBall.material.shininess = 300;
|
||||
greenGlassBall.material.transparency = 0.9;
|
||||
greenGlassBall.material.refractiveIndex = 1.5;
|
||||
w.addObject(&greenGlassBall);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(-4.9, 4.9, -1), Colour(1, 1, 1));
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(400, 200, 1.152);
|
||||
camera.setTransform(viewTransform(Point(-2.6, 1.5, -3.9),
|
||||
Point(-0.6, 1, -0.8),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch11_test.png");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
213
tests/ch12_test.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for reflection in chapter 12.
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <cube.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <ringpattern.h>
|
||||
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
World w = World();
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(0, 6.9, -5), Colour(1, 1, 0.9));
|
||||
w.addLight(&light);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* The floor / ceiling */
|
||||
Cube floor = Cube();
|
||||
floor.setTransform( scaling(20, 7, 20) * translation(0, 1, 0));
|
||||
floor.material.pattern = new CheckersPattern(Colour(0, 0, 0), Colour(0.25, 0.25, 0.25));
|
||||
floor.material.pattern->setTransform(scaling(0.07, 0.07, 0.07));
|
||||
floor.material.ambient = 0.25;
|
||||
floor.material.diffuse = 0.7;
|
||||
floor.material.specular = 0.9;
|
||||
floor.material.shininess = 300;
|
||||
floor.material.reflective = 0.1;
|
||||
w.addObject(&floor);
|
||||
|
||||
/* Walls */
|
||||
Cube walls = Cube();
|
||||
walls.setTransform(scaling(10, 10, 10));
|
||||
walls.material.pattern = new CheckersPattern(Colour( 0.4863, 0.3765, 0.2941), Colour(0.3725, 0.2902, 0.2275 ));
|
||||
walls.material.pattern->setTransform(scaling(0.05, 20, 0.05));
|
||||
walls.material.ambient = 0.1;
|
||||
walls.material.diffuse = 0.7;
|
||||
walls.material.specular = 0.9;
|
||||
walls.material.shininess = 300;
|
||||
walls.material.reflective = 0.1;
|
||||
w.addObject(&walls);
|
||||
|
||||
/* Table top */
|
||||
Cube tableTop = Cube();
|
||||
tableTop.setTransform(translation(0, 3.1, 0) * scaling(3, 0.1, 2));
|
||||
tableTop.material.pattern = new StripPattern(Colour(0.5529, 0.4235, 0.3255), Colour(0.6588, 0.5098, 0.4000 ));
|
||||
tableTop.material.pattern->setTransform(scaling(0.05, 0.05, 0.05) * rotationY(0.1));
|
||||
tableTop.material.ambient = 0.1;
|
||||
tableTop.material.diffuse = 0.7;
|
||||
tableTop.material.specular = 0.9;
|
||||
tableTop.material.shininess = 300;
|
||||
tableTop.material.reflective = 0.2;
|
||||
w.addObject(&tableTop);
|
||||
|
||||
/* Leg 1 */
|
||||
Cube leg1 = Cube();
|
||||
leg1.setTransform(translation(2.7, 1.5, -1.7) * scaling(0.1, 1.5, 0.1));
|
||||
leg1.material.colour = Colour(0.5529, 0.4235, 0.3255);
|
||||
leg1.material.ambient = 0.2;
|
||||
leg1.material.diffuse = 0.7;
|
||||
w.addObject(&leg1);
|
||||
|
||||
/* Leg 2 */
|
||||
Cube leg2 = Cube();
|
||||
leg2.setTransform(translation(2.7, 1.5, 1.7) * scaling(0.1, 1.5, 0.1));
|
||||
leg2.material.colour = Colour(0.5529, 0.4235, 0.3255);
|
||||
leg2.material.ambient = 0.2;
|
||||
leg2.material.diffuse = 0.7;
|
||||
w.addObject(&leg2);
|
||||
|
||||
/* Leg 3 */
|
||||
Cube leg3 = Cube();
|
||||
leg3.setTransform(translation(-2.7, 1.5, -1.7) * scaling(0.1, 1.5, 0.1));
|
||||
leg3.material.colour = Colour(0.5529, 0.4235, 0.3255);
|
||||
leg3.material.ambient = 0.2;
|
||||
leg3.material.diffuse = 0.7;
|
||||
w.addObject(&leg3);
|
||||
|
||||
/* Leg 4 */
|
||||
Cube leg4 = Cube();
|
||||
leg4.setTransform(translation(-2.7, 1.5, 1.7) * scaling(0.1, 1.5, 0.1));
|
||||
leg4.material.colour = Colour(0.5529, 0.4235, 0.3255);
|
||||
leg4.material.ambient = 0.2;
|
||||
leg4.material.diffuse = 0.7;
|
||||
w.addObject(&leg4);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* Glass cube */
|
||||
Cube glassCube = Cube();
|
||||
glassCube.setTransform(translation(0, 3.45001, 0) * rotationY(0.2) * scaling(0.25, 0.25, 0.25));
|
||||
glassCube.dropShadow = false;
|
||||
glassCube.material.colour = Colour(1, 1, 0.8);
|
||||
glassCube.material.ambient = 0;
|
||||
glassCube.material.diffuse = 0.3;
|
||||
glassCube.material.specular = 0.9;
|
||||
glassCube.material.shininess = 300;
|
||||
glassCube.material.reflective = 0.7;
|
||||
glassCube.material.transparency = 0.7;
|
||||
glassCube.material.refractiveIndex = 1.5;
|
||||
w.addObject(&glassCube);
|
||||
|
||||
/* Little cube 1 */
|
||||
Cube lilCube1 = Cube();
|
||||
lilCube1.setTransform(translation(1, 3.25, -0.9) *
|
||||
rotationY(-0.4) *
|
||||
scaling(0.15, 0.15, 0.15));
|
||||
lilCube1.material.colour = Colour(1, 0.5, 0.5);
|
||||
lilCube1.material.reflective = 0.6;
|
||||
lilCube1.material.diffuse = 0.4;
|
||||
w.addObject(&lilCube1);
|
||||
|
||||
/* Little cube 2 */
|
||||
Cube lilCube2 = Cube();
|
||||
lilCube2.setTransform(translation(-1.5, 3.27, 0.3) *
|
||||
rotationY(0.4) *
|
||||
scaling(0.15, 0.07, 0.15));
|
||||
lilCube2.material.colour = Colour(1, 1, 0.5);
|
||||
w.addObject(&lilCube2);
|
||||
|
||||
/* Little cube 3 */
|
||||
Cube lilCube3 = Cube();
|
||||
lilCube3.setTransform(translation(0, 3.25, 1) *
|
||||
rotationY(0.4) *
|
||||
scaling(0.2, 0.05, 0.05));
|
||||
lilCube3.material.colour = Colour(0.5, 1, 0.5);
|
||||
w.addObject(&lilCube3);
|
||||
|
||||
/* Little cube 4 */
|
||||
Cube lilCube4 = Cube();
|
||||
lilCube4.setTransform(translation(-0.6, 3.4, -1) *
|
||||
rotationY(0.8) *
|
||||
scaling(0.05, 0.2, 0.05));
|
||||
lilCube4.material.colour = Colour(0.5, 0.5, 1);
|
||||
w.addObject(&lilCube4);
|
||||
|
||||
/* Little cube 5 */
|
||||
Cube lilCube5 = Cube();
|
||||
lilCube5.setTransform(translation(2, 3.4, 1) *
|
||||
rotationY(0.8) *
|
||||
scaling(0.05, 0.2, 0.05));
|
||||
lilCube5.material.colour = Colour(0.5, 1, 1);
|
||||
w.addObject(&lilCube5);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* Frame 1 */
|
||||
Cube frame1 = Cube();
|
||||
frame1.setTransform(translation(-10, 4, 1) * scaling(0.05, 1, 1));
|
||||
frame1.material.colour = Colour(0.7098, 0.2471, 0.2196);
|
||||
frame1.material.diffuse = 0.6;
|
||||
w.addObject(&frame1);
|
||||
|
||||
/* Frame 2 */
|
||||
Cube frame2 = Cube();
|
||||
frame2.setTransform(translation(-10, 3.4, 2.7) * scaling(0.05, 0.4, 0.4));
|
||||
frame2.material.colour = Colour(0.2667, 0.2706, 0.6902);
|
||||
frame2.material.diffuse = 0.6;
|
||||
w.addObject(&frame2);
|
||||
|
||||
/* Frame 3 */
|
||||
Cube frame3 = Cube();
|
||||
frame3.setTransform(translation(-10, 4.6, 2.7) * scaling(0.05, 0.4, 0.4));
|
||||
frame3.material.colour = Colour(0.3098, 0.5961, 0.3098);
|
||||
frame3.material.diffuse = 0.6;
|
||||
w.addObject(&frame3);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* Mirror */
|
||||
Cube mirror = Cube();
|
||||
mirror.setTransform(translation(-2, 3.5, 9.95) * scaling(4.8, 1.4, 0.06));
|
||||
mirror.material.colour = Colour(0, 0, 0);
|
||||
mirror.material.diffuse = 0;
|
||||
mirror.material.ambient = 0;
|
||||
mirror.material.specular = 1;
|
||||
mirror.material.shininess = 300;
|
||||
mirror.material.reflective = 1;
|
||||
w.addObject(&mirror);
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(400, 200, 0.785);
|
||||
camera.setTransform(viewTransform(Point(8, 6, -8),
|
||||
Point(0, 3, 0),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w, 4);
|
||||
|
||||
image.SaveAsPNG("ch12_test.png");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -29,14 +29,14 @@ int main()
|
||||
double worldY = (wallSize / 2) - pixelSize * y;
|
||||
for(x = 0; x < c.width; x++)
|
||||
{
|
||||
double worldX = (wallSize / 2) - pixelSize * x;
|
||||
double worldX = -(wallSize / 2) + pixelSize * x;
|
||||
Point position = Point(worldX, worldY, wallDistance);
|
||||
Ray r = Ray(cameraOrigin, (position - cameraOrigin).normalise());
|
||||
Intersect xs = s.intersect(r);
|
||||
|
||||
if (!xs.hit().nothing())
|
||||
{
|
||||
c.put_pixel(x, y, red);
|
||||
c.putPixel(x, y, red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
tests/ch6_test.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for chapter 6 "Put it together".
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <ray.h>
|
||||
#include <sphere.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int x, y;
|
||||
Canvas c = Canvas(100, 100);
|
||||
|
||||
Sphere s = Sphere();
|
||||
|
||||
s.material.colour = Colour(1, 0.2, 1);
|
||||
|
||||
|
||||
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
Point cameraOrigin = Point(0, 0, -5);
|
||||
double wallDistance = 10;
|
||||
double wallSize = 7;
|
||||
double pixelSize = wallSize / c.width;
|
||||
for(y = 0; y < c.height; y++)
|
||||
{
|
||||
double worldY = (wallSize / 2) - pixelSize * y;
|
||||
for(x = 0; x < c.width; x++)
|
||||
{
|
||||
double worldX = -(wallSize / 2) + pixelSize * x;
|
||||
Point position = Point(worldX, worldY, wallDistance);
|
||||
Ray r = Ray(cameraOrigin, (position - cameraOrigin).normalise());
|
||||
Intersect xs = s.intersect(r);
|
||||
|
||||
Intersection hit = xs.hit();
|
||||
|
||||
if (!hit.nothing())
|
||||
{
|
||||
Tuple hitPoint = r.position(hit.t);
|
||||
Tuple hitNormalVector = hit.object->normalAt(hitPoint);
|
||||
Tuple eye = -r.direction;
|
||||
Colour pixelColour = hit.object->material.lighting(light, hitPoint, eye, hitNormalVector, hit.object);
|
||||
|
||||
c.putPixel(x, y, pixelColour);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
c.SaveAsPNG("ch6_test.png");
|
||||
return 0;
|
||||
}
|
||||
83
tests/ch7_test.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for chapter 7 "Put it together".
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
/* First we need to construct the world */
|
||||
Sphere floor = Sphere();
|
||||
floor.setTransform(scaling(10, 0.01, 10));
|
||||
floor.material.colour = Colour(1, 0.9, 0.9);
|
||||
floor.material.specular = 0;
|
||||
|
||||
Sphere left_wall = Sphere();
|
||||
left_wall.setTransform(translation(0, 0, 5) *
|
||||
rotationY(-M_PI / 4) * rotationX((M_PI / 2)) *
|
||||
scaling(10, 0.01, 10));
|
||||
left_wall.material = floor.material;
|
||||
|
||||
Sphere right_wall = Sphere();
|
||||
right_wall.setTransform(translation(0, 0, 5) *
|
||||
rotationY(M_PI / 4) * rotationX((M_PI / 2)) *
|
||||
scaling(10, 0.01, 10));
|
||||
right_wall.material = floor.material;
|
||||
|
||||
Sphere middle = Sphere();
|
||||
middle.setTransform(translation(-0.5, 1, 0.5));
|
||||
middle.material.colour = Colour(0.1, 1, 0.5);
|
||||
middle.material.diffuse = 0.7;
|
||||
middle.material.specular = 0.3;
|
||||
|
||||
Sphere right = Sphere();
|
||||
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
|
||||
right.material.colour = Colour(0.5, 1, 0.1);
|
||||
right.material.diffuse = 0.7;
|
||||
right.material.specular = 0.3;
|
||||
|
||||
Sphere left = Sphere();
|
||||
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
|
||||
left.material.colour = Colour(1, 0.8, 0.1);
|
||||
left.material.diffuse = 0.7;
|
||||
left.material.specular = 0.3;
|
||||
|
||||
World w = World();
|
||||
|
||||
w.addObject(&floor);
|
||||
w.addObject(&left_wall);
|
||||
w.addObject(&right_wall);
|
||||
w.addObject(&middle);
|
||||
w.addObject(&left);
|
||||
w.addObject(&right);
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(100, 50, M_PI / 3);
|
||||
camera.setTransform(viewTransform(Point(0, 1.5, -5),
|
||||
Point(0, 1, 0),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch7_test.png");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
69
tests/ch9_test.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Render test for chapter 9.
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
/* First we need to construct the world */
|
||||
Plane floor = Plane();
|
||||
floor.material.colour = Colour(1, 0.9, 0.9);
|
||||
floor.material.specular = 0;
|
||||
|
||||
Sphere middle = Sphere();
|
||||
middle.setTransform(translation(-0.5, 1, 0.5));
|
||||
middle.material.colour = Colour(0.1, 1, 0.5);
|
||||
middle.material.diffuse = 0.7;
|
||||
middle.material.specular = 0.3;
|
||||
|
||||
Sphere right = Sphere();
|
||||
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
|
||||
right.material.colour = Colour(0.5, 1, 0.1);
|
||||
right.material.diffuse = 0.7;
|
||||
right.material.specular = 0.3;
|
||||
|
||||
Sphere left = Sphere();
|
||||
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
|
||||
left.material.colour = Colour(1, 0.8, 0.1);
|
||||
left.material.diffuse = 0.7;
|
||||
left.material.specular = 0.3;
|
||||
|
||||
World w = World();
|
||||
|
||||
w.addObject(&floor);
|
||||
w.addObject(&middle);
|
||||
w.addObject(&left);
|
||||
w.addObject(&right);
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(100, 50, M_PI / 3);
|
||||
camera.setTransform(viewTransform(Point(0, 1.5, -5),
|
||||
Point(0, 1, 0),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch9_test.png");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
117
tests/cube_test.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Cube unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <intersect.h>
|
||||
#include <intersection.h>
|
||||
#include <cube.h>
|
||||
#include <transformation.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(CubeTest, A_ray_intersects_a_cube)
|
||||
{
|
||||
Cube c = Cube();
|
||||
|
||||
Point Origins[] = {
|
||||
Point(5, 0.5, 0),
|
||||
Point(-5, 0.5, 0),
|
||||
Point(0.5, 5, 0),
|
||||
Point(0.5, -5, 0),
|
||||
Point(0.5, 0, 5),
|
||||
Point(0.5, 0, -5),
|
||||
Point(0, 0.5, 0),
|
||||
};
|
||||
|
||||
Vector Directions[] = {
|
||||
Vector(-1, 0, 0),
|
||||
Vector(1, 0, 0),
|
||||
Vector(0, -1, 0),
|
||||
Vector(0, 1, 0),
|
||||
Vector(0, 0, -1),
|
||||
Vector(0, 0, 1),
|
||||
Vector(0, 0, 1),
|
||||
};
|
||||
|
||||
double t1[] = { 4, 4, 4, 4, 4, 4, -1 };
|
||||
double t2[] = { 6, 6, 6, 6, 6, 6, 1 };
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 7; i++)
|
||||
{
|
||||
Ray r = Ray(Origins[i], Directions[i]);
|
||||
Intersect xs = c.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 2);
|
||||
EXPECT_EQ(xs[0].t, t1[i]);
|
||||
EXPECT_EQ(xs[1].t, t2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CubeTest, A_ray_miss_a_cube)
|
||||
{
|
||||
Cube c = Cube();
|
||||
|
||||
Point Origins[] = {
|
||||
Point(-2, 0, 0),
|
||||
Point(0, -2, 0),
|
||||
Point(0, 0, -2),
|
||||
Point(2, 0, 2),
|
||||
Point(0, 2, 2),
|
||||
Point(2, 2, 0),
|
||||
};
|
||||
|
||||
Vector Directions[] = {
|
||||
Vector(0.2673, 0.5345, 0.8018),
|
||||
Vector(0.8018, 0.2673, 0.5345),
|
||||
Vector(0.5345, 0.8018, 0.2673),
|
||||
Vector(0, 0, -1),
|
||||
Vector(0, -1, 0),
|
||||
Vector(-1, 0, 0),
|
||||
};
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 6; i++)
|
||||
{
|
||||
Ray r = Ray(Origins[i], Directions[i]);
|
||||
Intersect xs = c.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CubeTest, The_normal_on_the_surface_of_a_cube)
|
||||
{
|
||||
Cube c = Cube();
|
||||
|
||||
Point HitPoints[] = {
|
||||
Point(1, 0.5, -0.8),
|
||||
Point(-1, -0.2, 0.9),
|
||||
Point(-0.4, 1, -0.1),
|
||||
Point(0.3, -1, -0.7),
|
||||
Point(-0.6, 0.3, 1),
|
||||
Point(0.4, 0.4, -1),
|
||||
Point(1, 1, 1),
|
||||
Point(-1, -1, -1),
|
||||
};
|
||||
|
||||
Vector ExpectedNormals[] = {
|
||||
Vector(1, 0, 0),
|
||||
Vector(-1, 0, 0),
|
||||
Vector(0, 1, 0),
|
||||
Vector(0, -1, 0),
|
||||
Vector(0, 0, 1),
|
||||
Vector(0, 0, -1),
|
||||
Vector(1, 0, 0),
|
||||
Vector(-1, 0, 0),
|
||||
};
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
ASSERT_EQ(c.normalAt(HitPoints[i]), ExpectedNormals[i]);
|
||||
}
|
||||
}
|
||||
42
tests/hw3render.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Renderer using hw3 files as world builder.
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include <world.h>
|
||||
#include <worldbuilder.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
#include <transformation.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
printf("usage: %s file.hw3\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Hw3File world = Hw3File(argv[1]);
|
||||
|
||||
/* Set the camera resolution */
|
||||
Camera cam = Camera(640, 480, world.camFoV);
|
||||
|
||||
cam.setTransform(world.cam);
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = cam.render(world);
|
||||
|
||||
image.SaveAsPNG("hw3render.png");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -7,10 +7,12 @@
|
||||
*
|
||||
*/
|
||||
#include <intersect.h>
|
||||
#include <intersection.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <transformation.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
TEST(IntersectTest, Creating_an_intersect_and_do_some_check)
|
||||
{
|
||||
Intersect i;
|
||||
@@ -32,7 +34,7 @@ TEST(IntersectTest, An_intersection_encapsulate_t_and_object)
|
||||
Intersection i = Intersection(3.5, &s);
|
||||
|
||||
ASSERT_EQ(i.t, 3.5);
|
||||
ASSERT_EQ(i.object, (Object *)&s);
|
||||
ASSERT_EQ(i.object, (Shape *)&s);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, Aggregating_intersections)
|
||||
@@ -57,8 +59,8 @@ TEST(IntersectTest, Intersect_sets_the_object_on_the_intersection)
|
||||
Intersect xs = s.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 2);
|
||||
ASSERT_EQ(xs[0].object, (Object *)&s);
|
||||
ASSERT_EQ(xs[1].object, (Object *)&s);
|
||||
ASSERT_EQ(xs[0].object, (Shape *)&s);
|
||||
ASSERT_EQ(xs[1].object, (Shape *)&s);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_hit_when_all_intersection_have_positive_t)
|
||||
@@ -129,4 +131,175 @@ TEST(IntersectTest, The_hit_is_always_the_lowest_nonnegative_intersection)
|
||||
Intersection i = xs.hit();
|
||||
|
||||
ASSERT_EQ(i, i4);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, Precomputing_the_state_of_an_intersection)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Sphere shape = Sphere();
|
||||
Intersection i = Intersection(4, &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
|
||||
ASSERT_EQ(comps.t, i.t);
|
||||
ASSERT_EQ(comps.object, i.object);
|
||||
ASSERT_EQ(comps.hitPoint, Point(0, 0, -1));
|
||||
ASSERT_EQ(comps.eyeVector, Vector(0, 0, -1));
|
||||
ASSERT_EQ(comps.normalVector, Vector(0, 0, -1));
|
||||
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_hit_when_an_intersection_occurs_on_the_outside)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Sphere shape = Sphere();
|
||||
Intersection i = Intersection(4, &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
|
||||
ASSERT_EQ(comps.inside, false);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_hit_when_an_intersection_occurs_on_the_inside)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||
Sphere shape = Sphere();
|
||||
Intersection i = Intersection(1, &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
ASSERT_EQ(comps.hitPoint, Point(0, 0, 1));
|
||||
ASSERT_EQ(comps.eyeVector, Vector(0, 0, -1));
|
||||
ASSERT_EQ(comps.inside, true);
|
||||
|
||||
/* Normal vector would have been (0, 0, 1); but is inverted ! */
|
||||
|
||||
ASSERT_EQ(comps.normalVector, Vector(0, 0, -1));
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_hit_should_offset_the_point)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Sphere shape = Sphere();
|
||||
shape.setTransform(translation(0, 0, 1));
|
||||
|
||||
Intersection i = Intersection(5, &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
|
||||
/* Normal vector would have been (0, 0, 1); but is inverted ! */
|
||||
|
||||
ASSERT_LT(comps.overHitPoint.z, -getEpsilon() / 2);
|
||||
ASSERT_GT(comps.hitPoint.z, comps.overHitPoint.z);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, Precomputing_the_reflection_vector)
|
||||
{
|
||||
Plane s = Plane();
|
||||
Ray r = Ray(Point(0, 1, -1), Vector(0, -sqrt(2) / 2, sqrt(2) / 2));
|
||||
Intersection i = Intersection(sqrt(2), &s);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
|
||||
ASSERT_EQ(comps.reflectVector, Vector(0, sqrt(2) / 2, sqrt(2) / 2));
|
||||
}
|
||||
|
||||
TEST(IntersectTest, Finding_n1_and_n2_at_various_intersections)
|
||||
{
|
||||
int i;
|
||||
double n1_res[6] = { 1.0, 1.5, 2.0, 2.5, 2.5, 1.5 };
|
||||
double n2_res[6] = { 1.5, 2.0, 2.5, 2.5, 1.5, 1.0 };
|
||||
|
||||
GlassSphere A = GlassSphere();
|
||||
A.setTransform(scaling(2, 2, 2));
|
||||
A.material.refractiveIndex = 1.5;
|
||||
|
||||
GlassSphere B = GlassSphere();
|
||||
B.setTransform(translation(0, 0, -0.25));
|
||||
B.material.refractiveIndex = 2.0;
|
||||
|
||||
GlassSphere C = GlassSphere();
|
||||
C.setTransform(translation(0, 0, 0.25));
|
||||
C.material.refractiveIndex = 2.5;
|
||||
|
||||
Ray r = Ray(Point(0, 0, -4), Vector(0, 0, 1));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(2.0, &A));
|
||||
xs.add(Intersection(2.75, &B));
|
||||
xs.add(Intersection(3.25, &C));
|
||||
xs.add(Intersection(4.75, &B));
|
||||
xs.add(Intersection(5.25, &C));
|
||||
xs.add(Intersection(6, &A));
|
||||
|
||||
for(i = 0; i < xs.count(); i++)
|
||||
{
|
||||
Intersection inter = xs[i];
|
||||
Computation comps = inter.prepareComputation(r, &xs);
|
||||
ASSERT_EQ(comps.n1, n1_res[i]);
|
||||
ASSERT_EQ(comps.n2, n2_res[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_under_point_is_offset_below_the_surface)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
GlassSphere shape = GlassSphere();
|
||||
shape.setTransform(translation(0, 0, 1));
|
||||
|
||||
Intersection i = Intersection(5, &shape);
|
||||
Intersect xs = Intersect();
|
||||
xs.add(i);
|
||||
|
||||
Computation comps = i.prepareComputation(r, &xs);
|
||||
|
||||
ASSERT_TRUE(double_equal(comps.underHitPoint.z, getEpsilon() / 2));
|
||||
ASSERT_LT(comps.hitPoint.z, comps.underHitPoint.z);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_Schlick_approximation_under_total_internal_reflection)
|
||||
{
|
||||
GlassSphere shape = GlassSphere();
|
||||
|
||||
Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(-sqrt(2)/2, &shape));
|
||||
xs.add(Intersection(sqrt(2)/2, &shape));
|
||||
|
||||
Computation comps = xs[1].prepareComputation(r, &xs);
|
||||
double reflectance = comps.schlick();
|
||||
|
||||
ASSERT_EQ(reflectance, 1.0);
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_Schlick_approximation_with_a_perpendicular_viewing_angle)
|
||||
{
|
||||
GlassSphere shape = GlassSphere();
|
||||
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(-1, &shape));
|
||||
xs.add(Intersection(1, &shape));
|
||||
|
||||
Computation comps = xs[1].prepareComputation(r, &xs);
|
||||
double reflectance = comps.schlick();
|
||||
|
||||
ASSERT_TRUE(double_equal(reflectance, 0.04));
|
||||
}
|
||||
|
||||
TEST(IntersectTest, The_Schlick_approximation_with_small_angle_and_n2_gt_n1)
|
||||
{
|
||||
GlassSphere shape = GlassSphere();
|
||||
|
||||
Ray r = Ray(Point(0, 0.99, -2), Vector(0, 0, 1));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(1.8589, &shape));
|
||||
|
||||
Computation comps = xs[0].prepareComputation(r, &xs);
|
||||
double reflectance = comps.schlick();
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_TRUE(double_equal(reflectance, 0.48873));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
24
tests/light_test.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Light unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <light.h>
|
||||
#include <math.h>
|
||||
#include <colour.h>
|
||||
#include <tuple.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(LightTest, A_point_lighthas_a_position_and_intensity)
|
||||
{
|
||||
Colour intensity = Colour(1, 1, 1);
|
||||
Point position = Point(0, 0, 0);
|
||||
|
||||
Light light = Light(POINT_LIGHT, position, intensity);
|
||||
|
||||
ASSERT_EQ(light.position, position);
|
||||
ASSERT_EQ(light.intensity, intensity);
|
||||
}
|
||||
124
tests/material_test.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Material unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <material.h>
|
||||
#include <math.h>
|
||||
#include <colour.h>
|
||||
#include <testshape.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(MaterialTest, The_default_material)
|
||||
{
|
||||
Material m = Material();
|
||||
|
||||
ASSERT_EQ(m.colour, Colour(1, 1, 1));
|
||||
ASSERT_EQ(m.ambient, 0.1);
|
||||
ASSERT_EQ(m.diffuse, 0.9);
|
||||
ASSERT_EQ(m.specular, 0.9);
|
||||
ASSERT_EQ(m.shininess, 200.0);
|
||||
}
|
||||
|
||||
// This is used by the next tests
|
||||
static Material m = Material();
|
||||
static Point position = Point(0, 0, 0);
|
||||
|
||||
TEST(MaterialTest, Lighting_with_the_eye_between_the_light_and_the_surface)
|
||||
{
|
||||
TestShape t = TestShape();
|
||||
Vector eyev = Vector(0, 0, -1);
|
||||
Vector normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
|
||||
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t);
|
||||
|
||||
ASSERT_EQ(result, Colour(1.9, 1.9, 1.9));
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Lighting_with_the_eye_between_the_light_and_the_surface_eye_offset_by_45)
|
||||
{
|
||||
TestShape t = TestShape();
|
||||
Vector eyev = Vector(0, -sqrt(2)/2, -sqrt(2)/2);
|
||||
Vector normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
|
||||
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t);
|
||||
|
||||
ASSERT_EQ(result, Colour(1.0, 1.0, 1.0));
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Lighting_with_the_eye_opposite_surface_light_offset_45)
|
||||
{
|
||||
TestShape t = TestShape();
|
||||
Vector eyev = Vector(0, 0, -1);
|
||||
Vector normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t);
|
||||
|
||||
set_equal_precision(0.0001);
|
||||
|
||||
ASSERT_EQ(result, Colour(0.7364, 0.7364, 0.7364));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Lighting_with_the_eye_in_the_path_of_the_reflection_vector)
|
||||
{
|
||||
TestShape t = TestShape();
|
||||
Vector eyev = Vector(0, -sqrt(2)/2, -sqrt(2)/2);
|
||||
Vector normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t);
|
||||
|
||||
set_equal_precision(0.0001);
|
||||
|
||||
ASSERT_EQ(result, Colour(1.6364, 1.6364, 1.6364));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Lighting_with_the_light_behind_the_surface)
|
||||
{
|
||||
TestShape t = TestShape();
|
||||
Vector eyev = Vector(0, 0, -1);
|
||||
Vector normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 0, 10), Colour(1, 1, 1));
|
||||
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t);
|
||||
|
||||
ASSERT_EQ(result, Colour(0.1, 0.1, 0.1));
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Lighting_with_the_surface_in_shadow)
|
||||
{
|
||||
TestShape t = TestShape();
|
||||
Vector eyev = Vector(0, 0, -1);
|
||||
Vector normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
|
||||
bool inShadow = true;
|
||||
|
||||
Colour result = m.lighting(light, position, eyev, normalv, &t, inShadow);
|
||||
|
||||
ASSERT_EQ(result, Colour(0.1, 0.1, 0.1));
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Reflectivity_for_the_default_material)
|
||||
{
|
||||
Material m = Material();
|
||||
|
||||
ASSERT_EQ(m.reflective, 0);
|
||||
}
|
||||
|
||||
TEST(MaterialTest, Transparency_and_refractive_index_for_the_default_material)
|
||||
{
|
||||
Material m = Material();
|
||||
|
||||
ASSERT_EQ(m.transparency, 0.0);
|
||||
ASSERT_EQ(m.refractiveIndex, 1.0);
|
||||
}
|
||||
97
tests/math_test.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Math helper unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <math_helper.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(MathTest, Default_epsilon)
|
||||
{
|
||||
ASSERT_EQ(getEpsilon(), FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(MathTest, Check_setting_epsilon)
|
||||
{
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(getEpsilon(), 0.00001);
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(MathTest, Check_double_equal_is_working)
|
||||
{
|
||||
ASSERT_TRUE(double_equal(0 - DBL_EPSILON, 0));
|
||||
|
||||
ASSERT_FALSE(double_equal(0 - FLT_EPSILON, 0));
|
||||
ASSERT_TRUE(double_equal(0 - FLT_EPSILON/2.0, 0));
|
||||
|
||||
ASSERT_FALSE(double_equal(0 - 0.001, 0));
|
||||
ASSERT_FALSE(double_equal(0 - 0.00001, 0));
|
||||
ASSERT_FALSE(double_equal(0 - 0.000001, 0));
|
||||
|
||||
set_equal_precision(0.00001);
|
||||
ASSERT_TRUE(double_equal(0 - FLT_EPSILON*2, 0));
|
||||
ASSERT_FALSE(double_equal(0 - 0.001, 0));
|
||||
ASSERT_FALSE(double_equal(0 - 0.00001, 0));
|
||||
ASSERT_TRUE(double_equal(0 - 0.000001, 0));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST(MathTest, Check_that_deg_to_rad_is_working)
|
||||
{
|
||||
double angle180 = deg_to_rad(180);
|
||||
double angle90 = deg_to_rad(90);
|
||||
double angle270 = deg_to_rad(270);
|
||||
|
||||
ASSERT_EQ(angle180, M_PI);
|
||||
ASSERT_EQ(angle90, M_PI / 2.);
|
||||
ASSERT_EQ(angle270, M_PI * 1.5);
|
||||
}
|
||||
|
||||
TEST(MathTest, Min_is_working)
|
||||
{
|
||||
ASSERT_EQ(min3(1, 2, 3), 1);
|
||||
ASSERT_EQ(min3(1, 3, 2), 1);
|
||||
ASSERT_EQ(min3(2, 1, 3), 1);
|
||||
ASSERT_EQ(min3(2, 3, 1), 1);
|
||||
ASSERT_EQ(min3(3, 1, 2), 1);
|
||||
ASSERT_EQ(min3(3, 2, 1), 1);
|
||||
|
||||
ASSERT_EQ(min3(1, 2, 2), 1);
|
||||
ASSERT_EQ(min3(2, 1, 2), 1);
|
||||
ASSERT_EQ(min3(2, 2, 1), 1);
|
||||
|
||||
ASSERT_EQ(min3(3, 2, 2), 2);
|
||||
ASSERT_EQ(min3(2, 3, 2), 2);
|
||||
ASSERT_EQ(min3(2, 2, 3), 2);
|
||||
|
||||
ASSERT_EQ(min3(2, 2, 2), 2);
|
||||
}
|
||||
|
||||
TEST(MathTest, Max_is_working)
|
||||
{
|
||||
ASSERT_EQ(max3(1, 2, 3), 3);
|
||||
ASSERT_EQ(max3(1, 3, 2), 3);
|
||||
ASSERT_EQ(max3(2, 1, 3), 3);
|
||||
ASSERT_EQ(max3(2, 3, 1), 3);
|
||||
ASSERT_EQ(max3(3, 1, 2), 3);
|
||||
ASSERT_EQ(max3(3, 2, 1), 3);
|
||||
|
||||
ASSERT_EQ(max3(1, 2, 2), 2);
|
||||
ASSERT_EQ(max3(2, 1, 2), 2);
|
||||
ASSERT_EQ(max3(2, 2, 1), 2);
|
||||
|
||||
ASSERT_EQ(max3(3, 2, 2), 3);
|
||||
ASSERT_EQ(max3(2, 3, 2), 3);
|
||||
ASSERT_EQ(max3(2, 2, 3), 3);
|
||||
|
||||
ASSERT_EQ(max3(2, 2, 2), 2);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ TEST(MatrixTest, Constructing_and_inspecting_a_4x4_Matrix)
|
||||
ASSERT_EQ(m.get(3, 2), 15.5);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, A_2x2_matric_ought_to_be_representable)
|
||||
TEST(MatrixTest, Change_a_single_value_and_check_it)
|
||||
{
|
||||
double values[] = {-3, 5,
|
||||
1, -2};
|
||||
@@ -41,9 +41,13 @@ TEST(MatrixTest, A_2x2_matric_ought_to_be_representable)
|
||||
ASSERT_EQ(m.get(0, 1), 5);
|
||||
ASSERT_EQ(m.get(1, 0), 1);
|
||||
ASSERT_EQ(m.get(1, 1), -2);
|
||||
|
||||
m.set(0, 0, 12);
|
||||
|
||||
ASSERT_EQ(m.get(0, 0), 12);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, A_3x3_matric_ought_to_be_representable)
|
||||
TEST(MatrixTest, A_3x3_matrix_ought_to_be_representable)
|
||||
{
|
||||
double values[] = {-3, 5, 0,
|
||||
1, -2, -7,
|
||||
@@ -56,6 +60,19 @@ TEST(MatrixTest, A_3x3_matric_ought_to_be_representable)
|
||||
ASSERT_EQ(m.get(2, 2), 1);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, A_2x2_matrix_ought_to_be_representable)
|
||||
{
|
||||
double values[] = {-3, 5,
|
||||
1, -2};
|
||||
|
||||
Matrix2 m = Matrix2(values);
|
||||
|
||||
ASSERT_EQ(m.get(0, 0), -3);
|
||||
ASSERT_EQ(m.get(0, 1), 5);
|
||||
ASSERT_EQ(m.get(1, 0), 1);
|
||||
ASSERT_EQ(m.get(1, 1), -2);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, Matrix_equality_with_identical_matrix)
|
||||
{
|
||||
double values1[] = {1, 2, 3, 4,
|
||||
|
||||
211
tests/pattern_test.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Pattern unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <ringpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <testpattern.h>
|
||||
#include <transformation.h>
|
||||
#include <colour.h>
|
||||
#include <sphere.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <material.h>
|
||||
|
||||
Colour black = Colour(0, 0, 0);
|
||||
Colour white = Colour(1, 1, 1);
|
||||
|
||||
TEST(PatternTest, Creating_a_stripe_pattern)
|
||||
{
|
||||
StripPattern p = StripPattern(white, black);
|
||||
|
||||
ASSERT_EQ(p.a, white);
|
||||
ASSERT_EQ(p.b, black);
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_strip_pattern_is_constant_in_y)
|
||||
{
|
||||
StripPattern p = StripPattern(white, black);
|
||||
|
||||
ASSERT_EQ(p.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(p.patternAt(Point(0, 1, 0)), white);
|
||||
ASSERT_EQ(p.patternAt(Point(0, 2, 0)), white);
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_strip_pattern_is_constant_in_z)
|
||||
{
|
||||
StripPattern p = StripPattern(white, black);
|
||||
|
||||
ASSERT_EQ(p.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(p.patternAt(Point(0, 0, 1)), white);
|
||||
ASSERT_EQ(p.patternAt(Point(0, 0, 2)), white);
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_strip_pattern_alternate_in_x)
|
||||
{
|
||||
StripPattern p = StripPattern(white, black);
|
||||
|
||||
ASSERT_EQ(p.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(p.patternAt(Point(0.9, 0, 0)), white);
|
||||
ASSERT_EQ(p.patternAt(Point(1, 0, 0)), black);
|
||||
ASSERT_EQ(p.patternAt(Point(-0.1, 0, 0)), black);
|
||||
ASSERT_EQ(p.patternAt(Point(-1, 0, 0)), black);
|
||||
ASSERT_EQ(p.patternAt(Point(-1.1, 0, 0)), white);
|
||||
}
|
||||
|
||||
TEST(PatternTest, Lightning_with_a_pattern_applied)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Material m;
|
||||
StripPattern p = StripPattern(white, black);
|
||||
m.pattern = &p;
|
||||
m.ambient = 1;
|
||||
m.diffuse = 0;
|
||||
m.specular = 0;
|
||||
|
||||
Tuple eyev = Vector(0, 0, -1);
|
||||
Tuple normalv = Vector(0, 0, -1);
|
||||
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
|
||||
|
||||
Colour c1 = m.lighting(light, Point(0, 9, 0), eyev, normalv, &s, false);
|
||||
Colour c2 = m.lighting(light, Point(1, 1, 0), eyev, normalv, &s, false);
|
||||
|
||||
ASSERT_EQ(c1, Colour(1, 1, 1));
|
||||
ASSERT_EQ(c2, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(PatternTest, Stripe_with_an_object_transformation)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
s.setTransform(scaling(2, 2, 2));
|
||||
StripPattern pattern = StripPattern(white, black);
|
||||
Colour c = pattern.patternAtObject(&s, Point(1.5, 0, 0));
|
||||
|
||||
ASSERT_EQ(c, white);
|
||||
}
|
||||
|
||||
TEST(PatternTest, Stripe_with_a_pattern_transformation)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
StripPattern pattern = StripPattern(white, black);
|
||||
pattern.setTransform(scaling(2, 2, 2));
|
||||
Colour c = pattern.patternAtObject(&s, Point(1.5, 0, 0));
|
||||
|
||||
ASSERT_EQ(c, white);
|
||||
}
|
||||
|
||||
TEST(PatternTest, Stripe_with_both_an_object_and_a_pattern_transformation)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
s.setTransform(scaling(2, 2, 2));
|
||||
StripPattern pattern = StripPattern(white, black);
|
||||
pattern.setTransform(translation(0.5, 0, 0));
|
||||
|
||||
Colour c = pattern.patternAtObject(&s, Point(2.5, 0, 0));
|
||||
|
||||
ASSERT_EQ(c, white);
|
||||
}
|
||||
|
||||
TEST(PatternTest, The_default_pattern_transformation)
|
||||
{
|
||||
TestPattern pattern = TestPattern();
|
||||
|
||||
ASSERT_EQ(pattern.transformMatrix, Matrix4().identity());
|
||||
}
|
||||
|
||||
TEST(PatternTest, Assigning_a_transformation)
|
||||
{
|
||||
TestPattern pattern = TestPattern();
|
||||
pattern.setTransform(translation(1, 2, 3));
|
||||
|
||||
ASSERT_EQ(pattern.transformMatrix, translation(1, 2, 3));
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_pattern_with_an_object_transformation)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
s.setTransform(scaling(2, 2, 2));
|
||||
TestPattern pattern = TestPattern();
|
||||
|
||||
Colour c = pattern.patternAtObject(&s, Point(2, 3, 4));
|
||||
|
||||
ASSERT_EQ(c, Colour(1, 1.5, 2));
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_pattern_with_a_pattern_transformation)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
TestPattern pattern = TestPattern();
|
||||
pattern.setTransform(scaling(2, 2, 2));
|
||||
|
||||
Colour c = pattern.patternAtObject(&s, Point(2, 3, 4));
|
||||
|
||||
ASSERT_EQ(c, Colour(1, 1.5, 2));
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_pattern_with_an_object_and_a_pattern_transformation)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
s.setTransform(scaling(2, 2, 2));
|
||||
TestPattern pattern = TestPattern();
|
||||
pattern.setTransform(translation(0.5, 1, 1.5));
|
||||
|
||||
Colour c = pattern.patternAtObject(&s, Point(2.5, 3, 3.5));
|
||||
|
||||
ASSERT_EQ(c, Colour(0.75, 0.5, 0.25));
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_gradient_linearly_interpolates_betweens_colors)
|
||||
{
|
||||
GradientPattern pattern = GradientPattern(white, black);
|
||||
|
||||
ASSERT_EQ(pattern.patternAt(Point(0.25, 0, 0)), Colour(0.75, 0.75, 0.75));
|
||||
ASSERT_EQ(pattern.patternAt(Point(0.5, 0, 0)), Colour(0.5, 0.5, 0.5));
|
||||
ASSERT_EQ(pattern.patternAt(Point(0.75, 0, 0)), Colour(0.25, 0.25, 0.25));
|
||||
}
|
||||
|
||||
TEST(PatternTest, A_ring_should_extend_in_both_x_and_z)
|
||||
{
|
||||
RingPattern pattern = RingPattern(white, black);
|
||||
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(1, 0, 0)), black);
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 1)), black);
|
||||
|
||||
/* 0.708 is just bit more than sqrt(2)/2 */
|
||||
ASSERT_EQ(pattern.patternAt(Point(0.708, 0, 0.708)), black);
|
||||
}
|
||||
|
||||
TEST(PatternTest, Checkers_should_repeat_in_x)
|
||||
{
|
||||
CheckersPattern pattern = CheckersPattern(white, black);
|
||||
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(0.99, 0, 0)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(1.01, 0, 0)), black);
|
||||
}
|
||||
|
||||
TEST(PatternTest, Checkers_should_repeat_in_y)
|
||||
{
|
||||
CheckersPattern pattern = CheckersPattern(white, black);
|
||||
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0.99, 0)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 1.01, 0)), black);
|
||||
}
|
||||
|
||||
TEST(PatternTest, Checkers_should_repeat_in_z)
|
||||
{
|
||||
CheckersPattern pattern = CheckersPattern(white, black);
|
||||
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0.99)), white);
|
||||
ASSERT_EQ(pattern.patternAt(Point(0, 0, 1.01)), black);
|
||||
}
|
||||
71
tests/plane_test.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Plane unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ray.h>
|
||||
#include <shape.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <transformation.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(PlaneTest, The_normal_of_a_plane_is_constant_everywhere)
|
||||
{
|
||||
Plane p = Plane();
|
||||
Tuple n1 = p.normalAt(Point(0, 0, 0));
|
||||
Tuple n2 = p.normalAt(Point(10, 0, -10));
|
||||
Tuple n3 = p.normalAt(Point(-5, 0, 0150));
|
||||
|
||||
ASSERT_EQ(n1, Vector(0, 1, 0));
|
||||
ASSERT_EQ(n2, Vector(0, 1, 0));
|
||||
ASSERT_EQ(n3, Vector(0, 1, 0));
|
||||
}
|
||||
|
||||
TEST(PlaneTest, Intersect_with_a_ray_parallel_to_the_plane)
|
||||
{
|
||||
Plane p = Plane();
|
||||
Ray r = Ray(Point(0, 10, 0), Vector(0, 0, 1));
|
||||
|
||||
Intersect xs = p.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 0);
|
||||
}
|
||||
|
||||
TEST(PlaneTest, Intersect_with_a_coplanar_ray)
|
||||
{
|
||||
Plane p = Plane();
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||
|
||||
Intersect xs = p.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 0);
|
||||
}
|
||||
|
||||
TEST(PlaneTest, A_ray_intersecting_a_plane_from_above)
|
||||
{
|
||||
Plane p = Plane();
|
||||
Ray r = Ray(Point(0, 1, 0), Vector(0, -1, 0));
|
||||
|
||||
Intersect xs = p.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 1);
|
||||
ASSERT_EQ(xs[0].t, 1);
|
||||
ASSERT_EQ(xs[0].object, &p);
|
||||
}
|
||||
|
||||
TEST(PlaneTest, A_ray_intersecting_a_plane_from_below)
|
||||
{
|
||||
Plane p = Plane();
|
||||
Ray r = Ray(Point(0, -1, 0), Vector(0, 1, 0));
|
||||
|
||||
Intersect xs = p.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 1);
|
||||
ASSERT_EQ(xs[0].t, 1);
|
||||
ASSERT_EQ(xs[0].object, &p);
|
||||
}
|
||||
@@ -8,7 +8,8 @@
|
||||
*/
|
||||
#include <ray.h>
|
||||
#include <transformation.h>
|
||||
#include <object.h>
|
||||
#include <shape.h>
|
||||
#include <testshape.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
@@ -38,7 +39,7 @@ TEST(RayTest, Translating_a_ray)
|
||||
Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0));
|
||||
|
||||
Matrix m = translation(3, 4, 5);
|
||||
Object o = Object();
|
||||
TestShape o = TestShape();
|
||||
|
||||
o.setTransform(m);
|
||||
|
||||
@@ -53,7 +54,7 @@ TEST(RayTest, Scaling_a_ray)
|
||||
Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0));
|
||||
|
||||
Matrix m = scaling(2, 3, 4);
|
||||
Object o = Object();
|
||||
TestShape o = TestShape();
|
||||
|
||||
o.setTransform(m);
|
||||
|
||||
|
||||
98
tests/shape_test.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Shape unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <shape.h>
|
||||
#include <testshape.h>
|
||||
#include <matrix.h>
|
||||
#include <transformation.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(ShapeTest, The_default_transformation)
|
||||
{
|
||||
TestShape s = TestShape();
|
||||
ASSERT_EQ(s.transformMatrix, Matrix4().identity());
|
||||
}
|
||||
|
||||
TEST(ShapeTest, Assigning_a_transformation)
|
||||
{
|
||||
TestShape s = TestShape();
|
||||
|
||||
s.setTransform(translation(2, 3, 4));
|
||||
|
||||
ASSERT_EQ(s.transformMatrix, translation(2, 3, 4));
|
||||
}
|
||||
|
||||
TEST(ShapeTest, The_default_material)
|
||||
{
|
||||
TestShape s = TestShape();
|
||||
|
||||
ASSERT_EQ(s.material, Material());
|
||||
}
|
||||
|
||||
TEST(ShapeTest, Assigning_a_material)
|
||||
{
|
||||
TestShape s = TestShape();
|
||||
Material m = Material();
|
||||
m.ambient = 1;
|
||||
|
||||
s.material = m;
|
||||
|
||||
ASSERT_EQ(s.material, m);
|
||||
}
|
||||
|
||||
TEST(ShapeTest, Intersecting_a_scaled_shape_with_a_ray)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
TestShape s = TestShape();
|
||||
|
||||
s.setTransform(scaling(2, 2, 2));
|
||||
Intersect xs = s.intersect(r);
|
||||
|
||||
ASSERT_EQ(s.localRay.origin, Point(0, 0, -2.5));
|
||||
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 0.5));
|
||||
}
|
||||
|
||||
TEST(ShapeTest, Intersecting_a_translated_shape_with_a_ray)
|
||||
{
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
TestShape s = TestShape();
|
||||
|
||||
s.setTransform(translation(5, 0, 0));
|
||||
Intersect xs = s.intersect(r);
|
||||
|
||||
ASSERT_EQ(s.localRay.origin, Point(-5, 0, -5));
|
||||
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 1));
|
||||
}
|
||||
|
||||
TEST(ShapeTest, Computing_the_normal_on_a_translated_shape)
|
||||
{
|
||||
TestShape s = TestShape();
|
||||
s.setTransform(translation(0, 1, 0));
|
||||
Tuple n = s.normalAt(Point(0, 1.70711, -0.70711));
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(n, Vector(0, 0.70711, -0.70711));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(ShapeTest, Computing_the_normal_on_a_tranformed_shape)
|
||||
{
|
||||
TestShape s = TestShape();
|
||||
s.setTransform(scaling(1, 0.5, 1) * rotationZ(M_PI / 5));
|
||||
Tuple n = s.normalAt(Point(0, sqrt(2)/2, -sqrt(2)/2));
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(n, Vector(0, 0.97014, -0.24254));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#include <ray.h>
|
||||
#include <sphere.h>
|
||||
#include <material.h>
|
||||
#include <transformation.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -105,4 +106,105 @@ TEST(SphereTest, Intersecting_a_translated_sphere_with_a_ray)
|
||||
Intersect xs = s.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 0);
|
||||
}
|
||||
|
||||
TEST(SphereTest, The_normal_on_a_sphere_at_a_point_on_the_X_axis)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Tuple n = s.normalAt(Point(1, 0, 0));
|
||||
|
||||
ASSERT_EQ(n, Vector(1, 0, 0));
|
||||
}
|
||||
|
||||
TEST(SphereTest, The_normal_on_a_sphere_at_a_point_on_the_Y_axis)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Tuple n = s.normalAt(Point(0, 1, 0));
|
||||
|
||||
ASSERT_EQ(n, Vector(0, 1, 0));
|
||||
}
|
||||
|
||||
TEST(SphereTest, The_normal_on_a_sphere_at_a_point_on_the_Z_axis)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Tuple n = s.normalAt(Point(0, 0, 1));
|
||||
|
||||
ASSERT_EQ(n, Vector(0, 0, 1));
|
||||
}
|
||||
|
||||
TEST(SphereTest, The_normal_on_a_sphere_at_a_nonaxial_point)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Tuple n = s.normalAt(Point(sqrt(3)/3, sqrt(3)/3, sqrt(3)/3));
|
||||
|
||||
ASSERT_EQ(n, Vector(sqrt(3)/3, sqrt(3)/3, sqrt(3)/3));
|
||||
}
|
||||
|
||||
TEST(SphereTest, The_normal_is_a_normalise_vector)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Tuple n = s.normalAt(Point(sqrt(3)/3, sqrt(3)/3, sqrt(3)/3));
|
||||
|
||||
ASSERT_EQ(n, n.normalise());
|
||||
}
|
||||
|
||||
TEST(SphereTest, Computing_the_normal_on_a_translated_sphere)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
|
||||
s.setTransform(translation(0, 1, 0));
|
||||
|
||||
Tuple n = s.normalAt(Point(0, 1.70711, -0.70711));
|
||||
|
||||
set_equal_precision(0.0001);
|
||||
|
||||
ASSERT_EQ(n, Vector(0, 0.70711, -0.70711));
|
||||
|
||||
/* Revert to default */
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(SphereTest, Computing_the_normal_on_a_tranformed_sphere)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
|
||||
s.setTransform(scaling(1, 0.5, 1) * rotationZ(M_PI / 5));
|
||||
|
||||
Tuple n = s.normalAt(Point(0, sqrt(2)/2, -sqrt(2)/2));
|
||||
|
||||
set_equal_precision(0.0001);
|
||||
|
||||
ASSERT_EQ(n, Vector(0, 0.97014, -0.24254));
|
||||
|
||||
/* Revert to default */
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(SphereTest, A_sphere_have_a_default_material)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
Material m = Material();
|
||||
|
||||
ASSERT_EQ(s.material, m);
|
||||
}
|
||||
|
||||
TEST(SphereTest, A_sphere_may_be_assigned_a_material)
|
||||
{
|
||||
Sphere s = Sphere();
|
||||
|
||||
Material m = Material();
|
||||
m.ambient = 1;
|
||||
|
||||
s.setMaterial(m);
|
||||
|
||||
ASSERT_EQ(s.material, m);
|
||||
}
|
||||
|
||||
TEST(SphereTest, A_helper_for_producing_a_sphere_with_a_glassy_material)
|
||||
{
|
||||
GlassSphere s = GlassSphere();
|
||||
|
||||
ASSERT_EQ(s.transformMatrix, Matrix4().identity());
|
||||
ASSERT_EQ(s.material.transparency, 1.0);
|
||||
ASSERT_EQ(s.material.refractiveIndex, 1.5);
|
||||
}
|
||||
@@ -78,8 +78,8 @@ TEST(TransformationTest, Reflexion_is_scaling_by_a_negative_value)
|
||||
TEST(TransformationTest, Rotating_a_point_around_the_X_axis)
|
||||
{
|
||||
Point p = Point(0, 1, 0);
|
||||
Matrix half_quarter = rotation_x(M_PI / 4.);
|
||||
Matrix full_quarter = rotation_x(M_PI / 2.);
|
||||
Matrix half_quarter = rotationX(M_PI / 4.);
|
||||
Matrix full_quarter = rotationX(M_PI / 2.);
|
||||
|
||||
ASSERT_EQ(half_quarter * p, Point(0, sqrt(2)/2, sqrt(2)/2));
|
||||
ASSERT_EQ(full_quarter * p, Point(0, 0, 1));
|
||||
@@ -88,7 +88,7 @@ TEST(TransformationTest, Rotating_a_point_around_the_X_axis)
|
||||
TEST(TransformationTest, The_inverse_of_an_x_rotation_rotates_in_the_opposite_direction)
|
||||
{
|
||||
Point p = Point(0, 1, 0);
|
||||
Matrix half_quarter = rotation_x(M_PI / 4.);
|
||||
Matrix half_quarter = rotationX(M_PI / 4.);
|
||||
Matrix inv = half_quarter.inverse();
|
||||
|
||||
ASSERT_EQ(inv * p, Point(0, sqrt(2)/2, -sqrt(2)/2));
|
||||
@@ -97,8 +97,8 @@ TEST(TransformationTest, The_inverse_of_an_x_rotation_rotates_in_the_opposite_di
|
||||
TEST(TransformationTest, Rotating_a_point_around_the_Y_axis)
|
||||
{
|
||||
Point p = Point(0, 0, 1);
|
||||
Matrix half_quarter = rotation_y(M_PI / 4.);
|
||||
Matrix full_quarter = rotation_y(M_PI / 2.);
|
||||
Matrix half_quarter = rotationY(M_PI / 4.);
|
||||
Matrix full_quarter = rotationY(M_PI / 2.);
|
||||
|
||||
ASSERT_EQ(half_quarter * p, Point(sqrt(2)/2, 0, sqrt(2)/2));
|
||||
ASSERT_EQ(full_quarter * p, Point(1, 0, 0));
|
||||
@@ -107,8 +107,8 @@ TEST(TransformationTest, Rotating_a_point_around_the_Y_axis)
|
||||
TEST(TransformationTest, Rotating_a_point_around_the_Z_axis)
|
||||
{
|
||||
Point p = Point(0, 1, 0);
|
||||
Matrix half_quarter = rotation_z(M_PI / 4.);
|
||||
Matrix full_quarter = rotation_z(M_PI / 2.);
|
||||
Matrix half_quarter = rotationZ(M_PI / 4.);
|
||||
Matrix full_quarter = rotationZ(M_PI / 2.);
|
||||
|
||||
ASSERT_EQ(half_quarter * p, Point(-sqrt(2)/2, sqrt(2)/2, 0));
|
||||
ASSERT_EQ(full_quarter * p, Point(-1, 0, 0));
|
||||
@@ -165,7 +165,7 @@ TEST(TransformationTest, A_shearing_transformation_moves_z_in_proportion_to_y)
|
||||
TEST(TransformationTest, Individual_trnasformations_are_applied_in_sequence)
|
||||
{
|
||||
Point p = Point(1, 0, 1);
|
||||
Matrix A = rotation_x(M_PI / 2.);
|
||||
Matrix A = rotationX(M_PI / 2.);
|
||||
Matrix B = scaling(5, 5, 5);
|
||||
Matrix C = translation(10, 5, 7);
|
||||
|
||||
@@ -182,10 +182,67 @@ TEST(TransformationTest, Individual_trnasformations_are_applied_in_sequence)
|
||||
TEST(TransformationTest, Chained_transformation_must_be_applied_in_reverse_order)
|
||||
{
|
||||
Point p = Point(1, 0, 1);
|
||||
Matrix A = rotation_x(M_PI / 2.);
|
||||
Matrix A = rotationX(M_PI / 2.);
|
||||
Matrix B = scaling(5, 5, 5);
|
||||
Matrix C = translation(10, 5, 7);
|
||||
|
||||
Matrix T = C * B * A;
|
||||
ASSERT_EQ(T * p, Point(15, 0, 7));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TransformationTest, The_transformation_matrix_for_the_default_orientation)
|
||||
{
|
||||
Tuple from = Point(0, 0, 0);
|
||||
Tuple to = Point(0, 0, -1);
|
||||
Tuple up = Vector(0, 1, 0);
|
||||
|
||||
Matrix t = viewTransform(from, to, up);
|
||||
|
||||
ASSERT_EQ(t, Matrix4().identity());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransformationTest, A_view_transformation_matrix_looking_in_positive_z_direction)
|
||||
{
|
||||
Tuple from = Point(0, 0, 0);
|
||||
Tuple to = Point(0, 0, 1);
|
||||
Tuple up = Vector(0, 1, 0);
|
||||
|
||||
Matrix t = viewTransform(from, to, up);
|
||||
|
||||
ASSERT_EQ(t, scaling(-1, 1, -1));
|
||||
}
|
||||
|
||||
TEST(TransformationTest, The_view_transformation_move_the_world)
|
||||
{
|
||||
Tuple from = Point(0, 0, 8);
|
||||
Tuple to = Point(0, 0, 0);
|
||||
Tuple up = Vector(0, 1, 0);
|
||||
|
||||
Matrix t = viewTransform(from, to, up);
|
||||
|
||||
ASSERT_EQ(t, translation(0, 0, -8));
|
||||
}
|
||||
|
||||
TEST(TransformationTest, An_arbitrary_view_transformation)
|
||||
{
|
||||
Tuple from = Point(1, 3, 2);
|
||||
Tuple to = Point(4, -2, 8);
|
||||
Tuple up = Vector(1, 1, 0);
|
||||
|
||||
Matrix t = viewTransform(from, to, up);
|
||||
|
||||
double values[] = {-0.50709, 0.50709, 0.67612, -2.36643,
|
||||
0.76772, 0.60609, 0.12122, -2.82843,
|
||||
-0.35857, 0.59761, -0.71714, 0.00000,
|
||||
0.00000, 0.00000, 0.00000, 1.00000};
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(t, Matrix4(values));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <math.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(TuplesTests, Tuple_With_w_equal_1_and_is_point)
|
||||
TEST(TupleTest, Tuple_With_w_equal_1_and_is_point)
|
||||
{
|
||||
Tuple a = Tuple(4.3, -4.2, 3.1, 1.0);
|
||||
|
||||
@@ -22,7 +22,7 @@ TEST(TuplesTests, Tuple_With_w_equal_1_and_is_point)
|
||||
ASSERT_FALSE(a.isVector());
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Tuple_With_w_equal_0_and_is_vector)
|
||||
TEST(TupleTest, Tuple_With_w_equal_0_and_is_vector)
|
||||
{
|
||||
Tuple a = Tuple(4.3, -4.2, 3.1, 0.0);
|
||||
|
||||
@@ -34,21 +34,21 @@ TEST(TuplesTests, Tuple_With_w_equal_0_and_is_vector)
|
||||
ASSERT_TRUE(a.isVector());
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Point_create_tuples_with_w_equal_1)
|
||||
TEST(TupleTest, Point_create_tuples_with_w_equal_1)
|
||||
{
|
||||
Tuple a = Point(4, -4, 3);
|
||||
|
||||
ASSERT_EQ(a, Tuple(4, -4, 3, 1));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Vector_create_tuples_with_w_equal_0)
|
||||
TEST(TupleTest, Vector_create_tuples_with_w_equal_0)
|
||||
{
|
||||
Tuple a = Vector(4, -4, 3);
|
||||
|
||||
ASSERT_EQ(a, Tuple(4, -4, 3, 0));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Adding_two_tuples)
|
||||
TEST(TupleTest, Adding_two_tuples)
|
||||
{
|
||||
Tuple a1 = Tuple(3, -2, 5, 1);
|
||||
Tuple a2 = Tuple(-2, 3, 1, 0);
|
||||
@@ -56,7 +56,7 @@ TEST(TuplesTests, Adding_two_tuples)
|
||||
ASSERT_EQ(a1 + a2, Tuple(1, 1, 6, 1));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_two_points)
|
||||
TEST(TupleTest, Substracting_two_points)
|
||||
{
|
||||
Point p1 = Point(3, 2, 1);
|
||||
Point p2 = Point(5, 6, 7);
|
||||
@@ -64,7 +64,7 @@ TEST(TuplesTests, Substracting_two_points)
|
||||
ASSERT_EQ(p1 - p2, Vector(-2, -4, -6));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_a_vector_from_a_point)
|
||||
TEST(TupleTest, Substracting_a_vector_from_a_point)
|
||||
{
|
||||
Point p = Point(3, 2, 1);
|
||||
Vector v = Vector(5, 6, 7);
|
||||
@@ -72,7 +72,7 @@ TEST(TuplesTests, Substracting_a_vector_from_a_point)
|
||||
ASSERT_EQ(p - v, Point(-2, -4, -6));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_two_vectors)
|
||||
TEST(TupleTest, Substracting_two_vectors)
|
||||
{
|
||||
Vector v1 = Vector(3, 2, 1);
|
||||
Vector v2 = Vector(5, 6, 7);
|
||||
@@ -80,7 +80,7 @@ TEST(TuplesTests, Substracting_two_vectors)
|
||||
ASSERT_EQ(v1 - v2, Vector(-2, -4, -6));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_a_vector_from_zero_vector)
|
||||
TEST(TupleTest, Substracting_a_vector_from_zero_vector)
|
||||
{
|
||||
Vector zero = Vector(0, 0, 0);
|
||||
Vector v = Vector(1, -2, 3);
|
||||
@@ -88,84 +88,84 @@ TEST(TuplesTests, Substracting_a_vector_from_zero_vector)
|
||||
ASSERT_EQ(zero - v, Vector(-1, 2, -3));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Negating_a_tuple)
|
||||
TEST(TupleTest, Negating_a_tuple)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(-a, Tuple(-1, 2, -3, 4));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Multiplying_a_tuple_by_a_scalar)
|
||||
TEST(TupleTest, Multiplying_a_tuple_by_a_scalar)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(a * 3.5, Tuple(3.5, -7, 10.5, -14));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Multiplying_a_tuple_by_a_fraction)
|
||||
TEST(TupleTest, Multiplying_a_tuple_by_a_fraction)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(a * 0.5, Tuple(0.5, -1, 1.5, -2));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Dividing_a_tuple_by_a_scalar)
|
||||
TEST(TupleTest, Dividing_a_tuple_by_a_scalar)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(a / 2, Tuple(0.5, -1, 1.5, -2));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_1_0_0)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_1_0_0)
|
||||
{
|
||||
Vector v = Vector(1, 0, 0);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), 1);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_0_1_0)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_0_1_0)
|
||||
{
|
||||
Vector v = Vector(0, 1, 0);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), 1);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_0_0_1)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_0_0_1)
|
||||
{
|
||||
Vector v = Vector(0, 0, 1);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), 1);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_1_2_3)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_1_2_3)
|
||||
{
|
||||
Vector v = Vector(1, 2, 3);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), sqrt(14));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_n1_n2_n3)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_n1_n2_n3)
|
||||
{
|
||||
Vector v = Vector(-1, -2, -3);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), sqrt(14));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Nomilise_vector_4_0_0_give_1_0_0)
|
||||
TEST(TupleTest, Nomilise_vector_4_0_0_give_1_0_0)
|
||||
{
|
||||
Vector v = Vector(4, 0, 0);
|
||||
|
||||
ASSERT_EQ(v.normalise(), Vector(1, 0, 0));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Nomilise_vector_1_2_3)
|
||||
TEST(TupleTest, Nomilise_vector_1_2_3)
|
||||
{
|
||||
Vector v = Vector(1, 2, 3);
|
||||
|
||||
ASSERT_EQ(v.normalise(), Vector(1 / sqrt(14), 2 / sqrt(14), 3 / sqrt(14)));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Dot_product_of_two_tuples)
|
||||
TEST(TupleTest, Dot_product_of_two_tuples)
|
||||
{
|
||||
Vector a = Vector(1, 2, 3);
|
||||
Vector b = Vector(2, 3, 4);
|
||||
@@ -173,7 +173,7 @@ TEST(TuplesTests, Dot_product_of_two_tuples)
|
||||
ASSERT_EQ(a.dot(b), 20);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Cross_product_of_two_vector)
|
||||
TEST(TupleTest, Cross_product_of_two_vector)
|
||||
{
|
||||
Vector a = Vector(1, 2, 3);
|
||||
Vector b = Vector(2, 3, 4);
|
||||
@@ -181,3 +181,23 @@ TEST(TuplesTests, Cross_product_of_two_vector)
|
||||
ASSERT_EQ(a.cross(b), Vector(-1, 2, -1));
|
||||
ASSERT_EQ(b.cross(a), Vector(1, -2, 1));
|
||||
}
|
||||
|
||||
TEST(TupleTest, Reflecting_a_vector_approaching_at_45)
|
||||
{
|
||||
Vector v = Vector(1, -1, 0); /* Vector */
|
||||
Vector n = Vector(0, 1, 0); /* Normal */
|
||||
|
||||
Tuple r = v.reflect(n);
|
||||
|
||||
ASSERT_EQ(r, Vector(1, 1, 0));
|
||||
}
|
||||
|
||||
TEST(TupleTest, Reflecting_a_vector_off_a_slanted_surface)
|
||||
{
|
||||
Vector v = Vector(0, -1, 0); /* Vector */
|
||||
Vector n = Vector(sqrt(2)/2, sqrt(2)/2, 0); /* Normal */
|
||||
|
||||
Tuple r = v.reflect(n);
|
||||
|
||||
ASSERT_EQ(r, Vector(1, 0, 0));
|
||||
}
|
||||
429
tests/world_test.cpp
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* World unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <material.h>
|
||||
#include <transformation.h>
|
||||
#include <worldbuilder.h>
|
||||
#include <testpattern.h>
|
||||
#include <math.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <plane.h>
|
||||
|
||||
|
||||
TEST(WorldTest, Creating_a_world)
|
||||
{
|
||||
World w;
|
||||
|
||||
ASSERT_EQ(w.lightCount, 0);
|
||||
ASSERT_EQ(w.objectCount, 0);
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_default_world)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
|
||||
Light l = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
Sphere s1 = Sphere();
|
||||
Sphere s2 = Sphere();
|
||||
Material s1Mat = Material();
|
||||
s1Mat.colour = Colour(0.8, 1.0, 0.6);
|
||||
s1Mat.diffuse = 0.7;
|
||||
s1Mat.specular = 0.2;
|
||||
s1.setMaterial(s1Mat);
|
||||
|
||||
s2.setTransform(scaling(0.5, 0.5,0.5));
|
||||
|
||||
ASSERT_TRUE(w.lightIsIn(l));
|
||||
ASSERT_TRUE(w.objectIsIn(s1));
|
||||
ASSERT_TRUE(w.objectIsIn(s2));
|
||||
};
|
||||
|
||||
TEST(WorldTest, Intersect_a_world_with_a_ray)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
|
||||
Intersect xs = w.intersect(r);
|
||||
|
||||
ASSERT_EQ(xs.count(), 4);
|
||||
ASSERT_EQ(xs[0].t, 4);
|
||||
ASSERT_EQ(xs[1].t, 4.5);
|
||||
ASSERT_EQ(xs[2].t, 5.5);
|
||||
ASSERT_EQ(xs[3].t, 6);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Shading_an_intersection)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Shape *s = w.getObject(0);
|
||||
Intersection i = Intersection(4, s);
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Tuple c = w.shadeHit(comps);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(c, Colour(0.38066, 0.47583, 0.2855));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_when_ray_miss)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 1, 0));
|
||||
Tuple c = w.colourAt(r);
|
||||
|
||||
ASSERT_EQ(c, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_when_ray_hit)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Tuple c = w.colourAt(r);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(c, Colour(0.38066, 0.47583, 0.2855));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_colour_with_an_intersection_behind_the_ray)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Shape *outer = w.getObject(0);
|
||||
outer->material.ambient = 1;
|
||||
Shape *inner = w.getObject(1);
|
||||
inner->material.ambient = 1;
|
||||
|
||||
Ray r = Ray(Point(0, 0, 0.75), Vector(0, 0, -1));
|
||||
|
||||
Tuple c = w.colourAt(r);
|
||||
|
||||
ASSERT_EQ(c, inner->material.colour);
|
||||
}
|
||||
|
||||
TEST(WorldTest, There_is_no_shadow_when_nothing_is_collinear_with_point_and_light)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Tuple p = Point(0, 10, 0);
|
||||
|
||||
ASSERT_FALSE(w.isShadowed(p));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_shadow_when_an_object_is_between_the_point_and_the_light)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Tuple p = Point(10, -10, 10);
|
||||
|
||||
ASSERT_TRUE(w.isShadowed(p));
|
||||
}
|
||||
|
||||
TEST(WorldTest, There_is_no_shadow_whne_an_object_is_behing_the_light)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Tuple p = Point(-20, 20, -20);
|
||||
|
||||
ASSERT_FALSE(w.isShadowed(p));
|
||||
}
|
||||
|
||||
TEST(WorldTest, There_is_no_shadow_when_an_object_is_behing_the_point)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Tuple p = Point(-2, 2, -2);
|
||||
|
||||
ASSERT_FALSE(w.isShadowed(p));
|
||||
}
|
||||
|
||||
TEST(WorldTest, Shade_hit_is_given_an_intersection_in_shadow)
|
||||
{
|
||||
World w = World();
|
||||
|
||||
Light l = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
|
||||
w.addLight(&l);
|
||||
|
||||
Sphere s1 = Sphere();
|
||||
|
||||
w.addObject(&s1);
|
||||
|
||||
Sphere s2 = Sphere();
|
||||
s2.setTransform(translation(0, 0, 10));
|
||||
|
||||
w.addObject(&s2);
|
||||
|
||||
Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1));
|
||||
Intersection i = Intersection(4, &s2);
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Tuple c = w.shadeHit(comps);
|
||||
|
||||
ASSERT_EQ(c, Colour(0.1, 0.1, 0.1));
|
||||
};
|
||||
|
||||
TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||
|
||||
Shape *shape = w.getObject(1); /* The second object */
|
||||
shape->material.ambient = 1; /* We use this to get a predictable colour */
|
||||
|
||||
Intersection i = Intersection(1, shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Colour colour = w.reflectColour(comps);
|
||||
|
||||
ASSERT_EQ(colour, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_reflected_colour_for_a_reflective_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Plane shape = Plane();
|
||||
shape.material.reflective = 0.5;
|
||||
shape.setTransform(translation(0, -1, 0));
|
||||
w.addObject(&shape);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
|
||||
Intersection i = Intersection(sqrt(2), &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Colour colour = w.reflectColour(comps);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00002);
|
||||
|
||||
ASSERT_EQ(colour, Colour(0.19032, 0.2379, 0.14274));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Shade_hit_with_a_reflective_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Plane shape = Plane();
|
||||
shape.material.reflective = 0.5;
|
||||
shape.setTransform(translation(0, -1, 0));
|
||||
w.addObject(&shape);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
|
||||
Intersection i = Intersection(sqrt(2), &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Tuple colour = w.shadeHit(comps);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00005);
|
||||
|
||||
ASSERT_EQ(colour, Colour(0.87677, 0.92436, 0.82918));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Colour_at_with_mutually_reflective_surfaces)
|
||||
{
|
||||
World w = World();
|
||||
|
||||
Light l = Light(POINT_LIGHT, Point(0, 0, 0), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&l);
|
||||
|
||||
Plane lower = Plane();
|
||||
lower.material.reflective = 1;
|
||||
lower.setTransform(translation(0, -1, 0));
|
||||
|
||||
Plane higher = Plane();
|
||||
higher.material.reflective = 1;
|
||||
higher.setTransform(translation(0, 1, 0));
|
||||
|
||||
w.addObject(&lower);
|
||||
w.addObject(&higher);
|
||||
|
||||
Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
|
||||
|
||||
/* It should just exit, we don't care about the actual colour */
|
||||
w.colourAt(r);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_reflected_colour_at_the_maximum_recursion_depth)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Plane shape = Plane();
|
||||
shape.material.reflective = 0.5;
|
||||
shape.setTransform(translation(0, -1, 0));
|
||||
w.addObject(&shape);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
Intersection i = Intersection(sqrt(2), &shape);
|
||||
|
||||
Computation comps = i.prepareComputation(r);
|
||||
Tuple colour = w.reflectColour(comps, 0);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
ASSERT_EQ(colour, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_refracted_colour_with_an_opaque_surface)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Shape *shape = w.getObject(0);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(4, shape));
|
||||
xs.add(Intersection(6, shape));
|
||||
|
||||
Computation comps = xs[0].prepareComputation(r, &xs);
|
||||
Colour c = w.refractedColour(comps, 5);
|
||||
|
||||
ASSERT_EQ(c, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_refracted_colour_at_the_maximum_recursive_depth)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Shape *shape = w.getObject(0);
|
||||
|
||||
shape->material.transparency = 1.0;
|
||||
shape->material.refractiveIndex = 1.5;
|
||||
|
||||
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(4, shape));
|
||||
xs.add(Intersection(6, shape));
|
||||
|
||||
Computation comps = xs[0].prepareComputation(r, &xs);
|
||||
Colour c = w.refractedColour(comps, 0);
|
||||
|
||||
ASSERT_EQ(c, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_refracted_colour_under_total_internal_reflection)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Shape *shape = w.getObject(0);
|
||||
|
||||
shape->material.transparency = 1.0;
|
||||
shape->material.refractiveIndex = 1.5;
|
||||
|
||||
Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(-sqrt(2)/2, shape));
|
||||
xs.add(Intersection(sqrt(2)/2, shape));
|
||||
|
||||
Computation comps = xs[1].prepareComputation(r, &xs);
|
||||
Colour c = w.refractedColour(comps, 5);
|
||||
|
||||
ASSERT_EQ(c, Colour(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(WorldTest, The_refracted_coloud_with_a_refracted_ray)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
|
||||
Shape *A = w.getObject(0);
|
||||
A->material.ambient = 1.0;
|
||||
A->material.pattern = new TestPattern();
|
||||
|
||||
Shape *B = w.getObject(1);
|
||||
B->material.transparency = 1.0;
|
||||
B->material.refractiveIndex = 1.5;
|
||||
|
||||
Ray r = Ray(Point(0, 0, 0.1), Vector(0, 1, 0));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(-0.9899, A));
|
||||
xs.add(Intersection(-0.4899, B));
|
||||
xs.add(Intersection(0.4899, B));
|
||||
xs.add(Intersection(0.9899, A));
|
||||
|
||||
Computation comps = xs[2].prepareComputation(r, &xs);
|
||||
Colour c = w.refractedColour(comps, 5);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00005);
|
||||
|
||||
ASSERT_EQ(c, Colour(0, 0.99888, 0.04725));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Shade_hit_with_a_transparent_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
|
||||
Plane floor = Plane();
|
||||
floor.setTransform(translation(0, -1, 0));
|
||||
floor.material.transparency = 0.5;
|
||||
floor.material.refractiveIndex = 1.5;
|
||||
w.addObject(&floor);
|
||||
|
||||
Sphere ball = Sphere();
|
||||
ball.material.colour = Colour(1, 0, 0);
|
||||
ball.material.ambient = 0.5;
|
||||
ball.setTransform(translation(0, -3.5, -0.5));
|
||||
w.addObject(&ball);
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(sqrt(2), &floor));
|
||||
|
||||
Computation comps = xs[0].prepareComputation(r, &xs);
|
||||
|
||||
Tuple c = w.shadeHit(comps, 5);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(c, Colour(0.93642, 0.68642, 0.68642));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(WorldTest, Shade_hit_with_a_reflective_transparent_material)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
|
||||
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
|
||||
|
||||
Plane floor = Plane();
|
||||
floor.setTransform(translation(0, -1, 0));
|
||||
floor.material.transparency = 0.5;
|
||||
floor.material.reflective = 0.5;
|
||||
floor.material.refractiveIndex = 1.5;
|
||||
w.addObject(&floor);
|
||||
|
||||
Sphere ball = Sphere();
|
||||
ball.material.colour = Colour(1, 0, 0);
|
||||
ball.material.ambient = 0.5;
|
||||
ball.setTransform(translation(0, -3.5, -0.5));
|
||||
w.addObject(&ball);
|
||||
|
||||
Intersect xs = Intersect();
|
||||
xs.add(Intersection(sqrt(2), &floor));
|
||||
|
||||
Computation comps = xs[0].prepareComputation(r, &xs);
|
||||
|
||||
Tuple c = w.shadeHit(comps, 5);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(c, Colour(0.93391, 0.69643, 0.69243));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||