Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
656ff52204 | ||
|
|
00b283053e | ||
|
|
b799e5f819 | ||
|
|
cabe7ff147 | ||
|
|
17aebe6538 | ||
|
|
a1087a9871 | ||
|
|
c4418683c6 | ||
|
|
1900d1f45d | ||
|
|
513cd9d7eb | ||
|
|
01a0de09ab | ||
|
|
8faf1db3be | ||
|
|
79af9fbc97 | ||
|
|
1e2588441f | ||
|
|
1f4b14c896 | ||
|
|
dee4b9ae91 | ||
|
|
514bd649c1 | ||
|
|
0621ca86e1 | ||
|
|
9f2a41e6f3 | ||
|
|
95c7616646 | ||
|
|
c4c216647d | ||
|
|
fac2212661 | ||
|
|
a9321b5051 | ||
|
|
f3678992c5 |
6
.gitmodules
vendored
@@ -1,12 +1,12 @@
|
|||||||
[submodule "external/googletest"]
|
[submodule "external/googletest"]
|
||||||
path = external/googletest
|
path = external/googletest
|
||||||
url = https://github.com/google/googletest.git
|
url = https://github.com/google/googletest.git
|
||||||
[submodule "external/nanogui"]
|
|
||||||
path = external/nanogui
|
|
||||||
url = https://github.com/Godzil/nanogui.git
|
|
||||||
[submodule "external/glfw"]
|
[submodule "external/glfw"]
|
||||||
path = external/glfw
|
path = external/glfw
|
||||||
url = https://github.com/glfw/glfw.git
|
url = https://github.com/glfw/glfw.git
|
||||||
[submodule "external/lodepng"]
|
[submodule "external/lodepng"]
|
||||||
path = external/lodepng
|
path = external/lodepng
|
||||||
url = https://github.com/lvandeve/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,18 +6,36 @@ project(DoRayMe)
|
|||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
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)
|
||||||
|
|
||||||
#Add external projects that directly need to be builded
|
option(PACKAGE_TESTS "Build the tests" ON)
|
||||||
ExternalProject_Add(googletest
|
|
||||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/googletest"
|
if (COVERALLS)
|
||||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/googletest"
|
include(Coveralls)
|
||||||
CONFIGURE_COMMAND ""
|
coveralls_turn_on_coverage()
|
||||||
BUILD_COMMAND ""
|
endif()
|
||||||
INSTALL_COMMAND ""
|
|
||||||
TEST_COMMAND ""
|
|
||||||
)
|
# 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
|
# Main app
|
||||||
add_subdirectory(source)
|
add_subdirectory(source)
|
||||||
# Unit Tests
|
|
||||||
add_subdirectory(tests)
|
if(PACKAGE_TESTS OR COVERALLS)
|
||||||
|
enable_testing()
|
||||||
|
include(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
|
||||||
47
README.md
@@ -0,0 +1,47 @@
|
|||||||
|
[](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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
1
external/coveralls-cmake
vendored
Submodule
2
external/glfw
vendored
2
external/googletest
vendored
2
external/lodepng
vendored
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 |
@@ -1,16 +1,22 @@
|
|||||||
# To simplify testing, the app is build in two passes,
|
# To simplify testing, the app is build in two passes,
|
||||||
|
|
||||||
# First most is build as a library
|
# First most is build as a library
|
||||||
add_library(rayonnement STATIC math_helper.cpp)
|
add_library(rayonnement STATIC)
|
||||||
|
|
||||||
set(RAY_HEADERS include/tuples.h include/math_helper.h)
|
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/pattern/*.h)
|
||||||
set(RAY_SOURCES tuples.cpp)
|
|
||||||
|
|
||||||
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_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES})
|
||||||
|
target_link_libraries(rayonnement LodePNG)
|
||||||
|
|
||||||
# Second we build the main executable
|
# Second we build the main executable
|
||||||
add_executable(dorayme main.cpp)
|
add_executable(dorayme main.cpp)
|
||||||
target_include_directories(rayonnement PUBLIC include)
|
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;
|
||||||
|
}
|
||||||
70
source/canvas.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Canvas implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <lodepng.h>
|
||||||
|
|
||||||
|
#define BPP (24)
|
||||||
|
#define BytePP (BPP / 8)
|
||||||
|
|
||||||
|
#define MIN(_a, _b) ((_a)<(_b)?(_a):(_b))
|
||||||
|
#define MAX(_a, _b) ((_a)>(_b)?(_a):(_b))
|
||||||
|
|
||||||
|
Canvas::Canvas(uint32_t width, uint32_t height) : width(width), height(height)
|
||||||
|
{
|
||||||
|
this->bitmap = (uint8_t *)calloc(4, width * 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)
|
||||||
|
{
|
||||||
|
free(this->bitmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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::getPixel(uint32_t x, uint32_t y)
|
||||||
|
{
|
||||||
|
uint32_t offset = y * this->stride + x * BytePP;
|
||||||
|
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);
|
||||||
|
|
||||||
|
return ret == 0;
|
||||||
|
}
|
||||||
10
source/colour.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Colour implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "colour.h"
|
||||||
|
|
||||||
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 */
|
||||||
34
source/include/canvas.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Canvas header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_CANVAS_H
|
||||||
|
#define DORAYME_CANVAS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <colour.h>
|
||||||
|
|
||||||
|
class Canvas
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint8_t *bitmap;
|
||||||
|
uint32_t stride;
|
||||||
|
public:
|
||||||
|
uint32_t width, height;
|
||||||
|
|
||||||
|
Canvas(uint32_t width, uint32_t height);
|
||||||
|
Canvas(const Canvas *c);
|
||||||
|
Canvas(const Canvas &c);
|
||||||
|
~Canvas();
|
||||||
|
|
||||||
|
void putPixel(uint32_t x, uint32_t y, Tuple c);
|
||||||
|
Colour getPixel(uint32_t x, uint32_t y);
|
||||||
|
|
||||||
|
bool SaveAsPNG(const char *filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_CANVAS_H */
|
||||||
32
source/include/colour.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Colour header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_COLOUR_H
|
||||||
|
#define DORAYME_COLOUR_H
|
||||||
|
|
||||||
|
#include <tuple.h>
|
||||||
|
|
||||||
|
class Colour : public Tuple
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Colour(double red, double green, double blue) : Tuple(red, green, blue, 0) {};
|
||||||
|
double red() { return this->x; };
|
||||||
|
double green() { return this->y; };
|
||||||
|
double blue() { return this->z; };
|
||||||
|
double red(double v) { this->x = v; return v; };
|
||||||
|
double green(double v) { this->y = v; return v; };
|
||||||
|
double blue(double v) { this->z = v; return v; };
|
||||||
|
|
||||||
|
using Tuple::operator*;
|
||||||
|
Colour operator*(const Colour &b) const { return Colour(this->x * b.x,
|
||||||
|
this->y * b.y,
|
||||||
|
this->z * b.z); };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* DORAYME_COLOUR_H */
|
||||||
30
source/include/intersect.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Intersect header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_INTERSECT_H
|
||||||
|
#define DORAYME_INTERSECT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <intersection.h>
|
||||||
|
|
||||||
|
class Intersect
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
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 hit();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_INTERSECT_H */
|
||||||
82
source/include/intersection.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Intersection header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_INTERSECTION_H
|
||||||
|
#define DORAYME_INTERSECTION_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ray.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
Shape *object;
|
||||||
|
|
||||||
|
public:
|
||||||
|
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 */
|
||||||
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
|
||||||
47
source/include/material.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 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), 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) &&
|
||||||
|
(this->colour == b.colour); };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* DORAYME_MATERIAL_H */
|
||||||
@@ -10,6 +10,12 @@
|
|||||||
#ifndef DORAYME_MATH_HELPER_H
|
#ifndef DORAYME_MATH_HELPER_H
|
||||||
#define DORAYME_MATH_HELPER_H
|
#define DORAYME_MATH_HELPER_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
void set_equal_precision(double v);
|
||||||
|
double getEpsilon();
|
||||||
bool double_equal(double a, double b);
|
bool double_equal(double a, double b);
|
||||||
|
|
||||||
#endif //DORAYME_MATH_HELPER_H
|
double deg_to_rad(double deg);
|
||||||
|
|
||||||
|
#endif /* DORAYME_MATH_HELPER_H */
|
||||||
|
|||||||
75
source/include/matrix.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Matrix header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_MATRIX_H
|
||||||
|
#define DORAYME_MATRIX_H
|
||||||
|
|
||||||
|
#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:
|
||||||
|
/* 4x4 is the default */
|
||||||
|
double data[4*4];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Matrix(int size = 4);
|
||||||
|
Matrix(double values[], int size);
|
||||||
|
|
||||||
|
double get(int x, int y) const { return this->data[this->size * x + y]; };
|
||||||
|
void set(int x, int y, double v) { this->data[this->size * x + y] = v; };
|
||||||
|
|
||||||
|
Matrix identity();
|
||||||
|
Matrix transpose();
|
||||||
|
double determinant();
|
||||||
|
Matrix submatrix(int row, int column);
|
||||||
|
Matrix inverse();
|
||||||
|
double minor(int row, int column) { return this->submatrix(row, column).determinant(); }
|
||||||
|
double cofactor(int row, int column) { return (((column+row)&1)?-1:1) * this->minor(row, column); }
|
||||||
|
bool operator==(const Matrix &b) const;
|
||||||
|
bool operator!=(const Matrix &b) const;
|
||||||
|
|
||||||
|
bool isInvertible() { return this->determinant() != 0; }
|
||||||
|
|
||||||
|
Matrix operator*(const Matrix &b) const;
|
||||||
|
Tuple operator*(const Tuple &b) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Matrix4: public Matrix
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Matrix4() : Matrix(4) { };
|
||||||
|
Matrix4(double values[]) : Matrix(values, 4) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
class Matrix3 : public Matrix
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Matrix3() : Matrix(3) { };
|
||||||
|
Matrix3(double values[]) : Matrix(values, 3) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
class Matrix2 : public Matrix
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using Matrix::data;
|
||||||
|
public:
|
||||||
|
Matrix2() : Matrix(2) { };
|
||||||
|
Matrix2(double values[]) : Matrix(values, 2) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_MATRIX_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
|
||||||
25
source/include/ray.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Ray header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_RAY_H
|
||||||
|
#define DORAYME_RAY_H
|
||||||
|
|
||||||
|
#include <tuple.h>
|
||||||
|
|
||||||
|
class Ray
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Tuple direction;
|
||||||
|
Tuple origin;
|
||||||
|
|
||||||
|
Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { };
|
||||||
|
|
||||||
|
Tuple position(double t) { return this->origin + this->direction * t; };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_RAY_H */
|
||||||
59
source/include/shape.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
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 */
|
||||||
34
source/include/sphere.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Sphere header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_SPHERE_H
|
||||||
|
#define DORAYME_SPHERE_H
|
||||||
|
|
||||||
|
#include <shape.h>
|
||||||
|
#include <ray.h>
|
||||||
|
#include <intersect.h>
|
||||||
|
|
||||||
|
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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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
|
||||||
26
source/include/transformation.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Transformation header
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DORAYME_TRANSFORMATION_H
|
||||||
|
#define DORAYME_TRANSFORMATION_H
|
||||||
|
|
||||||
|
#include <matrix.h>
|
||||||
|
|
||||||
|
Matrix translation(double x, double y, double z);
|
||||||
|
|
||||||
|
Matrix scaling(double x, double y, double z);
|
||||||
|
|
||||||
|
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 */
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
* Copyright (c) 2020 986-Studio.
|
* Copyright (c) 2020 986-Studio.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef DORAYME_TUPLES_H
|
#ifndef DORAYME_TUPLE_H
|
||||||
#define DORAYME_TUPLES_H
|
#define DORAYME_TUPLE_H
|
||||||
|
|
||||||
#include <math_helper.h>
|
#include <math_helper.h>
|
||||||
|
|
||||||
@@ -41,6 +41,8 @@ public:
|
|||||||
double magnitude();
|
double magnitude();
|
||||||
Tuple normalise();
|
Tuple normalise();
|
||||||
double dot(const Tuple &b);
|
double dot(const Tuple &b);
|
||||||
|
Tuple cross(const Tuple &b) const;
|
||||||
|
Tuple reflect(const Tuple &normal);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Point: public Tuple
|
class Point: public Tuple
|
||||||
@@ -53,7 +55,6 @@ class Vector: public Tuple
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Vector(double x, double y, double z) : Tuple(x, y, z, 0.0) {};
|
Vector(double x, double y, double z) : Tuple(x, y, z, 0.0) {};
|
||||||
Vector cross(const Vector &b) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DORAYME_TUPLES_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);
|
||||||
|
|
||||||
|
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
|
||||||
|
Colour refractedColour(Computation comps, uint32_t depthCount = 4);
|
||||||
|
|
||||||
|
Intersect intersect(Ray r);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_WORLD_H */
|
||||||
29
source/include/worldbuilder.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
/* Let's keep a single header for now, will see later */
|
||||||
|
|
||||||
|
class DefaultWorld : public World
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DefaultWorld();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Not implemented yet */
|
||||||
|
class Hw3File : public World
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Hw3File(const char *filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DORAYME_WORLDBUILDER_H */
|
||||||
66
source/intersect.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Intersect implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math_helper.h>
|
||||||
|
#include <intersect.h>
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#define MIN_ALLOC (2)
|
||||||
|
|
||||||
|
Intersect::Intersect()
|
||||||
|
{
|
||||||
|
this->allocated = MIN_ALLOC;
|
||||||
|
this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
|
||||||
|
this->num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intersect::~Intersect()
|
||||||
|
{
|
||||||
|
/* Free stuff */
|
||||||
|
}
|
||||||
|
|
||||||
|
void Intersect::add(Intersection i)
|
||||||
|
{
|
||||||
|
Intersection *x;
|
||||||
|
int j, k;
|
||||||
|
|
||||||
|
if ((this->num + 1) > this->allocated)
|
||||||
|
{
|
||||||
|
this->allocated *= 2;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intersection Intersect::hit()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < this->num; i++)
|
||||||
|
{
|
||||||
|
if (this->list[i]->t >= 0)
|
||||||
|
return *this->list[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -11,7 +11,24 @@
|
|||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <math_helper.h>
|
#include <math_helper.h>
|
||||||
|
|
||||||
|
static double current_precision = FLT_EPSILON;
|
||||||
|
|
||||||
|
void set_equal_precision(double v)
|
||||||
|
{
|
||||||
|
current_precision = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getEpsilon()
|
||||||
|
{
|
||||||
|
return current_precision;
|
||||||
|
}
|
||||||
|
|
||||||
bool double_equal(double a, double b)
|
bool double_equal(double a, double b)
|
||||||
{
|
{
|
||||||
return fabs(a - b) < DBL_EPSILON;
|
return fabs(a - b) < current_precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double deg_to_rad(double deg)
|
||||||
|
{
|
||||||
|
return deg * M_PI / 180.;
|
||||||
|
}
|
||||||
201
source/matrix.cpp
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Matrix implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <matrix.h>
|
||||||
|
#include <tuple.h>
|
||||||
|
#include <math_helper.h>
|
||||||
|
|
||||||
|
Matrix::Matrix(int width)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
this->size = width;
|
||||||
|
|
||||||
|
for(i = 0; i < width*width; i++)
|
||||||
|
{
|
||||||
|
this->data[i] = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix::Matrix(double values[], int width)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
this->size = width;
|
||||||
|
|
||||||
|
for(y = 0; y < this->size; y++)
|
||||||
|
{
|
||||||
|
for (x = 0 ; x < this->size ; x++)
|
||||||
|
{
|
||||||
|
this->data[this->size * x + y] = values[this->size * x + y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Matrix::operator==(const Matrix &b) const
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (this->size != b.size)
|
||||||
|
{
|
||||||
|
/* If they are not the same size don't even bother */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < this->size*this->size; i++)
|
||||||
|
{
|
||||||
|
if (!double_equal(this->data[i], b.data[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Matrix::operator!=(const Matrix &b) const
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (this->size != b.size)
|
||||||
|
{
|
||||||
|
/* If they are not the same size don't even bother */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < this->size*this->size; i++)
|
||||||
|
{
|
||||||
|
if (!double_equal(this->data[i], b.data[i]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::operator*(const Matrix &b) const
|
||||||
|
{
|
||||||
|
int x, y, k;
|
||||||
|
Matrix ret = Matrix(this->size);
|
||||||
|
|
||||||
|
if (this->size == b.size)
|
||||||
|
{
|
||||||
|
for (y = 0 ; y < this->size ; y++)
|
||||||
|
{
|
||||||
|
for (x = 0 ; x < this->size ; x++)
|
||||||
|
{
|
||||||
|
double v = 0;
|
||||||
|
for (k = 0 ; k < this->size ; k++)
|
||||||
|
{
|
||||||
|
v += this->get(x, k) * b.get(k, y);
|
||||||
|
}
|
||||||
|
ret.set(x, y, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple Matrix::operator*(const Tuple &b) const
|
||||||
|
{
|
||||||
|
return Tuple(b.x * this->get(0, 0) + b.y * this->get(0, 1) + b.z * this->get(0, 2) + b.w * this->get(0, 3),
|
||||||
|
b.x * this->get(1, 0) + b.y * this->get(1, 1) + b.z * this->get(1, 2) + b.w * this->get(1, 3),
|
||||||
|
b.x * this->get(2, 0) + b.y * this->get(2, 1) + b.z * this->get(2, 2) + b.w * this->get(2, 3),
|
||||||
|
b.x * this->get(3, 0) + b.y * this->get(3, 1) + b.z * this->get(3, 2) + b.w * this->get(3, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::identity()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < this->size; i++)
|
||||||
|
{
|
||||||
|
this->set(i, i, 1);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::transpose()
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
Matrix ret = Matrix(this->size);
|
||||||
|
for (y = 0 ; y < this->size ; y++)
|
||||||
|
{
|
||||||
|
for (x = 0 ; x < this->size ; x++)
|
||||||
|
{
|
||||||
|
ret.set(y, x, this->get(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::submatrix(int row, int column)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int x = 0, y = 0;
|
||||||
|
Matrix ret = Matrix(this->size - 1);
|
||||||
|
for (i = 0 ; i < this->size ; i++)
|
||||||
|
{
|
||||||
|
if (i == row)
|
||||||
|
{
|
||||||
|
/* Skip that row */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
y = 0;
|
||||||
|
for (j = 0 ; j < this->size ; j++)
|
||||||
|
{
|
||||||
|
if (j == column)
|
||||||
|
{
|
||||||
|
/* skip that column */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret.set(x, y, this->get(i, j));
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Matrix::determinant()
|
||||||
|
{
|
||||||
|
double det = 0;
|
||||||
|
if (this->size == 2)
|
||||||
|
{
|
||||||
|
det = this->data[0] * this->data[3] - this->data[1] * this->data[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int col;
|
||||||
|
for(col = 0; col < this->size; col++)
|
||||||
|
{
|
||||||
|
det = det + this->get(0, col) * this->cofactor(0, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return det;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::inverse()
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix(this->size);
|
||||||
|
|
||||||
|
if (this->isInvertible())
|
||||||
|
{
|
||||||
|
int row, col;
|
||||||
|
double c;
|
||||||
|
for (row = 0; row < this->size; row++)
|
||||||
|
{
|
||||||
|
for (col = 0; col < this->size; col++)
|
||||||
|
{
|
||||||
|
c = this->cofactor(row, col);
|
||||||
|
ret.set(col, row, c / this->determinant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
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 */
|
||||||
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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
66
source/shapes/material.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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 diffuseColour = Colour(0, 0, 0);
|
||||||
|
Tuple specularColour = Colour(0, 0, 0);
|
||||||
|
Tuple finalColour = Colour(0, 0, 0);
|
||||||
|
|
||||||
|
double lightDotNormal, reflectDotEye;
|
||||||
|
|
||||||
|
ambientColour = effectiveColour * this->ambient;
|
||||||
|
|
||||||
|
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 = 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);
|
||||||
|
}
|
||||||
8
source/shapes/ray.cpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Ray implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
46
source/shapes/shape.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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->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();
|
||||||
|
}
|
||||||
41
source/shapes/sphere.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Sphere implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <sphere.h>
|
||||||
|
#include <ray.h>
|
||||||
|
#include <tuple.h>
|
||||||
|
#include <intersect.h>
|
||||||
|
|
||||||
|
Intersect Sphere::localIntersect(Ray r)
|
||||||
|
{
|
||||||
|
Intersect ret;
|
||||||
|
double a, b, c, discriminant;
|
||||||
|
|
||||||
|
Tuple sphere_to_ray = r.origin - Point(0, 0, 0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (discriminant >= 0)
|
||||||
|
{
|
||||||
|
ret.add(Intersection((-b - sqrt(discriminant)) / (2 * a), this));
|
||||||
|
ret.add(Intersection((-b + sqrt(discriminant)) / (2 * a), this));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
99
source/transformation.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Transformation implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <transformation.h>
|
||||||
|
|
||||||
|
Matrix translation(double x, double y, double z)
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix4().identity();
|
||||||
|
|
||||||
|
ret.set(0, 3, x);
|
||||||
|
ret.set(1, 3, y);
|
||||||
|
ret.set(2, 3, z);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix scaling(double x, double y, double z)
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix4();
|
||||||
|
|
||||||
|
ret.set(0, 0, x);
|
||||||
|
ret.set(1, 1, y);
|
||||||
|
ret.set(2, 2, z);
|
||||||
|
ret.set(3, 3, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix rotationX(double angle)
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix4().identity();
|
||||||
|
|
||||||
|
ret.set(1, 1, cos(angle));
|
||||||
|
ret.set(1, 2, -sin(angle));
|
||||||
|
ret.set(2, 1, sin(angle));
|
||||||
|
ret.set(2, 2, cos(angle));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix rotationY(double angle)
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix4().identity();
|
||||||
|
|
||||||
|
ret.set(0, 0, cos(angle));
|
||||||
|
ret.set(0, 2, sin(angle));
|
||||||
|
ret.set(2, 0, -sin(angle));
|
||||||
|
ret.set(2, 2, cos(angle));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix rotationZ(double angle)
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix4().identity();
|
||||||
|
|
||||||
|
ret.set(0, 0, cos(angle));
|
||||||
|
ret.set(0, 1, -sin(angle));
|
||||||
|
ret.set(1, 0, sin(angle));
|
||||||
|
ret.set(1, 1, cos(angle));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix shearing(double Xy, double Xz, double Yx, double Yz, double Zx, double Zy)
|
||||||
|
{
|
||||||
|
Matrix ret = Matrix4().identity();
|
||||||
|
|
||||||
|
ret.set(0, 1, Xy);
|
||||||
|
ret.set(0, 2, Xz);
|
||||||
|
ret.set(1, 0, Yx);
|
||||||
|
ret.set(1, 2, Yz);
|
||||||
|
ret.set(2, 0, Zx);
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* Copyright (c) 2020 986-Studio.
|
* Copyright (c) 2020 986-Studio.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <tuples.h>
|
#include <tuple.h>
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
@@ -27,9 +27,15 @@ double Tuple::dot(const Tuple &b)
|
|||||||
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
|
return this->x * b.x + this->y * b.y + this->z * b.z + this->w * b.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector Vector::cross(const Vector &b) const
|
Tuple Tuple::cross(const Tuple &b) const
|
||||||
{
|
{
|
||||||
return Vector(this->y * b.z - this->z * b.y,
|
return Tuple(this->y * b.z - this->z * b.y,
|
||||||
this->z * b.x - this->x * b.z,
|
this->z * b.x - this->x * b.z,
|
||||||
this->x * b.y - this->y * b.x);
|
this->x * b.y - this->y * b.x,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple Tuple::reflect(const Tuple &normal)
|
||||||
|
{
|
||||||
|
return *this - normal * 2 * this->dot(normal);
|
||||||
}
|
}
|
||||||
187
source/world.cpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
/* TODO: Add support for more than one light */
|
||||||
|
|
||||||
|
bool isThereAnObstacle = this->isShadowed(comps.overHitPoint);
|
||||||
|
|
||||||
|
Tuple surface = comps.object->material.lighting(*this->lightList[0], 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)
|
||||||
|
{
|
||||||
|
/* TODO: Add support for more than one light */
|
||||||
|
|
||||||
|
Tuple v = this->lightList[0]->position - point;
|
||||||
|
double distance = v.magnitude();
|
||||||
|
Tuple direction = v.normalise();
|
||||||
|
|
||||||
|
Ray r = Ray(point, direction);
|
||||||
|
Intersection h = this->intersect(r).hit();
|
||||||
|
|
||||||
|
if (!h.nothing() && (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);
|
||||||
|
}
|
||||||
307
source/worldbuilder/hw3file.cpp
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Hw3file implementation
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/* Don't build for now */
|
||||||
|
#if 0
|
||||||
|
/* This file is parsing a text format from another raytracer I made in the past. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define IS_CMD(_cmd) (strncmp(buffer, _cmd, sizeof(_cmd)) == 0)
|
||||||
|
|
||||||
|
typedef void (*cmdFunc)(scene *sc, uint32_t curLine, char *first, float argv[15]);
|
||||||
|
|
||||||
|
typedef struct cmd_def
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
int8_t numparam; /* -1 == one string, else >0 = number of argv */
|
||||||
|
cmdFunc f;
|
||||||
|
} cmd_def;
|
||||||
|
|
||||||
|
void cmdCamera (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->cam.seteye(point(argv[0], argv[1], argv[2]));
|
||||||
|
sc->cam.setat(point(argv[3], argv[4], argv[5]));
|
||||||
|
sc->cam.setup(vector(argv[6], argv[7], argv[8]));
|
||||||
|
sc->cam.setfovy(argv[9]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdSphere (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
/* Instanciate a sphere */
|
||||||
|
sphere *sp = new sphere(point(argv[0], argv[1], argv[2]), argv[3], sc->getMatrix());
|
||||||
|
sp->ambient = sc->ambient;
|
||||||
|
sp->diffuse = sc->diffuse;
|
||||||
|
sp->specular = sc->specular;
|
||||||
|
sp->emission = sc->emission;
|
||||||
|
sp->shininess = sc->shininess;
|
||||||
|
sp->sourceLine = curLine;
|
||||||
|
sc->o[sc->o_count] = sp;
|
||||||
|
sc->o_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdCube (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void cmdMaxVerts (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->vertexCount = (uint32_t) argv[0];
|
||||||
|
sc->vertex = new point[sc->vertexCount];
|
||||||
|
sc->curVertex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdMaxVertN (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
/* ignore for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdVertex (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->vertex[sc->curVertex].set(argv[0], argv[1], argv[2]);
|
||||||
|
sc->curVertex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdVertexN (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
/* ignore for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdTri (scene *sc, uint32_t curLine, char *first, float 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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdTriN (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
/* ignore for noz */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cmdTrans (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
matrix m;
|
||||||
|
m.translation(argv[0], argv[1], argv[2]);
|
||||||
|
sc->applyTransform(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdRotate (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
matrix m;
|
||||||
|
vector axis;
|
||||||
|
axis.set(argv[0], argv[1], argv[2]);
|
||||||
|
m.rotation(axis, argv[3]);
|
||||||
|
sc->applyTransform(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdScale (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
matrix m;
|
||||||
|
m.scale(argv[0], argv[1], argv[2]);
|
||||||
|
sc->applyTransform(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdPushT (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->pushMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdPopT (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->popMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void cmdLDire (scene *sc, uint32_t curLine, char *first, float 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++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cmdLPoint (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
/* create point light */
|
||||||
|
light *cur = new light();
|
||||||
|
|
||||||
|
cur->type = LIGHT_POINT;
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void cmdAtten (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->attenuation = vector(argv[0], argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cmdAmbient (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->ambient = color(argv[0], argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdDiffuse (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->diffuse = color(argv[0], argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdSpecular (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->specular = color(argv[0], argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdShine (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->shininess = argv[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmdEmission (scene *sc, uint32_t curLine, char *first, float argv[15])
|
||||||
|
{
|
||||||
|
sc->emission = color(argv[0], argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
{ "ambient", 3, cmdAmbient },
|
||||||
|
{ "diffuse", 3, cmdDiffuse },
|
||||||
|
{ "specular", 3, cmdSpecular },
|
||||||
|
{ "shininess", 1, cmdShine },
|
||||||
|
{ "emission", 3, cmdEmission },
|
||||||
|
};
|
||||||
|
#define CMD_COUNT (sizeof(commandList) / sizeof(cmd_def))
|
||||||
|
|
||||||
|
|
||||||
|
scene *readfile::read(char *filename)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
scene *sc = new scene;
|
||||||
|
if (sc != NULL)
|
||||||
|
{
|
||||||
|
fp = fopen(filename, "rt");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
delete sc;
|
||||||
|
sc = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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 */
|
||||||
|
//printf("::%d:> %s", strlen(buffer), buffer);
|
||||||
|
for (i = 0; i < CMD_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (strncmp(buffer, commandList[i].name, strlen(commandList[i].name)) == 0)
|
||||||
|
{
|
||||||
|
char first[512];
|
||||||
|
float 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);
|
||||||
|
sc = NULL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandList[i].f(sc, line, first, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef USE_OCTREE
|
||||||
|
sc->createOctree();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,10 +3,67 @@ project(DoRayTested)
|
|||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
set(TESTS_SRC tuples_test.cpp)
|
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 light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
|
||||||
|
shape_test.cpp plane_test.cpp pattern_test.cpp)
|
||||||
|
|
||||||
add_executable(testMyRays)
|
add_executable(testMyRays)
|
||||||
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||||
target_include_directories(testMyRays PUBLIC ../source/include)
|
target_include_directories(testMyRays PUBLIC ../source/include)
|
||||||
target_sources(testMyRays PRIVATE ${TESTS_SRC})
|
target_sources(testMyRays PRIVATE ${TESTS_SRC})
|
||||||
target_link_libraries(testMyRays gtest gtest_main rayonnement Threads::Threads)
|
target_link_libraries(testMyRays gtest gtest_main rayonnement Threads::Threads)
|
||||||
|
|
||||||
|
gtest_discover_tests(testMyRays
|
||||||
|
WORKING_DIRECTORY ${PROJECT_DIR}
|
||||||
|
PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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_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>)
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
71
tests/canvas_test.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Canvas unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <colour.h>
|
||||||
|
#include <canvas.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(CanvasTest, Creating_a_canvas)
|
||||||
|
{
|
||||||
|
Canvas c = Canvas(10, 20);
|
||||||
|
int x, y;
|
||||||
|
ASSERT_EQ(c.width, 10);
|
||||||
|
ASSERT_EQ(c.height, 20);
|
||||||
|
for(y = 0; y < 20; y++)
|
||||||
|
{
|
||||||
|
for(x = 0; x < 10; x++)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(c.getPixel(x, y), Colour(0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasTest, Test_Writing_pixels_to_a_canvas_Test)
|
||||||
|
{
|
||||||
|
Canvas c = Canvas(10, 20);
|
||||||
|
Colour red = Colour(1, 0, 0);
|
||||||
|
|
||||||
|
c.putPixel(2, 3, red);
|
||||||
|
|
||||||
|
ASSERT_EQ(c.getPixel(2, 3), red);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasTest, Save_a_PNG_file)
|
||||||
|
{
|
||||||
|
Canvas c = Canvas(5, 3);
|
||||||
|
Colour c1 = Colour(1.5, 0, 0);
|
||||||
|
Colour c2 = Colour(0, 0.5, 0);
|
||||||
|
Colour c3 = Colour(-0.5, 0, 1);
|
||||||
|
|
||||||
|
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(1920, 1080, 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, 100, 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;
|
||||||
|
}
|
||||||
46
tests/ch5_test.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Render test for chapter 5 "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();
|
||||||
|
Colour red = Colour(1, 0, 0);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!xs.hit().nothing())
|
||||||
|
{
|
||||||
|
c.putPixel(x, y, red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SaveAsPNG("ch5_test.png");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
51
tests/colour_test.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Colour unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <colour.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(ColourTest, Color_is_tuple)
|
||||||
|
{
|
||||||
|
Colour c = Colour(-0.5, 0.4, 1.7);
|
||||||
|
|
||||||
|
ASSERT_EQ(c.red(), -0.5);
|
||||||
|
ASSERT_EQ(c.green(), 0.4);
|
||||||
|
ASSERT_EQ(c.blue(), 1.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ColourTest, Adding_colours)
|
||||||
|
{
|
||||||
|
Colour c1 = Colour(0.9, 0.6, 0.75);
|
||||||
|
Colour c2 = Colour(0.7, 0.1, 0.25);
|
||||||
|
|
||||||
|
ASSERT_EQ(c1 + c2, Colour(1.6, 0.7, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ColourTest, Substracting_colours)
|
||||||
|
{
|
||||||
|
Colour c1 = Colour(0.9, 0.6, 0.75);
|
||||||
|
Colour c2 = Colour(0.7, 0.1, 0.25);
|
||||||
|
|
||||||
|
ASSERT_EQ(c1 - c2, Colour(0.2, 0.5, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ColourTest, Multiplying_colour_by_a_scalar)
|
||||||
|
{
|
||||||
|
Colour c = Colour(0.2, 0.3, 0.4);
|
||||||
|
|
||||||
|
ASSERT_EQ(c * 2, Colour(0.4, 0.6, 0.8));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ColourTest, Multiplying_colours)
|
||||||
|
{
|
||||||
|
Colour c1 = Colour(1, 0.2, 0.4);
|
||||||
|
Colour c2 = Colour(0.9, 1, 0.1);
|
||||||
|
|
||||||
|
ASSERT_EQ(c1 * c2, Colour(0.9, 0.2, 0.04));
|
||||||
|
}
|
||||||
305
tests/intersect_test.cpp
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Intersect unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#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;
|
||||||
|
|
||||||
|
ASSERT_EQ(i.count(), 0);
|
||||||
|
|
||||||
|
i.add(Intersection(1.0, nullptr));
|
||||||
|
i.add(Intersection(4.2, nullptr));
|
||||||
|
|
||||||
|
ASSERT_EQ(i.count(), 2);
|
||||||
|
|
||||||
|
ASSERT_EQ(i[0].t, 1.0);
|
||||||
|
ASSERT_EQ(i[1].t, 4.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, An_intersection_encapsulate_t_and_object)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersection i = Intersection(3.5, &s);
|
||||||
|
|
||||||
|
ASSERT_EQ(i.t, 3.5);
|
||||||
|
ASSERT_EQ(i.object, (Shape *)&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, Aggregating_intersections)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersection i1 = Intersection(1, &s);
|
||||||
|
Intersection i2 = Intersection(2, &s);
|
||||||
|
|
||||||
|
Intersect xs = Intersect();
|
||||||
|
xs.add(i1);
|
||||||
|
xs.add(i2);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].t, 1);
|
||||||
|
ASSERT_EQ(xs[1].t, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, Intersect_sets_the_object_on_the_intersection)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].object, (Shape *)&s);
|
||||||
|
ASSERT_EQ(xs[1].object, (Shape *)&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, The_hit_when_all_intersection_have_positive_t)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = Intersect();
|
||||||
|
|
||||||
|
Intersection i1 = Intersection(1, &s);
|
||||||
|
Intersection i2 = Intersection(2, &s);
|
||||||
|
|
||||||
|
xs.add(i1);
|
||||||
|
xs.add(i2);
|
||||||
|
|
||||||
|
Intersection i = xs.hit();
|
||||||
|
|
||||||
|
ASSERT_EQ(i, i1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, The_hit_when_some_intersection_have_negative_t)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = Intersect();
|
||||||
|
|
||||||
|
Intersection i1 = Intersection(-1, &s);
|
||||||
|
Intersection i2 = Intersection(2, &s);
|
||||||
|
Intersection i3 = Intersection(12, &s);
|
||||||
|
|
||||||
|
xs.add(i1);
|
||||||
|
xs.add(i2);
|
||||||
|
xs.add(i3);
|
||||||
|
|
||||||
|
Intersection i = xs.hit();
|
||||||
|
|
||||||
|
ASSERT_EQ(i, i2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, The_hit_when_all_intersection_have_negative_t)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = Intersect();
|
||||||
|
|
||||||
|
Intersection i1 = Intersection(-2, &s);
|
||||||
|
Intersection i2 = Intersection(-1, &s);
|
||||||
|
|
||||||
|
xs.add(i1);
|
||||||
|
xs.add(i2);
|
||||||
|
|
||||||
|
Intersection i = xs.hit();
|
||||||
|
|
||||||
|
ASSERT_TRUE(i.nothing());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IntersectTest, The_hit_is_always_the_lowest_nonnegative_intersection)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = Intersect();
|
||||||
|
|
||||||
|
Intersection i1 = Intersection(5, &s);
|
||||||
|
Intersection i2 = Intersection(7, &s);
|
||||||
|
Intersection i3 = Intersection(-3, &s);
|
||||||
|
Intersection i4 = Intersection(2, &s);
|
||||||
|
|
||||||
|
xs.add(i1);
|
||||||
|
xs.add(i2);
|
||||||
|
xs.add(i3);
|
||||||
|
xs.add(i4);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
407
tests/matrix_test.cpp
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Matric unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <matrix.h>
|
||||||
|
#include <tuple.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
TEST(MatrixTest, Constructing_and_inspecting_a_4x4_Matrix)
|
||||||
|
{
|
||||||
|
double values[] = {1, 2, 3, 4,
|
||||||
|
5.5, 6.5, 7.5, 8.5,
|
||||||
|
9, 10, 11, 12,
|
||||||
|
13.5, 14.5, 15.5, 16.5};
|
||||||
|
|
||||||
|
Matrix4 m = Matrix4(values);
|
||||||
|
|
||||||
|
ASSERT_EQ(m.get(0, 0), 1);
|
||||||
|
ASSERT_EQ(m.get(0, 3), 4);
|
||||||
|
ASSERT_EQ(m.get(1, 0), 5.5);
|
||||||
|
ASSERT_EQ(m.get(1, 2), 7.5);
|
||||||
|
ASSERT_EQ(m.get(2, 2), 11);
|
||||||
|
ASSERT_EQ(m.get(3, 0), 13.5);
|
||||||
|
ASSERT_EQ(m.get(3, 2), 15.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Change_a_single_value_and_check_it)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
m.set(0, 0, 12);
|
||||||
|
|
||||||
|
ASSERT_EQ(m.get(0, 0), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, A_3x3_matrix_ought_to_be_representable)
|
||||||
|
{
|
||||||
|
double values[] = {-3, 5, 0,
|
||||||
|
1, -2, -7,
|
||||||
|
0, 1, 1};
|
||||||
|
|
||||||
|
Matrix3 m = Matrix3(values);
|
||||||
|
|
||||||
|
ASSERT_EQ(m.get(0, 0), -3);
|
||||||
|
ASSERT_EQ(m.get(1, 1), -2);
|
||||||
|
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,
|
||||||
|
5, 6, 7, 8,
|
||||||
|
9, 8, 7, 6,
|
||||||
|
5, 4, 3, 2};
|
||||||
|
|
||||||
|
double values2[] = {1, 2, 3, 4,
|
||||||
|
5, 6, 7, 8,
|
||||||
|
9, 8, 7, 6,
|
||||||
|
5, 4, 3, 2};
|
||||||
|
Matrix4 A = Matrix4(values1);
|
||||||
|
Matrix4 B = Matrix4(values2);
|
||||||
|
|
||||||
|
ASSERT_EQ(A, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Matrix_equality_with_different_matrix)
|
||||||
|
{
|
||||||
|
double values1[] = {1, 2, 3, 4,
|
||||||
|
5, 6, 7, 8,
|
||||||
|
9, 8, 7, 6,
|
||||||
|
5, 4, 3, 2};
|
||||||
|
|
||||||
|
double values2[] = {2, 3, 4, 5,
|
||||||
|
6, 7, 8, 9,
|
||||||
|
8, 7, 6, 5,
|
||||||
|
4, 3, 2, 1};
|
||||||
|
Matrix4 A = Matrix4(values1);
|
||||||
|
Matrix4 B = Matrix4(values2);
|
||||||
|
|
||||||
|
ASSERT_NE(A, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Multiplying_two_matrices)
|
||||||
|
{
|
||||||
|
double values1[] = {1, 2, 3, 4,
|
||||||
|
5, 6, 7, 8,
|
||||||
|
9, 8, 7, 6,
|
||||||
|
5, 4, 3, 2};
|
||||||
|
|
||||||
|
double values2[] = {-2, 1, 2, 3,
|
||||||
|
3, 2, 1, -1,
|
||||||
|
4, 3, 6, 5,
|
||||||
|
1, 2, 7, 8};
|
||||||
|
|
||||||
|
double results[] = {20, 22, 50, 48,
|
||||||
|
44, 54, 114, 108,
|
||||||
|
40, 58, 110, 102,
|
||||||
|
16, 26, 46, 42};
|
||||||
|
|
||||||
|
Matrix4 A = Matrix4(values1);
|
||||||
|
Matrix4 B = Matrix4(values2);
|
||||||
|
|
||||||
|
|
||||||
|
ASSERT_EQ(A * B, Matrix4(results));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, A_matrix_multiplyed_by_a_tuple)
|
||||||
|
{
|
||||||
|
double valuesA[] = {1, 2, 3, 4,
|
||||||
|
2, 4, 4, 2,
|
||||||
|
8, 6, 4, 1,
|
||||||
|
0, 0, 0, 1};
|
||||||
|
|
||||||
|
Matrix4 A = Matrix4(valuesA);
|
||||||
|
Tuple b = Tuple(1, 2, 3, 1);
|
||||||
|
|
||||||
|
ASSERT_EQ(A * b, Tuple(18, 24, 33, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Multiplying_a_matrix_by_the_identity_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = {0, 1, 2, 4,
|
||||||
|
1, 2, 4, 8,
|
||||||
|
2, 4, 8, 16,
|
||||||
|
4, 8, 16, 32};
|
||||||
|
|
||||||
|
Matrix4 A = Matrix4(valuesA);
|
||||||
|
Matrix ident = Matrix4().identity();
|
||||||
|
|
||||||
|
ASSERT_EQ(A * ident, A);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Multiplying_the_identity_matrix_by_a_tuple)
|
||||||
|
{
|
||||||
|
Tuple a = Tuple(1, 2, 3, 4);
|
||||||
|
Matrix ident = Matrix4().identity();
|
||||||
|
|
||||||
|
ASSERT_EQ(ident * a, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Transposing_a_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = {0, 9, 3, 0,
|
||||||
|
9, 8, 0, 8,
|
||||||
|
1, 8, 5, 3,
|
||||||
|
0, 0, 5, 8};
|
||||||
|
|
||||||
|
double results[] = {0, 9, 1, 0,
|
||||||
|
9, 8, 8, 0,
|
||||||
|
3, 0, 5, 5,
|
||||||
|
0, 8, 3, 8};
|
||||||
|
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.transpose(), Matrix4(results));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Transposing_this_identity_matrix)
|
||||||
|
{
|
||||||
|
Matrix ident = Matrix4().identity();
|
||||||
|
|
||||||
|
ASSERT_EQ(ident.transpose(), ident);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_the_determinant_of_a_2x2_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 1, 5,
|
||||||
|
-3, 2 };
|
||||||
|
Matrix2 A = Matrix2(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.determinant(), 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, A_submatrix_of_a_3x3_matrix_is_a_2x2_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 1, 5, 0,
|
||||||
|
-3, 2, 7,
|
||||||
|
0, 6, -3 };
|
||||||
|
double results[] = { -3, 2,
|
||||||
|
0, 6 };
|
||||||
|
Matrix3 A = Matrix3(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.submatrix(0, 2), Matrix2(results));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, A_submatrix_of_a_4x4_matrix_is_a_3x3_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { -6, 1, 1, 6,
|
||||||
|
-8, 5, 8, 6,
|
||||||
|
-1, 0, 8, 2,
|
||||||
|
-7, 1, -1, 1 };
|
||||||
|
double results[] = { -6, 1, 6,
|
||||||
|
-8, 8, 6,
|
||||||
|
-7,-1, 1 };
|
||||||
|
Matrix4 A = Matrix4(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.submatrix(2, 1), Matrix3(results));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculate_a_minor_of_a_3x3_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 3, 5, 0,
|
||||||
|
2, -1, -7,
|
||||||
|
6, -1, 5 };
|
||||||
|
|
||||||
|
Matrix3 A = Matrix3(valuesA);
|
||||||
|
|
||||||
|
Matrix B = A.submatrix(1, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(B.determinant(), 25);
|
||||||
|
ASSERT_EQ(A.minor(1, 0), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_a_cofactor_of_a_3x3_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 3, 5, 0,
|
||||||
|
2, -1, -7,
|
||||||
|
6, -1, 5 };
|
||||||
|
|
||||||
|
Matrix3 A = Matrix3(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.minor(0, 0), -12);
|
||||||
|
ASSERT_EQ(A.cofactor(0, 0), -12);
|
||||||
|
ASSERT_EQ(A.minor(1, 0), 25);
|
||||||
|
ASSERT_EQ(A.cofactor(1, 0), -25);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_the_determinant_of_a_3x3_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 1, 2, 6,
|
||||||
|
-5, 8, -4,
|
||||||
|
2, 6, 4 };
|
||||||
|
|
||||||
|
Matrix A = Matrix3(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.cofactor(0, 0), 56);
|
||||||
|
ASSERT_EQ(A.cofactor(0, 1), 12);
|
||||||
|
ASSERT_EQ(A.minor(0, 2), -46);
|
||||||
|
ASSERT_EQ(A.determinant(), -196);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_the_determinant_of_a_4x4_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { -2, -8, 3, 5,
|
||||||
|
-3, 1, 7, 3,
|
||||||
|
1, 2, -9, 6,
|
||||||
|
-6, 7, 7, -9 };
|
||||||
|
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.cofactor(0, 0), 690);
|
||||||
|
ASSERT_EQ(A.cofactor(0, 1), 447);
|
||||||
|
ASSERT_EQ(A.cofactor(0, 2), 210);
|
||||||
|
ASSERT_EQ(A.minor(0, 3), -51);
|
||||||
|
ASSERT_EQ(A.determinant(), -4071);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Testing_an_invertible_matrix_for_invertibility)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 6, 4, 4, 4,
|
||||||
|
5, 5, 7, 6,
|
||||||
|
4, -9, 3, -7,
|
||||||
|
9, 1, 7, -6 };
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.determinant(), -2120);
|
||||||
|
ASSERT_TRUE(A.isInvertible());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Testing_an_noninvertible_matrix_for_invertibility)
|
||||||
|
{
|
||||||
|
double valuesA[] = { -4, 2, -2, -3,
|
||||||
|
9, 6, 2, 6,
|
||||||
|
0, -5, 1, -5,
|
||||||
|
0, 0, 0, 0 };
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
|
||||||
|
ASSERT_EQ(A.determinant(), 0);
|
||||||
|
ASSERT_FALSE(A.isInvertible());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_the_inverse_of_a_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { -5, 2, 6, -8,
|
||||||
|
1, -5, 1, 8,
|
||||||
|
7, 7, -6, -7,
|
||||||
|
1, -3, 7, 4 };
|
||||||
|
|
||||||
|
double results[] = { 0.21805, 0.45113, 0.24060, -0.04511,
|
||||||
|
-0.80827, -1.45677, -0.44361, 0.52068,
|
||||||
|
-0.07895, -0.22368, -0.05263, 0.19737,
|
||||||
|
-0.52256, -0.81391, -0.30075, 0.30639 };
|
||||||
|
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
Matrix B = A.inverse();
|
||||||
|
|
||||||
|
ASSERT_EQ(A.determinant(), 532);
|
||||||
|
ASSERT_EQ(A.cofactor(2, 3), -160);
|
||||||
|
ASSERT_NEAR(B.get(3, 2), -160./532., DBL_EPSILON);
|
||||||
|
ASSERT_EQ(A.cofactor(3, 2), 105);
|
||||||
|
ASSERT_NEAR(B.get(2, 3), 105./532., DBL_EPSILON);
|
||||||
|
|
||||||
|
/* Temporary lower the precision */
|
||||||
|
set_equal_precision(0.00001);
|
||||||
|
|
||||||
|
ASSERT_EQ(B, Matrix4(results));
|
||||||
|
|
||||||
|
/* Revert to default */
|
||||||
|
set_equal_precision(FLT_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_the_inverse_of_another_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 8, -5, 9, 2,
|
||||||
|
7, 5, 6, 1,
|
||||||
|
-6, 0, 9, 6,
|
||||||
|
-3, 0, -9, -4 };
|
||||||
|
|
||||||
|
double results[] = { -0.15385, -0.15385, -0.28205, -0.53846,
|
||||||
|
-0.07692, 0.12308, 0.02564, 0.03077,
|
||||||
|
0.35897, 0.35897, 0.43590, 0.92308,
|
||||||
|
-0.69231, -0.69231, -0.76923, -1.92308 };
|
||||||
|
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
Matrix B = A.inverse();
|
||||||
|
|
||||||
|
|
||||||
|
/* Temporary lower the precision */
|
||||||
|
set_equal_precision(0.00001);
|
||||||
|
|
||||||
|
ASSERT_EQ(B, Matrix4(results));
|
||||||
|
|
||||||
|
/* Revert to default */
|
||||||
|
set_equal_precision(FLT_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Calculating_the_inverse_of_third_matrix)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 9, 3, 0, 9,
|
||||||
|
-5, -2, -6, -3,
|
||||||
|
-4, 9, 6, 4,
|
||||||
|
-7, 6, 6, 2 };
|
||||||
|
|
||||||
|
double results[] = { -0.04074, -0.07778, 0.14444, -0.22222,
|
||||||
|
-0.07778, 0.03333, 0.36667, -0.33333,
|
||||||
|
-0.02901, -0.14630, -0.10926, 0.12963,
|
||||||
|
0.17778, 0.06667, -0.26667, 0.33333 };
|
||||||
|
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
Matrix B = A.inverse();
|
||||||
|
|
||||||
|
|
||||||
|
/* Temporary lower the precision */
|
||||||
|
set_equal_precision(0.00001);
|
||||||
|
|
||||||
|
ASSERT_EQ(B, Matrix4(results));
|
||||||
|
|
||||||
|
/* Revert to default */
|
||||||
|
set_equal_precision(FLT_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MatrixTest, Multiplying_a_product_by_its_inverse)
|
||||||
|
{
|
||||||
|
double valuesA[] = { 3, -9, 7, 3,
|
||||||
|
3, -8, 2, -9,
|
||||||
|
-4, 4, 4, 1,
|
||||||
|
-6, 5, -1, 1 };
|
||||||
|
|
||||||
|
double valuesB[] = { 8, 2, 2, 2,
|
||||||
|
3, -1, 7, 0,
|
||||||
|
7, 0, 5, 4,
|
||||||
|
6, -2, 0, 5 };
|
||||||
|
|
||||||
|
Matrix A = Matrix4(valuesA);
|
||||||
|
Matrix B = Matrix4(valuesB);
|
||||||
|
|
||||||
|
Matrix C = A * B;
|
||||||
|
|
||||||
|
ASSERT_EQ(C * B.inverse(), A);
|
||||||
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
65
tests/ray_test.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Ray unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <ray.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <shape.h>
|
||||||
|
#include <testshape.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
TEST(RayTest, Creating_a_ray_and_querying_it)
|
||||||
|
{
|
||||||
|
Point origin = Point(1, 2, 3);
|
||||||
|
Vector direction = Vector(4, 5, 6);
|
||||||
|
|
||||||
|
Ray r = Ray(origin, direction);
|
||||||
|
|
||||||
|
ASSERT_EQ(r.origin, origin);
|
||||||
|
ASSERT_EQ(r.direction, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RayTest, Computing_a_point_from_a_distance)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(2, 3, 4), Vector(1, 0, 0));
|
||||||
|
|
||||||
|
ASSERT_EQ(r.position(0), Point(2, 3, 4));
|
||||||
|
ASSERT_EQ(r.position(1), Point(3, 3, 4));
|
||||||
|
ASSERT_EQ(r.position(-1), Point(1, 3, 4));
|
||||||
|
ASSERT_EQ(r.position(2.5), Point(4.5, 3, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RayTest, Translating_a_ray)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0));
|
||||||
|
|
||||||
|
Matrix m = translation(3, 4, 5);
|
||||||
|
TestShape o = TestShape();
|
||||||
|
|
||||||
|
o.setTransform(m);
|
||||||
|
|
||||||
|
Ray r2 = o.transform(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(r2.origin, Point(4, 6, 8));
|
||||||
|
ASSERT_EQ(r2.direction, Vector(0, 1, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RayTest, Scaling_a_ray)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0));
|
||||||
|
|
||||||
|
Matrix m = scaling(2, 3, 4);
|
||||||
|
TestShape o = TestShape();
|
||||||
|
|
||||||
|
o.setTransform(m);
|
||||||
|
|
||||||
|
Ray r2 = o.transform(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(r2.origin, Point(2, 6, 12));
|
||||||
|
ASSERT_EQ(r2.direction, Vector(0, 3, 0));
|
||||||
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
210
tests/sphere_test.cpp
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Sphere unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <ray.h>
|
||||||
|
#include <sphere.h>
|
||||||
|
#include <material.h>
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
TEST(SphereTest, A_ray_intersect_a_sphere_at_two_points)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].t, 4.0);
|
||||||
|
ASSERT_EQ(xs[1].t, 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, A_ray_intersect_a_sphere_at_a_tangent)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 1, -5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].t, 5.0);
|
||||||
|
ASSERT_EQ(xs[1].t, 5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, A_ray_miss_a_sphere)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 2, -5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, A_ray_originate_inside_a_sphere)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].t, -1.0);
|
||||||
|
ASSERT_EQ(xs[1].t, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, A_sphere_is_behind_a_ray)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 0, 5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].t, -6.0);
|
||||||
|
ASSERT_EQ(xs[1].t, -4.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, A_sphere_default_transformation)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
ASSERT_EQ(s.transformMatrix, Matrix4().identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, Changing_a_sphere_transformation)
|
||||||
|
{
|
||||||
|
Sphere s = Sphere();
|
||||||
|
Matrix t = translation(2, 3, 4);
|
||||||
|
|
||||||
|
s.setTransform(t);
|
||||||
|
|
||||||
|
ASSERT_EQ(s.transformMatrix, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, Intersecting_a_scaled_sphere_with_a_ray)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
|
||||||
|
s.setTransform(scaling(2, 2, 2));
|
||||||
|
|
||||||
|
Intersect xs = s.intersect(r);
|
||||||
|
|
||||||
|
ASSERT_EQ(xs.count(), 2);
|
||||||
|
ASSERT_EQ(xs[0].t, 3.0);
|
||||||
|
ASSERT_EQ(xs[1].t, 7.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SphereTest, Intersecting_a_translated_sphere_with_a_ray)
|
||||||
|
{
|
||||||
|
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
|
||||||
|
Sphere s = Sphere();
|
||||||
|
|
||||||
|
s.setTransform(translation(5, 0, 0));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
258
tests/transformation_test.cpp
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
|
* Transformations unit tests
|
||||||
|
*
|
||||||
|
* Created by Manoël Trapier
|
||||||
|
* Copyright (c) 2020 986-Studio.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <transformation.h>
|
||||||
|
#include <tuple.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(TransformationTest, Multiplying_by_a_translation_matrix)
|
||||||
|
{
|
||||||
|
Matrix transform = translation(5, -3, 2);
|
||||||
|
Point p = Point(-3, 4, 5);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(2, 1, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Multiplying_by_the_inverse_of_a_translation_matrix)
|
||||||
|
{
|
||||||
|
Matrix transform = translation(5, -3, 2);
|
||||||
|
Matrix inv = transform.inverse();
|
||||||
|
|
||||||
|
Point p = Point(-3, 4, 5);
|
||||||
|
|
||||||
|
ASSERT_EQ(inv * p, Point(-8, 7, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Translation_does_not_affect_vectors)
|
||||||
|
{
|
||||||
|
Matrix transform = translation(5, -3, 2);
|
||||||
|
|
||||||
|
Vector v = Vector(-3, 4, 5);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * v, Vector(-3, 4, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_scaling_matrix_applied_to_a_point)
|
||||||
|
{
|
||||||
|
Matrix transform = scaling(2, 3, 4);
|
||||||
|
|
||||||
|
Point p = Point(-4, 6, 8);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(-8, 18, 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_scaling_matrix_applied_to_a_vector)
|
||||||
|
{
|
||||||
|
Matrix transform = scaling(2, 3, 4);
|
||||||
|
|
||||||
|
Vector v = Vector(-4, 6, 8);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * v, Vector(-8, 18, 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Multiplaying_by_the_inverse_of_a_scaling_matrix)
|
||||||
|
{
|
||||||
|
Matrix transform = scaling(2, 3, 4);
|
||||||
|
Matrix inv = transform.inverse();
|
||||||
|
|
||||||
|
Vector v = Vector(-4, 6, 8);
|
||||||
|
|
||||||
|
ASSERT_EQ(inv * v, Vector(-2, 2, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Reflexion_is_scaling_by_a_negative_value)
|
||||||
|
{
|
||||||
|
Matrix transform = scaling(-1, 1, 1);
|
||||||
|
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(-2, 3, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Rotating_a_point_around_the_X_axis)
|
||||||
|
{
|
||||||
|
Point p = Point(0, 1, 0);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, The_inverse_of_an_x_rotation_rotates_in_the_opposite_direction)
|
||||||
|
{
|
||||||
|
Point p = Point(0, 1, 0);
|
||||||
|
Matrix half_quarter = rotationX(M_PI / 4.);
|
||||||
|
Matrix inv = half_quarter.inverse();
|
||||||
|
|
||||||
|
ASSERT_EQ(inv * p, Point(0, sqrt(2)/2, -sqrt(2)/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Rotating_a_point_around_the_Y_axis)
|
||||||
|
{
|
||||||
|
Point p = Point(0, 0, 1);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Rotating_a_point_around_the_Z_axis)
|
||||||
|
{
|
||||||
|
Point p = Point(0, 1, 0);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_shearing_transformation_moves_x_in_proportion_to_y)
|
||||||
|
{
|
||||||
|
Matrix transform = shearing(1, 0, 0, 0, 0, 0);
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(5, 3, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_shearing_transformation_moves_x_in_proportion_to_z)
|
||||||
|
{
|
||||||
|
Matrix transform = shearing(0, 1, 0, 0, 0, 0);
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(6, 3, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_shearing_transformation_moves_y_in_proportion_to_x)
|
||||||
|
{
|
||||||
|
Matrix transform = shearing(0, 0, 1, 0, 0, 0);
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(2, 5, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_shearing_transformation_moves_y_in_proportion_to_z)
|
||||||
|
{
|
||||||
|
Matrix transform = shearing(0, 0, 0, 1, 0, 0);
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(2, 7, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_shearing_transformation_moves_z_in_proportion_to_x)
|
||||||
|
{
|
||||||
|
Matrix transform = shearing(0, 0, 0, 0, 1, 0);
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(2, 3, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, A_shearing_transformation_moves_z_in_proportion_to_y)
|
||||||
|
{
|
||||||
|
Matrix transform = shearing(0, 0, 0, 0, 0, 1);
|
||||||
|
Point p = Point(2, 3, 4);
|
||||||
|
|
||||||
|
ASSERT_EQ(transform * p, Point(2, 3, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Individual_trnasformations_are_applied_in_sequence)
|
||||||
|
{
|
||||||
|
Point p = Point(1, 0, 1);
|
||||||
|
Matrix A = rotationX(M_PI / 2.);
|
||||||
|
Matrix B = scaling(5, 5, 5);
|
||||||
|
Matrix C = translation(10, 5, 7);
|
||||||
|
|
||||||
|
Tuple p2 = A * p;
|
||||||
|
ASSERT_EQ(p2, Point(1, -1, 0));
|
||||||
|
|
||||||
|
Tuple p3 = B * p2;
|
||||||
|
ASSERT_EQ(p3, Point(5, -5, 0));
|
||||||
|
|
||||||
|
Tuple p4 = C * p3;
|
||||||
|
ASSERT_EQ(p4, Point(15, 0, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, Chained_transformation_must_be_applied_in_reverse_order)
|
||||||
|
{
|
||||||
|
Point p = Point(1, 0, 1);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationTest, 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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* DoRayMe - a quick and dirty Raytracer
|
* DoRayMe - a quick and dirty Raytracer
|
||||||
* Tuples tests
|
* Tuples unit tests
|
||||||
*
|
*
|
||||||
* Created by Manoël Trapier
|
* Created by Manoël Trapier
|
||||||
* Copyright (c) 2020 986-Studio.
|
* Copyright (c) 2020 986-Studio.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <tuples.h>
|
#include <tuple.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <gtest/gtest.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);
|
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());
|
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);
|
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());
|
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);
|
Tuple a = Point(4, -4, 3);
|
||||||
|
|
||||||
ASSERT_EQ(a, Tuple(4, -4, 3, 1));
|
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);
|
Tuple a = Vector(4, -4, 3);
|
||||||
|
|
||||||
ASSERT_EQ(a, Tuple(4, -4, 3, 0));
|
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 a1 = Tuple(3, -2, 5, 1);
|
||||||
Tuple a2 = Tuple(-2, 3, 1, 0);
|
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));
|
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 p1 = Point(3, 2, 1);
|
||||||
Point p2 = Point(5, 6, 7);
|
Point p2 = Point(5, 6, 7);
|
||||||
@@ -64,7 +64,7 @@ TEST(TuplesTests, Substracting_two_points)
|
|||||||
ASSERT_EQ(p1 - p2, Vector(-2, -4, -6));
|
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);
|
Point p = Point(3, 2, 1);
|
||||||
Vector v = Vector(5, 6, 7);
|
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));
|
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 v1 = Vector(3, 2, 1);
|
||||||
Vector v2 = Vector(5, 6, 7);
|
Vector v2 = Vector(5, 6, 7);
|
||||||
@@ -80,7 +80,7 @@ TEST(TuplesTests, Substracting_two_vectors)
|
|||||||
ASSERT_EQ(v1 - v2, Vector(-2, -4, -6));
|
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 zero = Vector(0, 0, 0);
|
||||||
Vector v = Vector(1, -2, 3);
|
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));
|
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);
|
Tuple a = Tuple(1, -2, 3, -4);
|
||||||
|
|
||||||
ASSERT_EQ(-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);
|
Tuple a = Tuple(1, -2, 3, -4);
|
||||||
|
|
||||||
ASSERT_EQ(a * 3.5, Tuple(3.5, -7, 10.5, -14));
|
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);
|
Tuple a = Tuple(1, -2, 3, -4);
|
||||||
|
|
||||||
ASSERT_EQ(a * 0.5, Tuple(0.5, -1, 1.5, -2));
|
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);
|
Tuple a = Tuple(1, -2, 3, -4);
|
||||||
|
|
||||||
ASSERT_EQ(a / 2, Tuple(0.5, -1, 1.5, -2));
|
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);
|
Vector v = Vector(1, 0, 0);
|
||||||
|
|
||||||
ASSERT_EQ(v.magnitude(), 1);
|
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);
|
Vector v = Vector(0, 1, 0);
|
||||||
|
|
||||||
ASSERT_EQ(v.magnitude(), 1);
|
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);
|
Vector v = Vector(0, 0, 1);
|
||||||
|
|
||||||
ASSERT_EQ(v.magnitude(), 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);
|
Vector v = Vector(1, 2, 3);
|
||||||
|
|
||||||
ASSERT_EQ(v.magnitude(), sqrt(14));
|
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);
|
Vector v = Vector(-1, -2, -3);
|
||||||
|
|
||||||
ASSERT_EQ(v.magnitude(), sqrt(14));
|
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);
|
Vector v = Vector(4, 0, 0);
|
||||||
|
|
||||||
ASSERT_EQ(v.normalise(), Vector(1, 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);
|
Vector v = Vector(1, 2, 3);
|
||||||
|
|
||||||
ASSERT_EQ(v.normalise(), Vector(1 / sqrt(14), 2 / sqrt(14), 3 / sqrt(14)));
|
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 a = Vector(1, 2, 3);
|
||||||
Vector b = Vector(2, 3, 4);
|
Vector b = Vector(2, 3, 4);
|
||||||
@@ -173,7 +173,7 @@ TEST(TuplesTests, Dot_product_of_two_tuples)
|
|||||||
ASSERT_EQ(a.dot(b), 20);
|
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 a = Vector(1, 2, 3);
|
||||||
Vector b = Vector(2, 3, 4);
|
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(a.cross(b), Vector(-1, 2, -1));
|
||||||
ASSERT_EQ(b.cross(a), 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);
|
||||||
|
}
|
||||||