Commit 817a8848 authored by Florian Oetke's avatar Florian Oetke
Browse files

billboards (lit/unlit and world/screen-space coordinates) [fixes #22; fixes #42]

parent 7d674097
......@@ -3,4 +3,5 @@ blueprint:sponza = blueprints/sponza.json
blueprint:cornell = blueprints/cornell.json
blueprint:sun = blueprints/sun.json
blueprint:cube = blueprints/cube.json
blueprint:billboard = blueprints/billboard.json
{
"Transform":{
},
"Billboard": {
"billboards": [
{
"size": {"w":1, "h":1},
"material_aid": "mat:billboard_material.msf",
"clip_rect": {"x":0, "y":0, "z":1, "w":1},
"dynamic_lighting": true
}
]
}
}
{
"albedo_aid": "billboard.ktx",
"emission_aid": "white"
}
......@@ -2,6 +2,7 @@
#include <mirrage/renderer/deferred_renderer.hpp>
#include <mirrage/renderer/pass/animation_pass.hpp>
#include <mirrage/renderer/pass/billboard_pass.hpp>
#include <mirrage/renderer/pass/blit_pass.hpp>
#include <mirrage/renderer/pass/bloom_pass.hpp>
#include <mirrage/renderer/pass/debug_draw_pass.hpp>
......@@ -44,6 +45,7 @@ namespace mirrage {
renderer::make_pass_factory<renderer::Depth_of_field_pass_factory>(),
renderer::make_pass_factory<renderer::Bloom_pass_factory>(),
renderer::make_pass_factory<renderer::Tone_mapping_pass_factory>(),
renderer::make_pass_factory<renderer::Billboard_pass_factory>(),
renderer::make_pass_factory<renderer::Debug_draw_pass_factory>(),
renderer::make_pass_factory<renderer::Blit_pass_factory>(),
renderer::make_pass_factory<renderer::Gui_pass_factory>())))
......
......@@ -97,6 +97,12 @@ namespace mirrage {
_meta_system.entities().emplace("sponza");
auto billboard = _meta_system.entities().emplace("billboard");
billboard.get<Transform_comp>().process([](auto& transform) {
transform.position = {-8, 1, 0.5f};
transform.orientation = glm::quatLookAt(glm::vec3{-1, 0, 0}, glm::vec3{0, 1, 0});
});
_sun = _meta_system.entities().emplace("sun");
_set_preset(1);
......
......@@ -27,7 +27,8 @@
using namespace mirrage;
auto extract_arg(std::vector<std::string>& args, const std::string& key) -> util::maybe<std::string>;
auto extract_arg(std::vector<std::string>& args, const std::string& key, bool flag = false)
-> util::maybe<std::string>;
auto load_config(const util::maybe<std::string>& config_arg,
const util::maybe<std::string>& out_arg,
const std::string& working_dir,
......@@ -60,6 +61,8 @@ int main(int argc, char** argv)
auto output_arg = extract_arg(args, "--output");
auto config_arg = extract_arg(args, "--cfg");
auto config = load_config(extract_arg(args, "--cfg"), output_arg, argv[0], args);
auto normal = extract_arg(args, "--normal", true).is_some();
auto srgb = !extract_arg(args, "--rg", true).is_some();
auto output = output_arg.get_or(config.default_output_directory);
......@@ -70,7 +73,7 @@ int main(int argc, char** argv)
for(auto&& input : args) {
if(util::ends_with(input, ".png"))
convert_texture(input, output);
convert_texture(input, output, normal, srgb);
else
convert_model(input, output, config);
}
......@@ -89,7 +92,8 @@ int main(int argc, char** argv)
}
}
auto extract_arg(std::vector<std::string>& args, const std::string& key) -> util::maybe<std::string>
auto extract_arg(std::vector<std::string>& args, const std::string& key, bool flag)
-> util::maybe<std::string>
{
auto found =
std::find_if(args.begin(), args.end(), [&](auto& str) { return util::starts_with(str, key); });
......@@ -97,6 +101,11 @@ auto extract_arg(std::vector<std::string>& args, const std::string& key) -> util
if(found == args.end())
return mirrage::util::nothing;
if(flag) {
args.erase(found);
return util::just(std::string());
}
// found contains the key and the value
if(util::contains(*found, '=') && found->back() != '=') {
auto ret = *found;
......
......@@ -446,15 +446,45 @@ namespace mirrage {
return true;
}
void convert_texture(const std::string& input, const std::string& output_dir)
void convert_texture(const std::string& input,
const std::string& output_dir,
bool normal_texture,
bool srgb)
{
auto data = load_texture2d(input, true);
generate_mip_maps(data, [](auto a, auto b, auto c, auto d) { return (a + b + c + d) / 4.f; });
auto data = load_texture2d(input, srgb);
if(normal_texture) {
data.foreach([&](auto& pixel, auto level, auto x, auto y) {
// generate mip levels
if(level > 0) {
auto n_00 = data.pixel(level - 1, x * 2, y * 2);
auto n_10 = data.pixel(level - 1, x * 2 + 1, y * 2);
auto n_11 = data.pixel(level - 1, x * 2 + 1, y * 2 + 1);
auto n_01 = data.pixel(level - 1, x * 2, y * 2 + 1);
auto n = glm::normalize(glm::vec3(n_00.r * 2 - 1, n_00.g * 2 - 1, n_00.b * 2 - 1))
+ glm::normalize(glm::vec3(n_10.r * 2 - 1, n_10.g * 2 - 1, n_10.b * 2 - 1))
+ glm::normalize(glm::vec3(n_11.r * 2 - 1, n_11.g * 2 - 1, n_11.b * 2 - 1))
+ glm::normalize(glm::vec3(n_01.r * 2 - 1, n_01.g * 2 - 1, n_01.b * 2 - 1));
n = glm::normalize(n / 4.f);
pixel.r = n.x * 0.5f + 0.5f;
pixel.g = n.y * 0.5f + 0.5f;
pixel.b = n.z * 0.5f + 0.5f;
pixel.a = 1;
}
});
} else {
generate_mip_maps(data, [](auto a, auto b, auto c, auto d) { return (a + b + c + d) / 4.f; });
}
auto input_path = util::split_on_last(input, "/");
auto input_name = input_path.second.empty() ? input_path.first : input_path.second;
input_name = util::split_on_last(input_name, ".").first;
store_texture(data, output_dir + "/" + input_name + ".ktx", Texture_format::s_rgba);
store_texture(data,
output_dir + "/" + input_name + ".ktx",
srgb ? Texture_format::s_rgba : Texture_format::rg);
}
} // namespace mirrage
......@@ -14,6 +14,9 @@ namespace mirrage {
const std::string& output,
const Mesh_converted_config& cfg);
extern void convert_texture(const std::string& input, const std::string& output_dir);
extern void convert_texture(const std::string& input,
const std::string& output_dir,
bool normal_texture,
bool srgb);
} // namespace mirrage
......@@ -41,6 +41,7 @@ file(GLOB_RECURSE HEADER_FILES
add_library(mirrage_renderer STATIC
src/pass/animation_pass.cpp
src/pass/billboard_pass.cpp
src/pass/blit_pass.cpp
src/pass/bloom_pass.cpp
src/pass/debug_draw_pass.cpp
......@@ -59,6 +60,7 @@ add_library(mirrage_renderer STATIC
src/animation.cpp
src/animation_comp.cpp
src/billboard.cpp
src/camera_comp.cpp
src/debug_ui.hpp
src/deferred_renderer.cpp
......
shader: = shader/
vert_shader:billboard = shader/billboard.vert.spv
frag_shader:billboard_lit = shader/billboard.frag.spv
frag_shader:billboard_unlit = shader/billboard_unlit.frag.spv
vert_shader:blit = shader/fullscreen.vert.spv
frag_shader:blit = shader/blit.frag.spv
......
tex: = textures/
tex:black = textures/default_black.png
tex:white = textures/default_white.ktx
tex:normal = textures/default_normal.png
tex:default_normal = textures/default_normal.ktx
tex:default_brdf = textures/default_brdf.ktx
tex:placeholder = textures/default_placeholder.ktx
tex:blue_noise = textures/blue_noise.ktx
......
#pragma once
#include <mirrage/renderer/model.hpp>
#include <mirrage/ecs/component.hpp>
#include <mirrage/utils/sf2_glm.hpp>
#include <mirrage/utils/small_vector.hpp>
namespace mirrage::renderer {
struct Billboard_data {
glm::vec3 offset{0, 0, 0};
glm::vec2 size{1, 1};
glm::vec4 clip_rect{0, 0, 1, 1}; // x,y,w,h
util::Rgba color{1, 1, 1, 1};
util::Rgba emissive_color{1, 1, 1, 1000};
bool active = true;
bool dynamic_lighting = true;
bool absolute_screen_space = false;
bool fixed_screen_size = false;
bool vertical_rotation = false;
std::string material_aid;
};
#ifdef sf2_structDef
sf2_structDef(Billboard_data,
offset,
size,
clip_rect,
color,
emissive_color,
dynamic_lighting,
absolute_screen_space,
fixed_screen_size,
vertical_rotation,
material_aid);
#endif
struct Billboard : Billboard_data {
Material_ptr material;
Billboard() = default;
Billboard(Billboard_data&& data, Material_ptr m)
: Billboard_data(std::move(data)), material(std::move(m))
{
}
};
class Billboard_comp : public ecs::Component<Billboard_comp> {
public:
static constexpr const char* name() { return "Billboard"; }
friend void load_component(ecs::Deserializer& state, Billboard_comp&);
friend void save_component(ecs::Serializer& state, const Billboard_comp&);
using Component::Component;
util::small_vector<Billboard, 1> billboards;
};
struct Billboard_push_constants {
glm::vec4 position;
glm::vec4 size; // xy, z=screen_space, w=fixed_screen_size
glm::vec4 clip_rect;
glm::vec4 color;
glm::vec4 emissive_color;
glm::vec4 placeholder[2];
};
extern auto construct_push_constants(const Billboard&, const glm::mat4& view, const glm::vec4& viewport)
-> Billboard_push_constants;
} // namespace mirrage::renderer
namespace mirrage::asset {
template <>
struct Loader<renderer::Billboard> {
public:
static auto load(istream in) -> async::task<renderer::Billboard>;
void save(ostream, const renderer::Billboard&)
{
MIRRAGE_FAIL("Save of billboards is not supported!");
}
};
} // namespace mirrage::asset
#pragma once
#include <mirrage/renderer/deferred_renderer.hpp>
#include <mirrage/graphic/render_pass.hpp>
namespace mirrage::renderer {
class Billboard_pass_factory;
class Billboard_pass : public Render_pass {
public:
using Factory = Billboard_pass_factory;
Billboard_pass(Deferred_renderer&, ecs::Entity_manager&, graphic::Render_target_2D& target);
void update(util::Time dt) override;
void draw(Frame_data&) override;
auto name() const noexcept -> const char* override { return "Billboard"; }
private:
Deferred_renderer& _renderer;
ecs::Entity_manager& _entities;
graphic::Framebuffer _framebuffer;
graphic::Render_pass _render_pass;
};
class Billboard_pass_factory : public Render_pass_factory {
public:
auto id() const noexcept -> Render_pass_id override
{
return render_pass_id_of<Billboard_pass_factory>();
}
auto create_pass(Deferred_renderer&,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t> graphics_queue, int current_score)
-> int override;
void configure_device(vk::PhysicalDevice,
util::maybe<std::uint32_t> graphics_queue,
graphic::Device_create_info&) override;
};
} // namespace mirrage::renderer
......@@ -29,6 +29,9 @@ namespace mirrage::renderer {
void configure_animation_subpass(Deferred_renderer&, graphic::Subpass_builder&);
void configure_animation_emissive_subpass(Deferred_renderer&, graphic::Subpass_builder&);
void configure_billboard_pipeline(Deferred_renderer&, graphic::Pipeline_description&);
void configure_billboard_subpass(Deferred_renderer&, graphic::Subpass_builder&);
void update(util::Time dt);
void pre_draw(Frame_data&);
void draw(Frame_data&, graphic::Render_pass&);
......
#pragma once
#include <mirrage/renderer/billboard.hpp>
#include <mirrage/ecs/entity_handle.hpp>
#include <mirrage/utils/maybe.hpp>
#include <mirrage/utils/ranges.hpp>
......@@ -118,6 +120,7 @@ namespace mirrage::renderer {
std::vector<Geometry> geometry_queue;
std::vector<Light> light_queue;
std::vector<Debug_geometry> debug_geometry_queue;
std::vector<Billboard> billboard_queue;
auto partition_geometry(std::uint32_t mask) -> util::vector_range<Geometry>;
};
......
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
#include "normal_encoding.glsl"
layout(location = 0) in vec3 view_pos;
layout(location = 1) in vec2 tex_coords;
layout(location = 0) out vec4 depth_out;
layout(location = 1) out vec4 albedo_mat_id_out;
layout(location = 2) out vec4 mat_data_out;
layout(location = 3) out vec4 color_out;
layout(location = 4) out vec4 color_diffuse_out;
layout(set=1, binding = 0) uniform sampler2D albedo_sampler;
layout(set=1, binding = 1) uniform sampler2D normal_sampler;
layout(set=1, binding = 2) uniform sampler2D brdf_sampler;
layout(set=1, binding = 3) uniform sampler2D emission_sampler;
layout(push_constant) uniform Per_model_uniforms {
vec4 position;
vec4 size; // xy, z=screen_space, w=fixed_screen_size
vec4 clip_rect;
vec4 color;
vec4 emissive_color;
} model_uniforms;
const float PI = 3.14159265359;
vec3 decode_tangent_normal(vec2 tn);
vec3 tangent_space_to_world(vec3 N);
void main() {
vec4 albedo = texture(albedo_sampler, tex_coords);
albedo *= model_uniforms.color;
if(albedo.a<0.05)
discard;
vec3 N = tangent_space_to_world(decode_tangent_normal(texture(normal_sampler, tex_coords).rg));
vec4 brdf = texture(brdf_sampler, tex_coords);
float roughness = brdf.r;
float metallic = brdf.g;
roughness = mix(0.01, 0.99, roughness*roughness);
float emissive_power = texture(emission_sampler, tex_coords).r;
depth_out = vec4(-view_pos.z / global_uniforms.proj_planes.y, 0,0,1);
albedo_mat_id_out = vec4(albedo.rgb, 0.0);
mat_data_out = vec4(encode_normal(N), roughness, metallic);
color_out = vec4(albedo.rgb * model_uniforms.emissive_color.rgb
* emissive_power * model_uniforms.emissive_color.a * albedo.a, 0.0);
color_diffuse_out = color_out;
}
vec3 decode_tangent_normal(vec2 tn) {
if(dot(tn,tn)<0.00001)
return vec3(0,0,1);
vec3 N = vec3(tn*2-1, 0);
N.z = sqrt(1 - dot(N.xy, N.xy));
return N;
}
vec3 tangent_space_to_world(vec3 N) {
vec3 VN = vec3(0,0,1);
// calculate tangent
vec3 p_dx = dFdx(view_pos);
vec3 p_dy = dFdy(view_pos);
vec2 tc_dx = dFdx(tex_coords);
vec2 tc_dy = dFdy(tex_coords);
vec3 p_dy_N = cross(p_dy, VN);
vec3 p_dx_N = cross(VN, p_dx);
vec3 T = p_dy_N * tc_dx.x + p_dx_N * tc_dy.x;
vec3 B = p_dy_N * tc_dx.y + p_dx_N * tc_dy.y;
float inv_max = inversesqrt(max(dot(T,T), dot(B,B)));
mat3 TBN = mat3(T*inv_max, B*inv_max, VN);
return normalize(TBN * N);
}
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
layout(location = 0) out vec3 out_view_pos;
layout(location = 1) out vec2 out_tex_coords;
layout(push_constant) uniform Per_model_uniforms {
vec4 position;
vec4 size; // xy, z=screen_space, w=fixed_screen_size
vec4 clip_rect;
vec4 color;
vec4 emissive_color;
} model_uniforms;
out gl_PerVertex {
vec4 gl_Position;
};
const vec2 vertex_positions[4] = vec2[](
vec2(0, 0),
vec2(1, 0),
vec2(0, 1),
vec2(1, 1)
);
void main() {
vec2 p = vertex_positions[gl_VertexIndex];
vec2 offset = (p-0.5)*model_uniforms.size.xy;
if(model_uniforms.position.w>0.5)
offset = vec2(offset.x, 0) + offset.y*vec2(global_uniforms.view_mat[1][0], global_uniforms.view_mat[1][1]);
vec4 view_pos = vec4(model_uniforms.position.xyz, 1.0);
if(model_uniforms.size.w<0.5)
view_pos.xy += offset;
vec4 ndc_pos = global_uniforms.proj_mat * view_pos;
if(model_uniforms.size.z>=0.5)
ndc_pos = view_pos;
if(model_uniforms.size.w>=0.5) {
ndc_pos.xy += vec2(offset.x, -offset.y)*ndc_pos.w;
}
gl_Position = ndc_pos;
out_view_pos = view_pos.xyz;
out_tex_coords = model_uniforms.clip_rect.xy + p * model_uniforms.clip_rect.zw;
out_tex_coords.y = 1-out_tex_coords.y;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment