In Part 1, we concluded by providing some insights into when and where crowds will form across several entrances and attractions as a result of visitorship prediction. We now shift the discussion to attractions.
Why do queues form?
Queues are fascinating and it has become a part of our lives. It forms because of three reasons:
-
customers arrive faster than they can be serviced
-
the number of service staff are insufficient and/or inefficient
-
the inherent variability of visitor arrival times and service times
A confluence of the above results in a queue that misbehaves. When this happens, different queueing strategies are adopted to make queueing fun.
How long do I have to wait?
To understand queues, a mathematical model is constructed so that queueing time can be predicted. Sentosa, however, has slightly different characteristics and standard queueing models do not apply.
With the prediction on visitorship, we are able to tell the queueing time one hour ahead and
-
as a visitor, decide which attraction to visit in an hour
-
as operations, decide which attraction requires more resources in an hour
The figure below shows expected queueing times for an arbitrary day at the following attractions:
-
Skyline Luge
-
Tiger Sky Tower
-
Butterfly Park
-
Wings of Time
On to Part 3 of 3.


|
Gephi Toolkit Javadoc | ||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
public interface TimelineController
Controls the timeline bounds and animation features.
By default the timeline is disabled and can be enabled with the setEnabled()
method. Once enabled, the controller is setting its interval value to the
DynamicModel
.
The interval can be animated using the startPlay()
and stopPlay()
methods. Configuration parameters are also available.
This controller also allows to lookup graph attribute columns that can be used
as sparklines (e.g. node count, average degree...). Use the selectColumn()
to create a TimelineChart
accessible from the TimelineModel
All interval values are in the same space as the DynamicAPI
module and
no value should eb out of the min/max bounds maintained by the DynamicModel
.
TimelineModel
| Method Summary | |
|---|---|
void |
addListener(TimelineModelListener listener)
Add listener to the list of event listerners. |
AttributeColumn[] |
getDynamicGraphColumns()
Returns all the possible dynamic attribute columns. |
TimelineModel |
getModel()
Get the current model from the current workspace |
TimelineModel |
getModel(Workspace workspace)
Returns the timeline model from workspace . |
void |
removeListener(TimelineModelListener listener)
Remove listerner from the list of event listeners. |
void |
selectColumn(AttributeColumn column)
Select a column to make a TimelineChart of it. |
void |
setCustomBounds(double min,
double max)
Sets the timeline custom bounds. |
void |
setEnabled(boolean enabled)
Sets the timeline enable status. |
void |
setInterval(double from,
double to)
Sets the current timeline interval. |
void |
setPlayMode(TimelineModel.PlayMode playMode)
Sets the play mode. |
void |
setPlaySpeed(int delay)
Sets the play delay in milliseconds. |
void |
setPlayStep(double step)
Sets the play step. |
void |
startPlay()
Starts the timeline animation using the current delay, step size and play mode. |
void |
stopPlay()
Stops the timeline animation. |
| Method Detail |
|---|
TimelineModel getModel(Workspace workspace)
workspace
.
workspace
- the workspace to get the model from
TimelineModel getModel()
null
if no active workspacevoid setCustomBounds(double min, double max)
min
- the lower boundmax
- the upper bound
java.lang.IllegalArgumentException
- if min is superior or equal than
max
or out of bounds
void setEnabled(boolean enabled)
enabled
- the enabled value to setvoid setInterval(double from, double to)
DynamicModel
and defines the interval the graph is filtered with.
from
- the lower boundto
- the upper bound
java.lang.IllegalArgumentException
- if min is superior or equal than
max
or out of bounds
void startPlay()
void stopPlay()
void setPlaySpeed(int delay)
delay
- the delay in millisecondsvoid setPlayStep(double step)
step
- the step, between 0 and 1void setPlayMode(TimelineModel.PlayMode playMode)
playMode
- the play modeAttributeColumn[] getDynamicGraphColumns()
void selectColumn(AttributeColumn column)
TimelineChart
of it. The column must be member
of the graph table.
column
- the column to select
java.lang.IllegalArgumentException
- if column
is not a graph columnvoid addListener(TimelineModelListener listener)
listener
to the list of event listerners.
listener
- the listener to addvoid removeListener(TimelineModelListener listener)
listerner
from the list of event listeners.
listener
- the listener to remove
|
Gephi Toolkit Javadoc | ||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |


| Figure 1 |
Change-points and trends of the annual precipitation ( a), p ... |
| Figure 2 |
Elasticity of streamflow of 96 watersheds of Canada to ( a) ... |
| Figure 3 |
Relative contributions of changes in precipitation ( c), PET ... |
| Figure 4 |
Temporal Budyko analysis results of Contributions of climate ... |


Loading...
Administrator
Game Status:
Stopped
Start Game
Reset Game
Users
Loading...
#
Username
Name
Status
Challenges
Loading...
Gifts
Loading...
#
Gift
Original Owner
Current Owner
Steal Count
Game Status
×


The four seasons of nature are Spring, Summer, Fall, and Winter. But in many states would often only receive 1 or 2 seasons year round. Just as it is in Montana.


java.io
package in useful
ways for our applications.See: Description
| Interface | Description |
|---|---|
| Lexer |
A Lexer interface to be used with
LexerTokenizer . |
| Class | Description |
|---|---|
| BZip2PipedOutputStream |
Opens a outputstream for writing into a bzip2 file by piping into the bzip2 command.
|
| EncodingFileReader |
This is a convenience class which works almost exactly like
FileReader
but allows for the specification of input encoding. |
| EncodingPrintWriter |
A convenience IO class with print and println statements to
standard output and standard error allowing encoding in an
arbitrary character set.
|
| EncodingPrintWriter.err |
Print methods wrapped around System.err
|
| EncodingPrintWriter.out |
Print methods wrapped around System.out
|
| ExtensionFileFilter |
Implements a file filter that uses file extensions to filter files.
|
| FileSystem |
Provides various filesystem operations common to scripting languages such
as Perl and Python but not present (currently) in the Java standard libraries.
|
| IOUtils |
Helper Class for various I/O related things.
|
| NullOutputStream |
An OutputStream which throws away all output instead of outputting anything
Taken from http://stackoverflow.com/questions/2127979 |
| NumberRangeFileFilter |
Implements a file filter that examines a number in a filename to
determine acceptance.
|
| NumberRangesFileFilter |
Implements a file filter that examines a number in a filename to
determine acceptance.
|
| PrintFile |
Shorthand class for opening an output file for human-readable output.
|
| ReaderInputStream |
Adapts a
Reader as an InputStream . |
| Exception | Description |
|---|---|
| RuntimeIOException |
An unchecked version of
IOException . |
A few classes that augment the java.io
package in useful
ways for our applications.
ExtensionFileFilter
and
NumberRangeFileFilter
InDataStreamFile
,
OutDataStreamFile
, PrintFile
FileArrayList
,
FileSequentialCollection


Sorry but the page you're looking for could not be found.


| Q |
Why
am I getting so much data back?
|
| A |
Each association will be traversed in both
directions, unless there is a restriction defined. For example, if the
subject table is employee,
and the association from department
table to employee
table is enabled, you will not only get all departments associated with
any subject employee, but also all employees associated with
any of that departments.
If you get rows which you don't intend to get, then a restriction is
missing. You can use the explain
feature in such a case. The explain log shows the chain of associations
for each exported row back to a subject row, where the exportation
starts. I recommend to read the tutorial,
which explains the matters.
Instead of adding all restrictions you need, starting from the
unrestricted model, you can also go the opposit way: at first disable
all associations (except dependencies) using "Edit->Disable all
associations" menu item. Then remove restrictions until
you get all the rows you are looking for.
It's easier that way to get a working extraction model, but it's harder
to ensure that the model isn't too restrictive, i.e. that you will
get all
rows you intend to get.
From my experience the best way to define an extraction model is to use
the "Closure Browser"
(use "Closure..."
button).
The closure browser lists all tables which are directly or indirectly
associated with the subject table (where extraction starts) according
to the restrictions, ordered by distance.
Check each table of this list from top to bottom. If you don't want to
have rows from the table to be exported, select the table and disable
the association which gets automatically selected then (table's context
menu is also available here). The table disapears from the list as soon
as it is no longer associated with subject.
This way you easily get an extraction model which extracts rows only
from tables you want to extract data from.
Finally check all associations between the remaining tables.
|
| Q |
How can I disable triggers and constraints during import?
|
| A |
In order to disable triggers (or constraints) belonging to a table T during the execution of generated SQL scripts, create a file named 'prolog/INSERT/T.sql' and write the SQL statement to disable the trigger into it (T
is the fully qualified name of the table). The content of that file
will be written into the script, if at least 1 row of the table
has been exported.
Put the statement to re-enable the trigger into the file 'epilog/INSERT/T.sql'.
For more information, please read 'prolog/INSERT/Readme.txt'.
|
| Q |
What
does $DISTANCE and $IS_SUBJECT mean? |
| A |
$DISTANCE and $IS_SUBJECT are special
expressions used in
restriction conditions in order to define restriction models based on
topological properties of rows.
| Expression |
Type |
|
| $DISTANCE |
INTEGER
|
Minimum length of all paths from the
row back to any subject row.
The distance of subject rows is 0. |
| $BIRTHDAY |
INTEGER |
Synonym for $DISTANCE. |
| $IS_SUBJECT |
BOOLEAN |
Synonym for $DISTANCE=0. |
|
| Q |
How
do I anonymize the extracted data? |
| A |
It's possible to anonymize the extracted
data by defining filters (via "Edit"
menu "Filter editor...",
or table's context menu)
A filter is an assignment of a SQL expression to a table column, the
column values will be replaced by the result of the expression when the
export file is written.
For example:
In order to substitute the values of the column PERSON.SSN with the
constant value '123', define the filter:
PERSON.SSN := '123'
The filter
PERSON.SSN
:= substr(SSN, 1, length(SSN) - 3) || '***'
replaces the last 3 characters of the SSN with '***'
The filter:
PERSON.SSN
:= (SELECT SSN FROM TEST_SSN ORDER BY RAND()
FETCH FIRST 1 ROWS ONLY)
would replace the SSN with a randomly chosen SSN from a table holding
test SSNs (in IBM's DB2)
|
| Q |
Why
doesn't Jailer detect associations in MySQL databases? |
| A |
If you use MyISAM database engine, no
association will be detected. MyISAM doesn't know the concept of
foreign key relationships, even if they're defined in the DDL.
The typical alternative engine that supports foreign key relationships
is InnoDB.
|
| Q |
Is
it possible to avoid the creation of JAILER_* tables in the
database?
|
| A |
It's not possible to avoid the creation of
that tables, but it's possible to have them in an own schema.
The JAILER_* tables will always be created within the database user's
default schema. If you don't want to have them in the schema of user A, then
create a new database user B,
connect as B
and enter A's
schema as the schema to read rows from in the data export
dialog. The meta tables will be
created in B's schema
and the data will be exported from A's
schema.
For DB2, Oracle, PostgreSQL,
Sybase and MS SQL Server, it is also possible to create the tables as
temporary tables.
|
| Q |
I'm
getting the message "Table has no primary key". What's wrong? |
| A |
Jailer cannot handle tables without primary
keys. Exporting data is not possible, if a table without a primary key
is associated with the subject table. In this case, you should define
a key of the table in the tool's data model (not in
the database!) manually, using the data model editor. Note that a key
must be unique and known (i.e. not NULL).
|
| Q |
The
GUI is slow, what can I do?
|
| A |
Due to the dynamic nature of the layouting
algorithm, the
table-association graph is permanently redrawn. It seems that this
consumes too much cpu time on some systems. However, there is a
parameter called "animation
step time" which determines the minimum
time (in ms) for one animation step. Increasing that parameter
(View->Animation
step time) will
reduce the cpu utilization.
|


The documentation is organized around the command line options and as a series of “how to” style examples.
The basic operations support by dataset are listed below organized by collection and JSON document level.
dataset is based around the concept of key/value pairs where the key is the unique identifier for an object stored (i.e. the value) in the collection. Each storage option supported by dataset and its own issues around what things can be called. Keys should be lower case alpha numeric or underscore only. E.g. the pairtree storage relies on the file system to store the JSON objects. Some file systems are not case sensitive, others face challenges with non-alpha numeric filenames.
The following commands are provided to support pairtrees and a limitted amount of backward campatiblity with v1 dataset collections.
New as of version v2.x is a web service providing access to dataset collections. This is described in the datasetd documentation page.
datasetd supports the following end points.
In v2 dataset is starting to suport storing your JSON document in a SQL database. This is an experimental feature and likely to contain some surprises in the 2.0 series implementation. Currently three SQL databases can be used to store the JSON documents, SQLite 3 (used in dataset’s test suites), MySQL 8 (used by some related projects but under tested), Postgres 14 (not tested yet). See storage engines for more details.
As of 2.1 a minimal level of backward dataset v1.1 was added. This includes support for libdataset, a C-shared library. The goal in adding support for v1.1 was to facilate migration in the Caltech Library feeds project. Some features in v1.1 are not available and the features recreated from v1.1 were done so in a way that maintains the approach of v2. Aside from continuing support for libdataset the specific v1.1 features will likely be depreciated over time in the name of keeping the project as simple as possible maintaining focus on the core benefits of dataset versus other JSON store systems.
The methods related to Namaste data have not be implememented as v2
of dataset uses a codemeta.json file for collection metadata. E.g.
Who
, Where
, Location
,
Contact
.
The methods KeySort
and KeyFilter
are not
included.
The DocPath
method returns a full path to the JSON
documented stored in a Pairtree collection. If the collection uses an
SQL store then you will get an empty string and an error indicating that
storage type is not supported by DocPath
.
The Keys
method returns a list of keys (slice of
strings) and an error value.
The following methods were normalized to conform with Go idioms in the standard library.
KeyExists
became HasKey
FrameExists
became HasFrame
The methods for working with versioned content have the order of the parameters revised, basically semver is now the last paramter.
Do to the changes in how keys and attachments are handled in v2 you
don’t need to “santize” your returned JSON object. Dataset v2 does not
inject a _Key
or _Attachments
values in the
JSON document stored. This changes the Read
method
signature for collection objects.
The Init
method takes a DSN as the second parameter, if
the DSN is an empty string then the collection created will use a
Pairtree store.


System Architect Picture "Temporal Link Type"
Parent Diagram:
Associative Category
(OMT Object Model)


Logout
Total Users
Total Counselors
Recently Blocked Users
Name
Email
Phone
See More
Reports
Name
Category
Complain
See More
×
All Counselors
All Users
Single User


Presented by Mike Smith
<EMAIL_ADDRESS>I'm presenting other people's work today.
See The Algorithmic Foundations of Differential Privacy by Dwork and Roth for a rigorous introduction to the framework.
In the mid-1990s the Massachusetts Group Insurance Commission released 'anonymised' health records for state employees.
"William Weld, then Governor of Massachusetts, assured the public that GIC had protected patient privacy by deleting identifiers."
Broken Promises of Privacy, Paul Ohm
The data was 'anonymised' by removing names. Other identifying columns remained.
Latanya Sweeney used the voter rolls (which contain the name, address, ZIP code, date of birth and sex of every voter) to find Governor Weld's medical records...
...which she posted to him.
This is known as a linkage attack:
An attack that uses auxiliary information to compromise privacy in a database.
Although a convenient story about privacy, how many people were really at risk from re-identification in this database is still being debated.
Because of swapping (which we'll get to) the following is a fictitious example.
Run by the Office for National Statistics. The smallest geographical area one can query is called an Output Area
I've queried my output area's "Religion by Age and Sex"
My real output area's data!
My neighbour is this man who's aged over 75.
My neighbour isn't really over 75, and this isn't really a photo of him.
There's only one person (a man) over 75 in the data...
It appears we could re-identify individuals.
Although he's a very private person we can work out his religion by looking at the table.
The ONS have thought of this and perform targeted record swapping to try and achieve anonymity.
Rare households are more likely to be swapped
Households are (usually) swapped within local authority areas
Swaps are matched to preserve some statistical features
Making a dataset private needs more than just erasing names
To achieve a level of privacy the ONS have added randomness to the data
This is a fundamental feature of differential privacy
Maximize the accuracy of queries from statistical databases while minimizing the chances of identifying its records.
Dwork and Roth. "The algorithmic foundations of differential privacy."
We might want to find out whether people hold a controversial view, that they wouldn't normally express:
Many people in this room work in the field, but maybe wouldn't like to say 'Yes' to this question.
If they're the same...
say
YES for heads
and
NO for tails
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 50 | 38 | 27 | 19 | 13 | 9 | 6 | 4 | 3 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 63 | 50 | 38 | 28 | 20 | 14 | 10 | 7 | 4 | 3 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 73 | 62 | 50 | 39 | 29 | 21 | 15 | 11 | 7 | 5 | 3 | 2 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| 3 | 81 | 72 | 62 | 50 | 40 | 30 | 22 | 16 | 11 | 8 | 6 | 4 | 3 | 2 | 1 | 1 | 1 | 0 | 0 | 0 |
| 4 | 87 | 80 | 72 | 61 | 50 | 40 | 31 | 23 | 17 | 12 | 9 | 6 | 4 | 3 | 2 | 1 | 1 | 1 | 0 | 0 |
| 5 | 91 | 86 | 79 | 71 | 61 | 51 | 41 | 32 | 24 | 18 | 13 | 10 | 7 | 5 | 3 | 2 | 2 | 1 | 1 | 1 |
| 6 | 94 | 91 | 85 | 79 | 70 | 61 | 51 | 41 | 32 | 25 | 19 | 14 | 10 | 8 | 5 | 4 | 3 | 2 | 1 | 1 |
| 7 | 96 | 94 | 90 | 85 | 78 | 69 | 60 | 51 | 42 | 33 | 26 | 20 | 15 | 11 | 8 | 6 | 4 | 3 | 2 | 2 |
| 8 | 97 | 96 | 93 | 89 | 84 | 77 | 69 | 60 | 51 | 42 | 34 | 27 | 21 | 16 | 12 | 9 | 7 | 5 | 3 | 3 |
| 9 | 98 | 97 | 95 | 92 | 88 | 83 | 76 | 68 | 60 | 51 | 42 | 35 | 28 | 22 | 17 | 13 | 10 | 7 | 5 | 4 |
| 10 | 99 | 98 | 97 | 95 | 92 | 87 | 82 | 75 | 68 | 59 | 51 | 43 | 35 | 28 | 23 | 18 | 14 | 10 | 8 | 6 |
| 11 | 99 | 99 | 98 | 96 | 94 | 91 | 87 | 81 | 75 | 67 | 59 | 51 | 43 | 36 | 29 | 23 | 18 | 14 | 11 | 8 |
| 12 | 99 | 99 | 98 | 97 | 96 | 94 | 90 | 86 | 80 | 74 | 67 | 59 | 51 | 43 | 36 | 30 | 24 | 19 | 15 | 12 |
| 13 | 100 | 99 | 99 | 98 | 97 | 95 | 93 | 90 | 85 | 80 | 73 | 66 | 59 | 51 | 44 | 37 | 30 | 25 | 20 | 16 |
| 14 | 100 | 100 | 99 | 99 | 98 | 97 | 95 | 92 | 89 | 84 | 79 | 73 | 66 | 58 | 51 | 44 | 37 | 31 | 25 | 21 |
| 15 | 100 | 100 | 100 | 99 | 99 | 98 | 96 | 94 | 92 | 88 | 84 | 78 | 72 | 65 | 58 | 51 | 44 | 38 | 32 | 26 |
| 16 | 100 | 100 | 100 | 99 | 99 | 98 | 97 | 96 | 94 | 91 | 87 | 83 | 78 | 71 | 65 | 58 | 51 | 44 | 38 | 32 |
| 17 | 100 | 100 | 100 | 100 | 99 | 99 | 98 | 97 | 96 | 93 | 91 | 87 | 82 | 77 | 71 | 65 | 58 | 51 | 45 | 38 |
| 18 | 100 | 100 | 100 | 100 | 100 | 99 | 99 | 98 | 97 | 95 | 93 | 90 | 86 | 82 | 76 | 71 | 64 | 58 | 51 | 45 |
| 19 | 100 | 100 | 100 | 100 | 100 | 99 | 99 | 99 | 98 | 97 | 95 | 92 | 89 | 86 | 81 | 76 | 70 | 64 | 58 | 51 |
An individual's opinion is fairly safe in this example (plausible deniability)
We can still perform inference over the whole group
To protect the group, we could introduce more randomness
The go-to book on differential privacy, by Dwork and Roth;
Dwork, Cynthia, and Aaron Roth. "The algorithmic foundations of differential privacy." Theoretical Computer Science 9.3-4 (2013): 211-407.
link
I found this paper allowed me to start applying DP to GP;
Hall, Rob, Alessandro Rinaldo, and Larry Wasserman. "Differential privacy for functions and functional data." The Journal of Machine Learning Research 14.1 (2013): 703-727.
link
Articles about the Massachusetts privacy debate
Barth-Jones, Daniel C. "The're-identification'of Governor William Weld's medical information: a critical re-examination of health data identification risks and privacy protections, then and now." Then and Now (June 4, 2012) (2012). link
Ohm, Paul. "Broken promises of privacy: Responding to the surprising failure of anonymization." UCLA Law Review 57 (2010): 1701. link
Narayanan, Arvind, and Edward W. Felten. "No silver bullet: De-identification still doesn’t work." White Paper (2014). link
Images used: BostonGlobe: Mass Mutual, Weld. Harvard: Sweeney. Rich on flickr: Sheffield skyline.


Date: 9 July
What is 'scope' and how does it work in JavaScript?
Scope relates to where a variable is placed, it is also called variable scope. The location where you declare a variable will affect where it can be used in the code. If a variable is declared within a function, it can only be used within that function. If declared within the function with a var keyword, it is called a local variable or funcation-level scope. It cannot be accessed outside of the function in which it was declared. The interpreter from the browser creates local variables when the function is run and removes them as soon as the function has finished it's task. This means that if the function runs twice, the variable can have different values each time. Local variables are only remembered during the period of time that a function is being executed.
If you create a variable outside of a function, then it can be used anywhere within the script. It is called a global variable and has global scope. Global variables are stored in the memory as long as the page is loaded into the web browser. This means that they take up more memory than local variables, and it also increases the risk of naming conflicts. If you forget to declare a variable using the var keyword, the variable will work but it will be treated as a global variable (this is considered bad practice though).


Introducing Privacy Butler
Many people like you and I feel that we are unable to cope with all the various privacy notices we have to deal with on a daily basis. Our data is important to us, and indeed defines our online identity.
Your Privacy Butler will help you to understand any privacy notices. Simply tell Privacy Butler which data processing is a “no go” for you. It converts the privacy notice into icons that show you immediately whether your desired data protection standard is met or not.
Exquisite Reports
We provide you with clearly readable results that use a visual language to indicate details of the privacy policy in a way you will understand.
Intelligent Advice
Based on machine learning algorithms used in Artificial Intelligence, (almost*) any online privacy policy can be understood and explained by our solution.
Browser Extension
Coming soon, a browser extension to help you make the decision before you share sensitive information with a service whose conditions you do not agree with.


[ all classes ]
[ nl.tudelft.jpacman.game ]
Coverage Summary for Class: SinglePlayerGame (nl.tudelft.jpacman.game)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| SinglePlayerGame | 100% (1/ 1) | 100% (4/ 4) | 100% (10/ 10) |
1 package nl.tudelft.jpacman.game;
2
3 import java.util.List;
4
5 import nl.tudelft.jpacman.level.Level;
6 import nl.tudelft.jpacman.level.Player;
7
8 import com.google.common.collect.ImmutableList;
9 import nl.tudelft.jpacman.points.PointCalculator;
10
11 /**
12 * A game with one player and a single level.
13 *
14 * @author Jeroen Roosen
15 */
16 public class SinglePlayerGame extends Game {
17
18 /**
19 * The player of this game.
20 */
21 private final Player player;
22
23 /**
24 * The level of this game.
25 */
26 private final Level level;
27
28 /**
29 * Create a new single player game for the provided level and player.
30 *
31 * @param player
32 * The player.
33 * @param level
34 * The level.
35 * @param pointCalculator
36 * The way to calculate points upon collisions.
37 */
38 protected SinglePlayerGame(Player player, Level level, PointCalculator pointCalculator) {
39 super(pointCalculator);
40
41 assert player != null;
42 assert level != null;
43
44 this.player = player;
45 this.level = level;
46 this.level.registerPlayer(player);
47 }
48
49 @Override
50 public List<Player> getPlayers() {
51 return ImmutableList.of(player);
52 }
53
54 @Override
55 public Level getLevel() {
56 return level;
57 }
58 }


Transactional Id:
Producer Id (Epoch):
Timeout (ms):
Success RPM:
Fail RPM:
Execution Time Statistics: Min: Max: Mean:
Topics:


asquare.gwt.tests.coordinates.client.Demo
Windows 1.0.21
Hosted Browser (XP), IE6 (XP), FFox 1.5 (XP), Opera (XP), Safari (OS 10.4.7)
Follow instructions on the test page
Strict mode test
Quirks mode test
See also Mouse position on Quirksmode


{{ equipmentCategogy.equipmentCategory}}
{{selectedEquipmentType}}
Type of Equipment
Select Manufacturer


Realtime Bitcoin Transactions
Not connected to controller
Time since observation start:
Time since last block
:
pending

TxRate:
tps
Value
Fees
Size
Fee Density
NumTx:
Max:
Max:
Max:
bytes
NumIn:
Total:
Total:
Total:
bytes
NumOut:
Avg:
Avg:
Avg:
bytes
Avg:
NumNodes:


Make memories, be inspired
Built in 1890, the W.H. Jones Mansion has wood work like nothing seen today. A historic mansion all to your self in the heart of downtown Columbus, Ohio. Read about the space on Wikipedia.
The Columbus Learning Cooperative
We offer a large space on the first floor which includes a dinning room, two living rooms, a kitchenette, and a kitchen with a refridgerator and stove. On the third floor we have a ballroom
Located near downtown in Olde Towne East


**Ray Tracing in One Weekend**
Peter Shirley
Version 2.0.0, 2019-Oct-07
Copyright 2018-2019. Peter Shirley. All rights reserved.
Overview
====================================================================================================
I’ve taught many graphics classes over the years. Often I do them in ray tracing, because you are
forced to write all the code but you can still get cool images with no API. I decided to adapt my
course notes into a how-to, to get you to a cool program as quickly as possible. It will not be a
full-featured ray tracer, but it does have the indirect lighting which has made ray tracing a staple
in movies. Follow these steps, and the architecture of the ray tracer you produce will be good for
extending to a more extensive ray tracer if you get excited and want to pursue that.
When somebody says “ray tracing” it could mean many things. What I am going to describe is
technically a path tracer, and a fairly general one. While the code will be pretty simple (let the
computer do the work!) I think you’ll be very happy with the images you can make.
I’ll take you through writing a ray tracer in the order I do it, along with some debugging tips. By
the end, you will have a ray tracer that produces some great images. You should be able to do this
in a weekend. If you take longer, don’t worry about it. I use C++ as the driving language, but you
don’t need to. However, I suggest you do, because it’s fast, portable, and most production movie and
video game renderers are written in C++. Note that I avoid most “modern features” of C++, but
inheritance and operator overloading are too useful for ray tracers to pass on. I do not provide the
code online, but the code is real and I show all of it except for a few straightforward operators in
the vec3 class. I am a big believer in typing in code to learn it, but when code is available I use
it, so I only practice what I preach when the code is not available. So don’t ask!
I have left that last part in because it is funny what a 180 I have done. Several readers ended up
with subtle errors that were helped when we compared code. So please do type in the code, but if you
want to look at mine it is at:
https://github.com/RayTracing/raytracing.github.io/
I assume a little bit of familiarity with vectors (like dot product and vector addition). If you
don’t know that, do a little review. If you need that review, or to learn it for the first time,
check out Marschner’s and my graphics text, Foley, Van Dam, _et al._, or McGuire’s graphics codex.
If you run into trouble, or do something cool you’d like to show somebody, send me some email at
<EMAIL_ADDRESS>I’ll be maintaining a site related to the book including further reading and links to resources at a
blog https://in1weekend.blogspot.com/ related to this book.
Let’s get on with it!
Output an Image
====================================================================================================
Whenever you start a renderer, you need a way to see an image. The most straightforward way is to
write it to a file. The catch is, there are so many formats and many of those are complex. I always
start with a plain text ppm file. Here’s a nice description from Wikipedia:
![Image 2-1: PPM Example](../images/img-1-02-1.jpg)
Let’s make some C++ code to output such a thing:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#include
int main() {
int nx = 200;
int ny = 100;
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
for (int j = ny-1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
float r = float(i) / float(nx);
float g = float(j) / float(ny);
float b = 0.2;
int ir = int(255.99*r);
int ig = int(255.99*g);
int ib = int(255.99*b);
std::cout << ir << " " << ig << " " << ib << "\n";
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are some things to note in that code:
1. The pixels are written out in rows with pixels left to right.
2. The rows are written out from top to bottom.
3. By convention, each of the red/green/blue components range from 0.0 to 1.0. We will relax that
later when we internally use high dynamic range, but before output we will tone map to the zero
to one range, so this code won’t change.
4. Red goes from black to fully on from left to right, and green goes from black at the bottom to
fully on at the top. Red and green together make yellow so we should expect the upper right
corner to be yellow.
Opening the output file (in ToyViewer on my mac, but try it in your favorite viewer and google “ppm
viewer” if your viewer doesn’t support it) shows:
![Image 2-2](../images/img-1-02-2.jpg)
Hooray! This is the graphics “hello world”. If your image doesn’t look like that, open the output
file in a text editor and see what it looks like. It should start something like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
P3
200 100
255
0 253 51
1 253 51
2 253 51
3 253 51
4 253 51
5 253 51
6 253 51
7 253 51
8 253 51
9 253 51
10 253 51
11 253 51
12 253 51
13 253 51
14 253 51
15 253 51
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If it doesn’t, then you probably just have some newlines or something similar that is confusing the
image reader.
If you want to produce more image types than PPM, I am a fan of `stb_image.h` available on github.
The vec3 Class
====================================================================================================
Almost all graphics programs have some class(es) for storing geometric vectors and colors. In many
systems these vectors are 4D (3D plus a homogeneous coordinate for geometry, and RGB plus an alpha
transparency channel for colors). For our purposes, three coordinates suffices. We’ll use the same
class `vec3` for colors, locations, directions, offsets, whatever.  They
have a good point, but we’re going to always take the “less code” route when not obviously wrong.
Now we can change our main to use this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#include "vec3.h"
#include
int main() {
int nx = 200;
int ny = 100;
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
for (int j = ny-1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 col(float(i) / float(nx), float(j) / float(ny), 0.2);
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
std::cout << ir << " " << ig << " " << ib << "\n";
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rays, a Simple Camera, and Background
====================================================================================================
The one thing that all ray tracers have is a ray class, and a computation of what color is seen
along a ray. Let’s think of a ray as a function $p(t) = A + t*B$. Here $p$ is a 3D position along a
line in 3D. $A$ is the ray origin and $B$ is the ray direction. The ray parameter $t$ is a real
number (float in the code). Plug in a different $t$ and $p(t)$ moves the point along the ray. Add in
negative $t$ and you can go anywhere on the 3D line. For positive $t$, you get only the parts in
front of $A$, and this is what is often called a half-line or ray. The example $C = p(2)$ is shown
here:
![Figure 4-1](../images/fig-1-04-1.jpg)
The function $p(t)$ in more verbose code form I call “point_at_parameter(t)”:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef RAYH
#define RAYH
#include "vec3.h"
class ray
{
public:
ray() {}
ray(const vec3& a, const vec3& b) { A = a; B = b; }
vec3 origin() const { return A; }
vec3 direction() const { return B; }
vec3 point_at_parameter(float t) const { return A + t*B; }
vec3 A;
vec3 B;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now we are ready to turn the corner and make a ray tracer. At the core of a ray tracer is to send
rays through pixels and compute what color is seen in the direction of those rays. This is of the
form calculate which ray goes from the eye to a pixel, compute what that ray intersects, and compute
a color for that intersection point. When first developing a ray tracer, I always do a simple camera
for getting the code up and running. I also make a simple `color(ray)` function that returns the
color of the background (a simple gradient).
I’ve often gotten into trouble using square images for debugging because I transpose $x$ and $y$ too
often, so I’ll stick with a 200×100 image. I’ll put the “eye” (or camera center if you think of a
camera) at $(0,0,0)$. I will have the y-axis go up, and the x-axis to the right. In order to respect
the convention of a right handed coordinate system, into the screen is the negative z-axis. I will
traverse the screen from the lower left hand corner and use two offset vectors along the screen
sides to move the ray endpoint across the screen. Note that I do not make the ray direction a unit
length vector because I think not doing that makes for simpler and slightly faster code.
![Figure 4-2](../images/fig-1-04-2.jpg)
Below in code, the ray $r$ goes to approximately the pixel centers (I won’t worry about exactness
for now because we’ll add antialiasing later):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#include "ray.h"
#include
vec3 color(const ray& r) {
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
}
int main() {
int nx = 200;
int ny = 100;
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 lower_left_corner(-2.0, -1.0, -1.0);
vec3 horizontal(4.0, 0.0, 0.0);
vec3 vertical(0.0, 2.0, 0.0);
vec3 origin(0.0, 0.0, 0.0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
for (int j = ny-1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
float u = float(i) / float(nx);
float v = float(j) / float(ny);
ray r(origin, lower_left_corner + u*horizontal + v*vertical);
vec3 col = color(r);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
std::cout << ir << " " << ig << " " << ib << "\n";
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The `color(ray)` function linearly blends white and blue depending on the up/downess of the $y$
coordinate. I first made it a unit vector so $-1.0 < y < 1.0$. I then did a standard graphics trick
of scaling that to $0.0 < t < 1.0$. When $t = 1.0$ I want blue. When $t = 0.0$ I want white. In
between, I want a blend. This forms a “linear blend”, or “linear interpolation”, or “lerp” for
short, between two things. A lerp is always of the form
$$ blendedValue = (1-t)*startValue + t*endValue, $$
with $t$ going from zero to one. In our case this produces:
![Image 4-1](../images/img-1-04-1.jpg)
Adding a Sphere
====================================================================================================
Let’s add a single object to our ray tracer. People often use spheres in ray tracers because
calculating whether a ray hits a sphere is pretty straightforward. Recall that the equation for a
sphere centered at the origin of radius $R$ is $x^2 + y^2 + z^2 = R^2$. The way you can read that
equation is “for any $(x, y, z)$, if $x^2 + y^2 + z^2 = R^2$ then $(x,y,z)$ is on the sphere, and
otherwise it is not”. It gets uglier if the sphere center is at $(C_x, C_y, C_z)$:
$$ (x-C_x)^2 + (y-C_y)^2 + (z-C_z)^2 = R^2 $$
In graphics, you almost always want your formulas to be in terms of vectors so all the x/y/z stuff
is under the hood in the `vec3` class. You might note that the vector from center
$C = (C_x,C_y,C_z)$ to point $P = (x,y,z)$ is $(p - C)$, and therefore
$$ dot((p - C),(p - C)) = (x-C_x)^2 + (y-C_y)^2 + (z-C_z)^2 $$
So the equation of the sphere in vector form is:
$$ dot((p - C),(p - C)) = R^2 $$
We can read this as “any point p that satisfies this equation is on the sphere”. We want to know
if our ray $p(t) = A + t*B$ ever hits the sphere anywhere. If it does hit the sphere, there is
some $t$ for which $p(t)$ satisfies the sphere equation. So we are looking for any $t$ where this
is true:
$$ dot((p(t) - C),(p(t) - C)) = R^2 $$
or expanding the full form of the ray $p(t)$:
$$ dot((A + t*B - C), (A + t*B - C)) = R^2 $$
The rules of vector algebra are all that we would want here, and if we expand that equation and
move all the terms to the left hand side we get:
$$ t^2 \cdot dot(B,B) + 2t \cdot dot(B,A-C) + dot(A-C,A-C) - R^2 = 0 $$
The vectors and $R$ in that equation are all constant and known. The unknown is $t$, and the
equation is a quadratic, like you probably saw in your high school math class. You can solve for $t$
and there is a square root part that is either positive (meaning two real solutions), negative
(meaning no real solutions), or zero (meaning one real solution). In graphics, the algebra almost
always relates very directly to the geometry. What we have is:
![Figure 5-1](../images/fig-1-05-1.jpg)
If we take that math and hard-code it into our program, we can test it by coloring red any pixel
that hits a small sphere we place at -1 on the z-axis:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
bool hit_sphere(const vec3& center, float radius, const ray& r) {
vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = 2.0 * dot(oc, r.direction());
float c = dot(oc, oc) - radius*radius;
float discriminant = b*b - 4*a*c;
return (discriminant > 0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 color(const ray& r) {
if (hit_sphere(vec3(0,0,-1), 0.5, r))
return vec3(1, 0, 0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
What we get is this:
![Image 5-1](../images/img-1-05-1.jpg)
Now this lacks all sorts of things -- like shading and reflection rays and more than one object --
but we are closer to halfway done than we are to our start! One thing to be aware of is that we
tested whether the ray hits the sphere at all, but $t < 0$ solutions work fine. If you change your
sphere center to $z = +1$ you will get exactly the same picture because you see the things behind
you. This is not a feature! We’ll fix those issues next.
Surface Normals and Multiple Objects
====================================================================================================
First, let’s get ourselves a surface normal so we can shade. This is a vector that is perpendicular
to the surface, and by convention, points out. One design decision is whether these normals
(again by convention) are unit length. That is convenient for shading so I will say yes, but I won’t
enforce that in the code. This could allow subtle bugs, so be aware this is personal preference
as are most design decisions like that. For a sphere, the normal is in the direction of the hitpoint
minus the center:
![Figure 6-1](../images/fig-1-06-1.jpg)
On the earth, this implies that the vector from the earth’s center to you points straight up. Let’s
throw that into the code now, and shade it. We don’t have any lights or anything yet, so let’s just
visualize the normals with a color map. A common trick used for visualizing normals (because it’s
easy and somewhat intuitive to assume $N$ is a unit length vector -- so each component is between -1
and 1) is to map each component to the interval from 0 to 1, and then map x/y/z to r/g/b. For the
normal, we need the hit point, not just whether we hit or not. Let’s assume the closest hit point
(smallest $t$). These changes in the code let us compute and visualize $N$:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
float hit_sphere(const vec3& center, float radius, const ray& r) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = 2.0 * dot(oc, r.direction());
float c = dot(oc, oc) - radius*radius;
float discriminant = b*b - 4*a*c;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
if (discriminant < 0) {
return -1.0;
}
else {
return (-b - sqrt(discriminant) ) / (2.0*a);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
vec3 color(const ray& r) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
float t = hit_sphere(vec3(0,0,-1), 0.5, r);
if (t > 0.0) {
vec3 N = unit_vector(r.point_at_parameter(t) - vec3(0,0,-1));
return 0.5*vec3(N.x()+1, N.y()+1, N.z()+1);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 unit_direction = unit_vector(r.direction());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
t = 0.5*(unit_direction.y() + 1.0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And that yields this picture:
![Image 6-1](../images/img-1-06-1.jpg)
Now, how about several spheres? While it is tempting to have an array of spheres, a very clean
solution is the make an “abstract class” for anything a ray might hit and make both a sphere and a
list of spheres just something you can hit. What that class should be called is something of a
quandary -- calling it an “object” would be good if not for “object oriented” programming. “Surface”
is often used, with the weakness being maybe we will want volumes. “hittable” emphasizes the member
function that unites them. I don’t love any of these but I will go with “hittable”.
This `hittable` abstract class will have a hit function that takes in a ray. Most ray tracers have
found it convenient to add a valid interval for hits $t_{min}$ to $t_{max}$, so the hit only
“counts” if $t_{min} < t < t_{max}$. For the initial rays this is positive $t$, but as we will see,
it can help some details in the code to have an interval $t_{min}$ to $t_{max}$. One design question
is whether to do things like compute the normal if we hit something. We might end up hitting
something closer as we do our search, and we will only need the normal of the closest thing. I will
go with the simple solution and compute a bundle of stuff I will store in some structure. I know
we’ll want motion blur at some point, so I’ll add a time input variable. Here’s the abstract class:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef HITTABLEH
#define HITTABLEH
#include "ray.h"
struct hit_record {
float t;
vec3 p;
vec3 normal;
};
class hittable {
public:
virtual bool hit(
const ray& r, float t_min, float t_max, hit_record& rec) const = 0;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And here’s the sphere (note that I eliminated a bunch of redundant 2’s that cancel each other out):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef SPHEREH
#define SPHEREH
#include "hittable.h"
class sphere: public hittable {
public:
sphere() {}
sphere(vec3 cen, float r) : center(cen), radius(r) {};
virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
vec3 center;
float radius;
};
bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = dot(oc, r.direction());
float c = dot(oc, oc) - radius*radius;
float discriminant = b*b - a*c;
if (discriminant > 0) {
float temp = (-b - sqrt(discriminant))/a;
if (temp < t_max && temp > t_min) {
rec.t = temp;
rec.p = r.point_at_parameter(rec.t);
rec.normal = (rec.p - center) / radius;
return true;
}
temp = (-b + sqrt(discriminant)) / a;
if (temp < t_max && temp > t_min) {
rec.t = temp;
rec.p = r.point_at_parameter(rec.t);
rec.normal = (rec.p - center) / radius;
return true;
}
}
return false;
}
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And a list of objects:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef HITTABLELISTH
#define HITTABLELISTH
#include "hittable.h"
class hittable_list: public hittable {
public:
hittable_list() {}
hittable_list(hittable **l, int n) {list = l; list_size = n; }
virtual bool hit(
const ray& r, float tmin, float tmax, hit_record& rec) const;
hittable **list;
int list_size;
};
bool hittable_list::hit(const ray& r, float t_min, float t_max,
hit_record& rec) const {
hit_record temp_rec;
bool hit_anything = false;
double closest_so_far = t_max;
for (int i = 0; i < list_size; i++) {
if (list[i]->hit(r, t_min, closest_so_far, temp_rec)) {
hit_anything = true;
closest_so_far = temp_rec.t;
rec = temp_rec;
}
}
return hit_anything;
}
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And the new main:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#include "float.h"
#include "hittable_list.h"
#include "sphere.h"
#include
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 color(const ray& r, hittable *world) {
hit_record rec;
if (world->hit(r, 0.0, MAXFLOAT, rec)) {
return 0.5*vec3(rec.normal.x()+1, rec.normal.y()+1, rec.normal.z()+1);
}
else {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 unit_direction = unit_vector(r.direction());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
float t = 0.5*(unit_direction.y() + 1.0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
}
}
int main() {
int nx = 200;
int ny = 100;
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
vec3 lower_left_corner(-2.0, -1.0, -1.0);
vec3 horizontal(4.0, 0.0, 0.0);
vec3 vertical(0.0, 2.0, 0.0);
vec3 origin(0.0, 0.0, 0.0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
hittable *list[2];
list[0] = new sphere(vec3(0,0,-1), 0.5);
list[1] = new sphere(vec3(0,-100.5,-1), 100);
hittable *world = new hittable_list(list,2);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
for (int j = ny-1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
float u = float(i) / float(nx);
float v = float(j) / float(ny);
ray r(origin, lower_left_corner + u*horizontal + v*vertical);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 p = r.point_at_parameter(2.0);
vec3 col = color(r, world);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
std::cout << ir << " " << ig << " " << ib << "\n";
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This yields a picture that is really just a visualization of where the spheres are along with their
surface normal. This is often a great way to look at your model for flaws and characteristics.
![Image 6-2](../images/img-1-06-2.jpg)
Antialiasing
====================================================================================================
When a real camera takes a picture, there are usually no jaggies along edges because the edge pixels
are a blend of some foreground and some background.  We will not bother with stratification, which is controversial but is
usual for my programs. For some ray tracers it is critical, but the kind of general one we are
writing doesn’t benefit very much from it and it makes the code uglier. We abstract the camera class
a bit so we can make a cooler camera later.
One thing we need is a random number generator that returns real random numbers. We need a function
that returns a canonical random number which by convention returns random real in the range
$0 ≤ ran < 1$. The “less than” before the 1 is important as we will sometimes take advantage of
that.
A simple approach to this is to use the `rand()` function that can be found in ``. This
function returns a random integer in the range 0 and RANDMAX. Hence we can get a real random number
as desired with the following code snippet:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef RANDOMH
#define RANDOMH
#include
inline double random_double() {
return rand() / (RAND_MAX + 1.0);
}
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C++ did not traditionally have a standard random number generator, but newer versions of C++ have
addressed this issue with the `` header (if imperfectly according to some experts).
If you want to use this, you can obtain a random number with the conditions we need as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef RANDOMH
#define RANDOMH
#include
#include
inline double random_double() {
static std::uniform_real_distribution distribution(0.0, 1.0);
static std::mt19937 generator;
static std::function rand_generator =
std::bind(distribution, generator);
return rand_generator();
}
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For a given pixel we have several samples within that pixel and send rays through each of the
samples. The colors of these rays are then averaged:
![Figure 7-1](../images/fig-1-07-1.jpg)
Putting that all together yields a camera class encapsulating our simple axis-aligned camera from
before:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef CAMERAH
#define CAMERAH
#include "ray.h"
class camera {
public:
camera() {
lower_left_corner = vec3(-2.0, -1.0, -1.0);
horizontal = vec3(4.0, 0.0, 0.0);
vertical = vec3(0.0, 2.0, 0.0);
origin = vec3(0.0, 0.0, 0.0);
}
ray get_ray(float u, float v) {
return ray(origin,
lower_left_corner + u*horizontal + v*vertical - origin);
}
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Main is also changed:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
int main() {
int nx = 200;
int ny = 100;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
int ns = 100;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
hittable *list[2];
list[0] = new sphere(vec3(0,0,-1), 0.5);
list[1] = new sphere(vec3(0,-100.5,-1), 100);
hittable *world = new hittable_list(list,2);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
camera cam;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
for (int j = ny-1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 col(0, 0, 0);
for (int s = 0; s < ns; s++) {
float u = float(i + random_double()) / float(nx);
float v = float(j + random_double()) / float(ny);
ray r = cam.get_ray(u, v);
col += color(r, world);
}
col /= float(ns);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
std::cout << ir << " " << ig << " " << ib << "\n";
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Zooming into the image that is produced, the big change is in edge pixels that are part background
and part foreground:
![Image 7-1](../images/img-1-07-1.jpg)
Diffuse Materials
====================================================================================================
Now that we have objects and multiple rays per pixel, we can make some realistic looking materials.
We’ll start with diffuse (matte) materials. One question is whether we can mix and match shapes and
materials (so we assign a sphere a material) or if it’s put together so the geometry and material
are tightly bound (that could be useful for procedural objects where the geometry and material are
linked). We’ll go with separate -- which is usual in most renderers -- but do be aware of the
limitation.
Diffuse objects that don’t emit light merely take on the color of their surroundings, but they
modulate that with their own intrinsic color. Light that reflects off a diffuse surface has its
direction randomized. So, if we send three rays into a crack between two diffuse surfaces they will
each have different random behavior:
![Figure 8-1](../images/fig-1-08-1.jpg)
They also might be absorbed rather than reflected. The darker the surface, the more likely
absorption is. (That’s why it is dark!) Really any algorithm that randomizes direction will produce
surfaces that look matte. One of the simplest ways to do this turns out to be exactly correct for
ideal diffuse surfaces. (I used to do it as a lazy hack that approximates mathematically ideal
Lambertian.)
Pick a random point s from the unit radius sphere that is tangent to the hitpoint, and send a ray
from the hitpoint $p$ to the random point $s$. That sphere has center $(p + N)$:
![Figure 8-2](../images/fig-1-08-2.jpg)
We also need a way to pick a random point in a unit radius sphere centered at the origin. We’ll use
what is usually the easiest algorithm: a rejection method. First, we pick a random point in the unit
cube where x, y, and z all range from -1 to +1. We reject this point and try again if the point is
outside the sphere. A do/while construct is perfect for that:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 random_in_unit_sphere() {
vec3 p;
do {
p = 2.0*vec3(random_double(), random_double(), random_double()) - vec3(1,1,1);
} while (p.squared_length() >= 1.0);
return p;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Then update the `color()` function to use the new random direction generator:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 color(const ray& r, hittable *world) {
hit_record rec;
if (world->hit(r, 0.0, MAXFLOAT, rec)) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 target = rec.p + rec.normal + random_in_unit_sphere();
return 0.5 * color(ray(rec.p, target - rec.p), world);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
else {
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This gives us:
![Image 8-1](../images/img-1-08-1.jpg)
Note the shadowing under the sphere. This picture is very dark, but our spheres only absorb half the
energy on each bounce, so they are 50% reflectors. If you can’t see the shadow, don’t worry, we will
fix that now. These spheres should look pretty light (in real life, a light grey). The reason for
this is that almost all image viewers assume that the image is “gamma corrected”, meaning the 0 to 1
values have some transform before being stored as a byte. There are many good reasons for that, but
for our purposes we just need to be aware of it. To a first approximation, we can use “gamma 2”
which means raising the color to the power $1/gamma$, or in our simple case ½, which is just
square-root:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
col /= float(ns);
col = vec3( sqrt(col[0]), sqrt(col[1]), sqrt(col[2]) );
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
std::cout << ir << " " << ig << " " << ib << "\n";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
That yields light grey, as we desire:
![Image 8-2](../images/img-1-08-2.jpg)
There’s also a subtle bug in there. Some of the reflected rays hit the object they are reflecting
off of not at exactly $t=0$, but instead at $t=-0.0000001$ or $t=0.00000001$ or whatever floating
point approximation the sphere intersector gives us. So we need to ignore hits very near zero:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
if (world->hit(r, 0.001, MAXFLOAT, rec)) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This gets rid of the shadow acne problem. Yes it is really called that.
Metal
====================================================================================================
If we want different objects to have different materials, we have a design decision. We could have a
universal material with lots of parameters and different material types just zero out some of those
parameters. This is not a bad approach. Or we could have an abstract material class that
encapsulates behavior. I am a fan of the latter approach. For our program the material needs to do
two things:
1. Produce a scattered ray (or say it absorbed the incident ray).
2. If scattered, say how much the ray should be attenuated.
This suggests the abstract class:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class material {
public:
virtual bool scatter(
const ray& r_in, const hit_record& rec, vec3& attenuation,
ray& scattered) const = 0;
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The `hit_record` is to avoid a bunch of arguments so we can stuff whatever info we want in there.
You can use arguments instead; it’s a matter of taste. Hittables and materials need to know each
other so there is some circularity of the references. In C++ you just need to alert the compiler
that the pointer is to a class, which the “class material” in the hittable class below does:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef HITTABLEH
#define HITTABLEH
#include "ray.h"
class material;
struct hit_record {
float t;
vec3 p;
vec3 normal;
material *mat_ptr;
};
class hittable {
public:
virtual bool hit(
const ray& r, float t_min, float t_max, hit_record& rec) const = 0;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
What we have set up here is that material will tell us how rays interact with the surface.
`hit_record` is just a way to stuff a bunch of arguments into a struct so we can send them as a
group. When a ray hits a surface (a particular sphere for example), the material pointer in the
`hit_record` will be set to point at the material pointer the sphere was given when it was set up in
`main()` when we start. When the `color()` routine gets the `hit_record` it can call member
functions of the material pointer to find out what ray, if any, is scattered.
To achieve this, we must have a reference to the material for our sphere class to returned
within `hit_record`. See the lines below marked with **`/* NEW */`**.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class sphere: public hittable {
public:
sphere() {}
sphere(vec3 cen, float r, material *m)
: center(cen), radius(r), mat_ptr(m) {};
virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
vec3 center;
float radius;
material *mat_ptr; /* NEW */
};
bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = dot(oc, r.direction());
float c = dot(oc, oc) - radius*radius;
float discriminant = b*b - a*c;
if (discriminant > 0) {
float temp = (-b - sqrt(discriminant))/a;
if (temp < t_max && temp > t_min) {
rec.t = temp;
rec.p = r.point_at_parameter(rec.t);
rec.normal = (rec.p - center) / radius;
rec.mat_ptr = mat_ptr; /* NEW */
return true;
}
temp = (-b + sqrt(discriminant)) / a;
if (temp < t_max && temp > t_min) {
rec.t = temp;
rec.p = r.point_at_parameter(rec.t);
rec.normal = (rec.p - center) / radius;
rec.mat_ptr = mat_ptr; /* NEW */
return true;
}
}
return false;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For the Lambertian (diffuse) case we already have, it can either scatter always and attenuate by its
reflectance $R$, or it can scatter with no attenuation but absorb the fraction $1-R$ of the rays. Or
it could be a mixture of those strategies. For Lambertian materials we get this simple class:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class lambertian : public material {
public:
lambertian(const vec3& a) : albedo(a) {}
virtual bool scatter(const ray& r_in, const hit_record& rec,
vec3& attenuation, ray& scattered) const {
vec3 target = rec.p + rec.normal + random_in_unit_sphere();
scattered = ray(rec.p, target-rec.p);
attenuation = albedo;
return true;
}
vec3 albedo;
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note we could just as well only scatter with some probability $p$ and have attenuation be
$albedo/p$. Your choice.
For smooth metals the ray won’t be randomly scattered. The key math is: how does a ray get
reflected from a metal mirror? Vector math is our friend here:
![Figure 9-1](../images/fig-1-09-1.jpg)
The reflected ray direction in red is just $(v + 2B)$. In our design, $N$ is a unit vector, but $v$
may not be. The length of $B$ should be $dot(v,N)$. Because $v$ points in, we will need a minus
sign, yielding:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 reflect(const vec3& v, const vec3& n) {
return v - 2*dot(v,n)*n;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The metal material just reflects rays using that formula:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class metal : public material {
public:
metal(const vec3& a) : albedo(a) {}
virtual bool scatter(const ray& r_in, const hit_record& rec,
vec3& attenuation, ray& scattered) const {
vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected);
attenuation = albedo;
return (dot(scattered.direction(), rec.normal) > 0);
}
vec3 albedo;
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We need to modify the color function to use this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 color(const ray& r, hittable *world, int depth) {
hit_record rec;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
if (world->hit(r, 0.001, MAXFLOAT, rec)) {
ray scattered;
vec3 attenuation;
if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered)) {
return attenuation*color(scattered, world, depth+1);
}
else {
return vec3(0,0,0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
else {
vec3 unit_direction = unit_vector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You will also need to modify the sphere class to have a material pointer to it. And add some
metal spheres:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
int main() {
int nx = 200;
int ny = 100;
int ns = 100;
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
hittable *list[4];
list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.8, 0.3, 0.3)));
list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2)));
list[3] = new sphere(vec3(-1,0,-1), 0.5, new metal(vec3(0.8, 0.8, 0.8)));
hittable *world = new hittable_list(list,4);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
camera cam;
for (int j = ny-1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
vec3 col(0, 0, 0);
for (int s = 0; s < ns; s++) {
float u = float(i + random_double()) / float(nx);
float v = float(j + random_double()) / float(ny);
ray r = cam.get_ray(u, v);
col += color(r, world, 0);
}
col /= float(ns);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
col = vec3( sqrt(col[0]), sqrt(col[1]), sqrt(col[2]) );
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
std::cout << ir << " " << ig << " " << ib << "\n";
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Which gives:
![Image 9-1](../images/img-1-09-1.jpg)
We can also randomize the reflected direction by using a small sphere and choosing a new endpoint
for the ray:
![Figure 9-2](../images/fig-1-09-2.jpg)
The bigger the sphere, the fuzzier the reflections will be. This suggests adding a fuzziness
parameter that is just the radius of the sphere (so zero is no perturbation). The catch is that for
big spheres or grazing rays, we may scatter below the surface.  We’ll put a maximum of 1 on the radius of the sphere which yields:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class metal : public material {
public:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
metal(const vec3& a, float f) : albedo(a) {
if (f < 1) fuzz = f; else fuzz = 1;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
virtual bool scatter(const ray& r_in, const hit_record& rec,
vec3& attenuation, ray& scattered) const {
vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere());
attenuation = albedo;
return (dot(scattered.direction(), rec.normal) > 0);
}
vec3 albedo;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
float fuzz;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We can try that out by adding fuzziness 0.3 and 1.0 to the metals:
![Image 9-2](../images/img-1-09-2.jpg)
Dielectrics
====================================================================================================
Clear materials such as water, glass, and diamonds are dielectrics. When a light ray hits them, it
splits into a reflected ray and a refracted (transmitted) ray. We’ll handle that by randomly
choosing between reflection or refraction and only generating one scattered ray per interaction.
The hardest part to debug is the refracted ray. I usually first just have all the light refract if
there is a refraction ray at all. For this project, I tried to put two glass balls in our scene, and
I got this (I have not told you how to do this right or wrong yet, but soon!):
![Image 10-1](../images/img-1-10-1.jpg)
Is that right?  But no, it isn’t right. The world should be
flipped upside down and no weird black stuff. I just printed out the ray straight through the middle
of the image and it was clearly wrong. That often does the job.
The refraction is described by Snell’s law:
$$ n \cdot sin(theta) = n' \cdot sin(theta') $$
Where $n$ and $n'$ are the refractive indices (typically air = 1, glass = 1.3–1.7, diamond = 2.4)
and the geometry is:
![Figure 10-1](../images/fig-1-10-1.jpg)
One troublesome practical issue is that when the ray is in the material with the higher refractive
index, there is no real solution to Snell’s law and thus there is no refraction possible. Here all
the light is reflected, and because in practice that is usually inside solid objects, it is called
“total internal reflection”. This is why sometimes the water-air boundary acts as a perfect mirror
when you are submerged. The code for refraction is thus a bit more complicated than for reflection:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
bool refract(const vec3& v, const vec3& n, float ni_over_nt, vec3& refracted) {
vec3 uv = unit_vector(v);
float dt = dot(uv, n);
float discriminant = 1.0 - ni_over_nt*ni_over_nt*(1-dt*dt);
if (discriminant > 0) {
refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminant);
return true;
}
else
return false;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And the dielectric material that always refracts when possible is:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class dielectric : public material {
public:
dielectric(float ri) : ref_idx(ri) {}
virtual bool scatter(const ray& r_in, const hit_record& rec,
vec3& attenuation, ray& scattered) const {
vec3 outward_normal;
vec3 reflected = reflect(r_in.direction(), rec.normal);
float ni_over_nt;
attenuation = vec3(1.0, 1.0, 0.0);
vec3 refracted;
if (dot(r_in.direction(), rec.normal) > 0) {
outward_normal = -rec.normal;
ni_over_nt = ref_idx;
}
else {
outward_normal = rec.normal;
ni_over_nt = 1.0 / ref_idx;
}
if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) {
scattered = ray(rec.p, refracted);
}
else {
scattered = ray(rec.p, reflected);
return false;
}
return true;
}
float ref_idx;
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Attenuation is always 1 -- the glass surface absorbs nothing. The `attenuation = vec3(1.0, 1.0,
0.0)` above will also kill the blue channel which is the type of color bug that can be hard to find
-- it will give a color shift only. Try it the way it is and then change it to
`attenuation = vec3(1.0, 1.0, 1.0)` to see the difference.
If we try that out with these parameters:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));
list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We get:
![Image 10-2](../images/img-1-10-2.jpg)
(The reader Becker has pointed out that when there is a reflection ray the function returns `false`
so there are no reflections. He is right, and that is why there are none in the image above. I
am leaving this in rather than correcting this because it is a very interesting example of a major
bug that still leaves a reasonably plausible image. These sleeper bugs are the hardest bugs to
find because we humans are not designed to find fault with what we see.)
Now real glass has reflectivity that varies with angle -- look at a window at a steep angle and it
becomes a mirror. There is a big ugly equation for that, but almost everybody uses a simple and
surprisingly simple polynomial approximation by Christophe Schlick:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
float schlick(float cosine, float ref_idx) {
float r0 = (1-ref_idx) / (1+ref_idx);
r0 = r0*r0;
return r0 + (1-r0)*pow((1 - cosine),5);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This yields our full glass material:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class dielectric : public material {
public:
dielectric(float ri) : ref_idx(ri) {}
virtual bool scatter(const ray& r_in, const hit_record& rec,
vec3& attenuation, ray& scattered) const {
vec3 outward_normal;
vec3 reflected = reflect(r_in.direction(), rec.normal);
float ni_over_nt;
attenuation = vec3(1.0, 1.0, 1.0);
vec3 refracted;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
float reflect_prob;
float cosine;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
if (dot(r_in.direction(), rec.normal) > 0) {
outward_normal = -rec.normal;
ni_over_nt = ref_idx;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
cosine = ref_idx * dot(r_in.direction(), rec.normal)
/ r_in.direction().length();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
else {
outward_normal = rec.normal;
ni_over_nt = 1.0 / ref_idx;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
cosine = -dot(r_in.direction(), rec.normal)
/ r_in.direction().length();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
reflect_prob = schlick(cosine, ref_idx);
}
else {
reflect_prob = 1.0;
}
if (random_double() < reflect_prob) {
scattered = ray(rec.p, reflected);
}
else {
scattered = ray(rec.p, refracted);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
return true;
}
float ref_idx;
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An interesting and easy trick with dielectric spheres is to note that if you use a negative radius,
the geometry is unaffected but the surface normal points inward, so it can be used as a bubble
to make a hollow glass sphere:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.3));
list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));
list[4] = new sphere(vec3(-1,0,-1), -0.45, new dielectric(1.5));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This gives:
![Image 10-3](../images/img-1-10-3.jpg)
Positionable Camera
====================================================================================================
Cameras, like dielectrics, are a pain to debug. So I always develop mine incrementally. First, let’s
allow an adjustable field of view (_fov_). This is the angle you see through the portal. Since our
image is not square, the fov is different horizontally and vertically. I always use vertical fov. I
also usually specify it in degrees and change to radians inside a constructor -- a matter of
personal taste.
I first keep the rays coming from the origin and heading to the $z = -1$ plane. We could make it the
$z = -2$ plane, or whatever, as long as we made $h$ a ratio to that distance. Here is our setup:
![Figure 11-1](../images/fig-1-11-1.jpg)
This implies $h = tan(\theta/2)$. Our camera now becomes:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef CAMERAH
#define CAMERAH
#include "ray.h"
class camera {
public:
camera(float vfov, float aspect) { // vfov is top to bottom in degrees
float theta = vfov*M_PI/180;
float half_height = tan(theta/2);
float half_width = aspect * half_height;
lower_left_corner = vec3(-half_width, -half_height, -1.0);
horizontal = vec3(2*half_width, 0.0, 0.0);
vertical = vec3(0.0, 2*half_height, 0.0);
origin = vec3(0.0, 0.0, 0.0);
}
ray get_ray(float u, float v) {
return ray(origin,
lower_left_corner + u*horizontal + v*vertical - origin);
}
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When calling it with camera `cam(90, float(nx)/float(ny))` and these spheres:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
float R = cos(M_PI/4);
list[0] = new sphere(vec3(-R,0,-1), R, new lambertian(vec3(0, 0, 1)));
list[1] = new sphere(vec3( R,0,-1), R, new lambertian(vec3(1, 0, 0)));
hittable *world = new hittable_list(list,2);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gives:
![Image 11-1](../images/img-1-11-1.jpg)
To get an arbitrary viewpoint, let’s first name the points we care about. We’ll call the position
where we place the camera _lookfrom_, and the point we look at _lookat_. (Later, if you want, you
could define a direction to look in instead of a point to look at.)
We also need a way to specify the roll, or sideways tilt, of the camera; the rotation around the
lookat-lookfrom axis.  What we need is a way to specify an up
vector for the camera. Notice we already we already have a plane that the up vector should be in,
the plane orthogonal to the view direction.
![Figure 11-2](../images/fig-1-11-2.jpg)
We can actually use any up vector we want, and simply project it onto this plane to get an up vector
for the camera. I use the common convention of naming a “view up” (_vup_) vector. A couple of cross
products, and we now have a complete orthonormal basis (u,v,w) to describe our camera’s orientation.
![Figure 11-3](../images/fig-1-11-3.jpg)
Remember that `vup`, `v`, and `w` are all in the same plane. Note that, like before when our fixed
camera faced -Z, our arbitrary view camera faces -w. And keep in mind that we can -- but we don’t
have to -- use world up (0,1,0) to specify vup. This is convenient and will naturally keep your
camera horizontally level until you decide to experiment with crazy camera angles.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef CAMERAH
#define CAMERAH
#include "ray.h"
class camera {
public:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect) {
// vfov is top to bottom in degrees
vec3 u, v, w;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
float theta = vfov*M_PI/180;
float half_height = tan(theta/2);
float half_width = aspect * half_height;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
origin = lookfrom;
w = unit_vector(lookfrom - lookat);
u = unit_vector(cross(vup, w));
v = cross(w, u);
lower_left_corner = origin - half_width*u - half_height*v - w;
horizontal = 2*half_width*u;
vertical = 2*half_height*v;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
ray get_ray(float s, float t) {
return ray(origin,
lower_left_corner + s*horizontal + t*vertical - origin);
}
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This allows us to change the viewpoint:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
camera cam(vec3(-2,2,1), vec3(0,0,-1), vec3(0,1,0), 90, float(nx)/float(ny));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
to get:
![Image 11-2](../images/img-1-11-2.jpg)
And we can change field of view to get:
![Image 11-3](../images/img-1-11-3.jpg)
Defocus Blur
====================================================================================================
Now our final feature: defocus blur. Note, all photographers will call it “depth of field” so be
aware of only using “defocus blur” among friends.
The reason we defocus blur in real cameras is because they need a big hole (rather than just a
pinhole) to gather light. This would defocus everything, but if we stick a lens in the hole, there
will be a certain distance where everything is in focus. The distance to that plane where things are
in focus is controlled by the distance between the lens and the film/sensor. That is why you see the
lens move relative to the camera when you change what is in focus (that may happen in your phone
camera too, but the sensor moves). The “aperture” is a hole to control how big the lens is
effectively. For a real camera, if you need more light you make the aperture bigger, and will get
more defocus blur. For our virtual camera, we can have a perfect sensor and never need more light,
so we only have an aperture when we want defocus blur.
A real camera has a compound lens that is complicated. For our code we could simulate the order:
sensor, then lens, then aperture, and figure out where to send the rays and flip the image once
computed (the image is projected upside down on the film). Graphics people usually use a thin lens
approximation.
![Figure 12-1](../images/fig-1-12-1.jpg)
We also don’t need to simulate any of the inside of the camera. For the purposes of rendering an
image outside the camera, that would be unnecessary complexity. Instead I usually start rays from
the surface of the lens, and send them toward a virtual film plane, by finding the projection of the
film on the plane that is in focus (at the distance `focus_dist`).
![Figure 12-2](../images/fig-1-12-2.jpg)
For that we just need to have the ray origins be on a disk around `lookfrom` rather than from a
point:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef CAMERAH
#define CAMERAH
#include "random.h"
#include "ray.h"
vec3 random_in_unit_disk() {
vec3 p;
do {
p = 2.0*vec3(random_double(),random_double(),0) - vec3(1,1,0);
} while (dot(p,p) >= 1.0);
return p;
}
class camera {
public:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect,
float aperture, float focus_dist) {
lens_radius = aperture / 2;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
float theta = vfov*M_PI/180;
float half_height = tan(theta/2);
float half_width = aspect * half_height;
origin = lookfrom;
w = unit_vector(lookfrom - lookat);
u = unit_vector(cross(vup, w));
v = cross(w, u);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
lower_left_corner = origin
- half_width * focus_dist * u
- half_height * focus_dist * v
- focus_dist * w;
horizontal = 2*half_width*focus_dist*u;
vertical = 2*half_height*focus_dist*v;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
ray get_ray(float s, float t) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 rd = lens_radius*random_in_unit_disk();
vec3 offset = u * rd.x() + v * rd.y();
return ray(origin + offset,
lower_left_corner + s*horizontal + t*vertical
- origin - offset);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
vec3 u, v, w;
float lens_radius;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using a big aperture:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 lookfrom(3,3,2);
vec3 lookat(0,0,-1);
float dist_to_focus = (lookfrom-lookat).length();
float aperture = 2.0;
camera cam(lookfrom, lookat, vec3(0,1,0), 20,
float(nx)/float(ny), aperture, dist_to_focus);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We get:
![Image 12-1](../images/img-1-12-1.jpg)
Where Next?
====================================================================================================
First let’s make the image on the cover of this book -- lots of random spheres:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
hittable *random_scene() {
int n = 500;
hittable **list = new hittable*[n+1];
list[0] = new sphere(vec3(0,-1000,0), 1000, new lambertian(vec3(0.5, 0.5, 0.5)));
int i = 1;
for (int a = -11; a < 11; a++) {
for (int b = -11; b < 11; b++) {
float choose_mat = random_double();
vec3 center(a+0.9*random_double(),0.2,b+0.9*random_double());
if ((center-vec3(4,0.2,0)).length() > 0.9) {
if (choose_mat < 0.8) { // diffuse
list[i++] = new sphere(center, 0.2,
new lambertian(vec3(random_double()*random_double(),
random_double()*random_double(),
random_double()*random_double())
)
);
}
else if (choose_mat < 0.95) { // metal
list[i++] = new sphere(center, 0.2,
new metal(vec3(0.5*(1 + random_double()),
0.5*(1 + random_double()),
0.5*(1 + random_double())),
0.5*random_double()));
}
else { // glass
list[i++] = new sphere(center, 0.2, new dielectric(1.5));
}
}
}
}
list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5));
list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1)));
list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0));
return new hittable_list(list,i);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This gives:
![Image 13-1](../images/img-1-13-1.jpg)
An interesting thing you might note is the glass balls don’t really have shadows which makes them
look like they are floating. This is not a bug (you don’t see glass balls much in real life, where
they also look a bit strange and indeed seem to float on cloudy days). A point on the big sphere
under a glass ball still has lots of light hitting it because the sky is re-ordered rather than
blocked.
You now have a cool ray tracer! What next?
1. Lights. You can do this explicitly, by sending shadow rays to lights. Or it can be done
implicitly by making some objects emit light,
2. Biasing scattered rays toward them, and then downweighting those rays to cancel out the bias.
Both work. I am in the minority in favoring the latter approach.
3. Triangles. Most cool models are in triangle form. The model I/O is the worst and almost
everybody tries to get somebody else’s code to do this.
4. Surface textures.  Pretty easy and a good thing
to do.
5. Solid textures. Ken Perlin has his code online. Andrew Kensler has some very cool info at his
blog.
6. Volumes and media. Cool stuff and will challenge your software architecture. I favor making
volumes have the hittable interface and probabilistically have intersections based on density.
Your rendering code doesn’t even have to know it has volumes with that method.
7. Parallelism. Run $N$ copies of your code on $N$ cores with different random seeds. Average the
$N$ runs. This averaging can also be done hierarchically where $N/2$ pairs can be averaged to
get $N/4$ images, and pairs of those can be averaged. That method of parallelism should extend
well into the thousands of cores with very little coding.
Have fun, and please send me your cool images!
Acknowledgements
====================================================================================================
Thanks to readers Apoorva Joshi, Jason Stone, Becker, Matthew Heimlich, Frank He, Benjamin
Summerton, Marcus Ottosson and Lorenzo Mancini for finding bugs, and to the [limnu.com][] team with
help on the figures. Thanks to Fabio San for pull requests.
[limnu.com]: https://limnu.com/


In OsmAnd, you can add places to Favorites to save their location and make navigation even handier. To add a place to favorites, just make a long tap on the map, then choose Add to favorites (Star icon) or GPX Waypoint. After that, you'll be able to find it in your Favorites list in seconds.
Favorites and GPX Waypoint: when you edit or create a new point you can choose colors, icons, and shapes for it. You can create groups of Favorites by giving them a certain name in the Category submenu as you add them. Adding a name and a description to a new Favorite location is possible, as well.
To view all your favorites on the map, just go to the dashboard, and tap Show all on the Favorites menu, then tap option button and choose Show on map. You can also choose the color of the group. This works just fine with separate groups of favorites.
The other way to view your favorites is just go to Configure map - Show - Favorites, remember about individual settings for each navigation profile. You'll see all your favorites on the map.
You can easily choose the favorite points to be your waypoints in a trip: just use the waypoint button (Actions-> Add GXP waypoint) and add those you need to the trip. Tap the button once again.
To delete a favorite or group of favorites, just hit the trashbin button, then check them and press delete in the Favorites list.
In OsmAnd for iOS, you can add places to Favorites to save their location and make navigation even handier. To add a place to favorites, just make a long tap on the map, then choose Add favorite. After that, you'll be able to find it in your Favorites list in seconds. You can create groups of Favorites by giving them a certain name in the Category submenu as you add them. Adding a name, color and a description to a new Favorite location is possible, as well.
To view all your favorites on the map go to menu Map - Show on Map - Favorite - Turn On. You'll see all your favorites on the map.
You can export favorite points as files from your device and share it with another device. To export/import favorite, go to menu My Places > Favorites > Import/Export.
To add favorite as your waypoint, go to menu My Places > Favorite > Select needed point > Press the Actions-> Add waypoint.
To create a trip with favorite points, enter the My Places menu > Trips, choose Create new trip and then add favorite spots and description. Then hit Save. You'll be able to view your saved trip in My places menu > Trips.


About such a beautiful, rich and unrealistically cool person like me.
I love beer, hate twitter , prediction in "The Simpsons", win retirees in various games and building walls. I am a very simple man. I am only the richest, sexiest, smartest president in America's history. In 2016, I simply destroyed a crazy grandmother Hillary Clinton, and then did everything to make the number 45 the most favorite number of Americans. But after that the pensioners became very angry with me, so I succumbed to one of them. His name is an old stinking fart or Joe Biden.


Select this option to enable smart handling of
LF
and CRLF
line separators. IntelliJ
IDEA will analyze your configuration, warn you if you are about to commit CRLF into the repository, and suggest
changing the core.autocrlf
setting to true
or input
depending on your
operating system.
(This setting is not applied to files where you have set any related Git attributes. In this case, IntelliJ IDEA
assumes that you clearly understand what you are doing and excludes such files from analysis.)
If this option is deselected, you will have to fix issues with line endings manually using the Difference Viewer dialog.


ZeeWebist
Home
About
My Work
Contact
My Work
Check out some of my projects
Design
GREAT GRADIENTS
UI/UX
WORLD EXPERIENCE
Photography & Design
VANISHING
Design
GAP INC
Mobile UI Design
FACE THE EXPERIENCE
Design Concepts
BEZIER CURVES
Photography
DOCK PONDER
Web Applications
RESTAURANT APP
Social Network Concept
FRIENDFEED


Welcome to the Okuna.js documentation page! Okuna.js is a JavaScript library that allows you to interact with the Okuna API. It can be used both in the browser and in Node.js.
The first thing you need to do is to install the package. To do this, you can either run
npm i okuna
or, if you prefer Yarn:
yarn add okuna
Depending on your setup, you want to use one of these:
CommonJS:
const { Client } = require('okuna');
const client = new Client();
const health = await client.health().getHealth();
ES6 imports:
import { Client } from 'okuna';
const client = new Client();
const health = await client.health().getHealth();
The process is nearly identical to the one of Node.js. Install the library using the steps described in the Node.js setup and use the ES6 imports method for working with the library.
Import the bundled and minified script from the JSDelivr CDN:
<script<EMAIL_ADDRESS>This will register the Okuna
variable in your website's DOM. You can use the library like this:
const { Client } = Okuna;
// or const client = new Okuna.Client();
const client = new Client();
const health = await client.health().getHealth();
// Note: older browsers don't support async/await.
Note: The okuna.min.js
file has additional dependencies bundled in. These dependencies include Axios, isomorphic-fetch, and isomorphic-form-data to ensure that the library works. You may or may not have one or more of these libraries loaded in your web page already. Unless you know what you doing, you should use a bundler to reduce the size of your assets, have a better development experience, and remove potential conflicts.
This web page contains a documentation on how exactly the API of the library works and how you should use it in your projects. You should start by reading more about the Okuna.js Client class.
It is very important to know how to work with file uploads when using the library. File uploads are done using the multipart/form-data
header, so we use FormData to create a payload to be sent to the API server.
When passing a file as a parameter, it should always have these two parameters:
file
- A blob or file input objectname
- The name of the fileExample (Node.js):
const fs = require('fs');
const { Client } = require('okuna');
// ... client setup
const blob = fs.readFileSync('/path/to/file');
client.posts().createPost({
text: 'Here is an image!',
image: {
file: blob,
name: 'filename_with.extension' // if you know the path, you should know the name too (you can use path.basename() here)
}
})
.then(res => console.log(res))
.catch(err => console.error(err));
Example (file inputs):
// ... client setup
const fileInput = document.querySelector("input[name=image]");
client.posts().createPost({
text: 'Here is an image!',
image: {
file: fileInput.files[0], // you'd probably store fileInput.files[0] in a separate variable
name: fileInput.files[0].name
}
})
.then(/* ... */)
.catch(/* ... */);
Feel free to open an issue in the library's Github repository.
Generated using TypeDoc