Comments (6)
Thank you very much, I've got it!
from tinyraytracer.
Thank you so much @DamianCoventry for your help!
from tinyraytracer.
Hi KnewHow,
Regarding the call to material.setDiffuse(), the key feature to achieve is a checkerboard pattern. One of the tools ssloy used for the job is the bitwise AND (&) operator. Let's isolate that code here:
(int(.5 * hit.x + 1000) + int(.5 * hit.z)) & 1
When you AND any integer by 1 you're effectively asking "is this number odd or even?". This works because the least significant bit of an integer represents the value 0 or 1, and therefore also indicates an even or odd value respectively.
The next tool used is the ternary operator ?:
. Passing this operator the result of the bitwise AND allows the code to choose either of the two colours, based on whether the result is even or odd.
If we supply this formula with a series of values, say, 1,2,3,4,5,... etc, you can see we achieve the on/off pattern of a checkboard due to the nature of sequential numbers being even and odd.
This is why the hit.x and hit.z are being cast to integers and used as input to this formula -- we can guarantee that the numbers are a changing series due to knowing the application's core loop is walking across the rows and columns of the output image.
So why the +1000? Good question, I'm glad you asked. The gotcha here is the checkboard rectangle spans the 0 value on the x axis. In fact, ssloy has the code written to go from -10 on the x axis to +10 on the x axis. In terms of passing integers from -10 to +10 through the above formula, you arrive at a problem because there is no 0 value, 0 is the absence of a value. When traversing from -x to +x you go from -1, to +1 and obviously they're both odd numbers. So you ruin the nice the checkboard pattern when you cross the 0 coord.
The easiest way out is to shift the numbers away from the origin just for this formula, hence, ssloy adds 1000. Chances are high in this case we won't come anywhere near 0 on the x axis with such a large value.
So why doesn't he add +1000 to the z value too? You're asking some great questions. Because the checkerboard is on the -z axis. It doesn't span 0 like the x axis does. ssloy placed the checkboard at -10 to -30 on the z axis.
from tinyraytracer.
To your other question, let's consider this code:
int a = std::max(0, std::min(env.getWidth() -1, static_cast<int>((atan2(d.z, d.x) / (2 * M_PI) + .5)* env.getWidth())));
int b = std::max(0, std::min(env.getHeight() -1, static_cast<int>(acos(d.y) / M_PI * env.getHeight())));
return env.getPixelColor(a, b); // background color
In a nutshell, this is spherical environment mapping. ssloy has thrown in boundary checks for the image so that env.getPixelColor()
doesn't access an invalid pixel. Let's remove those and concentrate on the guts of it.
a = (int)(atan2(d.z, d.x) / (2 * M_PI) + .5) * env.getWidth()
b = (int)acos(d.y) / M_PI * env.getHeight()
Spherical environment mapping is all about converting a unit vector (that is, a vector whose length is 1) to UV texture coords. This is ideal for the case where your camera is inside a sphere and looking around. That's what ssloy is simulating in this tinyraytracer.
This works when you map an image to the sphere that has already been distorted beforehand. Either with an expensive digital camera, or modified afterwards using Photoshop. The image that ssloy has supplied is one such image. That is what's going on in the above formulae -- the code has made the assumption that the input data (the image) have already been distorted and now it's time to convert back to sphere.
To make the formulae more clear, let's rewrite them in terms of UV texture coords.
float texCoordU = atan2(d.z, d.x) / (2 * M_PI) + .5;
float texCoordV = acos(d.y) / M_PI;
int pixelX = (int)(texCoordU * (float)env.getWidth());
int pixelY = (int)(texCoordV * (float)env.getHeight());
Typically the values texCoordU and texCoordV are in the range (0,1) so that when we multiply them with the width and height of the image we're choosing a value within the bounds of the pixel data. The std::min/std::max handle this case above.
Let's look at the Y coord first as it's easier to explain. We know that d.y is a unit vector, therefore it's value will be in the range (-1,1). We want our tex coord to be in the range (0,1). We need a simple way to linearly map a value from the former range to the latter. The formula used here is
acos(d.y) / M_PI
This works because acos accepts values in the range (-1,1) and returns a value (θ) that is 0 ≤ θ ≤ π. This is why ssloy divides by PI afterwards -- it converts the return value into the range (0,1). Great! So we're done, if the value passed is 1 then we get a 0 back. If the value passed in is -1, we get PI back. Divide by PI and we've got a suitable value for a tex coord.
On to the other bit:
atan2(d.z, d.x) / (2 * M_PI) + .5
The idea is similar here. We need to produce a valid tex coord in the range (0,1). The trick is we have two input variables: x and z. This formula achieves the conversion using atan2(). The neat thing about atan2() is that it essentially tells you which way a vector is facing. If it helps, you could think of it as passing atan2 a 2D vector and it telling you which way it's facing from 0° up to 359°.
Of course atan2 actually uses radians, and instead of atan2 returning values in the range 0-359, it returns values in the −π < θ ≤ π range. That is why ssloy divides by 2*PI, because it converts the range of possible return values from -0.5 to +0.5. And that is why he adds 0.5 to the result -- it shifts the range of possible values nicely into (0,1). Once again, we have a nice tex coord.
Hopefully this helped you understand what is going on in the code :-)
from tinyraytracer.
afterwards
Your explanation is very professional! Now, I have completely understand! Thank you very much!
from tinyraytracer.
You're welcome. I'm glad you've got it now. Happy to have helped :-)
from tinyraytracer.
Related Issues (20)
- Visual Studio OpenMP 2.0 Support HOT 1
- Tinyraytracer in Golang HOT 1
- Wont compile in gitpod HOT 6
- Tinyraytracer in Python HOT 4
- How do you rotate the camera? HOT 1
- Raytracer in shadertoy HOT 1
- Rotating the background (envmap) image HOT 1
- It can't successfully compile HOT 1
- Why my mirror sphere not reflect sea
- Why the mirror don't reflect the sea? HOT 2
- I have add a quack!
- Can't get right render image. HOT 2
- Colored Light Sources
- Step 1 is broken HOT 5
- question on Step 6: shadows HOT 3
- Issues with shadows when rendering duck HOT 1
- Multithreading for performance HOT 3
- Error relating to pragma when running tinyraytracer.cpp HOT 1
- Can you please post the answers to the homework?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tinyraytracer.