/
model.cpp
347 lines (277 loc) · 13.4 KB
/
model.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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#include "glcommon.h"
#include "scene.h"
#include "image.h"
#include "tesselation.h"
#include <cstdio>
// OpenGL state for shading
struct ShadeState {
int gl_program_id = 0; // OpenGL program handle
int gl_vertex_shader_id = 0; // OpenGL vertex shader handle
int gl_fragment_shader_id = 0; // OpenGL fragment shader handle
map<image3f*,int> gl_texture_id;// OpenGL texture handles
};
// initialize the shaders
void init_shaders(ShadeState* state) {
// load shader code from files
auto vertex_shader_code = load_text_file("model_vertex.glsl");
auto fragment_shader_code = load_text_file("model_fragment.glsl");
auto vertex_shader_codes = (char *)vertex_shader_code.c_str();
auto fragment_shader_codes = (char *)fragment_shader_code.c_str();
// create shaders
state->gl_vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
state->gl_fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
// load shaders code onto the GPU
glShaderSource(state->gl_vertex_shader_id,1,(const char**)&vertex_shader_codes,nullptr);
glShaderSource(state->gl_fragment_shader_id,1,(const char**)&fragment_shader_codes,nullptr);
// compile shaders
glCompileShader(state->gl_vertex_shader_id);
glCompileShader(state->gl_fragment_shader_id);
// check if shaders are valid
error_if_glerror();
error_if_shader_not_valid(state->gl_vertex_shader_id);
error_if_shader_not_valid(state->gl_fragment_shader_id);
// create program
state->gl_program_id = glCreateProgram();
// attach shaders
glAttachShader(state->gl_program_id,state->gl_vertex_shader_id);
glAttachShader(state->gl_program_id,state->gl_fragment_shader_id);
// bind vertex attributes locations
glBindAttribLocation(state->gl_program_id, 0, "vertex_pos");
glBindAttribLocation(state->gl_program_id, 1, "vertex_norm");
glBindAttribLocation(state->gl_program_id, 2, "vertex_texcoord");
// link program
glLinkProgram(state->gl_program_id);
// check if program is valid
error_if_glerror();
error_if_program_not_valid(state->gl_program_id);
}
// initialize the textures
void init_textures(Scene* scene, ShadeState* state) {
// YOUR CODE GOES HERE ---------------------
// grab textures from scene
auto texture_vector = get_textures(scene);
GLuint tex_id;
// foreach texture
for (auto tex_ : texture_vector)
{
// if already in the state->gl_texture_id map, skip
auto txt = state->gl_texture_id.find(tex_);
if (txt->first == tex_) continue;
// gen texture id
glGenTextures(1, &tex_id);
// set id to the state->gl_texture_id map for later use
// If the texture is not in the map, we have to add it: so we insert the
// tuple made by <image3f, id> of the current texture
state->gl_texture_id.insert(pair<image3f*, int>(tex_, tex_id));
// bind texture
glBindTexture(GL_TEXTURE_2D, tex_id);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// load texture data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_->width(), tex_->height(), 0, GL_RGB, GL_FLOAT, tex_->data());
glGenerateMipmap(GL_TEXTURE_2D);
}
}
// utility to bind texture parameters for shaders
// uses texture name, texture_on name, texture pointer and texture unit position
void _bind_texture(string name_map, string name_on, image3f* txt, int pos, ShadeState* state) {
// YOUR CODE GOES HERE ---------------------
// if txt is not null
if (txt != nullptr)
{
// set texture on boolean parameter to true
auto uni_bool_location = glGetUniformLocation(state->gl_program_id, name_on.c_str());
auto uni_sampler_location = glGetUniformLocation(state->gl_program_id, name_map.c_str());
glUniform1f(uni_bool_location, GL_TRUE);
// activate a texture unit at position pos
glActiveTexture(GL_TEXTURE0 + pos);
// bind texture object to it from state->gl_texture_id map
glBindTexture(GL_TEXTURE_2D, state->gl_texture_id.find(txt)->second);
// set texture parameter to the position pos
glUniform1i(uni_sampler_location, pos);
}
// else
else
{
// set texture on boolean parameter to false
auto uni_bool_location = glGetUniformLocation(state->gl_program_id, name_on.c_str());
auto uni_sampler_location = glGetUniformLocation(state->gl_program_id, name_map.c_str());
glUniform1f(uni_bool_location, GL_FALSE);
// activate a texture unit at position pos
glActiveTexture(GL_TEXTURE0 + pos);
// set zero as the texture id
glUniform1i(uni_sampler_location, (int)0);
}
}
// render the scene with OpenGL
void shade(Scene* scene, ShadeState* state) {
// enable depth test
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// disable culling face
glDisable(GL_CULL_FACE);
// let the shader control the points
glEnable(GL_POINT_SPRITE);
// set up the viewport from the scene image size
glViewport(0, 0, scene->image_width, scene->image_height);
// clear the screen (both color and depth) - set cleared color to background
glClearColor(scene->background.x, scene->background.y, scene->background.z, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// enable program
glUseProgram(state->gl_program_id);
// bind camera's position, inverse of frame and projection
// use frame_to_matrix_inverse and frustum_matrix
glUniform3fv(glGetUniformLocation(state->gl_program_id,"camera_pos"),
1, &scene->camera->frame.o.x);
glUniformMatrix4fv(glGetUniformLocation(state->gl_program_id,"camera_frame_inverse"),
1, true, &frame_to_matrix_inverse(scene->camera->frame)[0][0]);
glUniformMatrix4fv(glGetUniformLocation(state->gl_program_id,"camera_projection"),
1, true, &frustum_matrix(-scene->camera->dist*scene->camera->width/2, scene->camera->dist*scene->camera->width/2,
-scene->camera->dist*scene->camera->height/2, scene->camera->dist*scene->camera->height/2,
scene->camera->dist,10000)[0][0]);
// bind ambient and number of lights
glUniform3fv(glGetUniformLocation(state->gl_program_id,"ambient"),1,&scene->ambient.x);
glUniform1i(glGetUniformLocation(state->gl_program_id,"lights_num"),scene->lights.size());
// foreach light
auto count = 0;
for(auto light : scene->lights) {
// bind light position and internsity (create param name with tostring)
glUniform3fv(glGetUniformLocation(state->gl_program_id,tostring("light_pos[%d]",count).c_str()),
1, &light->frame.o.x);
glUniform3fv(glGetUniformLocation(state->gl_program_id,tostring("light_intensity[%d]",count).c_str()),
1, &light->intensity.x);
count++;
}
// foreach mesh
for(auto mesh : scene->meshes) {
// bind material kd, ks, n
glUniform3fv(glGetUniformLocation(state->gl_program_id,"material_kd"),
1,&mesh->mat->kd.x);
glUniform3fv(glGetUniformLocation(state->gl_program_id,"material_ks"),
1,&mesh->mat->ks.x);
glUniform1f(glGetUniformLocation(state->gl_program_id,"material_n"),
mesh->mat->n);
// YOUR CODE GOES HERE ---------------------
// bind texture params (txt_on, sampler)
_bind_texture("material_kd_txt", "material_kd_txt_on", mesh->mat->kd_txt, 0, state);
_bind_texture("material_ks_txt", "material_ks_txt_on", mesh->mat->ks_txt, 1, state);
_bind_texture("material_norm_txt", "material_norm_txt_on", mesh->mat->norm_txt, 2, state);
// bind mesh frame - use frame_to_matrix
glUniformMatrix4fv(glGetUniformLocation(state->gl_program_id,"mesh_frame"),
1,true,&frame_to_matrix(mesh->frame)[0][0]);
// enable vertex attributes arrays and set up pointers to the mesh data
auto vertex_pos_location = glGetAttribLocation(state->gl_program_id, "vertex_pos");
auto vertex_norm_location = glGetAttribLocation(state->gl_program_id, "vertex_norm");
// YOUR CODE GOES HERE ---------------------
auto vertex_texcoord_location = glGetAttribLocation(state->gl_program_id, "vertex_texcoord");
glEnableVertexAttribArray(vertex_pos_location);
glVertexAttribPointer(vertex_pos_location, 3, GL_FLOAT, GL_FALSE, 0, &mesh->pos[0].x);
glEnableVertexAttribArray(vertex_norm_location);
glVertexAttribPointer(vertex_norm_location, 3, GL_FLOAT, GL_FALSE, 0, &mesh->norm[0].x);
// YOUR CODE GOES HERE ---------------------
if (!mesh->texcoord.empty())
{
glEnableVertexAttribArray(vertex_texcoord_location);
glVertexAttribPointer(vertex_texcoord_location, 2, GL_FLOAT, GL_FALSE, 0, &mesh->texcoord[0].x);
}
// draw triangles and quads
if(! scene->draw_wireframe) {
if(mesh->triangle.size()) glDrawElements(GL_TRIANGLES, mesh->triangle.size()*3, GL_UNSIGNED_INT, &mesh->triangle[0].x);
if(mesh->quad.size()) glDrawElements(GL_QUADS, mesh->quad.size()*4, GL_UNSIGNED_INT, &mesh->quad[0].x);
} else {
auto edges = EdgeMap(mesh->triangle, mesh->quad).edges();
glDrawElements(GL_LINES, edges.size()*2, GL_UNSIGNED_INT, &edges[0].x);
}
// draw line sets
if(! mesh->line.empty()) glDrawElements(GL_LINES, mesh->line.size()*2, GL_UNSIGNED_INT, mesh->line.data());
for(auto segment : mesh->spline) glDrawElements(GL_LINE_STRIP, 4, GL_UNSIGNED_INT, &segment);
// disable vertex attribute arrays
glDisableVertexAttribArray(vertex_pos_location);
glDisableVertexAttribArray(vertex_norm_location);
// YOUR CODE GOES HERE ---------------------
if (!mesh->texcoord.empty())
glDisableVertexAttribArray(vertex_texcoord_location);
}
}
string scene_filename; // scene filename
string image_filename; // image filename
Scene* scene; // scene arrays
// uiloop
void uiloop() {
auto ok = glfwInit();
error_if_not(ok, "glfw init error");
// setting an error callback
glfwSetErrorCallback([](int ecode, const char* msg){ return error(msg); });
// glfwWindowHint(GLFW_SAMPLES, scene->image_samples*scene->image_samples);
auto window = glfwCreateWindow(scene->image_width,
scene->image_height,
"graphics14 | model", NULL, NULL);
error_if_not(window, "glfw window error");
glfwMakeContextCurrent(window);
glfwSetCharCallback(window, [](GLFWwindow* window, unsigned int key) {
switch (key) {
case 's':
scene->draw_captureimage = true;
break;
case 'w':
scene->draw_wireframe = ! scene->draw_wireframe;
break;
}
});
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
#ifdef _WIN32
auto ok1 = glewInit();
error_if_not(GLEW_OK == ok1, "glew init error");
#endif
auto state = new ShadeState();
init_shaders(state);
init_textures(scene,state);
auto mouse_last_x = -1.0;
auto mouse_last_y = -1.0;
while(! glfwWindowShouldClose(window)) {
glfwGetFramebufferSize(window, &scene->image_width, &scene->image_height);
scene->camera->width = (scene->camera->height * scene->image_width) / scene->image_height;
shade(scene,state);
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT)) {
double x, y;
glfwGetCursorPos(window, &x, &y);
if (mouse_last_x < 0 || mouse_last_y < 0) { mouse_last_x = x; mouse_last_y = y; }
auto delta_x = x - mouse_last_x, delta_y = y - mouse_last_y;
set_view_turntable(scene->camera, delta_x*0.01, -delta_y*0.01, 0, 0, 0);
mouse_last_x = x;
mouse_last_y = y;
} else { mouse_last_x = -1; mouse_last_y = -1; }
if(scene->draw_captureimage) {
auto image = image3f(scene->image_width,scene->image_height);
glReadPixels(0, 0, scene->image_width, scene->image_height, GL_RGB, GL_FLOAT, &image.at(0,0));
write_png(image_filename, image, true);
scene->draw_captureimage = false;
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
delete state;
}
// main function
int main(int argc, char** argv) {
auto args = parse_cmdline(argc, argv,
{ "03_model", "raytrace a scene",
{ {"resolution", "r", "image resolution", "int", true, jsonvalue() } },
{ {"scene_filename", "", "scene filename", "string", false, jsonvalue("scene.json")},
{"image_filename", "", "image filename", "string", true, jsonvalue("")} }
});
scene_filename = args.object_element("scene_filename").as_string();
image_filename = (args.object_element("image_filename").as_string() != "") ?
args.object_element("image_filename").as_string() :
scene_filename.substr(0,scene_filename.size()-5)+".png";
scene = load_json_scene(scene_filename);
if(! args.object_element("resolution").is_null()) {
scene->image_height = args.object_element("resolution").as_int();
scene->image_width = scene->camera->width * scene->image_height / scene->camera->height;
}
subdivide(scene);
uiloop();
}