mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 12:41:35 -10:00
The memory panel showed no texture bytes and no geometry bytes under OpenGL. Only the depth renderbuffers went into the render target group. All three groups now give a value. Textures: GL_texture_bytes_used holds the bytes of every live texture cache slot. opengl_texture_release_bytes() removes the bytes of a slot at each site that drops its texture data. The bitmap render targets record the bytes of their color texture. Geometry: the unused GL_vertex_data_in global becomes GL_geometry_bytes_used, which counts only vertex and index buffers. This matches the geometry group of the Vulkan backend. gr_opengl_delete_buffer() sets the size of the buffer to 0, because opengl_destroy_all_buffers() calls it a second time for a buffer that the game already deleted. Render targets: three new functions add the scene textures, the post-processing textures, and the shadow map to the group. They sum the bytes at the moment of the call from the textures that exist, thus they need no bookkeeping at the create and delete sites. Co-Authored-By: Claude Opus 5 <[email protected]>
1211 lines
37 KiB
C++
1211 lines
37 KiB
C++
|
|
#include "gropenglpostprocessing.h"
|
|
|
|
#include "ShaderProgram.h"
|
|
#include "SmaaAreaTex.h"
|
|
#include "SmaaSearchTex.h"
|
|
#include "freespace.h"
|
|
#include "gropengl.h"
|
|
#include "gropengldraw.h"
|
|
#include "gropenglshader.h"
|
|
#include "gropenglstate.h"
|
|
|
|
#include "cmdline/cmdline.h"
|
|
#include "def_files/def_files.h"
|
|
#include "graphics/shader_types.h"
|
|
#include "graphics/grinternal.h"
|
|
#include "graphics/openxr.h"
|
|
#include "graphics/util/uniform_structs.h"
|
|
#include "io/timer.h"
|
|
#include "lighting/lighting.h"
|
|
#include "lighting/lighting_profiles.h"
|
|
#include "mod_table/mod_table.h"
|
|
#include "nebula/neb.h"
|
|
#include "parse/parselo.h"
|
|
#include "ship/ship.h"
|
|
#include "tracing/tracing.h"
|
|
#ifdef USE_OPENGL_ES
|
|
#include "es_compatibility.h"
|
|
#endif
|
|
|
|
extern bool PostProcessing_override;
|
|
extern int opengl_check_framebuffer();
|
|
// Needed to track where the FXAA shaders are
|
|
// In case we don't find the shaders at all, this override is needed
|
|
bool fxaa_unavailable = false;
|
|
bool zbuffer_saved = false;
|
|
|
|
const int MAX_MIP_BLUR_LEVELS = 4;
|
|
|
|
static int Post_initialized = 0;
|
|
|
|
bool Post_in_frame = false;
|
|
|
|
static int Post_active_shader_index = -1;
|
|
|
|
static GLuint Bloom_framebuffer = 0;
|
|
static GLuint Bloom_textures[2] = { 0 };
|
|
|
|
static GLuint Post_framebuffer_id[2] = { 0 };
|
|
|
|
static int Post_texture_width = 0;
|
|
static int Post_texture_height = 0;
|
|
|
|
static GLuint Smaa_edge_detection_fb = 0;
|
|
static GLuint Smaa_edges_tex = 0;
|
|
|
|
static GLuint Smaa_blending_weight_fb = 0;
|
|
static GLuint Smaa_blend_tex = 0;
|
|
|
|
static GLuint Smaa_neighborhood_blending_fb = 0;
|
|
static GLuint Smaa_output_tex = 0;
|
|
|
|
static GLuint Smaa_search_tex = 0;
|
|
static GLuint Smaa_area_tex = 0;
|
|
|
|
namespace ltp = lighting_profiles;
|
|
using namespace ltp;
|
|
|
|
void opengl_post_pass_tonemap()
|
|
{
|
|
GR_DEBUG_SCOPE("Tonemapping");
|
|
TRACE_SCOPE(tracing::Tonemapping);
|
|
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_TONEMAPPING, openxr_enabled() ? SDR_FLAG_TONEMAPPING_LINEAR_OUT : 0));
|
|
|
|
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::tonemapping_data>(
|
|
[](graphics::generic_data::tonemapping_data* data) {
|
|
auto ppc = ltp::current_piecewise_intermediates();
|
|
auto tn = ltp::current_tonemapper();
|
|
data->tonemapper = (int)tn;
|
|
data->sh_B = ppc.sh_B;
|
|
data->sh_lnA = ppc.sh_lnA;
|
|
data->sh_offsetX =ppc.sh_offsetX;
|
|
data->sh_offsetY = ppc.sh_offsetY;
|
|
data->toe_B = ppc.toe_B;
|
|
data->toe_lnA = ppc.toe_lnA;
|
|
data->x0 = ppc.x0;
|
|
data->x1 = ppc.x1;
|
|
data->y0 = ppc.y0;
|
|
data->exposure = ltp::current_exposure();
|
|
// OpenGL never negotiates HDR10 output, so Gr_hdr_output_active is
|
|
// always false here and current_tonemapper() never returns HdrScene.
|
|
data->hdr_paperwhite_nits = 0.0f;
|
|
data->hdr_peak_nits = 0.0f; });
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_ldr_texture, 0);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
}
|
|
|
|
void opengl_post_pass_bloom()
|
|
{
|
|
GR_DEBUG_SCOPE("Bloom");
|
|
TRACE_SCOPE(tracing::Bloom);
|
|
|
|
// we need the scissor test disabled
|
|
GLboolean scissor_test = GL_state.ScissorTest(GL_FALSE);
|
|
|
|
// ------ begin bright pass ------
|
|
int width, height;
|
|
{
|
|
GR_DEBUG_SCOPE("Bloom bright pass");
|
|
TRACE_SCOPE(tracing::BloomBrightPass);
|
|
|
|
GL_state.BindFrameBuffer(Bloom_framebuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Bloom_textures[0], 0);
|
|
|
|
// width and height are 1/2 for the bright pass
|
|
width = Post_texture_width >> 1;
|
|
height = Post_texture_height >> 1;
|
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BRIGHTPASS, 0));
|
|
|
|
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
|
|
}
|
|
// ------ end bright pass ------
|
|
|
|
// ------ begin blur pass ------
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Bloom_textures[0]);
|
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
for ( int iteration = 0; iteration < 2; iteration++) {
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
GR_DEBUG_SCOPE("Bloom iteration step");
|
|
TRACE_SCOPE(tracing::BloomIterationStep);
|
|
|
|
GLuint source_tex = Bloom_textures[pass];
|
|
GLuint dest_tex = Bloom_textures[1 - pass];
|
|
|
|
if (pass) {
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLUR, SDR_FLAG_BLUR_HORIZONTAL));
|
|
} else {
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLUR, SDR_FLAG_BLUR_VERTICAL));
|
|
}
|
|
|
|
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, source_tex);
|
|
|
|
for (int mipmap = 0; mipmap < MAX_MIP_BLUR_LEVELS; ++mipmap) {
|
|
int bloom_width = width >> mipmap;
|
|
int bloom_height = height >> mipmap;
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::blur_data>(
|
|
[&](graphics::generic_data::blur_data* data) {
|
|
data->texSize = (pass) ? 1.0f / i2fl(bloom_width) : 1.0f / i2fl(bloom_height);
|
|
data->level = mipmap;
|
|
});
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dest_tex, mipmap);
|
|
|
|
glViewport(0, 0, bloom_width, bloom_height);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// composite blur to the color texture
|
|
{
|
|
GR_DEBUG_SCOPE("Bloom composite step");
|
|
TRACE_SCOPE(tracing::BloomCompositeStep);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_color_texture, 0);
|
|
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLOOM_COMP, 0));
|
|
|
|
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::bloom_composition_data>(
|
|
[](graphics::generic_data::bloom_composition_data* data) {
|
|
data->levels = MAX_MIP_BLUR_LEVELS;
|
|
data->bloom_intensity = gr_bloom_intensity() / 100.0f;
|
|
});
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Bloom_textures[0]);
|
|
|
|
GL_state.SetAlphaBlendMode(ALPHA_BLEND_ADDITIVE);
|
|
|
|
glViewport(0, 0, gr_screen.max_w, gr_screen.max_h);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
|
|
|
|
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
|
|
}
|
|
|
|
// ------ end blur pass --------
|
|
|
|
// reset viewport, scissor test and exit
|
|
GL_state.ScissorTest(scissor_test);
|
|
}
|
|
|
|
void gr_opengl_post_process_begin()
|
|
{
|
|
if ( !Post_initialized ) {
|
|
return;
|
|
}
|
|
|
|
if (Post_in_frame) {
|
|
return;
|
|
}
|
|
|
|
if (PostProcessing_override) {
|
|
return;
|
|
}
|
|
|
|
GL_state.PushFramebufferState();
|
|
GL_state.BindFrameBuffer(Post_framebuffer_id[0]);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
Post_in_frame = true;
|
|
}
|
|
|
|
SCP_vector<shader_type> get_aa_shader_types(AntiAliasMode aa_mode) {
|
|
auto stypes = SCP_vector<shader_type>();
|
|
|
|
if (gr_is_fxaa_mode(aa_mode)) {
|
|
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_FXAA);
|
|
}
|
|
else if (gr_is_smaa_mode(aa_mode)) {
|
|
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT);
|
|
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_EDGE);
|
|
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING);
|
|
}
|
|
|
|
return stypes;
|
|
}
|
|
|
|
void recompile_aa_shader() {
|
|
mprintf(("Recompiling AA shader(s)...\n"));
|
|
|
|
for (auto sdr : get_aa_shader_types(Gr_aa_mode_last_frame)) {
|
|
opengl_delete_shader( gr_opengl_maybe_create_shader(sdr, 0) );
|
|
}
|
|
|
|
for (auto sdr : get_aa_shader_types(Gr_aa_mode)) {
|
|
gr_opengl_maybe_create_shader(sdr, 0);
|
|
}
|
|
|
|
Gr_aa_mode_last_frame = Gr_aa_mode;
|
|
}
|
|
|
|
void opengl_post_pass_fxaa()
|
|
{
|
|
GR_DEBUG_SCOPE("FXAA");
|
|
TRACE_SCOPE(tracing::FXAA);
|
|
|
|
// If the preset changed, recompile the shader
|
|
if (Gr_aa_mode_last_frame != Gr_aa_mode) {
|
|
recompile_aa_shader();
|
|
}
|
|
|
|
// We only want to draw to ATTACHMENT0
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
GL_state.ColorMask(true, true, true, true);
|
|
|
|
// Do a prepass to convert the main shaders' RGBA output into RGBL
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA_PREPASS, 0));
|
|
|
|
// basic/default uniforms
|
|
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_luminance_texture, 0);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_ldr_texture);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
|
|
// set and configure post shader ..
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA, 0));
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_ldr_texture, 0);
|
|
|
|
// basic/default uniforms
|
|
Current_shader->program->Uniforms.setTextureUniform("tex0", 0);
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::fxaa_data>([](graphics::generic_data::fxaa_data* data) {
|
|
data->rt_w = i2fl(Post_texture_width);
|
|
data->rt_h = i2fl(Post_texture_height);
|
|
});
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_luminance_texture);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
|
|
opengl_shader_set_current();
|
|
}
|
|
|
|
static void smaa_detect_edges()
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Detect Edges");
|
|
TRACE_SCOPE(tracing::SMAAEdgeDetection);
|
|
|
|
GL_state.BindFrameBuffer(Smaa_edge_detection_fb);
|
|
|
|
GLfloat clearValues[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
glClearBufferfv(GL_COLOR, 0, clearValues);
|
|
|
|
// Do the edge detection step
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_SMAA_EDGE, 0));
|
|
|
|
// basic/default uniforms
|
|
Current_shader->program->Uniforms.setTextureUniform("colorTex", 0);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_ldr_texture);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
}
|
|
|
|
static void smaa_calculate_blending_weights()
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Blending Weights calculation");
|
|
TRACE_SCOPE(tracing::SMAACalculateBlendingWeights);
|
|
|
|
GL_state.BindFrameBuffer(Smaa_blending_weight_fb);
|
|
|
|
GLfloat clearValues[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
glClearBufferfv(GL_COLOR, 0, clearValues);
|
|
|
|
// Do the edge detection step
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT, 0));
|
|
|
|
// basic/default uniforms
|
|
Current_shader->program->Uniforms.setTextureUniform("edgesTex", 0);
|
|
Current_shader->program->Uniforms.setTextureUniform("areaTex", 1);
|
|
Current_shader->program->Uniforms.setTextureUniform("searchTex", 2);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Smaa_edges_tex);
|
|
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Smaa_area_tex);
|
|
GL_state.Texture.Enable(2, GL_TEXTURE_2D, Smaa_search_tex);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
}
|
|
|
|
static void smaa_neighborhood_blending()
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Neighborhood Blending");
|
|
TRACE_SCOPE(tracing::SMAANeighborhoodBlending);
|
|
|
|
GL_state.BindFrameBuffer(Smaa_neighborhood_blending_fb);
|
|
|
|
GLfloat clearValues[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
glClearBufferfv(GL_COLOR, 0, clearValues);
|
|
|
|
// Do the edge detection step
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, 0));
|
|
|
|
// basic/default uniforms
|
|
Current_shader->program->Uniforms.setTextureUniform("colorTex", 0);
|
|
Current_shader->program->Uniforms.setTextureUniform("blendTex", 1);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_ldr_texture);
|
|
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Smaa_blend_tex);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
}
|
|
|
|
void smaa_resolve()
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Resolve");
|
|
TRACE_SCOPE(tracing::SMAAResolve);
|
|
|
|
opengl_shader_set_passthrough(true, false);
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Smaa_output_tex);
|
|
|
|
// Copy SMAA output back to the original framebuffer
|
|
if (GL_rendering_to_texture) {
|
|
opengl_draw_textured_quad(0.0f, 0.0f, 0.0f, 0.0f, (float)gr_screen.max_w, (float)gr_screen.max_h,
|
|
Scene_texture_u_scale, Scene_texture_v_scale);
|
|
} else {
|
|
opengl_draw_textured_quad(0.0f, 0.0f, 0.0f, Scene_texture_v_scale, (float)gr_screen.max_w,
|
|
(float)gr_screen.max_h, Scene_texture_u_scale, 0.0f);
|
|
}
|
|
}
|
|
|
|
void opengl_post_pass_smaa()
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA");
|
|
TRACE_SCOPE(tracing::SMAA);
|
|
|
|
// If the preset changed, recompile the shader
|
|
if (Gr_aa_mode_last_frame != Gr_aa_mode) {
|
|
recompile_aa_shader();
|
|
}
|
|
|
|
GL_state.PushFramebufferState();
|
|
|
|
GL_state.ColorMask(true, true, true, true);
|
|
|
|
// All SMAA stages share the same shader data so we only need this once
|
|
opengl_set_generic_uniform_data<graphics::generic_data::smaa_data>([](graphics::generic_data::smaa_data* data) {
|
|
data->smaa_rt_metrics.x = i2fl(Post_texture_width);
|
|
data->smaa_rt_metrics.y = i2fl(Post_texture_height);
|
|
});
|
|
|
|
smaa_detect_edges();
|
|
|
|
smaa_calculate_blending_weights();
|
|
|
|
smaa_neighborhood_blending();
|
|
|
|
GL_state.PopFramebufferState();
|
|
|
|
smaa_resolve();
|
|
}
|
|
|
|
extern GLuint Shadow_map_depth_texture;
|
|
extern GLuint Scene_depth_texture;
|
|
extern GLuint Cockpit_depth_texture;
|
|
extern GLuint Scene_position_texture;
|
|
extern GLuint Scene_normal_texture;
|
|
extern GLuint Scene_specular_texture;
|
|
extern float Sun_spot;
|
|
void opengl_post_lightshafts()
|
|
{
|
|
GR_DEBUG_SCOPE("Lightshafts");
|
|
TRACE_SCOPE(tracing::Lightshafts);
|
|
|
|
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_LIGHTSHAFTS, 0));
|
|
|
|
float x, y;
|
|
|
|
// should we even be here?
|
|
if ( !Game_subspace_effect && gr_sunglare_enabled() && gr_lightshafts_enabled() ) {
|
|
int n_lights = light_get_global_count();
|
|
|
|
for ( int idx = 0; idx<n_lights; idx++ ) {
|
|
vec3d light_dir;
|
|
vec3d local_light_dir;
|
|
light_get_global_dir(&light_dir, idx);
|
|
vm_vec_rotate(&local_light_dir, &light_dir, &Eye_matrix);
|
|
|
|
if ( !light_has_glare(idx) ) {
|
|
continue;
|
|
}
|
|
|
|
float dot;
|
|
if ( (dot = vm_vec_dot(&light_dir, &Eye_matrix.vec.fvec)) > 0.7f ) {
|
|
|
|
x = asinf_safe(vm_vec_dot(&light_dir, &Eye_matrix.vec.rvec)) / PI * 1.5f +
|
|
0.5f; // cant get the coordinates right but this works for the limited glare fov
|
|
y = asinf_safe(vm_vec_dot(&light_dir, &Eye_matrix.vec.uvec)) / PI * 1.5f * gr_screen.clip_aspect + 0.5f;
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::lightshaft_data>(
|
|
[&](graphics::generic_data::lightshaft_data* data) {
|
|
data->sun_pos.x = x;
|
|
data->sun_pos.y = y;
|
|
|
|
auto& ls_params = graphics::Post_processing_manager->getLightshaftParams();
|
|
data->density = ls_params.density;
|
|
data->falloff = ls_params.falloff;
|
|
data->weight = ls_params.weight;
|
|
data->intensity = Sun_spot * ls_params.intensity;
|
|
data->cp_intensity = Sun_spot * ls_params.cpintensity;
|
|
});
|
|
|
|
Current_shader->program->Uniforms.setTextureUniform("scene", 0);
|
|
Current_shader->program->Uniforms.setTextureUniform("cockpit", 1);
|
|
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_depth_texture);
|
|
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Cockpit_depth_texture);
|
|
GL_state.Blend(GL_TRUE);
|
|
GL_state.SetAlphaBlendMode(ALPHA_BLEND_ADDITIVE);
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
|
|
GL_state.Blend(GL_FALSE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void gr_opengl_post_process_end()
|
|
{
|
|
GR_DEBUG_SCOPE("Draw scene texture");
|
|
TRACE_SCOPE(tracing::DrawSceneTexture);
|
|
|
|
// state switch just the once (for bloom pass and final render-to-screen)
|
|
GLboolean depth = GL_state.DepthTest(GL_FALSE);
|
|
GLboolean depth_mask = GL_state.DepthMask(GL_FALSE);
|
|
GLboolean blend = GL_state.Blend(GL_FALSE);
|
|
GLboolean cull = GL_state.CullFace(GL_FALSE);
|
|
|
|
GL_state.Texture.SetShaderMode(GL_TRUE);
|
|
|
|
GL_state.PushFramebufferState();
|
|
|
|
// do bloom, hopefully ;)
|
|
opengl_post_pass_bloom();
|
|
|
|
// do tone mapping
|
|
opengl_post_pass_tonemap();
|
|
|
|
// Do Post processing AA
|
|
if (!GL_rendering_to_texture) {
|
|
if (gr_is_smaa_mode(Gr_aa_mode)) {
|
|
opengl_post_pass_smaa();
|
|
} else if (gr_is_fxaa_mode(Gr_aa_mode) && !fxaa_unavailable) {
|
|
opengl_post_pass_fxaa();
|
|
}
|
|
}
|
|
|
|
// render lightshafts
|
|
opengl_post_lightshafts();
|
|
|
|
GR_DEBUG_SCOPE("Draw post effects");
|
|
TRACE_SCOPE(tracing::DrawPostEffects);
|
|
|
|
// now write to the previous buffer
|
|
GL_state.PopFramebufferState();
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
// set and configure post shader ...
|
|
int flags = 0;
|
|
const auto& postEffects = graphics::Post_processing_manager->getPostEffects();
|
|
for ( int i = 0; i < (int)postEffects.size(); i++) {
|
|
if (postEffects[i].always_on) {
|
|
flags |= (1 << i);
|
|
}
|
|
}
|
|
|
|
int post_sdr_handle = Post_active_shader_index;
|
|
|
|
if (post_sdr_handle < 0) {
|
|
// no active shader index? use the always on shader.
|
|
post_sdr_handle = gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_MAIN, flags);
|
|
}
|
|
|
|
opengl_shader_set_current(post_sdr_handle);
|
|
|
|
// basic/default uniforms
|
|
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
|
|
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_ldr_texture);
|
|
|
|
Current_shader->program->Uniforms.setTextureUniform("depth_tex", 1);
|
|
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_depth_texture);
|
|
|
|
opengl_set_generic_uniform_data<graphics::generic_data::post_data>([&](graphics::generic_data::post_data* data) {
|
|
data->timer = i2fl(timer_get_milliseconds() % 100 + 1);
|
|
|
|
for (size_t idx = 0; idx < postEffects.size(); idx++) {
|
|
if (GL_shader[post_sdr_handle].flags & (1 << idx)) {
|
|
float value = postEffects[idx].intensity;
|
|
|
|
switch (postEffects[idx].uniform_type) {
|
|
case graphics::PostEffectUniformType::Invalid:
|
|
// Invalid name specified, do nothing
|
|
break;
|
|
case graphics::PostEffectUniformType::NoiseAmount:
|
|
data->noise_amount = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::Saturation:
|
|
data->saturation = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::Brightness:
|
|
data->brightness = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::Contrast:
|
|
data->contrast = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::FilmGrain:
|
|
data->film_grain = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::TvStripes:
|
|
data->tv_stripes = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::Cutoff:
|
|
data->cutoff = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::Dither:
|
|
data->dither = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::Tint:
|
|
data->tint = postEffects[idx].rgb;
|
|
break;
|
|
case graphics::PostEffectUniformType::CustomEffectVEC3A:
|
|
data->custom_effect_vec3_a = postEffects[idx].rgb;
|
|
break;
|
|
case graphics::PostEffectUniformType::CustomEffectFloatA:
|
|
data->custom_effect_float_a = value;
|
|
break;
|
|
case graphics::PostEffectUniformType::CustomEffectVEC3B:
|
|
data->custom_effect_vec3_b = postEffects[idx].rgb;
|
|
break;
|
|
case graphics::PostEffectUniformType::CustomEffectFloatB:
|
|
data->custom_effect_float_b = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// now render it to the screen ...
|
|
GL_state.PopFramebufferState();
|
|
|
|
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
|
|
//Shadow Map debug window
|
|
//#define SHADOW_DEBUG
|
|
#ifdef SHADOW_DEBUG
|
|
opengl_shader_set_current( &GL_post_shader[8] );
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
// GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D_ARRAY);
|
|
// GL_state.Texture.Enable(Shadow_map_depth_texture);
|
|
extern GLuint Post_shadow_texture_id;
|
|
GL_state.Texture.Enable(Shadow_map_depth_texture);
|
|
glUniform1iARB( opengl_shader_get_uniform("shadow_map"), 0);
|
|
glUniform1iARB( opengl_shader_get_uniform("index"), 0);
|
|
//opengl_draw_textured_quad(-1.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
//opengl_draw_textured_quad(-1.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.5f);
|
|
opengl_draw_textured_quad(-1.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 1.0f, 1.0f);
|
|
glUniform1iARB( opengl_shader_get_uniform("index"), 1);
|
|
//opengl_draw_textured_quad(-1.0f, -0.5f, 0.5f, 0.0f, -0.5f, 0.0f, 0.75f, 0.25f);
|
|
opengl_draw_textured_quad(-1.0f, -0.5f, 0.0f, 0.0f, -0.5f, 0.0f, 1.0f, 1.0f);
|
|
glUniform1iARB( opengl_shader_get_uniform("index"), 2);
|
|
opengl_draw_textured_quad(-0.5f, -1.0f, 0.0f, 0.0f, 0.0f, -0.5f, 1.0f, 1.0f);
|
|
glUniform1iARB( opengl_shader_get_uniform("index"), 3);
|
|
opengl_draw_textured_quad(-0.5f, -1.0f, 0.0f, 0.0f, 0.0f, -0.5f, 1.0f, 1.0f);
|
|
opengl_shader_set_current();
|
|
#endif
|
|
|
|
/*GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_depth_texture);
|
|
|
|
|
|
*/
|
|
// Done!
|
|
/*GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_effect_texture);
|
|
|
|
opengl_draw_textured_quad(0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_normal_texture);
|
|
|
|
opengl_draw_textured_quad(-1.0f, -0.0f, 0.0f, 0.0f, 0.0f, 1.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Scene_specular_texture);
|
|
|
|
opengl_draw_textured_quad(0.0f, -0.0f, 0.0f, 0.0f, 1.0f, 1.0f, Scene_texture_u_scale, Scene_texture_u_scale);
|
|
*/
|
|
|
|
GL_state.Texture.SetShaderMode(GL_FALSE);
|
|
|
|
// reset state
|
|
GL_state.DepthTest(depth);
|
|
GL_state.DepthMask(depth_mask);
|
|
GL_state.Blend(blend);
|
|
GL_state.CullFace(cull);
|
|
|
|
opengl_shader_set_current();
|
|
|
|
Post_in_frame = false;
|
|
}
|
|
|
|
void get_post_process_effect_names(SCP_vector<SCP_string> &names)
|
|
{
|
|
size_t idx;
|
|
|
|
const auto& postEffects = graphics::Post_processing_manager->getPostEffects();
|
|
for (idx = 0; idx < postEffects.size(); idx++) {
|
|
names.push_back(postEffects[idx].name);
|
|
}
|
|
}
|
|
|
|
void gr_opengl_post_process_set_effect(const char *name, int value, const vec3d *rgb)
|
|
{
|
|
if ( !Post_initialized ) {
|
|
return;
|
|
}
|
|
|
|
if (name == NULL) {
|
|
return;
|
|
}
|
|
|
|
size_t idx;
|
|
int sflags = 0;
|
|
|
|
auto& ls_params = graphics::Post_processing_manager->getLightshaftParams();
|
|
|
|
if(!stricmp("lightshafts",name))
|
|
{
|
|
ls_params.intensity = value / 100.0f;
|
|
ls_params.on = !!value;
|
|
return;
|
|
}
|
|
|
|
auto& postEffects = graphics::Post_processing_manager->getPostEffects();
|
|
for (idx = 0; idx < postEffects.size(); idx++) {
|
|
const char *eff_name = postEffects[idx].name.c_str();
|
|
|
|
if ( !stricmp(eff_name, name) ) {
|
|
postEffects[idx].intensity = (value / postEffects[idx].div) + postEffects[idx].add;
|
|
if ((rgb != nullptr) && !(vmd_zero_vector == *rgb)) {
|
|
postEffects[idx].rgb = *rgb;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// figure out new flags
|
|
for (idx = 0; idx < postEffects.size(); idx++) {
|
|
if ( postEffects[idx].always_on || (postEffects[idx].intensity != postEffects[idx].default_intensity) ) {
|
|
sflags |= (1<<idx);
|
|
}
|
|
}
|
|
|
|
Post_active_shader_index = gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_MAIN, sflags);
|
|
}
|
|
|
|
void gr_opengl_post_process_set_defaults()
|
|
{
|
|
size_t idx;
|
|
|
|
if ( !Post_initialized ) {
|
|
return;
|
|
}
|
|
|
|
auto& postEffects = graphics::Post_processing_manager->getPostEffects();
|
|
// reset all effects to their default values
|
|
for (idx = 0; idx < postEffects.size(); idx++) {
|
|
postEffects[idx].intensity = postEffects[idx].default_intensity;
|
|
}
|
|
|
|
Post_active_shader_index = -1;
|
|
}
|
|
|
|
void gr_opengl_post_process_save_zbuffer()
|
|
{
|
|
GR_DEBUG_SCOPE("Save z-Buffer");
|
|
if (Post_initialized)
|
|
{
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Cockpit_depth_texture, 0);
|
|
gr_zbuffer_clear(TRUE);
|
|
zbuffer_saved = true;
|
|
}
|
|
else
|
|
{
|
|
// If we can't save the z-buffer then just clear it so cockpits are still rendered correctly when
|
|
// post-processing isn't available/enabled.
|
|
gr_zbuffer_clear(TRUE);
|
|
}
|
|
}
|
|
void gr_opengl_post_process_restore_zbuffer()
|
|
{
|
|
GR_DEBUG_SCOPE("Restore z-Buffer");
|
|
|
|
if (zbuffer_saved) {
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, Scene_depth_texture, 0);
|
|
|
|
zbuffer_saved = false;
|
|
}
|
|
}
|
|
|
|
void set_smaa_defines(SCP_stringstream& sflags)
|
|
{
|
|
sflags << shader_get_smaa_defines(Gr_aa_mode, GLSL_version >= 400).c_str();
|
|
}
|
|
void opengl_post_shader_header(SCP_stringstream& sflags, shader_type shader_t, int flags)
|
|
{
|
|
const auto& postEffects = graphics::Post_processing_manager->getPostEffects();
|
|
if (shader_t == SDR_TYPE_POST_PROCESS_MAIN) {
|
|
for (size_t idx = 0; idx < postEffects.size(); idx++) {
|
|
if (flags & (1 << idx)) {
|
|
sflags << "#define ";
|
|
sflags << postEffects[idx].define_name.c_str();
|
|
sflags << "\n";
|
|
}
|
|
}
|
|
} else if (shader_t == SDR_TYPE_POST_PROCESS_LIGHTSHAFTS) {
|
|
char temp[64];
|
|
const auto& ls_params = graphics::Post_processing_manager->getLightshaftParams();
|
|
snprintf(temp, 64, "#define SAMPLE_NUM %d\n", ls_params.samplenum);
|
|
sflags << temp;
|
|
} else if (shader_t == SDR_TYPE_POST_PROCESS_FXAA) {
|
|
sflags << shader_get_fxaa_defines(Gr_aa_mode, GLSL_version >= 400);
|
|
} else if (shader_t == SDR_TYPE_POST_PROCESS_SMAA_EDGE || shader_t == SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT ||
|
|
shader_t == SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING) {
|
|
set_smaa_defines(sflags);
|
|
}
|
|
}
|
|
|
|
bool opengl_post_init_shaders()
|
|
{
|
|
int idx;
|
|
int flags = 0;
|
|
|
|
// Make sure this is set properly before we actually try to render anything with the post pipeline
|
|
Gr_aa_mode_last_frame = Gr_aa_mode;
|
|
|
|
const auto& postEffects = graphics::Post_processing_manager->getPostEffects();
|
|
// figure out which flags we need for the main post process shader
|
|
for (idx = 0; idx < (int)postEffects.size(); idx++) {
|
|
if (postEffects[idx].always_on) {
|
|
flags |= (1 << idx);
|
|
}
|
|
}
|
|
|
|
if ( gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_MAIN, flags) < 0 ) {
|
|
// only the main shader is actually required for post-processing
|
|
return false;
|
|
}
|
|
|
|
if ( gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BRIGHTPASS, 0) < 0 ||
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLUR, SDR_FLAG_BLUR_HORIZONTAL) < 0 ||
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLUR, SDR_FLAG_BLUR_VERTICAL) < 0 ||
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLOOM_COMP, 0) < 0) {
|
|
// disable bloom if we don't have those shaders available
|
|
graphics::Post_processing_manager->setBloomShadersOk(false);
|
|
}
|
|
|
|
if (gr_is_fxaa_mode(Gr_aa_mode))
|
|
{
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA, 0);
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA_PREPASS, 0);
|
|
}
|
|
|
|
if (gr_is_smaa_mode(Gr_aa_mode)) {
|
|
// Precompile the SMAA shaders if enabled
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_SMAA_EDGE, 0);
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT, 0);
|
|
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, 0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void opengl_setup_bloom_textures()
|
|
{
|
|
// two more framebuffers, one each for the two different sized bloom textures
|
|
glGenFramebuffers(1, &Bloom_framebuffer);
|
|
|
|
// need to generate textures for bloom too
|
|
glGenTextures(2, Bloom_textures);
|
|
|
|
// half size
|
|
int width = Post_texture_width >> 1;
|
|
int height = Post_texture_height >> 1;
|
|
|
|
for (int tex = 0; tex < 2; tex++) {
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Bloom_textures[tex]);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, MAX_MIP_BLUR_LEVELS-1);
|
|
}
|
|
|
|
GL_state.BindFrameBuffer(0);
|
|
}
|
|
|
|
void setup_smaa_edges_resources()
|
|
{
|
|
glGenTextures(1, &Smaa_edges_tex);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Smaa_edges_tex);
|
|
|
|
opengl_set_object_label(GL_TEXTURE, Smaa_edges_tex, "SMAA Edge detection texture");
|
|
|
|
if (GLAD_GL_ARB_texture_storage) {
|
|
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, Post_texture_width, Post_texture_height);
|
|
} else {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Post_texture_width, Post_texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
|
|
nullptr);
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glGenFramebuffers(1, &Smaa_edge_detection_fb);
|
|
GL_state.BindFrameBuffer(Smaa_edge_detection_fb);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, Smaa_edge_detection_fb, "SMAA Edge detection framebuffer");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Smaa_edges_tex, 0);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
void setup_smaa_blending_weight_resources()
|
|
{
|
|
glGenTextures(1, &Smaa_blend_tex);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Smaa_blend_tex);
|
|
|
|
opengl_set_object_label(GL_TEXTURE, Smaa_blend_tex, "SMAA Blending weight calculation texture");
|
|
|
|
if (GLAD_GL_ARB_texture_storage) {
|
|
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, Post_texture_width, Post_texture_height);
|
|
} else {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Post_texture_width, Post_texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
|
|
nullptr);
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glGenFramebuffers(1, &Smaa_blending_weight_fb);
|
|
GL_state.BindFrameBuffer(Smaa_blending_weight_fb);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, Smaa_blending_weight_fb, "SMAA Blending widght calculation framebuffer");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Smaa_blend_tex, 0);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
void setup_smaa_neighborhood_blending_resources()
|
|
{
|
|
glGenTextures(1, &Smaa_output_tex);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(Smaa_output_tex);
|
|
|
|
opengl_set_object_label(GL_TEXTURE, Smaa_output_tex, "SMAA output texture");
|
|
|
|
if (GLAD_GL_ARB_texture_storage) {
|
|
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, Post_texture_width, Post_texture_height);
|
|
} else {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Post_texture_width, Post_texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
|
|
nullptr);
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glGenFramebuffers(1, &Smaa_neighborhood_blending_fb);
|
|
GL_state.BindFrameBuffer(Smaa_neighborhood_blending_fb);
|
|
opengl_set_object_label(GL_FRAMEBUFFER, Smaa_neighborhood_blending_fb, "SMAA neighborhood blending framebuffer");
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Smaa_output_tex, 0);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
static GLuint load_smaa_texture(GLsizei width, GLsizei height, GLenum format, const uint8_t* pixels, const char* name)
|
|
{
|
|
GLuint tex;
|
|
glGenTextures(1, &tex);
|
|
|
|
GL_state.Texture.SetActiveUnit(0);
|
|
GL_state.Texture.SetTarget(GL_TEXTURE_2D);
|
|
GL_state.Texture.Enable(tex);
|
|
|
|
opengl_set_object_label(GL_TEXTURE, tex, name);
|
|
|
|
if (GLAD_GL_ARB_texture_storage) {
|
|
glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height);
|
|
} else {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, Post_texture_width, Post_texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
|
|
nullptr);
|
|
}
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format == GL_RG8 ? GL_RG : GL_RED, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
return tex;
|
|
}
|
|
|
|
static void setup_smaa_resources()
|
|
{
|
|
GL_state.PushFramebufferState();
|
|
|
|
Smaa_area_tex = load_smaa_texture(AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG8, areaTexBytes, "SMAA Area Texture");
|
|
Smaa_search_tex =
|
|
load_smaa_texture(SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_R8, searchTexBytes, "SMAA Search Texture");
|
|
|
|
setup_smaa_edges_resources();
|
|
|
|
setup_smaa_blending_weight_resources();
|
|
|
|
setup_smaa_neighborhood_blending_resources();
|
|
|
|
GL_state.PopFramebufferState();
|
|
}
|
|
|
|
// generate and test the framebuffer and textures that we are going to use
|
|
static bool opengl_post_init_framebuffer()
|
|
{
|
|
bool rval = false;
|
|
|
|
// clamp size, if needed
|
|
Post_texture_width = gr_screen.max_w;
|
|
Post_texture_height = gr_screen.max_h;
|
|
|
|
if (Post_texture_width > GL_max_renderbuffer_size) {
|
|
Post_texture_width = GL_max_renderbuffer_size;
|
|
}
|
|
|
|
if (Post_texture_height > GL_max_renderbuffer_size) {
|
|
Post_texture_height = GL_max_renderbuffer_size;
|
|
}
|
|
|
|
opengl_setup_bloom_textures();
|
|
|
|
// Always set up SMAA resources so the user can switch to an SMAA preset
|
|
// at runtime even when starting with a non-SMAA AA mode, such as None.
|
|
//if (Gr_aa_mode != AntiAliasMode::None) {
|
|
setup_smaa_resources();
|
|
//}
|
|
|
|
GL_state.BindFrameBuffer(0);
|
|
|
|
rval = true;
|
|
|
|
if ( opengl_check_for_errors("post_init_framebuffer()") ) {
|
|
rval = false;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
|
|
void opengl_post_process_shutdown_bloom()
|
|
{
|
|
if ( Bloom_textures[0] ) {
|
|
glDeleteTextures(1, &Bloom_textures[0]);
|
|
Bloom_textures[0] = 0;
|
|
}
|
|
|
|
if ( Bloom_textures[1] ) {
|
|
glDeleteTextures(1, &Bloom_textures[1]);
|
|
Bloom_textures[1] = 0;
|
|
}
|
|
|
|
if ( Bloom_framebuffer > 0 ) {
|
|
glDeleteFramebuffers(1, &Bloom_framebuffer);
|
|
Bloom_framebuffer = 0;
|
|
}
|
|
}
|
|
|
|
size_t opengl_get_postprocessing_render_target_bytes()
|
|
{
|
|
// The bytes for each pixel come from the internal formats at the creation sites in this file:
|
|
// GL_RGBA16F holds 8 bytes and GL_RGBA8 holds 4. Keep these values equal to those formats.
|
|
constexpr size_t RGBA16F_BYTES = 8;
|
|
constexpr size_t RGBA8_BYTES = 4;
|
|
|
|
size_t bytes = 0;
|
|
|
|
// The bloom textures are half the size of the post-processing textures. opengl_setup_bloom_textures()
|
|
// calls glGenerateMipmap() before it sets GL_TEXTURE_MAX_LEVEL, thus the driver holds the full
|
|
// mipmap chain, not only MAX_MIP_BLUR_LEVELS levels.
|
|
for (GLuint bloom_texture : Bloom_textures) {
|
|
if (bloom_texture == 0) {
|
|
continue;
|
|
}
|
|
|
|
auto width = static_cast<size_t>(std::max(1, Post_texture_width >> 1));
|
|
auto height = static_cast<size_t>(std::max(1, Post_texture_height >> 1));
|
|
while (true) {
|
|
bytes += width * height * RGBA16F_BYTES;
|
|
if ((width == 1) && (height == 1)) {
|
|
break;
|
|
}
|
|
width = std::max<size_t>(1, width >> 1);
|
|
height = std::max<size_t>(1, height >> 1);
|
|
}
|
|
}
|
|
|
|
// The SMAA area and search textures stay out of this total. They hold lookup data of a fixed
|
|
// small size, thus they are not render targets.
|
|
const auto post_w = static_cast<size_t>(Post_texture_width);
|
|
const auto post_h = static_cast<size_t>(Post_texture_height);
|
|
|
|
if (Smaa_edges_tex != 0) {
|
|
bytes += post_w * post_h * RGBA8_BYTES;
|
|
}
|
|
if (Smaa_blend_tex != 0) {
|
|
bytes += post_w * post_h * RGBA8_BYTES;
|
|
}
|
|
if (Smaa_output_tex != 0) {
|
|
bytes += post_w * post_h * RGBA8_BYTES;
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
void opengl_post_process_init()
|
|
{
|
|
// Externally used manager class to expose information about post processing externally
|
|
graphics::Post_processing_manager.reset(new graphics::PostProcessingManager());
|
|
|
|
Post_initialized = 0;
|
|
|
|
//We need to read the tbl first. This is mostly for FRED's benefit, as otherwise the list of post effects for the sexp doesn't get updated.
|
|
if ( !graphics::Post_processing_manager->parse_table() ) {
|
|
mprintf((" Unable to read post-processing table! Disabling post-processing...\n\n"));
|
|
Gr_post_processing_enabled = false;
|
|
return;
|
|
}
|
|
|
|
if ( !Gr_post_processing_enabled ) {
|
|
return;
|
|
}
|
|
|
|
if ( !Scene_texture_initialized ) {
|
|
return;
|
|
}
|
|
|
|
if ( Cmdline_no_fbo ) {
|
|
Gr_post_processing_enabled = false;
|
|
return;
|
|
}
|
|
|
|
if ( !opengl_post_init_shaders() ) {
|
|
mprintf((" Unable to initialize post-processing shaders! Disabling post-processing...\n\n"));
|
|
Gr_post_processing_enabled = false;
|
|
return;
|
|
}
|
|
|
|
if ( !opengl_post_init_framebuffer() ) {
|
|
mprintf((" Unable to initialize post-processing framebuffer! Disabling post-processing...\n\n"));
|
|
Gr_post_processing_enabled = false;
|
|
return;
|
|
}
|
|
|
|
Post_initialized = 1;
|
|
}
|
|
|
|
void opengl_post_process_shutdown()
|
|
{
|
|
if ( !Post_initialized ) {
|
|
return;
|
|
}
|
|
|
|
if (Post_framebuffer_id[0]) {
|
|
glDeleteFramebuffers(1, &Post_framebuffer_id[0]);
|
|
Post_framebuffer_id[0] = 0;
|
|
|
|
if (Post_framebuffer_id[1]) {
|
|
glDeleteFramebuffers(1, &Post_framebuffer_id[1]);
|
|
Post_framebuffer_id[1] = 0;
|
|
}
|
|
}
|
|
|
|
graphics::Post_processing_manager->clear();
|
|
graphics::Post_processing_manager = nullptr;
|
|
|
|
opengl_post_process_shutdown_bloom();
|
|
|
|
Post_in_frame = false;
|
|
Post_active_shader_index = 0;
|
|
|
|
Post_initialized = 0;
|
|
}
|