Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
[submodule "external/lodepng"]
|
||||
path = external/lodepng
|
||||
url = https://github.com/lvandeve/lodepng
|
||||
[submodule "external/coveralls-cmake"]
|
||||
path = external/coveralls-cmake
|
||||
url = https://github.com/Godzil/coveralls-cmake.git
|
||||
|
||||
36
.travis.yml
Normal file
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,24 +6,36 @@ project(DoRayMe)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
option(COVERALLS "Generate coveralls data" OFF)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/external/coveralls-cmake/cmake)
|
||||
|
||||
option(PACKAGE_TESTS "Build the tests" ON)
|
||||
|
||||
if (COVERALLS)
|
||||
include(Coveralls)
|
||||
coveralls_turn_on_coverage()
|
||||
endif()
|
||||
|
||||
#Add external projects that directly need to be builded
|
||||
ExternalProject_Add(googletest
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/googletest"
|
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/external/googletest"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
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
|
||||
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
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
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
|
||||
39
README.md
39
README.md
@@ -0,0 +1,39 @@
|
||||
[](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:
|
||||
|
||||

|
||||
1
external/coveralls-cmake
vendored
Submodule
1
external/coveralls-cmake
vendored
Submodule
Submodule external/coveralls-cmake added at b040bc02eb
2
external/glfw
vendored
2
external/glfw
vendored
Submodule external/glfw updated: 76406c7894...6aca3e99f0
2
external/googletest
vendored
2
external/googletest
vendored
Submodule external/googletest updated: 41b5f149ab...23b2a3b1cf
2
external/lodepng
vendored
2
external/lodepng
vendored
Submodule external/lodepng updated: 5a0dba1038...48e5364ef4
BIN
output/ch10_test.png
Normal file
BIN
output/ch10_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
BIN
output/ch5_test.png
Normal file
BIN
output/ch5_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 273 B |
BIN
output/ch6_test.png
Normal file
BIN
output/ch6_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
output/ch7_test.png
Normal file
BIN
output/ch7_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
output/ch8_test.png
Normal file
BIN
output/ch8_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
output/ch9_test.png
Normal file
BIN
output/ch9_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
@@ -3,14 +3,20 @@
|
||||
# First most is build as a library
|
||||
add_library(rayonnement STATIC)
|
||||
|
||||
set(RAY_HEADERS include/tuples.h include/math_helper.h include/colour.h include/canvas.h include/matrix.h)
|
||||
set(RAY_SOURCES tuples.cpp math_helper.cpp colour.cpp canvas.cpp matrix.cpp)
|
||||
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/pattern/*.h)
|
||||
|
||||
target_include_directories(rayonnement PUBLIC include)
|
||||
file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp)
|
||||
|
||||
target_include_directories(rayonnement PUBLIC include pattern)
|
||||
target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES})
|
||||
target_link_libraries(rayonnement LodePNG)
|
||||
|
||||
# Second we build the main executable
|
||||
add_executable(dorayme main.cpp)
|
||||
target_include_directories(rayonnement PUBLIC include ${LODEPNG_INCLUDE_FOLDER})
|
||||
target_link_libraries(dorayme rayonnement)
|
||||
target_link_libraries(dorayme rayonnement)
|
||||
|
||||
if (COVERALLS)
|
||||
set(COVERAGE_SRCS ${RAY_HEADERS} ${RAY_SOURCES} ${COVERAGE_SRCS} PARENT_SCOPE)
|
||||
endif()
|
||||
74
source/camera.cpp
Normal file
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 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);
|
||||
image.putPixel(x, y, colour);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
@@ -22,6 +22,24 @@ Canvas::Canvas(uint32_t width, uint32_t height) : width(width), height(height)
|
||||
this->stride = BytePP * width;
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Canvas &b)
|
||||
{
|
||||
this->width = b.width;
|
||||
this->height = b.height;
|
||||
this->stride = b.stride;
|
||||
this->bitmap = (uint8_t *)calloc(4, b.width * b.height);
|
||||
memcpy(this->bitmap, b.bitmap, 4 * b.width * b.height);
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Canvas *b)
|
||||
{
|
||||
this->width = b->width;
|
||||
this->height = b->height;
|
||||
this->stride = b->stride;
|
||||
this->bitmap = (uint8_t *)calloc(4, b->width * b->height);
|
||||
memcpy(this->bitmap, b->bitmap, 4 * b->width * b->height);
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
if (this->bitmap != nullptr)
|
||||
@@ -30,28 +48,23 @@ Canvas::~Canvas()
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::put_pixel(uint32_t x, uint32_t y, Colour c)
|
||||
void Canvas::putPixel(uint32_t x, uint32_t y, Tuple colour)
|
||||
{
|
||||
uint32_t offset = y * this->stride + x * BytePP;
|
||||
this->bitmap[offset + 0] = MAX(MIN(c.red() * 255, 255), 0);
|
||||
this->bitmap[offset + 1] = MAX(MIN(c.green() * 255, 255), 0);
|
||||
this->bitmap[offset + 2] = MAX(MIN(c.blue() * 255, 255), 0);
|
||||
this->bitmap[offset + 0] = MAX(MIN(colour.x * 255, 255), 0);
|
||||
this->bitmap[offset + 1] = MAX(MIN(colour.y * 255, 255), 0);
|
||||
this->bitmap[offset + 2] = MAX(MIN(colour.z * 255, 255), 0);
|
||||
}
|
||||
|
||||
Colour Canvas::get_pixel(uint32_t x, uint32_t y)
|
||||
Colour Canvas::getPixel(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t offset = y * this->stride + x * BytePP;
|
||||
return Colour(this->bitmap[offset + 0] / 255, this->bitmap[offset + 1] / 255, this->bitmap[offset + 2] / 255);
|
||||
return Colour(this->bitmap[offset + 0] / 255.0, this->bitmap[offset + 1] / 255.0, this->bitmap[offset + 2] / 255.0);
|
||||
}
|
||||
|
||||
bool Canvas::SaveAsPNG(const char *filename)
|
||||
{
|
||||
uint32_t ret = lodepng_encode24_file(filename, this->bitmap, this->width, this->height);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
printf("lodepng_encode_file returned %d!\n", ret);
|
||||
}
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
}
|
||||
|
||||
38
source/include/camera.h
Normal file
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);
|
||||
};
|
||||
|
||||
#endif /* DORAYME_CAMERA_H */
|
||||
@@ -21,10 +21,12 @@ public:
|
||||
uint32_t width, height;
|
||||
|
||||
Canvas(uint32_t width, uint32_t height);
|
||||
Canvas(const Canvas *c);
|
||||
Canvas(const Canvas &c);
|
||||
~Canvas();
|
||||
|
||||
void put_pixel(uint32_t x, uint32_t y, Colour c);
|
||||
Colour get_pixel(uint32_t x, uint32_t y);
|
||||
void putPixel(uint32_t x, uint32_t y, Tuple c);
|
||||
Colour getPixel(uint32_t x, uint32_t y);
|
||||
|
||||
bool SaveAsPNG(const char *filename);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef DORAYME_COLOUR_H
|
||||
#define DORAYME_COLOUR_H
|
||||
|
||||
#include <tuples.h>
|
||||
#include <tuple.h>
|
||||
|
||||
class Colour : public Tuple
|
||||
{
|
||||
|
||||
30
source/include/intersect.h
Normal file
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 */
|
||||
47
source/include/intersection.h
Normal file
47
source/include/intersection.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
struct Computation
|
||||
{
|
||||
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP, bool inside) :
|
||||
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside), overHitPoint(overHitP) { };
|
||||
|
||||
Shape *object;
|
||||
double t;
|
||||
Tuple hitPoint;
|
||||
Tuple overHitPoint;
|
||||
Tuple eyeVector;
|
||||
Tuple normalVector;
|
||||
|
||||
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);
|
||||
|
||||
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
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 */
|
||||
42
source/include/material.h
Normal file
42
source/include/material.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
Pattern *pattern;
|
||||
|
||||
public:
|
||||
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200), 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,7 +10,12 @@
|
||||
#ifndef 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);
|
||||
|
||||
#endif //DORAYME_MATH_HELPER_H
|
||||
double deg_to_rad(double deg);
|
||||
|
||||
#endif /* DORAYME_MATH_HELPER_H */
|
||||
|
||||
@@ -9,7 +9,15 @@
|
||||
#ifndef DORAYME_MATRIX_H
|
||||
#define DORAYME_MATRIX_H
|
||||
|
||||
#include <tuples.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
|
||||
{
|
||||
@@ -19,7 +27,7 @@ protected:
|
||||
int size;
|
||||
|
||||
public:
|
||||
Matrix(int size);
|
||||
Matrix(int size = 4);
|
||||
Matrix(double values[], int size);
|
||||
|
||||
double get(int x, int y) const { return this->data[this->size * x + y]; };
|
||||
|
||||
36
source/include/pattern.h
Normal file
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
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
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
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 */
|
||||
27
source/include/sphere.h
Normal file
27
source/include/sphere.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 */
|
||||
};
|
||||
|
||||
#endif /* DORAYME_SPHERE_H */
|
||||
28
source/include/testshape.h
Normal file
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
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.
|
||||
*
|
||||
*/
|
||||
#ifndef DORAYME_TUPLES_H
|
||||
#define DORAYME_TUPLES_H
|
||||
#ifndef DORAYME_TUPLE_H
|
||||
#define DORAYME_TUPLE_H
|
||||
|
||||
#include <math_helper.h>
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
double magnitude();
|
||||
Tuple normalise();
|
||||
double dot(const Tuple &b);
|
||||
Tuple cross(const Tuple &b) const;
|
||||
Tuple reflect(const Tuple &normal);
|
||||
};
|
||||
|
||||
class Point: public Tuple
|
||||
@@ -53,7 +55,6 @@ class Vector: public Tuple
|
||||
{
|
||||
public:
|
||||
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 */
|
||||
52
source/include/world.h
Normal file
52
source/include/world.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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]; };
|
||||
|
||||
Tuple shadeHit(Computation comps);;
|
||||
Tuple colourAt(Ray r);
|
||||
bool isShadowed(Tuple point);
|
||||
|
||||
Intersect intersect(Ray r);
|
||||
|
||||
};
|
||||
|
||||
#endif /* DORAYME_WORLD_H */
|
||||
29
source/include/worldbuilder.h
Normal file
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
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);
|
||||
}
|
||||
34
source/intersection.cpp
Normal file
34
source/intersection.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Intersection implementation
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <intersection.h>
|
||||
#include <shape.h>
|
||||
|
||||
Computation Intersection::prepareComputation(Ray r)
|
||||
{
|
||||
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();
|
||||
|
||||
return Computation(this->object,
|
||||
this->t,
|
||||
hitP,
|
||||
eyeV,
|
||||
normalV,
|
||||
overHitP,
|
||||
inside);
|
||||
}
|
||||
@@ -18,7 +18,17 @@ void set_equal_precision(double v)
|
||||
current_precision = v;
|
||||
}
|
||||
|
||||
double getEpsilon()
|
||||
{
|
||||
return current_precision;
|
||||
}
|
||||
|
||||
bool double_equal(double a, double b)
|
||||
{
|
||||
return fabs(a - b) < current_precision;
|
||||
}
|
||||
|
||||
double deg_to_rad(double deg)
|
||||
{
|
||||
return deg * M_PI / 180.;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <matrix.h>
|
||||
#include <tuples.h>
|
||||
#include <tuple.h>
|
||||
#include <math_helper.h>
|
||||
|
||||
Matrix::Matrix(int width)
|
||||
|
||||
31
source/pattern.cpp
Normal file
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
|
||||
*
|
||||
*/
|
||||
#include <tuples.h>
|
||||
#include <tuple.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;
|
||||
}
|
||||
|
||||
Vector Vector::cross(const Vector &b) const
|
||||
Tuple Tuple::cross(const Tuple &b) const
|
||||
{
|
||||
return Vector(this->y * b.z - this->z * b.y,
|
||||
this->z * b.x - this->x * b.z,
|
||||
this->x * b.y - this->y * b.x);
|
||||
return Tuple(this->y * b.z - this->z * b.y,
|
||||
this->z * b.x - this->x * b.z,
|
||||
this->x * b.y - this->y * b.x,
|
||||
0);
|
||||
}
|
||||
|
||||
Tuple Tuple::reflect(const Tuple &normal)
|
||||
{
|
||||
return *this - normal * 2 * this->dot(normal);
|
||||
}
|
||||
136
source/world.cpp
Normal file
136
source/world.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
/* TODO: Add support for more than one light */
|
||||
|
||||
bool isThereAnObstacle = this->isShadowed(comps.overHitPoint);
|
||||
|
||||
return comps.object->material.lighting(*this->lightList[0], comps.overHitPoint, comps.eyeVector,
|
||||
comps.normalVector, comps.object, isThereAnObstacle);
|
||||
}
|
||||
|
||||
Tuple World::colourAt(Ray r)
|
||||
{
|
||||
Intersection hit = this->intersect(r).hit();
|
||||
|
||||
if (hit.nothing())
|
||||
{
|
||||
return Colour(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->shadeHit(hit.prepareComputation(r));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
34
source/worldbuilder/default_world.cpp
Normal file
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
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,49 @@ project(DoRayTested)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
set(TESTS_SRC tuples_test.cpp colour_test.cpp canvas_test.cpp matrix_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)
|
||||
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||
target_include_directories(testMyRays PUBLIC ../source/include)
|
||||
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_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>)
|
||||
|
||||
111
tests/camera_test.cpp
Normal file
111
tests/camera_test.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* DoRayMe - a quick and dirty Raytracer
|
||||
* Camera unit tests
|
||||
*
|
||||
* Created by Manoël Trapier
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <camera.h>
|
||||
#include <math.h>
|
||||
#include <math_helper.h>
|
||||
#include <matrix.h>
|
||||
#include <tuple.h>
|
||||
#include <ray.h>
|
||||
#include <world.h>
|
||||
#include <canvas.h>
|
||||
#include <colour.h>
|
||||
#include <worldbuilder.h>
|
||||
#include <transformation.h>
|
||||
#include <stdint.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(CameraTest, Constructing_a_camera)
|
||||
{
|
||||
uint32_t hsize = 160;
|
||||
uint32_t vsize = 120;
|
||||
double field_of_view = M_PI / 2;
|
||||
|
||||
Camera c = Camera(hsize, vsize, field_of_view);
|
||||
|
||||
ASSERT_EQ(c.horizontalSize, 160);
|
||||
ASSERT_EQ(c.verticalSize, 120);
|
||||
ASSERT_TRUE(double_equal(c.fieldOfView, M_PI / 2));
|
||||
ASSERT_EQ(c.transformMatrix, Matrix4().identity());
|
||||
}
|
||||
|
||||
TEST(CameraTest, Pixel_size_for_a_horizontal_canvas)
|
||||
{
|
||||
Camera c = Camera(200, 125, M_PI / 2);
|
||||
|
||||
ASSERT_TRUE(double_equal(c.pixelSize, 0.01));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Pixel_size_for_a_vertical_canvas)
|
||||
{
|
||||
Camera c = Camera(125, 200, M_PI / 2);
|
||||
|
||||
ASSERT_TRUE(double_equal(c.pixelSize, 0.01));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Constructing_a_ray_through_the_center_of_the_canvas)
|
||||
{
|
||||
Camera c = Camera(201, 101, M_PI / 2);
|
||||
Ray r = c.rayForPixel(100, 50);
|
||||
|
||||
ASSERT_EQ(r.origin, Point(0, 0, 0));
|
||||
ASSERT_EQ(r.direction, Vector(0, 0, -1));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Constructing_a_ray_through_a_corner_of_the_canvas)
|
||||
{
|
||||
Camera c = Camera(201, 101, M_PI / 2);
|
||||
Ray r = c.rayForPixel(0, 0);
|
||||
|
||||
ASSERT_EQ(r.origin, Point(0, 0, 0));
|
||||
|
||||
/* Temporary lower the precision */
|
||||
set_equal_precision(0.00001);
|
||||
|
||||
ASSERT_EQ(r.direction, Vector(0.66519, 0.33259, -0.66851));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
|
||||
TEST(CameraTest, Constructing_a_ray_when_the_camera_is_transformed)
|
||||
{
|
||||
Camera c = Camera(201, 101, M_PI / 2);
|
||||
c.setTransform(rotationY(M_PI / 4) * translation(0, -2, 5));
|
||||
Ray r = c.rayForPixel(100, 50);
|
||||
|
||||
ASSERT_EQ(r.origin, Point(0, 2, -5));
|
||||
ASSERT_EQ(r.direction, Vector(sqrt(2)/2, 0, -sqrt(2)/2));
|
||||
}
|
||||
|
||||
TEST(CameraTest, Rendering_a_world_with_a_camera)
|
||||
{
|
||||
World w = DefaultWorld();
|
||||
Camera c = Camera(11, 11, M_PI / 2);
|
||||
|
||||
Tuple from = Point(0, 0, -5);
|
||||
Tuple to = Point(0, 0, 0);
|
||||
Tuple up = Vector(0, 1, 0);
|
||||
|
||||
c.setTransform(viewTransform(from, to, up));
|
||||
|
||||
Canvas image = c.render(w);
|
||||
|
||||
/* Temporary lower the precision */
|
||||
/* We need to lower a lot as Canvas is not keeping the
|
||||
* floating point value, but a value between 0 and 255 per channel,
|
||||
* as it is storing the actual frame buffer, so there is a more big different
|
||||
* between the value.
|
||||
*/
|
||||
set_equal_precision(0.005);
|
||||
|
||||
Colour col = image.getPixel(5, 5);
|
||||
|
||||
ASSERT_EQ(col, Colour(0.38066, 0.47583, 0.2855));
|
||||
|
||||
set_equal_precision(FLT_EPSILON);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ TEST(CanvasTest, Creating_a_canvas)
|
||||
{
|
||||
for(x = 0; x < 10; x++)
|
||||
{
|
||||
ASSERT_EQ(c.get_pixel(x, y), Colour(0, 0, 0));
|
||||
ASSERT_EQ(c.getPixel(x, y), Colour(0, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ TEST(CanvasTest, Test_Writing_pixels_to_a_canvas_Test)
|
||||
Canvas c = Canvas(10, 20);
|
||||
Colour red = Colour(1, 0, 0);
|
||||
|
||||
c.put_pixel(2, 3, red);
|
||||
c.putPixel(2, 3, red);
|
||||
|
||||
ASSERT_EQ(c.get_pixel(2, 3), red);
|
||||
ASSERT_EQ(c.getPixel(2, 3), red);
|
||||
|
||||
}
|
||||
|
||||
@@ -44,10 +44,28 @@ TEST(CanvasTest, Save_a_PNG_file)
|
||||
Colour c2 = Colour(0, 0.5, 0);
|
||||
Colour c3 = Colour(-0.5, 0, 1);
|
||||
|
||||
c.put_pixel(0, 0, c1);
|
||||
c.put_pixel(2, 1, c2);
|
||||
c.put_pixel(4, 2, c3);
|
||||
c.putPixel(0, 0, c1);
|
||||
c.putPixel(2, 1, c2);
|
||||
c.putPixel(4, 2, c3);
|
||||
|
||||
ASSERT_TRUE(c.SaveAsPNG("Save_a_PNG_file.png"));
|
||||
|
||||
}
|
||||
|
||||
TEST(CanvasTest, Create_a_canvas_from_another_using_reference)
|
||||
{
|
||||
Canvas c = Canvas(100, 100);
|
||||
|
||||
Canvas copy = Canvas(c);
|
||||
|
||||
ASSERT_EQ(c.width, copy.width);
|
||||
}
|
||||
|
||||
TEST(CanvasTest, Create_a_canvas_from_another_using_pointer)
|
||||
{
|
||||
Canvas c = Canvas(100, 100);
|
||||
|
||||
Canvas copy = Canvas(&c);
|
||||
|
||||
ASSERT_EQ(c.width, copy.width);
|
||||
}
|
||||
94
tests/ch10_test.cpp
Normal file
94
tests/ch10_test.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 <world.h>
|
||||
#include <light.h>
|
||||
#include <sphere.h>
|
||||
#include <plane.h>
|
||||
#include <material.h>
|
||||
#include <colour.h>
|
||||
#include <canvas.h>
|
||||
#include <camera.h>
|
||||
|
||||
#include <pattern.h>
|
||||
#include <strippattern.h>
|
||||
#include <gradientpattern.h>
|
||||
#include <checkerspattern.h>
|
||||
#include <ringpattern.h>
|
||||
|
||||
#include <transformation.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
/* First we need to construct the world */
|
||||
Plane floor = Plane();
|
||||
floor.material.specular = 0;
|
||||
floor.material.pattern = new RingPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
|
||||
|
||||
Plane wall = Plane();
|
||||
wall.material.specular = 0;
|
||||
wall.material.pattern = new StripPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
|
||||
wall.material.pattern->setTransform(translation(0, 0, 1) * rotationY(M_PI/4));
|
||||
wall.setTransform(translation(0, 0, 5) * rotationX(M_PI/2));
|
||||
|
||||
Sphere middle = Sphere();
|
||||
middle.setTransform(translation(-0.7, 1, 0.6));
|
||||
middle.material.diffuse = 0.7;
|
||||
middle.material.specular = 0.3;
|
||||
middle.material.pattern = new StripPattern(Colour(0.1, 1, 0.5), Colour(0, 0.2, 0.2));
|
||||
middle.material.pattern->setTransform((rotationZ(M_PI/4) * rotationY(M_PI/5) * scaling(0.2, 0.2, 0.2)));
|
||||
|
||||
Sphere right = Sphere();
|
||||
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
|
||||
right.material.diffuse = 0.7;
|
||||
right.material.specular = 0.3;
|
||||
right.material.pattern = new StripPattern(Colour(0.5, 1, 0.1), Colour(0, 0, 0));
|
||||
right.material.pattern->setTransform((scaling(0.1, 0.1, 0.1)));
|
||||
|
||||
Sphere left = Sphere();
|
||||
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
|
||||
left.material.diffuse = 0.7;
|
||||
left.material.specular = 0.3;
|
||||
left.material.pattern = new GradientPattern(Colour(1, 0.8, 0.1), Colour(0.1, 0.1, 1));
|
||||
left.material.pattern->setTransform(translation(1.5, 0, 0) * scaling(2.1, 2, 2) * rotationY(-M_PI/4));
|
||||
|
||||
Sphere fourth = Sphere();
|
||||
fourth.setTransform(translation(.5, 0.25, 0.4) * scaling(0.3, 0.3, 0.3));
|
||||
fourth.material.diffuse = 0.7;
|
||||
fourth.material.specular = 0.3;
|
||||
fourth.material.pattern = new CheckersPattern(Colour(0.1, 0.8, 0.1), Colour(0.8, 1, 0.8));
|
||||
fourth.material.pattern->setTransform( scaling(0.2, 0.2, 0.2));
|
||||
|
||||
World w = World();
|
||||
|
||||
w.addObject(&floor);
|
||||
w.addObject(&wall);
|
||||
w.addObject(&middle);
|
||||
w.addObject(&left);
|
||||
w.addObject(&right);
|
||||
w.addObject(&fourth);
|
||||
|
||||
/* Add light */
|
||||
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
|
||||
|
||||
w.addLight(&light);
|
||||
|
||||
/* Set the camera */
|
||||
Camera camera = Camera(100, 50, M_PI / 3);
|
||||
camera.setTransform(viewTransform(Point(0, 1.5, -5),
|
||||
Point(0, 1, 0),
|
||||
Vector(0, 1, 0)));
|
||||
|
||||
/* Now render it */
|
||||
Canvas image = camera.render(w);
|
||||
|
||||
image.SaveAsPNG("ch10_test.png");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
46
tests/ch5_test.cpp
Normal file
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
57
tests/ch6_test.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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();
|
||||
|
||||
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
83
tests/ch7_test.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <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
69
tests/ch9_test.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 <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;
|
||||
}
|
||||
192
tests/intersect_test.cpp
Normal file
192
tests/intersect_test.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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 <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);
|
||||
}
|
||||
24
tests/light_test.cpp
Normal file
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);
|
||||
}
|
||||
109
tests/material_test.cpp
Normal file
109
tests/material_test.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*/
|
||||
#include <matrix.h>
|
||||
#include <tuples.h>
|
||||
#include <tuple.h>
|
||||
#include <math.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -30,7 +30,7 @@ TEST(MatrixTest, Constructing_and_inspecting_a_4x4_Matrix)
|
||||
ASSERT_EQ(m.get(3, 2), 15.5);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, A_2x2_matric_ought_to_be_representable)
|
||||
TEST(MatrixTest, Change_a_single_value_and_check_it)
|
||||
{
|
||||
double values[] = {-3, 5,
|
||||
1, -2};
|
||||
@@ -41,9 +41,13 @@ TEST(MatrixTest, A_2x2_matric_ought_to_be_representable)
|
||||
ASSERT_EQ(m.get(0, 1), 5);
|
||||
ASSERT_EQ(m.get(1, 0), 1);
|
||||
ASSERT_EQ(m.get(1, 1), -2);
|
||||
|
||||
m.set(0, 0, 12);
|
||||
|
||||
ASSERT_EQ(m.get(0, 0), 12);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, A_3x3_matric_ought_to_be_representable)
|
||||
TEST(MatrixTest, A_3x3_matrix_ought_to_be_representable)
|
||||
{
|
||||
double values[] = {-3, 5, 0,
|
||||
1, -2, -7,
|
||||
@@ -56,6 +60,19 @@ TEST(MatrixTest, A_3x3_matric_ought_to_be_representable)
|
||||
ASSERT_EQ(m.get(2, 2), 1);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, A_2x2_matrix_ought_to_be_representable)
|
||||
{
|
||||
double values[] = {-3, 5,
|
||||
1, -2};
|
||||
|
||||
Matrix2 m = Matrix2(values);
|
||||
|
||||
ASSERT_EQ(m.get(0, 0), -3);
|
||||
ASSERT_EQ(m.get(0, 1), 5);
|
||||
ASSERT_EQ(m.get(1, 0), 1);
|
||||
ASSERT_EQ(m.get(1, 1), -2);
|
||||
}
|
||||
|
||||
TEST(MatrixTest, Matrix_equality_with_identical_matrix)
|
||||
{
|
||||
double values1[] = {1, 2, 3, 4,
|
||||
|
||||
211
tests/pattern_test.cpp
Normal file
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
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
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
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);
|
||||
}
|
||||
201
tests/sphere_test.cpp
Normal file
201
tests/sphere_test.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
258
tests/transformation_test.cpp
Normal file
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);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
* Copyright (c) 2020 986-Studio.
|
||||
*
|
||||
*/
|
||||
#include <tuples.h>
|
||||
#include <tuple.h>
|
||||
#include <math.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(TuplesTests, Tuple_With_w_equal_1_and_is_point)
|
||||
TEST(TupleTest, Tuple_With_w_equal_1_and_is_point)
|
||||
{
|
||||
Tuple a = Tuple(4.3, -4.2, 3.1, 1.0);
|
||||
|
||||
@@ -22,7 +22,7 @@ TEST(TuplesTests, Tuple_With_w_equal_1_and_is_point)
|
||||
ASSERT_FALSE(a.isVector());
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Tuple_With_w_equal_0_and_is_vector)
|
||||
TEST(TupleTest, Tuple_With_w_equal_0_and_is_vector)
|
||||
{
|
||||
Tuple a = Tuple(4.3, -4.2, 3.1, 0.0);
|
||||
|
||||
@@ -34,21 +34,21 @@ TEST(TuplesTests, Tuple_With_w_equal_0_and_is_vector)
|
||||
ASSERT_TRUE(a.isVector());
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Point_create_tuples_with_w_equal_1)
|
||||
TEST(TupleTest, Point_create_tuples_with_w_equal_1)
|
||||
{
|
||||
Tuple a = Point(4, -4, 3);
|
||||
|
||||
ASSERT_EQ(a, Tuple(4, -4, 3, 1));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Vector_create_tuples_with_w_equal_0)
|
||||
TEST(TupleTest, Vector_create_tuples_with_w_equal_0)
|
||||
{
|
||||
Tuple a = Vector(4, -4, 3);
|
||||
|
||||
ASSERT_EQ(a, Tuple(4, -4, 3, 0));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Adding_two_tuples)
|
||||
TEST(TupleTest, Adding_two_tuples)
|
||||
{
|
||||
Tuple a1 = Tuple(3, -2, 5, 1);
|
||||
Tuple a2 = Tuple(-2, 3, 1, 0);
|
||||
@@ -56,7 +56,7 @@ TEST(TuplesTests, Adding_two_tuples)
|
||||
ASSERT_EQ(a1 + a2, Tuple(1, 1, 6, 1));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_two_points)
|
||||
TEST(TupleTest, Substracting_two_points)
|
||||
{
|
||||
Point p1 = Point(3, 2, 1);
|
||||
Point p2 = Point(5, 6, 7);
|
||||
@@ -64,7 +64,7 @@ TEST(TuplesTests, Substracting_two_points)
|
||||
ASSERT_EQ(p1 - p2, Vector(-2, -4, -6));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_a_vector_from_a_point)
|
||||
TEST(TupleTest, Substracting_a_vector_from_a_point)
|
||||
{
|
||||
Point p = Point(3, 2, 1);
|
||||
Vector v = Vector(5, 6, 7);
|
||||
@@ -72,7 +72,7 @@ TEST(TuplesTests, Substracting_a_vector_from_a_point)
|
||||
ASSERT_EQ(p - v, Point(-2, -4, -6));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_two_vectors)
|
||||
TEST(TupleTest, Substracting_two_vectors)
|
||||
{
|
||||
Vector v1 = Vector(3, 2, 1);
|
||||
Vector v2 = Vector(5, 6, 7);
|
||||
@@ -80,7 +80,7 @@ TEST(TuplesTests, Substracting_two_vectors)
|
||||
ASSERT_EQ(v1 - v2, Vector(-2, -4, -6));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Substracting_a_vector_from_zero_vector)
|
||||
TEST(TupleTest, Substracting_a_vector_from_zero_vector)
|
||||
{
|
||||
Vector zero = Vector(0, 0, 0);
|
||||
Vector v = Vector(1, -2, 3);
|
||||
@@ -88,84 +88,84 @@ TEST(TuplesTests, Substracting_a_vector_from_zero_vector)
|
||||
ASSERT_EQ(zero - v, Vector(-1, 2, -3));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Negating_a_tuple)
|
||||
TEST(TupleTest, Negating_a_tuple)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(-a, Tuple(-1, 2, -3, 4));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Multiplying_a_tuple_by_a_scalar)
|
||||
TEST(TupleTest, Multiplying_a_tuple_by_a_scalar)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(a * 3.5, Tuple(3.5, -7, 10.5, -14));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Multiplying_a_tuple_by_a_fraction)
|
||||
TEST(TupleTest, Multiplying_a_tuple_by_a_fraction)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(a * 0.5, Tuple(0.5, -1, 1.5, -2));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Dividing_a_tuple_by_a_scalar)
|
||||
TEST(TupleTest, Dividing_a_tuple_by_a_scalar)
|
||||
{
|
||||
Tuple a = Tuple(1, -2, 3, -4);
|
||||
|
||||
ASSERT_EQ(a / 2, Tuple(0.5, -1, 1.5, -2));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_1_0_0)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_1_0_0)
|
||||
{
|
||||
Vector v = Vector(1, 0, 0);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), 1);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_0_1_0)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_0_1_0)
|
||||
{
|
||||
Vector v = Vector(0, 1, 0);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), 1);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_0_0_1)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_0_0_1)
|
||||
{
|
||||
Vector v = Vector(0, 0, 1);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), 1);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_1_2_3)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_1_2_3)
|
||||
{
|
||||
Vector v = Vector(1, 2, 3);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), sqrt(14));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Computing_the_magnitude_of_vector_n1_n2_n3)
|
||||
TEST(TupleTest, Computing_the_magnitude_of_vector_n1_n2_n3)
|
||||
{
|
||||
Vector v = Vector(-1, -2, -3);
|
||||
|
||||
ASSERT_EQ(v.magnitude(), sqrt(14));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Nomilise_vector_4_0_0_give_1_0_0)
|
||||
TEST(TupleTest, Nomilise_vector_4_0_0_give_1_0_0)
|
||||
{
|
||||
Vector v = Vector(4, 0, 0);
|
||||
|
||||
ASSERT_EQ(v.normalise(), Vector(1, 0, 0));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Nomilise_vector_1_2_3)
|
||||
TEST(TupleTest, Nomilise_vector_1_2_3)
|
||||
{
|
||||
Vector v = Vector(1, 2, 3);
|
||||
|
||||
ASSERT_EQ(v.normalise(), Vector(1 / sqrt(14), 2 / sqrt(14), 3 / sqrt(14)));
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Dot_product_of_two_tuples)
|
||||
TEST(TupleTest, Dot_product_of_two_tuples)
|
||||
{
|
||||
Vector a = Vector(1, 2, 3);
|
||||
Vector b = Vector(2, 3, 4);
|
||||
@@ -173,7 +173,7 @@ TEST(TuplesTests, Dot_product_of_two_tuples)
|
||||
ASSERT_EQ(a.dot(b), 20);
|
||||
}
|
||||
|
||||
TEST(TuplesTests, Cross_product_of_two_vector)
|
||||
TEST(TupleTest, Cross_product_of_two_vector)
|
||||
{
|
||||
Vector a = Vector(1, 2, 3);
|
||||
Vector b = Vector(2, 3, 4);
|
||||
@@ -181,3 +181,23 @@ TEST(TuplesTests, Cross_product_of_two_vector)
|
||||
ASSERT_EQ(a.cross(b), Vector(-1, 2, -1));
|
||||
ASSERT_EQ(b.cross(a), Vector(1, -2, 1));
|
||||
}
|
||||
|
||||
TEST(TupleTest, Reflecting_a_vector_approaching_at_45)
|
||||
{
|
||||
Vector v = Vector(1, -1, 0); /* Vector */
|
||||
Vector n = Vector(0, 1, 0); /* Normal */
|
||||
|
||||
Tuple r = v.reflect(n);
|
||||
|
||||
ASSERT_EQ(r, Vector(1, 1, 0));
|
||||
}
|
||||
|
||||
TEST(TupleTest, Reflecting_a_vector_off_a_slanted_surface)
|
||||
{
|
||||
Vector v = Vector(0, -1, 0); /* Vector */
|
||||
Vector n = Vector(sqrt(2)/2, sqrt(2)/2, 0); /* Normal */
|
||||
|
||||
Tuple r = v.reflect(n);
|
||||
|
||||
ASSERT_EQ(r, Vector(1, 0, 0));
|
||||
}
|
||||
170
tests/world_test.cpp
Normal file
170
tests/world_test.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
#include <gtest/gtest.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));
|
||||
};
|
||||
Reference in New Issue
Block a user