mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 06:49:29 +01:00
Vulkan: scene rendering (D1–D7) on a scene walk shared with GL (N6a, N6b)
This commit is contained in:
@@ -309,6 +309,8 @@ set(SOURCES
|
||||
"gl/cubemap.cpp"
|
||||
"gl/glsl_common.cpp"
|
||||
"rendering/common/shadersource.cpp"
|
||||
"rendering/common/scenewalk.cpp"
|
||||
"rendering/common/locallights.cpp"
|
||||
"gl/buffer.cpp"
|
||||
"gl/fence.cpp"
|
||||
"gl/pbo.cpp"
|
||||
@@ -394,7 +396,6 @@ if (WITH_OPENGL_MODERN)
|
||||
"rendering/opengl46/opengl46weather.cpp"
|
||||
"rendering/opengl46/opengl46exposure.cpp"
|
||||
"rendering/opengl46/opengl46ao.cpp"
|
||||
"rendering/opengl46/opengl46locallights.cpp"
|
||||
"rendering/opengl46/opengl46localshadows.cpp"
|
||||
"rendering/opengl46/opengl46instancecull.cpp"
|
||||
"rendering/opengl46/opengl46hiz.cpp"
|
||||
@@ -433,6 +434,9 @@ if (WITH_VULKAN)
|
||||
"rendering/vulkan14/vulkan14texture.cpp"
|
||||
"rendering/vulkan14/vulkan14imgui.cpp"
|
||||
"rendering/vulkan14/vulkan14renderer.cpp"
|
||||
"rendering/vulkan14/vulkan14geometry.cpp"
|
||||
"rendering/vulkan14/vulkan14scene.cpp"
|
||||
"rendering/vulkan14/vulkan14frame.cpp"
|
||||
"ref/volk/volk.c")
|
||||
set(DEFINITIONS ${DEFINITIONS} "WITH_VULKAN")
|
||||
|
||||
|
||||
@@ -116,7 +116,15 @@ bool check_ogl46(std::string const &Name)
|
||||
bool check_vk14(std::string const &Name)
|
||||
{
|
||||
std::cout << "spir-v..." << std::flush;
|
||||
auto const suc{check_text(gfx::preprocess_shader(gfx::shader_api::vulkan14, Name, {}, {}, false, vk14_dir), "-V --target-env " + vulkan_env)};
|
||||
auto const source{gfx::preprocess_shader(gfx::shader_api::vulkan14, Name, {}, {}, false, vk14_dir)};
|
||||
auto suc{check_text(source, "-V --target-env " + vulkan_env)};
|
||||
// the variant of the passes without the velocity target, of the shaders which have one
|
||||
if (source.text.find("NO_MOTION_OUTPUT") != std::string::npos)
|
||||
{
|
||||
std::cout << "done, no motion output..." << std::flush;
|
||||
if (!check_text(gfx::preprocess_shader(gfx::shader_api::vulkan14, Name, "#define NO_MOTION_OUTPUT 1\n", {}, false, vk14_dir), "-V --target-env " + vulkan_env))
|
||||
suc = false;
|
||||
}
|
||||
std::cout << "done" << std::flush;
|
||||
return suc;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ int opengl_material::texture_slot( std::string const &Key )
|
||||
return lookup->second;
|
||||
}
|
||||
// ignore unrecognized texture bindings in legacy render mode, it's most likely data for more advanced shaders
|
||||
if( Global.GfxRenderer == "default" ) {
|
||||
if( ( Global.GfxRenderer == "default" ) || ( Global.GfxRenderer == "vulkan" ) ) {
|
||||
log_error( "unknown texture binding: " + key );
|
||||
is_good = false;
|
||||
}
|
||||
@@ -175,7 +175,7 @@ void opengl_material::finalize(bool Loadnow)
|
||||
}
|
||||
|
||||
// TBD, TODO: move material validation to renderer, to eliminate branching?
|
||||
if( Global.GfxRenderer == "default" ) {
|
||||
if( ( Global.GfxRenderer == "default" ) || ( Global.GfxRenderer == "vulkan" ) ) {
|
||||
|
||||
if( !shader ) {
|
||||
is_good = false;
|
||||
@@ -222,7 +222,7 @@ void opengl_material::finalize(bool Loadnow)
|
||||
parse_info.reset();
|
||||
}
|
||||
|
||||
if( Global.GfxRenderer != "default" ) {
|
||||
if( ( Global.GfxRenderer != "default" ) && ( Global.GfxRenderer != "vulkan" ) ) {
|
||||
// basic texture validation for legacy branch
|
||||
for( auto texturehandle : textures ) {
|
||||
if( texturehandle == null_handle ) {
|
||||
|
||||
@@ -9,7 +9,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "rendering/opengl46/opengl46locallights.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -107,7 +107,7 @@ void extend_bounds(TSubModel const *Submodel, glm::mat4 const &Transform, gfx::g
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void opengl46_local_lights::gather(glm::dvec3 const &Camera, scene::basic_region &Region, gfx::geometrybank_manager const &Geometry, unsigned int const Framestamp)
|
||||
void gfx::local_lights::gather(glm::dvec3 const &Camera, scene::basic_region &Region, gfx::geometrybank_manager const &Geometry, unsigned int const Framestamp)
|
||||
{
|
||||
auto const start{std::chrono::steady_clock::now()};
|
||||
|
||||
@@ -185,7 +185,7 @@ void opengl46_local_lights::gather(glm::dvec3 const &Camera, scene::basic_region
|
||||
m_gathertime = (m_gathertime == 0.0 ? elapsed : m_gathertime * 0.95 + elapsed * 0.05);
|
||||
}
|
||||
|
||||
void opengl46_local_lights::reset()
|
||||
void gfx::local_lights::reset()
|
||||
{
|
||||
m_lights.clear();
|
||||
m_cablampon = false;
|
||||
@@ -196,7 +196,7 @@ void opengl46_local_lights::reset()
|
||||
m_scenerycount = m_vehiclecount = m_compartmentcount = m_placed = m_dropped = 0;
|
||||
}
|
||||
|
||||
void opengl46_local_lights::gather_instance(TAnimModel *Instance, glm::dvec3 const &Camera, float const Brightness, unsigned int const Framestamp)
|
||||
void gfx::local_lights::gather_instance(TAnimModel *Instance, glm::dvec3 const &Camera, float const Brightness, unsigned int const Framestamp)
|
||||
{
|
||||
if ((false == Instance->m_visible) || (Instance->pModel == nullptr))
|
||||
{
|
||||
@@ -226,7 +226,7 @@ void opengl46_local_lights::gather_instance(TAnimModel *Instance, glm::dvec3 con
|
||||
m_scenerycount += static_cast<std::uint32_t>(m_lights.size() - count);
|
||||
}
|
||||
|
||||
void opengl46_local_lights::gather_vehicle(TDynamicObject *Vehicle, glm::dvec3 const &Camera, gfx::geometrybank_manager const &Geometry, float const Freespotbrightness,
|
||||
void gfx::local_lights::gather_vehicle(TDynamicObject *Vehicle, glm::dvec3 const &Camera, gfx::geometrybank_manager const &Geometry, float const Freespotbrightness,
|
||||
float const Compartmentbrightness)
|
||||
{
|
||||
if ((false == Global.render_cab) && (Global.pCamera.m_owner == Vehicle))
|
||||
@@ -303,7 +303,7 @@ void opengl46_local_lights::gather_vehicle(TDynamicObject *Vehicle, glm::dvec3 c
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_local_lights::gather_cab_lamp(gfx::geometrybank_manager const &Geometry)
|
||||
void gfx::local_lights::gather_cab_lamp(gfx::geometrybank_manager const &Geometry)
|
||||
{
|
||||
// the cab is drawn, Render_cab() conditions
|
||||
if (FreeFlyModeFlag || (false == Global.render_cab) || (simulation::Train == nullptr))
|
||||
@@ -343,7 +343,7 @@ void opengl46_local_lights::gather_cab_lamp(gfx::geometrybank_manager const &Geo
|
||||
m_cablampon = (max_component(m_cablamp.color) > 0.f);
|
||||
}
|
||||
|
||||
void opengl46_local_lights::walk(TSubModel *Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness)
|
||||
void gfx::local_lights::walk(TSubModel *Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness)
|
||||
{
|
||||
// visibility, lod ranges, transforms and animations as in opengl46_renderer::Render_Alpha(TSubModel *)
|
||||
for (auto *submodel{Submodel}; submodel != nullptr; submodel = submodel->Next)
|
||||
@@ -375,7 +375,7 @@ void opengl46_local_lights::walk(TSubModel *Submodel, glm::mat4 const &Transform
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_local_lights::add_light_point(TSubModel const &Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness)
|
||||
void gfx::local_lights::add_light_point(TSubModel const &Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness)
|
||||
{
|
||||
// lit points: with self-illumination, past its threshold. the compartments of vehicles set it with their lights
|
||||
if ((Submodel.f4Emision.a <= 0.f) || (Global.fLuminance >= Submodel.fLight) || (Submodel.fVisible <= 0.f))
|
||||
@@ -409,7 +409,7 @@ void opengl46_local_lights::add_light_point(TSubModel const &Submodel, glm::mat4
|
||||
m_lights.emplace_back(lightpoint);
|
||||
}
|
||||
|
||||
bool opengl46_local_lights::has_light_points(TModel3d *Model)
|
||||
bool gfx::local_lights::has_light_points(TModel3d *Model)
|
||||
{
|
||||
auto lookup{m_modellights.find(Model)};
|
||||
if (lookup != m_modellights.end())
|
||||
@@ -439,7 +439,7 @@ bool opengl46_local_lights::has_light_points(TModel3d *Model)
|
||||
return found;
|
||||
}
|
||||
|
||||
opengl46_local_lights::cell_data const &opengl46_local_lights::cell_instances(scene::basic_cell &Cell, bool const Rebuild)
|
||||
gfx::local_lights::cell_data const &gfx::local_lights::cell_instances(scene::basic_cell &Cell, bool const Rebuild)
|
||||
{
|
||||
// light points are drawn in the translucent phase, so the instances which have them are on the translucent list of the cell
|
||||
auto &instances{Cell.m_instancetranslucent};
|
||||
@@ -460,7 +460,7 @@ opengl46_local_lights::cell_data const &opengl46_local_lights::cell_instances(sc
|
||||
return data;
|
||||
}
|
||||
|
||||
opengl46_local_lights::section_data const &opengl46_local_lights::section(TModel3d *Model, TSubModel *Section, gfx::geometrybank_manager const &Geometry)
|
||||
gfx::local_lights::section_data const &gfx::local_lights::section(TModel3d *Model, TSubModel *Section, gfx::geometrybank_manager const &Geometry)
|
||||
{
|
||||
auto lookup{m_sections.find(Section)};
|
||||
if (lookup != m_sections.end())
|
||||
@@ -483,7 +483,7 @@ opengl46_local_lights::section_data const &opengl46_local_lights::section(TModel
|
||||
return m_sections.emplace(Section, data).first->second;
|
||||
}
|
||||
|
||||
opengl46_local_lights::section_data const &opengl46_local_lights::model_bounds(TModel3d *Model, gfx::geometrybank_manager const &Geometry)
|
||||
gfx::local_lights::section_data const &gfx::local_lights::model_bounds(TModel3d *Model, gfx::geometrybank_manager const &Geometry)
|
||||
{
|
||||
auto lookup{m_models.find(Model)};
|
||||
if (lookup != m_models.end())
|
||||
@@ -502,14 +502,14 @@ opengl46_local_lights::section_data const &opengl46_local_lights::model_bounds(T
|
||||
return m_models.emplace(Model, data).first->second;
|
||||
}
|
||||
|
||||
std::string opengl46_local_lights::info() const
|
||||
std::string gfx::local_lights::info() const
|
||||
{
|
||||
return "local lights: " + std::to_string(m_lights.size()) + " (scenery " + std::to_string(m_scenerycount) + ", vehicles " + std::to_string(m_vehiclecount) + ", compartments " +
|
||||
std::to_string(m_compartmentcount) + ")" + (m_cablampon ? " + cab lamp" : "") + ", placed " + std::to_string(m_placed) + ", over capacity " + std::to_string(m_dropped) +
|
||||
", gather " + to_string(m_gathertime, 2) + " ms";
|
||||
}
|
||||
|
||||
void opengl46_local_lights::draw_debug_ui()
|
||||
void gfx::local_lights::draw_debug_ui()
|
||||
{
|
||||
ImGui::TextUnformatted("local lights (need clustered lights)");
|
||||
ImGui::Checkbox("light points of scenery models", &Global.gfx_lights_scenery);
|
||||
@@ -28,15 +28,18 @@ class TSubModel;
|
||||
class TAnimModel;
|
||||
class TDynamicObject;
|
||||
|
||||
// lights of the opengl 4.6 renderer other than the sun and the vehicle headlights, which go to the light clusters:
|
||||
// lights of the scene other than the sun and the vehicle headlights, which go to the light clusters of the renderers:
|
||||
// - the light points (freespot lights) of scenery models and of vehicles, taken the way the experimental renderer takes them:
|
||||
// a point which is visible, has self-illumination (emission) and is past its self-illumination threshold shines as a spot
|
||||
// light along the -z axis of its submodel, with the cone of its glare. lamps of scenery models switched with Light_On / Light_Off
|
||||
// and the self-illumination of vehicle compartments (TDynamicObject::Sections) work through the same state
|
||||
// - the lit compartments of vehicles (low poly interior sections) without light points of their own: a point light in the middle
|
||||
// - the lamp of the occupied cab: a point light under the top of the cab model, in place of the ambient light it used to add
|
||||
// gathered once per frame around the camera, written to the light buffer by opengl46_renderer::Update_Lights()
|
||||
class opengl46_local_lights
|
||||
// gathered once per frame around the camera, written to the light buffers by the renderers (Update_Lights())
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
class local_lights
|
||||
{
|
||||
public:
|
||||
struct light
|
||||
@@ -53,9 +56,9 @@ class opengl46_local_lights
|
||||
void const *part{nullptr};
|
||||
};
|
||||
|
||||
opengl46_local_lights() = default;
|
||||
opengl46_local_lights(const opengl46_local_lights &) = delete;
|
||||
opengl46_local_lights &operator=(const opengl46_local_lights &) = delete;
|
||||
local_lights() = default;
|
||||
local_lights(const local_lights &) = delete;
|
||||
local_lights &operator=(const local_lights &) = delete;
|
||||
|
||||
// collects the lights within Global.gfx_lights_range of Camera, for the frame about to be drawn. it sets the state of the
|
||||
// shared models of the instances and vehicles it goes through, as the render does before it draws each of them.
|
||||
@@ -139,3 +142,5 @@ class opengl46_local_lights
|
||||
std::uint32_t m_dropped{0};
|
||||
double m_gathertime{0.0}; // milliseconds, smoothed
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
3062
rendering/common/scenewalk.cpp
Normal file
3062
rendering/common/scenewalk.cpp
Normal file
File diff suppressed because it is too large
Load Diff
346
rendering/common/scenewalk.h
Normal file
346
rendering/common/scenewalk.h
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// the walk of the scene shared by the renderers (stage N6 of claude/vulkan14-renderer-plan.md, decision K11): the decisions of
|
||||
// what is drawn in a pass (the sections, cells, tracks, shapes, models, instances, vehicles, the cab, the translucent parts),
|
||||
// in which order, with which material, transform and level of detail. a renderer derives from it and gets the draws through
|
||||
// the walk_* methods: the opengl renderer makes them at once (the same gl calls in the same order as before the walk was
|
||||
// shared), the vulkan renderer records them. what the backend may draw itself (the gpu culling of the opengl renderer) is
|
||||
// asked about with the walk_* methods which do nothing by default.
|
||||
//
|
||||
// the walk keeps the state of the draw in model_ubs (the block of the model of the shaders) and the transform on the matrix
|
||||
// stack of OpenGLMatrices (a cpu stack, despite the name)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "rendering/openglcamera.h"
|
||||
#include "scene/scene.h"
|
||||
#include "world/Track.h"
|
||||
#include "world/TrackSleepers.h"
|
||||
#include "model/material.h"
|
||||
#include "gl/ubo.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class TAnimModel;
|
||||
class TDynamicObject;
|
||||
class TModel3d;
|
||||
class TSubModel;
|
||||
class TTraction;
|
||||
class TMemCell;
|
||||
struct material_data;
|
||||
struct lighting_data;
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
class scene_walk
|
||||
{
|
||||
public:
|
||||
virtual ~scene_walk() = default;
|
||||
|
||||
protected:
|
||||
// types
|
||||
enum class rendermode
|
||||
{
|
||||
none,
|
||||
color,
|
||||
shadows,
|
||||
reflections,
|
||||
pickcontrols,
|
||||
pickscenery
|
||||
};
|
||||
|
||||
struct debug_stats
|
||||
{
|
||||
int dynamics{0};
|
||||
int models{0};
|
||||
int submodels{0};
|
||||
int paths{0};
|
||||
int traction{0};
|
||||
int shapes{0};
|
||||
int lines{0};
|
||||
int particles{0};
|
||||
int drawcalls{0};
|
||||
int instanced_drawcalls{0}; // drawcalls issued via instanced render path (one per TModel3d batch)
|
||||
int instances{0}; // total instances drawn via instanced render path
|
||||
int triangles{0};
|
||||
|
||||
debug_stats &operator+=(const debug_stats &Right)
|
||||
{
|
||||
dynamics += Right.dynamics;
|
||||
models += Right.models;
|
||||
submodels += Right.submodels;
|
||||
paths += Right.paths;
|
||||
traction += Right.traction;
|
||||
shapes += Right.shapes;
|
||||
lines += Right.lines;
|
||||
particles += Right.particles;
|
||||
drawcalls += Right.drawcalls;
|
||||
instanced_drawcalls += Right.instanced_drawcalls;
|
||||
instances += Right.instances;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
using section_sequence = std::vector<scene::basic_section *>;
|
||||
using distancecell_pair = std::pair<double, scene::basic_cell *>;
|
||||
using cell_sequence = std::vector<distancecell_pair>;
|
||||
|
||||
struct renderpass_config
|
||||
{
|
||||
opengl_camera pass_camera;
|
||||
opengl_camera viewport_camera;
|
||||
rendermode draw_mode{rendermode::none};
|
||||
float draw_range{0.0f};
|
||||
// depth range of the projection of a color pass; the far plane is only the end of the range which is drawn,
|
||||
// as the projection matrix itself has none
|
||||
float znear{0.0f};
|
||||
float zfar{0.0f};
|
||||
debug_stats draw_stats;
|
||||
};
|
||||
|
||||
// an instance of a bucket of a scene cell, queued to be drawn by Render_Instanced()
|
||||
struct queued_instance
|
||||
{
|
||||
scene::basic_cell::instance_bucket const *bucket;
|
||||
std::size_t index; // of the instance and its record in the bucket
|
||||
};
|
||||
|
||||
// the programs of the draws which aren't drawn with the programs of their materials
|
||||
enum class walk_shader
|
||||
{
|
||||
pick, // a colour for the picking of the scenery or the controls
|
||||
pick_surface, // the gradient of a touch screen for the picking of the controls
|
||||
line, // the wires of the traction and the lines
|
||||
billboard, // the glare of a lamp
|
||||
freespot // the point of light of a lamp
|
||||
};
|
||||
|
||||
// how far the reflection pass draws the simplified versions of things, as if they were farther away
|
||||
static int constexpr EU07_REFLECTIONFIDELITYOFFSET{250};
|
||||
|
||||
// the walk
|
||||
void Render(scene::basic_region *Region);
|
||||
void Render(section_sequence::iterator First, section_sequence::iterator Last);
|
||||
void Render(cell_sequence::iterator First, cell_sequence::iterator Last);
|
||||
void Render(scene::shape_node const &Shape, bool const Ignorerange);
|
||||
void Render(TAnimModel *Instance);
|
||||
// batched render path for many TAnimModel instances that share the same TModel3d.
|
||||
// Caller guarantees every instance was instanceable when bucketed (no per-instance lights,
|
||||
// no submodel animation, same replacable skins); the records of the instances tell which
|
||||
// ones still are. Instances are individually frustum/distance culled by their records, the
|
||||
// TAnimModel itself is read only for the first instance drawn. Each lod band drawn counts as
|
||||
// one instanced_drawcall in draw_stats; the instances drawn are added to the instances counter.
|
||||
// Bandcounts, when given, gets the number of instances drawn in each lod band.
|
||||
void Render_Instanced(TModel3d *Model, std::vector<queued_instance> const &Instances, std::vector<std::uint32_t> *Bandcounts = nullptr);
|
||||
// adds the bucketed instances of a scene cell to the frame-level buckets drawn by Render_Instanced(),
|
||||
// less the ones their records show to be out of sight in the current render pass
|
||||
void Queue_Instances(scene::basic_cell const *Cell);
|
||||
// makes the records of the instances held by a bucket of a scene cell, with locations relative to Origin
|
||||
void Update_Instance_Records(TModel3d const *Model, scene::basic_cell::instance_bucket const &Bucket, glm::dvec3 const &Origin);
|
||||
// whether none of the instances of a bucket can pass the culling of the current pass, by the bounds of their records
|
||||
bool Bucket_Culled(scene::basic_cell::instance_bucket const &Bucket) const;
|
||||
// the sphere around the locations of the visible instances of a bucket, with a margin for the precision of the records
|
||||
static scene::bounding_area Bucket_Area(scene::basic_cell::instance_bucket const &Bucket);
|
||||
bool Render(TDynamicObject *Dynamic);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render(TSubModel *Submodel);
|
||||
void Render(TTrack *Track);
|
||||
void Render(scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last);
|
||||
// renders the per-track sleeper instances (TTrack::m_sleeper_local_transforms) via GPU instancing.
|
||||
// caller must already have the camera-relative world-space transform set on the matrix stack.
|
||||
// no-op if the track has no sleepermodel, Global.SleeperDistance is 0, or the camera is beyond
|
||||
// Global.SleeperDistance from the track origin.
|
||||
void Render_Sleepers(TTrack *Track);
|
||||
// renders the sleepers and switch timbers of the tracks in range (TTrack::m_sleepers3d), laying them first where they're out of date.
|
||||
// the parts of the model are drawn in instanced batches for the whole run of tracks, the runs of beams out of sight left out
|
||||
void Render_Timbers(scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last);
|
||||
bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false);
|
||||
bool Render_interior(bool const Alpha = false);
|
||||
bool Render_lowpoly(TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false);
|
||||
bool Render_coupler_adapter(TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false);
|
||||
void Render(TMemCell *Memcell);
|
||||
void Render_Alpha(scene::basic_region *Region);
|
||||
void Render_Alpha(cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last);
|
||||
void Render_Alpha(TAnimModel *Instance);
|
||||
void Render_Alpha(TTraction *Traction);
|
||||
void Render_Alpha(scene::lines_node const &Lines);
|
||||
bool Render_Alpha(TDynamicObject *Dynamic);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render_Alpha(TSubModel *Submodel);
|
||||
void Render_Alpha_text(TSubModel *Submodel); // characters of a text display
|
||||
|
||||
// the colour of the picking of the item of the index, and the index of a colour read back
|
||||
static glm::vec3 pick_color(std::size_t const Index);
|
||||
static std::size_t pick_index(glm::ivec3 const &Color);
|
||||
// the range the top view of the editor draws the tracks to, and the parts of the scenery it leaves out
|
||||
static float editor_track_view_range(float const Range, bool const Apply);
|
||||
static bool editor_ballast_hidden(TTrack const *Track, bool const Colour);
|
||||
static bool editor_far_tracks_only(bool const Apply, scene::basic_cell const *Cell, glm::dvec3 const &Camera, double const SceneryLimit);
|
||||
// material assigned to the submodel, resolving replacable skins. null_handle if no skin set is bound
|
||||
static material_handle submodel_material(TSubModel const *Submodel);
|
||||
|
||||
// what the backend does with the draws of the walk
|
||||
// binds the material for the colour passes: its program, its textures and its parameters in model_ubs (opacity, alpha_mult,
|
||||
// the params of the submodel or of the lighting of a shape)
|
||||
virtual void walk_material(material_handle const Material, TSubModel const *Submodel = nullptr, lighting_data const *Lighting = nullptr) = 0;
|
||||
// the same for the shadow passes: the program of the shadows, with the alpha test of the diffuse texture when there is one
|
||||
virtual void walk_shadow_material(material_handle const Material) = 0;
|
||||
virtual opengl_material const &walk_material_data(material_handle const Material) const = 0;
|
||||
virtual opengl_material const &walk_material_data(TSubModel const *Submodel) const = 0;
|
||||
virtual void walk_program(walk_shader const Program) = 0;
|
||||
virtual void walk_texture(std::size_t const Unit, texture_handle const Texture) = 0;
|
||||
// what follows is translucent (blended, without the depth written) or opaque, culled as the pass culls
|
||||
virtual void walk_blending(bool const Alpha) = 0;
|
||||
// the strength of the sun and the ambient light, for things in the shade (tunnels, vehicles in them), 1: the full light
|
||||
virtual void walk_sunlight(float const Factor = 1.f) = 0;
|
||||
virtual void walk_environment(TEnvironmentType const Environment = e_flat) = 0;
|
||||
// the ambient light of the cab: kept, added to (the lamp of the cab), put back
|
||||
virtual void walk_ambient_save() = 0;
|
||||
virtual void walk_ambient_add(glm::vec3 const &Addition) = 0;
|
||||
virtual void walk_ambient_restore() = 0;
|
||||
// the ambient light of the sun, which lights the lines
|
||||
virtual glm::vec4 walk_sun_ambient() const = 0;
|
||||
// a draw of the chunk with the state of the walk: model_ubs with the modelview of OpenGLMatrices, and the instances of
|
||||
// m_current_instance_count / m_current_instance_base when there are any. the second draws the chunks together
|
||||
virtual void walk_draw(gfx::geometry_handle const &Geometry) = 0;
|
||||
virtual void walk_draw(std::vector<gfx::geometrybank_handle>::iterator First, std::vector<gfx::geometrybank_handle>::iterator Last) = 0;
|
||||
// places the modelview matrices of instances in the instance buffer of the frame. returns: base instance of the first of them
|
||||
virtual std::uint32_t walk_instances(glm::mat4 const *Modelviews, std::size_t const Count) = 0;
|
||||
virtual void walk_cull_face(bool const Enabled) = 0;
|
||||
// the glare of a lamp: drawn without the depth test, added to what is there; drawn only where the point of the light was
|
||||
// seen (walk_light_query() of the last frame, when the backend has it)
|
||||
virtual void walk_glare(bool const Enabled) = 0;
|
||||
virtual void walk_glare_draw(TSubModel *Submodel, gfx::geometry_handle const &Geometry) = 0;
|
||||
virtual void walk_light_query(TSubModel *Submodel, bool const Begin) = 0;
|
||||
// what the backend may draw itself (the gpu culling of the opengl renderer); by default the walk draws everything
|
||||
// the start of the walk of a region
|
||||
virtual void walk_region_begin() {}
|
||||
// the opaque ground of the colour pass is drawn, the editor takes the position of the cursor from the depth
|
||||
virtual void walk_editor_cursor() {}
|
||||
// a bucket of instances of a cell: false when the backend took the instances (the walk leaves them out); Area is the cell's
|
||||
virtual bool walk_bucket(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, bool const Bucketrange, scene::bounding_area const &Area)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// a bucket left out of the pass as wholly out of sight
|
||||
virtual void walk_bucket_culled(scene::basic_cell::instance_bucket const &Bucket) {}
|
||||
// a model drawn on its own: true when the backend took it. Inrange: the cell of the instance lies within the range of the pass
|
||||
virtual bool walk_queue_model(TAnimModel *Instance, bool const Inrange)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// the sleepers of a track: true when the backend took them
|
||||
virtual bool walk_queue_sleepers(TTrack *Track)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// the sleepers the walk drew for a track, by their squared distances, and the lod bounds of their model
|
||||
virtual void walk_sleepers_drawn(TTrack const &Track, std::vector<float> const &Lodbounds, std::vector<std::pair<float, glm::mat4>> const &Drawn) {}
|
||||
// the instances of the cells are about to be drawn: whether the walk reports what it drew of each bucket (walk_instances_compare())
|
||||
virtual bool walk_instances_begin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual void walk_instances_compare(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts) {}
|
||||
virtual void walk_instances_end() {}
|
||||
|
||||
// members
|
||||
renderpass_config m_renderpass; // parameters for current render pass
|
||||
section_sequence m_sectionqueue; // list of sections in current render pass
|
||||
cell_sequence m_cellqueue;
|
||||
// frame-level accumulation of per-cell opaque instance buckets. Each visited
|
||||
// cell's buckets are merged here keyed by (TModel3d*, skins), so that
|
||||
// Render_Instanced() runs once per unique model across the whole pass instead
|
||||
// of once per cell -- collapsing many tiny instanced draws into a few large
|
||||
// batches. Reused every pass; the vectors are emptied (not freed) at the top of Render(scene::basic_region*).
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, std::vector<queued_instance>, scene::basic_cell::instance_bucket_key_hash> m_frame_instance_buckets;
|
||||
// geometry of neighbouring tracks of a cell which share the material, gathered to be drawn together. reused by each Render(path_sequence)
|
||||
std::vector<gfx::geometrybank_handle> m_pathbatch;
|
||||
// instance records kept by the buckets of scene cells are good as long as they carry this number.
|
||||
// it changes with each frame drawn with the scenery editor active, as the editor moves and resizes instances in place,
|
||||
// and whenever scene::node_state_version does, as instances are shown, hidden or drawn on their own
|
||||
unsigned int m_instancerecordsversion{1};
|
||||
// the model block of the draw being made
|
||||
gl::model_ubs model_ubs;
|
||||
unsigned int m_framestamp{0}; // id of currently rendered gfx frame
|
||||
unsigned int m_timbersweepframe{0}; // the frame the beams of the tracks far away were last let go in
|
||||
float m_fogrange{2000.0f};
|
||||
// when > 0, the walk is inside a Render_Instanced() call: the draws are made for this many instances from
|
||||
// m_current_instance_base onwards. The vertex shader reads per-instance modelview from the instance buffer
|
||||
std::size_t m_current_instance_count{0};
|
||||
std::uint32_t m_current_instance_base{0};
|
||||
// camera-space transforms of the sleepers and timbers drawn by Render_Timbers, by the model and its part or its level of
|
||||
// detail; kept from frame to frame to reuse their allocations
|
||||
struct timber_bucket
|
||||
{
|
||||
track_sleepers::beam_model const *model{nullptr};
|
||||
int part{-1}; // of a model put together from parts; -1: the whole model
|
||||
float band{0.f}; // the squared distance the level of detail of the whole model is seen from
|
||||
std::vector<glm::mat4> instances;
|
||||
};
|
||||
std::vector<timber_bucket> m_timberbuckets;
|
||||
// persistent scratch buffer for Render_Instanced(): holds the per-instance
|
||||
// camera-space root modelview matrices for the batch currently being built.
|
||||
// Kept as a member rather than a function-local so its heap allocation is
|
||||
// reused across calls -- clear() retains capacity, so once it has grown to
|
||||
// the largest batch seen, steady-state frames perform no allocation here.
|
||||
std::vector<glm::mat4> m_instance_modelviews;
|
||||
// further persistent scratch buffers for Render_Instanced(): culled instances paired with their
|
||||
// squared distance, and the lod bounds of the drawn model which split them into lod bands
|
||||
struct instance_survivor
|
||||
{
|
||||
glm::mat4 modelview;
|
||||
float distancesquared;
|
||||
std::uint32_t band;
|
||||
};
|
||||
std::vector<instance_survivor> m_instance_survivors;
|
||||
std::vector<float> m_instance_lodbounds;
|
||||
std::vector<TSubModel const *> m_instance_lodpending;
|
||||
// per sort key (lod band and distance slot): first index in m_instance_modelviews (with end sentinel)
|
||||
// and fill cursor; per lod band: nearest distance
|
||||
std::vector<std::size_t> m_instance_bandstarts;
|
||||
std::vector<std::size_t> m_instance_bandcursors;
|
||||
std::vector<float> m_instance_banddistances;
|
||||
std::vector<std::uint32_t> m_instance_bandcounts; // reported by Render_Instanced() for the comparison
|
||||
// comparison of the gpu culling: the key of the model Render( TAnimModel * ) is called for, and the models it drew in
|
||||
// the pass with their scaled squared distances
|
||||
scene::basic_cell::instance_bucket_key const *m_modelcomparekey{nullptr};
|
||||
std::vector<std::pair<scene::basic_cell::instance_bucket_key, float>> m_modelcpudistances;
|
||||
bool m_indirectchanging{false}; // the walk under way met a submodel which may look different in the next frame
|
||||
// a vehicle drawn in the colour pass of this frame moved (motion blur)
|
||||
bool m_vehiclesmoving{false};
|
||||
// set while a preview is drawn: submodels faded out entirely are skipped, instead of being drawn invisible over the stencil
|
||||
bool m_previewing{false};
|
||||
// the last lighting of the pass placed the cab lamp among the lights: the cab is lit by it, not by an addition to the ambient light
|
||||
bool m_cablamplit{false};
|
||||
// the shadow pass draws the kept scenery part of the map of a lamp: no vehicles, and not the model of the lamp itself
|
||||
bool m_cubescenery{false};
|
||||
TAnimModel const *m_cubeskipinstance{nullptr};
|
||||
// the shadow pass leaves this vehicle out (the one the headlights of the map being drawn belong to)
|
||||
TDynamicObject const *m_shadowskipvehicle{nullptr};
|
||||
// what the picking passes drew, by the index of their colours
|
||||
std::vector<TSubModel const *> m_pickcontrolsitems;
|
||||
std::vector<TSubModel const *> m_picksurfaceitems;
|
||||
std::vector<scene::basic_node *> m_picksceneryitems;
|
||||
// the quad of the glare of lamps and its texture
|
||||
gfx::geometry_handle m_billboardgeometry{0, 0};
|
||||
texture_handle m_glaretexture{-1};
|
||||
|
||||
private:
|
||||
static float editor_ortho_reach();
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
@@ -350,7 +350,7 @@ glm::vec4 opengl46_local_shadows::weights() const
|
||||
return glm::vec4{static_cast<float>(CAB_SLOT), SHADOW_NEAR, 2.f / static_cast<float>(std::max(m_size, 1)), setup(m_level).radius};
|
||||
}
|
||||
|
||||
void opengl46_local_shadows::update_lamps(std::vector<opengl46_local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp)
|
||||
void opengl46_local_shadows::update_lamps(std::vector<gfx::local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp)
|
||||
{
|
||||
m_frame = Framestamp;
|
||||
m_wanted.clear();
|
||||
@@ -368,7 +368,7 @@ void opengl46_local_shadows::update_lamps(std::vector<opengl46_local_lights::lig
|
||||
m_sceneryleft = setup(level).sceneryperframe;
|
||||
auto const reach{static_cast<double>(Global.gfx_lights_shadows_lamps_range)};
|
||||
// the lights come the most important first: the first ones within reach get the slots
|
||||
std::vector<opengl46_local_lights::light const *> picked;
|
||||
std::vector<gfx::local_lights::light const *> picked;
|
||||
for (auto const &light : Lights)
|
||||
{
|
||||
if (picked.size() >= m_slots.size())
|
||||
@@ -382,7 +382,7 @@ void opengl46_local_shadows::update_lamps(std::vector<opengl46_local_lights::lig
|
||||
picked.emplace_back(&light);
|
||||
}
|
||||
// lamps which already have a slot keep it
|
||||
std::vector<opengl46_local_lights::light const *> unplaced;
|
||||
std::vector<gfx::local_lights::light const *> unplaced;
|
||||
for (auto const *light : picked)
|
||||
{
|
||||
auto placed{false};
|
||||
@@ -520,7 +520,7 @@ void opengl46_local_shadows::lamps_timer_end()
|
||||
m_lampscputime = (m_lampscputime == 0.0 ? elapsed : m_lampscputime * 0.9 + elapsed * 0.1);
|
||||
}
|
||||
|
||||
opengl46_local_shadows::lamp_slot const *opengl46_local_shadows::lamp(opengl46_local_lights::light const &Light, std::size_t &Slot) const
|
||||
opengl46_local_shadows::lamp_slot const *opengl46_local_shadows::lamp(gfx::local_lights::light const &Light, std::size_t &Slot) const
|
||||
{
|
||||
if (Light.owner == nullptr)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "gl/shader.h"
|
||||
#include "gl/query.h"
|
||||
#include "rendering/opengl46/opengl46locallights.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
#include "rendering/lightarray.h"
|
||||
|
||||
class TDynamicObject;
|
||||
@@ -98,7 +98,7 @@ class opengl46_local_shadows
|
||||
// a lamp with a slot
|
||||
struct lamp_slot
|
||||
{
|
||||
void const *owner{nullptr}; // the light it is, see opengl46_local_lights::light
|
||||
void const *owner{nullptr}; // the light it is, see gfx::local_lights::light
|
||||
void const *part{nullptr};
|
||||
glm::dvec3 position{0.0}; // world space
|
||||
float range{0.f}; // of the light, metres
|
||||
@@ -111,7 +111,7 @@ class opengl46_local_shadows
|
||||
// picks the lamps which get shadows in this frame from the gathered lights (the most important first): the scenery lamps
|
||||
// within reach of Camera. a lamp keeps its slot while it is picked; one which got a slot or moved, and all of them when the
|
||||
// scene changes (Version), need their scenery drawn. (re)makes the maps when their count or size changed
|
||||
void update_lamps(std::vector<opengl46_local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp);
|
||||
void update_lamps(std::vector<gfx::local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp);
|
||||
// forgets the lamps: the scene they come from is about to be replaced (a scenery being loaded)
|
||||
void reset_lamps();
|
||||
// slots of the lamps picked in this frame
|
||||
@@ -141,7 +141,7 @@ class opengl46_local_shadows
|
||||
void lamps_timer_begin();
|
||||
void lamps_timer_end();
|
||||
// the slot of the lamp the light is, if it has its shadows ready, nullptr otherwise
|
||||
lamp_slot const *lamp(opengl46_local_lights::light const &Light, std::size_t &Slot) const;
|
||||
lamp_slot const *lamp(gfx::local_lights::light const &Light, std::size_t &Slot) const;
|
||||
// the headlight_weights of the light of the lamp in the slot, see weights()
|
||||
glm::vec4 lamp_weights(std::size_t const Slot) const;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,14 +40,15 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/opengl46/opengl46weather.h"
|
||||
#include "rendering/opengl46/opengl46exposure.h"
|
||||
#include "rendering/opengl46/opengl46ao.h"
|
||||
#include "rendering/opengl46/opengl46locallights.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
#include "rendering/opengl46/opengl46localshadows.h"
|
||||
#include "rendering/opengl46/opengl46instancecull.h"
|
||||
#include "rendering/opengl46/opengl46hiz.h"
|
||||
#include "rendering/opengl46/opengl46shadowfit.h"
|
||||
#include "rendering/common/scenewalk.h"
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class opengl46_renderer : public gfx_renderer {
|
||||
class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
|
||||
public:
|
||||
// constructors
|
||||
opengl46_renderer() = default;
|
||||
@@ -189,64 +190,42 @@ class opengl46_renderer : public gfx_renderer {
|
||||
static gfx_backend_desc backend_desc();
|
||||
|
||||
private:
|
||||
using gfx::scene_walk::Render;
|
||||
using gfx::scene_walk::Render_Alpha;
|
||||
// the draws of the shared walk of the scene
|
||||
void walk_material(material_handle const Material, TSubModel const *Submodel = nullptr, lighting_data const *Lighting = nullptr) override;
|
||||
void walk_shadow_material(material_handle const Material) override;
|
||||
opengl_material const &walk_material_data(material_handle const Material) const override;
|
||||
opengl_material const &walk_material_data(TSubModel const *Submodel) const override;
|
||||
void walk_program(walk_shader const Program) override;
|
||||
void walk_texture(std::size_t const Unit, texture_handle const Texture) override;
|
||||
void walk_blending(bool const Alpha) override;
|
||||
void walk_sunlight(float const Factor = 1.f) override;
|
||||
void walk_environment(TEnvironmentType const Environment = e_flat) override;
|
||||
void walk_ambient_save() override;
|
||||
void walk_ambient_add(glm::vec3 const &Addition) override;
|
||||
void walk_ambient_restore() override;
|
||||
glm::vec4 walk_sun_ambient() const override;
|
||||
void walk_draw(gfx::geometry_handle const &Geometry) override;
|
||||
void walk_draw(std::vector<gfx::geometrybank_handle>::iterator First, std::vector<gfx::geometrybank_handle>::iterator Last) override;
|
||||
std::uint32_t walk_instances(glm::mat4 const *Modelviews, std::size_t const Count) override;
|
||||
void walk_cull_face(bool const Enabled) override;
|
||||
void walk_glare(bool const Enabled) override;
|
||||
void walk_glare_draw(TSubModel *Submodel, gfx::geometry_handle const &Geometry) override;
|
||||
void walk_light_query(TSubModel *Submodel, bool const Begin) override;
|
||||
// the gpu culling of the instances, models and sleepers, the depth pass of the whole scene
|
||||
void walk_region_begin() override;
|
||||
void walk_editor_cursor() override;
|
||||
bool walk_bucket(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, bool const Bucketrange, scene::bounding_area const &Area) override;
|
||||
void walk_bucket_culled(scene::basic_cell::instance_bucket const &Bucket) override;
|
||||
bool walk_queue_model(TAnimModel *Instance, bool const Inrange) override;
|
||||
bool walk_queue_sleepers(TTrack *Track) override;
|
||||
void walk_sleepers_drawn(TTrack const &Track, std::vector<float> const &Lodbounds, std::vector<std::pair<float, glm::mat4>> const &Drawn) override;
|
||||
bool walk_instances_begin() override;
|
||||
void walk_instances_compare(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts) override;
|
||||
void walk_instances_end() override;
|
||||
|
||||
// types
|
||||
enum class rendermode
|
||||
{
|
||||
none,
|
||||
color,
|
||||
shadows,
|
||||
reflections,
|
||||
pickcontrols,
|
||||
pickscenery
|
||||
};
|
||||
|
||||
struct debug_stats
|
||||
{
|
||||
int dynamics{0};
|
||||
int models{0};
|
||||
int submodels{0};
|
||||
int paths{0};
|
||||
int traction{0};
|
||||
int shapes{0};
|
||||
int lines{0};
|
||||
int particles{0};
|
||||
int drawcalls{0};
|
||||
int instanced_drawcalls{0}; // drawcalls issued via instanced render path (one per TModel3d batch)
|
||||
int instances{0}; // total instances drawn via instanced render path
|
||||
int triangles{0};
|
||||
|
||||
debug_stats& operator+=( const debug_stats& Right ) {
|
||||
dynamics += Right.dynamics;
|
||||
models += Right.models;
|
||||
submodels += Right.submodels;
|
||||
paths += Right.paths;
|
||||
traction += Right.traction;
|
||||
shapes += Right.shapes;
|
||||
lines += Right.lines;
|
||||
particles += Right.particles;
|
||||
drawcalls += Right.drawcalls;
|
||||
instanced_drawcalls += Right.instanced_drawcalls;
|
||||
instances += Right.instances;
|
||||
return *this; }
|
||||
};
|
||||
|
||||
using section_sequence = std::vector<scene::basic_section *>;
|
||||
using distancecell_pair = std::pair<double, scene::basic_cell *>;
|
||||
using cell_sequence = std::vector<distancecell_pair>;
|
||||
|
||||
struct renderpass_config
|
||||
{
|
||||
opengl_camera pass_camera;
|
||||
opengl_camera viewport_camera;
|
||||
rendermode draw_mode{rendermode::none};
|
||||
float draw_range{0.0f};
|
||||
// depth range of the projection of a color pass; the far plane is only the end of the range which is drawn,
|
||||
// as the projection matrix itself has none
|
||||
float znear{0.0f};
|
||||
float zfar{0.0f};
|
||||
debug_stats draw_stats;
|
||||
};
|
||||
|
||||
struct viewport_config {
|
||||
int width;
|
||||
int height;
|
||||
@@ -349,33 +328,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// Position in world space, Rotation from world axes to its axes; Near is the distance of the near plane from the projector
|
||||
void Headlight_Projector(light_array::light_record const &Light, glm::dvec3 &Position, glm::mat3 &Rotation, glm::mat4 &Projection, float &Near, float &Fieldofview);
|
||||
bool Render(world_environment *Environment);
|
||||
void Render(scene::basic_region *Region);
|
||||
void Render(section_sequence::iterator First, section_sequence::iterator Last);
|
||||
void Render(cell_sequence::iterator First, cell_sequence::iterator Last);
|
||||
void Render(scene::shape_node const &Shape, bool const Ignorerange);
|
||||
void Render(TAnimModel *Instance);
|
||||
// an instance of a bucket of a scene cell, queued to be drawn by Render_Instanced()
|
||||
struct queued_instance {
|
||||
scene::basic_cell::instance_bucket const *bucket;
|
||||
std::size_t index; // of the instance and its record in the bucket
|
||||
};
|
||||
// batched render path for many TAnimModel instances that share the same TModel3d.
|
||||
// Caller guarantees every instance was instanceable when bucketed (no per-instance lights,
|
||||
// no submodel animation, same replacable skins); the records of the instances tell which
|
||||
// ones still are. Instances are individually frustum/distance culled by their records, the
|
||||
// TAnimModel itself is read only for the first instance drawn. Each lod band drawn counts as
|
||||
// one instanced_drawcall in draw_stats; the instances drawn are added to the instances counter.
|
||||
// Bandcounts, when given, gets the number of instances drawn in each lod band.
|
||||
void Render_Instanced( TModel3d *Model, std::vector<queued_instance> const &Instances, std::vector<std::uint32_t> *Bandcounts = nullptr );
|
||||
// adds the bucketed instances of a scene cell to the frame-level buckets drawn by Render_Instanced(),
|
||||
// less the ones their records show to be out of sight in the current render pass
|
||||
void Queue_Instances( scene::basic_cell const *Cell );
|
||||
// makes the records of the instances held by a bucket of a scene cell, with locations relative to Origin
|
||||
void Update_Instance_Records( TModel3d const *Model, scene::basic_cell::instance_bucket const &Bucket, glm::dvec3 const &Origin );
|
||||
// whether none of the instances of a bucket can pass the culling of the current pass, by the bounds of their records
|
||||
bool Bucket_Culled( scene::basic_cell::instance_bucket const &Bucket ) const;
|
||||
// the sphere around the locations of the visible instances of a bucket, with a margin for the precision of the records
|
||||
static scene::bounding_area Bucket_Area( scene::basic_cell::instance_bucket const &Bucket );
|
||||
// places supplied modelview matrices in the instance buffer of the frame. returns: base instance of the first of them
|
||||
std::uint32_t Upload_Instance_Modelviews( glm::mat4 const *Modelviews, std::size_t const Count );
|
||||
// binds the instance buffer of the frame for the vertex shaders (INSTANCE_SSBO_BINDING)
|
||||
@@ -479,39 +431,20 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::array<std::optional<std::array<glm::vec3, 8>>, gl::MAX_CASCADES> &Areas );
|
||||
// whether the depth pass of the whole scene draws the material bound last (Bind_Material()) and the geometry
|
||||
bool Scene_Depth_Draws( gfx::geometry_handle const &Geometry ) const;
|
||||
bool Render(TDynamicObject *Dynamic);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render(TSubModel *Submodel);
|
||||
void Render(TTrack *Track);
|
||||
void Render(scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last);
|
||||
// renders the per-track sleeper instances (TTrack::m_sleeper_local_transforms) via GPU instancing.
|
||||
// caller must already have the camera-relative world-space transform set on the matrix stack.
|
||||
// no-op if the track has no sleepermodel, Global.SleeperDistance is 0, or the camera is beyond
|
||||
// Global.SleeperDistance from the track origin.
|
||||
void Render_Sleepers( TTrack *Track );
|
||||
// queues the sleepers of a track in the gpu culling of the pass (Global.gfx_gpucull_sleepers), with the gates of
|
||||
// Render_Sleepers(), making their records first when they're missing or out of date
|
||||
void Queue_Sleepers( TTrack *Track );
|
||||
// renders the sleepers and switch timbers of the tracks in range (TTrack::m_sleepers3d), laying them first where they're out of date.
|
||||
// the parts of the model are drawn in instanced batches for the whole run of tracks, the runs of beams out of sight left out
|
||||
void Render_Timbers( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last );
|
||||
// queues a model instance drawn on its own in the gpu culling of the pass (Global.gfx_gpucull_models), as a bucket of
|
||||
// its own, making its record first when it's missing or out of date. Inrange: the cell of the instance lies within the
|
||||
// range of the pass. returns true when the instance isn't drawn by the cpu in the pass: it's drawn from the result of the
|
||||
// culling, or it was culled and recorded by the depth pass of the whole scene, or it lies out of range
|
||||
bool Queue_Model( TAnimModel *Instance, bool const Inrange );
|
||||
bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false);
|
||||
bool Render_interior( bool const Alpha = false );
|
||||
bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
bool Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false );
|
||||
// releases gl textures used only by vehicles farther than gfx.textures.releasedistance times the draw range
|
||||
void Update_Texture_Release();
|
||||
// textures used by materials of the sub-models of specified model, replaceable skins excluded
|
||||
std::vector<texture_handle> const &Model_Textures( TModel3d *Model );
|
||||
// has the data of the textures read ahead of the camera, with gfx.textures.streaming
|
||||
void Update_Texture_Prefetch();
|
||||
void Render(TMemCell *Memcell);
|
||||
void Render_particles();
|
||||
void Render_precipitation();
|
||||
// the view of the colour pass being drawn, for the particles of the gpu
|
||||
@@ -519,24 +452,14 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// the grass of the heightmap terrain around the camera of the main view; Future: the matrix of the motion vectors
|
||||
void Render_grass(glm::mat4 const &Future);
|
||||
void Render_vr_models();
|
||||
void Render_Alpha(scene::basic_region *Region);
|
||||
void Render_Alpha(cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last);
|
||||
void Render_Alpha(TAnimModel *Instance);
|
||||
void Render_Alpha(TTraction *Traction);
|
||||
void Render_Alpha(scene::lines_node const &Lines);
|
||||
void Render_editor_overlay();
|
||||
bool Render_Alpha(TDynamicObject *Dynamic);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render_Alpha(TSubModel *Submodel);
|
||||
void Render_Alpha_text(TSubModel *Submodel); // characters of a text display
|
||||
// writes the lighting of the pass about to be drawn to the light buffer: the sun, the ambient light and the fog, and the headlights of Lights
|
||||
// unless Headlights is false, as in the pass which draws the environment map, where lights of vehicles would only get baked into the map.
|
||||
// Clustered tells the shading to take the headlights from the lists of the clusters (see Cull_Lights()) and not to go through the first few.
|
||||
// with Headlights and Clustered the cab lamp (first) and the lamps of models and vehicles (after the headlights) are written as well
|
||||
void Update_Lights(light_array &Lights, bool const Headlights = true, bool const Clustered = false);
|
||||
// writes a light of a model or a vehicle, or the cab lamp, to the slot of the light buffer, in the view space of the pass
|
||||
void Write_Local_Light(gl::light_element &Light, opengl46_local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View);
|
||||
void Write_Local_Light(gl::light_element &Light, gfx::local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View);
|
||||
// brings the light cluster grid of the viewport up to date with the projection of the color pass being drawn.
|
||||
// Targetsize is the size in pixels of what the pass draws to, which gl_FragCoord of the shading counts in
|
||||
void Update_Clusters(viewport_config &vp, glm::ivec2 const &Targetsize);
|
||||
@@ -547,8 +470,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
void Render_cluster_heatmap(viewport_config &vp, gl::framebuffer *Target, glm::ivec2 const &Targetsize);
|
||||
// ambient occlusion from the resolved depth (vp.main_texd), multiplied into the image of Target. Mode: Global.gfx_postfx_ao
|
||||
void Render_ambient_occlusion(viewport_config &vp, gl::framebuffer &Target, int const Mode);
|
||||
glm::vec3 pick_color(std::size_t const Index);
|
||||
std::size_t pick_index(glm::ivec3 const &Color);
|
||||
|
||||
bool init_viewport(viewport_config &vp);
|
||||
|
||||
@@ -590,8 +511,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
/*
|
||||
float m_sunandviewangle; // cached dot product of sunlight and camera vectors
|
||||
*/
|
||||
gfx::geometry_handle m_billboardgeometry{0, 0};
|
||||
texture_handle m_glaretexture{-1};
|
||||
texture_handle m_suntexture{-1};
|
||||
texture_handle m_moontexture{-1};
|
||||
texture_handle m_smoketexture{-1};
|
||||
@@ -612,8 +531,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
opengl46_gpu_particles m_gpuparticles; // the smoke, rain and snow of the gpu (gfx.smoke.gpu, gfx.precipitation.gpu)
|
||||
bool m_particlesoft{false}; // the depth for the soft particles is bound in the colour pass being drawn
|
||||
|
||||
unsigned int m_framestamp; // id of currently rendered gfx frame
|
||||
unsigned int m_timbersweepframe { 0 }; // the frame the beams of the tracks far away were last let go in
|
||||
float m_framerate;
|
||||
double m_updateaccumulator{0.0};
|
||||
std::string m_debugtimestext;
|
||||
@@ -638,29 +555,12 @@ class opengl46_renderer : public gfx_renderer {
|
||||
float m_specularopaquescalefactor{1.f};
|
||||
float m_speculartranslucentscalefactor{1.f};
|
||||
|
||||
float m_fogrange = 2000.0f;
|
||||
|
||||
renderpass_config m_renderpass; // parameters for current render pass
|
||||
section_sequence m_sectionqueue; // list of sections in current render pass
|
||||
cell_sequence m_cellqueue;
|
||||
// frame-level accumulation of per-cell opaque instance buckets. Each visited
|
||||
// cell's buckets are merged here keyed by (TModel3d*, skins), so that
|
||||
// Render_Instanced() runs once per unique model across the whole pass instead
|
||||
// of once per cell -- collapsing many tiny instanced draws into a few large
|
||||
// batches. Reused every pass; the vectors are emptied (not freed) at the top of Render(scene::basic_region*).
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, std::vector<queued_instance>, scene::basic_cell::instance_bucket_key_hash> m_frame_instance_buckets;
|
||||
// textures used by vehicle models, for the release of far textures
|
||||
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
||||
// the read ahead of textures (Update_Texture_Prefetch()): the cell it goes on with
|
||||
std::size_t m_prefetchcursor { 0 };
|
||||
std::chrono::steady_clock::time_point m_prefetchvehicletime;
|
||||
std::chrono::steady_clock::time_point m_prefetchdroptime;
|
||||
// geometry of neighbouring tracks of a cell which share the material, gathered to be drawn together. reused by each Render(path_sequence)
|
||||
std::vector<gfx::geometrybank_handle> m_pathbatch;
|
||||
// instance records kept by the buckets of scene cells are good as long as they carry this number.
|
||||
// it changes with each frame drawn with the scenery editor active, as the editor moves and resizes instances in place,
|
||||
// and whenever scene::node_state_version does, as instances are shown, hidden or drawn on their own
|
||||
unsigned int m_instancerecordsversion { 1 };
|
||||
unsigned int m_nodestateversion { 0 }; // scene::node_state_version which m_instancerecordsversion was last checked against
|
||||
// culling of instances in compute passes, run along the first color pass of the main viewport of a frame
|
||||
// when Global.gfx_gpucull is set: the instances of the pass are drawn from its result (1), or the result is compared
|
||||
@@ -683,7 +583,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
int m_shadowcascade { -1 }; // the region is drawn for this cascade of the sun shadows
|
||||
// Instances_Cast_Shadow() of the groups of the shadow pass under way
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, bool, scene::basic_cell::instance_bucket_key_hash> m_shadowcasters;
|
||||
std::vector<std::uint32_t> m_instance_bandcounts; // reported by Render_Instanced() for the comparison
|
||||
// the sleepers of tracks culled on the gpu with the instances of the main view (Global.gfx_gpucull_sleepers): records made
|
||||
// from TTrack::m_sleeper_local_transforms when the track is queued first, and again when they or the instance records
|
||||
// (editor, m_instancerecordsversion) went out of date. the records of tracks not queued for a while are let go
|
||||
@@ -710,10 +609,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::unordered_map<TAnimModel const *, model_records> m_modelrecords;
|
||||
bool m_modelspass { false }; // the models drawn on their own of the pass under way go to its gpu culling
|
||||
bool m_indirectcolourmodels { false }; // with m_indirectcolourpending: the depth pass culled and recorded them too
|
||||
// comparison (Global.gfx_gpucull 2): the key of the model Render( TAnimModel * ) is called for, and the models it drew in
|
||||
// the pass with their scaled squared distances, reported once the culling ran
|
||||
scene::basic_cell::instance_bucket_key const *m_modelcomparekey { nullptr };
|
||||
std::vector<std::pair<scene::basic_cell::instance_bucket_key, float>> m_modelcpudistances;
|
||||
bool m_sleeperspass { false }; // the sleepers of the pass under way go to its gpu culling (the one of the main view)
|
||||
bool m_indirectcoloursleepers { false }; // with m_indirectcolourpending: the depth pass culled and recorded the sleepers too
|
||||
// counts per lod band of the sleepers Render_Sleepers() drew in the pass, by model and skin, for the comparison (they're
|
||||
@@ -786,7 +681,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
};
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, walk_record, scene::basic_cell::instance_bucket_key_hash> m_walkrecords;
|
||||
int m_walkrankcutoff { 0 }; // Global.gfx_shadow_rank_cutoff the walks were made with
|
||||
bool m_indirectchanging { false }; // the walk under way met a submodel which may look different in the next frame
|
||||
// what the walks of the frame did, for the panel: walks made, of them not kept, walks taken from earlier frames, and the cpu time
|
||||
// of the walks and of putting the draws in order with their blocks uploaded
|
||||
struct walk_counts {
|
||||
@@ -949,14 +843,10 @@ class opengl46_renderer : public gfx_renderer {
|
||||
bool m_contactshadowslast { false };
|
||||
// a vehicle drawn in the colour pass of the last frame moved, and one drawn in this frame did (motion blur, Render_pass())
|
||||
bool m_vehiclesmoved { true };
|
||||
bool m_vehiclesmoving { false };
|
||||
gl::gpu_timer m_contactshadowstimer; // resolve of the depth
|
||||
renderpass_config m_colorpass; // parametrs of most recent color pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
|
||||
std::vector<TSubModel const *> m_pickcontrolsitems;
|
||||
std::vector<TSubModel const *> m_picksurfaceitems;
|
||||
TSubModel const *m_pickcontrolitem{nullptr};
|
||||
std::vector<scene::basic_node *> m_picksceneryitems;
|
||||
scene::basic_node *m_picksceneryitem{nullptr};
|
||||
glm::vec3 m_worldmousecoordinates { 0.f };
|
||||
bool m_mousehit { false };
|
||||
@@ -997,21 +887,16 @@ class opengl46_renderer : public gfx_renderer {
|
||||
uint32_t m_lights_lit = 0;
|
||||
uint32_t m_lights_dropped = 0;
|
||||
// lights of models and vehicles and the cab lamp, gathered once per frame for the light clusters
|
||||
opengl46_local_lights m_locallights;
|
||||
// the last Update_Lights() placed the cab lamp in the light buffer: the cab is lit by it, not by an addition to the ambient light
|
||||
bool m_cablamplit = false;
|
||||
gfx::local_lights m_locallights;
|
||||
// the ambient light of the light buffer while the cab is lit (walk_ambient_save())
|
||||
packed_vec3 m_cabambient { 0.f };
|
||||
// cube shadow maps of the lamps
|
||||
opengl46_local_shadows m_localshadows;
|
||||
// the shadow pass draws to a cube shadow map, with the programs of m_localshadows
|
||||
bool m_cubeshadows = false;
|
||||
// the shadow pass draws the kept scenery part of the map of a lamp: no vehicles, and not the model of the lamp itself
|
||||
bool m_cubescenery = false;
|
||||
TAnimModel const *m_cubeskipinstance = nullptr;
|
||||
// vehicles near the camera which cast the shadows of scenery lamps, and those of them by the lamp being drawn
|
||||
std::vector<TDynamicObject *> m_lampshadowvehicles;
|
||||
std::vector<TDynamicObject *> m_lampshadowslot;
|
||||
// the shadow pass leaves this vehicle out (the one the headlights of the map being drawn belong to)
|
||||
TDynamicObject const *m_shadowskipvehicle = nullptr;
|
||||
// headlights which may get shadow maps in this frame, and their importance
|
||||
std::vector<std::pair<float, light_array::light_record const *>> m_headlightshadowcandidates;
|
||||
std::vector<light_array::light_record const *> m_headlightshadowpicked;
|
||||
@@ -1041,44 +926,7 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// matrices of their instances past the ones placed earlier in the frame, and draw them from that base instance.
|
||||
// persistently mapped with Global.gfx_bufferstreaming
|
||||
std::unique_ptr<gl::frame_stream> m_instancestream;
|
||||
// when > 0, the renderer is inside a Render_Instanced() call. The draw(handle)
|
||||
// method routes to glDrawElementsInstanced* with this many instances from
|
||||
// m_current_instance_base onwards rather than a single regular draw. The vertex shader reads
|
||||
// per-instance modelview from instance_ssbo[gl_BaseInstance + gl_InstanceID]. Reset to 0 on exit.
|
||||
std::size_t m_current_instance_count { 0 };
|
||||
std::uint32_t m_current_instance_base { 0 };
|
||||
// camera-space transforms of the sleepers and timbers drawn by Render_Timbers, by the model and its part or its level of
|
||||
// detail; kept from frame to frame to reuse their allocations
|
||||
struct timber_bucket {
|
||||
track_sleepers::beam_model const *model { nullptr };
|
||||
int part { -1 }; // of a model put together from parts; -1: the whole model
|
||||
float band { 0.f }; // the squared distance the level of detail of the whole model is seen from
|
||||
std::vector<glm::mat4> instances;
|
||||
};
|
||||
std::vector<timber_bucket> m_timberbuckets;
|
||||
// persistent scratch buffer for Render_Instanced(): holds the per-instance
|
||||
// camera-space root modelview matrices for the batch currently being built.
|
||||
// Kept as a member rather than a function-local so its heap allocation is
|
||||
// reused across calls -- clear() retains capacity, so once it has grown to
|
||||
// the largest batch seen, steady-state frames perform no allocation here.
|
||||
std::vector<glm::mat4> m_instance_modelviews;
|
||||
// further persistent scratch buffers for Render_Instanced(): culled instances paired with their
|
||||
// squared distance, and the lod bounds of the drawn model which split them into lod bands
|
||||
struct instance_survivor {
|
||||
glm::mat4 modelview;
|
||||
float distancesquared;
|
||||
std::uint32_t band;
|
||||
};
|
||||
std::vector<instance_survivor> m_instance_survivors;
|
||||
std::vector<float> m_instance_lodbounds;
|
||||
std::vector<TSubModel const *> m_instance_lodpending;
|
||||
// per sort key (lod band and distance slot): first index in m_instance_modelviews (with end sentinel)
|
||||
// and fill cursor; per lod band: nearest distance
|
||||
std::vector<std::size_t> m_instance_bandstarts;
|
||||
std::vector<std::size_t> m_instance_bandcursors;
|
||||
std::vector<float> m_instance_banddistances;
|
||||
gl::scene_ubs scene_ubs;
|
||||
gl::model_ubs model_ubs;
|
||||
gl::light_data light_data;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<gl::program>> m_shaders;
|
||||
@@ -1167,8 +1015,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::unique_ptr<opengl_texture> ldr_tex;
|
||||
std::unique_ptr<gl::framebuffer> ldr_fb;
|
||||
} m_preview;
|
||||
// set while a preview is drawn: submodels faded out entirely are skipped, instead of being drawn invisible over the stencil
|
||||
bool m_previewing { false };
|
||||
|
||||
static bool renderer_register;
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ http://mozilla.org/MPL/2.0/.
|
||||
// same values for the SPIR-V binaries, which glslc compiles without the renderer (the renderer checks they are the same).
|
||||
//
|
||||
// the descriptor sets (decision K6 of the plan): set 0 holds the resources of a pass, made when the renderer starts and bound
|
||||
// with a dynamic offset in the ring of the frame; set 1 is pushed with the draws (vkCmdPushDescriptorSet2): the textures of
|
||||
// the material and what changes between the draws. a layout can have only one set pushed, so the passes without a set of
|
||||
// their own (the user interface, the compute passes) push set 0
|
||||
// with dynamic offsets in the ring of the frame; set 1 is pushed with the draws (vkCmdPushDescriptorSet2): the textures of
|
||||
// the material. a layout can have only one set pushed, so the passes without a set of their own (the user interface, the
|
||||
// compute passes, the post-processing) push set 0
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -28,27 +28,63 @@ namespace vk14::bindings
|
||||
constexpr std::uint32_t UI_SET = 0;
|
||||
constexpr std::uint32_t UI_TEXTURE = 0;
|
||||
|
||||
// the sets of the passes of the scene (from the stage D1 of the plan)
|
||||
// the sets of the passes of the scene
|
||||
constexpr std::uint32_t PASS_SET = 0;
|
||||
constexpr std::uint32_t MATERIAL_SET = 1;
|
||||
// set 0 of the scene: the block of the scene (gl::scene_ubs, dynamic offset in the ring of the frame)
|
||||
constexpr std::uint32_t SCENE_UBO = 0;
|
||||
// the blocks of the draws of the frame (gl::model_ubs each), the one of a draw picked by the push constant draw
|
||||
constexpr std::uint32_t DRAW_SSBO = 1;
|
||||
// the modelview matrices of the instances of the frame, element 0 the identity (draws which aren't instanced)
|
||||
constexpr std::uint32_t INSTANCE_SSBO = 2;
|
||||
// the light buffer of the pass (gl::light_data, dynamic offset)
|
||||
constexpr std::uint32_t LIGHT_SSBO = 3;
|
||||
// the light clusters: the grid of the pass (gl::cluster_ubs, dynamic offset), the lists of the lights of the clusters
|
||||
constexpr std::uint32_t CLUSTER_UBO = 4;
|
||||
constexpr std::uint32_t CLUSTER_RANGE_SSBO = 5;
|
||||
constexpr std::uint32_t CLUSTER_INDEX_SSBO = 6;
|
||||
// the cascades of the sun shadows (depth compared), the environment map, the beams of the headlights
|
||||
constexpr std::uint32_t SHADOW_TEX = 7;
|
||||
constexpr std::uint32_t ENV_TEX = 8;
|
||||
constexpr std::uint32_t HEADLIGHT_TEX = 9;
|
||||
constexpr std::uint32_t PASS_BINDING_COUNT = 10;
|
||||
// the textures of a material take the bindings of their units (#pragma texture(name, unit, ...)), 0 .. MAX_TEXTURES - 1
|
||||
constexpr std::uint32_t MATERIAL_TEXTURE_FIRST = 0;
|
||||
|
||||
// the compute passes of the light clusters, set 0 pushed
|
||||
constexpr std::uint32_t CL_CLUSTER_UBO = 0;
|
||||
constexpr std::uint32_t CL_AABB_SSBO = 1;
|
||||
constexpr std::uint32_t CL_RANGE_SSBO = 2;
|
||||
constexpr std::uint32_t CL_INDEX_SSBO = 3;
|
||||
constexpr std::uint32_t CL_COUNTER_SSBO = 4;
|
||||
constexpr std::uint32_t CL_LIGHT_SSBO = 5;
|
||||
|
||||
// the post-processing and the copies of whole images: set 0 pushed, the images it reads from binding 0 on
|
||||
constexpr std::uint32_t POST_SET = 0;
|
||||
constexpr std::uint32_t POST_TEXTURE_FIRST = 0;
|
||||
|
||||
// the push constants of the graphics pipelines of the scene and of the post-processing (vk14::draw_constants): the draw, and
|
||||
// the parameters of the special shaders (the celestial bodies, the post-processing)
|
||||
constexpr std::uint32_t PUSH_CONSTANTS_SIZE = 112;
|
||||
|
||||
// the specialization constants of the shaders
|
||||
// 1: the colour written to the window is encoded by the shader (a window without an srgb format, gfx.shadergamma yes)
|
||||
constexpr std::uint32_t SPEC_OUTPUT_GAMMA = 0;
|
||||
// the settings of the game (gfx.* in eu07.ini) the shaders of the scene depend on, as in shaders/ogl46/settings.glsl
|
||||
constexpr std::uint32_t SPEC_SHADOWMAP_ENABLED = 1;
|
||||
constexpr std::uint32_t SPEC_ENVMAP_ENABLED = 2;
|
||||
constexpr std::uint32_t SPEC_MOTIONBLUR_ENABLED = 3;
|
||||
constexpr std::uint32_t SPEC_POSTFX_ENABLED = 4;
|
||||
constexpr std::uint32_t SPEC_EXTRAEFFECTS_ENABLED = 5;
|
||||
constexpr std::uint32_t SPEC_COUNT = 6;
|
||||
|
||||
} // namespace vk14::bindings
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
// the bindings above as #define lines, with MASZYNA_CONSTANTS, which tells shaders/vk14/common.glsl not to write its own
|
||||
inline std::string binding_preamble()
|
||||
{
|
||||
auto const define = [](char const *Name, std::uint32_t const Value) { return std::string("#define ") + Name + " " + std::to_string(Value) + "\n"; };
|
||||
return define("MASZYNA_CONSTANTS", 1) + define("UI_SET", bindings::UI_SET) + define("UI_TEXTURE", bindings::UI_TEXTURE) + define("PASS_SET", bindings::PASS_SET) + define("MATERIAL_SET", bindings::MATERIAL_SET) +
|
||||
define("MATERIAL_TEXTURE_FIRST", bindings::MATERIAL_TEXTURE_FIRST) + define("SPEC_OUTPUT_GAMMA", bindings::SPEC_OUTPUT_GAMMA);
|
||||
}
|
||||
// the bindings above as #define lines, with the constants of the game the shaders share with the opengl renderer (the sizes of
|
||||
// the blocks, the cluster grid), and MASZYNA_CONSTANTS, which tells shaders/vk14/common.glsl not to write its own
|
||||
std::string binding_preamble();
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
322
rendering/vulkan14/vulkan14context.h
Normal file
322
rendering/vulkan14/vulkan14context.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// the objects of the vulkan api of the vulkan renderer (vulkan14_renderer::context), for the files of the renderer only:
|
||||
// the core (vulkan14renderer.cpp) and the scene (vulkan14scene.cpp)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering/vulkan14/vulkan14renderer.h"
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
#include "rendering/vulkan14/vulkan14instance.h"
|
||||
#include "rendering/vulkan14/vulkan14device.h"
|
||||
#include "rendering/vulkan14/vulkan14swapchain.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
#include "rendering/vulkan14/vulkan14shaders.h"
|
||||
#include "rendering/vulkan14/vulkan14descriptors.h"
|
||||
#include "rendering/vulkan14/vulkan14passes.h"
|
||||
#include "rendering/vulkan14/vulkan14texture.h"
|
||||
#include "rendering/vulkan14/vulkan14imgui.h"
|
||||
#include "rendering/vulkan14/vulkan14bindings.h"
|
||||
#include "rendering/vulkan14/vulkan14geometry.h"
|
||||
#include "model/material.h"
|
||||
#include "gl/ubo.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
// how the vertices of a program of the scene come
|
||||
enum class vertex_kind : std::uint8_t
|
||||
{
|
||||
none, // made by the vertex shader (the celestial bodies, the full screen quads)
|
||||
scene, // gfx::basic_vertex and the user data, the geometry banks
|
||||
colour, // position and colour, the sky dome
|
||||
particle, // position, colour and texture coordinates in one stream, the smoke
|
||||
position_uv // position and texture coordinates in two streams, the precipitation
|
||||
};
|
||||
|
||||
// the push constants of the pipelines of the scene and of the post-processing (shaders/vk14/common.glsl, draw_constants_block)
|
||||
struct draw_constants
|
||||
{
|
||||
std::uint32_t draw{0};
|
||||
std::uint32_t flags{0};
|
||||
std::uint32_t pad0{0};
|
||||
std::uint32_t pad1{0};
|
||||
glm::vec4 data[6]{};
|
||||
};
|
||||
static_assert(sizeof(draw_constants) == bindings::PUSH_CONSTANTS_SIZE, "the push constants don't match the shaders");
|
||||
|
||||
// a program of the scene: the shaders and the pipelines made of them for the targets and the states they were drawn with
|
||||
struct scene_program : public gfx::shader_program
|
||||
{
|
||||
std::shared_ptr<shader_code const> vertex;
|
||||
std::shared_ptr<shader_code const> fragment;
|
||||
// the fragment shader of the passes without the velocity target (NO_MOTION_OUTPUT), made when it's first needed
|
||||
std::string fragment_name;
|
||||
std::shared_ptr<shader_code const> fragment_single;
|
||||
vertex_kind vertices{vertex_kind::scene};
|
||||
std::unordered_map<std::uint64_t, VkPipeline> pipelines;
|
||||
};
|
||||
|
||||
// what a pass of the scene draws to; the pipelines are made for these
|
||||
struct target_formats
|
||||
{
|
||||
std::array<VkFormat, 2> colour{VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED};
|
||||
std::uint32_t colours{0};
|
||||
VkFormat depth{VK_FORMAT_UNDEFINED};
|
||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||
// the number the pipelines of the targets are known by
|
||||
std::uint32_t id{0};
|
||||
};
|
||||
|
||||
// the images a viewport draws its scene to, made for its size and the settings
|
||||
struct viewport_targets
|
||||
{
|
||||
VkExtent2D extent{0, 0};
|
||||
VkSampleCountFlagBits samples{VK_SAMPLE_COUNT_1_BIT};
|
||||
bool motion{false}; // the velocity is drawn, for the motion blur
|
||||
bool aberration{false};
|
||||
// with more than one sample: the images drawn to, resolved to the ones below
|
||||
std::shared_ptr<image> msaa_colour;
|
||||
std::shared_ptr<image> msaa_motion;
|
||||
std::shared_ptr<image> msaa_depth;
|
||||
// the colour of the scene (hdr), the velocity and the depth: the resolved ones, or the ones drawn to with one sample
|
||||
std::shared_ptr<image> colour;
|
||||
std::shared_ptr<image> motion_image;
|
||||
std::shared_ptr<image> depth;
|
||||
// the colour after the motion blur (hdr)
|
||||
std::shared_ptr<image> blurred;
|
||||
// the tonemapped image, before the chromatic aberration, and the one which goes to the window
|
||||
std::shared_ptr<image> tonemapped;
|
||||
std::shared_ptr<image> output;
|
||||
// the depth under the cursor of the editor: one sample of the depth of the ground resolved to it, and the buffer it's read back with
|
||||
std::shared_ptr<image> cursor_depth;
|
||||
target_formats formats;
|
||||
target_formats output_formats;
|
||||
bool srgb{true}; // the output encodes the colour (gfx.shadergamma no)
|
||||
};
|
||||
|
||||
// the ring of a frame some data of the draws goes through, the descriptor of which reads a window of it: the window starts again
|
||||
// further on when it's full, and the pass set is bound again with the new offset
|
||||
struct window_ring
|
||||
{
|
||||
frame_ring ring;
|
||||
VkDeviceSize window{0}; // the size of the window, bytes
|
||||
VkDeviceSize stride{0};
|
||||
VkDeviceSize base{0}; // where the current window starts
|
||||
bool open{false};
|
||||
};
|
||||
|
||||
// the light clusters (gl::cluster_grid), of the colour pass of each viewport in turn
|
||||
struct cluster_state
|
||||
{
|
||||
buffer aabbs;
|
||||
buffer ranges;
|
||||
buffer indices;
|
||||
buffer counters;
|
||||
gl::cluster_ubs params{};
|
||||
bool stale{true}; // the boxes have to be made anew
|
||||
VkDeviceSize params_offset{0}; // in the ring of the frame, for the pass under way
|
||||
std::uint32_t lights_total{0};
|
||||
};
|
||||
|
||||
// a picking or a reading of a value of the depth, waiting for the gpu to be done with the frame it was made in
|
||||
struct readback
|
||||
{
|
||||
std::shared_ptr<buffer> data;
|
||||
std::uint64_t value{0}; // of the timeline
|
||||
bool pending{false};
|
||||
glm::ivec2 position{0};
|
||||
};
|
||||
|
||||
// what the scene is drawn with
|
||||
struct scene_state
|
||||
{
|
||||
// the layouts: of the pass set (set 0), the pushed set of the materials (set 1), of the pipelines of the scene, the
|
||||
// post-processing and the compute passes of the clusters
|
||||
VkDescriptorSetLayout pass_layout{VK_NULL_HANDLE};
|
||||
push_set material_set;
|
||||
VkPipelineLayout scene_layout{VK_NULL_HANDLE};
|
||||
push_set post_set;
|
||||
VkPipelineLayout post_layout{VK_NULL_HANDLE};
|
||||
push_set cluster_set;
|
||||
VkPipelineLayout cluster_layout{VK_NULL_HANDLE};
|
||||
// the pass sets: made with the pool when the images they read are made anew
|
||||
VkDescriptorPool pass_pool{VK_NULL_HANDLE};
|
||||
enum pass_set_kind
|
||||
{
|
||||
set_colour, // the shadow map and the environment map
|
||||
set_reflections, // the shadow map and the empty environment map
|
||||
set_plain, // the empty maps (shadows, picking)
|
||||
set_count
|
||||
};
|
||||
std::array<VkDescriptorSet, set_count> pass_sets{};
|
||||
bool pass_sets_stale{true};
|
||||
// the rings of the draws: the blocks of the models (gl::model_ubs) and the modelview matrices of the instances
|
||||
window_ring draws;
|
||||
window_ring instances;
|
||||
// the images every pass may read: the stand-ins, the shadow maps, the environment map
|
||||
std::shared_ptr<image> white; // a texture of a material which isn't there
|
||||
std::shared_ptr<image> shadow_empty;
|
||||
std::shared_ptr<image> shadow_map;
|
||||
std::vector<VkImageView> shadow_layers; // a view of each cascade
|
||||
std::uint32_t shadow_size{0};
|
||||
std::shared_ptr<image> env_empty;
|
||||
std::shared_ptr<image> env_map;
|
||||
std::shared_ptr<image> env_depth;
|
||||
std::array<VkImageView, 6> env_faces{};
|
||||
std::shared_ptr<image> pick_colour;
|
||||
std::shared_ptr<image> pick_depth;
|
||||
VkImageView headlights_view{VK_NULL_HANDLE}; // the one the pass sets were made with
|
||||
cluster_state clusters;
|
||||
// the targets of the viewports, [0] the main window
|
||||
std::vector<viewport_targets> targets;
|
||||
// the formats of the passes which don't depend on the viewport
|
||||
target_formats shadow_formats;
|
||||
target_formats reflection_formats;
|
||||
target_formats pick_formats;
|
||||
std::uint32_t formats_count{0};
|
||||
// the programs: of the materials (by the name Fetch_Shader() is given), and the ones of the renderer
|
||||
std::unordered_map<std::string, std::shared_ptr<scene_program>> materials;
|
||||
std::shared_ptr<scene_program> shadow, alpha_shadow, pick, pick_surface, line, billboard, freespot, celestial, skydome, smoke, precipitation;
|
||||
// the post-processing, by the fragment shader
|
||||
std::unordered_map<std::string, std::shared_ptr<shader_code const>> post_fragments;
|
||||
std::shared_ptr<shader_code const> quad;
|
||||
VkPipeline cluster_build{VK_NULL_HANDLE};
|
||||
VkPipeline cluster_cull{VK_NULL_HANDLE};
|
||||
// the reading of the colour under the cursor of the picking passes and of the depth under the cursor of the editor
|
||||
readback pick_controls;
|
||||
readback pick_scenery;
|
||||
readback cursor;
|
||||
// the screenshot asked for, copied with the frame
|
||||
std::shared_ptr<buffer> screenshot;
|
||||
std::uint64_t screenshot_value{0};
|
||||
VkExtent2D screenshot_extent{0, 0};
|
||||
bool screenshot_pending{false};
|
||||
bool screenshot_wanted{false};
|
||||
// the occlusion queries of the points of light of the lamps, a pool for each frame in flight with the submodels they were made
|
||||
// for; the glare of a lamp is drawn where its point was seen in the frame the gpu was done with last
|
||||
std::vector<VkQueryPool> occlusion_pools;
|
||||
std::vector<std::vector<TSubModel const *>> occlusion_submodels;
|
||||
std::unordered_map<TSubModel const *, bool> glare_visible;
|
||||
static constexpr std::uint32_t occlusion_queries{1024};
|
||||
// the images of the scene were put in their layouts (the stand-ins cleared)
|
||||
bool images_ready{false};
|
||||
};
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
// the objects of the vulkan api. destroyed in the reverse order: the swapchains and the frames, then the device, then the instance
|
||||
struct vulkan14_renderer::context
|
||||
{
|
||||
struct frame
|
||||
{
|
||||
VkCommandPool pool{VK_NULL_HANDLE};
|
||||
// the uploads of the frame go to the queue first, then the commands of the frame
|
||||
VkCommandBuffer uploads{VK_NULL_HANDLE};
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
// the value of the timeline semaphore the gpu reaches when it's done with the frame; 0: never submitted
|
||||
std::uint64_t done{0};
|
||||
// the number of the frame, for the report of a lost device
|
||||
std::uint64_t number{0};
|
||||
};
|
||||
|
||||
vk14::instance instance;
|
||||
vk14::device device;
|
||||
vk14::allocator memory;
|
||||
vk14::deletion_queue deletions;
|
||||
vk14::uploader uploads;
|
||||
vk14::image_table images;
|
||||
vk14::samplers sampling;
|
||||
vk14::frame_ring ring;
|
||||
vk14::shader_library shaders;
|
||||
vk14::pipeline_library pipelines;
|
||||
vk14::pass_list passes;
|
||||
vk14::pending_textures pending;
|
||||
vk14::resources resources;
|
||||
vk14::imgui_backend imgui;
|
||||
vk14::scene_state scene;
|
||||
// the textures of the files; made after the objects above and gone before them
|
||||
std::unique_ptr<texture_bank> textures;
|
||||
// [0]: the main window, then the windows of the extra viewports
|
||||
std::vector<std::unique_ptr<vk14::swapchain>> windows;
|
||||
std::vector<frame> frames;
|
||||
// one semaphore counts the frames done by the gpu; the binary ones are only between the swapchains and the queue
|
||||
VkSemaphore timeline{VK_NULL_HANDLE};
|
||||
std::uint64_t timelinevalue{0};
|
||||
// frames recorded since the start
|
||||
std::uint64_t framecount{0};
|
||||
// the windows with an image acquired in the frame being made
|
||||
std::vector<vk14::swapchain *> acquired;
|
||||
bool devicelost{false};
|
||||
// the frame being recorded: its index among the frames in flight, its commands
|
||||
std::uint32_t slot{0};
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
|
||||
~context()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool create_frames(std::uint32_t const Count, std::string &Reason);
|
||||
// the parts of the renderer made once the device is there
|
||||
bool create_core(std::uint32_t const Frames, std::string &Reason);
|
||||
// the value of the timeline the gpu reached, 0 when it can't be read
|
||||
std::uint64_t completed() const;
|
||||
// waits until the gpu reached the value of the timeline; false: the device is lost
|
||||
bool wait(std::uint64_t const Value) const;
|
||||
void destroy();
|
||||
};
|
||||
|
||||
// the state of the draws of the walk being recorded into a pass of the scene
|
||||
struct vulkan14_renderer::draw_state
|
||||
{
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
vk14::target_formats const *targets{nullptr};
|
||||
VkExtent2D extent{0, 0};
|
||||
vk14::scene_state::pass_set_kind set{vk14::scene_state::set_plain};
|
||||
bool rendering{false}; // inside a rendering of a pass of the scene
|
||||
bool shadows{false}; // a pass of the sun shadows: the back faces are drawn
|
||||
// the offsets of the pass set in the rings of the frame
|
||||
std::uint32_t scene_offset{0};
|
||||
std::uint32_t light_offset{0};
|
||||
std::uint32_t cluster_offset{0};
|
||||
bool set_dirty{true};
|
||||
// the program and the textures for the next draw (walk_material(), walk_program(), walk_texture())
|
||||
vk14::scene_program *program{nullptr};
|
||||
std::array<texture_handle, gl::MAX_TEXTURES> textures{};
|
||||
bool textures_dirty{true};
|
||||
// the block of the draw last pushed, for the chunks drawn together
|
||||
std::uint32_t block{0};
|
||||
// what is bound
|
||||
VkPipeline pipeline{VK_NULL_HANDLE};
|
||||
VkBuffer vertices{VK_NULL_HANDLE};
|
||||
VkBuffer userdata{VK_NULL_HANDLE};
|
||||
VkBuffer indices{VK_NULL_HANDLE};
|
||||
VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_MAX_ENUM};
|
||||
VkCullModeFlags cull{VK_CULL_MODE_FLAG_BITS_MAX_ENUM};
|
||||
int depthtest{-1};
|
||||
int depthwrite{-1};
|
||||
std::uint32_t pushed_draw{~0u};
|
||||
// the state of the walk (setup_drawing() and the glare of the opengl renderer)
|
||||
bool blending{false};
|
||||
bool cullface{true};
|
||||
bool glare{false};
|
||||
bool depthmask_off{false}; // the editor overlay: no depth written whatever the blending
|
||||
// the occlusion queries of the glare of the lamps in the frame: the submodels, by the index of their query
|
||||
std::vector<TSubModel const *> queries;
|
||||
std::uint32_t query{~0u}; // the query under way
|
||||
// the draws made in the pass, for the debug panel
|
||||
std::uint32_t draws{0};
|
||||
};
|
||||
@@ -37,9 +37,10 @@ bool samplers::create(device const &Device, float Anisotropy, std::string &Reaso
|
||||
}
|
||||
if (Compare)
|
||||
{
|
||||
// reverse z: the fragment is lit where it's nearer the light than the map, its depth greater
|
||||
// as GL_LEQUAL of the opengl renderer: 1 (in the shadow) where the reference isn't nearer the light than the map (reverse
|
||||
// z: its depth isn't greater), the shaders take the result as the amount of the shadow
|
||||
info.compareEnable = VK_TRUE;
|
||||
info.compareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||
info.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
|
||||
}
|
||||
if (!VK14_VERIFY(vkCreateSampler(handle, &info, nullptr, &Sampler)))
|
||||
@@ -53,7 +54,7 @@ bool samplers::create(device const &Device, float Anisotropy, std::string &Reaso
|
||||
};
|
||||
return make(repeat_aniso, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_REPEAT, true, Anisotropy, false, "repeat aniso") && make(clamp_aniso, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, true, Anisotropy, false, "clamp aniso") &&
|
||||
make(clamp_linear, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, true, 1.f, false, "clamp linear") && make(clamp_nearest, VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, false, 1.f, false, "clamp nearest") &&
|
||||
make(shadow, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, false, 1.f, true, "shadow");
|
||||
make(shadow, VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, false, 1.f, true, "shadow");
|
||||
}
|
||||
|
||||
void samplers::destroy(VkDevice Device)
|
||||
|
||||
2839
rendering/vulkan14/vulkan14frame.cpp
Normal file
2839
rendering/vulkan14/vulkan14frame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
280
rendering/vulkan14/vulkan14geometry.cpp
Normal file
280
rendering/vulkan14/vulkan14geometry.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "rendering/vulkan14/vulkan14geometry.h"
|
||||
#include "rendering/vulkan14/vulkan14texture.h"
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// what opengl reads for the user data of a vertex without any (the attribute isn't given)
|
||||
gfx::vertex_userdata const no_userdata{glm::vec4{0.f, 0.f, 0.f, 1.f}};
|
||||
|
||||
} // namespace
|
||||
|
||||
VkPrimitiveTopology topology_of(unsigned int const Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case GL_POINTS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case GL_LINES:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case GL_LINE_STRIP:
|
||||
case GL_LINE_LOOP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case GL_TRIANGLE_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
case GL_TRIANGLE_FAN:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case GL_TRIANGLES:
|
||||
default:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
vulkan14_geometrybank::~vulkan14_geometrybank()
|
||||
{
|
||||
release_();
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::create_(gfx::geometry_handle const &Geometry)
|
||||
{
|
||||
// a new chunk: the buffers are made anew with it
|
||||
m_layoutdirty = true;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::replace_(gfx::geometry_handle const &Geometry)
|
||||
{
|
||||
if (m_layoutdirty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const index{Geometry.chunk - 1};
|
||||
if (index >= m_places.size())
|
||||
{
|
||||
m_layoutdirty = true;
|
||||
return;
|
||||
}
|
||||
auto const &chunk{gfx::geometry_bank::chunk(Geometry)};
|
||||
auto &place{m_places[index]};
|
||||
auto const loop{chunk.type == GL_LINE_LOOP};
|
||||
auto const vertexcount{static_cast<std::uint32_t>(chunk.vertices.size()) + ((loop && chunk.indices.empty() && (false == chunk.vertices.empty())) ? 1u : 0u)};
|
||||
auto const indexcount{static_cast<std::uint32_t>(chunk.indices.size()) + ((loop && (false == chunk.indices.empty())) ? 1u : 0u)};
|
||||
if ((vertexcount != place.vertexcount) || (indexcount != place.indexcount))
|
||||
{
|
||||
// it doesn't fit in its place any more
|
||||
m_layoutdirty = true;
|
||||
return;
|
||||
}
|
||||
place.dirty = true;
|
||||
m_chunksdirty = true;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::release_()
|
||||
{
|
||||
auto *deletions{m_resources.deletions};
|
||||
if ((deletions != nullptr) && (m_resources.uploads != nullptr) && (m_vertices || m_userdata || m_indices))
|
||||
{
|
||||
deletions->push(m_resources.uploads->current_value(), [vertices = std::move(m_vertices), userdata = std::move(m_userdata), indices = std::move(m_indices)]() {});
|
||||
}
|
||||
m_vertices.reset();
|
||||
m_userdata.reset();
|
||||
m_indices.reset();
|
||||
m_places.clear();
|
||||
m_bytes = 0;
|
||||
m_layoutdirty = true;
|
||||
m_chunksdirty = false;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::gpu_vertices(geometry_chunk const &Chunk, std::vector<gfx::basic_vertex> &Vertices, std::vector<gfx::vertex_userdata> &Userdata) const
|
||||
{
|
||||
Vertices.insert(Vertices.end(), Chunk.vertices.begin(), Chunk.vertices.end());
|
||||
if (Chunk.userdata.size() >= Chunk.vertices.size())
|
||||
{
|
||||
Userdata.insert(Userdata.end(), Chunk.userdata.begin(), Chunk.userdata.begin() + Chunk.vertices.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
Userdata.insert(Userdata.end(), Chunk.userdata.begin(), Chunk.userdata.end());
|
||||
Userdata.resize(Vertices.size(), no_userdata);
|
||||
}
|
||||
if ((Chunk.type == GL_LINE_LOOP) && Chunk.indices.empty() && (false == Chunk.vertices.empty()))
|
||||
{
|
||||
// back to the first vertex
|
||||
Vertices.push_back(Chunk.vertices.front());
|
||||
Userdata.push_back(Chunk.userdata.empty() ? no_userdata : Chunk.userdata.front());
|
||||
}
|
||||
}
|
||||
|
||||
bool vulkan14_geometrybank::build()
|
||||
{
|
||||
release_();
|
||||
|
||||
std::vector<gfx::basic_vertex> vertices;
|
||||
std::vector<gfx::vertex_userdata> userdata;
|
||||
std::vector<gfx::basic_index> indices;
|
||||
m_places.resize(m_chunks.size());
|
||||
for (std::size_t i = 0; i < m_chunks.size(); ++i)
|
||||
{
|
||||
auto const &chunk{m_chunks[i]};
|
||||
auto &place{m_places[i]};
|
||||
place.vertexfirst = static_cast<std::uint32_t>(vertices.size());
|
||||
place.indexfirst = static_cast<std::uint32_t>(indices.size());
|
||||
gpu_vertices(chunk, vertices, userdata);
|
||||
place.vertexcount = static_cast<std::uint32_t>(vertices.size()) - place.vertexfirst;
|
||||
indices.insert(indices.end(), chunk.indices.begin(), chunk.indices.end());
|
||||
if ((chunk.type == GL_LINE_LOOP) && (false == chunk.indices.empty()))
|
||||
{
|
||||
indices.push_back(chunk.indices.front());
|
||||
}
|
||||
place.indexcount = static_cast<std::uint32_t>(indices.size()) - place.indexfirst;
|
||||
place.dirty = false;
|
||||
}
|
||||
m_layoutdirty = false;
|
||||
m_chunksdirty = false;
|
||||
if (vertices.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &memory{*m_resources.memory};
|
||||
auto &uploads{*m_resources.uploads};
|
||||
auto const make = [&](std::shared_ptr<buffer> &Buffer, void const *Data, VkDeviceSize const Size, VkBufferUsageFlags const Usage, char const *Name) {
|
||||
Buffer = std::make_shared<buffer>();
|
||||
if ((false == Buffer->create(memory, Size, Usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT, memory_kind::device, Name)) || (false == uploads.upload_buffer(Buffer, Data, Size)))
|
||||
{
|
||||
Buffer.reset();
|
||||
return false;
|
||||
}
|
||||
m_bytes += Size;
|
||||
return true;
|
||||
};
|
||||
auto ok{make(m_vertices, vertices.data(), vertices.size() * sizeof(gfx::basic_vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "geometry vertices") &&
|
||||
make(m_userdata, userdata.data(), userdata.size() * sizeof(gfx::vertex_userdata), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "geometry user data")};
|
||||
if (ok && (false == indices.empty()))
|
||||
{
|
||||
ok = make(m_indices, indices.data(), indices.size() * sizeof(gfx::basic_index), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, "geometry indices");
|
||||
}
|
||||
if (false == ok)
|
||||
{
|
||||
log_error("no memory for the geometry of a bank of " + std::to_string(vertices.size()) + " vertices");
|
||||
release_();
|
||||
// tried again when it's drawn the next time
|
||||
m_layoutdirty = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void vulkan14_geometrybank::update()
|
||||
{
|
||||
m_chunksdirty = false;
|
||||
if ((false == bool(m_vertices)) || (false == bool(m_userdata)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<gfx::basic_vertex> vertices;
|
||||
std::vector<gfx::vertex_userdata> userdata;
|
||||
std::vector<gfx::basic_index> indices;
|
||||
std::vector<std::pair<VkDeviceSize, VkDeviceSize>> vertexparts, userdataparts, indexparts;
|
||||
for (std::size_t i = 0; i < m_places.size(); ++i)
|
||||
{
|
||||
auto &place{m_places[i]};
|
||||
if (false == place.dirty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
place.dirty = false;
|
||||
auto const &chunk{m_chunks[i]};
|
||||
gpu_vertices(chunk, vertices, userdata);
|
||||
vertexparts.emplace_back(place.vertexfirst * sizeof(gfx::basic_vertex), place.vertexcount * sizeof(gfx::basic_vertex));
|
||||
userdataparts.emplace_back(place.vertexfirst * sizeof(gfx::vertex_userdata), place.vertexcount * sizeof(gfx::vertex_userdata));
|
||||
if ((place.indexcount > 0) && m_indices)
|
||||
{
|
||||
indices.insert(indices.end(), chunk.indices.begin(), chunk.indices.end());
|
||||
if (chunk.type == GL_LINE_LOOP)
|
||||
{
|
||||
indices.push_back(chunk.indices.front());
|
||||
}
|
||||
indexparts.emplace_back(place.indexfirst * sizeof(gfx::basic_index), place.indexcount * sizeof(gfx::basic_index));
|
||||
}
|
||||
}
|
||||
auto &uploads{*m_resources.uploads};
|
||||
if (false == vertices.empty())
|
||||
{
|
||||
uploads.upload_buffer(m_vertices, vertices.data(), vertices.size() * sizeof(gfx::basic_vertex), vertexparts);
|
||||
uploads.upload_buffer(m_userdata, userdata.data(), userdata.size() * sizeof(gfx::vertex_userdata), userdataparts);
|
||||
}
|
||||
if (false == indices.empty())
|
||||
{
|
||||
uploads.upload_buffer(m_indices, indices.data(), indices.size() * sizeof(gfx::basic_index), indexparts);
|
||||
}
|
||||
}
|
||||
|
||||
bool vulkan14_geometrybank::source(gfx::geometry_handle const &Geometry, chunk_source &Source)
|
||||
{
|
||||
if ((Geometry.chunk == 0) || (Geometry.chunk > m_chunks.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_layoutdirty)
|
||||
{
|
||||
build();
|
||||
}
|
||||
else if (m_chunksdirty)
|
||||
{
|
||||
update();
|
||||
}
|
||||
auto const index{Geometry.chunk - 1};
|
||||
if ((false == bool(m_vertices)) || (index >= m_places.size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &place{m_places[index]};
|
||||
Source.vertices = m_vertices->handle();
|
||||
Source.userdata = m_userdata->handle();
|
||||
Source.topology = topology_of(m_chunks[index].type);
|
||||
if (place.indexcount > 0)
|
||||
{
|
||||
Source.indices = m_indices->handle();
|
||||
Source.count = place.indexcount;
|
||||
Source.first = place.indexfirst;
|
||||
Source.basevertex = static_cast<std::int32_t>(place.vertexfirst);
|
||||
}
|
||||
else
|
||||
{
|
||||
Source.indices = VK_NULL_HANDLE;
|
||||
Source.count = place.vertexcount;
|
||||
Source.first = place.vertexfirst;
|
||||
Source.basevertex = 0;
|
||||
}
|
||||
return Source.count > 0;
|
||||
}
|
||||
|
||||
bool vulkan14_geometrybank::indirect_source_(gfx::geometry_handle const &Geometry, gfx::indirect_source &Source)
|
||||
{
|
||||
m_last = chunk_source{};
|
||||
if (false == source(Geometry, m_last))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Source.count = m_last.count;
|
||||
Source.first = m_last.first;
|
||||
Source.basevertex = m_last.basevertex;
|
||||
Source.indexed = (m_last.indices != VK_NULL_HANDLE);
|
||||
Source.type = m_chunks[Geometry.chunk - 1].type;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace vk14
|
||||
99
rendering/vulkan14/vulkan14geometry.h
Normal file
99
rendering/vulkan14/vulkan14geometry.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// the geometry banks of the vulkan renderer (stage D1 of the plan): the chunks of a bank in three device buffers of the bank
|
||||
// (vertices, their user data, 32 bit indices), made the first time the bank is drawn after its chunks changed. a chunk replaced
|
||||
// with the same size is copied in place, otherwise the buffers are made anew and the old ones go once the gpu is done with them.
|
||||
// the data stays in memory as well (gfx::geometry_bank), the simulation reads it back. the primitives are the values of opengl
|
||||
// the program gives them with; a line loop is drawn as a strip which goes back to its first vertex
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
struct resources;
|
||||
|
||||
// where the data of a chunk is in the buffers of its bank
|
||||
struct chunk_source
|
||||
{
|
||||
VkBuffer vertices{VK_NULL_HANDLE};
|
||||
VkBuffer userdata{VK_NULL_HANDLE};
|
||||
VkBuffer indices{VK_NULL_HANDLE}; // null for a chunk without indices
|
||||
std::uint32_t count{0}; // vertices, or indices
|
||||
std::uint32_t first{0}; // first index, or vertex
|
||||
std::int32_t basevertex{0};
|
||||
VkPrimitiveTopology topology{VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST};
|
||||
};
|
||||
|
||||
// the vulkan primitive of a primitive of opengl (GL_TRIANGLES ...)
|
||||
VkPrimitiveTopology topology_of(unsigned int Type);
|
||||
|
||||
class vulkan14_geometrybank : public gfx::geometry_bank
|
||||
{
|
||||
public:
|
||||
explicit vulkan14_geometrybank(resources const &Resources) : m_resources(Resources) {}
|
||||
~vulkan14_geometrybank() override;
|
||||
|
||||
// the chunk with its data on the gpu: uploaded now when it changed since the last time. false: nothing to draw.
|
||||
// gfx::geometrybank_manager::indirect_source() calls it too (which marks the bank as used), and keeps its answer in last()
|
||||
bool source(gfx::geometry_handle const &Geometry, chunk_source &Source);
|
||||
chunk_source const &last() const
|
||||
{
|
||||
return m_last;
|
||||
}
|
||||
// the bytes on the gpu, for the debug panel
|
||||
VkDeviceSize gpu_bytes() const
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
struct chunk_place
|
||||
{
|
||||
std::uint32_t vertexfirst{0};
|
||||
std::uint32_t vertexcount{0}; // on the gpu: one more than the chunk for a line loop
|
||||
std::uint32_t indexfirst{0};
|
||||
std::uint32_t indexcount{0};
|
||||
bool dirty{true};
|
||||
};
|
||||
|
||||
void create_(gfx::geometry_handle const &Geometry) override;
|
||||
void replace_(gfx::geometry_handle const &Geometry) override;
|
||||
std::size_t draw_(gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
bool indirect_source_(gfx::geometry_handle const &Geometry, gfx::indirect_source &Source) override;
|
||||
void release_() override;
|
||||
// the buffers made anew for all the chunks
|
||||
bool build();
|
||||
// the chunks changed in place copied to the buffers
|
||||
void update();
|
||||
// the vertices of a chunk as they go to the gpu
|
||||
void gpu_vertices(geometry_chunk const &Chunk, std::vector<gfx::basic_vertex> &Vertices, std::vector<gfx::vertex_userdata> &Userdata) const;
|
||||
|
||||
resources const &m_resources;
|
||||
std::shared_ptr<buffer> m_vertices;
|
||||
std::shared_ptr<buffer> m_userdata;
|
||||
std::shared_ptr<buffer> m_indices;
|
||||
std::vector<chunk_place> m_places;
|
||||
bool m_layoutdirty{true}; // the buffers have to be made anew
|
||||
bool m_chunksdirty{false}; // some chunks have to be copied
|
||||
VkDeviceSize m_bytes{0};
|
||||
chunk_source m_last;
|
||||
};
|
||||
|
||||
} // namespace vk14
|
||||
@@ -177,6 +177,14 @@ void buffer::flush(VkDeviceSize Offset, VkDeviceSize Size) const
|
||||
}
|
||||
}
|
||||
|
||||
void buffer::invalidate(VkDeviceSize Offset, VkDeviceSize Size) const
|
||||
{
|
||||
if ((m_allocation != VK_NULL_HANDLE) && (Size > 0))
|
||||
{
|
||||
vmaInvalidateAllocation(m_allocator, m_allocation, Offset, Size);
|
||||
}
|
||||
}
|
||||
|
||||
// image
|
||||
|
||||
image::~image()
|
||||
@@ -334,12 +342,12 @@ void deletion_queue::flush()
|
||||
|
||||
// frame ring
|
||||
|
||||
bool frame_ring::create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name)
|
||||
bool frame_ring::create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name, VkDeviceSize Tail)
|
||||
{
|
||||
m_sizeperframe = SizePerFrame;
|
||||
m_base = 0;
|
||||
m_used = 0;
|
||||
return m_buffer.create(Allocator, SizePerFrame * Frames, Usage, memory_kind::upload, Name) && (m_buffer.mapped() != nullptr);
|
||||
return m_buffer.create(Allocator, SizePerFrame * Frames + Tail, Usage, memory_kind::upload, Name) && (m_buffer.mapped() != nullptr);
|
||||
}
|
||||
|
||||
void frame_ring::destroy()
|
||||
|
||||
@@ -91,6 +91,8 @@ class buffer
|
||||
}
|
||||
// makes the writes of the cpu seen by the gpu where the memory isn't coherent
|
||||
void flush(VkDeviceSize Offset, VkDeviceSize Size) const;
|
||||
// makes the writes of the gpu seen by the cpu where the memory isn't coherent (readback memory may be cached)
|
||||
void invalidate(VkDeviceSize Offset, VkDeviceSize Size) const;
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_buffer != VK_NULL_HANDLE;
|
||||
@@ -214,7 +216,8 @@ struct ring_slice
|
||||
class frame_ring
|
||||
{
|
||||
public:
|
||||
bool create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name);
|
||||
// Tail: bytes past the part of the last frame, so a descriptor of a fixed range can be given an offset anywhere in a part
|
||||
bool create(allocator const &Allocator, VkDeviceSize SizePerFrame, std::uint32_t Frames, VkBufferUsageFlags Usage, std::string const &Name, VkDeviceSize Tail = 0);
|
||||
void destroy();
|
||||
// the part of the frame Frame is used from its start again
|
||||
void begin_frame(std::uint32_t Frame);
|
||||
@@ -226,10 +229,20 @@ class frame_ring
|
||||
{
|
||||
return m_used;
|
||||
}
|
||||
// the offset in the buffer the next allocation aligned to Alignment would get
|
||||
VkDeviceSize next_offset(VkDeviceSize Alignment) const
|
||||
{
|
||||
auto const alignment{std::max<VkDeviceSize>(Alignment, 4)};
|
||||
return m_base + (m_used + alignment - 1) / alignment * alignment;
|
||||
}
|
||||
VkDeviceSize capacity() const
|
||||
{
|
||||
return m_sizeperframe;
|
||||
}
|
||||
VkBuffer handle() const
|
||||
{
|
||||
return m_buffer.handle();
|
||||
}
|
||||
|
||||
private:
|
||||
buffer m_buffer;
|
||||
|
||||
@@ -8,18 +8,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "rendering/vulkan14/vulkan14renderer.h"
|
||||
#include "rendering/vulkan14/vulkan14common.h"
|
||||
#include "rendering/vulkan14/vulkan14instance.h"
|
||||
#include "rendering/vulkan14/vulkan14device.h"
|
||||
#include "rendering/vulkan14/vulkan14swapchain.h"
|
||||
#include "rendering/vulkan14/vulkan14memory.h"
|
||||
#include "rendering/vulkan14/vulkan14shaders.h"
|
||||
#include "rendering/vulkan14/vulkan14descriptors.h"
|
||||
#include "rendering/vulkan14/vulkan14passes.h"
|
||||
#include "rendering/vulkan14/vulkan14texture.h"
|
||||
#include "rendering/vulkan14/vulkan14imgui.h"
|
||||
#include "rendering/vulkan14/vulkan14bindings.h"
|
||||
#include "rendering/vulkan14/vulkan14context.h"
|
||||
#include "rendering/nullrenderer.h"
|
||||
#include "application/application.h"
|
||||
#include "simulation/simulation.h"
|
||||
@@ -37,8 +26,11 @@ namespace
|
||||
// background of the opengl renderer (srgb). before that the windows are black, as with the opengl renderer
|
||||
glm::vec3 const clear_colour{51.f / 255.f, 102.f / 255.f, 85.f / 255.f};
|
||||
|
||||
// the ring each frame in flight streams its data through (the user interface for now)
|
||||
VkDeviceSize const ring_size{8 * 1024 * 1024};
|
||||
// the ring each frame in flight streams its data through: the user interface, the blocks of the scene and of the light clusters,
|
||||
// the light buffers of the passes, the vertices of the particles and the sky; the descriptors read the light buffers with the range
|
||||
// of the whole gl::light_data, which the tail past the last frame leaves room for
|
||||
VkDeviceSize const ring_size{16 * 1024 * 1024};
|
||||
VkDeviceSize const ring_tail{(sizeof(gl::light_data) + 65535) / 65536 * 65536};
|
||||
|
||||
float srgb_to_linear(float const Value)
|
||||
{
|
||||
@@ -47,192 +39,157 @@ float srgb_to_linear(float const Value)
|
||||
|
||||
} // namespace
|
||||
|
||||
// the objects of the vulkan api. destroyed in the reverse order: the swapchains and the frames, then the device, then the instance
|
||||
struct vulkan14_renderer::context
|
||||
bool vulkan14_renderer::context::create_frames(std::uint32_t const Count, std::string &Reason)
|
||||
{
|
||||
struct frame
|
||||
{
|
||||
VkCommandPool pool{VK_NULL_HANDLE};
|
||||
// the uploads of the frame go to the queue first, then the commands of the frame
|
||||
VkCommandBuffer uploads{VK_NULL_HANDLE};
|
||||
VkCommandBuffer commands{VK_NULL_HANDLE};
|
||||
// the value of the timeline semaphore the gpu reaches when it's done with the frame; 0: never submitted
|
||||
std::uint64_t done{0};
|
||||
// the number of the frame, for the report of a lost device
|
||||
std::uint64_t number{0};
|
||||
};
|
||||
auto const handle{device.handle()};
|
||||
|
||||
vk14::instance instance;
|
||||
vk14::device device;
|
||||
vk14::allocator memory;
|
||||
vk14::deletion_queue deletions;
|
||||
vk14::uploader uploads;
|
||||
vk14::image_table images;
|
||||
vk14::samplers sampling;
|
||||
vk14::frame_ring ring;
|
||||
vk14::shader_library shaders;
|
||||
vk14::pipeline_library pipelines;
|
||||
vk14::pass_list passes;
|
||||
vk14::pending_textures pending;
|
||||
vk14::resources resources;
|
||||
vk14::imgui_backend imgui;
|
||||
// the textures of the files; made after the objects above and gone before them
|
||||
std::unique_ptr<texture_bank> textures;
|
||||
// [0]: the main window, then the windows of the extra viewports
|
||||
std::vector<std::unique_ptr<vk14::swapchain>> windows;
|
||||
std::vector<frame> frames;
|
||||
// one semaphore counts the frames done by the gpu; the binary ones are only between the swapchains and the queue
|
||||
VkSemaphore timeline{VK_NULL_HANDLE};
|
||||
std::uint64_t timelinevalue{0};
|
||||
// frames recorded since the start
|
||||
std::uint64_t framecount{0};
|
||||
// the windows with an image acquired in the frame being made
|
||||
std::vector<vk14::swapchain *> acquired;
|
||||
bool devicelost{false};
|
||||
|
||||
~context()
|
||||
VkSemaphoreTypeCreateInfo timelineinfo{};
|
||||
timelineinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
|
||||
timelineinfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
||||
timelineinfo.initialValue = 0;
|
||||
VkSemaphoreCreateInfo semaphoreinfo{};
|
||||
semaphoreinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreinfo.pNext = &timelineinfo;
|
||||
if (!VK14_VERIFY(vkCreateSemaphore(handle, &semaphoreinfo, nullptr, &timeline)))
|
||||
{
|
||||
destroy();
|
||||
Reason = "no timeline semaphore";
|
||||
return false;
|
||||
}
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_SEMAPHORE, reinterpret_cast<std::uint64_t>(timeline), "frame timeline");
|
||||
|
||||
bool create_frames(std::uint32_t const Count, std::string &Reason)
|
||||
frames.resize(Count);
|
||||
for (std::uint32_t i = 0; i < Count; ++i)
|
||||
{
|
||||
auto const handle{device.handle()};
|
||||
|
||||
VkSemaphoreTypeCreateInfo timelineinfo{};
|
||||
timelineinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
|
||||
timelineinfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
|
||||
timelineinfo.initialValue = 0;
|
||||
VkSemaphoreCreateInfo semaphoreinfo{};
|
||||
semaphoreinfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreinfo.pNext = &timelineinfo;
|
||||
if (!VK14_VERIFY(vkCreateSemaphore(handle, &semaphoreinfo, nullptr, &timeline)))
|
||||
VkCommandPoolCreateInfo poolinfo{};
|
||||
poolinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolinfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
poolinfo.queueFamilyIndex = device.families().graphics;
|
||||
if (!VK14_VERIFY(vkCreateCommandPool(handle, &poolinfo, nullptr, &frames[i].pool)))
|
||||
{
|
||||
Reason = "no timeline semaphore";
|
||||
Reason = "no command pool";
|
||||
return false;
|
||||
}
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_SEMAPHORE, reinterpret_cast<std::uint64_t>(timeline), "frame timeline");
|
||||
|
||||
frames.resize(Count);
|
||||
for (std::uint32_t i = 0; i < Count; ++i)
|
||||
VkCommandBuffer buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
VkCommandBufferAllocateInfo bufferinfo{};
|
||||
bufferinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
bufferinfo.commandPool = frames[i].pool;
|
||||
bufferinfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
bufferinfo.commandBufferCount = 2;
|
||||
if (!VK14_VERIFY(vkAllocateCommandBuffers(handle, &bufferinfo, buffers)))
|
||||
{
|
||||
VkCommandPoolCreateInfo poolinfo{};
|
||||
poolinfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolinfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
poolinfo.queueFamilyIndex = device.families().graphics;
|
||||
if (!VK14_VERIFY(vkCreateCommandPool(handle, &poolinfo, nullptr, &frames[i].pool)))
|
||||
{
|
||||
Reason = "no command pool";
|
||||
return false;
|
||||
}
|
||||
VkCommandBuffer buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
VkCommandBufferAllocateInfo bufferinfo{};
|
||||
bufferinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
bufferinfo.commandPool = frames[i].pool;
|
||||
bufferinfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
bufferinfo.commandBufferCount = 2;
|
||||
if (!VK14_VERIFY(vkAllocateCommandBuffers(handle, &bufferinfo, buffers)))
|
||||
{
|
||||
Reason = "no command buffer";
|
||||
return false;
|
||||
}
|
||||
frames[i].uploads = buffers[0];
|
||||
frames[i].commands = buffers[1];
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].uploads), "uploads " + std::to_string(i));
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].commands), "frame " + std::to_string(i));
|
||||
Reason = "no command buffer";
|
||||
return false;
|
||||
}
|
||||
frames[i].uploads = buffers[0];
|
||||
frames[i].commands = buffers[1];
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].uploads), "uploads " + std::to_string(i));
|
||||
instance.set_object_name(handle, VK_OBJECT_TYPE_COMMAND_BUFFER, reinterpret_cast<std::uint64_t>(frames[i].commands), "frame " + std::to_string(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// the parts of the renderer made once the device is there
|
||||
bool vulkan14_renderer::context::create_core(std::uint32_t const Frames, std::string &Reason)
|
||||
{
|
||||
if (false == memory.create(instance, device, Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uploads.init(memory, deletions, device.physical());
|
||||
if (false == sampling.create(device, static_cast<float>(Global.AnisotropicFiltering), Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &limits{device.caps().limits};
|
||||
(void)limits;
|
||||
if (false == ring.create(memory, ring_size, Frames, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, "frame ring", ring_tail))
|
||||
{
|
||||
Reason = "no ring for the data of the frames";
|
||||
return false;
|
||||
}
|
||||
if ((false == shaders.init(vk14::binding_preamble(), Reason)) || (false == pipelines.init(device, Reason)) || (false == passes.create(device, Frames, Reason)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
resources.gpu = &device;
|
||||
resources.memory = &memory;
|
||||
resources.uploads = &uploads;
|
||||
resources.deletions = &deletions;
|
||||
resources.sampling = &sampling;
|
||||
resources.images = &images;
|
||||
resources.thread = std::this_thread::get_id();
|
||||
resources.pending = &pending;
|
||||
textures = std::make_unique<texture_bank>([this]() -> texture_source * { return new vk14::vulkan14_texture(resources); });
|
||||
return imgui.create(resources, shaders, pipelines, ring, Reason);
|
||||
}
|
||||
|
||||
// the value of the timeline the gpu reached, 0 when it can't be read
|
||||
std::uint64_t vulkan14_renderer::context::completed() const
|
||||
{
|
||||
std::uint64_t value{0};
|
||||
if ((timeline == VK_NULL_HANDLE) || (vkGetSemaphoreCounterValue(device.handle(), timeline, &value) != VK_SUCCESS))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::context::wait(std::uint64_t const Value) const
|
||||
{
|
||||
if ((Value == 0) || (completed() >= Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
VkSemaphoreWaitInfo wait{};
|
||||
wait.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
|
||||
wait.semaphoreCount = 1;
|
||||
wait.pSemaphores = &timeline;
|
||||
wait.pValues = &Value;
|
||||
return VK14_VERIFY(vkWaitSemaphores(device.handle(), &wait, UINT64_MAX));
|
||||
}
|
||||
|
||||
// the parts of the renderer made once the device is there
|
||||
bool create_core(std::uint32_t const Frames, std::string &Reason)
|
||||
void vulkan14_renderer::context::destroy()
|
||||
{
|
||||
auto const handle{device.handle()};
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
if (false == memory.create(instance, device, Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uploads.init(memory, deletions, device.physical());
|
||||
if (false == sampling.create(device, static_cast<float>(Global.AnisotropicFiltering), Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &limits{device.caps().limits};
|
||||
(void)limits;
|
||||
if (false == ring.create(memory, ring_size, Frames, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, "frame ring"))
|
||||
{
|
||||
Reason = "no ring for the data of the frames";
|
||||
return false;
|
||||
}
|
||||
if ((false == shaders.init(vk14::binding_preamble(), Reason)) || (false == pipelines.init(device, Reason)) || (false == passes.create(device, Frames, Reason)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
resources.gpu = &device;
|
||||
resources.memory = &memory;
|
||||
resources.uploads = &uploads;
|
||||
resources.deletions = &deletions;
|
||||
resources.sampling = &sampling;
|
||||
resources.images = &images;
|
||||
resources.thread = std::this_thread::get_id();
|
||||
resources.pending = &pending;
|
||||
textures = std::make_unique<texture_bank>([this]() -> texture_source * { return new vk14::vulkan14_texture(resources); });
|
||||
return imgui.create(resources, shaders, pipelines, ring, Reason);
|
||||
// the end of the renderer, not a frame: everything on the gpu has to be done
|
||||
vkDeviceWaitIdle(handle);
|
||||
}
|
||||
|
||||
// the value of the timeline the gpu reached, 0 when it can't be read
|
||||
std::uint64_t completed() const
|
||||
acquired.clear();
|
||||
windows.clear();
|
||||
imgui.destroy();
|
||||
// the textures give their images to the deletion queue, which goes now that the gpu is idle
|
||||
textures.reset();
|
||||
uploads.end_frame();
|
||||
uploads = vk14::uploader{};
|
||||
images.clear();
|
||||
deletions.flush();
|
||||
pipelines.shutdown();
|
||||
shaders.shutdown();
|
||||
passes.destroy();
|
||||
ring.destroy();
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
std::uint64_t value{0};
|
||||
if ((timeline == VK_NULL_HANDLE) || (vkGetSemaphoreCounterValue(device.handle(), timeline, &value) != VK_SUCCESS))
|
||||
sampling.destroy(handle);
|
||||
for (auto &frame : frames)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
auto const handle{device.handle()};
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
// the end of the renderer, not a frame: everything on the gpu has to be done
|
||||
vkDeviceWaitIdle(handle);
|
||||
}
|
||||
acquired.clear();
|
||||
windows.clear();
|
||||
imgui.destroy();
|
||||
// the textures give their images to the deletion queue, which goes now that the gpu is idle
|
||||
textures.reset();
|
||||
uploads.end_frame();
|
||||
uploads = vk14::uploader{};
|
||||
images.clear();
|
||||
deletions.flush();
|
||||
pipelines.shutdown();
|
||||
shaders.shutdown();
|
||||
passes.destroy();
|
||||
ring.destroy();
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
sampling.destroy(handle);
|
||||
for (auto &frame : frames)
|
||||
if (frame.pool != VK_NULL_HANDLE)
|
||||
{
|
||||
if (frame.pool != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyCommandPool(handle, frame.pool, nullptr);
|
||||
}
|
||||
}
|
||||
if (timeline != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySemaphore(handle, timeline, nullptr);
|
||||
vkDestroyCommandPool(handle, frame.pool, nullptr);
|
||||
}
|
||||
}
|
||||
memory.destroy();
|
||||
frames.clear();
|
||||
timeline = VK_NULL_HANDLE;
|
||||
device.destroy();
|
||||
instance.destroy();
|
||||
if (timeline != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySemaphore(handle, timeline, nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
memory.destroy();
|
||||
frames.clear();
|
||||
timeline = VK_NULL_HANDLE;
|
||||
device.destroy();
|
||||
instance.destroy();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -320,10 +277,20 @@ bool vulkan14_renderer::Init(GLFWwindow *Window)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false == init_scene(reason))
|
||||
{
|
||||
vk14::log_error("the renderer can't start: " + reason);
|
||||
destroy_scene();
|
||||
m_context.reset();
|
||||
return false;
|
||||
}
|
||||
init_frame_data();
|
||||
|
||||
vk14::log_info(std::to_string(framesinflight) + " frames in flight");
|
||||
// until the stages of the plan bring them, these parts of the simulator aren't there with vulkan
|
||||
vk14::log_info("not available with vulkan yet: the scenery and the vehicles (D1-D7), picking, mouse position in the scene and screenshots (D6), the map panel, "
|
||||
"python screens in their own windows (N5), panels in their own windows (R5b), VR (X4)");
|
||||
vk14::log_info("not available with vulkan yet: the atmosphere, the sky ambient light, automatic exposure and bloom, stars (P1), the weather fog and clouds (P2), "
|
||||
"ambient occlusion and contact shadows (P3), shadows of lamps and headlights (P4), the culling on the gpu (P5), the heightmap terrain, grass and the particles "
|
||||
"of the gpu (P6), the map panel, python screens in their own windows (N5), panels in their own windows (R5b), VR (X4)");
|
||||
WriteLog("Gfx Renderer: setup complete");
|
||||
update_info();
|
||||
return true;
|
||||
@@ -331,38 +298,64 @@ bool vulkan14_renderer::Init(GLFWwindow *Window)
|
||||
|
||||
bool vulkan14_renderer::AddViewport(global_settings::extraviewport_config const &conf)
|
||||
{
|
||||
if (!m_context)
|
||||
if (!m_context || m_viewports.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
viewport_config *viewport{nullptr};
|
||||
if (conf.monitor == "MAIN")
|
||||
{
|
||||
// the projection of the main window is changed only, which the drawing of the scenery takes care of (D6)
|
||||
vk14::log_info("extra viewport on the main window: drawn from the stage D6 of the plan");
|
||||
return true;
|
||||
// the main window drawn with the projection of the viewport
|
||||
viewport = &m_viewports.front();
|
||||
}
|
||||
|
||||
// a window of its own, without an opengl context
|
||||
auto *window{Application.window(-1, true, conf.width, conf.height, Application.find_monitor(conf.monitor), true, false)};
|
||||
if (window == nullptr)
|
||||
else
|
||||
{
|
||||
vk14::log_error("the window of the extra viewport on " + conf.monitor + " couldn't be made");
|
||||
return false;
|
||||
// a window of its own, without an opengl context, with a swapchain of its own
|
||||
auto *window{Application.window(-1, true, conf.width, conf.height, Application.find_monitor(conf.monitor), true, false)};
|
||||
if (window == nullptr)
|
||||
{
|
||||
vk14::log_error("the window of the extra viewport on " + conf.monitor + " couldn't be made");
|
||||
return false;
|
||||
}
|
||||
auto &ctx{*m_context};
|
||||
auto swapchain{std::make_unique<vk14::swapchain>()};
|
||||
std::string reason;
|
||||
if (false == swapchain->create(ctx.instance, ctx.device, window, "viewport " + conf.monitor, static_cast<std::uint32_t>(ctx.frames.size()), reason))
|
||||
{
|
||||
vk14::log_error(reason);
|
||||
return false;
|
||||
}
|
||||
ctx.windows.emplace_back(std::move(swapchain));
|
||||
m_viewports.emplace_back();
|
||||
viewport = &m_viewports.back();
|
||||
viewport->window = ctx.windows.size() - 1;
|
||||
}
|
||||
auto &ctx{*m_context};
|
||||
auto viewport{std::make_unique<vk14::swapchain>()};
|
||||
std::string reason;
|
||||
if (false == viewport->create(ctx.instance, ctx.device, window, "viewport " + conf.monitor, static_cast<std::uint32_t>(ctx.frames.size()), reason))
|
||||
{
|
||||
vk14::log_error(reason);
|
||||
return false;
|
||||
}
|
||||
ctx.windows.emplace_back(std::move(viewport));
|
||||
viewport->width = conf.width;
|
||||
viewport->height = conf.height;
|
||||
viewport->projection = conf.projection;
|
||||
viewport->proj_type = viewport_config::custom;
|
||||
viewport->draw_range = conf.draw_range;
|
||||
vk14::log_info("viewport " + conf.monitor + ": " + std::to_string(conf.width) + "x" + std::to_string(conf.height));
|
||||
return true;
|
||||
}
|
||||
|
||||
void vulkan14_renderer::Shutdown()
|
||||
{
|
||||
if (m_context && (m_context->device.handle() != VK_NULL_HANDLE))
|
||||
{
|
||||
// the end of the renderer, not a frame: everything on the gpu has to be done
|
||||
vkDeviceWaitIdle(m_context->device.handle());
|
||||
}
|
||||
// the geometry on the gpu goes with the context; the data of the banks stays for the simulation
|
||||
for (auto *bank : m_banks)
|
||||
{
|
||||
if (bank != nullptr)
|
||||
{
|
||||
bank->release();
|
||||
}
|
||||
}
|
||||
destroy_scene();
|
||||
m_draw.reset();
|
||||
m_context.reset();
|
||||
}
|
||||
|
||||
@@ -387,184 +380,6 @@ void vulkan14_renderer::report_device_lost(char const *Where)
|
||||
crashreport_add_info("vulkan_device_lost", report);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Render()
|
||||
{
|
||||
if (!m_context || m_context->devicelost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto &ctx{*m_context};
|
||||
auto const device{ctx.device.handle()};
|
||||
auto const slot{static_cast<std::uint32_t>(ctx.framecount % ctx.frames.size())};
|
||||
auto &frame{ctx.frames[slot]};
|
||||
|
||||
Timer::subsystem.gfx_total.start();
|
||||
|
||||
// the gpu has to be done with the frame which used the slot before
|
||||
if (frame.done > 0)
|
||||
{
|
||||
VkSemaphoreWaitInfo wait{};
|
||||
wait.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;
|
||||
wait.semaphoreCount = 1;
|
||||
wait.pSemaphores = &ctx.timeline;
|
||||
wait.pValues = &frame.done;
|
||||
if (!VK14_VERIFY(vkWaitSemaphores(device, &wait, UINT64_MAX)))
|
||||
{
|
||||
report_device_lost("waiting for a frame");
|
||||
return false;
|
||||
}
|
||||
ctx.passes.collect(slot);
|
||||
}
|
||||
// what the frames done used last goes
|
||||
ctx.deletions.collect(std::max(ctx.completed(), frame.done));
|
||||
VK14_VERIFY(vkResetCommandPool(device, frame.pool, 0));
|
||||
ctx.ring.begin_frame(slot);
|
||||
|
||||
VkCommandBufferBeginInfo begin{};
|
||||
begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
VK14_VERIFY(vkBeginCommandBuffer(frame.uploads, &begin));
|
||||
VK14_VERIFY(vkBeginCommandBuffer(frame.commands, &begin));
|
||||
auto const value{ctx.timelinevalue + 1};
|
||||
ctx.uploads.begin_frame(frame.uploads, value);
|
||||
// the content other threads gave the textures (python)
|
||||
{
|
||||
std::vector<vk14::vulkan14_texture *> pending;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(ctx.pending.mutex);
|
||||
pending.swap(ctx.pending.textures);
|
||||
}
|
||||
for (auto *texture : pending)
|
||||
{
|
||||
texture->apply_pending();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.acquired.clear();
|
||||
for (auto &window : ctx.windows)
|
||||
{
|
||||
switch (window->acquire(slot))
|
||||
{
|
||||
case vk14::swapchain::acquire_result::ready:
|
||||
ctx.acquired.push_back(window.get());
|
||||
break;
|
||||
case vk14::swapchain::acquire_result::skipped:
|
||||
break;
|
||||
default:
|
||||
report_device_lost("acquiring an image");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// the user interface: imgui makes its frame, the images it asks for are uploaded in this frame
|
||||
Timer::subsystem.gfx_gui.start();
|
||||
Application.render_ui();
|
||||
Timer::subsystem.gfx_gui.stop();
|
||||
|
||||
frame.number = ctx.framecount;
|
||||
ctx.passes.begin_frame(frame.commands, slot, ctx.framecount);
|
||||
auto const background{simulation::is_ready ? clear_colour : glm::vec3{0.f}};
|
||||
bool uidrawn{false};
|
||||
for (auto *window : ctx.acquired)
|
||||
{
|
||||
auto const colour{window->srgb() ? glm::vec3{srgb_to_linear(background.r), srgb_to_linear(background.g), srgb_to_linear(background.b)} : background};
|
||||
if (window != ctx.windows.front().get())
|
||||
{
|
||||
// the extra viewports: their colour only, until the scenery is drawn into them (D6)
|
||||
window->record_clear(frame.commands, glm::vec4{colour, 1.f});
|
||||
continue;
|
||||
}
|
||||
// the main window: cleared, the user interface over it, shown
|
||||
ctx.passes.begin_pass("ui", {window->use(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT)});
|
||||
VkRenderingAttachmentInfo attachment{};
|
||||
attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
attachment.imageView = window->view();
|
||||
attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachment.clearValue.color = {{colour.r, colour.g, colour.b, 1.f}};
|
||||
VkRenderingInfo rendering{};
|
||||
rendering.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
rendering.renderArea = {{0, 0}, window->extent()};
|
||||
rendering.layerCount = 1;
|
||||
rendering.colorAttachmentCount = 1;
|
||||
rendering.pColorAttachments = &attachment;
|
||||
vkCmdBeginRendering(frame.commands, &rendering);
|
||||
ctx.imgui.record(frame.commands, window->extent(), window->format(), window->srgb());
|
||||
vkCmdEndRendering(frame.commands);
|
||||
ctx.passes.end_pass();
|
||||
vk14::barriers(frame.commands, {window->use(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_2_NONE, VK_ACCESS_2_NONE)});
|
||||
uidrawn = true;
|
||||
}
|
||||
if (false == uidrawn)
|
||||
{
|
||||
// the main window is minimized: the frame of imgui has nowhere to go
|
||||
ctx.imgui.skip();
|
||||
}
|
||||
ctx.passes.end_frame();
|
||||
auto const uploaded{ctx.uploads.end_frame()};
|
||||
VK14_VERIFY(vkEndCommandBuffer(frame.uploads));
|
||||
VK14_VERIFY(vkEndCommandBuffer(frame.commands));
|
||||
ctx.ring.flush();
|
||||
|
||||
std::vector<VkSemaphoreSubmitInfo> waits;
|
||||
std::vector<VkSemaphoreSubmitInfo> signals;
|
||||
for (auto *window : ctx.acquired)
|
||||
{
|
||||
VkSemaphoreSubmitInfo wait{};
|
||||
wait.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
wait.semaphore = window->acquire_semaphore(slot);
|
||||
wait.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
waits.push_back(wait);
|
||||
|
||||
VkSemaphoreSubmitInfo signal{};
|
||||
signal.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
signal.semaphore = window->present_semaphore();
|
||||
signal.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
signals.push_back(signal);
|
||||
}
|
||||
VkSemaphoreSubmitInfo timelinesignal{};
|
||||
timelinesignal.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
|
||||
timelinesignal.semaphore = ctx.timeline;
|
||||
timelinesignal.value = value;
|
||||
timelinesignal.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
signals.push_back(timelinesignal);
|
||||
|
||||
// the uploads first, in the same submit: their barriers make them done before the frame samples the images
|
||||
std::vector<VkCommandBufferSubmitInfo> commands;
|
||||
if (uploaded)
|
||||
{
|
||||
VkCommandBufferSubmitInfo info{};
|
||||
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
|
||||
info.commandBuffer = frame.uploads;
|
||||
commands.push_back(info);
|
||||
}
|
||||
{
|
||||
VkCommandBufferSubmitInfo info{};
|
||||
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
|
||||
info.commandBuffer = frame.commands;
|
||||
commands.push_back(info);
|
||||
}
|
||||
|
||||
VkSubmitInfo2 submit{};
|
||||
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
|
||||
submit.waitSemaphoreInfoCount = static_cast<std::uint32_t>(waits.size());
|
||||
submit.pWaitSemaphoreInfos = waits.data();
|
||||
submit.commandBufferInfoCount = static_cast<std::uint32_t>(commands.size());
|
||||
submit.pCommandBufferInfos = commands.data();
|
||||
submit.signalSemaphoreInfoCount = static_cast<std::uint32_t>(signals.size());
|
||||
submit.pSignalSemaphoreInfos = signals.data();
|
||||
if (!VK14_VERIFY(vkQueueSubmit2(ctx.device.graphics_queue(), 1, &submit, VK_NULL_HANDLE)))
|
||||
{
|
||||
report_device_lost("submitting a frame");
|
||||
return false;
|
||||
}
|
||||
frame.done = ctx.timelinevalue = value;
|
||||
|
||||
Timer::subsystem.gfx_total.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
void vulkan14_renderer::SwapBuffers()
|
||||
{
|
||||
if (!m_context)
|
||||
@@ -616,143 +431,71 @@ float vulkan14_renderer::Framerate()
|
||||
return m_framerate;
|
||||
}
|
||||
|
||||
// geometry: kept in memory for the simulation, nothing on the gpu until D1
|
||||
|
||||
gfx::geometrybank_handle vulkan14_renderer::Create_Bank()
|
||||
{
|
||||
return m_geometry.register_bank(std::make_unique<null_geometrybank>());
|
||||
}
|
||||
|
||||
gfx::geometry_handle vulkan14_renderer::Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type)
|
||||
{
|
||||
// NOTE: we expect indexed geometry to come with calculated tangents
|
||||
return m_geometry.create_chunk(Indices, Vertices, Userdata, Geometry, Type);
|
||||
}
|
||||
|
||||
gfx::geometry_handle vulkan14_renderer::Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type)
|
||||
{
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
return m_geometry.create_chunk(Vertices, Userdata, Geometry, Type);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset)
|
||||
{
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
return m_geometry.replace(Vertices, Userdata, Geometry, Offset);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type)
|
||||
{
|
||||
gfx::calculate_tangents(Vertices, gfx::index_array(), Type);
|
||||
return m_geometry.append(Vertices, Userdata, Geometry);
|
||||
}
|
||||
|
||||
gfx::index_array const &vulkan14_renderer::Indices(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
return m_geometry.indices(Geometry);
|
||||
}
|
||||
|
||||
gfx::vertex_array const &vulkan14_renderer::Vertices(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
return m_geometry.vertices(Geometry);
|
||||
}
|
||||
|
||||
gfx::userdata_array const &vulkan14_renderer::UserData(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
return m_geometry.userdata(Geometry);
|
||||
}
|
||||
|
||||
// materials and their shaders: none until D1
|
||||
|
||||
material_handle vulkan14_renderer::Fetch_Material(std::string const &Filename, bool const Loadnow)
|
||||
{
|
||||
return null_handle;
|
||||
}
|
||||
|
||||
void vulkan14_renderer::Bind_Material(material_handle const Material, TSubModel const *sm, lighting_data const *lighting) {}
|
||||
|
||||
IMaterial const *vulkan14_renderer::Material(material_handle const Material) const
|
||||
{
|
||||
return IMaterial::null_material();
|
||||
}
|
||||
|
||||
std::shared_ptr<gfx::shader_program> vulkan14_renderer::Fetch_Shader(std::string const &name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// textures: the files read and decoded as for the opengl renderer, made into images when they are first used (create(), e.g. by the
|
||||
// launcher); the materials of the scene take them from D1
|
||||
|
||||
texture_handle vulkan14_renderer::Fetch_Texture(std::string const &Filename, bool const Loadnow, GLint format_hint)
|
||||
{
|
||||
if (!m_context || !m_context->textures)
|
||||
{
|
||||
return null_handle;
|
||||
}
|
||||
return m_context->textures->create(Filename, Loadnow, format_hint);
|
||||
}
|
||||
|
||||
// the shaders take the textures by the pushed descriptors (D1), there's nothing to bind
|
||||
void vulkan14_renderer::Bind_Texture(texture_handle const Texture) {}
|
||||
|
||||
void vulkan14_renderer::Bind_Texture(std::size_t const Unit, texture_handle const Texture) {}
|
||||
|
||||
ITexture &vulkan14_renderer::Texture(texture_handle const Texture)
|
||||
{
|
||||
if ((Texture == null_handle) || !m_context || !m_context->textures)
|
||||
{
|
||||
return *ITexture::null_texture();
|
||||
}
|
||||
return m_context->textures->mark_as_used(Texture);
|
||||
}
|
||||
|
||||
ITexture const &vulkan14_renderer::Texture(texture_handle const Texture) const
|
||||
{
|
||||
if ((Texture == null_handle) || !m_context || !m_context->textures)
|
||||
{
|
||||
return *ITexture::null_texture();
|
||||
}
|
||||
return m_context->textures->texture(Texture);
|
||||
}
|
||||
|
||||
// picking: D6
|
||||
|
||||
void vulkan14_renderer::Pick_Control_Callback(std::function<void(TSubModel const *, glm::vec2 const)> Callback) {}
|
||||
|
||||
void vulkan14_renderer::Pick_Node_Callback(std::function<void(scene::basic_node *)> Callback) {}
|
||||
|
||||
TSubModel const *vulkan14_renderer::Pick_Control() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
scene::basic_node const *vulkan14_renderer::Pick_Node() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::dvec3 vulkan14_renderer::Mouse_Position() const
|
||||
{
|
||||
return glm::dvec3(0.0);
|
||||
}
|
||||
|
||||
// maintenance
|
||||
|
||||
void vulkan14_renderer::Update(double const Deltatime)
|
||||
{
|
||||
if (m_context && m_context->textures && (true == Global.ResourceSweep) && (true == simulation::is_ready))
|
||||
{
|
||||
// textures nothing used for a while give their images back
|
||||
m_context->textures->update();
|
||||
}
|
||||
Update_Pick_Control();
|
||||
Update_Pick_Node();
|
||||
update_screenshot();
|
||||
|
||||
m_updateaccumulator += Deltatime;
|
||||
if (m_updateaccumulator < 1.0)
|
||||
{
|
||||
// too early for any work
|
||||
return;
|
||||
}
|
||||
m_updateaccumulator = 0.0;
|
||||
m_framerate = 1000.f / Timer::subsystem.mainloop_total.average();
|
||||
|
||||
// adjust draw ranges etc, based on recent performance (opengl46_renderer::Update())
|
||||
if (Global.targetfps != 0.0f)
|
||||
{
|
||||
float fps_diff = Global.targetfps - m_framerate;
|
||||
if (fps_diff > 0.0f)
|
||||
Global.fDistanceFactor = std::max(0.5f, Global.fDistanceFactor - 0.05f);
|
||||
else
|
||||
Global.fDistanceFactor = std::min(Global.gfx_distance_factor_max, Global.fDistanceFactor + 0.05f);
|
||||
}
|
||||
// legacy framerate parameters
|
||||
else if (Global.fFpsAverage != 0.f)
|
||||
{
|
||||
auto const fps_diff = m_framerate - Global.fFpsAverage;
|
||||
if (fps_diff < -Global.fFpsDeviation)
|
||||
{
|
||||
Global.fDistanceFactor = std::min(std::max(1.0f, Global.fDistanceFactor - 0.05f), Global.gfx_distance_factor_max);
|
||||
}
|
||||
else if (fps_diff > Global.fFpsDeviation)
|
||||
{
|
||||
Global.fDistanceFactor = std::min(std::min(3.0f, Global.fDistanceFactor + 0.05f), Global.gfx_distance_factor_max);
|
||||
}
|
||||
}
|
||||
|
||||
if (Global.UpdateMaterials)
|
||||
{
|
||||
// update resources if there was environmental change
|
||||
simulation_state simulationstate{Global.Weather, Global.Season};
|
||||
std::swap(m_simulationstate, simulationstate);
|
||||
if ((m_simulationstate.season != simulationstate.season) && (false == simulationstate.season.empty()))
|
||||
{
|
||||
m_materials.on_season_change();
|
||||
}
|
||||
if ((m_simulationstate.weather != simulationstate.weather) && (false == simulationstate.weather.empty()))
|
||||
{
|
||||
m_materials.on_weather_change();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_context && m_context->textures && (true == Global.ResourceSweep) && (true == simulation::is_ready))
|
||||
{
|
||||
// garbage collection
|
||||
m_geometry.update();
|
||||
m_context->textures->update();
|
||||
}
|
||||
if ((Global.gfx_texture_releasedistance > 0.f) && (true == simulation::is_ready))
|
||||
{
|
||||
update_texture_release();
|
||||
}
|
||||
update_info();
|
||||
}
|
||||
|
||||
@@ -781,15 +524,6 @@ void vulkan14_renderer::update_info()
|
||||
}
|
||||
}
|
||||
|
||||
void vulkan14_renderer::Update_Pick_Control() {}
|
||||
|
||||
void vulkan14_renderer::Update_Pick_Node() {}
|
||||
|
||||
glm::dvec3 vulkan14_renderer::Update_Mouse_Position()
|
||||
{
|
||||
return glm::dvec3(0.0);
|
||||
}
|
||||
|
||||
bool vulkan14_renderer::Debug_Ui_State(std::optional<bool> State)
|
||||
{
|
||||
if (State)
|
||||
@@ -813,8 +547,3 @@ imgui_renderer *vulkan14_renderer::GetImguiRenderer()
|
||||
{
|
||||
return (m_context ? &m_context->imgui : nullptr);
|
||||
}
|
||||
|
||||
void vulkan14_renderer::MakeScreenshot()
|
||||
{
|
||||
vk14::log_warning("screenshots aren't made by the vulkan renderer yet (D6)");
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// the vulkan 1.4 renderer, picked with gfxrenderer vulkan. so far the core of the renderer (the track R of the plan,
|
||||
// claude/vulkan14-renderer-plan.md): the gpu, the windows with their swapchains, the frames in flight, the memory and the
|
||||
// uploads, the shaders and the pipelines, the pushed descriptors, the passes with their barriers and gpu times, the textures
|
||||
// and the user interface (imgui) with the launcher. the scenery and the materials come in the later stages of the plan; until
|
||||
// then those methods keep the data the simulation reads back and draw nothing.
|
||||
// the vulkan 1.4 renderer, picked with gfxrenderer vulkan (claude/vulkan14-renderer-plan.md): the gpu, the windows with their
|
||||
// swapchains, the frames in flight, the memory and the uploads, the shaders and the pipelines, the pushed descriptors, the passes
|
||||
// with their barriers and gpu times, the textures and the user interface (imgui) with the launcher (the track R of the plan), and
|
||||
// the scene (the track D): the geometry, the materials, the passes of the colour of the scene with the sun shadows, the
|
||||
// environment map and the post-processing, the picking, the screenshots, the extra viewports and the light clusters. the scene
|
||||
// is walked by the walk shared with the opengl renderer (gfx::scene_walk), whose draws this renderer records.
|
||||
//
|
||||
// the vulkan headers stay in the files of rendering/vulkan14: the objects of the api live in vulkan14_renderer::context
|
||||
|
||||
@@ -19,8 +20,24 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "rendering/renderer.h"
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "rendering/light.h"
|
||||
#include "rendering/lightarray.h"
|
||||
#include "rendering/common/scenewalk.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
#include "rendering/particles.h"
|
||||
#include "model/material.h"
|
||||
#include "vehicle/Camera.h"
|
||||
#include "gl/ubo.h"
|
||||
|
||||
class vulkan14_renderer : public gfx_renderer
|
||||
class world_environment;
|
||||
namespace vk14
|
||||
{
|
||||
struct scene_program;
|
||||
struct target_formats;
|
||||
enum class blend_mode;
|
||||
} // namespace vk14
|
||||
|
||||
class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
|
||||
{
|
||||
public:
|
||||
vulkan14_renderer();
|
||||
@@ -38,16 +55,23 @@ class vulkan14_renderer : public gfx_renderer
|
||||
gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0) override;
|
||||
bool Replace_Indexed(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry) override;
|
||||
bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override;
|
||||
gfx::index_array const &Indices(gfx::geometry_handle const &Geometry) const override;
|
||||
gfx::vertex_array const &Vertices(gfx::geometry_handle const &Geometry) const override;
|
||||
gfx::userdata_array const &UserData(gfx::geometry_handle const &Geometry) const override;
|
||||
int Primitive(gfx::geometry_handle const &Geometry) const override;
|
||||
// materials
|
||||
material_handle Fetch_Material(std::string const &Filename, bool const Loadnow = true) override;
|
||||
material_handle Matte_Material(material_handle const Material) override;
|
||||
material_handle Tinted_Material(material_handle const Material, glm::vec3 const &Colour) override;
|
||||
material_handle Blend_Material(material_handle const Material, std::array<material_handle, 3> const &Others) override;
|
||||
bool Blendable_Material(material_handle const Material) override;
|
||||
void Bind_Material(material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr) override;
|
||||
IMaterial const *Material(material_handle const Material) const override;
|
||||
// shaders
|
||||
std::shared_ptr<gfx::shader_program> Fetch_Shader(std::string const &name) override;
|
||||
std::string Shader_Name(std::shared_ptr<gfx::shader_program> const &Program) const override;
|
||||
// textures
|
||||
texture_handle Fetch_Texture(std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA) override;
|
||||
void Bind_Texture(texture_handle const Texture) override;
|
||||
@@ -60,6 +84,10 @@ class vulkan14_renderer : public gfx_renderer
|
||||
TSubModel const *Pick_Control() const override;
|
||||
scene::basic_node const *Pick_Node() const override;
|
||||
glm::dvec3 Mouse_Position() const override;
|
||||
bool Mouse_Hit() const override;
|
||||
glm::mat4 Camera_View_Matrix() const override;
|
||||
glm::mat4 Camera_Projection_Matrix() const override;
|
||||
glm::dvec3 Camera_Position() const override;
|
||||
// maintenance
|
||||
void Update(double const Deltatime) override;
|
||||
void Update_Pick_Control() override;
|
||||
@@ -72,27 +100,203 @@ class vulkan14_renderer : public gfx_renderer
|
||||
// ui
|
||||
imgui_renderer *GetImguiRenderer() override;
|
||||
void MakeScreenshot() override;
|
||||
bool Render_Preview(TAnimModel *Instance, int const Size, int const Margin, bool const Shadows, std::vector<std::uint8_t> &Image) override;
|
||||
|
||||
static std::unique_ptr<gfx_renderer> create_func();
|
||||
// how the application makes the windows of the renderer, and whether it can run on the machine
|
||||
static gfx_backend_desc backend_desc();
|
||||
|
||||
private:
|
||||
// the objects of the vulkan api (vulkan14renderer.cpp)
|
||||
// the objects of the vulkan api (vulkan14context.h)
|
||||
struct context;
|
||||
|
||||
void update_info();
|
||||
// the device was lost: what was on the gpu goes to the log
|
||||
void report_device_lost(char const *Where);
|
||||
// a viewport the scene is drawn to: the main window, an extra viewport (a window of its own or the main window drawn with
|
||||
// another projection)
|
||||
struct viewport_config
|
||||
{
|
||||
int width{0};
|
||||
int height{0};
|
||||
float draw_range{1.f};
|
||||
bool main{false};
|
||||
bool shadow{false}; // its view is taken into the sun shadows
|
||||
std::size_t window{0}; // the swapchain it goes to (vulkan14_renderer::context::windows)
|
||||
enum vp_type
|
||||
{
|
||||
normal,
|
||||
custom
|
||||
} proj_type{normal};
|
||||
viewport_proj_config projection{};
|
||||
};
|
||||
// the sun of the frame, with the factor of the place drawn (tunnels)
|
||||
struct sun_light : public basic_light
|
||||
{
|
||||
float factor{1.f};
|
||||
};
|
||||
|
||||
using gfx::scene_walk::Render;
|
||||
using gfx::scene_walk::Render_Alpha;
|
||||
|
||||
// the draws of the shared walk of the scene, recorded
|
||||
void walk_material(material_handle const Material, TSubModel const *Submodel = nullptr, lighting_data const *Lighting = nullptr) override;
|
||||
void walk_shadow_material(material_handle const Material) override;
|
||||
opengl_material const &walk_material_data(material_handle const Material) const override;
|
||||
opengl_material const &walk_material_data(TSubModel const *Submodel) const override;
|
||||
void walk_program(walk_shader const Program) override;
|
||||
void walk_texture(std::size_t const Unit, texture_handle const Texture) override;
|
||||
void walk_blending(bool const Alpha) override;
|
||||
void walk_sunlight(float const Factor = 1.f) override;
|
||||
void walk_environment(TEnvironmentType const Environment = e_flat) override;
|
||||
void walk_ambient_save() override;
|
||||
void walk_ambient_add(glm::vec3 const &Addition) override;
|
||||
void walk_ambient_restore() override;
|
||||
glm::vec4 walk_sun_ambient() const override;
|
||||
void walk_draw(gfx::geometry_handle const &Geometry) override;
|
||||
void walk_draw(std::vector<gfx::geometrybank_handle>::iterator First, std::vector<gfx::geometrybank_handle>::iterator Last) override;
|
||||
std::uint32_t walk_instances(glm::mat4 const *Modelviews, std::size_t const Count) override;
|
||||
void walk_cull_face(bool const Enabled) override;
|
||||
void walk_glare(bool const Enabled) override;
|
||||
void walk_glare_draw(TSubModel *Submodel, gfx::geometry_handle const &Geometry) override;
|
||||
void walk_light_query(TSubModel *Submodel, bool const Begin) override;
|
||||
void walk_editor_cursor() override;
|
||||
|
||||
// the scene (vulkan14scene.cpp)
|
||||
bool init_scene(std::string &Reason);
|
||||
// what the renderer prepares for the frames once it's there: the viewport of the main window, the common textures and geometry
|
||||
void init_frame_data();
|
||||
void destroy_scene();
|
||||
// the scene of the frame: the passes of the viewports
|
||||
void render_scene();
|
||||
void setup_pass(viewport_config &Viewport, renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false);
|
||||
void setup_matrices();
|
||||
// the colour of a viewport, with the passes it needs first: the sun shadows, the environment map, the light clusters
|
||||
void render_viewport(std::size_t const Viewport);
|
||||
void render_shadows(viewport_config &Viewport);
|
||||
bool render_reflections(viewport_config &Viewport);
|
||||
void render_reflection_face(viewport_config &Viewport);
|
||||
void render_picking(rendermode const Mode);
|
||||
// the sky dome and the sun and the moon
|
||||
void render_environment(world_environment *Environment);
|
||||
void render_particles();
|
||||
void render_precipitation();
|
||||
void render_editor_overlay();
|
||||
// the post-processing of a viewport: the motion blur, the tonemapping, the chromatic aberration
|
||||
void render_post(std::size_t const Viewport, bool const Motionblur);
|
||||
// the output of a viewport to its window, with the user interface over it for the main window
|
||||
void present_viewport(std::size_t const Viewport);
|
||||
// the light buffer of the pass: the sun, the ambient light, the fog, the headlights, the lamps (the clustered lights)
|
||||
void update_lights(light_array &Lights, bool const Headlights = true, bool const Clustered = false);
|
||||
void write_local_light(gl::light_element &Light, gfx::local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View);
|
||||
void headlight_projector(light_array::light_record const &Light, glm::dvec3 &Position, glm::mat3 &Rotation, glm::mat4 &Projection, float &Near, float &Fieldofview);
|
||||
// the light buffer and its header written for the draws which follow
|
||||
void upload_light_header();
|
||||
void upload_lights();
|
||||
void upload_scene_block();
|
||||
void setup_sunlight_intensity(float const Factor = 1.f);
|
||||
void setup_shadow_color(glm::vec4 const &Shadowcolor);
|
||||
void setup_shadow_matrices();
|
||||
// the light clusters of the pass: the grid and the lists of the lights
|
||||
void update_clusters(glm::ivec2 const &Targetsize);
|
||||
// the materials
|
||||
material_handle invalid_material() const
|
||||
{
|
||||
return m_invalid_material;
|
||||
}
|
||||
// the draws: the pipeline of the program for the state of the walk, made when it's first needed
|
||||
void draw_chunk(gfx::geometry_handle const &Geometry, bool const Newblock);
|
||||
// a pass of the scene recorded with the walk: its targets, its size, its pass set (vk14::scene_state::pass_sets)
|
||||
void start_draws(vk14::target_formats const &Targets, int const Width, int const Height, int const Set, bool const Shadows);
|
||||
void end_draws();
|
||||
// the rendering of the colour of a viewport, begun with the images cleared or again where it was
|
||||
void begin_colour_rendering(std::size_t const Viewport, bool const Clear);
|
||||
void push_constants(void const *Constants);
|
||||
// a pipeline of the post-processing (a VkPipeline), nullptr when the shader isn't there. Encode: the colour is encoded by the
|
||||
// shader (OUTPUT_GAMMA), -1: as gfx.shadergamma says
|
||||
void *post_pipeline(char const *Fragment, int const Format, int const Encode = -1);
|
||||
// the screenshot copied with a frame the gpu is done with, saved
|
||||
void update_screenshot();
|
||||
void bind_pass_set();
|
||||
bool bind_scene_pipeline(vk14::scene_program &Program, vk14::target_formats const &Targets, vk14::blend_mode const Blend, int const Topology);
|
||||
void apply_draw_state();
|
||||
void push_textures();
|
||||
std::uint32_t push_draw_block();
|
||||
void open_instance_window();
|
||||
void update_texture_release();
|
||||
|
||||
std::unique_ptr<context> m_context;
|
||||
// the data of the geometry, which the simulation reads back; nothing on the gpu yet
|
||||
// the data of the geometry, which the simulation reads back, and the banks with it on the gpu
|
||||
gfx::geometrybank_manager m_geometry;
|
||||
std::vector<gfx::geometry_bank *> m_banks;
|
||||
material_manager m_materials;
|
||||
material_handle m_invalid_material{null_handle};
|
||||
std::vector<viewport_config> m_viewports;
|
||||
viewport_config *m_current_viewport{nullptr};
|
||||
renderpass_config m_colorpass; // of the latest colour pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // of the latest cascades of the sun shadows
|
||||
sun_light m_sunlight;
|
||||
glm::vec3 m_fogcolor{0.f};
|
||||
glm::vec4 m_shadowcolor{colors::shadow};
|
||||
gl::scene_ubs scene_ubs{};
|
||||
gl::light_data light_data{};
|
||||
std::uint32_t m_lightcount{1}; // lights written to light_data
|
||||
// the copies of the light buffer in the ring of the frame since the lights were last written, by the header and the sun
|
||||
std::vector<std::pair<std::array<std::uint8_t, offsetof(gl::light_data, lights) + sizeof(gl::light_element)>, std::uint32_t>> m_lightheaders;
|
||||
packed_vec3 m_cabambient{0.f};
|
||||
gfx::local_lights m_locallights;
|
||||
std::uint32_t m_lights_lit{0};
|
||||
std::uint32_t m_lights_dropped{0};
|
||||
// the reflections: the face drawn, and the place and the time of the last update
|
||||
int m_environmentcubetextureface{0};
|
||||
int m_environmentupdateface{6};
|
||||
bool m_environmentready{false};
|
||||
double m_environmentupdatetime{0.0};
|
||||
glm::dvec3 m_environmentupdatelocation{0.0};
|
||||
bool m_vehiclesmoved{true};
|
||||
texture_handle m_suntexture{-1};
|
||||
texture_handle m_moontexture{-1};
|
||||
texture_handle m_smoketexture{-1};
|
||||
texture_handle m_headlightstexture{-1};
|
||||
texture_handle m_precipitationtexture{-1};
|
||||
float m_precipitationovercast{-1.f};
|
||||
double m_precipitationrotation{0.0};
|
||||
// the editor overlay
|
||||
gfx::geometrybank_handle m_editor_overlay_bank{};
|
||||
std::vector<gfx::geometry_handle> m_editor_overlay_geometry;
|
||||
unsigned int m_editor_overlay_revision{0};
|
||||
unsigned int m_nodestateversion{0};
|
||||
// picking
|
||||
std::vector<std::function<void(TSubModel const *, glm::vec2)>> m_control_pick_requests;
|
||||
std::vector<std::function<void(scene::basic_node *)>> m_node_pick_requests;
|
||||
TSubModel const *m_pickcontrolitem{nullptr};
|
||||
scene::basic_node *m_picksceneryitem{nullptr};
|
||||
bool m_pickcontrolswanted{false};
|
||||
bool m_picknodewanted{false};
|
||||
glm::ivec2 m_pickposition{0};
|
||||
glm::vec3 m_worldmousecoordinates{0.f};
|
||||
bool m_mousehit{false};
|
||||
// the state of the draws of the walk being recorded
|
||||
struct draw_state;
|
||||
std::unique_ptr<draw_state> m_draw;
|
||||
// textures used by vehicle models, for the release of far textures
|
||||
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
||||
std::vector<texture_handle> const &Model_Textures(TModel3d *Model);
|
||||
|
||||
// the season and the weather the materials were last given
|
||||
struct simulation_state
|
||||
{
|
||||
std::string weather;
|
||||
std::string season;
|
||||
} m_simulationstate;
|
||||
|
||||
float m_framerate{0.f};
|
||||
double m_updateaccumulator{0.0};
|
||||
bool m_debug_ui_active{false};
|
||||
std::string m_infotimes;
|
||||
std::string m_infostats;
|
||||
debug_stats m_laststats;
|
||||
|
||||
void update_info();
|
||||
// the device was lost: what was on the gpu goes to the log
|
||||
void report_device_lost(char const *Where);
|
||||
|
||||
static bool renderer_register;
|
||||
};
|
||||
|
||||
1405
rendering/vulkan14/vulkan14scene.cpp
Normal file
1405
rendering/vulkan14/vulkan14scene.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "stdafx.h"
|
||||
#include "rendering/vulkan14/vulkan14shaders.h"
|
||||
#include "rendering/vulkan14/vulkan14device.h"
|
||||
#include "rendering/vulkan14/vulkan14bindings.h"
|
||||
#include "gl/ubo.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/utilities.h"
|
||||
@@ -562,6 +564,24 @@ VkPipeline pipeline_library::get(pipeline_desc const &Desc)
|
||||
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
case blend_mode::scene_alpha:
|
||||
attachment.blendEnable = VK_TRUE;
|
||||
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
case blend_mode::scene_additive:
|
||||
attachment.blendEnable = VK_TRUE;
|
||||
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
case blend_mode::additive:
|
||||
attachment.blendEnable = VK_TRUE;
|
||||
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
@@ -703,3 +723,60 @@ void set_draw_state(VkCommandBuffer Commands, VkExtent2D Extent)
|
||||
}
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
namespace vk14
|
||||
{
|
||||
|
||||
std::string binding_preamble()
|
||||
{
|
||||
std::string preamble{"#define MASZYNA_CONSTANTS 1\n"};
|
||||
auto const define = [&preamble](char const *Name, auto const Value, char const *Suffix = "") { preamble += std::string("#define ") + Name + " " + std::to_string(Value) + Suffix + "\n"; };
|
||||
using namespace vk14::bindings;
|
||||
define("UI_SET", UI_SET);
|
||||
define("UI_TEXTURE", UI_TEXTURE);
|
||||
define("PASS_SET", PASS_SET);
|
||||
define("MATERIAL_SET", MATERIAL_SET);
|
||||
define("SCENE_UBO", SCENE_UBO);
|
||||
define("DRAW_SSBO", DRAW_SSBO);
|
||||
define("INSTANCE_SSBO", INSTANCE_SSBO);
|
||||
define("LIGHT_SSBO", LIGHT_SSBO);
|
||||
define("CLUSTER_UBO", CLUSTER_UBO);
|
||||
define("CLUSTER_RANGE_SSBO", CLUSTER_RANGE_SSBO);
|
||||
define("CLUSTER_INDEX_SSBO", CLUSTER_INDEX_SSBO);
|
||||
define("SHADOW_TEX", SHADOW_TEX);
|
||||
define("ENV_TEX", ENV_TEX);
|
||||
define("HEADLIGHT_TEX", HEADLIGHT_TEX);
|
||||
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
|
||||
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
|
||||
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);
|
||||
define("CL_AABB_SSBO", CL_AABB_SSBO);
|
||||
define("CL_RANGE_SSBO", CL_RANGE_SSBO);
|
||||
define("CL_INDEX_SSBO", CL_INDEX_SSBO);
|
||||
define("CL_COUNTER_SSBO", CL_COUNTER_SSBO);
|
||||
define("CL_LIGHT_SSBO", CL_LIGHT_SSBO);
|
||||
define("POST_SET", POST_SET);
|
||||
define("POST_TEXTURE_FIRST", POST_TEXTURE_FIRST);
|
||||
define("PUSH_CONSTANTS_SIZE", PUSH_CONSTANTS_SIZE);
|
||||
define("SPEC_OUTPUT_GAMMA", SPEC_OUTPUT_GAMMA);
|
||||
define("SPEC_SHADOWMAP_ENABLED", SPEC_SHADOWMAP_ENABLED);
|
||||
define("SPEC_ENVMAP_ENABLED", SPEC_ENVMAP_ENABLED);
|
||||
define("SPEC_MOTIONBLUR_ENABLED", SPEC_MOTIONBLUR_ENABLED);
|
||||
define("SPEC_POSTFX_ENABLED", SPEC_POSTFX_ENABLED);
|
||||
define("SPEC_EXTRAEFFECTS_ENABLED", SPEC_EXTRAEFFECTS_ENABLED);
|
||||
define("SPEC_COUNT", SPEC_COUNT);
|
||||
// the constants of the game the blocks of the shaders are sized by, the same as for the opengl renderer (gl/ubo.h)
|
||||
define("MAX_LIGHTS", static_cast<unsigned int>(gl::MAX_LIGHTS), "U");
|
||||
define("MAX_CASCADES", static_cast<unsigned int>(gl::MAX_CASCADES), "U");
|
||||
define("MAX_PARAMS", static_cast<unsigned int>(gl::MAX_PARAMS), "U");
|
||||
define("MAX_HEADLIGHT_SHADOWS", static_cast<unsigned int>(gl::MAX_HEADLIGHT_SHADOWS), "U");
|
||||
define("CLUSTER_GRID_X", gl::CLUSTER_GRID_X);
|
||||
define("CLUSTER_GRID_Y", gl::CLUSTER_GRID_Y);
|
||||
define("CLUSTER_GRID_Z", gl::CLUSTER_GRID_Z);
|
||||
define("CLUSTER_COUNT", gl::CLUSTER_COUNT);
|
||||
define("MAX_LIGHTS_PER_CLUSTER", gl::MAX_LIGHTS_PER_CLUSTER);
|
||||
define("CLUSTER_BUILD_GROUP_SIZE", gl::CLUSTER_BUILD_GROUP_SIZE);
|
||||
define("CLUSTER_CULL_GROUP_SIZE", gl::CLUSTER_CULL_GROUP_SIZE);
|
||||
return preamble;
|
||||
}
|
||||
|
||||
} // namespace vk14
|
||||
|
||||
@@ -76,7 +76,10 @@ enum class blend_mode
|
||||
none,
|
||||
alpha, // the colour over what is there by its alpha, the alpha kept for what's behind (the user interface)
|
||||
premultiplied,
|
||||
additive
|
||||
additive,
|
||||
// the blending of the scene, as glBlendFunc() of the opengl renderer gives it to the colour and the alpha alike
|
||||
scene_alpha, // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
|
||||
scene_additive // GL_SRC_ALPHA, GL_ONE (the glare of the lamps)
|
||||
};
|
||||
|
||||
struct vertex_layout
|
||||
|
||||
@@ -57,10 +57,59 @@ void uploader::begin_frame(VkCommandBuffer Commands, std::uint64_t Value)
|
||||
|
||||
bool uploader::end_frame()
|
||||
{
|
||||
if (m_buffercopies && (m_commands != VK_NULL_HANDLE))
|
||||
{
|
||||
// the draws of the frame read what was copied
|
||||
VkMemoryBarrier2 barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
|
||||
barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT;
|
||||
VkDependencyInfo dependency{};
|
||||
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
dependency.memoryBarrierCount = 1;
|
||||
dependency.pMemoryBarriers = &barrier;
|
||||
vkCmdPipelineBarrier2(m_commands, &dependency);
|
||||
}
|
||||
m_buffercopies = false;
|
||||
m_commands = VK_NULL_HANDLE;
|
||||
return m_recorded;
|
||||
}
|
||||
|
||||
bool uploader::upload_buffer(std::shared_ptr<buffer> const &Target, void const *Data, VkDeviceSize Size, std::vector<std::pair<VkDeviceSize, VkDeviceSize>> const &Parts)
|
||||
{
|
||||
if ((Target == nullptr) || (Size == 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
job upload;
|
||||
upload.buffertarget = Target;
|
||||
upload.parts = Parts;
|
||||
if (upload.parts.empty())
|
||||
{
|
||||
upload.parts.emplace_back(0, Size);
|
||||
}
|
||||
upload.staging = std::make_shared<buffer>();
|
||||
if ((false == upload.staging->create(*m_allocator, Size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, memory_kind::upload)) || (upload.staging->mapped() == nullptr))
|
||||
{
|
||||
log_error("no staging buffer of " + memory_size_string(Size) + " for an upload");
|
||||
return false;
|
||||
}
|
||||
std::memcpy(upload.staging->mapped(), Data, static_cast<std::size_t>(Size));
|
||||
upload.staging->flush(0, Size);
|
||||
m_uploaded += Size;
|
||||
if (m_commands != VK_NULL_HANDLE)
|
||||
{
|
||||
record(upload);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pending.emplace_back(std::move(upload));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uploader::upload(std::shared_ptr<image> const &Image, void const *Data, VkDeviceSize Size, std::vector<level> const &Levels, bool const Mipmaps)
|
||||
{
|
||||
job upload;
|
||||
@@ -89,6 +138,47 @@ bool uploader::upload(std::shared_ptr<image> const &Image, void const *Data, VkD
|
||||
|
||||
void uploader::record(job &Job)
|
||||
{
|
||||
if (Job.buffertarget != nullptr)
|
||||
{
|
||||
if (false == m_buffercopies)
|
||||
{
|
||||
// the frames sent before may still read the buffers the copies write to
|
||||
VkMemoryBarrier2 barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
barrier.srcAccessMask = VK_ACCESS_2_NONE;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;
|
||||
VkDependencyInfo dependency{};
|
||||
dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
dependency.memoryBarrierCount = 1;
|
||||
dependency.pMemoryBarriers = &barrier;
|
||||
vkCmdPipelineBarrier2(m_commands, &dependency);
|
||||
m_buffercopies = true;
|
||||
}
|
||||
std::vector<VkBufferCopy2> regions;
|
||||
VkDeviceSize source{0};
|
||||
for (auto const &part : Job.parts)
|
||||
{
|
||||
VkBufferCopy2 region{};
|
||||
region.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2;
|
||||
region.srcOffset = source;
|
||||
region.dstOffset = part.first;
|
||||
region.size = part.second;
|
||||
regions.push_back(region);
|
||||
source += part.second;
|
||||
}
|
||||
VkCopyBufferInfo2 copy{};
|
||||
copy.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2;
|
||||
copy.srcBuffer = Job.staging->handle();
|
||||
copy.dstBuffer = Job.buffertarget->handle();
|
||||
copy.regionCount = static_cast<std::uint32_t>(regions.size());
|
||||
copy.pRegions = regions.data();
|
||||
vkCmdCopyBuffer2(m_commands, ©);
|
||||
m_recorded = true;
|
||||
m_deletions->push(m_value, [staging = Job.staging, target = Job.buffertarget]() {});
|
||||
return;
|
||||
}
|
||||
auto &target{*Job.target};
|
||||
auto const commands{m_commands};
|
||||
auto const aspect{target.aspect()};
|
||||
|
||||
@@ -54,6 +54,10 @@ class uploader
|
||||
// copies Data into Image (the levels given, the others made by blits of the first one when Mipmaps), the image left ready to be
|
||||
// sampled. Data is copied away. false: no staging memory, nothing will be copied
|
||||
bool upload(std::shared_ptr<image> const &Image, void const *Data, VkDeviceSize Size, std::vector<level> const &Levels, bool const Mipmaps);
|
||||
// copies Data into Target, done before the draws of the frame read it (and after the frames sent before did). Data holds the
|
||||
// pieces of Parts (offset in Target, size) one after another; all of Data goes to the start of Target when there are none.
|
||||
// false: no staging memory
|
||||
bool upload_buffer(std::shared_ptr<buffer> const &Target, void const *Data, VkDeviceSize Size, std::vector<std::pair<VkDeviceSize, VkDeviceSize>> const &Parts = {});
|
||||
// the bytes uploaded since the start, for the debug panel
|
||||
std::uint64_t uploaded() const
|
||||
{
|
||||
@@ -69,6 +73,9 @@ class uploader
|
||||
std::shared_ptr<buffer> staging;
|
||||
std::vector<level> levels;
|
||||
bool mipmaps{false};
|
||||
// a copy to a buffer instead: the pieces copied
|
||||
std::shared_ptr<buffer> buffertarget;
|
||||
std::vector<std::pair<VkDeviceSize, VkDeviceSize>> parts;
|
||||
};
|
||||
void record(job &Job);
|
||||
|
||||
@@ -78,6 +85,8 @@ class uploader
|
||||
VkCommandBuffer m_commands{VK_NULL_HANDLE};
|
||||
std::uint64_t m_value{0};
|
||||
bool m_recorded{false};
|
||||
// buffers were copied to in the frame: the barriers before the first copy and after the last one
|
||||
bool m_buffercopies{false};
|
||||
std::vector<job> m_pending;
|
||||
std::uint64_t m_uploaded{0};
|
||||
mutable std::unordered_map<VkFormat, bool> m_blittable;
|
||||
@@ -153,9 +162,10 @@ struct vulkan14_texture : public texture_source
|
||||
|
||||
bool create(bool const Static = false) override;
|
||||
void release() override;
|
||||
// as the opengl textures give it: one not made yet has no id of 0, which the materials take for a texture which is missing
|
||||
size_t get_id() const override
|
||||
{
|
||||
return static_cast<size_t>(m_id);
|
||||
return (m_made ? static_cast<size_t>(m_id) : std::numeric_limits<size_t>::max());
|
||||
}
|
||||
void update_from_memory(size_t width, size_t height, const uint8_t *data) override;
|
||||
bool resident() const override
|
||||
|
||||
BIN
shaders.7z
BIN
shaders.7z
Binary file not shown.
@@ -41,3 +41,25 @@ source, compile the binaries again (or delete them): a binary older than its sou
|
||||
* The constants of the game (bindings, sizes) are written in `ogl46/common.glsl` and `vk14/common.glsl` for the binaries; the
|
||||
game puts its own values before the sources and checks at the start that the files have the same ones (the shader
|
||||
validator in `ci_shadervalidator` checks them too).
|
||||
|
||||
## vk14
|
||||
|
||||
The shaders of `vk14/` are those of `ogl46/` made for the descriptor sets of the Vulkan renderer
|
||||
(`rendering/vulkan14/vulkan14bindings.h`), drawn with the same matrices and the same conventions as the OpenGL renderer
|
||||
(nothing is turned upside down but the picture shown in the window, `present.frag`).
|
||||
|
||||
* A stage file includes `common.glsl` right after its stage define. The pipeline layout it is drawn with is picked by a
|
||||
define before that include: none for the passes of the scene, `LAYOUT_POST` for the post-processing (with `quad.vert`),
|
||||
`LAYOUT_CLUSTER` for the compute passes of the light clusters, `LAYOUT_UI` for the user interface.
|
||||
* The scene: set `PASS_SET` holds the blocks of the scene, of the draws (`model_draws[]`, the one of a draw picked by the push
|
||||
constant `pc.draw`), of the instances and of the lights and clusters, and the shadow, environment and headlight maps;
|
||||
set `MATERIAL_SET` the textures of the material at `MATERIAL_TEXTURE_FIRST + unit` of their `#pragma texture`. The names of
|
||||
the model block (`modelview`, `param`, `opacity` ...) stand for the fields of the draw, as in the `MODEL_DRAWS` variant of
|
||||
`ogl46/`.
|
||||
* The push constants (`pc`, `PUSH_CONSTANTS_SIZE` bytes) carry the draw and the parameters of the special shaders in
|
||||
`pc.data[]`; each of those says at the top what it takes from them. In the post-processing `param` stands for `pc.data`.
|
||||
* The settings are specialization constants with the ids `SPEC_*` (`vk14/settings.glsl`).
|
||||
* The features of the later stages of the renderer (the shadows of the lamps and the headlights, the contact shadows, the
|
||||
weather and its fog, the sky ambient light, the prefiltered environment map, the automatic exposure, soft and gpu
|
||||
particles) aren't in `vk14/` yet; the code paths are the ones `ogl46/` takes with them off, the fog is an analytic one
|
||||
(`vk14/apply_fog.glsl`).
|
||||
|
||||
@@ -23,9 +23,10 @@ import sys
|
||||
|
||||
STAGES = ('.vert', '.frag', '.geom', '.comp')
|
||||
TREES = {
|
||||
# source directory, binary directory, target environment, variants of the vertex and fragment shaders
|
||||
'ogl46': ('ogl46', 'ogl46bin', 'opengl4.5', ['MODEL_DRAWS']),
|
||||
'vk14': ('vk14', 'vk14bin', 'vulkan1.4', []),
|
||||
# source directory, binary directory, target environment, variants of the vertex and fragment shaders, whether a variant is
|
||||
# made only for the files which use its name (opengl links a program from the binaries of all of its stages or from none)
|
||||
'ogl46': ('ogl46', 'ogl46bin', 'opengl4.5', ['MODEL_DRAWS'], False),
|
||||
'vk14': ('vk14', 'vk14bin', 'vulkan1.4', ['NO_MOTION_OUTPUT'], True),
|
||||
}
|
||||
INCLUDE = re.compile(r'^[ \t]*#include[ \t]*[<"]([^>"]+)[>"]', re.M)
|
||||
|
||||
@@ -84,7 +85,7 @@ def main():
|
||||
|
||||
failed = []
|
||||
count = 0
|
||||
for key, (sourcedir, binarydir, env, variants) in TREES.items():
|
||||
for key, (sourcedir, binarydir, env, variants, onlyused) in TREES.items():
|
||||
if args.only and args.only != key:
|
||||
continue
|
||||
sources = os.path.join(args.shaders, sourcedir)
|
||||
@@ -96,13 +97,17 @@ def main():
|
||||
names = sorted(n for n in os.listdir(sources) if os.path.splitext(n)[1] in STAGES)
|
||||
for name in names:
|
||||
jobs = [('', [])]
|
||||
if os.path.splitext(name)[1] in ('.vert', '.frag'):
|
||||
jobs += [(v, ['-D%s=1' % v]) for v in variants]
|
||||
try:
|
||||
deps = dependencies(sources, name)
|
||||
except OSError as e:
|
||||
failed.append('%s/%s (%s)' % (sourcedir, name, e))
|
||||
continue
|
||||
if os.path.splitext(name)[1] in ('.vert', '.frag'):
|
||||
text = ''
|
||||
for dep in deps:
|
||||
with open(os.path.join(sources, dep), 'r', encoding='utf-8', errors='replace') as f:
|
||||
text += f.read()
|
||||
jobs += [(v, ['-D%s=1' % v]) for v in variants if (not onlyused) or (v in text)]
|
||||
for variant, defines in jobs:
|
||||
binary = name + ('.' + variant if variant else '') + '.spv'
|
||||
command = [glslc, '--target-env=' + env] + defines + [os.path.join(sources, name), '-o', os.path.join(binaries, binary)]
|
||||
|
||||
13
shaders/vk14/alphashadowmap.frag
Normal file
13
shaders/vk14/alphashadowmap.frag
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (texture(tex1, f_coord).a < 0.5f)
|
||||
discard;
|
||||
}
|
||||
40
shaders/vk14/apply_fog.glsl
Normal file
40
shaders/vk14/apply_fog.glsl
Normal file
@@ -0,0 +1,40 @@
|
||||
// fog between the camera and a fragment, the api of shaders/ogl46/apply_fog.glsl. the fog volume and the weather of the opengl
|
||||
// renderer come with the stage P2 of the plan; until then the fog is analytic: exponential with the distance, of the density of
|
||||
// the draw (fog_density of the model block, a draw with 0 is not fogged), of the colour fog_color of the light buffer, turned
|
||||
// towards the colour of the sun around its direction
|
||||
// rgb: light the fog adds, a: what it lets through
|
||||
vec4 fog_at_fragment()
|
||||
{
|
||||
if (fog_density <= 0.0)
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
float amount = 1.0 - clamp(exp(-length(f_pos.xyz) * fog_density), 0.0, 1.0);
|
||||
vec3 color = fog_color;
|
||||
if ((lights_count >= 1U) && (lights[0].type == LIGHT_DIR))
|
||||
{
|
||||
float sunward = pow(max(dot(normalize(f_pos.xyz), normalize(-lights[0].dir)), 0.0), 30.0);
|
||||
color = mix(color, lights[0].color, sunward);
|
||||
}
|
||||
return vec4(color * amount, 1.0 - amount);
|
||||
}
|
||||
|
||||
vec3 get_fog_color()
|
||||
{
|
||||
vec4 fog = fog_at_fragment();
|
||||
return fog.rgb / max(1.0 - fog.a, 1e-4);
|
||||
}
|
||||
|
||||
float get_fog_amount()
|
||||
{
|
||||
return 1.0 - fog_at_fragment().a;
|
||||
}
|
||||
|
||||
vec3 apply_fog(vec3 color)
|
||||
{
|
||||
vec4 fog = fog_at_fragment();
|
||||
return color * fog.a + fog.rgb;
|
||||
}
|
||||
|
||||
vec3 add_fog(vec3 color, float amount)
|
||||
{
|
||||
return color + fog_at_fragment().rgb * amount;
|
||||
}
|
||||
34
shaders/vk14/billboard.frag
Normal file
34
shaders/vk14/billboard.frag
Normal file
@@ -0,0 +1,34 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(tex1, f_coord);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = tex_color * param[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(tex_color * param[0]);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
93
shaders/vk14/celestial.frag
Normal file
93
shaders/vk14/celestial.frag
Normal file
@@ -0,0 +1,93 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
// the sun, the moon and the textured sprite of the easter egg, drawn over the sky on a quad around the direction of the body
|
||||
// (celestial.vert). param[0]: colour (rgb) and alpha, param[1]: position in view space and half the size of the quad,
|
||||
// param[2].xy: corner (sprite) or centre (moon) of the picture in the texture, param[2].z: its size (sprite) or the radius of
|
||||
// the disc in it (moon), in texture units, param[2].w: kind of body, 0: textured sprite, 1: sun, 2: moon.
|
||||
// the colour goes out premultiplied, the alpha is how much of the sky behind the body it hides (blending ONE, ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 15) in vec2 f_local;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
// shape and light of the sun and the moon, pc.data[0..2] of the push constants (a loose uniform vec4 body[3] in the opengl
|
||||
// renderer, set by opengl46_renderer::Render(world_environment *)):
|
||||
// [0].xy: direction to the zenith on the quad, [0].z: height over width of the disc (flattened by the refraction near the
|
||||
// horizon), [0].w: radius of the disc in units of half the quad
|
||||
// [1].xyz: direction to the sun in view space, [1].w: light of the earth on the dark part of the moon, against the light of
|
||||
// the sun on the lit part
|
||||
// [2].rgb: limb darkening of the sun per channel (linear law)
|
||||
#define body pc.data
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
int kind = int(param[2].w + 0.5);
|
||||
vec4 color = vec4(0.0);
|
||||
if (kind == 0)
|
||||
{
|
||||
vec4 tex_color = texture(tex1, f_coord) * param[0];
|
||||
color = vec4(tex_color.rgb * tex_color.a, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the point on the disc in its radii, x along the horizon, y to the zenith, with the flattening undone
|
||||
vec2 zenith = body[0].xy;
|
||||
vec2 across = vec2(zenith.y, -zenith.x);
|
||||
vec2 p = vec2(dot(f_local, across), dot(f_local, zenith)) / body[0].w;
|
||||
p.y /= body[0].z;
|
||||
float r = length(p);
|
||||
float coverage = clamp((1.0 - r) / max(fwidth(r), 1e-4) + 0.5, 0.0, 1.0);
|
||||
// the horizon hides what is under it
|
||||
vec3 direction = normalize(param[1].xyz + vec3(f_local * param[1].w, 0.0));
|
||||
float height = (mat3(inv_view) * direction).y;
|
||||
coverage *= clamp(height / max(fwidth(height), 1e-7) + 0.5, 0.0, 1.0);
|
||||
// cosine of the angle between the line of sight and the surface of the sphere
|
||||
float mu = sqrt(max(1.0 - r * r, 0.0));
|
||||
if (kind == 1)
|
||||
{
|
||||
// limb darkening, kept to the average brightness of the disc
|
||||
vec3 limb = (vec3(1.0) - body[2].rgb * (1.0 - mu)) / (vec3(1.0) - body[2].rgb / 3.0);
|
||||
color = vec4(param[0].rgb * limb * coverage, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the moon as a sphere lit from the direction of the sun. the frame of the disc in view space, z to the viewer
|
||||
vec3 z = -normalize(param[1].xyz);
|
||||
vec3 x = vec3(across, 0.0);
|
||||
x = normalize(x - z * dot(x, z));
|
||||
vec3 y = cross(z, x);
|
||||
vec2 q = p / max(r, 1.0);
|
||||
vec3 normal = q.x * x + q.y * y + mu * z;
|
||||
float mu0 = max(dot(normal, normalize(body[1].xyz)), 0.0);
|
||||
// Lommel-Seeliger law: an even disc at the full moon, as the real one, no darkening towards the limb
|
||||
float lit = 2.0 * mu0 / (mu0 + max(mu, 1e-3));
|
||||
vec3 albedo = texture(tex1, param[2].xy + p * param[2].z).rgb;
|
||||
color = vec4(albedo * param[0].rgb * ((lit + body[1].w) * coverage), coverage * param[0].a);
|
||||
}
|
||||
}
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = vec4(tonemap(vec4(color.rgb, 1.0)).rgb, color.a);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
34
shaders/vk14/celestial.vert
Normal file
34
shaders/vk14/celestial.vert
Normal file
@@ -0,0 +1,34 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#include "common.glsl"
|
||||
const vec2 vert[4] = vec2[]
|
||||
(
|
||||
vec2(-1.0, 1.0),
|
||||
vec2(-1.0, -1.0),
|
||||
vec2( 1.0, 1.0),
|
||||
vec2( 1.0, -1.0)
|
||||
);
|
||||
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
// the corner of the quad, -1...1 across it
|
||||
layout(location = 15) out vec2 f_local;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * (vec4(param[1].xyz, 1.0) + vec4(vert[gl_VertexIndex] * param[1].w, 0.0, 0.0));
|
||||
|
||||
f_local = vert[gl_VertexIndex];
|
||||
|
||||
vec2 coord = param[2].xy;
|
||||
float size = param[2].z;
|
||||
|
||||
if (gl_VertexIndex == 0)
|
||||
f_coord = vec2(coord.x, coord.y);
|
||||
if (gl_VertexIndex == 1)
|
||||
f_coord = vec2(coord.x, coord.y - size);
|
||||
if (gl_VertexIndex == 2)
|
||||
f_coord = vec2(coord.x + size, coord.y);
|
||||
if (gl_VertexIndex == 3)
|
||||
f_coord = vec2(coord.x + size, coord.y - size);
|
||||
}
|
||||
59
shaders/vk14/cluster_build.comp
Normal file
59
shaders/vk14/cluster_build.comp
Normal file
@@ -0,0 +1,59 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
#define LAYOUT_CLUSTER 1
|
||||
#include "common.glsl"
|
||||
// bounding boxes of the light clusters in view space, see gl/cluster_grid.h
|
||||
// one invocation per cluster, run when the projection or the depth range of the pass changes
|
||||
|
||||
layout(local_size_x = CLUSTER_BUILD_GROUP_SIZE) in;
|
||||
|
||||
// clip space w at the lower boundary of slice k: the inverse of the slice formula used when shading
|
||||
float slice_depth(uint k)
|
||||
{
|
||||
return exp((float(k) - cluster_slice_bias) / cluster_slice_scale);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
if (index >= uint(CLUSTER_COUNT))
|
||||
return;
|
||||
|
||||
uvec3 cell = uvec3(
|
||||
index % uint(CLUSTER_GRID_X),
|
||||
(index / uint(CLUSTER_GRID_X)) % uint(CLUSTER_GRID_Y),
|
||||
index / uint(CLUSTER_GRID_X * CLUSTER_GRID_Y));
|
||||
|
||||
// clip x, y and w are affine functions of the view space position, while clip z is constant (reverse z with
|
||||
// the far plane at infinity), which makes the projection matrix singular. the position is recovered from
|
||||
// those three alone, so off-axis and rotated screens of custom viewports and vr eyes work too
|
||||
mat3 linear = mat3(
|
||||
vec3(cluster_projection[0][0], cluster_projection[0][1], cluster_projection[0][3]),
|
||||
vec3(cluster_projection[1][0], cluster_projection[1][1], cluster_projection[1][3]),
|
||||
vec3(cluster_projection[2][0], cluster_projection[2][1], cluster_projection[2][3]));
|
||||
vec3 translation = vec3(cluster_projection[3][0], cluster_projection[3][1], cluster_projection[3][3]);
|
||||
mat3 unproject = inverse(linear);
|
||||
|
||||
// the tile in clip space xy, the slice in clip space w
|
||||
vec2 tiles = vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y);
|
||||
vec2 ndc_min = vec2(cell.xy) / tiles * 2.0 - 1.0;
|
||||
vec2 ndc_max = vec2(cell.xy + 1U) / tiles * 2.0 - 1.0;
|
||||
float w_near = slice_depth(cell.z);
|
||||
float w_far = slice_depth(cell.z + 1U);
|
||||
|
||||
vec3 lower = vec3(3.402823e38);
|
||||
vec3 upper = vec3(-3.402823e38);
|
||||
for (int corner = 0; corner < 8; ++corner)
|
||||
{
|
||||
vec2 ndc = vec2(
|
||||
(corner & 1) != 0 ? ndc_max.x : ndc_min.x,
|
||||
(corner & 2) != 0 ? ndc_max.y : ndc_min.y);
|
||||
float w = (corner & 4) != 0 ? w_far : w_near;
|
||||
vec3 position = unproject * (vec3(ndc * w, w) - translation);
|
||||
lower = min(lower, position);
|
||||
upper = max(upper, position);
|
||||
}
|
||||
|
||||
cluster_aabbs[index].lower = vec4(lower, 0.0);
|
||||
cluster_aabbs[index].upper = vec4(upper, 0.0);
|
||||
}
|
||||
157
shaders/vk14/cluster_cull.comp
Normal file
157
shaders/vk14/cluster_cull.comp
Normal file
@@ -0,0 +1,157 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
#define LAYOUT_CLUSTER 1
|
||||
#include "common.glsl"
|
||||
// assigns the lights to the light clusters, see gl/cluster_grid.h
|
||||
// one invocation per cluster: tests every light of light_ssbo (the sun, slot 0, is handled apart from the clusters)
|
||||
// against the bounding box of the cluster in view space, and writes the indices of the lights which reach it.
|
||||
// run every frame after the lights were updated, for the grid of the pass about to be drawn
|
||||
|
||||
layout(local_size_x = CLUSTER_CULL_GROUP_SIZE) in;
|
||||
|
||||
// a light is taken to end where its brightest possible contribution drops under this, a fraction of
|
||||
// what the display can tell apart. there is no other limit: the 1 km filter on the cpu side is about the distance of a light
|
||||
// from the camera, not about how far it reaches, and the shading lights a surface at any distance
|
||||
const float MIN_CONTRIBUTION = 1.0 / 1024.0;
|
||||
// reach of a light whose contribution does not fall off with the distance; its square still fits in a float
|
||||
const float UNLIMITED_RANGE = 1.0e18;
|
||||
// the boxes and the light data are floats: planes of the headlight projection let this much (in meters) of a box through
|
||||
const float PLANE_MARGIN = 0.05;
|
||||
|
||||
// brightest value the light adds to a surface it hits, as the shading computes it
|
||||
float light_peak(light_s light)
|
||||
{
|
||||
float peak = max(light.color.r, max(light.color.g, light.color.b)) * light.intensity;
|
||||
if (light.type == LIGHT_HEADLIGHTS)
|
||||
// weights are the state of the individual lamps and the beam texture, which is not brighter than 1, picks from them
|
||||
return peak * max(light.headlight_weights.r, max(light.headlight_weights.g, light.headlight_weights.b));
|
||||
return peak * (1.0 + light.ambient);
|
||||
}
|
||||
|
||||
// distance from the light at which its contribution falls under MIN_CONTRIBUTION, 0 for a light which has none
|
||||
float light_range(light_s light)
|
||||
{
|
||||
float peak = light_peak(light);
|
||||
if (!(peak > MIN_CONTRIBUTION))
|
||||
return 0.0;
|
||||
// attenuation which the contribution can drop to without being lost: 1 / k
|
||||
float k = peak / MIN_CONTRIBUTION;
|
||||
|
||||
if (light.type == LIGHT_HEADLIGHTS)
|
||||
{
|
||||
// 1 + linear * d + quadratic * d^2 <= k, the positive root written the way which has no cancellation for a small quadratic
|
||||
float a = light.quadratic;
|
||||
float b = light.linear;
|
||||
float c = 1.0 - k;
|
||||
float denominator = b + sqrt(b * b - 4.0 * a * c);
|
||||
return (denominator > 1e-12) ? min(-2.0 * c / denominator, UNLIMITED_RANGE) : UNLIMITED_RANGE;
|
||||
}
|
||||
// point and spot lights fall off with the square of the distance; the shading ends them at 1 / sqrt(quadratic) where it is set
|
||||
float range = sqrt(k);
|
||||
return (light.quadratic > 0.0) ? min(range, inversesqrt(light.quadratic)) : range;
|
||||
}
|
||||
|
||||
bool sphere_touches_box(vec3 center, float radius, vec3 lower, vec3 upper)
|
||||
{
|
||||
vec3 offset = center - clamp(center, lower, upper);
|
||||
return dot(offset, offset) <= radius * radius;
|
||||
}
|
||||
|
||||
// whether any part of the box, given by its center and half extents, is on the positive side of the plane
|
||||
bool box_reaches_plane(vec4 plane, vec3 center, vec3 extent)
|
||||
{
|
||||
return dot(plane.xyz, center) + plane.w + dot(abs(plane.xyz), extent) + PLANE_MARGIN * length(plane.xyz) >= 0.0;
|
||||
}
|
||||
|
||||
// planes bounding the beam of a headlight: the projection takes a view space position to the texture coordinates
|
||||
// of the beam, and the beam is where those fall in 0..1 and the position is in front of the projector. clip z of that
|
||||
// projection is a constant (reverse z, no far plane), so only the four sides and the near plane are real planes
|
||||
bool box_in_headlight_beam(mat4 projection, vec3 center, vec3 extent)
|
||||
{
|
||||
vec4 row_x = vec4(projection[0][0], projection[1][0], projection[2][0], projection[3][0]);
|
||||
vec4 row_y = vec4(projection[0][1], projection[1][1], projection[2][1], projection[3][1]);
|
||||
vec4 row_z = vec4(projection[0][2], projection[1][2], projection[2][2], projection[3][2]);
|
||||
vec4 row_w = vec4(projection[0][3], projection[1][3], projection[2][3], projection[3][3]);
|
||||
|
||||
return box_reaches_plane(row_x, center, extent)
|
||||
&& box_reaches_plane(row_w - row_x, center, extent)
|
||||
&& box_reaches_plane(row_y, center, extent)
|
||||
&& box_reaches_plane(row_w - row_y, center, extent)
|
||||
&& box_reaches_plane(row_z, center, extent)
|
||||
&& box_reaches_plane(row_w - row_z, center, extent);
|
||||
}
|
||||
|
||||
// whether the sphere touches the infinite cone with the apex at the origin, the unit axis and the cosine of the half angle
|
||||
bool sphere_touches_cone(vec3 center, float radius, vec3 axis, float cosine)
|
||||
{
|
||||
float sine = max(sqrt(max(1.0 - cosine * cosine, 0.0)), 1e-4);
|
||||
float along = dot(center, axis);
|
||||
float across = sqrt(max(dot(center, center) - along * along, 0.0));
|
||||
// distance to the boundary ray if the closest point of the cone is on it, and to the apex otherwise
|
||||
float distance_to_cone = (along * cosine + across * sine > 0.0) ? across * cosine - along * sine : length(center);
|
||||
return distance_to_cone <= radius;
|
||||
}
|
||||
|
||||
bool light_reaches_box(light_s light, float range, vec3 lower, vec3 upper)
|
||||
{
|
||||
vec3 center = 0.5 * (lower + upper);
|
||||
vec3 extent = 0.5 * (upper - lower);
|
||||
|
||||
if (light.type == LIGHT_DIR)
|
||||
return true;
|
||||
|
||||
if (!sphere_touches_box(light.pos, range, lower, upper))
|
||||
return false;
|
||||
|
||||
if (light.type == LIGHT_HEADLIGHTS)
|
||||
return box_in_headlight_beam(light.headlight_projection, center, extent);
|
||||
|
||||
if ((light.type == LIGHT_SPOT) && (light.out_cutoff > 0.0))
|
||||
// the cone is tested against the sphere around the box, which lets some of the corners through. a cone wider than
|
||||
// a half of the space is taken as the whole sphere
|
||||
return sphere_touches_cone(center - light.pos, length(extent), normalize(light.dir), light.out_cutoff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
if (index >= uint(CLUSTER_COUNT))
|
||||
return;
|
||||
|
||||
vec3 lower = cluster_aabbs[index].lower.xyz;
|
||||
vec3 upper = cluster_aabbs[index].upper.xyz;
|
||||
|
||||
// the lights are in the order of their importance, so when a cluster is full the least important are the ones left out
|
||||
uint found[MAX_LIGHTS_PER_CLUSTER];
|
||||
uint found_count = 0U;
|
||||
uint dropped = 0U;
|
||||
|
||||
uint lights_end = min(lights_total, uint(lights.length()));
|
||||
for (uint i = 1U; i < lights_end; ++i)
|
||||
{
|
||||
float range = light_range(lights[i]);
|
||||
if (range <= 0.0 && lights[i].type != LIGHT_DIR)
|
||||
continue;
|
||||
if (!light_reaches_box(lights[i], range, lower, upper))
|
||||
continue;
|
||||
|
||||
if (found_count < uint(MAX_LIGHTS_PER_CLUSTER))
|
||||
found[found_count++] = i;
|
||||
else
|
||||
++dropped;
|
||||
}
|
||||
|
||||
uint offset = 0U;
|
||||
if (found_count > 0U)
|
||||
{
|
||||
offset = atomicAdd(cluster_index_count, found_count);
|
||||
for (uint k = 0U; k < found_count; ++k)
|
||||
cluster_light_indices[offset + k] = found[k];
|
||||
}
|
||||
if (dropped > 0U)
|
||||
atomicAdd(cluster_overflow_count, dropped);
|
||||
|
||||
cluster_ranges[index] = cluster_range_s(offset, found_count);
|
||||
}
|
||||
35
shaders/vk14/color.frag
Normal file
35
shaders/vk14/color.frag
Normal file
@@ -0,0 +1,35 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 13) in vec3 f_color;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#include "sky_shoulder.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 col = sky_shoulder(pow(max(f_color.rgb, vec3(0.0)), vec3(2.2)));
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = vec4(apply_fog(col), 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(vec4(apply_fog(col), 1.0f));
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,22 @@
|
||||
// what the shaders of the vulkan 1.4 renderer share: the bindings of rendering/vulkan14/vulkan14bindings.h and the settings.
|
||||
// what the shaders of the vulkan 1.4 renderer share: the bindings of rendering/vulkan14/vulkan14bindings.h, the constants of the
|
||||
// game, the settings, the push constants and the resources of the pipeline layout a shader is drawn with.
|
||||
// compiled by the game, the bindings come from the renderer (vk14::binding_preamble()) and are taken instead of these; the
|
||||
// SPIR-V binaries (shaders/vk14bin) are compiled with the values written here, so they have to stay the same as in the game,
|
||||
// which checks them when it starts (and doesn't load the binaries when they differ)
|
||||
//
|
||||
// the pipeline layout is picked by a define before the include of this file:
|
||||
// (none) the passes of the scene (materials, shadows, picking, lines, sprites, sky, particles): set PASS_SET with
|
||||
// the blocks of the scene, of the draws, of the instances, the lights, the clusters and the shadow,
|
||||
// environment and headlight maps; set MATERIAL_SET with the textures of the material
|
||||
// LAYOUT_POST the post-processing and the copies of whole images: set POST_SET pushed with the images read, the
|
||||
// parameters in the push constants (param stands for pc.data)
|
||||
// LAYOUT_CLUSTER the compute passes of the light clusters: set 0 pushed at the CL_* bindings
|
||||
// LAYOUT_UI the user interface: none of the above, its own push constants
|
||||
//
|
||||
// the locations of the inputs and outputs between the stages are the table of shaders/ogl46/common.glsl, the same for a name
|
||||
// in every shader: 0 f_normal, 1 f_normal_raw, 2 f_coord, 3 f_pos, 4 f_clip_pos, 5 f_clip_future_pos, 6 TangentFragPos,
|
||||
// 7 UserData, 8-10 f_tbn, 12 f_albedo, 13 f_color, 14 f_coords, 15 f_local, 16 f_soft, 17 f_streak (11 f_model_draw is not
|
||||
// used: the draw is in the push constants)
|
||||
#ifndef COMMON_GLSL
|
||||
#define COMMON_GLSL
|
||||
|
||||
@@ -15,18 +30,276 @@
|
||||
#define UI_TEXTURE 0
|
||||
#define PASS_SET 0
|
||||
#define MATERIAL_SET 1
|
||||
#define SCENE_UBO 0
|
||||
#define DRAW_SSBO 1
|
||||
#define INSTANCE_SSBO 2
|
||||
#define LIGHT_SSBO 3
|
||||
#define CLUSTER_UBO 4
|
||||
#define CLUSTER_RANGE_SSBO 5
|
||||
#define CLUSTER_INDEX_SSBO 6
|
||||
#define SHADOW_TEX 7
|
||||
#define ENV_TEX 8
|
||||
#define HEADLIGHT_TEX 9
|
||||
#define PASS_BINDING_COUNT 10
|
||||
#define MATERIAL_TEXTURE_FIRST 0
|
||||
#define CL_CLUSTER_UBO 0
|
||||
#define CL_AABB_SSBO 1
|
||||
#define CL_RANGE_SSBO 2
|
||||
#define CL_INDEX_SSBO 3
|
||||
#define CL_COUNTER_SSBO 4
|
||||
#define CL_LIGHT_SSBO 5
|
||||
#define POST_SET 0
|
||||
#define POST_TEXTURE_FIRST 0
|
||||
#define PUSH_CONSTANTS_SIZE 112
|
||||
#define SPEC_OUTPUT_GAMMA 0
|
||||
#define SPEC_SHADOWMAP_ENABLED 1
|
||||
#define SPEC_ENVMAP_ENABLED 2
|
||||
#define SPEC_MOTIONBLUR_ENABLED 3
|
||||
#define SPEC_POSTFX_ENABLED 4
|
||||
#define SPEC_EXTRAEFFECTS_ENABLED 5
|
||||
#define SPEC_COUNT 6
|
||||
#define MAX_LIGHTS 1024U
|
||||
#define MAX_CASCADES 3U
|
||||
#define MAX_PARAMS 3U
|
||||
#define MAX_HEADLIGHT_SHADOWS 4U
|
||||
#define CLUSTER_GRID_X 16
|
||||
#define CLUSTER_GRID_Y 9
|
||||
#define CLUSTER_GRID_Z 24
|
||||
#define CLUSTER_COUNT 3456
|
||||
#define MAX_LIGHTS_PER_CLUSTER 64
|
||||
#define CLUSTER_BUILD_GROUP_SIZE 64
|
||||
#define CLUSTER_CULL_GROUP_SIZE 64
|
||||
#endif
|
||||
|
||||
// whether the colour written to the window is encoded by the shader (FBOUT): the window has no srgb format (gfx.shadergamma yes).
|
||||
// a specialization constant, set by the renderer when it makes the pipeline
|
||||
layout(constant_id = SPEC_OUTPUT_GAMMA) const bool OUTPUT_GAMMA = false;
|
||||
#include "settings.glsl"
|
||||
|
||||
// the colour written to the window
|
||||
vec4 FBOUT(vec4 x)
|
||||
#ifndef LAYOUT_UI
|
||||
// vk14::draw_constants, PUSH_CONSTANTS_SIZE bytes, visible in every stage. draw: the element of model_draws[] the draw reads
|
||||
// (the passes of the scene); flags and data: the parameters of the special shaders, each says what it takes from them
|
||||
layout(push_constant) uniform draw_constants_block
|
||||
{
|
||||
return (OUTPUT_GAMMA ? vec4(pow(x.rgb, vec3(1.0 / 2.2)), x.a) : x);
|
||||
uint draw;
|
||||
uint flags;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
vec4 data[6];
|
||||
} pc;
|
||||
#endif
|
||||
|
||||
const uint LIGHT_SPOT = 0U;
|
||||
const uint LIGHT_POINT = 1U;
|
||||
const uint LIGHT_DIR = 2U;
|
||||
const uint LIGHT_HEADLIGHTS = 3U;
|
||||
|
||||
// gl::light_element
|
||||
struct light_s
|
||||
{
|
||||
vec3 pos;
|
||||
uint type;
|
||||
|
||||
vec3 dir;
|
||||
float in_cutoff;
|
||||
|
||||
vec3 color;
|
||||
float out_cutoff;
|
||||
|
||||
float linear;
|
||||
float quadratic;
|
||||
|
||||
float intensity;
|
||||
float ambient;
|
||||
|
||||
mat4 headlight_projection;
|
||||
vec4 headlight_weights;
|
||||
};
|
||||
|
||||
// light clusters, see gl/cluster_grid.h
|
||||
struct cluster_aabb_s
|
||||
{
|
||||
vec4 lower;
|
||||
vec4 upper;
|
||||
};
|
||||
|
||||
struct cluster_range_s
|
||||
{
|
||||
uint offset;
|
||||
uint count;
|
||||
};
|
||||
|
||||
#if defined(LAYOUT_CLUSTER)
|
||||
|
||||
layout(std140, set = 0, binding = CL_CLUSTER_UBO) uniform cluster_ubo
|
||||
{
|
||||
mat4 cluster_projection;
|
||||
float cluster_znear;
|
||||
float cluster_zfar;
|
||||
float cluster_slice_scale;
|
||||
float cluster_slice_bias;
|
||||
vec2 cluster_viewport_size;
|
||||
uint cluster_valid;
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = CL_AABB_SSBO) buffer cluster_aabb_ssbo
|
||||
{
|
||||
cluster_aabb_s cluster_aabbs[];
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = CL_RANGE_SSBO) buffer cluster_range_ssbo
|
||||
{
|
||||
cluster_range_s cluster_ranges[];
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = CL_INDEX_SSBO) buffer cluster_index_ssbo
|
||||
{
|
||||
uint cluster_light_indices[];
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = CL_COUNTER_SSBO) buffer cluster_counter_ssbo
|
||||
{
|
||||
uint cluster_index_count;
|
||||
uint cluster_overflow_count;
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = CL_LIGHT_SSBO) readonly buffer light_ssbo
|
||||
{
|
||||
vec3 ambient;
|
||||
// lights stored in lights[], what the cluster culling goes through
|
||||
uint lights_total;
|
||||
|
||||
vec3 fog_color;
|
||||
// lights the shading loop goes through when the lights are not clustered
|
||||
uint lights_count;
|
||||
// 1 if the shading takes the lights from the list of the cluster of a fragment, see cluster_ubo
|
||||
uint lights_clustered;
|
||||
// not used by the shaders of this renderer yet (the sky ambient light and the prefiltered environment map come later)
|
||||
float sky_ambient_factor;
|
||||
uint env_ggx;
|
||||
|
||||
light_s lights[];
|
||||
};
|
||||
|
||||
#elif defined(LAYOUT_POST)
|
||||
|
||||
// the post-processing reads the parameters the opengl renderer gives it in the model block from the push constants
|
||||
#define param pc.data
|
||||
|
||||
#elif !defined(LAYOUT_UI)
|
||||
|
||||
// the block of the scene, gl::scene_ubs (every member kept, the layout is the one of the c++ struct)
|
||||
layout(std140, set = PASS_SET, binding = SCENE_UBO) uniform scene_ubo
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 inv_view;
|
||||
mat4 lightview[MAX_CASCADES];
|
||||
vec3 cascade_end;
|
||||
float time;
|
||||
vec4 rain_params;
|
||||
vec4 wiper_pos;
|
||||
vec4 wiper_timer_out;
|
||||
vec4 wiper_timer_return;
|
||||
mat4 headlight_shadow[MAX_HEADLIGHT_SHADOWS];
|
||||
vec4 headlight_shadow_params[MAX_HEADLIGHT_SHADOWS];
|
||||
vec4 contact_shadow_params;
|
||||
vec4 contact_shadow_lamps;
|
||||
vec4 shadow_fit;
|
||||
};
|
||||
|
||||
// the model block of a draw, gl::model_ubs (256 bytes), the one of the draw picked by pc.draw. the names below stand for its
|
||||
// fields, so the shaders read them as the uniform block of the opengl renderer
|
||||
struct model_draw_s
|
||||
{
|
||||
mat4 modelview;
|
||||
mat3 modelviewnormal;
|
||||
vec4 param[MAX_PARAMS];
|
||||
|
||||
mat4 future;
|
||||
float opacity;
|
||||
float emission;
|
||||
float fog_density;
|
||||
float alpha_mult;
|
||||
float shadow_tone;
|
||||
};
|
||||
|
||||
layout(std430, set = PASS_SET, binding = DRAW_SSBO) readonly buffer model_draw_ssbo
|
||||
{
|
||||
model_draw_s model_draws[];
|
||||
};
|
||||
|
||||
#define modelview model_draws[pc.draw].modelview
|
||||
#define modelviewnormal model_draws[pc.draw].modelviewnormal
|
||||
#define param model_draws[pc.draw].param
|
||||
#define future model_draws[pc.draw].future
|
||||
#define opacity model_draws[pc.draw].opacity
|
||||
#define emission model_draws[pc.draw].emission
|
||||
#define fog_density model_draws[pc.draw].fog_density
|
||||
#define alpha_mult model_draws[pc.draw].alpha_mult
|
||||
#define shadow_tone model_draws[pc.draw].shadow_tone
|
||||
|
||||
// per-instance modelview matrices of the instanced draws, element 0 the identity. a draw which isn't instanced is drawn with
|
||||
// firstInstance 0, an instanced one from its base instance on, with modelview set to the submodel-local chain (computed from
|
||||
// the identity, not from the camera or the instance), so the product is the final transform per instance per submodel
|
||||
layout(std430, set = PASS_SET, binding = INSTANCE_SSBO) readonly buffer instance_ssbo
|
||||
{
|
||||
mat4 instance_modelview[];
|
||||
};
|
||||
|
||||
// the light buffer of the pass, gl::light_data
|
||||
layout(std430, set = PASS_SET, binding = LIGHT_SSBO) readonly buffer light_ssbo
|
||||
{
|
||||
vec3 ambient;
|
||||
// lights stored in lights[], what the cluster culling goes through
|
||||
uint lights_total;
|
||||
|
||||
vec3 fog_color;
|
||||
// lights the shading loop goes through when the lights are not clustered
|
||||
uint lights_count;
|
||||
// 1 if the shading takes the lights from the list of the cluster of a fragment, see cluster_ubo
|
||||
uint lights_clustered;
|
||||
// not used by the shaders of this renderer yet (the sky ambient light and the prefiltered environment map come later)
|
||||
float sky_ambient_factor;
|
||||
uint env_ggx;
|
||||
|
||||
light_s lights[];
|
||||
};
|
||||
|
||||
// the grid of the light clusters of the pass, gl::cluster_ubs; the compute passes fill the lists, shading only reads them
|
||||
layout(std140, set = PASS_SET, binding = CLUSTER_UBO) uniform cluster_ubo
|
||||
{
|
||||
mat4 cluster_projection;
|
||||
float cluster_znear;
|
||||
float cluster_zfar;
|
||||
float cluster_slice_scale;
|
||||
float cluster_slice_bias;
|
||||
vec2 cluster_viewport_size;
|
||||
uint cluster_valid;
|
||||
};
|
||||
|
||||
layout(std430, set = PASS_SET, binding = CLUSTER_RANGE_SSBO) readonly buffer cluster_range_ssbo
|
||||
{
|
||||
cluster_range_s cluster_ranges[];
|
||||
};
|
||||
|
||||
layout(std430, set = PASS_SET, binding = CLUSTER_INDEX_SSBO) readonly buffer cluster_index_ssbo
|
||||
{
|
||||
uint cluster_light_indices[];
|
||||
};
|
||||
|
||||
#ifdef STAGE_VERTEX
|
||||
mat4 effective_modelview()
|
||||
{
|
||||
return instance_modelview[gl_InstanceIndex] * modelview;
|
||||
}
|
||||
|
||||
mat3 effective_modelviewnormal()
|
||||
{
|
||||
// instance_modelview can include per-instance scale (uniform or per-axis), so its upper 3x3 is not its own inverse
|
||||
// transpose in the non-uniform case
|
||||
mat3 instance_mv3 = mat3(instance_modelview[gl_InstanceIndex]);
|
||||
mat3 instance_normal = transpose(inverse(instance_mv3));
|
||||
return instance_normal * modelviewnormal;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
108
shaders/vk14/conversion.glsl
Normal file
108
shaders/vk14/conversion.glsl
Normal file
@@ -0,0 +1,108 @@
|
||||
vec3 rgb2yuv(vec3 val)
|
||||
{
|
||||
return
|
||||
clamp(
|
||||
vec3(
|
||||
(0.257 * val.r) + (0.504 * val.g) + (0.098 * val.b) + 0.0625,
|
||||
-(0.148 * val.r) - (0.291 * val.g) + (0.439 * val.b) + 0.5,
|
||||
(0.439 * val.r) - (0.368 * val.g) - (0.071 * val.b) + 0.5 ),
|
||||
0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 yuv2rgb(vec3 val)
|
||||
{
|
||||
val -= vec3(0.0625, 0.5, 0.5);
|
||||
return
|
||||
clamp(
|
||||
vec3(
|
||||
(1.164 * val.r) + (1.596 * val.b),
|
||||
(1.164 * val.r) - (0.391 * val.g) - (0.813 * val.b),
|
||||
(1.164 * val.r) + (2.018 * val.g) ),
|
||||
0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 rgb2hsl( vec3 col )
|
||||
{
|
||||
float red = col.r;
|
||||
float green = col.g;
|
||||
float blue = col.b;
|
||||
|
||||
float minc = min( col.r, min( col.g, col.b ));
|
||||
float maxc = max( col.r, max( col.g, col.b ));
|
||||
float delta = maxc - minc;
|
||||
|
||||
float lum = (minc + maxc) * 0.5;
|
||||
float sat = 0.0;
|
||||
float hue = 0.0;
|
||||
|
||||
if (lum > 0.0 && lum < 1.0) {
|
||||
float mul = (lum < 0.5) ? (lum) : (1.0-lum);
|
||||
sat = delta / (mul * 2.0);
|
||||
}
|
||||
|
||||
vec3 masks = vec3(
|
||||
(maxc == red && maxc != green) ? 1.0 : 0.0,
|
||||
(maxc == green && maxc != blue) ? 1.0 : 0.0,
|
||||
(maxc == blue && maxc != red) ? 1.0 : 0.0
|
||||
);
|
||||
|
||||
vec3 adds = vec3(
|
||||
((green - blue ) / delta),
|
||||
2.0 + ((blue - red ) / delta),
|
||||
4.0 + ((red - green) / delta)
|
||||
);
|
||||
|
||||
float deltaGtz = (delta > 0.0) ? 1.0 : 0.0;
|
||||
|
||||
hue += dot( adds, masks );
|
||||
hue *= deltaGtz;
|
||||
hue /= 6.0;
|
||||
|
||||
if (hue < 0.0)
|
||||
hue += 1.0;
|
||||
|
||||
return vec3( hue, sat, lum );
|
||||
}
|
||||
|
||||
vec3 hsl2rgb( vec3 col )
|
||||
{
|
||||
const float onethird = 1.0 / 3.0;
|
||||
const float twothird = 2.0 / 3.0;
|
||||
const float rcpsixth = 6.0;
|
||||
|
||||
float hue = col.x;
|
||||
float sat = col.y;
|
||||
float lum = col.z;
|
||||
|
||||
vec3 xt = vec3(
|
||||
rcpsixth * (hue - twothird),
|
||||
0.0,
|
||||
rcpsixth * (1.0 - hue)
|
||||
);
|
||||
|
||||
if (hue < twothird) {
|
||||
xt.r = 0.0;
|
||||
xt.g = rcpsixth * (twothird - hue);
|
||||
xt.b = rcpsixth * (hue - onethird);
|
||||
}
|
||||
|
||||
if (hue < onethird) {
|
||||
xt.r = rcpsixth * (onethird - hue);
|
||||
xt.g = rcpsixth * hue;
|
||||
xt.b = 0.0;
|
||||
}
|
||||
|
||||
xt = min( xt, 1.0 );
|
||||
|
||||
float sat2 = 2.0 * sat;
|
||||
float satinv = 1.0 - sat;
|
||||
float luminv = 1.0 - lum;
|
||||
float lum2m1 = (2.0 * lum) - 1.0;
|
||||
vec3 ct = (sat2 * xt) + satinv;
|
||||
|
||||
vec3 rgb;
|
||||
if (lum >= 0.5) { rgb = (luminv * ct) + lum2m1; }
|
||||
else { rgb = lum * ct; }
|
||||
|
||||
return rgb;
|
||||
}
|
||||
15
shaders/vk14/copy.frag
Normal file
15
shaders/vk14/copy.frag
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = texture(tex1, f_coords);
|
||||
}
|
||||
26
shaders/vk14/dither.glsl
Normal file
26
shaders/vk14/dither.glsl
Normal file
@@ -0,0 +1,26 @@
|
||||
// noise of half a step of the 8-bit output, added to the colour about to be written to the window: smooth dark gradients (the
|
||||
// fog at night) show no bands, which otherwise crawl over the image with the smallest change of the light. interleaved gradient
|
||||
// noise (Jimenez 2014) of the pixel, the same in every frame. the colour is the one written to the window: encoded already with
|
||||
// OUTPUT_GAMMA, linear otherwise (the sRGB framebuffer encodes it, so the step is taken in the encoded values)
|
||||
#ifndef DITHER_GLSL
|
||||
#define DITHER_GLSL
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
vec3 dither_output(vec3 color)
|
||||
{
|
||||
float noise = (fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715)))) - 0.5) / 255.0;
|
||||
if (OUTPUT_GAMMA)
|
||||
{
|
||||
return max(color + noise, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 linear = max(color, vec3(0.0));
|
||||
vec3 encoded = mix(linear * 12.92, 1.055 * pow(linear, vec3(1.0 / 2.4)) - 0.055, step(vec3(0.0031308), linear));
|
||||
encoded = max(encoded + noise, vec3(0.0));
|
||||
return mix(encoded / 12.92, pow((encoded + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), encoded));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
54
shaders/vk14/envmapping.glsl
Normal file
54
shaders/vk14/envmapping.glsl
Normal file
@@ -0,0 +1,54 @@
|
||||
layout(set = PASS_SET, binding = ENV_TEX) uniform samplerCube envmap;
|
||||
|
||||
vec3 envmap_color( vec3 normal )
|
||||
{
|
||||
vec3 envcolor;
|
||||
if (ENVMAP_ENABLED)
|
||||
{
|
||||
vec3 refvec = reflect(f_pos.xyz, normal); // view space
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space
|
||||
envcolor = texture(envmap, refvec).rgb;
|
||||
// Sanitize. The env cubemap can briefly contain NaN/Inf after a
|
||||
// reflection-pass regeneration (m_empty_cubemap was bound during the
|
||||
// face render and on NVIDIA can sample as undefined). Without this
|
||||
// guard, a single bad texel propagates through env_reflection in
|
||||
// apply_lights() and produces one-frame black flashes on glossy
|
||||
// (esp. specgloss) surfaces.
|
||||
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
|
||||
envcolor = max(envcolor, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
envcolor = vec3(0.5);
|
||||
}
|
||||
return envcolor;
|
||||
}
|
||||
|
||||
// Roughness-aware env map lookup — uses mip levels for blurry reflections.
|
||||
// lod 0.0 = mirror sharp, lod ~8.0 = fully diffuse blur.
|
||||
vec3 envmap_color_lod(vec3 fragnormal, float lod)
|
||||
{
|
||||
if (ENVMAP_ENABLED)
|
||||
{
|
||||
vec3 refvec = reflect(f_pos.xyz, fragnormal); // view space — matches envmap_color exactly
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space — was missing
|
||||
vec3 envcolor = textureLod(envmap, refvec, lod).rgb;
|
||||
// See envmap_color() above — same NaN/Inf sanitize, also needed here
|
||||
// because mipmap generation propagates a single NaN texel across the
|
||||
// whole mip chain.
|
||||
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
|
||||
return max(envcolor, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
return vec3(0.5); // was vec3(0.0), match the non-LOD fallback
|
||||
}
|
||||
}
|
||||
|
||||
// reflection of the surroundings in a surface of the given perceptual roughness (0 mirror, 1 matte): the plain map, its mipmaps
|
||||
// (box filtered) standing for the roughness. the prefiltered GGX map of the opengl renderer (env_ggx) comes in a later stage
|
||||
vec3 envmap_color_rough(vec3 fragnormal, float roughness)
|
||||
{
|
||||
const float MAX_REFLECTION_LOD = 8.0;
|
||||
return envmap_color_lod(fragnormal, roughness * MAX_REFLECTION_LOD);
|
||||
}
|
||||
38
shaders/vk14/freespot.frag
Normal file
38
shaders/vk14/freespot.frag
Normal file
@@ -0,0 +1,38 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#include "tonemapping.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = (gl_PointCoord.x - 0.5f) * 2.0f;
|
||||
float y = (gl_PointCoord.y - 0.5f) * 2.0f;
|
||||
float dist = sqrt(x * x + y * y);
|
||||
if (dist > 0.5f)
|
||||
discard;
|
||||
// vec4 color = vec4(param[0].rgb * emission, mix(param[0].a, 0.0f, dist * 2.0f));
|
||||
vec4 color = vec4(apply_fog(pow(param[0].rgb, vec3(2.2)) * emission), mix(param[0].a, 0.0f, dist * 2.0f));
|
||||
// vec4 color = vec4(add_fog(param[0].rgb * emission * (1.0 - get_fog_amount()), 1.0), mix(param[0].a, 0.0f, dist * 2.0f));
|
||||
out_color = FBOUT(color);
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
21
shaders/vk14/freespot.vert
Normal file
21
shaders/vk14/freespot.vert
Normal file
@@ -0,0 +1,21 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 1) in vec3 v_normal;
|
||||
layout(location = 2) in vec2 v_coord;
|
||||
|
||||
|
||||
layout(location = 3) out vec4 f_pos;
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
|
||||
void main()
|
||||
{
|
||||
f_pos = modelview * vec4(v_vert, 1.0);
|
||||
f_clip_pos = (projection * modelview) * vec4(v_vert, 1.0f);
|
||||
f_clip_future_pos = (projection * future * modelview) * vec4(v_vert, 1.0f);
|
||||
|
||||
gl_Position = f_clip_pos;
|
||||
gl_PointSize = param[1].x;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_UI 1
|
||||
// the user interface (imgui): the colour of the vertices times the texture of the draw (the font, an image, a texture), pushed
|
||||
// with the draw
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#define LAYOUT_UI 1
|
||||
// the user interface (imgui), drawn by vk14::imgui_backend over the image of the window
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
594
shaders/vk14/light_common.glsl
Normal file
594
shaders/vk14/light_common.glsl
Normal file
@@ -0,0 +1,594 @@
|
||||
// stage D of the vulkan renderer: the sun with its cascaded shadows, the environment map, the headlights and the lamps (in the
|
||||
// clusters or not). the cube shadow maps of the lamps, the shadow maps of the headlights, the contact shadows, the shadows of
|
||||
// the clouds, the directional sky ambient light and the prefiltered environment map of shaders/ogl46/light_common.glsl come
|
||||
// with the later stages; the code below is the one of the opengl renderer with those left out, it shades as that one does
|
||||
// with them off
|
||||
layout(set = PASS_SET, binding = SHADOW_TEX) uniform sampler2DArrayShadow shadowmap;
|
||||
layout(set = PASS_SET, binding = HEADLIGHT_TEX) uniform sampler2D headlightmap;
|
||||
|
||||
#include "envmapping.glsl"
|
||||
#include "conversion.glsl"
|
||||
|
||||
float glossiness = 1.0;
|
||||
float metalic = 0.0;
|
||||
layout(location = 7) in vec4 UserData;
|
||||
#pragma param(weathering, 2, 3, 1, zero)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Lighting balance tunables - tweak these to control overall scene
|
||||
// exposure without touching tonemapping.glsl.
|
||||
//
|
||||
// AMBIENT_SCALE: brightness of SHADED faces (indirect/sky term).
|
||||
// Lower -> deeper shadows, less burn under bright
|
||||
// textures. Higher -> flatter / brighter shading.
|
||||
//
|
||||
// SUN_DIFFUSE_SCALE: brightness of UNSHADED (sun-lit) faces. Lower
|
||||
// this to dim hot surfaces in direct sunlight
|
||||
// without affecting shaded areas. Was 3.5; 2.5
|
||||
// calmly fits the ACES tonemap shoulder.
|
||||
//
|
||||
// SUN_NDOTL_SHARPNESS: N.L curve on the sun. 1.0 = pure Lambert, higher
|
||||
// = sharper terminator (more contrast between
|
||||
// lit and shaded faces of the same surface).
|
||||
// ---------------------------------------------------------------------
|
||||
const float AMBIENT_SCALE = 0.65;
|
||||
const float SUN_DIFFUSE_SCALE = 1.5;
|
||||
const float SUN_NDOTL_SHARPNESS = 1.25;
|
||||
|
||||
float length2(vec3 v)
|
||||
{
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
// share of the sun (the moon) held back before the fragment by the shadow map, 0..1
|
||||
float calc_shadow_map();
|
||||
float calc_shadow()
|
||||
{
|
||||
return calc_shadow_map();
|
||||
}
|
||||
|
||||
float calc_shadow_map()
|
||||
{
|
||||
if (SHADOWMAP_ENABLED)
|
||||
{
|
||||
float distance = dot(f_pos.xyz, f_pos.xyz);
|
||||
uint cascade;
|
||||
for (cascade = 0U; cascade < MAX_CASCADES; cascade++)
|
||||
if (distance <= cascade_end[cascade])
|
||||
break;
|
||||
// cascades fitted to the depth of the view of an earlier frame (gfx.shadow.sdsm) cover only the area of the points seen
|
||||
// then: a point at the edge of the view or one which came closer since may be outside the map of its cascade. it goes to
|
||||
// the first further cascade whose map holds it with the reach of the filter, and is lit past the last one
|
||||
if (shadow_fit.x > 0.0)
|
||||
{
|
||||
float border = 4.0 / float(textureSize(shadowmap, 0).x);
|
||||
for (; cascade < MAX_CASCADES; cascade++)
|
||||
{
|
||||
vec2 fitcoords = (lightview[cascade] * vec4(f_pos.xyz, 1.0)).xy;
|
||||
if (all(greaterThanEqual(fitcoords, vec2(border))) && all(lessThanEqual(fitcoords, vec2(1.0 - border))))
|
||||
break;
|
||||
}
|
||||
if (cascade >= MAX_CASCADES)
|
||||
return 0.0;
|
||||
}
|
||||
// Normal offset: sample the shadow map from a point pushed along the surface normal
|
||||
// by a fraction of the cascade's texel footprint. A constant depth bias can't keep up
|
||||
// with large texels (far cascades, small shadow maps) on surfaces at a grazing angle
|
||||
// to the sun, which then show shadow acne stripes. lightview maps view space metres
|
||||
// to [0,1] shadow map coordinates, so its x/y row lengths give texels per metre.
|
||||
vec3 shadownormal = normalize(f_normal);
|
||||
float shadowNdotL = clamp(dot(shadownormal, normalize(-lights[0].dir)), 0.0, 1.0);
|
||||
mat4 shadowview = lightview[cascade];
|
||||
float texelspermetre = min(length(vec3(shadowview[0][0], shadowview[1][0], shadowview[2][0])),
|
||||
length(vec3(shadowview[0][1], shadowview[1][1], shadowview[2][1]))) * float(textureSize(shadowmap, 0).x);
|
||||
float texelsize = 1.0 / texelspermetre;
|
||||
vec3 shadowpos = f_pos.xyz + shadownormal * (texelsize * 1.5 * (1.0 - shadowNdotL));
|
||||
vec4 lightpos = shadowview * vec4(shadowpos, 1.0);
|
||||
vec3 coords = lightpos.xyz / lightpos.w;
|
||||
if (coords.z < 0.0)
|
||||
return 0.0f;
|
||||
|
||||
|
||||
|
||||
float bias = 0.00005f * float(cascade + 1U);
|
||||
|
||||
// Fast path: 4 textureGather() calls cover a 4x4 texel block. Raw gathered
|
||||
// comparisons must be weighted by the sub-texel position (bilinear weights),
|
||||
// otherwise the result is constant across each shadow map texel and shadow
|
||||
// edges turn into texel-sized stair steps. With per-axis weights
|
||||
// (1-f, 1, 1, f) this equals a 3x3 grid of hardware bilinear PCF taps.
|
||||
vec2 size = vec2(textureSize(shadowmap, 0).xy);
|
||||
vec2 uv = coords.xy * size - 0.5;
|
||||
vec2 base = floor(uv);
|
||||
vec2 f = uv - base;
|
||||
float refz = coords.z + bias;
|
||||
float layer = float(cascade);
|
||||
// sampling at texel corners selects blocks with lower-left texel base-1 and base+1
|
||||
vec4 g00 = textureGather(shadowmap, vec3((base + vec2(0.0, 0.0)) / size, layer), refz);
|
||||
vec4 g10 = textureGather(shadowmap, vec3((base + vec2(2.0, 0.0)) / size, layer), refz);
|
||||
vec4 g01 = textureGather(shadowmap, vec3((base + vec2(0.0, 2.0)) / size, layer), refz);
|
||||
vec4 g11 = textureGather(shadowmap, vec3((base + vec2(2.0, 2.0)) / size, layer), refz);
|
||||
// gather component order: x=(0,1) y=(1,1) z=(1,0) w=(0,0)
|
||||
float shadow = dot(g00, vec4(1.0 - f.x, 1.0, 1.0 - f.y, (1.0 - f.x) * (1.0 - f.y)))
|
||||
+ dot(g10, vec4(1.0, f.x, f.x * (1.0 - f.y), 1.0 - f.y))
|
||||
+ dot(g01, vec4((1.0 - f.x) * f.y, f.y, 1.0, 1.0 - f.x))
|
||||
+ dot(g11, vec4(f.y, f.x * f.y, f.x, 1.0));
|
||||
return shadow / 9.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// GGX Microfacet BRDF helpers (Cook-Torrance)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// Trowbridge-Reitz (GGX) Normal Distribution Function
|
||||
// D(N,H,α) = α⁴ / (π · ((NdotH)²·(α⁴−1)+1)²)
|
||||
// α = roughness² (perceptual remapping so the slider feels linear)
|
||||
float D_GGX(float NdotH, float roughness)
|
||||
{
|
||||
float a = roughness * roughness; // perceptual -> linear roughness
|
||||
float a2 = a * a;
|
||||
float d = (NdotH * NdotH) * (a2 - 1.0) + 1.0;
|
||||
return a2 / (3.14159265359 * d * d);
|
||||
}
|
||||
|
||||
// Schlick-GGX single-term masking/shadowing (k remapped for direct lighting)
|
||||
float G_SchlickGGX(float NdotX, float roughness)
|
||||
{
|
||||
float r = roughness + 1.0;
|
||||
float k = (r * r) * (1.0 / 8.0); // k_direct = (roughness+1)²/8
|
||||
return NdotX / (NdotX * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Separable Smith geometry term (not height-correlated)
|
||||
// G(N,V,L) = G_SchlickGGX(NdotV) · G_SchlickGGX(NdotL)
|
||||
float G_Smith(float NdotV, float NdotL, float roughness)
|
||||
{
|
||||
return G_SchlickGGX(NdotV, roughness) * G_SchlickGGX(NdotL, roughness);
|
||||
}
|
||||
|
||||
const float BRDF_PI = 3.14159265359;
|
||||
// the smoothest surface the lights are reflected by: a mirror would make a highlight smaller than a pixel
|
||||
const float MIN_ROUGHNESS = 0.045;
|
||||
// cap of the brdf for the lamps and headlights (see specular_light()): a tiny light next to a polished surface
|
||||
const float LOCAL_LIGHT_BRDF_LIMIT = 16.0;
|
||||
// no cap for the sun
|
||||
const float SUN_BRDF_LIMIT = 1.0e6;
|
||||
|
||||
// the specular reflectance at normal incidence and the perceptual roughness the lights are reflected with, worked out by
|
||||
// apply_lights() for the fragment before it adds the lamps (add_local_lights())
|
||||
vec3 spec_f0 = vec3(0.04);
|
||||
float spec_roughness = 0.5;
|
||||
|
||||
// a matte material (mat_default) reflects nothing: neither the surroundings nor the sun and the lamps, only the diffuse light
|
||||
bool matte = false;
|
||||
|
||||
// light of a light from Light_dir reflected by the surface, in the units of the diffuse shading (a white lambertian surface
|
||||
// facing the light reflects 1 of it): pi times the cook-torrance brdf with schlick's fresnel of F0, times n.l. Limit caps the
|
||||
// brdf without the n.l
|
||||
vec3 specular_light(vec3 light_dir, vec3 fragnormal, vec3 f0, float roughness, float limit)
|
||||
{
|
||||
float NdotL = dot(fragnormal, light_dir);
|
||||
if (NdotL <= 0.0)
|
||||
return vec3(0.0);
|
||||
vec3 V = normalize(-f_pos.xyz);
|
||||
vec3 H = normalize(light_dir + V);
|
||||
float NdotV = max(dot(fragnormal, V), 1e-4);
|
||||
float NdotH = max(dot(fragnormal, H), 0.0);
|
||||
float VdotH = max(dot(V, H), 0.0);
|
||||
float brdf = D_GGX(NdotH, roughness) * G_Smith(NdotV, NdotL, roughness) / (4.0 * NdotL * NdotV);
|
||||
vec3 fresnel = f0 + (1.0 - f0) * pow(1.0 - VdotH, 5.0);
|
||||
return fresnel * (min(brdf, limit) * NdotL * BRDF_PI);
|
||||
}
|
||||
|
||||
// perceptual roughness raised where the normal changes much from a pixel to the next (normal maps, tight curves), so the
|
||||
// highlight of a detail smaller than a pixel spreads over the pixel instead of flickering. Kaplanyan, Tokuyoshi, "Improved
|
||||
// geometric specular antialiasing", 2019
|
||||
float specular_antialiasing(vec3 fragnormal, float roughness)
|
||||
{
|
||||
vec3 dndx = dFdx(fragnormal);
|
||||
vec3 dndy = dFdy(fragnormal);
|
||||
float variance = 0.25 * (dot(dndx, dndx) + dot(dndy, dndy));
|
||||
float kernel = min(2.0 * variance, 0.18);
|
||||
float a2 = roughness * roughness * roughness * roughness;
|
||||
return sqrt(sqrt(clamp(a2 + kernel, 0.0, 1.0)));
|
||||
}
|
||||
|
||||
// the materials without a specgloss map give glossiness: (param[1].w, 10 by default) as the exponent of the blinn-phong
|
||||
// highlight of the old renderer. a lobe of exponent n is about a ggx one of alpha sqrt(2 / (n + 2))
|
||||
float phong_roughness(float exponent)
|
||||
{
|
||||
return sqrt(sqrt(2.0 / (max(exponent, 0.0) + 2.0)));
|
||||
}
|
||||
|
||||
// glossiness which apply_lights() turns into the roughness of a blinn-phong exponent (it takes 1 - glossiness / param[1].w)
|
||||
float phong_glossiness(float exponent)
|
||||
{
|
||||
return (1.0 - phong_roughness(exponent)) * max(abs(param[1].w), 1.0);
|
||||
}
|
||||
|
||||
// the reflection of the surroundings of a material without a specgloss map is a smooth varnish over the rough surface: the
|
||||
// old renderer showed it as a sharp reflection, as much of it as reflection: (and the masks of the material) asked for
|
||||
bool legacy_coat = false;
|
||||
|
||||
// set up the gloss of a material without a specgloss map, before apply_lights()
|
||||
void legacy_gloss()
|
||||
{
|
||||
glossiness = phong_glossiness(abs(param[1].w));
|
||||
legacy_coat = true;
|
||||
}
|
||||
|
||||
// Returns vec2(diffuse, specular) for a single punctual light.
|
||||
//
|
||||
// diffuse – Lambert N·L (Fresnel-weighted diffuse is handled per-material
|
||||
// in apply_lights, so we return raw N·L here).
|
||||
// specular – Cook-Torrance GGX: D·G / (4·NdotL·NdotV).
|
||||
// The Fresnel factor (F) is intentionally omitted here;
|
||||
// apply_lights already carries a per-material Fresnel term
|
||||
// that is applied to env reflections and can be routed to
|
||||
// direct specular there.
|
||||
//
|
||||
// Roughness is derived identically to env_roughness in apply_lights so
|
||||
// that direct and indirect specular highlights read consistently.
|
||||
vec2 calc_light(vec3 light_dir, vec3 fragnormal)
|
||||
{
|
||||
vec3 N = fragnormal;
|
||||
vec3 L = light_dir;
|
||||
vec3 V = normalize(-f_pos.xyz);
|
||||
vec3 H = normalize(L + V);
|
||||
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float NdotV = max(dot(N, V), 1e-4);
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
|
||||
float diffuse_v = NdotL;
|
||||
|
||||
// Mirror the env-map roughness derivation so direct and indirect lobes match.
|
||||
// glossiness == param[1].w → roughness == 0.04 (near-mirror)
|
||||
// glossiness == 0 → roughness == 1.0 (fully diffuse)
|
||||
float roughness = clamp(1.0 - glossiness / max(abs(param[1].w), 1.0), 0.04, 1.0);
|
||||
|
||||
// Cook-Torrance specular (no Fresnel — see above):
|
||||
// f_spec = D(N,H,α) · G(N,V,L,α) / (4 · NdotL · NdotV)
|
||||
float D = D_GGX(NdotH, roughness);
|
||||
float G = G_Smith(NdotV, NdotL, roughness);
|
||||
float specular_v = (NdotL > 0.0)
|
||||
? (D * G) / max(4.0 * NdotL * NdotV, 1e-4)
|
||||
: 0.0;
|
||||
|
||||
return vec2(diffuse_v, specular_v);
|
||||
}
|
||||
|
||||
vec2 calc_point_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 light_dir = normalize(light.pos - f_pos.xyz);
|
||||
vec2 val = calc_light(light_dir, fragnormal);
|
||||
val.x += light.ambient;
|
||||
val *= light.intensity;
|
||||
|
||||
float distance = length(light.pos - f_pos.xyz);
|
||||
float atten = 1.0f / (distance * distance);
|
||||
//float atten = 1.0f / (1.0f + light.linear * distance + light.quadratic * (distance * distance));
|
||||
|
||||
return val * atten;
|
||||
}
|
||||
|
||||
vec2 calc_spot_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 light_dir = normalize(light.pos - f_pos.xyz);
|
||||
|
||||
float theta = dot(light_dir, normalize(-light.dir));
|
||||
float epsilon = light.in_cutoff - light.out_cutoff;
|
||||
float intensity = clamp((theta - light.out_cutoff) / epsilon, 0.0, 1.0);
|
||||
|
||||
vec2 point = calc_point_light(light, fragnormal);
|
||||
return point * intensity;
|
||||
}
|
||||
|
||||
vec2 calc_dir_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 light_dir = normalize(-light.dir);
|
||||
return calc_light(light_dir, fragnormal);
|
||||
}
|
||||
|
||||
// dpdx and dpdy are the change of the view space position of the fragment from a pixel to the next one. the beam texture is
|
||||
// sampled with gradients worked out from them instead of the ones of the hardware: those are undefined where the fragments of
|
||||
// a quad do not go through the same lights, which is what happens at the edges of the clusters
|
||||
vec2 calc_headlights(light_s light, vec3 fragnormal, vec3 dpdx, vec3 dpdy)
|
||||
{
|
||||
vec4 headlightpos = light.headlight_projection * f_pos;
|
||||
vec3 coords = headlightpos.xyz / headlightpos.w;
|
||||
|
||||
if (coords.z > 1.0)
|
||||
return vec2(0.0);
|
||||
if (coords.z < 0.0)
|
||||
return vec2(0.0);
|
||||
// the beam is what the projection takes in. beyond its sides the texture would be clamped to its edge, and near the projector
|
||||
// (where a pixel spans many texels) its coarsest levels spill the average of the whole beam over the surroundings
|
||||
if (coords.x < 0.0 || coords.x > 1.0 || coords.y < 0.0 || coords.y > 1.0)
|
||||
return vec2(0.0);
|
||||
|
||||
vec3 light_dir = normalize(light.pos - f_pos.xyz);
|
||||
// Tighter wrap (was +0.25): faces angled away from the headlight cone
|
||||
// fall off to dark much faster, so cab/exterior surfaces read with a
|
||||
// clear directional shape instead of a flat half-lit wash.
|
||||
vec2 part = vec2(1.0) * clamp(dot(fragnormal, light_dir) + 0.10, 0.0, 1.0);
|
||||
float distance = length(light.pos - f_pos.xyz);
|
||||
float atten = 1.0f / (1.0f + light.linear * distance + light.quadratic * (distance * distance));
|
||||
atten *= mix(1.0, 0.0, clamp((coords.z - 0.998) * 500.0, 0.0, 1.0));
|
||||
vec2 beamcoords = headlightpos.xy / headlightpos.w;
|
||||
vec4 dheadlightposdx = light.headlight_projection * vec4(dpdx, 0.0);
|
||||
vec4 dheadlightposdy = light.headlight_projection * vec4(dpdy, 0.0);
|
||||
vec2 dbeamdx = (dheadlightposdx.xy - beamcoords * dheadlightposdx.w) / headlightpos.w;
|
||||
vec2 dbeamdy = (dheadlightposdy.xy - beamcoords * dheadlightposdy.w) / headlightpos.w;
|
||||
vec3 lights = textureGrad(headlightmap, beamcoords, dbeamdx, dbeamdy).rgb * light.headlight_weights.rgb;
|
||||
float lightintensity = max(max(lights.r, lights.g), lights.b);
|
||||
// x the diffuse light, y the light the specular reflection takes (specular_light() brings its own n.l)
|
||||
return vec2(part.x, 1.0) * (atten * lightintensity);
|
||||
}
|
||||
|
||||
// closest distance (squared) the light of a lamp is computed for: nearer the inverse square would grow without a limit
|
||||
const float LOCAL_LIGHT_MIN_DISTANCE2 = 0.01;
|
||||
|
||||
// light of a lamp (point or spot light: the lamps of models and vehicles, the cab lamp) reaching the fragment, x the diffuse
|
||||
// part and y the light the specular reflection takes (specular_light() brings its own n.l). it falls off with the square of the distance, and smoothly to nothing at the range of the light,
|
||||
// 1 / sqrt(quadratic), past which the culling of the clusters does not take it. a spot light fades from its inner cone to the
|
||||
// outer one
|
||||
vec2 calc_local_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 tolight = light.pos - f_pos.xyz;
|
||||
float distance2 = dot(tolight, tolight);
|
||||
vec3 light_dir = tolight * inversesqrt(max(distance2, 1e-8));
|
||||
float edge = clamp(1.0 - distance2 * distance2 * (light.quadratic * light.quadratic), 0.0, 1.0);
|
||||
float atten = edge * edge / max(distance2, LOCAL_LIGHT_MIN_DISTANCE2);
|
||||
if (light.type == LIGHT_SPOT)
|
||||
atten *= smoothstep(light.out_cutoff, light.in_cutoff, dot(-light_dir, normalize(light.dir)));
|
||||
if (atten <= 0.0)
|
||||
return vec2(0.0);
|
||||
return vec2(max(dot(fragnormal, light_dir), 0.0), 1.0) * atten;
|
||||
}
|
||||
|
||||
// index of the light cluster the fragment is in, see gl/cluster_grid.h: its screen tile and slice of depth. the depth is the clip
|
||||
// space w, which is 1 / gl_FragCoord.w. a tile is kept in the grid, as the far edge of the render target and rounding
|
||||
// right below it give one past the last
|
||||
uint fragment_cluster()
|
||||
{
|
||||
ivec2 tile = ivec2(gl_FragCoord.xy * (vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y) / cluster_viewport_size));
|
||||
tile = clamp(tile, ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1));
|
||||
int slice = clamp(int(floor(cluster_slice_bias - log(gl_FragCoord.w) * cluster_slice_scale)), 0, CLUSTER_GRID_Z - 1);
|
||||
return uint(tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice));
|
||||
}
|
||||
|
||||
// adds the light of a headlight or lamp to the diffuse and the specular light of the fragment
|
||||
void add_local_light(light_s light, vec2 part, vec3 fragnormal, inout vec3 diffuse, inout vec3 specular)
|
||||
{
|
||||
if (part.y <= 0.0)
|
||||
return;
|
||||
vec3 color = light.color * light.intensity;
|
||||
diffuse += color * part.x;
|
||||
if (matte)
|
||||
return;
|
||||
specular += color * (part.y * param[1].y) * specular_light(normalize(light.pos - f_pos.xyz), fragnormal, spec_f0, spec_roughness, LOCAL_LIGHT_BRDF_LIMIT);
|
||||
}
|
||||
|
||||
// adds the light of the headlights and lamps which reach the fragment to its diffuse and specular light. these are the ones in
|
||||
// the list of its cluster when the lights are clustered (the sun is not on the list, the callers take care of it), and the
|
||||
// first lights_count - 1 of lights[] otherwise, which are headlights only. the specular light is reflected with spec_f0 and
|
||||
// spec_roughness, and is already coloured by the fresnel term
|
||||
void add_local_lights(vec3 fragnormal, inout vec3 diffuse, inout vec3 specular)
|
||||
{
|
||||
// worked out before the loops, which the fragments of a quad do not leave together
|
||||
vec3 dpdx = dFdx(f_pos.xyz);
|
||||
vec3 dpdy = dFdy(f_pos.xyz);
|
||||
|
||||
if (lights_clustered != 0U)
|
||||
{
|
||||
cluster_range_s range = cluster_ranges[fragment_cluster()];
|
||||
for (uint i = 0U; i < range.count; i++)
|
||||
{
|
||||
light_s light = lights[cluster_light_indices[range.offset + i]];
|
||||
vec2 part = (light.type == LIGHT_HEADLIGHTS) ? calc_headlights(light, fragnormal, dpdx, dpdy) : calc_local_light(light, fragnormal);
|
||||
add_local_light(light, part, fragnormal, diffuse, specular);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint i = 1U; i < lights_count; i++)
|
||||
{
|
||||
light_s light = lights[i];
|
||||
add_local_light(light, calc_headlights(light, fragnormal, dpdx, dpdy), fragnormal, diffuse, specular);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the light of the headlights and lamps added to Fragcolor, diffuse and specular together (for shaders which don't tell
|
||||
// the albedo from the reflection)
|
||||
vec3 add_headlights(vec3 fragcolor, vec3 fragnormal)
|
||||
{
|
||||
vec3 diffuse = vec3(0.0);
|
||||
vec3 specular = vec3(0.0);
|
||||
add_local_lights(fragnormal, diffuse, specular);
|
||||
return fragcolor + diffuse + specular;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Split-sum environment BRDF (Karis / UE4 analytic approximation).
|
||||
//
|
||||
// This is the missing piece that made matte specgloss surfaces "shine like
|
||||
// crazy": previously the env reflection was added at full strength
|
||||
// (envcolor * fresnel * reflectivity) and roughness only blurred the mip,
|
||||
// never dimmed the energy. A rough surface therefore mirrored the bright
|
||||
// sky just as strongly as a polished one.
|
||||
//
|
||||
// EnvBRDFApprox returns the pre-integrated specular scale (the "DFG" term)
|
||||
// for a given F0, roughness and view angle. For rough surfaces it collapses
|
||||
// toward ~0, so low-glossiness materials reflect almost nothing — matching
|
||||
// what Substance 3D Painter shows. Polished surfaces keep their full
|
||||
// reflection, and the grazing-angle Fresnel edge is preserved.
|
||||
// roughness 1.0 (matte) -> scale ~0.015 (virtually no reflection)
|
||||
// roughness 0.0 (mirror) -> scale ~F0..1 (full reflection + Fresnel rim)
|
||||
vec3 EnvBRDFApprox(vec3 F0, float roughness, float NoV)
|
||||
{
|
||||
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
||||
const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040);
|
||||
vec4 r = roughness * c0 + c1;
|
||||
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
return F0 * AB.x + AB.y;
|
||||
}
|
||||
|
||||
// Diffuse colour of the submodel (t3d/e3d) or shape, used as the albedo multiplier:
|
||||
// untextured materials take it as their colour, textured ones multiply the base texture by it
|
||||
// (same as the legacy renderer's GL_MODULATE). param[0].rgb holds the authored sRGB colour,
|
||||
// param[1].x its intensity (HSV value unless a material overrides it).
|
||||
vec3 material_diffuse()
|
||||
{
|
||||
vec3 diffusecolor = pow(max(param[0].rgb, vec3(0.0)), vec3(2.2));
|
||||
float value = max(param[0].r, max(param[0].g, param[0].b));
|
||||
return (value > 0.0 ? diffusecolor * (param[1].x / value) : vec3(0.0));
|
||||
}
|
||||
|
||||
// lights the surface: the ambient light, the sun (the moon), the headlights and lamps and the reflection of the surroundings.
|
||||
// the units are the ones of the ambient light, the irradiance over pi: a white lambertian surface lit by 1 of it shows 1.
|
||||
// Texturecolor is the albedo (the base colour of a metal), Specularity the specular level of a dielectric (0.5 is the
|
||||
// reflectance of 4% at normal incidence), Reflectivity how much of the reflected surroundings is the cubemap rather than the
|
||||
// ambient light, Shadowtone the share of the sun left in its shadow (1 for the materials which take no shadows).
|
||||
// a Sunless surface (the interior of a cab) gets no light of the sun, its shadow only darkens the ambient light
|
||||
vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone, bool sunless)
|
||||
{
|
||||
// Diffuse colour is applied once, to the albedo; the light terms below are not scaled by it again
|
||||
texturecolor *= material_diffuse();
|
||||
float polished = 0.0;
|
||||
if (sunless)
|
||||
{
|
||||
// Cab interior: dim ambient less than the exterior path - ambient
|
||||
// is the dominant indoor illumination, but it was still too bright.
|
||||
fragcolor *= 0.80;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef NO_WEATHERING
|
||||
float weathered = (param[2].w > 0.0 ? clamp(param[2].w * UserData.x, 0.0, 1.0) : 0.0);
|
||||
float tinted = (param[2].w < 0.0 ? clamp(-param[2].w * UserData.x, 0.0, 1.0) : 0.0);
|
||||
// a tint only scales the colour of the texture, the steel keeps its own polish
|
||||
polished = weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)));
|
||||
float weatheredluma = min(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 0.05);
|
||||
vec3 unused = texturecolor * (weatheredluma / max(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 1e-4));
|
||||
unused = mix(unused, vec3(1.0, 0.82, 0.74) * (weatheredluma / 0.8525), 0.5);
|
||||
texturecolor = mix(texturecolor, unused, smoothstep(0.0, 0.4, weathered));
|
||||
texturecolor = (tinted > 0.0 ? texturecolor * mix(vec3(1.0), param[2].rgb, tinted) : texturecolor);
|
||||
specularity = mix(specularity, min(specularity, 0.03), smoothstep(0.0, 0.4, polished));
|
||||
metalic = mix(metalic, 0.0, polished);
|
||||
glossiness = mix(glossiness, min(glossiness, 0.06 * abs(param[1].w)), polished);
|
||||
reflectivity = mix(reflectivity, min(reflectivity, 0.08 * param[1].z), smoothstep(0.0, 0.4, polished));
|
||||
#endif
|
||||
// Scale ambient before it gets tinted by the texture.
|
||||
// Sun, headlights and emission are added afterwards so they are NOT
|
||||
// attenuated by AMBIENT_SCALE - this only dims the indirect term.
|
||||
fragcolor *= AMBIENT_SCALE;
|
||||
}
|
||||
|
||||
vec3 emissioncolor = vec3(emission);
|
||||
vec3 view_dir = normalize(-f_pos.xyz);
|
||||
float NdotV = max(dot(fragnormal, view_dir), 1e-4);
|
||||
|
||||
// glossiness == param[1].w -> mirror, glossiness == 0 -> fully rough
|
||||
float roughness = clamp(1.0 - glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0);
|
||||
roughness = max(specular_antialiasing(fragnormal, roughness), MIN_ROUGHNESS);
|
||||
// metals reflect with their base colour, dielectrics with the specular level (0.5 -> 0.04)
|
||||
vec3 f0 = mix(vec3(0.08 * specularity), texturecolor, metalic);
|
||||
spec_f0 = f0;
|
||||
spec_roughness = roughness;
|
||||
|
||||
// Pre-integrated env BRDF: how much of the surroundings the surface reflects, for its F0, roughness and the view angle.
|
||||
// the surroundings are the cubemap where the material asks for it (reflectivity) and the ambient light elsewhere, so a
|
||||
// surface reflects them always, the rough one barely
|
||||
vec3 env_spec = EnvBRDFApprox(f0, roughness, NdotV);
|
||||
float env_roughness = roughness;
|
||||
vec3 cubemap_spec = env_spec;
|
||||
float cubemap_share = max(reflectivity, 0.0);
|
||||
float ambient_share = clamp(1.0 - reflectivity, 0.0, 1.0);
|
||||
if (matte)
|
||||
{
|
||||
env_spec = vec3(0.0);
|
||||
cubemap_share = 0.0;
|
||||
}
|
||||
// what the diffuse layer is left with
|
||||
vec3 diffuse_albedo = texturecolor * (1.0 - metalic) * (1.0 - env_spec);
|
||||
if (legacy_coat)
|
||||
{
|
||||
// the varnish: a smooth dielectric layer reflecting the cubemap, worn off where the material is weathered
|
||||
env_roughness = specular_antialiasing(fragnormal, roughness * smoothstep(0.0, 0.4, polished));
|
||||
cubemap_spec = EnvBRDFApprox(mix(vec3(0.04), texturecolor, metalic), env_roughness, NdotV);
|
||||
ambient_share = 1.0;
|
||||
diffuse_albedo *= 1.0 - cubemap_spec * min(cubemap_share, 1.0);
|
||||
}
|
||||
vec3 envcolor = (matte ? vec3(0.0) : envmap_color_rough(fragnormal, env_roughness));
|
||||
|
||||
if (lights_count == 0U)
|
||||
return fragcolor * diffuse_albedo
|
||||
+ emissioncolor * texturecolor
|
||||
+ fragcolor * env_spec * ambient_share
|
||||
+ envcolor * cubemap_spec * cubemap_share;
|
||||
|
||||
vec3 light_dir = normalize(-lights[0].dir);
|
||||
float NdotL = max(dot(fragnormal, light_dir), 0.0);
|
||||
// Sharpen sun N.L falloff so the lit-to-shaded terminator on cab
|
||||
// panels, vehicle bodies and terrain reads as a clear edge rather
|
||||
// than a soft Lambertian ramp. Tunable via SUN_NDOTL_SHARPNESS.
|
||||
float sun_NdotL = pow(NdotL, SUN_NDOTL_SHARPNESS);
|
||||
// the shadows of the clouds come with the weather (stage P2): nothing holds the sun back yet
|
||||
float cloudsun = 1.0;
|
||||
// Sun HDR scale -> SUN_DIFFUSE_SCALE, the same for the diffuse and the specular light
|
||||
vec3 sunlight = lights[0].color * (SUN_DIFFUSE_SCALE * lights[0].intensity * cloudsun);
|
||||
|
||||
float shadow = 0.0;
|
||||
if (shadowtone < 1.0)
|
||||
shadow = clamp(calc_shadow(), 0.0, 1.0);
|
||||
|
||||
// the cubemap knows nothing of what shades the fragment: it is dimmed a little in the shadow of the sun
|
||||
float cubemap_shadow = 1.0;
|
||||
if (sunless)
|
||||
{
|
||||
// the sun adds no light in the cab, where it would fall its shadow darkens the ambient light
|
||||
float diffuseamount = sun_NdotL * lights[0].intensity;
|
||||
float darkness = (shadowtone < 1.0 ? max(shadow, 1.0 - cloudsun) : 0.0);
|
||||
fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * darkness, 0.0, 1.0));
|
||||
}
|
||||
else
|
||||
cubemap_shadow = 1.0 - (1.0 - shadowtone) * shadow * 0.5;
|
||||
|
||||
// the reflection of the surroundings, before the light of the sun and the lamps joins the ambient light
|
||||
vec3 env_reflection = fragcolor * env_spec * ambient_share
|
||||
+ envcolor * cubemap_spec * (cubemap_share * cubemap_shadow);
|
||||
|
||||
// the shadow leaves the share of the sun Shadowtone tells (the old shadow colour of the scenery) to the diffuse light,
|
||||
// nothing to the highlight
|
||||
if (!sunless)
|
||||
fragcolor += sunlight * (sun_NdotL * (1.0 - (1.0 - shadowtone) * shadow));
|
||||
vec3 specular = matte ? vec3(0.0) : sunlight * specular_light(light_dir, fragnormal, f0, roughness, SUN_BRDF_LIMIT) * (1.0 - shadow);
|
||||
|
||||
vec3 lampspecular = vec3(0.0);
|
||||
add_local_lights(fragnormal, fragcolor, lampspecular);
|
||||
|
||||
return fragcolor * diffuse_albedo // ambient, sun and lamps on the diffuse layer
|
||||
+ specular * param[1].y // highlight of the sun, fresnel coloured (metals take their base colour)
|
||||
+ lampspecular // highlights of the lamps, param[1].y already applied
|
||||
+ emissioncolor * texturecolor
|
||||
+ env_reflection;
|
||||
}
|
||||
|
||||
vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
|
||||
{
|
||||
return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, false);
|
||||
}
|
||||
|
||||
// the interior of a cab: no light of the sun, its shadow darkens the ambient light
|
||||
vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
|
||||
{
|
||||
return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, true);
|
||||
}
|
||||
4
shaders/vk14/mat_colored.frag
Normal file
4
shaders/vk14/mat_colored.frag
Normal file
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "mat_colored.glsl"
|
||||
60
shaders/vk14/mat_colored.glsl
Normal file
60
shaders/vk14/mat_colored.glsl
Normal file
@@ -0,0 +1,60 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
// no texture: the diffuse colour alone makes the albedo, apply_lights() applies it to the white base
|
||||
vec4 tex_color = vec4(1.0, 1.0, 1.0, param[0].a);
|
||||
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = dot(pow(param[0].rgb, vec3(2.2)), vec3(0.5));
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
4
shaders/vk14/mat_default.frag
Normal file
4
shaders/vk14/mat_default.frag
Normal file
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "mat_default.glsl"
|
||||
69
shaders/vk14/mat_default.glsl
Normal file
69
shaders/vk14/mat_default.glsl
Normal file
@@ -0,0 +1,69 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
matte = true;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, 0.0, 0.0, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
/*
|
||||
float distance = dot(f_pos.xyz, f_pos.xyz);
|
||||
if( distance <= cascade_end.x ) { color.r += 0.25; }
|
||||
else if( distance <= cascade_end.y ) { color.g += 0.25; }
|
||||
else if( distance <= cascade_end.z ) { color.b += 0.25; }
|
||||
*/
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
4
shaders/vk14/mat_default_0.frag
Normal file
4
shaders/vk14/mat_default_0.frag
Normal file
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "mat_colored.glsl"
|
||||
4
shaders/vk14/mat_default_1.frag
Normal file
4
shaders/vk14/mat_default_1.frag
Normal file
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "mat_default.glsl"
|
||||
4
shaders/vk14/mat_default_2.frag
Normal file
4
shaders/vk14/mat_default_2.frag
Normal file
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "mat_reflmap.glsl"
|
||||
77
shaders/vk14/mat_default_detail.frag
Normal file
77
shaders/vk14/mat_default_detail.frag
Normal file
@@ -0,0 +1,77 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma texture(detailnormalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D detailnormalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(detailnormalmap, f_coord * param[2].x).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
77
shaders/vk14/mat_default_specgloss.frag
Normal file
77
shaders/vk14/mat_default_specgloss.frag
Normal file
@@ -0,0 +1,77 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(specgloss, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D specgloss;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
/*
|
||||
float distance = dot(f_pos.xyz, f_pos.xyz);
|
||||
if( distance <= cascade_end.x ) { color.r += 0.25; }
|
||||
else if( distance <= cascade_end.y ) { color.g += 0.25; }
|
||||
else if( distance <= cascade_end.z ) { color.b += 0.25; }
|
||||
*/
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
89
shaders/vk14/mat_detail_normalmap.frag
Normal file
89
shaders/vk14/mat_detail_normalmap.frag
Normal file
@@ -0,0 +1,89 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(detailnormalmap, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D detailnormalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec4 normal_map = texture(normalmap, f_coord);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord * param[2].x);
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
|
||||
|
||||
float reflblend = (normal_map.a + detailnormal_map.a);
|
||||
float reflectivity = reflblend > 1.0 ? param[1].z * 1.0 : param[1].z * reflblend;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
96
shaders/vk14/mat_detail_normalmap_specgloss.frag
Normal file
96
shaders/vk14/mat_detail_normalmap_specgloss.frag
Normal file
@@ -0,0 +1,96 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(detailnormalmap, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D detailnormalmap;
|
||||
|
||||
#pragma texture(specgloss, 3, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D specgloss;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec4 normal_map = texture(normalmap, f_coord);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord * param[2].x);
|
||||
vec4 specgloss_map = texture(specgloss, f_coord);
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
|
||||
float reflblend = (normal_map.a + detailnormal_map.a);
|
||||
float reflectivity = reflblend > 1.0 ? param[1].z * 1.0 : param[1].z * reflblend;
|
||||
float specularity = specgloss_map.r;
|
||||
glossiness = specgloss_map.g * abs(param[1].w);
|
||||
metalic = specgloss_map.b;
|
||||
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
152
shaders/vk14/mat_detail_parallax.frag
Normal file
152
shaders/vk14/mat_detail_parallax.frag
Normal file
@@ -0,0 +1,152 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
#pragma param(height_scale, 2, 2, 1, zero)
|
||||
#pragma param(height_offset, 2, 3, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(detailnormalmap, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D detailnormalmap;
|
||||
|
||||
#define PARALLAX
|
||||
// param[2].w is the height offset, not the weathering
|
||||
#define NO_WEATHERING
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(-TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
vec4 normal_map = texture(normalmap, f_coord_p);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord_p * param[2].x);
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
float reflectivity = param[1].z * normal_map.a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].z * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
158
shaders/vk14/mat_detail_parallax_specgloss.frag
Normal file
158
shaders/vk14/mat_detail_parallax_specgloss.frag
Normal file
@@ -0,0 +1,158 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
#pragma param(height_scale, 2, 2, 1, zero)
|
||||
#pragma param(height_offset, 2, 3, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D specgloss;
|
||||
|
||||
#pragma texture(detailnormalmap, 3, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D detailnormalmap;
|
||||
|
||||
|
||||
#define PARALLAX
|
||||
// param[2].w is the height offset, not the weathering
|
||||
#define NO_WEATHERING
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(vec3(0.0f, 0.0f, 0.0f) - TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
|
||||
vec4 normal_map = texture(normalmap, f_coord_p);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord_p * param[2].x);
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
vec4 specgloss_map = texture(specgloss, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
float reflectivity = param[1].z * normal_map.a;
|
||||
float specularity = specgloss_map.r;
|
||||
glossiness = specgloss_map.g * abs(param[1].w);
|
||||
metalic = specgloss_map.b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].z * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
29
shaders/vk14/mat_invalid.frag
Normal file
29
shaders/vk14/mat_invalid.frag
Normal file
@@ -0,0 +1,29 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = vec4(1.0, 0.0, 1.0, 1.0);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
76
shaders/vk14/mat_normalmap.frag
Normal file
76
shaders/vk14/mat_normalmap.frag
Normal file
@@ -0,0 +1,76 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
81
shaders/vk14/mat_normalmap_specgloss.frag
Normal file
81
shaders/vk14/mat_normalmap_specgloss.frag
Normal file
@@ -0,0 +1,81 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
137
shaders/vk14/mat_parallax.frag
Normal file
137
shaders/vk14/mat_parallax.frag
Normal file
@@ -0,0 +1,137 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(height_scale, 2, 1, 1, zero)
|
||||
#pragma param(height_offset, 2, 2, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#define PARALLAX
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(vec3(0.0f, 0.0f, 0.0f) - TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord_p).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord_p).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].y * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
142
shaders/vk14/mat_parallax_specgloss.frag
Normal file
142
shaders/vk14/mat_parallax_specgloss.frag
Normal file
@@ -0,0 +1,142 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(height_scale, 2, 1, 1, zero)
|
||||
#pragma param(height_offset, 2, 2, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define PARALLAX
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(vec3(0.0f, 0.0f, 0.0f) - TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord_p).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord_p).a;
|
||||
float specularity = texture(specgloss, f_coord_p).r;
|
||||
glossiness = texture(specgloss, f_coord_p).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord_p).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].y * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
216
shaders/vk14/mat_rain_windscreen.frag
Normal file
216
shaders/vk14/mat_rain_windscreen.frag
Normal file
@@ -0,0 +1,216 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
// Windshield rain effects
|
||||
// by @lcddisplay; wiper movement @MichauSto
|
||||
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
#pragma texture(raindropsatlas, 1, sRGB_A)
|
||||
#pragma texture(wipermask, 2, sRGB_A)
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(raindrop_grid_size, 2, 0, 1, one)
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
#include "random.glsl"
|
||||
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D raindropsatlas;
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D wipermask;
|
||||
|
||||
const float specular_intensity = 1.0;
|
||||
const float wobble_strength = 0.002;
|
||||
const float wobble_speed = 30.0;
|
||||
|
||||
vec4 getDropTex(float choice, vec2 uv) {
|
||||
vec2 offset;
|
||||
if (choice < 0.25) offset = vec2(0.0, 0.0);
|
||||
else if (choice < 0.5) offset = vec2(0.5, 0.0);
|
||||
else if (choice < 0.75) offset = vec2(0.0, 0.5);
|
||||
else offset = vec2(0.5, 0.5);
|
||||
return texture(raindropsatlas, offset + uv * 0.5);
|
||||
}
|
||||
|
||||
float GetMixFactor(in vec2 co, out float side);
|
||||
|
||||
void main() {
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
if (tex_color.a < 0.01) discard;
|
||||
|
||||
vec2 rainCoord = f_coord;
|
||||
float gridSize = ceil(param[2].x);
|
||||
|
||||
const float numDrops = 20000.0;
|
||||
const float cycleDuration = 4.0;
|
||||
|
||||
float squareMin = 0.5 / gridSize;
|
||||
float squareMax = 1.2 / gridSize;
|
||||
|
||||
vec2 cell = floor(rainCoord * gridSize);
|
||||
|
||||
vec3 dropLayer = vec3(0.0);
|
||||
float dropMaskSum = 0.0;
|
||||
|
||||
// Grid of 9 droplets in immediate neighbourhood
|
||||
for (int oy = -1; oy <= 1; oy++) {
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
|
||||
vec2 neighborCell = cell + vec2(ox, oy);
|
||||
vec2 neighborCenter = (neighborCell + .5) / gridSize;
|
||||
|
||||
float side;
|
||||
float mixFactor = GetMixFactor(neighborCenter, side);
|
||||
|
||||
uint seed = Hash(uvec3(neighborCell, side));
|
||||
|
||||
if(mixFactor < RandF(seed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show a percentage of droplets given by rain intensity param
|
||||
float activationSeed = RandF(seed);
|
||||
if (activationSeed > rain_params.x)
|
||||
continue; // kropla nieaktywna
|
||||
|
||||
// Randomly modulate droplet center & size
|
||||
vec2 dropCenter = (neighborCell + vec2(RandF(seed), RandF(seed))) / gridSize;
|
||||
float squareSize = mix(squareMin, squareMax, RandF(seed));
|
||||
|
||||
float lifeTime = time + RandF(seed) * cycleDuration;
|
||||
float phase = fract(lifeTime / cycleDuration);
|
||||
float actiwe = clamp(1.0 - phase, 0.0, 1.0);
|
||||
|
||||
// Gravity influence (TODO add vehicle speed & wind here!)
|
||||
float gravityStart = 0.5;
|
||||
float gravityPhase = smoothstep(gravityStart, 1.0, phase);
|
||||
float dropMass = mix(0.3, 1.2, RandF(seed));
|
||||
float gravitySpeed = 0.15 * dropMass;
|
||||
vec2 gravityOffset = vec2(0.0, gravityPhase * gravitySpeed * phase);
|
||||
|
||||
// Random wobble
|
||||
bool hasWobble = (RandF(seed) < 0.10);
|
||||
vec2 wobbleOffset = vec2(0.0);
|
||||
if (hasWobble && gravityPhase > 0.0) {
|
||||
float intensity = sin(time * wobble_speed + RandF(seed) * 100.) * wobble_strength * gravityPhase;
|
||||
wobbleOffset = vec2(intensity, 0.0);
|
||||
}
|
||||
|
||||
vec2 slideOffset = gravityOffset + wobbleOffset;
|
||||
|
||||
// Flatten droplets influenced by gravity
|
||||
float flattenAmount = smoothstep(0.1, 0.5, gravityPhase);
|
||||
float flattenX = mix(1.0, 0.4, flattenAmount);
|
||||
float stretchY = mix(1.0, 1.6, flattenAmount);
|
||||
|
||||
// Droplet local position & mask
|
||||
vec2 diff = (rainCoord + slideOffset) - dropCenter;
|
||||
diff.x *= 1.0 / flattenX;
|
||||
diff.y *= 1.0 / stretchY;
|
||||
float mask = smoothstep(squareSize * 0.5, squareSize * 0.45, max(abs(diff.x), abs(diff.y)));
|
||||
|
||||
if (mask > 0.001) {
|
||||
vec2 localUV = (diff + squareSize * 0.5) / squareSize;
|
||||
float choice = RandF(seed);
|
||||
vec4 dropTex = getDropTex(choice, localUV);
|
||||
float sharpAlpha = smoothstep(0.3, 0.9, dropTex.a);
|
||||
|
||||
float ambLum = clamp(dot(ambient, vec3(0.2126, 0.7152, 0.0722)), 0.0, 1.0);
|
||||
float sunFactor = pow(clamp(ambLum * 6.0, 0.0, 1.0), 0.5);
|
||||
float dynBright = mix(0.0, 1.0, sunFactor);
|
||||
|
||||
float colorLuma = length(dropTex.rgb);
|
||||
float alphaRange = smoothstep(0.1, 0.3, colorLuma);
|
||||
float blackAlpha = mix(0.25, 0.85, alphaRange);
|
||||
|
||||
float sparkle = 0.0;
|
||||
if (hasWobble) {
|
||||
sparkle = pow(abs(sin(f_coord.x * 8.0 + time * 2.0 + RandF(seed) * 6.2831)), 40.0)
|
||||
* mix(0.2, 1.0, sunFactor);
|
||||
}
|
||||
|
||||
vec3 specularColor = vec3(1.0) * specular_intensity * sparkle;
|
||||
vec3 dropLit = dropTex.rgb * dynBright + specularColor * sharpAlpha;
|
||||
|
||||
dropLayer += dropLit * sharpAlpha * actiwe * blackAlpha * mask;
|
||||
dropMaskSum += sharpAlpha * actiwe * blackAlpha * mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
vec3 finalMix = dropLayer;
|
||||
float alphaOut = clamp(dropMaskSum, 0.0, 1.0);
|
||||
out_color = vec4(finalMix, alphaOut);
|
||||
|
||||
{ // Overlay windshield texture with alpha
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(fragcolor, tex_color.a * alpha_mult);
|
||||
|
||||
out_color.xyz = apply_fog(mix(out_color.xyz, color.xyz, color.a));
|
||||
out_color.a = mix(out_color.a, 1., color.a);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
float GetMixFactor(in vec2 co, out float side) {
|
||||
vec4 movePhase = wiper_pos;
|
||||
bvec4 is_out = lessThanEqual(movePhase, vec4(1.));
|
||||
movePhase = mix(vec4(2.) - movePhase, movePhase, is_out);
|
||||
|
||||
vec4 mask = texture(wipermask, co);
|
||||
|
||||
vec4 areaMask = step(.001, mask);
|
||||
|
||||
vec4 maskVal = mix(1. - mask, mask, is_out);
|
||||
vec4 wipeWidth = smoothstep(1., .9, movePhase) * .25;
|
||||
vec4 cleaned = smoothstep(movePhase - wipeWidth, movePhase, maskVal) * areaMask;
|
||||
vec4 side_v = step(maskVal, movePhase);
|
||||
cleaned *= side_v;
|
||||
side_v = mix(side_v, vec4(1.) - side_v, is_out);
|
||||
|
||||
// "regeneration", raindrops gradually returning after wiper pass:
|
||||
vec4 regenPhase = clamp((vec4(time) - mix(wiper_timer_out, wiper_timer_return, side_v) - vec4(.2)) / vec4(rain_params.y), vec4(0.), vec4(1.));
|
||||
|
||||
side_v = mix(vec4(0.), vec4(1.) - side_v, areaMask);
|
||||
|
||||
vec4 factor_v = mix(vec4(1.), regenPhase * (vec4(1.) - cleaned), areaMask);
|
||||
|
||||
side = 0.;
|
||||
float out_factor = 1.;
|
||||
|
||||
// Find out the wiper blade that influences given grid cell the most
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
bool is_candidate = factor_v[i] < out_factor;
|
||||
out_factor = mix(out_factor, factor_v[i], is_candidate);
|
||||
side = mix(side, side_v[i], is_candidate);
|
||||
}
|
||||
|
||||
return out_factor;
|
||||
}
|
||||
4
shaders/vk14/mat_reflmap.frag
Normal file
4
shaders/vk14/mat_reflmap.frag
Normal file
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "mat_reflmap.glsl"
|
||||
72
shaders/vk14/mat_reflmap.glsl
Normal file
72
shaders/vk14/mat_reflmap.glsl
Normal file
@@ -0,0 +1,72 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(reflmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D reflmap;
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z * texture(reflmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, 0.0, shadow_tone);
|
||||
|
||||
if(alphatestfail)
|
||||
fragcolor.r = 1.0;
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
79
shaders/vk14/mat_reflmap_specgloss.frag
Normal file
79
shaders/vk14/mat_reflmap_specgloss.frag
Normal file
@@ -0,0 +1,79 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(reflmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D reflmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z * texture(reflmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
if(alphatestfail)
|
||||
fragcolor.r = 1.0;
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
75
shaders/vk14/mat_shadowlessnormalmap.frag
Normal file
75
shaders/vk14/mat_shadowlessnormalmap.frag
Normal file
@@ -0,0 +1,75 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, 1.0);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
80
shaders/vk14/mat_shadowlessnormalmap_specgloss.frag
Normal file
80
shaders/vk14/mat_shadowlessnormalmap_specgloss.frag
Normal file
@@ -0,0 +1,80 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, 1.0);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
38
shaders/vk14/mat_stars.frag
Normal file
38
shaders/vk14/mat_stars.frag
Normal file
@@ -0,0 +1,38 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 1) flat in vec3 f_normal_raw;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = (gl_PointCoord.x - 0.5f) * 2.0f;
|
||||
float y = (gl_PointCoord.y - 0.5f) * 2.0f;
|
||||
float dist2 = abs(x * x + y * y);
|
||||
if (dist2 > 0.5f * 0.5f)
|
||||
discard;
|
||||
|
||||
// color data space is shared with normals, ugh
|
||||
vec4 color = vec4(pow(f_normal_raw.rgb, vec3(2.2)), 1.0f);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
75
shaders/vk14/mat_sunlessnormalmap.frag
Normal file
75
shaders/vk14/mat_sunlessnormalmap.frag
Normal file
@@ -0,0 +1,75 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
80
shaders/vk14/mat_sunlessnormalmap_specgloss.frag
Normal file
80
shaders/vk14/mat_sunlessnormalmap_specgloss.frag
Normal file
@@ -0,0 +1,80 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
13
shaders/vk14/mat_trackblend.frag
Normal file
13
shaders/vk14/mat_trackblend.frag
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
// the rails and the ballast of a track faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(diffuse3, 3, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D diffuse3;
|
||||
#include "trackblend.glsl"
|
||||
22
shaders/vk14/mat_trackblend_normalmap.frag
Normal file
22
shaders/vk14/mat_trackblend_normalmap.frag
Normal file
@@ -0,0 +1,22 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
// the rails and the ballast of a track of a normal mapped material faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#define TRACKBLEND_NORMALMAP 1
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(diffuse3, 3, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D diffuse3;
|
||||
#pragma texture(normalmap, 4, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 4) uniform sampler2D normalmap;
|
||||
#pragma texture(normalmap1, 5, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 5) uniform sampler2D normalmap1;
|
||||
#pragma texture(normalmap2, 6, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 6) uniform sampler2D normalmap2;
|
||||
#pragma texture(normalmap3, 7, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 7) uniform sampler2D normalmap3;
|
||||
#include "trackblend.glsl"
|
||||
25
shaders/vk14/mat_trackblend_parallax.frag
Normal file
25
shaders/vk14/mat_trackblend_parallax.frag
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
// the rails and the ballast of a track of a parallax material faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#define TRACKBLEND_NORMALMAP 1
|
||||
#define TRACKBLEND_PARALLAX 1
|
||||
#pragma param(height_scale, 2, 1, 1, zero)
|
||||
#pragma param(height_offset, 2, 2, 1, zero)
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(diffuse3, 3, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D diffuse3;
|
||||
#pragma texture(normalmap, 4, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 4) uniform sampler2D normalmap;
|
||||
#pragma texture(normalmap1, 5, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 5) uniform sampler2D normalmap1;
|
||||
#pragma texture(normalmap2, 6, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 6) uniform sampler2D normalmap2;
|
||||
#pragma texture(normalmap3, 7, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 7) uniform sampler2D normalmap3;
|
||||
#include "trackblend.glsl"
|
||||
21
shaders/vk14/mat_trackblend_specgloss.frag
Normal file
21
shaders/vk14/mat_trackblend_specgloss.frag
Normal file
@@ -0,0 +1,21 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
// the rails and the ballast of a track of a material with normal and specgloss maps faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#define TRACKBLEND_NORMALMAP 1
|
||||
#define TRACKBLEND_SPECGLOSS 1
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(normalmap, 3, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D normalmap;
|
||||
#pragma texture(normalmap1, 4, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 4) uniform sampler2D normalmap1;
|
||||
#pragma texture(normalmap2, 5, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 5) uniform sampler2D normalmap2;
|
||||
#pragma texture(specgloss, 6, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 6) uniform sampler2D specgloss;
|
||||
#include "trackblend.glsl"
|
||||
98
shaders/vk14/mat_water.frag
Normal file
98
shaders/vk14/mat_water.frag
Normal file
@@ -0,0 +1,98 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(wave_strength, 2, 1, 1, one)
|
||||
#pragma param(wave_speed, 2, 2, 1, one)
|
||||
|
||||
#pragma texture(normalmap, 0, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(dudvmap, 1, RGB)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D dudvmap;
|
||||
|
||||
#pragma texture(diffuse, 2, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D diffuse;
|
||||
|
||||
//wave distortion variables
|
||||
float move_factor = 0.0;
|
||||
|
||||
#define WATER
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
//wave distortion
|
||||
move_factor += (param[2].z * time);
|
||||
move_factor = mod(move_factor, 1.0);
|
||||
vec2 texture_coords = f_coord;
|
||||
vec2 distorted_tex_coord = texture(dudvmap, vec2(texture_coords.x + move_factor, texture_coords.y)).rg * 0.1;
|
||||
distorted_tex_coord = texture_coords + vec2(distorted_tex_coord.x , distorted_tex_coord.y + move_factor);
|
||||
vec2 total_distorted_tex_coord = (texture(dudvmap, distorted_tex_coord).rg * 2.0 - 1.0 ) * param[2].y;
|
||||
texture_coords += total_distorted_tex_coord;
|
||||
|
||||
vec4 tex_color = texture(diffuse, texture_coords);
|
||||
|
||||
vec3 fragcolor = ambient * 0.25;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, texture_coords).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, texture_coords ).a;
|
||||
// water reflects 2% at normal incidence
|
||||
float specularity = 0.25;
|
||||
glossiness = abs(param[1].w);
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
//fragcolor = mix(fragcolor, param[0].rgb, param[1].z);
|
||||
// fragcolor = (fragcolor * tex_color.rgb * param[1].z) + (fragcolor * (1.0 - param[1].z)); //multiply
|
||||
//fragcolor = ( max(fragcolor + param[0].rgb -1.0,0.0) * param[1].z + fragcolor * (1.0 - param[1].z)); //linear burn
|
||||
//fragcolor = ( min(param[0].rgb,fragcolor) * param[1].z + fragcolor * (1.0 - param[1].z)); //darken
|
||||
|
||||
// fresnel effect
|
||||
vec3 view_dir = normalize(vec3(0.0f, 0.0f, 0.0f) - f_pos.xyz);
|
||||
float fresnel = pow(max(dot(normalize(f_normal), view_dir), 0.0), 0.2);
|
||||
float fresnel_inv = ((fresnel - 1.0 ) * -1.0 );
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), clamp((fresnel_inv + param[0].a), 0.0, 1.0));
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
103
shaders/vk14/mat_water_specgloss.frag
Normal file
103
shaders/vk14/mat_water_specgloss.frag
Normal file
@@ -0,0 +1,103 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(wave_strength, 2, 1, 1, one)
|
||||
#pragma param(wave_speed, 2, 2, 1, one)
|
||||
|
||||
#pragma texture(normalmap, 0, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(dudvmap, 1, RGB)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 1) uniform sampler2D dudvmap;
|
||||
|
||||
#pragma texture(diffuse, 2, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 2) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(specgloss, 3, RGBA)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 3) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
//wave distortion variables
|
||||
float move_factor = 0.0;
|
||||
|
||||
#define WATER
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
//wave distortion
|
||||
move_factor += (param[2].z * time);
|
||||
move_factor = mod(move_factor, 1.0);
|
||||
vec2 texture_coords = f_coord;
|
||||
vec2 distorted_tex_coord = texture(dudvmap, vec2(texture_coords.x + move_factor, texture_coords.y)).rg * 0.1;
|
||||
distorted_tex_coord = texture_coords + vec2(distorted_tex_coord.x , distorted_tex_coord.y + move_factor);
|
||||
vec2 total_distorted_tex_coord = (texture(dudvmap, distorted_tex_coord).rg * 2.0 - 1.0 ) * param[2].y;
|
||||
texture_coords += total_distorted_tex_coord;
|
||||
|
||||
vec4 tex_color = texture(diffuse, texture_coords);
|
||||
|
||||
vec3 fragcolor = ambient * 0.25;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, texture_coords).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, texture_coords ).a;
|
||||
vec4 specgloss_map = texture(specgloss, texture_coords);
|
||||
float specularity = specgloss_map.r;
|
||||
glossiness = specgloss_map.g * abs(param[1].w);
|
||||
metalic = specgloss_map.b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
//fragcolor = mix(fragcolor, param[0].rgb, param[1].z);
|
||||
// fragcolor = (fragcolor * tex_color.rgb * param[1].z) + (fragcolor * (1.0 - param[1].z)); //multiply
|
||||
//fragcolor = ( max(fragcolor + param[0].rgb -1.0,0.0) * param[1].z + fragcolor * (1.0 - param[1].z)); //linear burn
|
||||
//fragcolor = ( min(param[0].rgb,fragcolor) * param[1].z + fragcolor * (1.0 - param[1].z)); //darken
|
||||
|
||||
// fresnel effect
|
||||
vec3 view_dir = normalize(vec3(0.0f, 0.0f, 0.0f) - f_pos.xyz);
|
||||
float fresnel = pow(max(dot(normalize(f_normal), view_dir), 0.0), 0.2);
|
||||
float fresnel_inv = ((fresnel - 1.0 ) * -1.0 );
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), clamp((fresnel_inv + param[0].a), 0.0, 1.0));
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
10
shaders/vk14/pick.frag
Normal file
10
shaders/vk14/pick.frag
Normal file
@@ -0,0 +1,10 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = vec4(param[0].rgb, 1.0);
|
||||
}
|
||||
11
shaders/vk14/pick_surface.frag
Normal file
11
shaders/vk14/pick_surface.frag
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = vec4(param[0].r, f_coord, 1.0);
|
||||
}
|
||||
64
shaders/vk14/postfx_chromaticaberration.frag
Normal file
64
shaders/vk14/postfx_chromaticaberration.frag
Normal file
@@ -0,0 +1,64 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(color_tex, 0, RGB)
|
||||
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D iChannel0;
|
||||
|
||||
#include "dither.glsl"
|
||||
|
||||
vec2 barrelDistortion(vec2 f_coords, float amt) {
|
||||
vec2 cc = f_coords - 0.5;
|
||||
float dist = dot(cc, cc);
|
||||
return f_coords + cc * dist * amt;
|
||||
}
|
||||
|
||||
float sat( float t )
|
||||
{
|
||||
return clamp( t, 0.0, 1.0 );
|
||||
}
|
||||
|
||||
float linterp( float t ) {
|
||||
return sat( 1.0 - abs( 1.0*t - 1.0 ) );
|
||||
}
|
||||
|
||||
float remap( float t, float a, float b ) {
|
||||
return sat( (t - a) / (b - a) );
|
||||
}
|
||||
|
||||
vec4 spectrum_offset( float t ) {
|
||||
vec4 ret;
|
||||
float lo = step(t,0.5);
|
||||
float hi = 1.0-lo;
|
||||
float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );
|
||||
ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);
|
||||
|
||||
return pow( ret, vec4(1.0/2.2) );
|
||||
}
|
||||
|
||||
const float max_distort = 0.05;
|
||||
const int num_iter = 10;
|
||||
const float reci_num_iter_f = 1.0 / float(num_iter);
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 uv=f_coords.xy;
|
||||
|
||||
vec4 sumcol = vec4(0.0);
|
||||
vec4 sumw = vec4(0.0);
|
||||
for ( int i=0; i<num_iter;++i )
|
||||
{
|
||||
float t = float(i) * reci_num_iter_f;
|
||||
vec4 w = spectrum_offset( t );
|
||||
sumw += w;
|
||||
sumcol += w * texture( iChannel0, barrelDistortion(uv, .6 * max_distort*t ) );
|
||||
}
|
||||
|
||||
out_color = sumcol / sumw;
|
||||
out_color.rgb = dither_output(out_color.rgb);
|
||||
}
|
||||
46
shaders/vk14/postfx_motionblur.frag
Normal file
46
shaders/vk14/postfx_motionblur.frag
Normal file
@@ -0,0 +1,46 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// param is pc.data of the push constants (the model block in the opengl renderer): param[0].x the scale of the velocities
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(color_tex, 0, RGB)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D color_tex;
|
||||
|
||||
#pragma texture(velocity_tex, 1, RG)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 1) uniform sampler2D velocity_tex;
|
||||
|
||||
#pragma texture(depth_tex, 2, R)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 2) uniform sampler2D depth_tex;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
float ori_depth = texture(depth_tex, f_coords).x;
|
||||
|
||||
vec2 texelSize = 1.0 / vec2(textureSize(color_tex, 0));
|
||||
vec2 velocity = texture(velocity_tex, f_coords).xy * param[0].x;
|
||||
|
||||
float speed = length(velocity / texelSize);
|
||||
int nSamples = clamp(int(speed), 1, 32);
|
||||
|
||||
float depth_diff = 0.001 * 8.0;
|
||||
vec4 result = vec4(0.0);
|
||||
|
||||
result += vec4(texture(color_tex, f_coords).rgb, 1.0);
|
||||
|
||||
for (int i = 1; i < nSamples; ++i)
|
||||
{
|
||||
vec2 offset = velocity * (float(i) / float(nSamples - 1) - 0.5);
|
||||
|
||||
vec2 coord = f_coords + offset;
|
||||
float depth = texture(depth_tex, coord).x;
|
||||
|
||||
if (abs(depth - ori_depth) < depth_diff)
|
||||
result += vec4(texture(color_tex, coord).rgb, 1.0);
|
||||
}
|
||||
|
||||
out_color = vec4((result.rgb / result.w), 1.0);
|
||||
}
|
||||
78
shaders/vk14/postfx_ssao.frag
Normal file
78
shaders/vk14/postfx_ssao.frag
Normal file
@@ -0,0 +1,78 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// the projection of the view (projection of the scene block in the opengl renderer) is pc.data[0..3] of the push constants,
|
||||
// its columns
|
||||
#define projection mat4(pc.data[0], pc.data[1], pc.data[2], pc.data[3])
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(depth_tex, 0, R)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D depth_tex;
|
||||
|
||||
#pragma texture(noise_tex, 1, RGB)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 1) uniform sampler2D noise_tex;
|
||||
|
||||
// the depth is reversed (0..1, 1 at the near plane, 0 at infinity), so it is the clip z itself
|
||||
vec3 view_pos_from_depth(vec2 uv, float depth) {
|
||||
vec4 clip = vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
vec4 view = inverse(projection) * clip;
|
||||
return view.xyz / view.w;
|
||||
}
|
||||
|
||||
// Deterministic hemisphere sample (z > 0), mimics the C++ kernel generator
|
||||
vec3 kernel_sample(int i) {
|
||||
float fi = float(i);
|
||||
vec3 dir = normalize(vec3(
|
||||
fract(sin(fi * 12.9898) * 43758.5453) * 2.0 - 1.0,
|
||||
fract(sin(fi * 78.2330) * 43758.5453) * 2.0 - 1.0,
|
||||
fract(sin(fi * 37.7190) * 43758.5453) // z >= 0 -> hemisphere
|
||||
));
|
||||
float len = fract(sin(fi * 94.6720) * 43758.5453);
|
||||
float scale = fi / 32.0;
|
||||
scale = mix(0.1, 1.0, scale * scale); // bias samples inward
|
||||
return dir * len * scale;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float d = texture(depth_tex, f_coords).r;
|
||||
if (d <= 0.0) { out_color = vec4(1.0); return; } // skybox -> no occlusion
|
||||
|
||||
vec3 pos = view_pos_from_depth(f_coords, d);
|
||||
|
||||
vec3 ddx = dFdx(pos);
|
||||
vec3 ddy = dFdy(pos);
|
||||
vec3 n = normalize(cross(ddx, ddy));
|
||||
|
||||
// derive screen size from the depth texture instead of a UBO field
|
||||
vec2 screen_size = vec2(textureSize(depth_tex, 0));
|
||||
vec2 noise_uv = f_coords * (screen_size / 4.0);
|
||||
vec3 rvec = texture(noise_tex, noise_uv).xyz;
|
||||
|
||||
vec3 t = normalize(rvec - n * dot(rvec, n));
|
||||
vec3 b = cross(n, t);
|
||||
mat3 TBN = mat3(t, b, n);
|
||||
|
||||
const int KERNEL = 32;
|
||||
const float RADIUS = 0.5;
|
||||
const float BIAS = 0.025;
|
||||
|
||||
float occ = 0.0;
|
||||
for (int i = 0; i < KERNEL; ++i) {
|
||||
vec3 sp = TBN * kernel_sample(i);
|
||||
sp = pos + sp * RADIUS;
|
||||
|
||||
vec4 clip = projection * vec4(sp, 1.0);
|
||||
vec3 ndc = clip.xyz / clip.w;
|
||||
vec2 suv = ndc.xy * 0.5 + 0.5;
|
||||
|
||||
float sd = texture(depth_tex, suv).r;
|
||||
float szv = view_pos_from_depth(suv, sd).z;
|
||||
|
||||
float range = smoothstep(0.0, 1.0, RADIUS / abs(pos.z - szv));
|
||||
occ += (szv >= sp.z + BIAS ? 1.0 : 0.0) * range;
|
||||
}
|
||||
|
||||
out_color = vec4(1.0 - occ / float(KERNEL));
|
||||
}
|
||||
99
shaders/vk14/postfx_ssao_apply.frag
Normal file
99
shaders/vk14/postfx_ssao_apply.frag
Normal file
@@ -0,0 +1,99 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// applies the ambient occlusion of the half size target to the image at full size, as a multiplier: drawn with the
|
||||
// blending dst * src over the image (opengl46_ao::apply). the four nearest pixels of the occlusion are weighed by how
|
||||
// close they are and by how close their distance is to that of the pixel, so the occlusion does not spill over the
|
||||
// edges of objects. each pixel of the occlusion stands for the top left one of the four full size pixels it covers
|
||||
// params.x: 0 ground truth ambient occlusion, 1 the older ssao (its own fixed curve)
|
||||
// params.y: strength, a power of the occlusion (1 = as computed); params.z: albedo assumed by the multiple bounce term.
|
||||
// the fog in front of a surface hides its occlusion as it hides the surface: the analytic fog of apply_fog.glsl, the fog
|
||||
// volume comes with the stage P2.
|
||||
// the parameters are in the push constants (loose uniforms in the opengl renderer): pc.data[0..3] the columns of the inverse
|
||||
// of the projection (inv_projection), pc.data[4] params, pc.data[5].x the density of the fog of the scene (fog_density of the
|
||||
// regular draws, 0: no fog), pc.data[5].yzw unused
|
||||
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D depth_tex;
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 1) uniform sampler2D ao_tex;
|
||||
|
||||
#define inv_projection mat4(pc.data[0], pc.data[1], pc.data[2], pc.data[3])
|
||||
#define params pc.data[4]
|
||||
#define scene_fog_density pc.data[5].x
|
||||
|
||||
vec3 view_position(ivec2 pixel, float depth)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
vec2 uv = (vec2(pixel) + 0.5) / vec2(full_size);
|
||||
vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
return position.xyz / position.w;
|
||||
}
|
||||
|
||||
float view_distance(ivec2 pixel, float depth)
|
||||
{
|
||||
return length(view_position(pixel, depth));
|
||||
}
|
||||
|
||||
// light which comes back after more than one bounce off surfaces of the given albedo, fit of Jimenez et al. 2016
|
||||
float multi_bounce(float visibility, float albedo)
|
||||
{
|
||||
float a = 2.0404 * albedo - 0.3324;
|
||||
float b = -4.7951 * albedo + 0.6417;
|
||||
float c = 2.7552 * albedo + 0.6903;
|
||||
return max(visibility, ((visibility * a + b) * visibility + c) * visibility);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
ivec2 half_size = textureSize(ao_tex, 0);
|
||||
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
||||
float depth = texelFetch(depth_tex, pixel, 0).r;
|
||||
if (depth <= 0.0)
|
||||
{
|
||||
// the sky
|
||||
out_color = vec4(1.0);
|
||||
return;
|
||||
}
|
||||
float distance = view_distance(pixel, depth);
|
||||
|
||||
vec2 position = vec2(pixel) * 0.5;
|
||||
ivec2 base = ivec2(floor(position));
|
||||
vec2 fraction = position - vec2(base);
|
||||
float sum = 0.0;
|
||||
float weights = 0.0;
|
||||
for (int y = 0; y < 2; ++y)
|
||||
{
|
||||
for (int x = 0; x < 2; ++x)
|
||||
{
|
||||
ivec2 texel = clamp(base + ivec2(x, y), ivec2(0), half_size - 1);
|
||||
ivec2 texelpixel = min(texel * 2, full_size - 1);
|
||||
float texeldepth = texelFetch(depth_tex, texelpixel, 0).r;
|
||||
float texeldistance = (texeldepth > 0.0) ? view_distance(texelpixel, texeldepth) : 1e30;
|
||||
float bilinear = (x == 0 ? 1.0 - fraction.x : fraction.x) * (y == 0 ? 1.0 - fraction.y : fraction.y);
|
||||
float weight = (bilinear + 1e-3) / (1e-3 + abs(texeldistance - distance) / distance);
|
||||
sum += texelFetch(ao_tex, texel, 0).r * weight;
|
||||
weights += weight;
|
||||
}
|
||||
}
|
||||
float ao = clamp(sum / max(weights, 1e-6), 0.0, 1.0);
|
||||
|
||||
if (params.x > 0.5)
|
||||
{
|
||||
// the curve the older ssao was applied with, a floor so the shadows do not go black
|
||||
ao = mix(0.3, 1.0, pow(ao, 0.8));
|
||||
}
|
||||
else
|
||||
{
|
||||
ao = pow(ao, params.y);
|
||||
ao = multi_bounce(ao, params.z);
|
||||
}
|
||||
// what the fog lets through of the surface is what is left to occlude
|
||||
float transmittance = (scene_fog_density > 0.0) ? clamp(exp(-distance * scene_fog_density), 0.0, 1.0) : 1.0;
|
||||
ao = mix(1.0, ao, transmittance);
|
||||
out_color = vec4(vec3(ao), 1.0);
|
||||
}
|
||||
18
shaders/vk14/postfx_ssao_blur.frag
Normal file
18
shaders/vk14/postfx_ssao_blur.frag
Normal file
@@ -0,0 +1,18 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(ssao_tex, 0, R)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D ssao_tex;
|
||||
|
||||
void main() {
|
||||
vec2 texel = 1.0 / vec2(textureSize(ssao_tex, 0));
|
||||
float sum = 0.0;
|
||||
for (int x = -2; x < 2; ++x)
|
||||
for (int y = -2; y < 2; ++y)
|
||||
sum += texture(ssao_tex, f_coords + vec2(x, y) * texel).r;
|
||||
out_color = vec4(sum / 16.0);
|
||||
}
|
||||
40
shaders/vk14/postfx_tonemapping.frag
Normal file
40
shaders/vk14/postfx_tonemapping.frag
Normal file
@@ -0,0 +1,40 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(tex1, 0, RGB)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
// glow of the bright parts of the image (postfx_bloom_*.frag)
|
||||
#pragma texture(bloom_tex, 1, RGB)
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 1) uniform sampler2D bloom_tex;
|
||||
|
||||
// the automatic exposure (exposure_ssbo of the opengl renderer, exposure_average.comp) comes in a later stage: the fixed
|
||||
// exposure multiplier alone
|
||||
const float exposure = 0.0;
|
||||
|
||||
#include "tonemapping.glsl"
|
||||
#include "dither.glsl"
|
||||
|
||||
// param is pc.data of the push constants (the model block in the opengl renderer).
|
||||
// param[0].x: share of the bloom in the image (0: no bloom, the texture is not read), param[0].y: 1 if the automatic
|
||||
// exposure is applied (there is none yet, so it has no effect), param[0].z: fixed exposure multiplier, param[0].w: scale of
|
||||
// the bloom texture (it holds the sum of the levels of the chain), param[1].x: 1 when this is the last pass before the
|
||||
// window, which takes the dither
|
||||
void main()
|
||||
{
|
||||
vec2 texcoord = f_coords;
|
||||
vec3 hdr_color = texture(tex1, texcoord).xyz;
|
||||
if (param[0].x > 0.0)
|
||||
hdr_color = mix(hdr_color, texture(bloom_tex, texcoord).rgb * param[0].w, param[0].x);
|
||||
float exposurescale = param[0].z;
|
||||
if (param[0].y > 0.5 && exposure > 0.0)
|
||||
exposurescale *= exposure;
|
||||
out_color = tonemap(vec4(hdr_color * exposurescale, 1.0));
|
||||
if (param[1].x > 0.5)
|
||||
out_color.rgb = dither_output(out_color.rgb);
|
||||
}
|
||||
42
shaders/vk14/precipitation.frag
Normal file
42
shaders/vk14/precipitation.frag
Normal file
@@ -0,0 +1,42 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(tex1, vec2(f_coord.x, f_coord.y + param[1].x));
|
||||
vec4 color = tex_color * param[0];
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
20
shaders/vk14/precipitation.vert
Normal file
20
shaders/vk14/precipitation.vert
Normal file
@@ -0,0 +1,20 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 1) in vec2 v_coord;
|
||||
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
f_clip_pos = (projection * modelview) * vec4(v_vert, 1.0f);
|
||||
f_clip_future_pos = (projection * future * modelview) * vec4(v_vert, 1.0f);
|
||||
|
||||
gl_Position = f_clip_pos;
|
||||
f_coord = v_coord;
|
||||
}
|
||||
17
shaders/vk14/present.frag
Normal file
17
shaders/vk14/present.frag
Normal file
@@ -0,0 +1,17 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
// the finished image to the window (with quad.vert). the renderer draws with the matrices of the opengl one, so its images
|
||||
// have the rows in the memory order of opengl, the bottom of the picture first; the window shows the first row at the top.
|
||||
// this is the only place the picture is turned upside down. no push constants
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
layout(set = POST_SET, binding = POST_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = FBOUT(texture(tex1, vec2(f_coords.x, 1.0 - f_coords.y)));
|
||||
}
|
||||
27
shaders/vk14/quad.vert
Normal file
27
shaders/vk14/quad.vert
Normal file
@@ -0,0 +1,27 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#define LAYOUT_POST 1
|
||||
#include "common.glsl"
|
||||
const vec2 vert[4] = vec2[]
|
||||
(
|
||||
vec2(-1.0, 1.0),
|
||||
vec2(-1.0, -1.0),
|
||||
vec2( 1.0, 1.0),
|
||||
vec2( 1.0, -1.0)
|
||||
);
|
||||
|
||||
const vec2 uv[4] = vec2[]
|
||||
(
|
||||
vec2(0.0, 1.0),
|
||||
vec2(0.0, 0.0),
|
||||
vec2(1.0, 1.0),
|
||||
vec2(1.0, 0.0)
|
||||
);
|
||||
|
||||
layout(location = 14) out vec2 f_coords;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vert[gl_VertexIndex], 0.0, 1.0);
|
||||
f_coords = uv[gl_VertexIndex];
|
||||
}
|
||||
39
shaders/vk14/random.glsl
Normal file
39
shaders/vk14/random.glsl
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
uint Hash(uint s);
|
||||
uint Hash(uvec2 s);
|
||||
uint Hash(uvec3 s);
|
||||
uint Hash(float s);
|
||||
uint HashCombine(uint seed, uint value);
|
||||
|
||||
// Evolving Sub-Grid Turbulence for Smoke Animation
|
||||
// H. Schechter and R. Bridson
|
||||
uint Hash(uint s) {
|
||||
s ^= 2747636419u;
|
||||
s *= 2654435769u;
|
||||
s ^= s >> 16u;
|
||||
s *= 2654435769u;
|
||||
s ^= s >> 16u;
|
||||
s *= 2654435769u;
|
||||
return s;
|
||||
}
|
||||
|
||||
uint Hash(uvec2 s) {
|
||||
return HashCombine(Hash(s.x), s.y);
|
||||
}
|
||||
|
||||
uint Hash(uvec3 s) {
|
||||
return HashCombine(Hash(s.xy), s.z);
|
||||
}
|
||||
|
||||
uint HashCombine(uint seed, uint value) {
|
||||
return seed ^ (Hash(value) + 0x9e3779b9u + (seed<<6u) + (seed>>2u));
|
||||
}
|
||||
|
||||
uint Rand(inout uint seed) {
|
||||
seed = Hash(seed);
|
||||
return seed;
|
||||
}
|
||||
|
||||
float RandF(inout uint seed) {
|
||||
return float(Rand(seed)) * 2.3283064365386963e-10;
|
||||
}
|
||||
25
shaders/vk14/settings.glsl
Normal file
25
shaders/vk14/settings.glsl
Normal file
@@ -0,0 +1,25 @@
|
||||
// the settings of the game the shaders depend on (gfx.* in eu07.ini): specialization constants the renderer sets when it makes a
|
||||
// pipeline, the ids are the SPEC_* of rendering/vulkan14/vulkan14bindings.h. included by common.glsl after the constants
|
||||
#ifndef SETTINGS_GLSL
|
||||
#define SETTINGS_GLSL
|
||||
|
||||
#ifndef SPEC_OUTPUT_GAMMA
|
||||
#error "settings.glsl takes the ids of the specialization constants from common.glsl: include common.glsl instead"
|
||||
#endif
|
||||
|
||||
layout(constant_id = SPEC_SHADOWMAP_ENABLED) const bool SHADOWMAP_ENABLED = true;
|
||||
layout(constant_id = SPEC_ENVMAP_ENABLED) const bool ENVMAP_ENABLED = true;
|
||||
layout(constant_id = SPEC_MOTIONBLUR_ENABLED) const bool MOTIONBLUR_ENABLED = true;
|
||||
// the colour goes on to the post-processing; tonemapped by the shaders of the scene otherwise
|
||||
layout(constant_id = SPEC_POSTFX_ENABLED) const bool POSTFX_ENABLED = true;
|
||||
layout(constant_id = SPEC_EXTRAEFFECTS_ENABLED) const bool EXTRAEFFECTS_ENABLED = true;
|
||||
// whether the colour written to the window is encoded by the shader (FBOUT): the window has no srgb format (gfx.shadergamma yes)
|
||||
layout(constant_id = SPEC_OUTPUT_GAMMA) const bool OUTPUT_GAMMA = false;
|
||||
|
||||
// the colour written to the window
|
||||
vec4 FBOUT(vec4 x)
|
||||
{
|
||||
return (OUTPUT_GAMMA ? vec4(pow(x.rgb, vec3(1.0 / 2.2)), x.a) : x);
|
||||
}
|
||||
|
||||
#endif
|
||||
8
shaders/vk14/shadowmap.frag
Normal file
8
shaders/vk14/shadowmap.frag
Normal file
@@ -0,0 +1,8 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
}
|
||||
14
shaders/vk14/simpleuv.vert
Normal file
14
shaders/vk14/simpleuv.vert
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 2) in vec2 v_coord;
|
||||
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = (projection * effective_modelview()) * vec4(v_vert, 1.0f);
|
||||
f_coord = v_coord;
|
||||
}
|
||||
16
shaders/vk14/sky_shoulder.glsl
Normal file
16
shaders/vk14/sky_shoulder.glsl
Normal file
@@ -0,0 +1,16 @@
|
||||
// the skydome vertex colours carry Perez luminance scaled by a fixed exposure
|
||||
// (see CSkyDome::SetExposure), so the circumsolar region and the horizon band
|
||||
// routinely land well above 1.0. hard-clipping them, as this shader used to do,
|
||||
// flattened everything within ~20 degrees of the sun - and most of the lower sky -
|
||||
// into one solid white patch. compress the top end instead: below the knee the
|
||||
// response is unchanged, above it it rolls off towards SKY_SHOULDER_WHITE, so the
|
||||
// brightness gradient around the sun survives and the patch keeps a soft edge.
|
||||
const float SKY_SHOULDER_KNEE = 0.25;
|
||||
const float SKY_SHOULDER_WHITE = 0.95;
|
||||
|
||||
vec3 sky_shoulder(vec3 color)
|
||||
{
|
||||
const float range = SKY_SHOULDER_WHITE - SKY_SHOULDER_KNEE;
|
||||
vec3 excess = max(color - vec3(SKY_SHOULDER_KNEE), vec3(0.0));
|
||||
return min(color, vec3(SKY_SHOULDER_KNEE)) + range * (vec3(1.0) - exp(-excess / range));
|
||||
}
|
||||
43
shaders/vk14/smoke.frag
Normal file
43
shaders/vk14/smoke.frag
Normal file
@@ -0,0 +1,43 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
layout(location = 13) in vec4 f_color;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D tex1;
|
||||
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
#ifdef NO_MOTION_OUTPUT
|
||||
// a pass without the velocity target: the writes go nowhere
|
||||
vec4 out_motion;
|
||||
#else
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(tex1, f_coord) * f_color;
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = vec4(apply_fog(tex_color.rgb), tex_color.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(vec4(apply_fog(tex_color.rgb), tex_color.a));
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
25
shaders/vk14/smoke.vert
Normal file
25
shaders/vk14/smoke.vert
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
#include "common.glsl"
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 1) in vec4 v_color;
|
||||
layout(location = 2) in vec2 v_coord;
|
||||
|
||||
layout(location = 3) out vec4 f_pos;
|
||||
layout(location = 13) out vec4 f_color;
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
f_pos = modelview * vec4(v_vert, 1.0f);
|
||||
f_clip_pos = (projection * modelview) * vec4(v_vert, 1.0f);
|
||||
f_clip_future_pos = (projection * future * modelview) * vec4(v_vert, 1.0f);
|
||||
|
||||
gl_Position = f_clip_pos;
|
||||
f_coord = v_coord;
|
||||
f_color = v_color;
|
||||
}
|
||||
154
shaders/vk14/tonemapping.glsl
Normal file
154
shaders/vk14/tonemapping.glsl
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "common.glsl"
|
||||
|
||||
const float pureWhite = 1.0;
|
||||
|
||||
vec3 reinhard(vec3 x)
|
||||
{
|
||||
// return x / (x + vec3(1.0));
|
||||
|
||||
// Reinhard tonemapping operator.
|
||||
// see: "Photographic Tone Reproduction for Digital Images", eq. 4
|
||||
float luminance = dot(x, vec3(0.2126, 0.7152, 0.0722));
|
||||
float mappedLuminance = (luminance * (1.0 + luminance/(pureWhite*pureWhite))) / (1.0 + luminance);
|
||||
// Scale color by ratio of average luminances.
|
||||
return (mappedLuminance / luminance) * x;
|
||||
}
|
||||
|
||||
// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
|
||||
vec3 ACESFilm(vec3 x)
|
||||
{
|
||||
float a = 2.51f;
|
||||
float b = 0.03f;
|
||||
float c = 2.43f;
|
||||
float d = 0.59f;
|
||||
float e = 0.14f;
|
||||
return (x*(a*x+b))/(x*(c*x+d)+e);
|
||||
}
|
||||
|
||||
// https://www.slideshare.net/ozlael/hable-john-uncharted2-hdr-lighting
|
||||
vec3 filmicF(vec3 x)
|
||||
{
|
||||
float A = 0.22f;
|
||||
float B = 0.30f;
|
||||
float C = 0.10f;
|
||||
float D = 0.20f;
|
||||
float E = 0.01f;
|
||||
float F = 0.30f;
|
||||
return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F)) - E/F;
|
||||
}
|
||||
|
||||
vec3 filmic(vec3 x)
|
||||
{
|
||||
return filmicF(x) / filmicF(vec3(11.2f));
|
||||
}
|
||||
|
||||
|
||||
// AgX tonemapping based on nxrighthere / Missing Deadlines implementation.
|
||||
// 0: Default, 1: Golden, 2: Punchy
|
||||
#ifndef AGX_LOOK
|
||||
#define AGX_LOOK 0
|
||||
#endif
|
||||
|
||||
vec3 AgxDefaultContrastApprox(vec3 x)
|
||||
{
|
||||
vec3 x2 = x * x;
|
||||
vec3 x4 = x2 * x2;
|
||||
|
||||
return 15.5 * x4 * x2
|
||||
- 40.14 * x4 * x
|
||||
+ 31.96 * x4
|
||||
- 6.868 * x2 * x
|
||||
+ 0.4298 * x2
|
||||
+ 0.1191 * x
|
||||
- 0.00232;
|
||||
}
|
||||
|
||||
vec3 Agx(vec3 val)
|
||||
{
|
||||
mat3 agx_mat = mat3(
|
||||
0.842479062253094, 0.0784335999999992, 0.0792237451477643,
|
||||
0.0423282422610123, 0.878468636469772, 0.0791661274605434,
|
||||
0.0423756549057051, 0.0784336, 0.879142973793104
|
||||
);
|
||||
|
||||
// DEFAULT_LOG2_MIN = -6.0
|
||||
// DEFAULT_LOG2_MAX = +4.5
|
||||
// MIDDLE_GRAY = 0.18
|
||||
// log2(pow(2, VALUE) * MIDDLE_GRAY)
|
||||
const float min_ev = -8.47393;
|
||||
const float max_ev = 2.026069;
|
||||
const float agx_eps = 1e-6;
|
||||
|
||||
// Input transform (inset)
|
||||
val = agx_mat * val;
|
||||
|
||||
// Log2 space encoding. max() avoids -INF/NaN for zero/negative inputs.
|
||||
val = clamp(log2(max(val, vec3(agx_eps))), min_ev, max_ev);
|
||||
val = (val - min_ev) / (max_ev - min_ev);
|
||||
|
||||
// Apply sigmoid function approximation.
|
||||
return AgxDefaultContrastApprox(val);
|
||||
}
|
||||
|
||||
vec3 AgxEotf(vec3 val)
|
||||
{
|
||||
mat3 agx_mat_inv = mat3(
|
||||
1.19687900512017, -0.0980208811401368, -0.0990297440797205,
|
||||
-0.0528968517574562, 1.15190312990417, -0.0989611768448433,
|
||||
-0.0529716355144438, -0.0980434501171241, 1.15107367264116
|
||||
);
|
||||
|
||||
// Inverse input transform (outset)
|
||||
val = agx_mat_inv * val;
|
||||
|
||||
return max(val, vec3(0.0));
|
||||
}
|
||||
|
||||
vec3 AgxLook(vec3 val)
|
||||
{
|
||||
vec3 lw = vec3(0.2126, 0.7152, 0.0722);
|
||||
float luma = dot(val, lw);
|
||||
|
||||
vec3 offset = vec3(0.0);
|
||||
vec3 slope = vec3(1.0);
|
||||
vec3 power = vec3(1.6);
|
||||
float sat = 0.9;
|
||||
|
||||
#if AGX_LOOK == 1
|
||||
// Golden
|
||||
slope = vec3(1.0, 0.9, 0.5);
|
||||
power = vec3(0.8);
|
||||
sat = 0.8;
|
||||
#elif AGX_LOOK == 2
|
||||
// Punchy
|
||||
slope = vec3(1.0);
|
||||
power = vec3(1.35);
|
||||
sat = 1.1;
|
||||
#endif
|
||||
|
||||
// ASC CDL
|
||||
val = pow(max(val * slope + offset, vec3(0.0)), power);
|
||||
return vec3(luma) + sat * (val - vec3(luma));
|
||||
}
|
||||
|
||||
vec3 ApplyAgX(vec3 linearColorRec709)
|
||||
{
|
||||
linearColorRec709 = Agx(linearColorRec709);
|
||||
linearColorRec709 = AgxLook(linearColorRec709);
|
||||
linearColorRec709 = AgxEotf(linearColorRec709);
|
||||
return linearColorRec709;
|
||||
}
|
||||
|
||||
vec4 tonemap(vec4 x)
|
||||
{
|
||||
// Use AgX by default. Reinhard and ACES above are kept for reference.
|
||||
|
||||
// Last-line-of-defense sanitize so NaN/Inf/negative HDR values do not
|
||||
// escape into log2()/pow() and produce black flashes or invalid output.
|
||||
vec3 hdr = x.rgb;
|
||||
hdr = mix(hdr, vec3(0.0), vec3(any(isnan(hdr)) || any(isinf(hdr))));
|
||||
hdr = max(hdr, vec3(0.0));
|
||||
return FBOUT(vec4(ApplyAgX(hdr), x.a));
|
||||
//return FBOUT(vec4(ACESFilm(hdr), x.a));
|
||||
//return FBOUT(vec4(reinhard(x.rgb), x.a));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user