/* by korenkonder GitHub/GitLab: korenkonder */ #include "post_process.hpp" #include "rob/rob.hpp" #include "fbo.hpp" #include "gl_state.hpp" #include "render_texture.hpp" #include "shader_ft.hpp" #include "stage.hpp" #include "stage_modern.hpp" #include "static_var.hpp" #include "texture.hpp" extern bool task_stage_is_modern; extern render_context* rctx_ptr; static void post_process_generate_area_texture(post_process* pp); static void post_process_calculate_area_texture_data(uint8_t* data, int32_t cross1, int32_t cross2); static void post_process_calculate_area_texture_data_area(float_t* val_left, float_t* val_right, int32_t cross1, int32_t cross2, int32_t dleft, int32_t dright); post_process_frame_texture_render_texture::post_process_frame_texture_render_texture() : texture() { type = POST_PROCESS_FRAME_TEXTURE_MAX; } post_process_frame_texture_render_texture::~post_process_frame_texture_render_texture() { } post_process_frame_texture::post_process_frame_texture() : capture() { } post_process_frame_texture::~post_process_frame_texture() { } post_process::post_process() : ssaa(), mlaa(), ss_alpha_mask(), aet_back_tex(), render_textures_data(), movie_textures_data(), aet_back(), mlaa_area_texture(), sss_contour_texture(), texture_counter(), lens_shaft_query(), lens_flare_query(), lens_shaft_query_data(), lens_flare_query_data(), lens_flare_query_index(), lens_flare_texture(), lens_shaft_texture(), lens_ghost_texture(), lens_ghost_count(), lens_flare_pos(), lens_shaft_scale(), lens_shaft_inv_scale(), lens_flare_power(), field_A10(), lens_flare_appear_power(), render_width(), render_height(), view_point(), interest(), view_point_prev(), interest_prev(), reset_exposure(), sprite_width(), sprite_height(), screen_x_offset(), screen_y_offset(), screen_width(), screen_height(), mag_filter() { blur = new post_process_blur(); dof = new post_process_dof(); exposure = new post_process_exposure(); tone_map = new post_process_tone_map(); transparency = new post_process_transparency(); stage_index = -1; stage_index_prev = -1; GLuint sampler; glGenSamplers(3, samplers); sampler = samplers[0]; glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); sampler = samplers[1]; glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); static const vec4 border_color = 0.0f; sampler = samplers[2]; glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); glGenVertexArrays(1, &query_vao); glGenVertexArrays(1, &lens_ghost_vao); gl_state_bind_vertex_array(lens_ghost_vao); glGenBuffers(1, &lens_ghost_vbo); gl_state_bind_array_buffer(lens_ghost_vbo, true); if (GLAD_GL_VERSION_4_4) glBufferStorage(GL_ARRAY_BUFFER, sizeof(float_t) * 5 * (6 * 16), 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); else glBufferData(GL_ARRAY_BUFFER, sizeof(float_t) * 5 * (6 * 16), 0, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float_t) * 5, (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float_t) * 5, (void*)(sizeof(float_t) * 2)); gl_state_bind_array_buffer(0); gl_state_bind_vertex_array(0); post_process_generate_area_texture(this); if (!lens_shaft_query[0]) glGenQueries(3, lens_shaft_query); if (!lens_flare_query[0]) glGenQueries(3, lens_flare_query); sun_quad_ubo.Create(sizeof(sun_quad_shader_data)); for (GLuint& i : lens_shaft_query_data) i = -1; for (GLuint& i : lens_flare_query_data) i = -1; reset(); } post_process::~post_process() { if (aet_back_tex) texture_free(aet_back_tex); if (blur) { delete blur; blur = 0; } if (dof) { delete dof; dof = 0; } if (exposure) { delete exposure; exposure = 0; } if (tone_map) { delete tone_map; tone_map = 0; } if (transparency) { delete transparency; transparency = 0; } if (mlaa_area_texture) { glDeleteTextures(1, &mlaa_area_texture); mlaa_area_texture = 0; } if (samplers[0]) { glDeleteSamplers(3, samplers); samplers[0] = 0; } if (query_vao) { glDeleteVertexArrays(1, &query_vao); query_vao = 0; } if (lens_ghost_vao) { glDeleteVertexArrays(1, &lens_ghost_vao); lens_ghost_vao = 0; } if (lens_ghost_vbo) { glDeleteBuffers(1, &lens_ghost_vbo); lens_ghost_vbo = 0; } if (lens_shaft_query[0]) { glDeleteQueries(3, lens_shaft_query); lens_shaft_query[0] = 0; } if (lens_flare_query[0]) { glDeleteQueries(3, lens_flare_query); lens_flare_query[0] = 0; } sun_quad_ubo.Destroy(); } void post_process::apply(camera* cam, texture* light_proj_tex, int32_t npr_param) { for (int32_t i = 0; i < 8; i++) gl_state_bind_sampler(i, 0); dof->apply(&rend_texture, &buf_texture, cam); draw_lens_ghost(&rend_texture); get_frame_texture(rend_texture.GetColorTex(), POST_PROCESS_FRAME_TEXTURE_PRE_PP); blur->downsample(&rend_texture); blur->get_blur(); exposure->get_exposure(cam, render_width, render_height, reset_exposure, blur->reduce_texture[4].GetColorTex(), blur->reduce_texture[2].GetColorTex()); tone_map->apply(&rend_texture, light_proj_tex, (texture*)(aet_back ? aet_back_tex : 0), &rend_texture, &buf_texture, /* sss_contour_texture,*/ blur->reduce_texture[0].GetColorTex(), exposure->exposure.GetColorTex(), npr_param, this); if (mlaa) apply_mlaa(samplers, ss_alpha_mask); for (int32_t i = 0; i < 16; i++) { if (!render_textures_data[i]) continue; render_textures[i].Bind(); texture* t = render_textures_data[i]; if (render_width > t->width * 2ULL || render_height > t->height * 2ULL) uniform_value[U_REDUCE] = 1; else uniform_value[U_REDUCE] = 0; glViewport(0, 0, t->width, t->height); gl_state_active_bind_texture_2d(0, rend_texture.GetColorTex()); gl_state_bind_sampler(0, samplers[0]); shaders_ft.set(SHADER_FT_REDUCE); RenderTexture::DrawQuad(&shaders_ft, render_width, render_height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); } get_frame_texture(rend_texture.GetColorTex(), POST_PROCESS_FRAME_TEXTURE_POST_PP); frame_texture_reset_capture(); screen_texture.Bind(); glViewport(0, 0, sprite_width, sprite_height); if (ssaa) { gl_state_active_bind_texture_2d(0, rend_texture.GetColorTex()); gl_state_bind_sampler(0, samplers[0]); uniform_value[U_ALPHA_MASK] = ss_alpha_mask ? 1 : 0; uniform_value[U_REDUCE] = 0; shaders_ft.set(SHADER_FT_REDUCE); RenderTexture::DrawQuad(&shaders_ft, render_width, render_height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); uniform_value[U_ALPHA_MASK] = 0; } else { gl_state_active_bind_texture_2d(0, rend_texture.GetColorTex()); if (mag_filter == POST_PROCESS_MAG_FILTER_NEAREST) gl_state_bind_sampler(0, samplers[1]); else gl_state_bind_sampler(0, samplers[0]); switch (mag_filter) { case POST_PROCESS_MAG_FILTER_NEAREST: case POST_PROCESS_MAG_FILTER_BILINEAR: default: uniform_value[U_MAGNIFY] = 0; break; case POST_PROCESS_MAG_FILTER_SHARPEN_5_TAP: uniform_value[U_MAGNIFY] = 2; break; case POST_PROCESS_MAG_FILTER_SHARPEN_4_TAP: uniform_value[U_MAGNIFY] = 3; break; case POST_PROCESS_MAG_FILTER_CONE_4_TAP: uniform_value[U_MAGNIFY] = 4; break; case POST_PROCESS_MAG_FILTER_CONE_2_TAP: uniform_value[U_MAGNIFY] = 5; break; } shaders_ft.set(SHADER_FT_MAGNIFY); RenderTexture::DrawQuad(&shaders_ft, render_width, render_height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); } shader::unbind(); for (int32_t i = 0; i < 8; i++) gl_state_bind_sampler(i, 0); } void post_process::apply_mlaa(GLuint* samplers, int32_t ss_alpha_mask) { gl_state_begin_event("PostProcess::mlaa"); mlaa_buffer.Bind(); gl_state_active_bind_texture_2d(0, rend_texture.GetColorTex()); gl_state_bind_sampler(0, samplers[1]); uniform_value[U_MLAA] = 0; shaders_ft.set(SHADER_FT_MLAA); RenderTexture::DrawQuad(&shaders_ft, render_width, render_height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); temp_buffer.Bind(); gl_state_active_bind_texture_2d(0, mlaa_buffer.GetColorTex()); gl_state_active_bind_texture_2d(1, mlaa_area_texture); gl_state_bind_sampler(0, samplers[0]); gl_state_bind_sampler(1, samplers[1]); uniform_value[U_MLAA] = 1; uniform_value[U_MLAA_SEARCH] = 2; shaders_ft.set(SHADER_FT_MLAA); RenderTexture::DrawQuad(&shaders_ft, render_width, render_height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); buf_texture.Bind(); gl_state_active_bind_texture_2d(0, rend_texture.GetColorTex()); gl_state_active_bind_texture_2d(1, temp_buffer.GetColorTex()); gl_state_bind_sampler(0, samplers[1]); uniform_value[U_MLAA] = 2; uniform_value[U_ALPHA_MASK] = ss_alpha_mask ? 1 : 0; shaders_ft.set(SHADER_FT_MLAA); RenderTexture::DrawQuad(&shaders_ft, render_width, render_height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); uniform_value[U_ALPHA_MASK] = 0; gl_state_active_bind_texture_2d(0, 0); gl_state_active_bind_texture_2d(1, 0); fbo::blit(buf_texture.fbos[0], rend_texture.fbos[0], 0, 0, buf_texture.GetWidth(), buf_texture.GetHeight(), 0, 0, rend_texture.GetWidth(), rend_texture.GetHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR); gl_state_end_event(); } void post_process::ctrl(camera* cam) { view_point_prev = view_point; interest_prev = interest; cam->get_view_point(view_point); cam->get_interest(interest); stage_index_prev = stage_index; if (task_stage_is_modern) stage_index = (int32_t)task_stage_modern_get_current_stage_hash(); else stage_index = task_stage_get_current_stage_index(); reset_exposure = cam->fast_change_hist1 && !cam->fast_change_hist0; if (reset_exposure) { float_t view_point_dist = vec3::distance(view_point, view_point_prev); vec3 dir = vec3::normalize(interest - view_point); vec3 dir_prev = vec3::normalize(interest_prev - view_point_prev); float_t dir_diff_angle = vec3::dot(dir, dir_prev); if (dir_diff_angle < 0.5f) dir_diff_angle = 0.0f; if (view_point_dist < dir_diff_angle * 0.4f) reset_exposure = false; } else if (stage_index != stage_index_prev) reset_exposure = true; } void post_process::draw_lens_flare(camera* cam) { static float_t flt_1411ACB84 = sinf(0.01365909819f); static float_t flt_1411ACB8C = tanf(0.01365909819f) * 2.0f * 0.94f; if (!lens_flare_texture) return; texture* tex = obj_database_get_obj_set_texture(5, 4549); if (!tex) return; float_t v5 = tanf((float_t)(cam->get_fov() * DEG_TO_RAD) * 0.5f); light_set* set = &rctx_ptr->light_set[LIGHT_SET_MAIN]; light_data* data = &set->lights[LIGHT_SUN]; vec3 position; vec4 emission; if (data->get_type() == LIGHT_PARALLEL) { data->get_position(position); data->get_diffuse(emission); } else { set->lights[LIGHT_STAGE].get_position(position); position = vec3::normalize(position) * 500.0f; set->lights[LIGHT_STAGE].get_ibl_color0(emission); data->set_type(LIGHT_PARALLEL); data->set_position(position); data->set_diffuse(emission); } vec4 v44; *(vec3*)&v44 = position; v44.w = 1.0f; mat4_transform_vector(&cam->view_projection, &v44, &v44); float_t v13 = 1.0f / v44.w; float_t v14 = v44.x * v13; float_t v15 = v44.y * v13; float_t v16 = (float_t)render_width * 0.01f / (float_t)render_height; v14 = clamp_def(v14, -0.99f, 0.99f); v15 = clamp_def(v15, v16 - 1.0f, 1.0f - v16); v44.x = v14 * v44.w; v44.y = v15 * v44.w; mat4_transform_vector(&cam->inv_view_projection, &v44, &v44); ((pos_scale*)&lens_flare_pos)->get_screen_pos_scale(cam->view_projection, position, false); float_t v17 = lens_flare_pos.x - (float_t)render_width * 0.5f; float_t v19 = v5 / (float_t)render_height * 0.5f; float_t v20 = lens_flare_pos.y - (float_t)render_height * 0.5f; float_t v22 = sqrtf(v17 * v19 * v17 * v19 + v20 * v19 * v20 * v19 + 1.0f); float_t v23 = ((float_t)render_width * (flt_1411ACB8C / v5)) * (v22 * v22 * 0.5f) * ((float_t)render_height * (flt_1411ACB8C / v5)); float_t v24 = vec3::distance(position, view_point) * flt_1411ACB84; emission *= 1.0f / (float_t)(1.0f - cosf((float_t)(3.0 * DEG_TO_RAD))); sun_quad_shader_data shader_data = {}; shader_data.g_emission = emission; gl_state_active_bind_texture_2d(0, tex->tex); shaders_ft.set(SHADER_FT_SUN); gl_state_bind_vertex_array(query_vao); sun_quad_ubo.Bind(0); int32_t query_index = (lens_flare_query_index + 1) % 3; lens_flare_query_index = query_index; int32_t next_query_index = (query_index + 2) % 3; if (lens_shaft_query_data[next_query_index] == -1) lens_shaft_query_data[next_query_index] = 0; if (lens_flare_query_data[next_query_index] == -1) lens_flare_query_data[next_query_index] = 0; gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); gl_state_set_depth_mask(GL_FALSE); gl_state_disable_cull_face(); mat4 mat; mat4_mul_translate(&cam->view, &position, &mat); mat4_clear_rot(&mat, &mat); mat4_scale_rot(&mat, v24 * 0.2f, &mat); mat4_mul(&mat, &cam->projection, &mat); mat4_transpose(&mat, &mat); shader_data.g_transform[0] = mat.row0; shader_data.g_transform[1] = mat.row1; shader_data.g_transform[2] = mat.row2; shader_data.g_transform[3] = mat.row3; sun_quad_ubo.WriteMemory(shader_data); glBeginQuery(GL_SAMPLES_PASSED, lens_shaft_query[next_query_index]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glEndQuery(GL_SAMPLES_PASSED); mat4_mul_translate(&cam->view, (vec3*)&v44, &mat); mat4_clear_rot(&mat, &mat); mat4_scale_rot(&mat, v24 * 2.0f, &mat); mat4_mul(&mat, &cam->projection, &mat); mat4_transpose(&mat, &mat); shader_data.g_transform[0] = mat.row0; shader_data.g_transform[1] = mat.row1; shader_data.g_transform[2] = mat.row2; shader_data.g_transform[3] = mat.row3; sun_quad_ubo.WriteMemory(shader_data); glBeginQuery(GL_SAMPLES_PASSED, lens_flare_query[next_query_index]); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glEndQuery(GL_SAMPLES_PASSED); gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); gl_state_set_depth_mask(GL_TRUE); gl_state_enable_cull_face(); if (lens_shaft_query_data[query_index] != -1) { int32_t res = 0; glGetQueryObjectiv(lens_shaft_query[query_index], GL_QUERY_RESULT_AVAILABLE, &res); if (res) glGetQueryObjectuiv(lens_shaft_query[query_index], GL_QUERY_RESULT, &lens_shaft_query_data[query_index]); } if (lens_flare_query_data[query_index] != -1) { int32_t res = 0; glGetQueryObjectiv(lens_flare_query[query_index], GL_QUERY_RESULT_AVAILABLE, &res); if (res) glGetQueryObjectuiv(lens_flare_query[query_index], GL_QUERY_RESULT, &lens_flare_query_data[query_index]); } if (emission.x + emission.y + emission.z > 0.0f) { gl_state_enable_blend(); gl_state_set_blend_func(GL_ONE, GL_ONE); gl_state_set_depth_mask(GL_FALSE); mat4_mul_translate(&cam->view, &position, &mat); mat4_clear_rot(&mat, &mat); mat4_scale_rot(&mat, v24 * 1.1f, &mat); mat4_mul(&mat, &cam->projection, &mat); mat4_transpose(&mat, &mat); shader_data.g_transform[0] = mat.row0; shader_data.g_transform[1] = mat.row1; shader_data.g_transform[2] = mat.row2; shader_data.g_transform[3] = mat.row3; sun_quad_ubo.WriteMemory(shader_data); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); gl_state_disable_blend(); gl_state_set_blend_func(GL_ONE, GL_ZERO); gl_state_set_depth_mask(GL_TRUE); } gl_state_active_bind_texture_2d(0, 0); gl_state_bind_vertex_array(0); if (lens_flare_appear_power <= 0.01f) lens_flare_appear_power = 0.0; else lens_flare_appear_power *= 0.93f; lens_shaft_scale = 100.0f; lens_flare_power = 0.0f; float_t v34 = lens_flare_pos.x; float_t v35 = lens_flare_pos.y; float_t v32 = lens_flare_pos.z; float_t v33 = 0.0f; if (v32 >= 0.0f || v34 < (float_t)-render_width || v34 > (float_t)(2 * render_width) || v35 < (float_t)-render_height || v35 > (float_t)(2 * render_height)) lens_flare_texture = 0; else { for (GLuint& i : lens_flare_query_data) if (i != -1) v33 += min_def((float_t)i, v23); v33 /= (v23 * 3.0f); lens_flare_power = powf(v33, 0.7f); if (v33 > 0.0f && lens_shaft_query_data[query_index]) { float_t v41 = max_def(v33 - 0.4f, 0.005f); lens_shaft_scale = ((0.6f / v41) * lens_shaft_inv_scale) * (v5 * 3.4f); } } if (v32 >= 0.0f || v34 < -12.0f || v34 > (float_t)(render_width + 12) || v35 < -12.0f || v35 > (float_t)(render_height + 12)) { field_A10 = 0.0f; } else if (v34 > -2.0f && v34 < (float_t)(render_width + 2) && v35 > -2.0f && v35 < (float_t)(render_height + 2)) { if (v33 > 0.4f && field_A10 == 0.0f && lens_flare_appear_power < 0.02f && !reset_exposure) lens_flare_appear_power = 8.0f; field_A10 = 1.0f; } } static void make_ghost_quad(uint8_t flags, float_t opacity, mat4* mat, float_t*& data) { const float_t x0 = flags & 0x01 ? 0.5f : 0.0f; const float_t x1 = flags & 0x01 ? 1.0f : 0.5f; const float_t y0 = flags & 0x02 ? 0.5f : 0.0f; const float_t y1 = flags & 0x02 ? 1.0f : 0.5f; vec4 p0 = { -0.5f, -0.5f, x0, y0 }; vec4 p1 = { 0.5f, -0.5f, x1, y0 }; vec4 p2 = { -0.5f, 0.5f, x0, y1 }; vec4 p3 = { 0.5f, 0.5f, x1, y1 }; mat4_transform_point(mat, (vec2*)&p0, (vec2*)&p0); mat4_transform_point(mat, (vec2*)&p1, (vec2*)&p1); mat4_transform_point(mat, (vec2*)&p2, (vec2*)&p2); mat4_transform_point(mat, (vec2*)&p3, (vec2*)&p3); *(vec4*)&data[0] = p0; data[4] = opacity; *(vec4*)&data[5] = p1; data[9] = opacity; *(vec4*)&data[10] = p2; data[14] = opacity; *(vec4*)&data[15] = p2; data[19] = opacity; *(vec4*)&data[20] = p1; data[24] = opacity; *(vec4*)&data[25] = p3; data[29] = opacity; data += 30; } void post_process::draw_lens_ghost(RenderTexture* rt) { static const float_t v13[16] = { -0.70f, -0.30f, 0.35f, 0.50f, -0.45f, -0.80f, 0.20f, 0.41f, 0.17f, -0.10f, 0.06f, 0.10f, 0.14f, 0.04f, -0.13f, -0.22f, }; static const float_t v14[16] = { 0.8f, 1.0f, 1.0f, 1.0f, 0.4f, 0.5f, 0.8f, 0.8f, 0.6f, 0.7f, 0.8f, 0.7f, 0.8f, 0.7f, 0.6f, 0.8f, }; static const float_t v15[16] = { 1.3f, 1.5f, 1.00f, 1.1f, 2.5f, 0.8f, 0.50f, 0.5f, 0.7f, 0.4f, 0.35f, 0.5f, 0.4f, 0.3f, 0.60f, 0.4f, }; if (!lens_flare_texture || lens_ghost_count <= 0) return; if (lens_ghost_count > 16) lens_ghost_count = 16; const float_t aspect = (float_t)render_width / (float_t)render_height; const float_t v7 = (lens_flare_pos.x - (float_t)(render_width / 2)) / (float_t)render_width; const float_t v8 = -((lens_flare_pos.y - (float_t)(render_height / 2)) / (float_t)render_height); const float_t v9 = (float_t)((1.1 - sqrtf(v8 * v8 + v7 * v7)) * lens_flare_power); if (v9 < 0.001f) return; const float_t v9a = v9 * v9; float_t angle = atan2f(v8, v7) - (float_t)M_PI_2; const float_t angle_sin = sinf(angle); const float_t angle_cos = cosf(angle); float_t* data; if (GLAD_GL_VERSION_4_5) { data = (float_t*)glMapNamedBuffer(lens_ghost_vbo, GL_WRITE_ONLY); if (!data) { glUnmapNamedBuffer(lens_ghost_vbo); return; } } else { gl_state_bind_array_buffer(lens_ghost_vbo); data = (float_t*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (!data) { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); return; } } const float_t lens_ghost = tone_map->lens_ghost; const int32_t lens_ghost_count = this->lens_ghost_count; for (int32_t i = 0; i < lens_ghost_count; i++) { float_t opacity = v9 * v14[i] * lens_ghost; float_t scale = (v9a * 0.03f + 0.02f) * v15[i]; mat4 mat; mat4_translate(v13[i] * v7 + 0.5f, v13[i] * v8 + 0.5f, 0.0f, &mat); mat4_scale_rot(&mat, scale, scale * aspect, 1.0f, &mat); mat4_mul_rotate_z(&mat, angle_sin, angle_cos, &mat); make_ghost_quad((uint8_t)(i & 0x03), opacity, &mat, data); } if (GLAD_GL_VERSION_4_5) glUnmapNamedBuffer(lens_ghost_vbo); else { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); } rt->Bind(); rt->SetViewport(); gl_state_enable_blend(); gl_state_set_blend_func(GL_ONE, GL_ONE); uniform_value[U_REDUCE] = 4; shaders_ft.set(SHADER_FT_REDUCE); gl_state_active_bind_texture_2d(0, lens_ghost_texture->tex); gl_state_bind_vertex_array(lens_ghost_vao); shaders_ft.draw_arrays(GL_TRIANGLES, 0, (GLsizei)(lens_ghost_count * 6LL)); gl_state_bind_vertex_array(0); gl_state_disable_blend(); gl_state_set_blend_func(GL_ONE, GL_ZERO); } void post_process::init_fbo(int32_t render_width, int32_t render_height, int32_t sprite_width, int32_t sprite_height, int32_t screen_width, int32_t screen_height) { if (this->render_width == render_width && this->render_height == render_height && this->sprite_width == sprite_width && this->sprite_height == sprite_height && this->screen_width == screen_width && this->screen_height == screen_height) return; if (this->render_width != render_width || this->render_height != render_height) { blur->init_fbo(render_width, render_height); dof->init_fbo(render_width, render_height); exposure->init_fbo(); tone_map->init_fbo(); rend_texture.Init(render_width, render_height, 0, GL_RGBA16F, GL_DEPTH_COMPONENT32F); buf_texture.Init(render_width, render_height, 0, GL_RGBA16F, 0); if (aet_back_tex) texture_free(aet_back_tex); aet_back_tex = texture_load_tex_2d(texture_id(0x25, texture_counter++), GL_RGBA8, render_width, render_height, 0, 0, false); aet_back_texture.SetColorDepthTextures(aet_back_tex->tex, 0, rend_texture.GetDepthTex()); transparency_texture.Init(render_width, render_height, 0, GL_RGBA16F, 0); transparency->init_fbo(transparency_texture.GetColorTex(), rend_texture.GetDepthTex(), render_width, render_height); mlaa_buffer.Init(render_width, render_height, 0, GL_RGBA8, GL_DEPTH_COMPONENT32F); temp_buffer.Init(render_width, render_height, 0, GL_RGBA8, 0); sss_contour_texture = &mlaa_buffer; gl_state_bind_texture_2d(rend_texture.GetDepthTex()); GLint swizzle[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); gl_state_bind_texture_2d(mlaa_buffer.GetDepthTex()); glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); gl_state_bind_texture_2d(0); this->render_width = render_width; this->render_height = render_height; } if (this->sprite_width != sprite_width || this->sprite_height != sprite_height) { screen_texture.Init(sprite_width, sprite_height, 0, GL_RGBA16F, 0); // Was GL_R11F_G11F_B10F this->sprite_width = sprite_width; this->sprite_height = sprite_height; } screen_x_offset = (screen_width - sprite_width) / 2 + (screen_width - sprite_width) % 2; screen_y_offset = (screen_height - sprite_height) / 2 + (screen_height - sprite_height) % 2; this->screen_width = screen_width; this->screen_height = screen_height; } bool post_process::frame_texture_cont_capture_set(bool value) { frame_texture[0].capture = value; return true; } void post_process::frame_texture_free() { for (post_process_frame_texture& i : frame_texture) { for (post_process_frame_texture_render_texture& j : i.render_textures) { j.texture = 0; j.render_texture.Free(); j.type = POST_PROCESS_FRAME_TEXTURE_MAX; } i.capture = false; } } int32_t post_process::frame_texture_load(int32_t slot, post_process_frame_texture_type type, texture* tex) { if (!tex || slot < 0 || slot >= 6) return -1; for (post_process_frame_texture_render_texture& i : frame_texture[slot].render_textures) if (!i.texture) { i.render_texture.SetColorDepthTextures(tex->tex); i.texture = tex; i.type = type; return (int32_t)(&i - frame_texture[slot].render_textures); } return -1; } void post_process::frame_texture_reset() { for (post_process_frame_texture& i : frame_texture) { for (post_process_frame_texture_render_texture& j : i.render_textures) { j.texture = 0; j.type = POST_PROCESS_FRAME_TEXTURE_MAX; } i.capture = false; } } void post_process::frame_texture_reset_capture() { for (post_process_frame_texture& i : frame_texture) if (&i - frame_texture) i.capture = false; } bool post_process::frame_texture_slot_capture_set(int32_t index) { if (index < 1 || index >= 5) return false; frame_texture[index].capture = true; return true; } bool post_process::frame_texture_unload(int32_t slot, texture* tex) { if (!tex || slot < 0 || slot >= 6) return false; for (post_process_frame_texture_render_texture& i : frame_texture[slot].render_textures) if (i.texture == tex) { i.render_texture.Free(); i.texture = 0; i.type = POST_PROCESS_FRAME_TEXTURE_MAX; return true; } return false; } int32_t post_process::movie_texture_set(texture* movie_texture) { if (!movie_texture) return -1; int32_t index = 0; while (movie_textures_data[index]) if (++index >= 1) return -1; movie_textures_data[index] = movie_texture; movie_textures[index].SetColorDepthTextures(movie_texture->tex); return index; } void post_process::get_frame_texture(GLuint tex, post_process_frame_texture_type type) { for (post_process_frame_texture& i : frame_texture) { if (!i.capture) continue; for (auto& j : i.render_textures) { if (!j.texture || j.type != type || j.render_texture.Bind() < 0) continue; texture* dst_tex = j.texture; gl_state_active_bind_texture_2d(0, tex); gl_state_bind_sampler(0, samplers[0]); glViewport(0, 0, dst_tex->width, dst_tex->height); uniform_value[U_REDUCE] = 0; shaders_ft.set(SHADER_FT_REDUCE); RenderTexture::DrawQuad(&shaders_ft, dst_tex->width, dst_tex->height, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); gl_state_bind_framebuffer(0); } } } void post_process::movie_texture_free(texture* movie_texture) { if (!movie_texture) return; int32_t index = 0; while (movie_textures_data[index] != movie_texture) if (++index >= 1) return; movie_textures_data[index] = 0; movie_textures[index].Free(); } int32_t post_process::render_texture_set(texture* render_texture, bool task_photo) { if (!render_texture) return -1; int32_t index = 0; if (task_photo) index = 15; else while (render_textures_data[index]) if (++index >= 15) return -1; render_textures_data[index] = render_texture; render_textures[index].SetColorDepthTextures(render_texture->tex); return index; } void post_process::render_texture_free(texture* render_texture, bool task_photo) { if (!render_texture) return; int32_t index = 0; if (task_photo) index = 15; else while (render_textures_data[index] != render_texture) if (++index >= 15) return; render_textures_data[index] = 0; render_textures[index].Free(); } void post_process::reset() { mlaa = true; mag_filter = POST_PROCESS_MAG_FILTER_BILINEAR; dof_debug_set(0); dof_pv_set(0); blur->initialize_data(2.0f, 1.0f); for (int32_t i = 0; i < 2; i++) { tone_map->reset_saturate_coeff(i, 0); tone_map->reset_scene_fade(i); tone_map->reset_tone_trans(i); } reset_exposure = true; } void post_process::set_render_texture(bool aet_back) { if (aet_back) { aet_back_texture.Bind(); this->aet_back = 1; } else rend_texture.Bind(); glViewport(0, 0, render_width, render_height); } #define MAX_EDGE_DETECTION_LEN (3) #define GRID_SIDE_LEN (2 * MAX_EDGE_DETECTION_LEN + 1) #define SIDE_LEN (5 * GRID_SIDE_LEN) static void post_process_generate_area_texture(post_process* pp) { uint8_t* data = (uint8_t*)malloc(SIDE_LEN * SIDE_LEN * 2); if (!data) return; for (int32_t cross2 = 0; cross2 < 5; cross2++) for (int32_t cross1 = 0; cross1 < 5; cross1++) post_process_calculate_area_texture_data(data, cross1, cross2); glGenTextures(1, &pp->mlaa_area_texture); gl_state_bind_texture_2d(pp->mlaa_area_texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, SIDE_LEN, SIDE_LEN, 0, GL_RG, GL_UNSIGNED_BYTE, data); gl_state_bind_texture_2d(0); free(data); } static void post_process_calculate_area_texture_data(uint8_t* data, int32_t cross1, int32_t cross2) { uint8_t* _data = &data[(SIDE_LEN * GRID_SIDE_LEN * cross2 + GRID_SIDE_LEN * cross1) * 2]; for (int32_t dright = 0; dright < GRID_SIDE_LEN; dright++) { for (int32_t dleft = 0; dleft < GRID_SIDE_LEN; dleft++) { float_t val_left = 0.0f; float_t val_right = 0.0f; post_process_calculate_area_texture_data_area(&val_left, &val_right, cross1, cross2, dleft, dright); _data[0] = (uint8_t)(val_left * 255.9f); _data[1] = (uint8_t)(val_right * 255.9f); _data += 2; } _data += GRID_SIDE_LEN * 4 * 2; } } static void post_process_calculate_area_texture_data_area(float_t* val_left, float_t* val_right, int32_t cross1, int32_t cross2, int32_t dleft, int32_t dright) { auto calc_area_tex_val = [&](int32_t a1, int32_t a2) { float_t v2 = (float_t)(-0.5 / ((float_t)a2 - 0.5)); float_t v3 = (float_t)a1 * v2 + 0.5f; if (a1 >= a2 - 1) return (v3 * 0.5f) * 0.5f; else return ((float_t)(a1 + 1) * v2 + 0.5f + v3) * 0.5f; }; int32_t dist = dleft + dright + 1; float_t _val_left = 0.0f; float_t _val_right = 0.0f; switch (cross2) { case 0: switch (cross1) { case 1: _val_right = calc_area_tex_val(dleft, min_def(dist, GRID_SIDE_LEN)); break; case 3: _val_left = calc_area_tex_val(dleft, min_def(dist, GRID_SIDE_LEN)); break; } break; case 1: switch (cross1) { case 0: _val_right = calc_area_tex_val(dright, min_def(dist, GRID_SIDE_LEN)); break; case 1: if (dist % 2) { int32_t v12 = dist / 2 + 1; int32_t v15 = dist / 2; if (dleft < v15) _val_right = calc_area_tex_val(dleft, v12); else if (dright < v15) _val_right = calc_area_tex_val(dright, v12); else _val_right = calc_area_tex_val(v15, v12) * 2.0f; } else { int32_t v12 = dist / 2; float_t v14 = -0.5f / (float_t)v12; if (dleft >= v12) _val_right = ((float_t)(dright * 2 + 1) * v14 + 1.0f) * 0.5f; else _val_right = ((float_t)(dleft * 2 + 1) * v14 + 1.0f) * 0.5f; } break; case 3: case 4: if (dist % 2) { int32_t v12 = dist / 2 + 1; int32_t v15 = dist / 2; if (dleft < v15) _val_left = calc_area_tex_val(dleft, v12); else if (dright < v15) _val_right = calc_area_tex_val(dright, v12); else _val_left = _val_right = calc_area_tex_val(v15, v12); } else { int32_t v12 = dist / 2; float_t v14 = -0.5f / (float_t)v12; if (dleft < v12) _val_left = ((float_t)(dleft * 2 + 1) * v14 + 1.0f) * 0.5f; else _val_right = ((float_t)(dright * 2 + 1) * v14 + 1.0f) * 0.5f; } break; } break; case 3: switch (cross1) { case 0: _val_left = calc_area_tex_val(dright, min_def(dist, GRID_SIDE_LEN)); break; case 1: case 4: if (dist % 2) { int32_t v12 = dist / 2 + 1; int32_t v15 = dist / 2; if (dleft < v15) _val_right = calc_area_tex_val(dleft, v12); else if (dright < v15) _val_left = calc_area_tex_val(dright, v12); else _val_left = _val_right = calc_area_tex_val(v15, v12); } else { int32_t v12 = dist / 2; float_t v14 = -0.5f / (float_t)v12; if (dleft < v12) _val_right = ((float_t)(dleft * 2 + 1) * v14 + 1.0f) * 0.5f; else _val_left = ((float_t)(dright * 2 + 1) * v14 + 1.0f) * 0.5f; } break; case 3: if (dist % 2) { int32_t v12 = dist / 2 + 1; int32_t v15 = dist / 2; if (dleft < v15) _val_left = calc_area_tex_val(dleft, v12); else if (dright < v15) _val_left = calc_area_tex_val(dright, v12); else _val_left = calc_area_tex_val(v15, v12) * 2.0f; } else { int32_t v12 = dist / 2; float_t v14 = -0.5f / (float_t)v12; if (dleft < v12) _val_left = ((float_t)(dleft * 2 + 1) * v14 + 1.0f) * 0.5f; else _val_left = ((float_t)(dright * 2 + 1) * v14 + 1.0f) * 0.5f; } break; } break; case 4: switch (cross1) { case 1: if (dist % 2) { int32_t v12 = dist / 2 + 1; int32_t v15 = dist / 2; if (dleft < v15) _val_right = calc_area_tex_val(dleft, v12); else if (dright < v15) _val_left = calc_area_tex_val(dright, v12); else _val_left = _val_right = calc_area_tex_val(v15, v12); } else { int32_t v12 = dist / 2; float_t v14 = -0.5f / (float_t)v12; if (dleft < v12) _val_right = ((float_t)(dleft * 2 + 1) * v14 + 1.0f) * 0.5f; else _val_left = ((float_t)(dright * 2 + 1) * v14 + 1.0f) * 0.5f; } break; case 3: if (dist % 2) { int32_t v12 = dist / 2 + 1; int32_t v15 = dist / 2; if (dleft < v15) _val_left = calc_area_tex_val(dleft, v12); else if (dright < v15) _val_right = calc_area_tex_val(dright, v12); else _val_left = _val_right = calc_area_tex_val(v15, v12); } else { int32_t v12 = dist / 2; float_t v14 = -0.5f / (float_t)v12; if (dleft < v12) _val_left = ((float_t)(dleft * 2 + 1) * v14 + 1.0f) * 0.5f; else _val_right = ((float_t)(dright * 2 + 1) * v14 + 1.0f) * 0.5f; } break; } break; } *val_left = _val_left; *val_right = _val_right; return; }