Skip to content

Commit 4e982d8

Browse files
committed
added init files with sample mirror balls
1 parent 084976d commit 4e982d8

File tree

2 files changed

+258
-0
lines changed

2 files changed

+258
-0
lines changed

main.cpp

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#include <cmath>
2+
#include <algorithm>
3+
#include <iostream>
4+
#include <memory>
5+
#include <numeric>
6+
7+
#include <QImage>
8+
#include <QColorSpace>
9+
#include <QVector>
10+
#include <QVector3D>
11+
12+
13+
namespace {
14+
15+
using Color = QVector3D;
16+
class Light
17+
{
18+
public:
19+
virtual float power(
20+
const QVector3D &origin,
21+
const QVector3D &normalDirection) const = 0;
22+
Color color = Color(1, 1, 1);
23+
QVector3D center;
24+
};
25+
26+
class Bulb : public Light
27+
{
28+
public:
29+
Bulb(const QVector3D &center, const Color &color)
30+
{
31+
this->center = center;
32+
this->color = color;
33+
}
34+
float power(
35+
const QVector3D &origin,
36+
const QVector3D &normalDirection) const override
37+
{
38+
const QVector3D v1 = (origin - center).normalized();
39+
const QVector3D v2 = normalDirection;
40+
const float rad = std::acos(QVector3D::dotProduct(v1, v2));
41+
if (std::abs(rad) > M_PI_2)
42+
return 0;
43+
return 1 - std::abs(rad) / M_PI_2;
44+
}
45+
};
46+
47+
class Shape
48+
{
49+
public:
50+
virtual ~Shape() = default;
51+
virtual bool intersects(
52+
const QVector3D &origin,
53+
const QVector3D &direction,
54+
QVector3D *intersectionOrigin,
55+
QVector3D *normalDirection,
56+
QVector3D *reflectionDirection) const = 0;
57+
Color color = Color(1, 0, 0);
58+
float mirror = 0.0f;
59+
};
60+
61+
class Sphere : public Shape
62+
{
63+
public:
64+
Sphere(const QVector3D &center, const float &radius, const Color &color, const float mirror)
65+
: center_(center), radius_(radius) { this->color = color; this->mirror = mirror; }
66+
private:
67+
QVector3D center_;
68+
float radius_ = 0.0f;
69+
bool intersects(
70+
const QVector3D &origin,
71+
const QVector3D &direction,
72+
QVector3D *intersectionOrigin,
73+
QVector3D *normalDirection,
74+
QVector3D *reflectionDirection) const override
75+
{
76+
const QVector3D m = origin - center_;
77+
const float b = QVector3D::dotProduct(direction, m);
78+
const float c = QVector3D::dotProduct(m, m) - radius_ * radius_;
79+
if (c > 0.0f && b > 0.0f)
80+
return false;
81+
82+
const float discr = b * b - c;
83+
if (discr < 0.0f)
84+
return false;
85+
86+
// distance? // aka starts inside the sphere
87+
const float t = std::max(0.0f, -b - std::sqrt(discr));
88+
const QVector3D intersection = origin + direction * t;
89+
const QVector3D normal = (intersection - center_).normalized();
90+
const QVector3D reflection = direction - 2 * normal * QVector3D::dotProduct(direction, normal);
91+
if (intersectionOrigin)
92+
*intersectionOrigin = intersection;
93+
if (normalDirection)
94+
*normalDirection = normal;
95+
if (reflectionDirection)
96+
*reflectionDirection = reflection;
97+
return true;
98+
}
99+
};
100+
101+
template <typename T>
102+
QVector<T> allBut(const QVector<T> &vector, const int index)
103+
{
104+
auto res = vector;
105+
res.removeAt(index);
106+
return res;
107+
}
108+
109+
Color cast(
110+
const QVector<std::shared_ptr<Shape>> &shapes,
111+
const QVector<std::shared_ptr<Light>> &lights,
112+
const QVector3D &origin,
113+
const QVector3D &direction,
114+
const Color &colorOnMiss,
115+
const Color &colorOnFullShade)
116+
{
117+
const int nShapes = shapes.size();
118+
float shortestDistanceSquared = std::numeric_limits<float>::max();
119+
int shapeIndex = -1;
120+
QVector3D intersectionOrigin;
121+
QVector3D normalDirection;
122+
QVector3D reflectionDirection;
123+
for (int index = 0; index < nShapes; ++index) {
124+
const auto &shape = shapes.at(index);
125+
QVector3D intersection;
126+
QVector3D normal;
127+
QVector3D reflection;
128+
if (!shape->intersects(
129+
origin,
130+
direction,
131+
&intersection,
132+
&normal,
133+
&reflection))
134+
continue;
135+
const float distanceSquared = (intersection - origin).lengthSquared();
136+
if (distanceSquared >= shortestDistanceSquared)
137+
continue;
138+
shortestDistanceSquared = distanceSquared;
139+
intersectionOrigin = intersection;
140+
normalDirection = normal;
141+
reflectionDirection = reflection;
142+
shapeIndex = index;
143+
}
144+
if (shapeIndex < 0)
145+
return colorOnMiss;
146+
147+
const auto &shape = shapes.at(shapeIndex);
148+
const auto otherShapes = allBut(shapes, shapeIndex);
149+
Color colorSelf = shape->color;
150+
if (shape->mirror > 0.0f) {
151+
const Color colorMirrored = cast(
152+
otherShapes,
153+
lights,
154+
intersectionOrigin,
155+
reflectionDirection,
156+
colorOnMiss,
157+
colorOnFullShade);
158+
colorSelf = colorSelf * (1 - shape->mirror) + colorMirrored * shape->mirror;
159+
}
160+
Color colorMask = colorOnFullShade;
161+
for (const auto &light : lights) {
162+
bool isBlocked = false;
163+
for (const auto &otherShape : otherShapes) {
164+
if (!otherShape->intersects(
165+
intersectionOrigin,
166+
(intersectionOrigin - light->center).normalized(),
167+
nullptr,
168+
nullptr,
169+
nullptr))
170+
continue;
171+
isBlocked = true;
172+
break;
173+
}
174+
if (isBlocked)
175+
continue;
176+
const float power = light->power(
177+
intersectionOrigin,
178+
normalDirection);
179+
if (power <= 0.0f)
180+
continue;
181+
colorMask += power * light->color;
182+
}
183+
return colorSelf * colorMask;
184+
}
185+
186+
inline int toRgbComponent(const float value)
187+
{
188+
return std::clamp(static_cast<int>(value * 255), 0, 255);
189+
}
190+
191+
QRgb toRgb(const Color &color)
192+
{
193+
return qRgb(toRgbComponent(color.x()), toRgbComponent(color.y()), toRgbComponent(color.z()));
194+
}
195+
196+
}
197+
198+
int main(int, char **)
199+
{
200+
using std::cout;
201+
202+
const QVector<std::shared_ptr<Shape>> shapes = {
203+
std::shared_ptr<Shape>(new Sphere(QVector3D(0, 0, 0), 5, Color(1.0, 0.5, 0.5), 0.9)),
204+
std::shared_ptr<Shape>(new Sphere(QVector3D(0, -12, 0), 4, Color(0.5, 1.0, 0.5), 0.9)),
205+
std::shared_ptr<Shape>(new Sphere(QVector3D(5, 8, 7), 3, Color(1, 1, 1), 0.5)),
206+
std::shared_ptr<Shape>(new Sphere(QVector3D(7, 5, 5), 2, Color(0.5, 0.5, 1.0), 0)),
207+
std::shared_ptr<Shape>(new Sphere(QVector3D(12, 4, 5), 1, Color(0.5, 0.5, 0.2), 0)),
208+
std::shared_ptr<Shape>(new Sphere(QVector3D(-100, 0, -50), 100, Color(0.5, 0.5, 0.5), 0.4)),
209+
std::shared_ptr<Shape>(new Sphere(QVector3D(-100, 0, 50), 100, Color(1, 1, 1), 0.4)),
210+
};
211+
const QVector<std::shared_ptr<Light>> lights = {
212+
std::shared_ptr<Light>(new Bulb(QVector3D(-20, -10, 20), Color(1, 1, 1) * 0.7)),
213+
std::shared_ptr<Light>(new Bulb(QVector3D(-20, -12, 22), Color(1, 1, 1) * 0.7)),
214+
};
215+
216+
const float cameraSize = 30.0f;
217+
const QVector3D cameraOrigin(100, 0, 0);
218+
const QVector3D cameraDirection(-1, 0, 0);
219+
const int resolutionPrefered = 512;
220+
const int msaaMultiplier = 2;
221+
const Color colorOnMiss(0, 0, 1);
222+
const Color colorOnFullShade(0.1, 0.1, 0.1);
223+
224+
QVector<int> resolutionsDownscaled;
225+
for (int resolutionDownscaled = resolutionPrefered * msaaMultiplier; resolutionDownscaled > 16; resolutionDownscaled /= 2)
226+
resolutionsDownscaled.push_front(resolutionDownscaled);
227+
228+
for (int resolution : resolutionsDownscaled){
229+
QImage image(resolution, resolution, QImage::Format_RGB888);
230+
image.setColorSpace(QColorSpace::SRgbLinear);
231+
const float delimeter = image.width() / (cameraSize);
232+
for (int y = 0; y < image.height(); ++y){
233+
for (int x = 0; x < image.width(); ++x) {
234+
const QVector3D origin(
235+
cameraOrigin.x(),
236+
cameraOrigin.y() - cameraSize * 0.5 + x / delimeter,
237+
cameraOrigin.z() - cameraSize * 0.5 + y / delimeter);
238+
const Color color = cast(shapes, lights, origin, cameraDirection, colorOnMiss, colorOnFullShade);
239+
image.setPixel(x, y, toRgb(color));
240+
}
241+
}
242+
image = image.scaled(
243+
resolutionPrefered,
244+
resolutionPrefered,
245+
Qt::IgnoreAspectRatio,
246+
Qt::SmoothTransformation);
247+
if (!image.save("output.png")) {
248+
cout << "can't save output image\n";
249+
return 1;
250+
}
251+
}
252+
return 0;
253+
}

raytracer.pro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
QT += core
2+
CONFIG -= console
3+
CONFIG += c++17
4+
SOURCES += \
5+
main.cpp

0 commit comments

Comments
 (0)