frag_color phong_tangent_space_shader::fragment( const vec3& bary, const mat3& verts, const mat3x2& tex_coords, const mat3& vert_norms) const { auto uv = bary_lerp(tex_coords[0], tex_coords[1], tex_coords[2], bary); auto objspace_norm = bary_lerp(vert_norms[0], vert_norms[1], vert_norms[2], bary); auto ts_normval = normalmap.get_from_ratio(uv[0], uv[1]); auto tanspace_norm = normalize(vec3(ts_normval.r - 127.5, ts_normval.g - 127.5, ts_normval.b - 127.5)); // objspace and texture space verts auto o1 = verts[1] - verts[0]; auto o2 = verts[2] - verts[0]; auto t1 = tex_coords[1] - tex_coords[0]; auto t2 = tex_coords[2] - tex_coords[0]; auto tanspace_mat = mat2(t1.x, t2.x, t1.y, t2.y); auto objspace_mat = mat3x2(o1.x, o2.x, o1.y, o2.y, o1.z, o2.z); // S * t1.x + T * t1.y = o1 // S * t2.x + T * t2.y = o2 // // [t1.x t1.y] * [tan = [o1.x o1.y o1.z] // t2.x t2.y] bitan] o2.x o2.y o2.z] // // tanspace_mat * tan_bitan_mat = objspace_mat // inverse(tanspace_mat) * tanspace_mat * tan_bitan_mat = inverse(tanspace_mat) * objspace_mat // I * tan_bitan_mat = inverse(tansapce_mat) * objspace_mat // tan_bitan_mat = inverse(tanspace_mat) * objspace_mat auto tan_bitan_mat = transpose(inverse(tanspace_mat) * objspace_mat); auto tan_to_objspace = mat3(normalize(tan_bitan_mat[0]), normalize(tan_bitan_mat[1]), normalize(objspace_norm)); auto norm = normalize(tan_to_objspace * tanspace_norm); // ambient color TGAColor ambient(5, 5, 5, 255); // specular color auto spec_val = specular.get_from_ratio(uv[0], uv[1]).raw[0]; auto r = normalize(reflect(light_dir(), norm)); auto spec_intensity = pow(max(0.0f, dot(r, to_cam)), spec_val); // diffuse color float diff_intensity = max(0.0f, dot(to_light(), norm)); auto diff_color = diffuse.get_from_ratio(uv[0], uv[1]); diff_color.scale(diff_intensity + spec_intensity * .6); return frag_color {ambient + diff_color, true}; }
frag_color phong_shader::fragment( const vec3& bary, const mat3& verts, const mat3x2& tex_coords, const mat3& vert_norms) const { auto uv = bary_lerp(tex_coords[0], tex_coords[1], tex_coords[2], bary); auto normval = normalmap.get_from_ratio(uv[0], uv[1]); auto norm = normalize(vec3(normval.r - 127.5, normval.g - 127.5, normval.b - 127.5)); // ambient color TGAColor ambient(5, 5, 5, 255); // specular color auto spec_val = specular.get_from_ratio(uv[0], uv[1]).raw[0]; auto r = normalize(reflect(light_dir(), norm)); auto spec_intensity = pow(max(0.0f, dot(r, to_cam)), spec_val); // diffuse color float diff_intensity = max(0.0f, dot(to_light(), norm)); auto diff_color = diffuse.get_from_ratio(uv[0], uv[1]); diff_color.scale(diff_intensity + spec_intensity * .6); return frag_color {ambient + diff_color, true}; }
void GLWidget::mouseMoveEvent(QMouseEvent *event) { last.x = event->x(); last.y = event->y(); vec3 begin = pointOnVirtualTrackball(first); vec3 end = pointOnVirtualTrackball(last); float dotProduct = dot(normalize(begin), normalize(end)); float angle = acos(dotProduct); vec3 crossP = cross(begin, end); if(length(crossP) > .00001f) { rotationMatrix = rotate(mat4(1.0), angle, normalize(crossP)) * rotationMatrix; glUseProgram(cubeProg); glUniformMatrix4fv(cubeRotationMatrixLoc, 1, false, value_ptr(rotationMatrix)); glUseProgram(gridProg); glUniformMatrix4fv(gridRotationMatrixLoc, 1, false, value_ptr(rotationMatrix)); update(); } first.x = last.x; first.y = last.y; }
frag_color normal_shader::fragment( const vec3& bary, const mat3& verts, const mat3x2& tex_coords, const mat3& vert_norms) const { auto uv = bary_lerp(tex_coords[0], tex_coords[1], tex_coords[2], bary); auto normval = normalmap.get_from_ratio(uv[0], uv[1]); auto normal = normalize(vec3(normval.r - 127.5, normval.g - 127.5, normval.b - 127.5)); float intensity = max(0.0f, dot(to_light(), normal)); vec3 norm_color = normalize(bary_lerp(vert_norms[0], vert_norms[1], vert_norms[2], bary)) * 255.0f; TGAColor c(abs(norm_color.x), abs(norm_color.y), abs(norm_color.z), 255); c.scale(intensity); return frag_color {c, true}; }
frag_color simple_normal_shader::fragment( const vec3& bary, const mat3& verts, const mat3x2& tex_coords, const mat3& vert_norms) const { vec3 norm_color = normalize(bary_lerp(vert_norms[0], vert_norms[1], vert_norms[2], bary)) * 255.0f; TGAColor c(abs(norm_color.x), abs(norm_color.y), abs(norm_color.z), 255); return frag_color {c, true}; }
frag_color simple_texture_shader::fragment( const vec3& bary, const mat3& verts, const mat3x2& tex_coords, const mat3& vert_norms) const { auto norm = bary_lerp(vert_norms[0], vert_norms[1], vert_norms[2], bary); float intensity = max(0.0f, dot(to_light(), normalize(norm))); auto uv = bary_lerp(tex_coords[0], tex_coords[1], tex_coords[2], bary); auto color = tex.get_from_ratio(uv[0], uv[1]); color.scale(intensity); return frag_color {color, true}; }
/* * Phillips wave spectrum, equation 40 with modification specified in equation 41 */ float Ocean::phillips(int n_prime, int m_prime) const { // Wavevector float kx = M_PI * (2 * n_prime - _N) / _length; float kz = M_PI * (2 * m_prime - _N) / _length; vec2 k(kx, kz); // Magnitude of wavevector float k_length = glm::length(k); // Wind speed float w_length = glm::length(_w); // If wavevector is very small, no need to calculate, just return zero if (k_length < 0.000001) return 0.0; // Precaculate k^2 and k^4 float k_length2 = k_length * k_length; float k_length4 = k_length2 * k_length2; // Cosine factor - eliminates waves that travel perpendicular to the wind float k_dot_w = dot(normalize(k), normalize(_w)); float k_dot_w2 = k_dot_w * k_dot_w; float L = w_length * w_length / _g; float L2 = L * L; // Something a bit extra to keep waves from exploding. Not in the paper. float damping = 0.001; float l2 = L2 * damping * damping; // Phillips spectrum as described in eqn 40 with modification described in 41 to // suppress very small waves that cause convergence problems return _A * (exp(-1.0f / (k_length2 * L2)) / k_length4) * k_dot_w2 * exp(-k_length2 * l2); }
int main(int argc, char** argv) { /* Initialize logging. */ START_EASYLOGGINGPP(argc, argv); LOG(TRACE) << "Logging initialized."; /* Initialize SDL. */ LOG(TRACE) << "Initializing SDL..."; int result = SDL_Init(SDL_INIT_VIDEO); if (result != 0) { LOG(FATAL) << "Could not initialize SDL: " << SDL_GetError(); } window = SDL_CreateWindow(argv[0], 0, 0, (int)window_width, (int)window_height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL); if (window == nullptr) { LOG(FATAL) << "Could not create window: " << SDL_GetError(); } SDL_ShowCursor(SDL_DISABLE); SDL_SetRelativeMouseMode(SDL_TRUE); frequency = SDL_GetPerformanceFrequency(); time_last_frame = SDL_GetPerformanceCounter(); LOG(INFO) << "Performance counter frequency: " << frequency; /* Initialize OpenGL. */ LOG(TRACE) << "Initializing OpenGL..."; SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GLContext gl_context = SDL_GL_CreateContext(window); if (gl_context == nullptr) { LOG(FATAL) << SDL_GetError(); } glewExperimental = GL_TRUE; GLenum glewError = glewInit(); if (glewError != GLEW_OK) { LOG(FATAL) << "Could not initialize GLEW: " << glewGetErrorString(glewError); } glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); glEnable(GL_VERTEX_ARRAY); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* Load assets. */ LOG(TRACE) << "Loading assets..."; shaders = new ShaderManager(); { shader_terrain = shaders->get("terrain"); shader_terrain->apply(); glActiveTexture(GL_TEXTURE0); texture_terrain = new Texture("terrain"); shader_terrain->updateUniform("tex", 0); terrain = new Terrain("heightmap.png"); shader_terrain->updateUniform("max_height", terrain->getMaxHeight()); auto world = mat4(); world = translate(world, vec3(-terrain->getWidth() / 2.f, -terrain->getMaxHeight() / 2.f, -terrain->getDepth() / 2.f)); shader_terrain->updateUniform("K_a", 0.1f); shader_terrain->updateUniform("K_d", 0.9f); shader_terrain->updateWorldMatrix(world); LOG(INFO) << "Maximum terrain height: " << terrain->getMaxHeight(); LOG(INFO) << "Terrain width: " << terrain->getWidth(); LOG(INFO) << "Terrain depth: " << terrain->getDepth(); } { shader_skybox = shaders->get("skybox"); shader_skybox->apply(); glActiveTexture(GL_TEXTURE2); environment_map = new CubeMap("terrain_"); shader_skybox->updateUniform("cubeMap", 2); auto world = mat4(); world = scale(world, vec3(terrain->getWidth(), terrain->getWidth(), terrain->getDepth())); skybox = new Cube(); shader_terrain->apply(); shader_skybox->updateWorldMatrix(world); } { auto world = mat4(); world = scale(world, vec3(terrain->getWidth() / 2.f, terrain->getMaxHeight() * 2.f, terrain->getDepth() / 2.f)); shader_colour = shaders->get("colour"); shader_colour->apply(); shader_colour->updateWorldMatrix(world); origin = new Origin(); } { auto world = mat4(); world = translate(world, vec3(-terrain->getWidth() / 2.f, -terrain->getMaxHeight() / 2.f + 25.f, -terrain->getDepth() / 2.f)); shader_water = shaders->get("water"); shader_water->apply(); shader_water->updateUniform("K_a", 0.1f); shader_water->updateUniform("K_d", 0.0f); shader_water->updateUniform("K_s", 0.9f); shader_water->updateWorldMatrix(world); water = new Grid(terrain->getDepth(), 1000.f); } /* Set up light. */ auto light = Camera(); light.eye = vec3(1024.0f, 1024.f, 1024.f); light.at = vec3(0.0f, 0.0f, 0.0f); light.up = vec3(0.0f, 0.0f, -1.0f); auto light_dir = normalize(vec3(0.f, 0.25f, -1.f)); shader_terrain->apply(); shader_terrain->updateUniform("light_dir", light_dir); shader_water->apply(); shader_water->updateUniform("light_dir", light_dir); /* Set up view. */ auto camera = Camera(); auto camera_height = terrain->getMaxHeight() * 3.f; camera.eye = glm::vec3(0.f, camera_height, -terrain->getDepth() / 2.f); camera.at = glm::vec3(0, 0, 0); camera.up = glm::vec3(0, 1, 0); auto view = glm::lookAt(camera.eye, camera.at, camera.up); shaders->updateViewMatrices(view); /* Set up projection. */ auto proj = glm::perspective(45.f, window_width / window_height, 100.f, 25000.f); shaders->updateProjectionMatrices(proj); /* Set up frame buffers. */ frame_buffer_color = new ColorFrameBuffer(window_width, window_height); /* Main loop. */ float angle = 0.0f; bool done = false; SDL_Event event; LOG(TRACE) << "Entering main loop..."; while (!done) { while (SDL_PollEvent(&event) != 0) { if (event.type == SDL_QUIT) { done = true; } else if (event.type == SDL_KEYDOWN) { switch (event.key.keysym.sym) { case SDLK_ESCAPE: LOG(INFO) << "Exiting normally at user request..."; done = true; break; case SDLK_PRINTSCREEN: LOG(INFO) << "Rendering screen shot..."; frame_buffer_color->bind(); draw(); frame_buffer_color->write(); frame_buffer_color->unbind(); break; default: break; } } else if (event.type == SDL_MOUSEMOTION) { const GLfloat X_SCALED = -(GLfloat)event.motion.xrel / window_width; const GLfloat Y_SCALED = -(GLfloat)event.motion.yrel / window_height; const GLfloat LEFT_RIGHT_ROT = X_SCALED * ANGLE_DELTA; const GLfloat UP_DOWN_ROT = Y_SCALED * ANGLE_DELTA; vec3 tempD(camera.at - camera.eye); vec4 d(tempD.x, tempD.y, tempD.z, 0.0f); vec3 right = cross(tempD, camera.up); mat4 rot; rot = rotate(rot, UP_DOWN_ROT, right); rot = rotate(rot, LEFT_RIGHT_ROT, camera.up); d = rot * d; camera.at.x = camera.eye.x + d.x; camera.at.y = camera.eye.y + d.y; camera.at.z = camera.eye.z + d.z; } } glm::vec3 direction = STEP * glm::normalize(camera.at - camera.eye); glm::vec3 right = STEP * glm::normalize(glm::cross(direction, camera.up)); auto keys = SDL_GetKeyboardState(nullptr); if (keys[SDL_SCANCODE_W]) { camera.eye += direction; camera.at += direction; } if (keys[SDL_SCANCODE_S]) { camera.eye -= direction; camera.at -= direction; } if (keys[SDL_SCANCODE_D]) { camera.eye += right; camera.at += right; } if (keys[SDL_SCANCODE_A]) { camera.eye -= right; camera.at -= right; } if (keys[SDL_SCANCODE_SPACE]) { camera.eye += STEP * camera.up; camera.at += STEP * camera.up; } if (keys[SDL_SCANCODE_LCTRL]) { camera.eye -= STEP * camera.up; camera.at -= STEP * camera.up; } view = glm::lookAt(camera.eye, camera.at, camera.up); shaders->updateViewMatrices(view); draw(); angle += 0.01f; } delete shaders; delete environment_map; delete frame_buffer_color; SDL_DestroyWindow(window); SDL_Quit(); return 0; }
void sceneRender() { using glm::cross; using glm::clamp; using glm::vec3; using glm::normalize; // TODO: Save scene pixels as png int width = g_theScene->output_size.x; int height = g_theScene->output_size.y; const char* output = isEmpty(g_theScene->output) ? "out.png" : g_theScene->output; unsigned char* pixels = (unsigned char*) malloc(c_bpp * width * height); camera_t& cam = g_theScene->camera; float halfFov = cam.fov / 2; float tanFov = tan(halfFov); float halfWidth = width / 2.0f; float halfHeight = height / 2.0f; vec3 w = normalize(cam.look_from - cam.look_at); vec3 u = normalize(cross(cam.up, w)); vec3 v = normalize(cross(u, w)); ray_t ray; ray.origin = cam.look_from; ray_query_t best; float multi = tanFov / halfHeight; unsigned char* currentPixel = pixels; for (int y = 0; y < height; ++y) { float cy = halfHeight - (y + 0.5f); for (int x = 0; x < width; currentPixel += c_bpp, ++x) { // reset best query. best.obj = NULL; best.t = FLT_MAX; // Get Ray through pixel. float cx = (x + 0.5f) - halfWidth; float a = cx * multi; float b = cy * multi; vec3 rayDirection = normalize((a * u) + (b * v) - w); // Find intersection with scene. ray_query_t query; vec3* vertices = g_theScene->vertices; triangle_t* tri = g_theScene->triangles; for (int t = 0; t < g_theScene->triangle_count; ++t, ++tri) { glm::vec4 rayOriginT = tri->xform_inv * glm::vec4(cam.look_from, 1.0f); glm::vec4 rayDirectionT = tri->xform_inv * glm::vec4(rayDirection, 0.0f); ray.origin = vec3(rayOriginT.x, rayOriginT.y, rayOriginT.z); ray.direction = vec3(rayDirectionT.x, rayDirectionT.y, rayDirectionT.z); int* indices = tri->indicies; if (intersectRayTriangle(ray, vertices[indices[0]], vertices[indices[1]], vertices[indices[2]], query)) { query.obj = tri; if (query.t < best.t) best = query; } } sphere_t* sph = g_theScene->spheres; for (int s = 0; s < g_theScene->sphere_count; ++s, ++sph) { glm::vec4 rayOriginT = sph->xform_inv * glm::vec4(cam.look_from, 1.0f); glm::vec4 rayDirectionT = sph->xform_inv * glm::vec4(rayDirection, 0.0f); ray.origin = vec3(rayOriginT.x, rayOriginT.y, rayOriginT.z); ray.direction = vec3(rayDirectionT.x, rayDirectionT.y, rayDirectionT.z); if (intersectRaySphere(ray, *sph, query)) { query.obj = sph; if (query.t < best.t) best = query; } } // TODO: Light object if (best.obj != NULL) { material_t& mat = best.obj->material; vec3 color = mat.ambient; // final color conversion. currentPixel[0] = (unsigned char) (clamp(color.b, 0.0f, 1.0f) * 255.0f); currentPixel[1] = (unsigned char) (clamp(color.g, 0.0f, 1.0f) * 255.0f); currentPixel[2] = (unsigned char) (clamp(color.r, 0.0f, 1.0f) * 255.0f); } } } printf("Rendering scene to %s...\n", output); FIBITMAP *img = FreeImage_ConvertFromRawBits(pixels, width, height, width * c_bpp, c_bpp * 8, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, false); FreeImage_Save(FIF_PNG, img, isEmpty(g_theScene->output) ? "out.png" : g_theScene->output, 0); free(pixels); printf("Render complete!\n"); }