-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathtiny_bvh_fenster.cpp
232 lines (207 loc) · 8.4 KB
/
tiny_bvh_fenster.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#define FENSTER_APP_IMPLEMENTATION
#define SCRWIDTH 800
#define SCRHEIGHT 600
#include "external/fenster.h" // https://github.com/zserge/fenster
//#define COLOR_PRIM // compute color as hashed triangle Index
//#define COLOR_DEPTH // compute color as depth of intersection
#define LOADSCENE
#define TINYBVH_IMPLEMENTATION
#include "tiny_bvh.h"
#include <fstream>
using namespace tinybvh;
BVH bvh;
int frameIdx = 0;
Ray* rays = 0;
#ifdef COLOR_DEPTH
int* depths = 0;
#endif
#ifdef LOADSCENE
bvhvec4* vertices = 0;
uint32_t* indices = 0;
const char scene[] = "cryteksponza.bin";
#else
ALIGNED( 16 ) bvhvec4 vertices[259 /* level 3 */ * 6 * 2 * 49 * 3]{};
ALIGNED( 16 ) uint32_t indices[259 /* level 3 */ * 6 * 2 * 49 * 3]{};
#endif
int verts = 0, inds = 0;
// setup view pyramid for a pinhole camera:
// eye, p1 (top-left), p2 (top-right) and p3 (bottom-left)
#ifdef LOADSCENE
static bvhvec3 eye( -15.24f, 21.5f, 2.54f ), p1, p2, p3;
static bvhvec3 view = tinybvh_normalize( bvhvec3( 0.826f, -0.438f, -0.356f ) );
#else
static bvhvec3 eye( -3.5f, -1.5f, -6.5f ), p1, p2, p3;
static bvhvec3 view = tinybvh_normalize( bvhvec3( 3, 1.5f, 5 ) );
#endif
void sphere_flake( float x, float y, float z, float s, int d = 0 )
{
// procedural tesselated sphere flake object
#define P(F,a,b,c) p[i+F*64]={(float)a ,(float)b,(float)c}
bvhvec3 p[384], pos( x, y, z ), ofs( 3.5 );
for (int i = 0, u = 0; u < 8; u++) for (int v = 0; v < 8; v++, i++)
P( 0, u, v, 0 ), P( 1, u, 0, v ), P( 2, 0, u, v ),
P( 3, u, v, 7 ), P( 4, u, 7, v ), P( 5, 7, u, v );
for (int i = 0; i < 384; i++) p[i] = tinybvh_normalize( p[i] - ofs ) * s + pos;
for (int i = 0, side = 0; side < 6; side++, i += 8)
for (int u = 0; u < 7; u++, i++) for (int v = 0; v < 7; v++, i++)
vertices[verts++] = p[i], vertices[verts++] = p[i + 8],
vertices[verts++] = p[i + 1], vertices[verts++] = p[i + 1],
vertices[verts++] = p[i + 9], vertices[verts++] = p[i + 8];
if (d < 3) sphere_flake( x + s * 1.55f, y, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake( x - s * 1.5f, y, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake( x, y + s * 1.5f, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake( x, x - s * 1.5f, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake( x, y, z + s * 1.5f, s * 0.5f, d + 1 );
if (d < 3) sphere_flake( x, y, z - s * 1.5f, s * 0.5f, d + 1 );
}
void sphere_flake_indexed( float x, float y, float z, float s, int d = 0 )
{
// procedural tesselated sphere flake object
#define P(F,a,b,c) p[i+F*64]={(float)a ,(float)b,(float)c}
bvhvec3 p[384], pos( x, y, z ), ofs( 3.5 );
for (int i = 0, u = 0; u < 8; u++) for (int v = 0; v < 8; v++, i++)
P( 0, u, v, 0 ), P( 1, u, 0, v ), P( 2, 0, u, v ),
P( 3, u, v, 7 ), P( 4, u, 7, v ), P( 5, 7, u, v );
for (int i = 0; i < 384; i++)
p[i] = vertices[verts + i] = tinybvh_normalize( p[i] - ofs ) * s + pos;
for (int i = verts, side = 0; side < 6; side++, i += 8, verts += 64)
for (int u = 0; u < 7; u++, i++) for (int v = 0; v < 7; v++, i++)
indices[inds++] = i, indices[inds++] = i + 8,
indices[inds++] = i + 1, indices[inds++] = i + 1,
indices[inds++] = i + 9, indices[inds++] = i + 8;
if (d < 3) sphere_flake_indexed( x + s * 1.55f, y, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake_indexed( x - s * 1.5f, y, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake_indexed( x, y + s * 1.5f, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake_indexed( x, x - s * 1.5f, z, s * 0.5f, d + 1 );
if (d < 3) sphere_flake_indexed( x, y, z + s * 1.5f, s * 0.5f, d + 1 );
if (d < 3) sphere_flake_indexed( x, y, z - s * 1.5f, s * 0.5f, d + 1 );
}
void Init()
{
#ifdef LOADSCENE
// load raw vertex data for Crytek's Sponza
std::string filename{ "./testdata/" };
filename += scene;
std::fstream s{ filename, s.binary | s.in };
s.seekp( 0 );
s.read( (char*)&verts, 4 );
printf( "Loading triangle data (%i tris).\n", verts );
verts *= 3, vertices = (bvhvec4*)malloc64( verts * 16 );
s.read( (char*)vertices, verts * 16 );
s.close();
#else
// generate a sphere flake scene
// sphere_flake( 0, 0, 0, 1.5f ); // regular sphere flake
sphere_flake_indexed( 0, 0, 0, 1.5f ); // indexed sphere flake
#endif
// build a BVH over the scene
if (inds > 0)
bvh.Build( vertices, indices, inds / 3 );
else
bvh.Build( vertices, verts / 3 );
// allocate buffers
rays = (Ray*)tinybvh::malloc64( SCRWIDTH * SCRHEIGHT * 16 * sizeof( Ray ) );
#ifdef COLOR_DEPTH
depths = (int*)tinybvh::malloc64( SCRWIDTH * SCRHEIGHT * sizeof( int ) );
#endif
// load camera position / direction from file
std::fstream t = std::fstream{ "camera.bin", t.binary | t.in };
if (!t.is_open()) return;
t.seekp( 0 );
t.read( (char*)&eye, sizeof( eye ) );
t.read( (char*)&view, sizeof( view ) );
t.close();
}
bool UpdateCamera( float delta_time_s, fenster& f )
{
bvhvec3 right = tinybvh_normalize( tinybvh_cross( bvhvec3( 0, 1, 0 ), view ) );
bvhvec3 up = 0.8f * tinybvh_cross( view, right );
// get camera controls.
bool moved = false;
if (f.keys['A']) eye += right * -1.0f * delta_time_s * 10, moved = true;
if (f.keys['D']) eye += right * delta_time_s * 10, moved = true;
if (f.keys['W']) eye += view * delta_time_s * 10, moved = true;
if (f.keys['S']) eye += view * -1.0f * delta_time_s * 10, moved = true;
if (f.keys['R']) eye += up * delta_time_s * 10, moved = true;
if (f.keys['F']) eye += up * -1.0f * delta_time_s * 10, moved = true;
if (f.keys[20]) view = tinybvh_normalize( view + right * -1.0f * delta_time_s ), moved = true;
if (f.keys[19]) view = tinybvh_normalize( view + right * delta_time_s ), moved = true;
if (f.keys[17]) view = tinybvh_normalize( view + up * -1.0f * delta_time_s ), moved = true;
if (f.keys[18]) view = tinybvh_normalize( view + up * delta_time_s ), moved = true;
// recalculate right, up
right = tinybvh_normalize( tinybvh_cross( bvhvec3( 0, 1, 0 ), view ) );
up = 0.8f * tinybvh_cross( view, right );
bvhvec3 C = eye + 2 * view;
p1 = C - right + up, p2 = C + right + up, p3 = C - right - up;
return moved;
}
void Tick( float delta_time_s, fenster& f, uint32_t* buf )
{
// handle user input and update camera
bool moved = UpdateCamera( delta_time_s, f ) || frameIdx++ == 0;
// handle user input and update camera
UpdateCamera( delta_time_s, f );
// clear the screen with a debug-friendly color
for (int i = 0; i < SCRWIDTH * SCRHEIGHT; i++) buf[i] = 0xff00ff;
// generate primary rays in a cacheline-aligned buffer - and, for data locality:
// organized in 4x4 pixel tiles, 16 samples per pixel, so 256 rays per tile.
int N = 0;
for (int ty = 0; ty < SCRHEIGHT; ty += 4) for (int tx = 0; tx < SCRWIDTH; tx += 4)
{
for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++)
{
float u = (float)(tx + x) / SCRWIDTH, v = (float)(ty + y) / SCRHEIGHT;
bvhvec3 D = tinybvh_normalize( p1 + u * (p2 - p1) + v * (p3 - p1) - eye );
rays[N++] = Ray( eye, D, 1e30f );
}
}
// trace primary rays
for (int i = 0; i < N; i++) {
#ifdef COLOR_DEPTH
depths[i] = bvh.Intersect( rays[i] );
#else
bvh.Intersect( rays[i] );
#endif
}
// visualize result
const bvhvec3 L = tinybvh_normalize( bvhvec3( 1, 2, 3 ) );
for (int i = 0, ty = 0; ty < SCRHEIGHT / 4; ty++) for (int tx = 0; tx < SCRWIDTH / 4; tx++)
{
for (int y = 0; y < 4; y++) for (int x = 0; x < 4; x++, i++) if (rays[i].hit.t < 10000)
{
int pixel_x = tx * 4 + x, pixel_y = ty * 4 + y, primIdx = rays[i].hit.prim;
#ifdef COLOR_DEPTH
buf[pixel_x + pixel_y * SCRWIDTH] = depths[i] << 17; // render depth as red
#elif defined COLOR_PRIM
buf[pixel_x + pixel_y * SCRWIDTH] = (primIdx * 0xdeece66d + 0xb) & 0xFFFFFF; // color is hashed primitive index
#else
int v0idx = primIdx * 3, v1idx = v0idx + 1, v2idx = v0idx + 2;
if (inds) v0idx = indices[v0idx], v1idx = indices[v1idx], v2idx = indices[v2idx];
bvhvec3 v0 = vertices[v0idx];
bvhvec3 v1 = vertices[v1idx];
bvhvec3 v2 = vertices[v2idx];
bvhvec3 N = tinybvh_normalize( tinybvh_cross( v1 - v0, v2 - v0 ) );
int c = (int)(255.9f * fabs( tinybvh_dot( N, L ) ));
buf[pixel_x + pixel_y * SCRWIDTH] = c + (c << 8) + (c << 16);
#endif
}
}
// print frame time / rate in window title
char title[50];
sprintf( title, "tiny_bvh %.2f s %.2f Hz", delta_time_s, 1.0f / delta_time_s );
fenster_update_title( &f, title );
}
void Shutdown()
{
// save camera position / direction to file
std::fstream s = std::fstream{ "camera.bin", s.binary | s.out };
s.seekp( 0 );
s.write( (char*)&eye, sizeof( eye ) );
s.write( (char*)&view, sizeof( view ) );
s.close();
// delete allocated buffers
tinybvh::free64( rays );
#ifdef COLOR_DEPTH
tinybvh::free64( depths );
#endif
}