How to understand this RayTracer code [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
So this RT code creates a 3D image, with blur, through raw code. How is that actually done without any modelling tools?
I am currently working to understand how RT work and different ways to implement them, so this was kind of cool to see such a small amount of code producing a pretty impressive 3D image.
#include <stdlib.h> // card > aek.ppm
#include <stdio.h>
#include <math.h>
#include <fstream>
typedef int i;
typedef float f;
struct v {
f x, y, z;
v operator+(v r) {
return v(x + r.x, y + r.y, z + r.z);
}
v operator*(f r) {
return v(x * r, y * r, z * r);
}
f operator%(v r) {
return x * r.x + y * r.y + z * r.z;
}
v() {}
v operator^(v r) {
return v(y * r.z - z * r.y, z * r.x - x * r.z, x * r.y - y * r.x);
}
v(f a, f b, f c) {x = a; y = b; z = c;}
v operator!() {
return*this * (1 / sqrt(*this % *this));
}
};
i G[] = {247570, 280596, 280600, 249748, 18578, 18577, 231184, 16, 16};
f R()
{
return(f)rand() / RAND_MAX;
}
i T(v o, v d, f&t, v&n)
{
t = 1e9; i m = 0;
f p = -o.z / d.z;
if(.01 < p)t = p, n = v(0, 0, 1), m = 1;
for(i k = 19; k--;)
for(i j = 9; j--;)if(G[j] & 1 << k) {
v p = o + v(-k, 0, -j - 4);
f b = p % d, c = p % p - 1, q = b * b - c;
if(q > 0) {
f s = -b - sqrt(q);
if(s < t && s > .01)
t = s, n = !(p + d * t), m = 2;
}
}
return m;
} v S(v o, v d)
{
f t;
v n;
i m = T(o, d, t, n);
if(!m)return v(.7, .6, 1) * pow(1 - d.z, 4);
v h = o + d * t, l = !(v(9 + R(), 9 + R(), 16) + h * -1), r = d + n * (n % d * -2);
f b = l % n; if(b < 0 || T(h, l, t, n))b = 0;
f p = pow(l % r * (b > 0), 99);
if(m & 1) {
h = h * .2;
return((i)(ceil(h.x) + ceil(h.y)) & 1 ? v(3, 1, 1) : v(3, 3, 3)) * (b * .2 + .1);
} return v(p, p, p) + S(h, r) * .5;
} i
main()
{
FILE * pFile;
pFile = fopen("d:\\myfile3.ppm", "w");
fprintf(pFile,"P6 512 512 255 ");
v g = !v(-6, -16, 0), a = !(v(0, 0, 1) ^ g) * .002, b = !(g ^ a) * .002, c = (a + b) * -256 + g;
for(i y = 512; y--;)
for(i x = 512; x--;) {
v p(13, 13, 13);
for(i r = 64; r--;) {
v t = a * (R() - .5) * 99 + b * (R() - .5) * 99;
p = S(v(17, 16, 8) + t, !(t * -1 + (a * (R() + x) + b * (y + R()) + c) * 16)) * 3.5 + p;
}
fprintf(pFile, "%c%c%c", (i)p.x, (i)p.y, (i)p.z);
}
}

My dear friend that's Paul Heckbert code's right?
You could at least mention it.
For people thinking this code is unreadable, here is why:
This guy made a code that could fit on a credit card, that was the goal :)
His website: http://www.cs.cmu.edu/~ph/
Edit: Knowing the source of this code may help you understand it. Even if it'snot your main motivation...
If you are really interested in raytracing, start with other sources.
Take a look at this website http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-1-writing-a-simple-raytracer/source-code/ (Plus it talk about your code)

This code is not really special. It is basically a ray tracer that was obfuscated into a form that makes it fit on a business card (see https://www.cs.cmu.edu/~ph/).
How is that actually done without any modelling tools?
You don't need tools to render anything. You could even create a complete game of WoW (or what's hip at the moment) without any modelling tool. Modelling tools just make your live easier w.r.t. certain kinds of scenes (read: very complex ones).
You could always hardcode these data, or hack them manually into some external file.
You could also use parametric generators; Perlin Noise is one of the more popular examples thereof.
In a ray tracer, it happens that it is very simple to start out without modelling tools, as it is very simple to calculate geometric intersections between the rendering primitive "ray" and any finite geometric primitive. E.g., intersection a non-approximated, "perfect" sphere is just a few lines of code.
tl;dr: Data is just data. How you create and crunch it is completely up to you.

Related

Intersection of ray and ellipsoid

Yes, I know that it is a popular problem. But I found nowhere the full clear implementing code without using OpenGL classes or a lot of headers files.
Okay, the math solution is to transfer ellipsoid to sphere. Then find intersections dots (if they exist of course) and make inverse transformation. Because affine transformation respect intersection.
But I have difficulties when trying to implement this.
I tried something for sphere but it is completely incorrect.
double CountDelta(Point X, Point Y, Sphere S)
{
double a = 0.0;
for(int i = 0; i < 3; i++){
a += (Y._coordinates[i] - X._coordinates[i]) * (Y._coordinates[i] - X._coordinates[i]);
}
double b = 0.0;
for(int i = 0; i < 3; i++)
b += (Y._coordinates[i] - X._coordinates[i]) * (X._coordinates[i] - S._coordinates[i]);
b *= 2;
double c = - S.r * S.r;
for(int i = 0; i < 3; i++)
c += (X._coordinates[i] - S._coordinates[i]) * (X._coordinates[i] - S._coordinates[i]);
return b * b - 4 * a * c;
}
Let I have start point P = (Px, Py, Pz), direction V = (Vx, Vy, Vz), ellipsoid = (Ex, Ey, Ec) and (a, b, c). How to construct clear code?
Let a line from P to P + D intersecting a sphere of center C and radius R.
WLOG, C can be the origin and R unit (otherwise translate by -C and scale by 1/R). Now using the parametric equation of the line and the implicit equation of the sphere,
(Px + t Dx)² + (Py + t Dy)² + (Pz + t Dz)² = 1
or
(Dx² + Dy² + Dz²) t² + 2 (Dx Px + Dy Py + Dz Pz) t + Px² + Py² + Pz² - 1 = 0
(Vectorially, D² t² + 2 D P t + P² - 1 = 0 and t = (- D P ±√((D P)² - D²(P² - 1))) / D².)
Solve this quadratic equation for t and get the two intersections as P + t D. (Don't forget to invert the initial transformations.)
For the ellipsoid, you can either plug the parametric equation of the line directly into the implicit equation of the conic, or reduce the conic (and the points simultaneously) and plug in the reduced equation.

How to get the calculate the RGB values of a pixel from the luminance?

I want to compute the RGB values from the luminance.
The data that I know are :
the new luminance (the value that I want to apply)
the old luminance
the old RGB values.
We can compute the luminance from the RGB values like this :
uint8_t luminance = R * 0.21 + G * 0.71 + B * 0.07;
My code is :
// We create a function to set the luminance of a pixel
void jpegImage::setLuminance(uint8_t newLuminance, unsigned int x, unsigned int y) {
// If the X or Y value is out of range, we throw an error
if(x >= width) {
throw std::runtime_error("Error : in jpegImage::setLuminance : The X value is out of range");
}
else if(y >= height) {
throw std::runtime_error("Error : in jpegImage::setLuminance : The Y value is out of range");
}
// If the image is monochrome
if(pixelSize == 1) {
// We set the pixel value to the luminance
pixels[y][x] = newLuminance;
}
// Else if the image is colored, we throw an error
else if(pixelSize == 3) {
// I don't know how to proceed
// My image is stored in a std::vector<std::vector<uint8_t>> pixels;
// This is a list that contain the lines of the image
// Each line contains the RGB values of the following pixels
// For example an image with 2 columns and 3 lines
// [[R, G, B, R, G, B], [R, G, B, R, G, B], [R, G, B, R, G, B]]
// For example, the R value with x = 23, y = 12 is:
// pixels[12][23 * pixelSize];
// For example, the B value with x = 23, y = 12 is:
// pixels[12][23 * pixelSize + 2];
// (If the image is colored, the pixelSize will be 3 (R, G and B)
// (If the image is monochrome the pixelSIze will be 1 (just the luminance value)
}
}
How can I proceed ?
Thanks !
You don't need the old luminance if you have the original RGB.
Referencing https://www.fourcc.org/fccyvrgb.php for YUV to RGB conversion.
Compute U and V from original RGB:
```
V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
```
Y is the new luminance normalized to a value between 0 and 255
Then just convert back to RGB:
B = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)
Make sure you clamp your computed values of each equation to be in range of 0..255. Some of these formulas can convert a YUV or RGB value to something less than 0 or higher than 255.
There's also multiple formula for converting between YUV and RGB. (Different constants). I noticed the page listed above has a different computation for Y than you cited. They are all relatively close with different precisions and adjustments. For just changing the brightness of a pixel, almost any formula will do.
Updated
I originally deleted this answer after the OP suggested it wasn't working for him. I was too busy for the last few days to investigate, but I wrote some sample code to confirm my hypothesis. At the bottom of this answer is a snippet of GDI+ based code increases the luminance of an image by a variable amount. Along with the code is an image that I tested this out on and two conversions. One at 130% brightness. Another at 170% brightness.
Here's a sample conversion
Original Image
Updated Image (at 130% Y)
Updated Image (at 170% Y)
Source:
#define CLAMP(val) {val = (val > 255) ? 255 : ((val < 0) ? 0 : val);}
void Brighten(Gdiplus::BitmapData& dataIn, Gdiplus::BitmapData& dataOut, const double YMultiplier=1.3)
{
if ( ((dataIn.PixelFormat != PixelFormat24bppRGB) && (dataIn.PixelFormat != PixelFormat32bppARGB)) ||
((dataOut.PixelFormat != PixelFormat24bppRGB) && (dataOut.PixelFormat != PixelFormat32bppARGB)))
{
return;
}
if ((dataIn.Width != dataOut.Width) || (dataIn.Height != dataOut.Height))
{
// images sizes aren't the same
return;
}
const size_t incrementIn = dataIn.PixelFormat == PixelFormat24bppRGB ? 3 : 4;
const size_t incrementOut = dataOut.PixelFormat == PixelFormat24bppRGB ? 3 : 4;
const size_t width = dataIn.Width;
const size_t height = dataIn.Height;
for (size_t y = 0; y < height; y++)
{
auto ptrRowIn = (BYTE*)(dataIn.Scan0) + (y * dataIn.Stride);
auto ptrRowOut = (BYTE*)(dataOut.Scan0) + (y * dataOut.Stride);
for (size_t x = 0; x < width; x++)
{
uint8_t B = ptrRowIn[0];
uint8_t G = ptrRowIn[1];
uint8_t R = ptrRowIn[2];
uint8_t A = (incrementIn == 3) ? 0xFF : ptrRowIn[3];
auto Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16;
auto V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128;
auto U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128;
Y *= YMultiplier;
auto newB = 1.164*(Y - 16) + 2.018*(U - 128);
auto newG = 1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128);
auto newR = 1.164*(Y - 16) + 1.596*(V - 128);
CLAMP(newR);
CLAMP(newG);
CLAMP(newB);
ptrRowOut[0] = newB;
ptrRowOut[1] = newG;
ptrRowOut[2] = newR;
if (incrementOut == 4)
{
ptrRowOut[3] = A; // keep original alpha
}
ptrRowIn += incrementIn;
ptrRowOut += incrementOut;
}
}
}

Difference between CBOW and Skipgram gradients in word2vec?

Why are f values that are greater than or lower than MAX_EXP taken into account during the updates in CBOW, but ignored in Skipgram?
I'm specifically looking at the Google implementation of word2vec, but the same functionality has been replicated throughout many other projects, one of which is here, for larger context.
// CBOW negative sampling gradient calculations
f = 0;
l2 = target * layer1_size;
for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1neg[c + l2];
// ** here, we still update, but essentially round the value to 1 or 0
if (f > MAX_EXP) g = (label - 1) * alpha;
else if (f < -MAX_EXP) g = (label - 0) * alpha;
else g = (label - expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))]) * alpha;
// ---------------------------
// Skipgram hierarchical softmax gradient calculations
f = 0;
l2 = vocab[word].point[d] * layer1_size;
for (c = 0; c < layer1_size; c++) f += syn0[c + l1] * syn1[c + l2];
// ** here, we don't update if f is outside the range given by MAX_EXP **
if (f <= -MAX_EXP) continue;
else if (f >= MAX_EXP) continue;
else f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];
g = (1 - vocab[word].code[d] - f) * alpha;

3D Line Segment and Plane Intersection - Contd

After advice from krlzlx I have posted it as a new question.
From here:
3D Line Segment and Plane Intersection
I have a problem with this algorithm, I have implemented it like so:
template <class T>
class AnyCollision {
public:
std::pair<bool, T> operator()(Point3d &ray, Point3d &rayOrigin, Point3d &normal, Point3d &coord) const {
// get d value
float d = (normal.x * coord.x) + (normal.y * coord.y) + (normal.z * coord.z);
if (((normal.x * ray.x) + (normal.y * ray.y) + (normal.z * ray.z)) == 0) {
return std::make_pair(false, T());
}
// Compute the X value for the directed line ray intersecting the plane
float a = (d - ((normal.x * rayOrigin.x) + (normal.y * rayOrigin.y) + (normal.z * rayOrigin.z)) / ((normal.x * ray.x) + (normal.y * ray.y) + (normal.z * ray.z)));
// output contact point
float rayMagnitude = (sqrt(pow(ray.x, 2) + pow(ray.y, 2) + pow(ray.z, 2)));
Point3d rayNormalised((ray.x / rayMagnitude), (ray.y / rayMagnitude), (ray.z / rayMagnitude));
Point3d contact((rayOrigin.x + (rayNormalised.x * a)), (rayOrigin.y + (rayNormalised.y * a)), (rayOrigin.z + (rayNormalised.z * a))); //Make sure the ray vector is normalized
return std::make_pair(true, contact);
};
Point3d is defined as:
class Point3d {
public:
double x;
double y;
double z;
/**
* constructor
*
* 0 all elements
*/
Point3d() {
x = 0.0;
y = 0.0;
z = 0.0;
}
I am forced to use this structure, because in the larger system my component runs in it is defined like this and it cannot be changed.
My code compiles fine, but testing I get incorrect values for the point. The ratio of x, y, z is correct but the magnitude is wrong.
For example if:
rayOrigin.x = 0;
rayOrigin.y = 0;
rayOrigin.z = 0;
ray.x = 3;
ray.y = -5;
ray.z = 12;
normal.x = -3;
normal.y = 12;
normal.z = 0;
coord.x = 7;
coord.y = -5;
coord.z = 10;
I expect the point to be:
(0.63, 1.26, 1.89)
However, it is:
(3.52, -5.87, 14.09)
A magnitude of 5.09 too big.
And I also tested:
rayOrigin.x = 0;
rayOrigin.y = 0;
rayOrigin.z = 0;
ray.x = 2;
ray.y = 3;
ray.z = 3;
normal.x = 4;
normal.y = 1;
normal.z = 0;
p0.x = 2;
p0.y = 1;
p0.z = 5;
I expect the point to be:
(1.64, 2.45, 2.45)
However, it is:
(3.83761, 5.75642, 5.75642)
A magnitude of 2.34 too big?
Pseudocode (does not require vector normalization):
Diff = PlaneBaseCoordinate - RayOrigin
d = Normal.dot.Diff
e = Normal.dot.RayVector
if (e)
IntersectionPoint = RayOrigin + RayVector * d / e
otherwise
ray belongs to the plane or is parallel
Quick check:
Ray (0,0,0) (2,2,2) //to catch possible scale issues
Plane (0,1,0) (0,3,0) //plane y=1
Diff = (0,1,0)
d = 3
e = 6
IntersectionPoint = (0,0,0) + (2,2,2) * 3 / 6 = (1, 1, 1)

Bilinear interpolation in 2D transformation Qt

I'm currently working on 2D transformations (translation, scaling, shearing and rotation) in Qt. I have a problem with bilinear interpolation, which I want to use to cover the 'black pixels' in output image. I'm using matrix calculations to get new coordinates of pixels of input image. Then I use reverse matrix calculation to check which pixel of input image responds to output pixel. Result of that is some float number which I use to interpolation. I check the four neighbour points and calculate the value (color) of output pixel. I have checked my calculations 'by hand' and they seem to be good.
Can anyone find any bug in that code? (I cut out the parts of code which are responsible for interface such as sliders).
Geometric::Geometric(QWidget* parent) : QWidget(parent) {
resize(1000, 800);
displayLogoDefault = true;
a = shx = shy = x0 = y0 = 0;
scx = scy = 1;
tx = ty = 0;
x = 200, y = 200;
paintT = paintSc = paintR = paintShx = paintShy = false;
img = new QImage(600,600,QImage::Format_RGB32);
img2 = new QImage("logo.jpeg");
}
Geometric::~Geometric() {
delete img;
delete img2;
img = NULL;
img2 = NULL;
}
void Geometric::makeChange() {
displayLogoDefault = false;
// iteration through whole input image
for(int i = 0; i < img2->width(); i++) {
for(int j = 0; j < img2->height(); j++) {
// calculate new coordinates basing on given 2D transformations values
//I calculated that formula eariler by multiplying/adding matrixes
x = cos(a)*scx*(i-x0) - sin(a)*scy*(j-y0) + shx*sin(a)*scx*(i-x0) + shx*cos(a)*scy*(j-y0);
y = shy*(x) + sin(a)*scx*(i-x0) + cos(a)*scy*(j-y0);
// tx and ty goes for translation. scx and scy for scaling
// shx and shy for shearing and a is angle for rotation
x += (x0 + tx);
y += (y0 + ty);
if(x >= 0 && y >= 0 && x < img->width() && y < img->height()) {
// reverse matrix calculation formula to find proper pixel from input image
float tmx = x - x0 - tx;
float tmy = y - y0 - ty;
float recX = 1/scx * ( cos(-a)*( (tmx + shx*shy*tmx - shx*tmx) ) + sin(-a)*( shy*tmx - tmy ) ) + x0 ;
float recY = 1/scy * ( sin(-a)*(tmx + shx*shy*tmx - shx*tmx) - cos(-a)*(shy*tmx-tmy) ) + y0;
// here the interpolation starts. I calculate the color basing on four points from input image
// that points are taken from the reverse matrix calculation
float a = recX - floorf(recX);
float b = recY - floorf (recY);
if(recX + 1 > img2->width()) recX -= 1;
if(recY + 1 > img2->height()) recY -= 1;
QColor c1 = QColor(img2->pixel(recX, recY));
QColor c2 = QColor(img2->pixel(recX + 1, recY));
QColor c3 = QColor(img2->pixel(recX , recY + 1));
QColor c4 = QColor(img2->pixel(recX + 1, recY + 1));
float colR = b * ((1.0 - a) * (float)c3.red() + a * (float)c4.red()) + (1.0 - b) * ((1.0 - a) * (float)c1.red() + a * (float)c2.red());
float colG = b * ((1.0 - a) * (float)c3.green() + a * (float)c4.green()) + (1.0 - b) * ((1.0 - a) * (float)c1.green() + a * (float)c2.green());
float colB = b * ((1.0 - a) * (float)c3.blue() + a * (float)c4.blue()) + (1.0 - b) * ((1.0 - a) * (float)c1.blue() + a * (float)c2.blue());
if(colR > 255) colR = 255; if(colG > 255) colG = 255; if(colB > 255) colB = 255;
if(colR < 0 ) colR = 0; if(colG < 0 ) colG = 0; if(colB < 0 ) colB = 0;
paintPixel(x, y, colR, colG, colB);
}
}
}
// x0 and y0 are the starting point of image
x0 = abs(x-tx);
y0 = abs(y-ty);
repaint();
}
// function painting a pixel. It works directly on memory
void Geometric::paintPixel(int i, int j, int r, int g, int b) {
unsigned char *ptr = img->bits();
ptr[4 * (img->width() * j + i)] = b;
ptr[4 * (img->width() * j + i) + 1] = g;
ptr[4 * (img->width() * j + i) + 2] = r;
}
void Geometric::paintEvent(QPaintEvent*) {
QPainter p(this);
p.drawImage(0, 0, *img);
if (displayLogoDefault == true) p.drawImage(0, 0, *img2);
}