803 lines
29 KiB
C++
Executable file
803 lines
29 KiB
C++
Executable file
#include "renderer.hpp"
|
|
|
|
#include "gfx_commandbuffer.hpp"
|
|
#include "math.hpp"
|
|
#include "file.hpp"
|
|
#include "scene.hpp"
|
|
#include "vector.hpp"
|
|
#include "imguipass.hpp"
|
|
#include "gfx.hpp"
|
|
#include "pass.hpp"
|
|
#include "shadowpass.hpp"
|
|
#include "smaapass.hpp"
|
|
#include "scenecapture.hpp"
|
|
#include "materialcompiler.hpp"
|
|
#include "assertions.hpp"
|
|
#include "dofpass.hpp"
|
|
#include "frustum.hpp"
|
|
#include "shadercompiler.hpp"
|
|
#include "debug.hpp"
|
|
|
|
using prism::renderer;
|
|
|
|
struct SceneMaterial {
|
|
prism::float4 color, info;
|
|
};
|
|
|
|
struct SceneLight {
|
|
prism::float4 positionType;
|
|
prism::float4 directionPower;
|
|
prism::float4 colorSize;
|
|
prism::float4 shadowsEnable;
|
|
};
|
|
|
|
struct SceneProbe {
|
|
prism::float4 position, size;
|
|
};
|
|
|
|
struct SceneInformation {
|
|
prism::float4 options;
|
|
prism::float4 camPos;
|
|
Matrix4x4 vp, lightspace;
|
|
Matrix4x4 spotLightSpaces[max_spot_shadows];
|
|
SceneMaterial materials[max_scene_materials];
|
|
SceneLight lights[max_scene_lights];
|
|
SceneProbe probes[max_environment_probes];
|
|
int numLights;
|
|
int p[3];
|
|
};
|
|
|
|
struct PostPushConstants {
|
|
prism::float4 viewport, options, transform_ops;
|
|
};
|
|
|
|
struct SkyPushConstant {
|
|
Matrix4x4 view;
|
|
prism::float4 sun_position_fov;
|
|
float aspect;
|
|
};
|
|
|
|
renderer::renderer(GFX* gfx, const bool enable_imgui) : gfx(gfx) {
|
|
Expects(gfx != nullptr);
|
|
|
|
shader_compiler.set_include_path(prism::get_domain_path(prism::domain::internal).string());
|
|
|
|
create_dummy_texture();
|
|
create_histogram_resources();
|
|
|
|
shadow_pass = std::make_unique<ShadowPass>(gfx);
|
|
scene_capture = std::make_unique<SceneCapture>(gfx);
|
|
smaa_pass = std::make_unique<SMAAPass>(gfx);
|
|
|
|
if(enable_imgui)
|
|
addPass<ImGuiPass>();
|
|
|
|
generate_brdf();
|
|
|
|
GFXRenderPassCreateInfo renderPassInfo = {};
|
|
renderPassInfo.label = "Offscreen";
|
|
renderPassInfo.attachments.push_back(GFXPixelFormat::RGBA_32F);
|
|
renderPassInfo.attachments.push_back(GFXPixelFormat::DEPTH_32F);
|
|
renderPassInfo.will_use_in_shader = true;
|
|
|
|
offscreen_render_pass = gfx->create_render_pass(renderPassInfo);
|
|
|
|
renderPassInfo.label = "Offscreen (UNORM)";
|
|
renderPassInfo.attachments = {GFXPixelFormat::R8G8B8A8_UNORM};
|
|
|
|
unorm_render_pass = gfx->create_render_pass(renderPassInfo);
|
|
|
|
create_sky_pipeline();
|
|
}
|
|
|
|
renderer::~renderer() = default;
|
|
|
|
RenderTarget* renderer::allocate_render_target(const prism::Extent extent) {
|
|
auto target = new RenderTarget();
|
|
|
|
resize_render_target(*target, extent);
|
|
|
|
render_targets.push_back(target);
|
|
|
|
return target;
|
|
}
|
|
|
|
void renderer::resize_render_target(RenderTarget& target, const prism::Extent extent) {
|
|
target.extent = extent;
|
|
|
|
create_render_target_resources(target);
|
|
smaa_pass->create_render_target_resources(target);
|
|
create_post_pipelines();
|
|
|
|
for(auto& pass : passes)
|
|
pass->create_render_target_resources(target);
|
|
}
|
|
|
|
void renderer::recreate_all_render_targets() {
|
|
|
|
}
|
|
|
|
void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarget& target, platform::window_ptr index) {
|
|
const auto extent = target.extent;
|
|
const auto render_extent = target.get_render_extent();
|
|
|
|
if(index != nullptr && !platform::is_main_window(index)) {
|
|
GFXRenderPassBeginInfo beginInfo = {};
|
|
beginInfo.render_area.extent = render_extent;
|
|
|
|
commandbuffer->set_render_pass(beginInfo);
|
|
|
|
Viewport viewport = {};
|
|
viewport.width = static_cast<float>(render_extent.width);
|
|
viewport.height = static_cast<float>(render_extent.height);
|
|
|
|
commandbuffer->set_viewport(viewport);
|
|
|
|
for(auto& pass : passes)
|
|
pass->render_post(commandbuffer, target, index);
|
|
|
|
return;
|
|
}
|
|
|
|
commandbuffer->push_group("Scene Rendering");
|
|
|
|
GFXRenderPassBeginInfo beginInfo = {};
|
|
beginInfo.framebuffer = target.offscreenFramebuffer;
|
|
beginInfo.render_pass = offscreen_render_pass;
|
|
beginInfo.render_area.extent = render_extent;
|
|
|
|
controller_continuity continuity;
|
|
|
|
if(scene != nullptr) {
|
|
commandbuffer->push_group("Shadow Rendering");
|
|
|
|
shadow_pass->render(commandbuffer, *scene);
|
|
|
|
commandbuffer->pop_group();
|
|
|
|
if(render_options.enable_ibl) {
|
|
commandbuffer->push_group("Scene Capture");
|
|
scene_capture->render(commandbuffer, scene);
|
|
commandbuffer->pop_group();
|
|
}
|
|
|
|
commandbuffer->set_render_pass(beginInfo);
|
|
|
|
const auto& cameras = scene->get_all<Camera>();
|
|
for(auto& [obj, camera] : cameras) {
|
|
const bool requires_limited_perspective = render_options.enable_depth_of_field;
|
|
if(requires_limited_perspective) {
|
|
camera.perspective = prism::perspective(radians(camera.fov),
|
|
static_cast<float>(render_extent.width) / static_cast<float>(render_extent.height),
|
|
camera.near,
|
|
100.0f);
|
|
} else {
|
|
camera.perspective = prism::infinite_perspective(radians(camera.fov),
|
|
static_cast<float>(render_extent.width) / static_cast<float>(render_extent.height),
|
|
camera.near);
|
|
}
|
|
|
|
camera.view = inverse(scene->get<Transform>(obj).model);
|
|
|
|
Viewport viewport = {};
|
|
viewport.width = static_cast<float>(render_extent.width);
|
|
viewport.height = static_cast<float>(render_extent.height);
|
|
|
|
commandbuffer->set_viewport(viewport);
|
|
|
|
commandbuffer->push_group("render camera");
|
|
|
|
render_camera(commandbuffer, *scene, obj, camera, render_extent, target, continuity);
|
|
|
|
commandbuffer->pop_group();
|
|
}
|
|
}
|
|
|
|
commandbuffer->pop_group();
|
|
|
|
if(render_options.enable_aa) {
|
|
commandbuffer->push_group("SMAA");
|
|
|
|
smaa_pass->render(commandbuffer, target);
|
|
|
|
commandbuffer->pop_group();
|
|
}
|
|
|
|
if(render_options.enable_depth_of_field && dof_pass != nullptr)
|
|
dof_pass->render(commandbuffer, *scene);
|
|
|
|
commandbuffer->push_group("Post Processing");
|
|
|
|
commandbuffer->end_render_pass();
|
|
|
|
// begin auto exposure
|
|
|
|
commandbuffer->set_compute_pipeline(histogram_pipeline);
|
|
|
|
commandbuffer->bind_texture(target.offscreenColorTexture, 0);
|
|
commandbuffer->bind_shader_buffer(histogram_buffer, 0, 1, sizeof(uint32_t) * 256);
|
|
|
|
const float lum_range = render_options.max_luminance - render_options.min_luminance;
|
|
|
|
prism::float4 params = prism::float4(render_options.min_luminance,
|
|
1.0f / lum_range,
|
|
static_cast<float>(render_extent.width),
|
|
static_cast<float>(render_extent.height));
|
|
|
|
commandbuffer->set_push_constant(¶ms, sizeof(prism::float4));
|
|
|
|
commandbuffer->dispatch(static_cast<uint32_t>(std::ceil(static_cast<float>(render_extent.width) / 16.0f)),
|
|
static_cast<uint32_t>(std::ceil(static_cast<float>(render_extent.height) / 16.0f)), 1);
|
|
|
|
commandbuffer->set_compute_pipeline(histogram_average_pipeline);
|
|
|
|
commandbuffer->bind_shader_buffer(histogram_buffer, 0, 1, sizeof(uint32_t) * 256);
|
|
|
|
params = prism::float4(render_options.min_luminance,
|
|
lum_range,
|
|
std::clamp(1.0f - std::exp(-(1.0f / 60.0f) * 1.1f), 0.0f, 1.0f),
|
|
static_cast<float>(render_extent.width * render_extent.height));
|
|
|
|
commandbuffer->set_push_constant(¶ms, sizeof(prism::float4));
|
|
|
|
commandbuffer->bind_texture(average_luminance_texture, 0);
|
|
|
|
commandbuffer->dispatch(1, 1, 1);
|
|
|
|
// continue post processing
|
|
beginInfo.framebuffer = nullptr;
|
|
beginInfo.render_pass = nullptr;
|
|
|
|
commandbuffer->set_render_pass(beginInfo);
|
|
|
|
Viewport viewport = {};
|
|
viewport.width = static_cast<float>(render_extent.width);
|
|
viewport.height = static_cast<float>(render_extent.height);
|
|
|
|
commandbuffer->set_viewport(viewport);
|
|
|
|
commandbuffer->set_graphics_pipeline(post_pipeline);
|
|
|
|
if(render_options.enable_depth_of_field)
|
|
commandbuffer->bind_texture(dof_pass->normal_field, 1);
|
|
else
|
|
commandbuffer->bind_texture(target.offscreenColorTexture, 1);
|
|
|
|
commandbuffer->bind_texture(target.blend_texture, 3);
|
|
|
|
if(auto texture = get_requested_texture(PassTextureType::SelectionSobel))
|
|
commandbuffer->bind_texture(texture, 5);
|
|
else
|
|
commandbuffer->bind_texture(dummy_texture, 5);
|
|
|
|
commandbuffer->bind_texture(average_luminance_texture, 6);
|
|
|
|
if(render_options.enable_depth_of_field)
|
|
commandbuffer->bind_texture(dof_pass->far_field, 7);
|
|
else
|
|
commandbuffer->bind_texture(dummy_texture, 7);
|
|
|
|
PostPushConstants pc;
|
|
pc.options.x = render_options.enable_aa;
|
|
pc.options.z = render_options.exposure;
|
|
|
|
if(render_options.enable_depth_of_field)
|
|
pc.options.w = 2;
|
|
|
|
pc.transform_ops.x = static_cast<float>(render_options.display_color_space);
|
|
pc.transform_ops.y = static_cast<float>(render_options.tonemapping);
|
|
|
|
const auto [width, height] = render_extent;
|
|
pc.viewport = prism::float4(1.0f / static_cast<float>(width), 1.0f / static_cast<float>(height), static_cast<float>(width), static_cast<float>(height));
|
|
|
|
commandbuffer->set_push_constant(&pc, sizeof(PostPushConstants));
|
|
|
|
commandbuffer->draw(0, 4, 0, 1);
|
|
|
|
commandbuffer->pop_group();
|
|
|
|
commandbuffer->push_group("Extra Passes");
|
|
|
|
for(auto& pass : passes)
|
|
pass->render_post(commandbuffer, target, index);
|
|
|
|
commandbuffer->pop_group();
|
|
|
|
target.current_frame = (target.current_frame + 1) % RT_MAX_FRAMES_IN_FLIGHT;
|
|
}
|
|
|
|
void renderer::render_camera(GFXCommandBuffer* command_buffer, Scene& scene, Object camera_object, Camera& camera, prism::Extent extent, RenderTarget& target, controller_continuity& continuity) {
|
|
// frustum test
|
|
const auto frustum = normalize_frustum(camera_extract_frustum(scene, camera_object));
|
|
|
|
SceneInformation sceneInfo = {};
|
|
sceneInfo.lightspace = scene.lightSpace;
|
|
sceneInfo.options = prism::float4(1, 0, 0, 0);
|
|
sceneInfo.camPos = scene.get<Transform>(camera_object).get_world_position();
|
|
sceneInfo.camPos.w = 2.0f * camera.near * std::tan(camera.fov * 0.5f) * (static_cast<float>(extent.width) / static_cast<float>(extent.height));
|
|
sceneInfo.vp = camera.perspective * camera.view;
|
|
|
|
int last_point_light = 0;
|
|
for(const auto& [obj, light] : scene.get_all<Light>()) {
|
|
SceneLight sl;
|
|
sl.positionType = prism::float4(scene.get<Transform>(obj).get_world_position(), static_cast<float>(light.type));
|
|
|
|
prism::float3 front = prism::float3(0.0f, 0.0f, 1.0f) * scene.get<Transform>(obj).rotation;
|
|
|
|
sl.directionPower = prism::float4(-front, light.power);
|
|
sl.colorSize = prism::float4(utility::from_srgb_to_linear(light.color), radians(light.spot_size));
|
|
|
|
sl.shadowsEnable = prism::float4(light.enable_shadows, radians(light.size), last_point_light, 0);
|
|
|
|
if(light.enable_shadows)
|
|
last_point_light++;
|
|
|
|
sceneInfo.lights[sceneInfo.numLights++] = sl;
|
|
}
|
|
|
|
for(int i = 0; i < max_spot_shadows; i++)
|
|
sceneInfo.spotLightSpaces[i] = scene.spotLightSpaces[i];
|
|
|
|
int last_probe = 0;
|
|
for(const auto& [obj, probe] : scene.get_all<EnvironmentProbe>()) {
|
|
SceneProbe p;
|
|
p.position = prism::float4(scene.get<Transform>(obj).position, probe.is_sized ? 1.0f : 2.0f);
|
|
p.size = prism::float4(probe.size, probe.intensity);
|
|
|
|
sceneInfo.probes[last_probe++] = p;
|
|
}
|
|
|
|
int numMaterialsInBuffer = 0;
|
|
std::map<Material*, int> material_indices;
|
|
|
|
const auto& meshes = scene.get_all<Renderable>();
|
|
for(const auto& [obj, mesh] : meshes) {
|
|
if(!mesh.mesh)
|
|
continue;
|
|
|
|
if(mesh.materials.empty())
|
|
continue;
|
|
|
|
for(auto& material : mesh.materials) {
|
|
if(!material)
|
|
continue;
|
|
|
|
if(material->static_pipeline == nullptr || material->skinned_pipeline == nullptr)
|
|
create_mesh_pipeline(*material.handle);
|
|
|
|
if(!material_indices.count(material.handle)) {
|
|
sceneInfo.materials[numMaterialsInBuffer].info.x = material->metallic;
|
|
sceneInfo.materials[numMaterialsInBuffer].info.y = material->roughness;
|
|
|
|
material_indices[material.handle] = numMaterialsInBuffer++;
|
|
}
|
|
}
|
|
|
|
struct PushConstant {
|
|
Matrix4x4 m;
|
|
} pc;
|
|
|
|
pc.m = scene.get<Transform>(obj).model;
|
|
|
|
command_buffer->set_vertex_buffer(mesh.mesh->position_buffer, 0, position_buffer_index);
|
|
command_buffer->set_vertex_buffer(mesh.mesh->normal_buffer, 0, normal_buffer_index);
|
|
command_buffer->set_vertex_buffer(mesh.mesh->texture_coord_buffer, 0, texcoord_buffer_index);
|
|
command_buffer->set_vertex_buffer(mesh.mesh->tangent_buffer, 0, tangent_buffer_index);
|
|
command_buffer->set_vertex_buffer(mesh.mesh->bitangent_buffer, 0, bitangent_buffer_index);
|
|
|
|
if(!mesh.mesh->bones.empty())
|
|
command_buffer->set_vertex_buffer(mesh.mesh->bone_buffer, 0, bone_buffer_index);
|
|
|
|
command_buffer->set_index_buffer(mesh.mesh->index_buffer, IndexType::UINT32);
|
|
|
|
for(const auto& part : mesh.mesh->parts) {
|
|
const int material_index = part.material_override == -1 ? 0 : part.material_override;
|
|
|
|
if(material_index >= mesh.materials.size())
|
|
continue;
|
|
|
|
if(mesh.materials[material_index].handle == nullptr || mesh.materials[material_index]->static_pipeline == nullptr)
|
|
continue;
|
|
|
|
if(render_options.enable_frustum_culling && !test_aabb_frustum(frustum, get_aabb_for_part(scene.get<Transform>(obj), part)))
|
|
continue;
|
|
|
|
command_buffer->set_graphics_pipeline(mesh.mesh->bones.empty() ? mesh.materials[material_index]->static_pipeline : mesh.materials[material_index]->skinned_pipeline);
|
|
|
|
command_buffer->bind_shader_buffer(target.sceneBuffer, 0, 1, sizeof(SceneInformation));
|
|
|
|
command_buffer->bind_texture(scene.depthTexture, 2);
|
|
command_buffer->bind_texture(scene.pointLightArray, 3);
|
|
command_buffer->bind_texture(scene.spotLightArray, 6);
|
|
command_buffer->bind_texture(scene.irradianceCubeArray, 7);
|
|
command_buffer->bind_texture(scene.prefilteredCubeArray, 8);
|
|
command_buffer->bind_texture(brdf_texture, 9);
|
|
|
|
if(!mesh.mesh->bones.empty())
|
|
command_buffer->bind_shader_buffer(part.bone_batrix_buffer, 0, 14, sizeof(Matrix4x4) * 128);
|
|
|
|
command_buffer->set_push_constant(&pc, sizeof(PushConstant));
|
|
|
|
for(const auto& [index, texture] : mesh.materials[material_index]->bound_textures) {
|
|
GFXTexture* texture_to_bind = dummy_texture;
|
|
if(texture)
|
|
texture_to_bind = texture->handle;
|
|
|
|
command_buffer->bind_texture(texture_to_bind, index);
|
|
}
|
|
|
|
command_buffer->draw_indexed(part.index_count, part.index_offset, part.vertex_offset, material_indices.at(*mesh.materials[material_index]));
|
|
}
|
|
}
|
|
|
|
SkyPushConstant pc;
|
|
pc.view = matrix_from_quat(scene.get<Transform>(camera_object).rotation);
|
|
pc.aspect = static_cast<float>(extent.width) / static_cast<float>(extent.height);
|
|
|
|
for(const auto& [obj, light] : scene.get_all<Light>()) {
|
|
if(light.type == Light::Type::Sun)
|
|
pc.sun_position_fov = prism::float4(scene.get<Transform>(obj).get_world_position(), radians(camera.fov));
|
|
}
|
|
|
|
command_buffer->set_graphics_pipeline(sky_pipeline);
|
|
|
|
command_buffer->set_push_constant(&pc, sizeof(SkyPushConstant));
|
|
|
|
command_buffer->draw(0, 4, 0, 1);
|
|
|
|
if(render_options.enable_extra_passes) {
|
|
for(auto& pass : passes)
|
|
pass->render_scene(scene, command_buffer);
|
|
}
|
|
|
|
gfx->copy_buffer(target.sceneBuffer, &sceneInfo, 0, sizeof(SceneInformation));
|
|
}
|
|
|
|
void renderer::create_mesh_pipeline(Material& material) const {
|
|
GFXShaderConstant materials_constant = {};
|
|
materials_constant.type = GFXShaderConstant::Type::Integer;
|
|
materials_constant.value = max_scene_materials;
|
|
|
|
GFXShaderConstant lights_constant = {};
|
|
lights_constant.index = 1;
|
|
lights_constant.type = GFXShaderConstant::Type::Integer;
|
|
lights_constant.value = max_scene_lights;
|
|
|
|
GFXShaderConstant spot_lights_constant = {};
|
|
spot_lights_constant.index = 2;
|
|
spot_lights_constant.type = GFXShaderConstant::Type::Integer;
|
|
spot_lights_constant.value = max_spot_shadows;
|
|
|
|
GFXShaderConstant probes_constant = {};
|
|
probes_constant.index = 3;
|
|
probes_constant.type = GFXShaderConstant::Type::Integer;
|
|
probes_constant.value = max_environment_probes;
|
|
|
|
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
|
|
pipelineInfo.label = "Mesh";
|
|
pipelineInfo.shaders.vertex_src = ShaderSource(prism::path("mesh.vert"));
|
|
pipelineInfo.shaders.fragment_src = ShaderSource(prism::path("mesh.frag"));
|
|
|
|
pipelineInfo.shaders.vertex_constants = {materials_constant, lights_constant, spot_lights_constant, probes_constant};
|
|
pipelineInfo.shaders.fragment_constants = {materials_constant, lights_constant, spot_lights_constant, probes_constant};
|
|
|
|
pipelineInfo.shader_input.push_constants = {
|
|
{sizeof(Matrix4x4), 0}
|
|
};
|
|
|
|
pipelineInfo.shader_input.bindings = {
|
|
{1, GFXBindingType::StorageBuffer},
|
|
{0, GFXBindingType::PushConstant},
|
|
{2, GFXBindingType::Texture},
|
|
{3, GFXBindingType::Texture},
|
|
{6, GFXBindingType::Texture},
|
|
{7, GFXBindingType::Texture},
|
|
{8, GFXBindingType::Texture},
|
|
{9, GFXBindingType::Texture}
|
|
};
|
|
|
|
pipelineInfo.render_pass = offscreen_render_pass;
|
|
pipelineInfo.depth.depth_mode = GFXDepthMode::Less;
|
|
pipelineInfo.rasterization.culling_mode = GFXCullingMode::Backface;
|
|
pipelineInfo.blending.src_rgb = GFXBlendFactor::SrcAlpha;
|
|
pipelineInfo.blending.dst_rgb = GFXBlendFactor::OneMinusSrcAlpha;
|
|
|
|
pipelineInfo.shaders.fragment_src = material_compiler.compile_material_fragment(material);
|
|
|
|
for (auto [index, texture] : material.bound_textures) {
|
|
GFXShaderBinding binding;
|
|
binding.binding = index;
|
|
binding.type = GFXBindingType::Texture;
|
|
|
|
pipelineInfo.shader_input.bindings.push_back(binding);
|
|
}
|
|
|
|
auto [static_pipeline, skinned_pipeline] = material_compiler.create_pipeline_permutations(pipelineInfo);
|
|
|
|
material.static_pipeline = static_pipeline;
|
|
material.skinned_pipeline = skinned_pipeline;
|
|
|
|
pipelineInfo.render_pass = scene_capture->renderPass;
|
|
|
|
pipelineInfo.shaders.fragment_src = material_compiler.compile_material_fragment(material, false); // scene capture does not use IBL
|
|
|
|
pipelineInfo.shader_input.push_constants[0].size += sizeof(Matrix4x4);
|
|
|
|
material.capture_pipeline = material_compiler.create_static_pipeline(pipelineInfo, false, true);
|
|
}
|
|
|
|
void renderer::create_dummy_texture() {
|
|
GFXTextureCreateInfo createInfo = {};
|
|
createInfo.label = "Dummy";
|
|
createInfo.width = 1;
|
|
createInfo.height = 1;
|
|
createInfo.format = GFXPixelFormat::R8G8B8A8_UNORM;
|
|
createInfo.usage = GFXTextureUsage::Sampled | GFXTextureUsage::TransferDst;
|
|
|
|
dummy_texture = gfx->create_texture(createInfo);
|
|
|
|
uint8_t tex[4] = {0, 0, 0, 0};
|
|
|
|
gfx->copy_texture(dummy_texture, tex, sizeof(tex));
|
|
}
|
|
|
|
void renderer::create_render_target_resources(RenderTarget& target) {
|
|
const auto extent = target.get_render_extent();
|
|
|
|
GFXTextureCreateInfo textureInfo = {};
|
|
textureInfo.label = "Offscreen Color";
|
|
textureInfo.width = extent.width;
|
|
textureInfo.height = extent.height;
|
|
textureInfo.format = GFXPixelFormat::RGBA_32F;
|
|
textureInfo.usage = GFXTextureUsage::Attachment | GFXTextureUsage::Sampled | GFXTextureUsage::Storage;
|
|
textureInfo.samplingMode = SamplingMode::ClampToEdge;
|
|
|
|
target.offscreenColorTexture = gfx->create_texture(textureInfo);
|
|
|
|
textureInfo.label = "Offscreen Depth";
|
|
textureInfo.format = GFXPixelFormat::DEPTH_32F;
|
|
textureInfo.usage = GFXTextureUsage::Attachment | GFXTextureUsage::Sampled;
|
|
|
|
target.offscreenDepthTexture = gfx->create_texture(textureInfo);
|
|
|
|
GFXFramebufferCreateInfo framebufferInfo = {};
|
|
framebufferInfo.render_pass = offscreen_render_pass;
|
|
framebufferInfo.attachments.push_back(target.offscreenColorTexture);
|
|
framebufferInfo.attachments.push_back(target.offscreenDepthTexture);
|
|
|
|
target.offscreenFramebuffer = gfx->create_framebuffer(framebufferInfo);
|
|
|
|
if(post_pipeline == nullptr) {
|
|
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
|
|
pipelineInfo.label = "Post";
|
|
|
|
pipelineInfo.shaders.vertex_src = ShaderSource(prism::path("post.vert"));
|
|
pipelineInfo.shaders.fragment_src = ShaderSource(prism::path("post.frag"));
|
|
|
|
pipelineInfo.shader_input.bindings = {
|
|
{4, GFXBindingType::PushConstant},
|
|
{1, GFXBindingType::Texture},
|
|
{2, GFXBindingType::Texture},
|
|
{3, GFXBindingType::Texture},
|
|
{5, GFXBindingType::Texture},
|
|
{6, GFXBindingType::Texture},
|
|
{7, GFXBindingType::Texture}
|
|
};
|
|
|
|
pipelineInfo.shader_input.push_constants = {
|
|
{sizeof(PostPushConstants), 0}
|
|
};
|
|
|
|
post_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
|
|
}
|
|
|
|
target.sceneBuffer = gfx->create_buffer(nullptr, sizeof(SceneInformation), true, GFXBufferUsage::Storage);
|
|
}
|
|
|
|
void renderer::create_post_pipelines() {
|
|
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
|
|
pipelineInfo.label = "Post";
|
|
|
|
pipelineInfo.shaders.vertex_src = ShaderSource(prism::path("post.vert"));
|
|
pipelineInfo.shaders.fragment_src = ShaderSource(prism::path("post.frag"));
|
|
|
|
pipelineInfo.shader_input.bindings = {
|
|
{4, GFXBindingType::PushConstant},
|
|
{1, GFXBindingType::Texture},
|
|
{2, GFXBindingType::Texture},
|
|
{3, GFXBindingType::Texture},
|
|
{5, GFXBindingType::Texture},
|
|
{6, GFXBindingType::Texture},
|
|
{7, GFXBindingType::Texture}
|
|
};
|
|
|
|
pipelineInfo.shader_input.push_constants = {
|
|
{sizeof(PostPushConstants), 0}
|
|
};
|
|
|
|
post_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
|
|
}
|
|
|
|
void renderer::create_sky_pipeline() {
|
|
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
|
|
pipelineInfo.label = "Sky";
|
|
pipelineInfo.render_pass = offscreen_render_pass;
|
|
|
|
pipelineInfo.shaders.vertex_src = register_shader("sky.vert");
|
|
pipelineInfo.shaders.fragment_src = register_shader("sky.frag");
|
|
|
|
pipelineInfo.shader_input.bindings = {
|
|
{1, GFXBindingType::PushConstant}
|
|
};
|
|
|
|
pipelineInfo.shader_input.push_constants = {
|
|
{sizeof(SkyPushConstant), 0}
|
|
};
|
|
|
|
pipelineInfo.depth.depth_mode = GFXDepthMode::LessOrEqual;
|
|
|
|
sky_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
|
|
|
|
associate_shader_reload("sky.vert", [this] {
|
|
create_sky_pipeline();
|
|
});
|
|
|
|
associate_shader_reload("sky.frag", [this] {
|
|
create_sky_pipeline();
|
|
});
|
|
}
|
|
|
|
void renderer::generate_brdf() {
|
|
GFXRenderPassCreateInfo renderPassInfo = {};
|
|
renderPassInfo.label = "BRDF Gen";
|
|
renderPassInfo.attachments.push_back(GFXPixelFormat::R8G8_SFLOAT);
|
|
renderPassInfo.will_use_in_shader = true;
|
|
|
|
brdf_render_pass = gfx->create_render_pass(renderPassInfo);
|
|
|
|
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
|
|
pipelineInfo.label = "BRDF";
|
|
|
|
pipelineInfo.shaders.vertex_src = ShaderSource(prism::path("brdf.vert"));
|
|
pipelineInfo.shaders.fragment_src = ShaderSource(prism::path("brdf.frag"));
|
|
|
|
pipelineInfo.render_pass = brdf_render_pass;
|
|
|
|
brdf_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
|
|
|
|
GFXTextureCreateInfo textureInfo = {};
|
|
textureInfo.label = "BRDF LUT";
|
|
textureInfo.format = GFXPixelFormat::R8G8_SFLOAT;
|
|
textureInfo.width = brdf_resolution;
|
|
textureInfo.height = brdf_resolution;
|
|
textureInfo.usage = GFXTextureUsage::Sampled | GFXTextureUsage::Attachment;
|
|
|
|
brdf_texture = gfx->create_texture(textureInfo);
|
|
|
|
GFXFramebufferCreateInfo framebufferInfo = {};
|
|
framebufferInfo.attachments = {brdf_texture};
|
|
framebufferInfo.render_pass = brdf_render_pass;
|
|
|
|
brdf_framebuffer = gfx->create_framebuffer(framebufferInfo);
|
|
|
|
// render
|
|
GFXCommandBuffer* command_buffer = gfx->acquire_command_buffer(false);
|
|
|
|
GFXRenderPassBeginInfo beginInfo = {};
|
|
beginInfo.render_pass = brdf_render_pass;
|
|
beginInfo.framebuffer = brdf_framebuffer;
|
|
beginInfo.render_area.extent = {brdf_resolution, brdf_resolution};
|
|
|
|
command_buffer->set_render_pass(beginInfo);
|
|
|
|
command_buffer->set_graphics_pipeline(brdf_pipeline);
|
|
|
|
Viewport viewport = {};
|
|
viewport.width = brdf_resolution;
|
|
viewport.height = brdf_resolution;
|
|
|
|
command_buffer->set_viewport(viewport);
|
|
|
|
command_buffer->draw(0, 4, 0, 1);
|
|
|
|
gfx->submit(command_buffer, nullptr);
|
|
}
|
|
|
|
void renderer::create_histogram_resources() {
|
|
GFXComputePipelineCreateInfo create_info = {};
|
|
create_info.label = "Histogram";
|
|
create_info.compute_src = ShaderSource(prism::path("histogram.comp"));
|
|
create_info.workgroup_size_x = 16;
|
|
create_info.workgroup_size_y = 16;
|
|
|
|
create_info.shader_input.bindings = {
|
|
{0, GFXBindingType::StorageImage},
|
|
{1, GFXBindingType::StorageBuffer},
|
|
{2, GFXBindingType::PushConstant}
|
|
};
|
|
|
|
create_info.shader_input.push_constants = {
|
|
{sizeof(prism::float4), 0}
|
|
};
|
|
|
|
histogram_pipeline = gfx->create_compute_pipeline(create_info);
|
|
|
|
create_info.label = "Histogram Average";
|
|
create_info.compute_src = ShaderSource(prism::path("histogram-average.comp"));
|
|
create_info.workgroup_size_x = 256;
|
|
create_info.workgroup_size_y = 1;
|
|
|
|
histogram_average_pipeline = gfx->create_compute_pipeline(create_info);
|
|
|
|
histogram_buffer = gfx->create_buffer(nullptr, sizeof(uint32_t) * 256, false, GFXBufferUsage::Storage);
|
|
|
|
GFXTextureCreateInfo texture_info = {};
|
|
texture_info.label = "Average Luminance Store";
|
|
texture_info.width = 1;
|
|
texture_info.height = 1;
|
|
texture_info.format = GFXPixelFormat::R_16F;
|
|
texture_info.usage = GFXTextureUsage::Sampled | GFXTextureUsage::ShaderWrite | GFXTextureUsage::Storage;
|
|
|
|
average_luminance_texture = gfx->create_texture(texture_info);
|
|
}
|
|
|
|
ShaderSource renderer::register_shader(const std::string_view shader_file) {
|
|
if(!reloading_shader) {
|
|
RegisteredShader shader;
|
|
shader.filename = shader_file;
|
|
|
|
registered_shaders.push_back(shader);
|
|
}
|
|
|
|
std::string found_shader_source;
|
|
for(auto& shader : registered_shaders) {
|
|
if(shader.filename == shader_file) {
|
|
found_shader_source = shader.injected_shader_source;
|
|
}
|
|
}
|
|
|
|
prism::path base_shader_path = get_shader_source_directory();
|
|
|
|
// if shader editor system is not initialized, use prebuilt shaders
|
|
if(base_shader_path.empty())
|
|
return ShaderSource(prism::path(shader_file));
|
|
|
|
shader_compiler.set_include_path(base_shader_path.string());
|
|
|
|
prism::path shader_path = prism::path(shader_file);
|
|
|
|
ShaderStage stage = ShaderStage::Vertex;
|
|
if(shader_path.extension() == ".vert")
|
|
stage = ShaderStage::Vertex;
|
|
else if(shader_path.extension() == ".frag")
|
|
stage = ShaderStage::Fragment;
|
|
|
|
if(found_shader_source.empty()) {
|
|
auto file = prism::open_file(base_shader_path / shader_path.replace_extension(shader_path.extension().string() + ".glsl"));
|
|
|
|
return shader_compiler.compile(ShaderLanguage::GLSL, stage, ShaderSource(file->read_as_string()), gfx->accepted_shader_language()).value();
|
|
} else {
|
|
return shader_compiler.compile(ShaderLanguage::GLSL, stage, ShaderSource(found_shader_source), gfx->accepted_shader_language()).value();
|
|
}
|
|
}
|
|
|
|
void renderer::associate_shader_reload(const std::string_view shader_file, const std::function<void()>& reload_function) {
|
|
if(reloading_shader)
|
|
return;
|
|
|
|
for(auto& shader : registered_shaders) {
|
|
if(shader.filename == shader_file)
|
|
shader.reload_function = reload_function;
|
|
}
|
|
}
|
|
|
|
void renderer::reload_shader(const std::string_view shader_file, const std::string_view shader_source) {
|
|
for(auto& shader : registered_shaders) {
|
|
if(shader.filename == shader_file) {
|
|
shader.injected_shader_source = shader_source;
|
|
reloading_shader = true;
|
|
shader.reload_function();
|
|
reloading_shader = false;
|
|
}
|
|
}
|
|
}
|