Files
Fabian Woltermann 115491f8a9 Refactor RT shadow handling and Vulkan depth operations (#7685)
- Introduced `shadow_ray_params` to simplify ray origin and cull mask management.
- Shifted shadow ray origins to TLAS-relative space for improved precision and consistency, especially for cockpit rendering.
- Refactored cascade bind functions, adding `shadow_cascade_params_bind_deferred()` for consolidated offset/count handling in deferred lighting.
- Expanded TLAS to include cockpits and additional object types, ensuring parity with rasterized shadows.
- Introduced per-instance cull masks to address cockpit self-shadowing issues.
- Improved Vulkan depth isolation for cockpit rendering via backup/restore mechanism, ensuring correct depth sampling in later passes.
- Extracted reusable helper functions for cockpit rendering state logic.
2026-09-21 05:12:55 +02:00

819 lines
27 KiB
C++

#include "gropengldeferred.h"
#include "graphics/util/primitives.h"
#include "ShaderProgram.h"
#include "gropengldraw.h"
#include "gropenglstate.h"
#include "gropengltnl.h"
#include "graphics/2d.h"
#include "graphics/light.h"
#include "graphics/matrix.h"
#include "graphics/shadows.h"
#include "graphics/util/UniformAligner.h"
#include "graphics/util/UniformBuffer.h"
#include "graphics/util/uniform_structs.h"
#include "lighting/lighting.h"
#include "lighting/lighting_profiles.h"
#include "mission/mission_flags.h"
#include "mission/missionparse.h"
#include "nebula/neb.h"
#include "nebula/volumetrics.h"
#include "mod_table/mod_table.h"
#include "render/3d.h"
#include "tracing/tracing.h"
#ifdef USE_OPENGL_ES
#include "es_compatibility.h"
#endif
#include <math/bitarray.h>
void gr_opengl_deferred_init()
{
gr_opengl_deferred_light_cylinder_init(16);
gr_opengl_deferred_light_sphere_init(16, 16);
}
void gr_opengl_deferred_shutdown() {}
void opengl_clear_deferred_buffers()
{
GR_DEBUG_SCOPE("Clear deferred buffers");
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.ColorMask(true, true, true, true);
opengl_shader_set_current( gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_CLEAR, 0) );
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
opengl_shader_set_current();
GL_state.ColorMask(true, true, true, false);
GL_state.DepthTest(depth);
GL_state.DepthMask(depth_mask);
GL_state.Blend(blend);
GL_state.CullFace(cull);
}
void gr_opengl_deferred_lighting_begin(bool clearNonColorBufs)
{
if (!light_deferred_enabled())
return;
static const float black[] = {0, 0, 0, 1.0f};
GR_DEBUG_SCOPE("Deferred lighting begin");
Deferred_lighting = true;
GL_state.ColorMask(true, true, true, true);
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
if (Cmdline_msaa_enabled > 0) {
//Ensure MSAA Mode if necessary
GL_state.BindFrameBuffer(Scene_framebuffer_ms);
glDrawBuffer(GL_COLOR_ATTACHMENT4);
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_COPY, 0));
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture);
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
GL_state.SetAlphaBlendMode(gr_alpha_blend::ALPHA_BLEND_NONE);
GL_state.SetZbufferType(ZBUFFER_TYPE_NONE);
opengl_draw_full_screen_textured(0, 0, 1, 1);
} else {
// Copy the existing color data into the emissive part of the G-buffer since everything that already existed is
// treated as emissive
glDrawBuffer(GL_COLOR_ATTACHMENT4);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, gr_screen.max_w, gr_screen.max_h, 0, 0, gr_screen.max_w, gr_screen.max_h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
glDrawBuffers(6, buffers);
glClearBufferfv(GL_COLOR, 0, black);
if (clearNonColorBufs) {
glClearBufferfv(GL_COLOR, 1, black);
glClearBufferfv(GL_COLOR, 2, black);
glClearBufferfv(GL_COLOR, 3, black);
glClearBufferfv(GL_COLOR, 5, black);
}
}
void gr_opengl_deferred_lighting_msaa()
{
if (!Deferred_lighting)
return;
if (Cmdline_msaa_enabled <= 0)
return;
GR_DEBUG_SCOPE("MSAA Pass");
GL_state.BindFrameBuffer(Scene_framebuffer);
GLenum buffers[] = {GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3,
GL_COLOR_ATTACHMENT4};
glDrawBuffers(5, buffers);
int msaa_resolve_flags = 0;
switch (Cmdline_msaa_enabled) {
case 4:
msaa_resolve_flags = SDR_FLAG_MSAA_SAMPLES_4;
break;
case 8:
msaa_resolve_flags = SDR_FLAG_MSAA_SAMPLES_8;
break;
case 16:
msaa_resolve_flags = SDR_FLAG_MSAA_SAMPLES_16;
break;
default:
Error(LOCATION, "Disallowed MSAA shader sample count %d!", Cmdline_msaa_enabled);
break;
}
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_MSAA_RESOLVE, msaa_resolve_flags));
GL_state.Texture.Enable(0, GL_TEXTURE_2D_MULTISAMPLE, Scene_color_texture_ms);
GL_state.Texture.Enable(1, GL_TEXTURE_2D_MULTISAMPLE, Scene_position_texture_ms);
GL_state.Texture.Enable(2, GL_TEXTURE_2D_MULTISAMPLE, Scene_normal_texture_ms);
GL_state.Texture.Enable(3, GL_TEXTURE_2D_MULTISAMPLE, Scene_specular_texture_ms);
GL_state.Texture.Enable(4, GL_TEXTURE_2D_MULTISAMPLE, Scene_emissive_texture_ms);
GL_state.Texture.Enable(5, GL_TEXTURE_2D_MULTISAMPLE, Scene_depth_texture_ms);
Current_shader->program->Uniforms.setTextureUniform("texColor", 0);
Current_shader->program->Uniforms.setTextureUniform("texPos", 1);
Current_shader->program->Uniforms.setTextureUniform("texNormal", 2);
Current_shader->program->Uniforms.setTextureUniform("texSpecular", 3);
Current_shader->program->Uniforms.setTextureUniform("texEmissive", 4);
Current_shader->program->Uniforms.setTextureUniform("texDepth", 5);
opengl_set_generic_uniform_data<graphics::generic_data::msaa_data>(
[&](graphics::generic_data::msaa_data* data) {
data->samples = Cmdline_msaa_enabled;
data->fov = g3_get_hfov(Proj_fov);
});
GL_state.SetAlphaBlendMode(gr_alpha_blend::ALPHA_BLEND_NONE);
GL_state.SetZbufferType(ZBUFFER_TYPE_WRITE);
opengl_draw_full_screen_textured(0, 0, 1, 1);
}
void gr_opengl_deferred_lighting_end()
{
if(!Deferred_lighting)
return;
GR_DEBUG_SCOPE("Deferred lighting end");
Deferred_lighting = false;
glDrawBuffer(GL_COLOR_ATTACHMENT0);
GL_state.ColorMask(true, true, true, false);
}
static GLuint deferred_light_cylinder_vbo = 0;
static GLuint deferred_light_cylinder_ibo = 0;
static GLushort deferred_light_cylinder_vcount = 0;
static GLuint deferred_light_cylinder_icount = 0;
static GLuint deferred_light_sphere_vbo = 0;
static GLuint deferred_light_sphere_ibo = 0;
static GLushort deferred_light_sphere_vcount = 0;
static GLuint deferred_light_sphere_icount = 0;
extern SCP_vector<light> Lights;
extern int Num_lights;
namespace ltp = lighting_profiles;
using namespace ltp;
static bool override_fog = false;
graphics::deferred_light_data*
// common conversion operations to translate a game light data structure into a render-ready light uniform.
prepare_light_uniforms(light& l, graphics::util::UniformAligner& uniformAligner, const ltp::profile* lp)
{
graphics::deferred_light_data* light_data = uniformAligner.addTypedElement<graphics::deferred_light_data>();
light_data->lightType = static_cast<int>(l.type);
float intensity =
(Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_intensity_modifier.handle(l.intensity) : l.intensity;
vec3d diffuse;
diffuse.xyz.x = l.r * intensity;
diffuse.xyz.y = l.g * intensity;
diffuse.xyz.z = l.b * intensity;
light_data->diffuseLightColor = diffuse;
// Set a default value for all lights. Only the first directional light will change this.
light_data->enable_shadows = false;
light_data->sourceRadius = l.source_radius;
return light_data;
}
void gr_opengl_deferred_lighting_finish()
{
GR_DEBUG_SCOPE("Deferred lighting finish");
TRACE_SCOPE(tracing::ApplyLights);
if (!light_deferred_enabled()) {
return;
}
GL_state.SetAlphaBlendMode(ALPHA_BLEND_ADDITIVE);
gr_zbuffer_set(GR_ZBUFF_NONE);
// GL_state.DepthFunc(GL_GREATER);
// GL_state.DepthMask(GL_FALSE);
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_DEFERRED_LIGHTING, ENVMAP > 0 ? SDR_FLAG_ENV_MAP : 0));
// Render on top of the composite buffer texture
glDrawBuffer(GL_COLOR_ATTACHMENT5);
glReadBuffer(GL_COLOR_ATTACHMENT4);
glBlitFramebuffer(0,
0,
gr_screen.max_w,
gr_screen.max_h,
0,
0,
gr_screen.max_w,
gr_screen.max_h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_color_texture);
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_normal_texture);
GL_state.Texture.Enable(2, GL_TEXTURE_2D, Scene_position_texture);
GL_state.Texture.Enable(3, GL_TEXTURE_2D, Scene_specular_texture);
if (Shadow_quality != ShadowQuality::Disabled) {
GL_state.Texture.Enable(4, GL_TEXTURE_2D_ARRAY, Shadow_map_depth_texture);
}
if (ENVMAP > 0) {
Current_shader->program->Uniforms.setTextureUniform("sEnvmap", 5);
Current_shader->program->Uniforms.setTextureUniform("sIrrmap", 6);
float u_scale, v_scale;
uint32_t array_index;
gr_opengl_tcache_set(ENVMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 5);
gr_opengl_tcache_set(IRRMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, 6);
Assertion(array_index == 0, "Cube map arrays are not supported yet!");
}
// We need to use stable sorting here to make sure that the relative ordering of the same light types is the same as
// the rest of the code. Otherwise the shadow mapping would be applied while rendering the wrong light which would
// lead to flickering lights in some circumstances
std::stable_sort(Lights.begin(), Lights.end(), light_compare_by_type);
using namespace graphics;
// We need to precompute how many elements we are going to need
size_t num_data_elements = Lights.size() + 1;
// Get a uniform buffer for our data
auto light_buffer = gr_get_uniform_buffer(uniform_block_type::Lights, num_data_elements);
auto& light_uniform_aligner = light_buffer.aligner();
auto matrix_buffer = gr_get_uniform_buffer(uniform_block_type::Matrices, num_data_elements);
auto& matrix_uniform_aligner = matrix_buffer.aligner();
// This is the light which is responsible for shadows and volumetric nebula lighting
const light* global_light = nullptr;
vec3d global_light_diffuse;
// To allow reduced bind calls, we sort lights into subsets based on rendering methods.
// It might seem optimal to create these subsets as lights are added, or some other method,
// but this keeps the graphics implementation methods better contained and profiling currently
// (dec 2023) shows negligable cost of doing it this way.
SCP_vector<light> full_frame_lights = SCP_vector<light>();
SCP_vector<light> sphere_lights = SCP_vector<light>();
SCP_vector<light> cylinder_lights = SCP_vector<light>();
for (auto& l : Lights) {
switch (l.type) {
case Light_Type::Directional:
full_frame_lights.push_back(l);
break;
case Light_Type::Cone:
FALLTHROUGH;
case Light_Type::Point:
sphere_lights.push_back(l);
break;
case Light_Type::Tube:
cylinder_lights.push_back(l);
break;
case Light_Type::Ambient:
Warning(LOCATION, "Multiple ambient lights are not supported!");
}
}
{
GR_DEBUG_SCOPE("Build buffer data");
auto lp = ltp::current();
auto header = light_uniform_aligner.getHeader<deferred_global_data>();
if (Shadow_quality != ShadowQuality::Disabled) {
vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render);
shadow_cascade_params_bind_deferred();
}
header->invScreenWidth = 1.0f / gr_screen.max_w;
header->invScreenHeight = 1.0f / gr_screen.max_h;
header->nearPlane = gr_near_plane;
{
//Prepare ambient light
light& l = full_frame_lights.emplace_back();
vec3d ambient;
gr_get_ambient_light(&ambient);
l.r = ambient.xyz.x;
l.g = ambient.xyz.y;
l.b = ambient.xyz.z;
l.type = Light_Type::Ambient;
l.intensity = 1.f;
l.source_radius = 0.f;
}
// Only the first directional light uses shaders so we need to know when we already saw that light
bool first_directional = true;
for (auto& l : full_frame_lights) {
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
if (l.type == Light_Type::Directional ) {
if (Shadow_quality != ShadowQuality::Disabled) {
light_data->enable_shadows = first_directional ? 1 : 0;
}
// Global light direction should match shadow light direction
if (first_directional) {
global_light = &l;
global_light_diffuse = light_data->diffuseLightColor;
first_directional = false;
}
vec4 light_dir;
light_dir.xyzw.x = -l.vec.xyz.x;
light_dir.xyzw.y = -l.vec.xyz.y;
light_dir.xyzw.z = -l.vec.xyz.z;
light_dir.xyzw.w = 0.0f;
vec4 view_dir;
vm_vec_transform(&view_dir, &light_dir, &gr_view_matrix);
light_data->lightDir.xyz.x = view_dir.xyzw.x;
light_data->lightDir.xyz.y = view_dir.xyzw.y;
light_data->lightDir.xyz.z = view_dir.xyzw.z;
}
}
for (auto& l : sphere_lights) {
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
if (l.type == Light_Type::Cone) {
light_data->dualCone = (l.flags & LF_DUAL_CONE) ? 1 : 0;
light_data->coneAngle = l.cone_angle;
light_data->coneInnerAngle = l.cone_inner_angle;
light_data->coneDir = l.vec2;
}
float rad = (Lighting_mode == lighting_mode::COCKPIT)
? lp->cockpit_light_radius_modifier.handle(MAX(l.rada, l.radb))
: MAX(l.rada, l.radb);
light_data->lightRadius = rad;
// A small padding factor is added to guard against potentially clipping the edges of the light with facets
// of the volume mesh.
light_data->scale.xyz.x = rad * 1.05f;
light_data->scale.xyz.y = rad * 1.05f;
light_data->scale.xyz.z = rad * 1.05f;
}
for (auto& l : cylinder_lights) {
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
float rad =
(Lighting_mode == lighting_mode::COCKPIT) ? lp->cockpit_light_radius_modifier.handle(l.radb) : l.radb;
light_data->lightRadius = rad;
light_data->lightType = LT_TUBE;
vec3d a;
vm_vec_sub(&a, &l.vec, &l.vec2);
auto length = vm_vec_mag(&a);
// Tube light volumes must be extended past the length of their requested light vector
// to allow smooth fall-off from all angles. Since the light volume starts at the mesh
// origin we must extend it here. Later the position will be adjusted as well.
length += light_data->lightRadius * 2.0f;
// A small padding factor is added to guard against potentially clipping the edges of the light with facets
// of the volume mesh.
light_data->scale.xyz.x = rad * 1.05f;
light_data->scale.xyz.y = rad * 1.05f;
light_data->scale.xyz.z = length;
}
// Uniform data has been assembled, upload it to the GPU and issue the draw calls
light_buffer.submitData();
}
{
for (size_t i = 0; i<full_frame_lights.size(); i++) {
// just keeping things aligned really.
auto matrix_data = matrix_uniform_aligner.addTypedElement<graphics::matrix_uniforms>();
matrix_data->modelViewMatrix = gr_env_texture_matrix;
}
for (auto& l : sphere_lights) {
auto matrix_data = matrix_uniform_aligner.addTypedElement<graphics::matrix_uniforms>();
g3_start_instance_matrix(&l.vec, &vmd_identity_matrix, true);
matrix_data->modelViewMatrix = gr_model_view_matrix;
matrix_data->projMatrix = gr_projection_matrix;
g3_done_instance(true);
}
for (auto& l : cylinder_lights ) {
auto matrix_data = matrix_uniform_aligner.addTypedElement<graphics::matrix_uniforms>();
vec3d dir, newPos;
matrix orient;
vm_vec_normalized_dir(&dir, &l.vec, &l.vec2);
vm_vector_2_matrix_norm(&orient, &dir, nullptr, nullptr);
// Tube light volumes must be extended past the length of their requested light vector
// to allow smooth fall-off from all angles. Since the light volume starts at the mesh
// origin we must extend it, which has been done above, and then move it backwards one radius.
vm_vec_scale_sub(&newPos, &l.vec2, &dir, l.radb);
g3_start_instance_matrix(&newPos, &orient, true);
matrix_data->modelViewMatrix = gr_model_view_matrix;
matrix_data->projMatrix = gr_projection_matrix;
g3_done_instance(true);
}
matrix_buffer.submitData();
}
{
GR_DEBUG_SCOPE("Render light geometry");
gr_bind_uniform_buffer(uniform_block_type::DeferredGlobals,
light_buffer.getBufferOffset(0),
sizeof(graphics::deferred_global_data),
light_buffer.bufferHandle());
gr_bind_uniform_buffer(uniform_block_type::Matrices,
matrix_buffer.getBufferOffset(0),
sizeof(graphics::matrix_uniforms),
matrix_buffer.bufferHandle());
size_t element_index = 0;
vertex_layout vertex_declare;
vertex_declare.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0);
for (size_t i = 0; i<full_frame_lights.size(); i++) {
GR_DEBUG_SCOPE("Deferred apply single dir light");
gr_bind_uniform_buffer(uniform_block_type::Lights,
light_buffer.getAlignerElementOffset(element_index),
sizeof(graphics::deferred_light_data),
light_buffer.bufferHandle());
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
++element_index;
}
if (!sphere_lights.empty()) {
opengl_bind_vertex_layout(vertex_declare, deferred_light_sphere_vbo, deferred_light_sphere_ibo);
}
for (size_t i = 0; i<sphere_lights.size(); i++) {
gr_bind_uniform_buffer(uniform_block_type::Lights,
light_buffer.getAlignerElementOffset(element_index),
sizeof(graphics::deferred_light_data),
light_buffer.bufferHandle());
gr_bind_uniform_buffer(uniform_block_type::Matrices,
matrix_buffer.getAlignerElementOffset(element_index),
sizeof(graphics::matrix_uniforms),
matrix_buffer.bufferHandle());
glDrawRangeElements(GL_TRIANGLES,
0,
deferred_light_sphere_vcount,
deferred_light_sphere_icount,
GL_UNSIGNED_SHORT,
0);
opengl_draw_sphere();
++element_index;
}
if (!cylinder_lights.empty()) {
opengl_bind_vertex_layout(vertex_declare, deferred_light_cylinder_vbo, deferred_light_cylinder_ibo);
}
for (size_t i = 0; i<cylinder_lights.size(); i++) {
gr_bind_uniform_buffer(uniform_block_type::Lights,
light_buffer.getAlignerElementOffset(element_index),
sizeof(graphics::deferred_light_data),
light_buffer.bufferHandle());
gr_bind_uniform_buffer(uniform_block_type::Matrices,
matrix_buffer.getAlignerElementOffset(element_index),
sizeof(graphics::matrix_uniforms),
matrix_buffer.bufferHandle());
glDrawRangeElements(GL_TRIANGLES,
0,
deferred_light_cylinder_vcount,
deferred_light_cylinder_icount,
GL_UNSIGNED_SHORT,
0);
++element_index;
}
}
gr_end_view_matrix();
gr_end_proj_matrix();
// Now reset back to drawing into the color buffer
glDrawBuffer(GL_COLOR_ATTACHMENT0);
bool bDrawFullNeb = The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb2_render_mode != NEB2_RENDER_NONE && !override_fog;
bool bDrawNebVolumetrics = The_mission.volumetrics && The_mission.volumetrics->get_enabled() && !override_fog;
if (bDrawFullNeb) {
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
gr_zbuffer_set(GR_ZBUFF_NONE);
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_SCENE_FOG, 0));
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_composite_texture);
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_depth_texture);
float fog_near, fog_density;
neb2_get_adjusted_fog_values(&fog_near, &fog_density);
unsigned char r, g, b;
neb2_get_fog_color(&r, &g, &b);
Current_shader->program->Uniforms.setTextureUniform("tex", 0);
Current_shader->program->Uniforms.setTextureUniform("depth_tex", 1);
opengl_set_generic_uniform_data<graphics::generic_data::fog_data>([&](graphics::generic_data::fog_data* data) {
data->fog_start = fog_near;
data->fog_density = fog_density;
data->fog_color.xyz.x = r / 255.f;
data->fog_color.xyz.y = g / 255.f;
data->fog_color.xyz.z = b / 255.f;
data->zNear = Min_draw_distance;
data->zFar = Max_draw_distance;
data->clip_inf_dist = Neb2_fog_skybox_clip_distance;
data->clip_dist = Neb2_fog_clip_distance;
});
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
if (bDrawNebVolumetrics) {
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT5);
glBlitFramebuffer(0,
0,
gr_screen.max_w,
gr_screen.max_h,
0,
0,
gr_screen.max_w,
gr_screen.max_h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_COLOR_ATTACHMENT0);
}
}
if (bDrawNebVolumetrics) {
GR_DEBUG_SCOPE("Volumetric Nebulae");
TRACE_SCOPE(tracing::Volumetrics);
const volumetric_nebula& neb = *The_mission.volumetrics;
Assertion(neb.isVolumeBitmapValid(), "The volumetric nebula was not properly initialized!");
gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
gr_set_view_matrix(&Eye_position, &Eye_matrix);
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
gr_zbuffer_set(GR_ZBUFF_NONE);
opengl_shader_set_current(gr_opengl_maybe_create_shader(SDR_TYPE_VOLUMETRIC_FOG,
(neb.getEdgeSmoothing() ? SDR_FLAG_VOLUMETRICS_DO_EDGE_SMOOTHING : 0) |
(neb.getNoiseActive() ? SDR_FLAG_VOLUMETRICS_NOISE : 0)
));
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_composite_texture);
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_emissive_texture);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
GL_state.Texture.Enable(2, GL_TEXTURE_2D, Scene_depth_texture);
{
//The following are not required, but the graphics API still returns them
float u_scale, v_scale;
uint32_t array_index;
gr_set_texture_addressing(TMAP_ADDRESS_CLAMP);
// only set the filter parameter if the bind succeeded, or else it is applied to whatever
// texture was previously bound to this unit
if (gr_opengl_tcache_set(neb.getVolumeBitmapHandle(), TCACHE_TYPE_3DTEX, &u_scale, &v_scale, &array_index, 3)) {
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
if (neb.getNoiseActive()) {
gr_set_texture_addressing(TMAP_ADDRESS_WRAP);
if (gr_opengl_tcache_set(neb.getNoiseVolumeBitmapHandle(), TCACHE_TYPE_3DTEX, &u_scale, &v_scale, &array_index, 4)) {
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
}
opengl_set_generic_uniform_data<graphics::generic_data::volumetric_fog_data>([&](graphics::generic_data::volumetric_fog_data* data) {
vm_inverse_matrix4(&data->p_inv, &gr_projection_matrix);
vm_inverse_matrix4(&data->v_inv, &gr_view_matrix);
data->zNear = Min_draw_distance;
data->zFar = Max_draw_distance;
data->cameraPos = Eye_position;
data->globalLightDirection = global_light ? global_light->vec : vec3d(ZERO_VECTOR);
data->globalLightDiffuse = global_light_diffuse;
data->nebPos = neb.getPos();
data->nebSize = neb.getSize();
data->stepsize = neb.getStepsize();
data->opacitydistance = neb.getOpacityDistance();
data->alphalimit = neb.getAlphaLim();
data->nebColor[0] = std::get<0>(neb.getNebulaColor());
data->nebColor[1] = std::get<1>(neb.getNebulaColor());
data->nebColor[2] = std::get<2>(neb.getNebulaColor());
data->udfScale = neb.getUDFScale();
data->emissiveSpreadFactor = neb.getEmissiveSpread();
data->emissiveIntensity = neb.getEmissiveIntensity();
data->emissiveFalloff = neb.getEmissiveFalloff();
data->henyeyGreensteinCoeff = neb.getHenyeyGreensteinCoeff();
data->directionalLightSampleSteps = neb.getGlobalLightSteps();
data->directionalLightStepSize = neb.getGlobalLightStepsize();
data->noiseColor[0] = std::get<0>(neb.getNoiseColor());
data->noiseColor[1] = std::get<1>(neb.getNoiseColor());
data->noiseColor[2] = std::get<2>(neb.getNoiseColor());
data->noiseColorScale1 = std::get<0>(neb.getNoiseColorScale());
data->noiseColorScale2 = std::get<1>(neb.getNoiseColorScale());
data->noiseColorIntensity = neb.getNoiseColorIntensity();
data->aspect = gr_screen.clip_aspect;
data->fov = g3_get_hfov(Proj_fov);
});
{
GR_DEBUG_SCOPE("Volumetric Nebulae Draw");
opengl_draw_full_screen_textured(0.0f, 0.0f, 1.0f, 1.0f);
}
GL_state.Texture.Enable(Scene_emissive_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gr_end_view_matrix();
gr_end_proj_matrix();
}
if(!bDrawFullNeb && !bDrawNebVolumetrics) {
// Transfer the resolved lighting back to the color texture
// TODO: Maybe this could be improved so that it doesn't require the copy back operation?
glReadBuffer(GL_COLOR_ATTACHMENT5);
glBlitFramebuffer(0,
0,
gr_screen.max_w,
gr_screen.max_h,
0,
0,
gr_screen.max_w,
gr_screen.max_h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glReadBuffer(GL_COLOR_ATTACHMENT0);
}
gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
gr_set_view_matrix(&Eye_position, &Eye_matrix);
// reset state
gr_clear_states();
}
void gr_opengl_override_fog(bool set_override)
{
override_fog = set_override;
}
void gr_opengl_draw_deferred_light_sphere(const vec3d *position)
{
g3_start_instance_matrix(position, &vmd_identity_matrix, true);
gr_matrix_set_uniforms();
opengl_draw_sphere();
g3_done_instance(true);
}
void gr_opengl_deferred_light_cylinder_init(int segments)
{
auto mesh = graphics::util::generate_cylinder_mesh(segments);
deferred_light_cylinder_vcount = static_cast<GLushort>(mesh.vertex_count);
deferred_light_cylinder_icount = mesh.index_count;
glGetError();
glGenBuffers(1, &deferred_light_cylinder_vbo);
// make sure we have one
if (deferred_light_cylinder_vbo) {
glBindBuffer(GL_ARRAY_BUFFER, deferred_light_cylinder_vbo);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float), mesh.vertices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_cylinder_vbo);
deferred_light_cylinder_vbo = 0;
return;
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glGenBuffers(1, &deferred_light_cylinder_ibo);
// make sure we have one
if (deferred_light_cylinder_ibo) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, deferred_light_cylinder_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(ushort), mesh.indices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_cylinder_ibo);
deferred_light_cylinder_ibo = 0;
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
void gr_opengl_deferred_light_sphere_init(int rings, int segments)
{
auto mesh = graphics::util::generate_sphere_mesh(rings, segments);
deferred_light_sphere_vcount = static_cast<GLushort>(mesh.vertex_count);
deferred_light_sphere_icount = mesh.index_count;
glGetError();
glGenBuffers(1, &deferred_light_sphere_vbo);
// make sure we have one
if (deferred_light_sphere_vbo) {
glBindBuffer(GL_ARRAY_BUFFER, deferred_light_sphere_vbo);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float), mesh.vertices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_sphere_vbo);
deferred_light_sphere_vbo = 0;
return;
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glGenBuffers(1, &deferred_light_sphere_ibo);
// make sure we have one
if (deferred_light_sphere_ibo) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, deferred_light_sphere_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(ushort), mesh.indices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_sphere_ibo);
deferred_light_sphere_ibo = 0;
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
void opengl_draw_sphere()
{
vertex_layout vertex_declare;
vertex_declare.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0);
opengl_bind_vertex_layout(vertex_declare, deferred_light_sphere_vbo, deferred_light_sphere_ibo);
glDrawRangeElements(GL_TRIANGLES, 0, deferred_light_sphere_vcount, deferred_light_sphere_icount, GL_UNSIGNED_SHORT, 0);
}
void gr_opengl_draw_deferred_light_cylinder(const vec3d *position, const matrix *orient)
{
g3_start_instance_matrix(position, orient, true);
gr_matrix_set_uniforms();
vertex_layout vertex_declare;
vertex_declare.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0);
opengl_bind_vertex_layout(vertex_declare, deferred_light_cylinder_vbo, deferred_light_cylinder_ibo);
glDrawRangeElements(GL_TRIANGLES, 0, deferred_light_cylinder_vcount, deferred_light_cylinder_icount, GL_UNSIGNED_SHORT, 0);
g3_done_instance(true);
}