1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-07 12:30:27 +00:00

Renaming of servers for coherency.

VisualServer -> RenderingServer
PhysicsServer -> PhysicsServer3D
Physics2DServer -> PhysicsServer2D
NavigationServer -> NavigationServer3D
Navigation2DServer -> NavigationServer2D

Also renamed corresponding files.
This commit is contained in:
Juan Linietsky
2020-03-27 15:21:27 -03:00
parent 307b1b3a58
commit a6f3bc7c69
390 changed files with 10701 additions and 10702 deletions

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env python
Import('env')
env.add_source_files(env.servers_sources, "*.cpp")
SConscript("shaders/SCsub")

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,255 @@
/*************************************************************************/
/* light_cluster_builder.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "light_cluster_builder.h"
void LightClusterBuilder::begin(const Transform &p_view_transform, const CameraMatrix &p_cam_projection) {
view_xform = p_view_transform;
projection = p_cam_projection;
z_near = -projection.get_z_near();
z_far = -projection.get_z_far();
//reset counts
light_count = 0;
refprobe_count = 0;
item_count = 0;
sort_id_count = 0;
}
void LightClusterBuilder::bake_cluster() {
float slice_depth = (z_near - z_far) / depth;
uint8_t *cluster_dataw = cluster_data.ptrw();
Cell *cluster_data_ptr = (Cell *)cluster_dataw;
//clear the cluster
zeromem(cluster_data_ptr, (width * height * depth * sizeof(Cell)));
/* Step 1, create cell positions and count them */
for (uint32_t i = 0; i < item_count; i++) {
const Item &item = items[i];
int from_slice = Math::floor((z_near - (item.aabb.position.z + item.aabb.size.z)) / slice_depth);
int to_slice = Math::floor((z_near - item.aabb.position.z) / slice_depth);
if (from_slice >= (int)depth || to_slice < 0) {
continue; //sorry no go
}
from_slice = MAX(0, from_slice);
to_slice = MIN((int)depth - 1, to_slice);
for (int j = from_slice; j <= to_slice; j++) {
Vector3 min = item.aabb.position;
Vector3 max = item.aabb.position + item.aabb.size;
float limit_near = MIN((z_near - slice_depth * j), max.z);
float limit_far = MAX((z_near - slice_depth * (j + 1)), min.z);
max.z = limit_near;
min.z = limit_near;
Vector3 proj_min = projection.xform(min);
Vector3 proj_max = projection.xform(max);
int near_from_x = int(Math::floor((proj_min.x * 0.5 + 0.5) * width));
int near_from_y = int(Math::floor((-proj_max.y * 0.5 + 0.5) * height));
int near_to_x = int(Math::floor((proj_max.x * 0.5 + 0.5) * width));
int near_to_y = int(Math::floor((-proj_min.y * 0.5 + 0.5) * height));
max.z = limit_far;
min.z = limit_far;
proj_min = projection.xform(min);
proj_max = projection.xform(max);
int far_from_x = int(Math::floor((proj_min.x * 0.5 + 0.5) * width));
int far_from_y = int(Math::floor((-proj_max.y * 0.5 + 0.5) * height));
int far_to_x = int(Math::floor((proj_max.x * 0.5 + 0.5) * width));
int far_to_y = int(Math::floor((-proj_min.y * 0.5 + 0.5) * height));
//print_line(itos(j) + " near - " + Vector2i(near_from_x, near_from_y) + " -> " + Vector2i(near_to_x, near_to_y));
//print_line(itos(j) + " far - " + Vector2i(far_from_x, far_from_y) + " -> " + Vector2i(far_to_x, far_to_y));
int from_x = MIN(near_from_x, far_from_x);
int from_y = MIN(near_from_y, far_from_y);
int to_x = MAX(near_to_x, far_to_x);
int to_y = MAX(near_to_y, far_to_y);
if (from_x >= (int)width || to_x < 0 || from_y >= (int)height || to_y < 0) {
continue;
}
int sx = MAX(0, from_x);
int sy = MAX(0, from_y);
int dx = MIN((int)width - 1, to_x);
int dy = MIN((int)height - 1, to_y);
//print_line(itos(j) + " - " + Vector2i(sx, sy) + " -> " + Vector2i(dx, dy));
for (int x = sx; x <= dx; x++) {
for (int y = sy; y <= dy; y++) {
uint32_t offset = j * (width * height) + y * width + x;
if (unlikely(sort_id_count == sort_id_max)) {
sort_id_max = nearest_power_of_2_templated(sort_id_max + 1);
sort_ids = (SortID *)memrealloc(sort_ids, sizeof(SortID) * sort_id_max);
if (ids.size()) {
ids.resize(sort_id_max);
RD::get_singleton()->free(items_buffer);
items_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * sort_id_max);
}
}
sort_ids[sort_id_count].cell_index = offset;
sort_ids[sort_id_count].item_index = item.index;
sort_ids[sort_id_count].item_type = item.type;
sort_id_count++;
//for now, only count
cluster_data_ptr[offset].item_pointers[item.type]++;
//print_line("at offset " + itos(offset) + " value: " + itos(cluster_data_ptr[offset].item_pointers[item.type]));
}
}
}
}
/* Step 2, Assign pointers (and reset counters) */
uint32_t offset = 0;
for (uint32_t i = 0; i < (width * height * depth); i++) {
for (int j = 0; j < ITEM_TYPE_MAX; j++) {
uint32_t count = cluster_data_ptr[i].item_pointers[j]; //save count
cluster_data_ptr[i].item_pointers[j] = offset; //replace count by pointer
offset += count; //increase offset by count;
}
}
//print_line("offset: " + itos(offset));
/* Step 3, Place item lists */
uint32_t *ids_ptr = ids.ptrw();
for (uint32_t i = 0; i < sort_id_count; i++) {
const SortID &id = sort_ids[i];
Cell &cell = cluster_data_ptr[id.cell_index];
uint32_t pointer = cell.item_pointers[id.item_type] & POINTER_MASK;
uint32_t counter = cell.item_pointers[id.item_type] >> COUNTER_SHIFT;
ids_ptr[pointer + counter] = id.item_index;
cell.item_pointers[id.item_type] = pointer | ((counter + 1) << COUNTER_SHIFT);
}
RD::get_singleton()->texture_update(cluster_texture, 0, cluster_data, true);
RD::get_singleton()->buffer_update(items_buffer, 0, offset * sizeof(uint32_t), ids_ptr, true);
}
void LightClusterBuilder::setup(uint32_t p_width, uint32_t p_height, uint32_t p_depth) {
if (width == p_width && height == p_height && depth == p_depth) {
return;
}
if (cluster_texture.is_valid()) {
RD::get_singleton()->free(cluster_texture);
}
width = p_width;
height = p_height;
depth = p_depth;
cluster_data.resize(width * height * depth * sizeof(Cell));
{
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
tf.type = RD::TEXTURE_TYPE_3D;
tf.width = width;
tf.height = height;
tf.depth = depth;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
cluster_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
}
RID LightClusterBuilder::get_cluster_texture() const {
return cluster_texture;
}
RID LightClusterBuilder::get_cluster_indices_buffer() const {
return items_buffer;
}
LightClusterBuilder::LightClusterBuilder() {
//initialize accumulators to something
lights = (LightData *)memalloc(sizeof(LightData) * 1024);
light_max = 1024;
refprobes = (OrientedBoxData *)memalloc(sizeof(OrientedBoxData) * 1024);
refprobe_max = 1024;
decals = (OrientedBoxData *)memalloc(sizeof(OrientedBoxData) * 1024);
decal_max = 1024;
items = (Item *)memalloc(sizeof(Item) * 1024);
item_max = 1024;
sort_ids = (SortID *)memalloc(sizeof(SortID) * 1024);
ids.resize(2014);
items_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 1024);
item_max = 1024;
}
LightClusterBuilder::~LightClusterBuilder() {
if (cluster_data.size()) {
RD::get_singleton()->free(cluster_texture);
}
if (lights) {
memfree(lights);
}
if (refprobes) {
memfree(refprobes);
}
if (decals) {
memfree(decals);
}
if (items) {
memfree(items);
}
if (sort_ids) {
memfree(sort_ids);
RD::get_singleton()->free(items_buffer);
}
}

View File

@@ -0,0 +1,291 @@
/*************************************************************************/
/* light_cluster_builder.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef LIGHT_CLUSTER_BUILDER_H
#define LIGHT_CLUSTER_BUILDER_H
#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h"
class LightClusterBuilder {
public:
enum LightType {
LIGHT_TYPE_OMNI,
LIGHT_TYPE_SPOT
};
enum ItemType {
ITEM_TYPE_OMNI_LIGHT,
ITEM_TYPE_SPOT_LIGHT,
ITEM_TYPE_REFLECTION_PROBE,
ITEM_TYPE_DECAL,
ITEM_TYPE_MAX //should always be 4
};
enum {
COUNTER_SHIFT = 20, //one million total ids
POINTER_MASK = (1 << COUNTER_SHIFT) - 1,
COUNTER_MASK = 0xfff // 4096 items per cell
};
private:
struct LightData {
float position[3];
uint32_t type;
float radius;
float spot_aperture;
uint32_t pad[2];
};
uint32_t light_count = 0;
uint32_t light_max = 0;
LightData *lights = nullptr;
struct OrientedBoxData {
float position[3];
uint32_t pad;
float x_axis[3];
uint32_t pad2;
float y_axis[3];
uint32_t pad3;
float z_axis[3];
uint32_t pad4;
};
uint32_t refprobe_count = 0;
uint32_t refprobe_max = 0;
OrientedBoxData *refprobes = nullptr;
uint32_t decal_count = 0;
uint32_t decal_max = 0;
OrientedBoxData *decals = nullptr;
struct Item {
AABB aabb;
ItemType type;
uint32_t index;
};
Item *items = nullptr;
uint32_t item_count = 0;
uint32_t item_max = 0;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 0;
struct Cell {
uint32_t item_pointers[ITEM_TYPE_MAX];
};
Vector<uint8_t> cluster_data;
RID cluster_texture;
struct SortID {
uint32_t cell_index;
uint32_t item_index;
ItemType item_type;
};
SortID *sort_ids = nullptr;
Vector<uint32_t> ids;
uint32_t sort_id_count = 0;
uint32_t sort_id_max = 0;
RID items_buffer;
Transform view_xform;
CameraMatrix projection;
float z_far = 0;
float z_near = 0;
_FORCE_INLINE_ void _add_item(const AABB &p_aabb, ItemType p_type, uint32_t p_index) {
if (unlikely(item_count == item_max)) {
item_max = nearest_power_of_2_templated(item_max + 1);
items = (Item *)memrealloc(items, sizeof(Item) * item_max);
}
Item &item = items[item_count];
item.aabb = p_aabb;
item.index = p_index;
item.type = p_type;
item_count++;
}
public:
void begin(const Transform &p_view_transform, const CameraMatrix &p_cam_projection);
_FORCE_INLINE_ void add_light(LightType p_type, const Transform &p_transform, float p_radius, float p_spot_aperture) {
if (unlikely(light_count == light_max)) {
light_max = nearest_power_of_2_templated(light_max + 1);
lights = (LightData *)memrealloc(lights, sizeof(LightData) * light_max);
}
LightData &ld = lights[light_count];
ld.type = p_type;
ld.position[0] = p_transform.origin.x;
ld.position[1] = p_transform.origin.y;
ld.position[2] = p_transform.origin.z;
ld.radius = p_radius;
ld.spot_aperture = p_spot_aperture;
Transform xform = view_xform * p_transform;
ld.radius *= xform.basis.get_uniform_scale();
AABB aabb;
switch (p_type) {
case LIGHT_TYPE_OMNI: {
aabb.position = xform.origin;
aabb.size = Vector3(ld.radius, ld.radius, ld.radius);
aabb.position -= aabb.size;
aabb.size *= 2.0;
_add_item(aabb, ITEM_TYPE_OMNI_LIGHT, light_count);
} break;
case LIGHT_TYPE_SPOT: {
Vector3 v(0, 0, -1);
v.rotated(Vector3(0, 1, 0), Math::deg2rad(ld.spot_aperture)); //rotate in x-z
v.normalize();
v *= ld.radius;
v.y = v.x;
aabb.position = xform.origin;
aabb.expand_to(xform.xform(v));
aabb.expand_to(xform.xform(Vector3(-v.x, v.y, v.z)));
aabb.expand_to(xform.xform(Vector3(-v.x, -v.y, v.z)));
aabb.expand_to(xform.xform(Vector3(v.x, -v.y, v.z)));
_add_item(aabb, ITEM_TYPE_SPOT_LIGHT, light_count);
} break;
}
light_count++;
}
_FORCE_INLINE_ void add_reflection_probe(const Transform &p_transform, const Vector3 &p_half_extents) {
if (unlikely(refprobe_count == refprobe_max)) {
refprobe_max = nearest_power_of_2_templated(refprobe_max + 1);
refprobes = (OrientedBoxData *)memrealloc(refprobes, sizeof(OrientedBoxData) * refprobe_max);
}
OrientedBoxData &rp = refprobes[refprobe_count];
Vector3 origin = p_transform.origin;
rp.position[0] = origin.x;
rp.position[1] = origin.y;
rp.position[2] = origin.z;
Vector3 x_axis = p_transform.basis.get_axis(0) * p_half_extents.x;
rp.x_axis[0] = x_axis.x;
rp.x_axis[1] = x_axis.y;
rp.x_axis[2] = x_axis.z;
Vector3 y_axis = p_transform.basis.get_axis(1) * p_half_extents.y;
rp.y_axis[0] = y_axis.x;
rp.y_axis[1] = y_axis.y;
rp.y_axis[2] = y_axis.z;
Vector3 z_axis = p_transform.basis.get_axis(2) * p_half_extents.z;
rp.z_axis[0] = z_axis.x;
rp.z_axis[1] = z_axis.y;
rp.z_axis[2] = z_axis.z;
AABB aabb;
aabb.position = origin + x_axis + y_axis + z_axis;
aabb.expand_to(origin + x_axis + y_axis - z_axis);
aabb.expand_to(origin + x_axis - y_axis + z_axis);
aabb.expand_to(origin + x_axis - y_axis - z_axis);
aabb.expand_to(origin - x_axis + y_axis + z_axis);
aabb.expand_to(origin - x_axis + y_axis - z_axis);
aabb.expand_to(origin - x_axis - y_axis + z_axis);
aabb.expand_to(origin - x_axis - y_axis - z_axis);
_add_item(aabb, ITEM_TYPE_REFLECTION_PROBE, refprobe_count);
refprobe_count++;
}
_FORCE_INLINE_ void add_decal(const Transform &p_transform, const Vector2 &p_half_extents, float p_depth) {
if (unlikely(decal_count == decal_max)) {
decal_max = nearest_power_of_2_templated(decal_max + 1);
decals = (OrientedBoxData *)memrealloc(decals, sizeof(OrientedBoxData) * decal_max);
}
OrientedBoxData &dc = decals[decal_count];
Vector3 z_axis = -p_transform.basis.get_axis(2) * p_depth * 0.5;
dc.z_axis[0] = z_axis.x;
dc.z_axis[1] = z_axis.y;
dc.z_axis[2] = z_axis.z;
Vector3 origin = p_transform.origin - z_axis;
dc.position[0] = origin.x;
dc.position[1] = origin.y;
dc.position[2] = origin.z;
Vector3 x_axis = p_transform.basis.get_axis(0) * p_half_extents.x;
dc.x_axis[0] = x_axis.x;
dc.x_axis[1] = x_axis.y;
dc.x_axis[2] = x_axis.z;
Vector3 y_axis = p_transform.basis.get_axis(1) * p_half_extents.y;
dc.y_axis[0] = y_axis.x;
dc.y_axis[1] = y_axis.y;
dc.y_axis[2] = y_axis.z;
AABB aabb;
aabb.position = origin + x_axis + y_axis + z_axis;
aabb.expand_to(origin + x_axis + y_axis - z_axis);
aabb.expand_to(origin + x_axis - y_axis + z_axis);
aabb.expand_to(origin + x_axis - y_axis - z_axis);
aabb.expand_to(origin - x_axis + y_axis + z_axis);
aabb.expand_to(origin - x_axis + y_axis - z_axis);
aabb.expand_to(origin - x_axis - y_axis + z_axis);
aabb.expand_to(origin - x_axis - y_axis - z_axis);
_add_item(aabb, ITEM_TYPE_DECAL, decal_count);
decal_count++;
}
void bake_cluster();
void setup(uint32_t p_width, uint32_t p_height, uint32_t p_depth);
RID get_cluster_texture() const;
RID get_cluster_indices_buffer() const;
LightClusterBuilder();
~LightClusterBuilder();
};
#endif // LIGHT_CLUSTER_BUILDER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,501 @@
/*************************************************************************/
/* rasterizer_canvas_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef RASTERIZER_CANVAS_RD_H
#define RASTERIZER_CANVAS_RD_H
#include "servers/rendering/rasterizer.h"
#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h"
#include "servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h"
#include "servers/rendering/rasterizer_rd/shader_compiler_rd.h"
#include "servers/rendering/rasterizer_rd/shaders/canvas.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/canvas_occlusion.glsl.gen.h"
#include "servers/rendering/rendering_device.h"
class RasterizerCanvasRD : public RasterizerCanvas {
RasterizerStorageRD *storage;
enum ShaderVariant {
SHADER_VARIANT_QUAD,
SHADER_VARIANT_NINEPATCH,
SHADER_VARIANT_PRIMITIVE,
SHADER_VARIANT_PRIMITIVE_POINTS,
SHADER_VARIANT_ATTRIBUTES,
SHADER_VARIANT_ATTRIBUTES_POINTS,
SHADER_VARIANT_QUAD_LIGHT,
SHADER_VARIANT_NINEPATCH_LIGHT,
SHADER_VARIANT_PRIMITIVE_LIGHT,
SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT,
SHADER_VARIANT_ATTRIBUTES_LIGHT,
SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
SHADER_VARIANT_MAX
};
enum {
FLAGS_INSTANCING_STRIDE_MASK = 0xF,
FLAGS_INSTANCING_ENABLED = (1 << 4),
FLAGS_INSTANCING_HAS_COLORS = (1 << 5),
FLAGS_INSTANCING_COLOR_8BIT = (1 << 6),
FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 7),
FLAGS_INSTANCING_CUSTOM_DATA_8_BIT = (1 << 8),
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
FLAGS_USING_LIGHT_MASK = (1 << 11),
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
FLAGS_USING_PARTICLES = (1 << 13),
FLAGS_USE_PIXEL_SNAP = (1 << 14),
FLAGS_USE_SKELETON = (1 << 15),
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
FLAGS_LIGHT_COUNT_SHIFT = 20,
FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27)
};
enum {
LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
LIGHT_FLAGS_BLEND_SHIFT = 16,
LIGHT_FLAGS_BLEND_MASK = (3 << 16),
LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16),
LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16),
LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16),
LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16),
LIGHT_FLAGS_HAS_SHADOW = (1 << 20),
LIGHT_FLAGS_FILTER_SHIFT = 22
};
enum {
MAX_RENDER_ITEMS = 256 * 1024,
MAX_LIGHT_TEXTURES = 1024,
DEFAULT_MAX_LIGHTS_PER_ITEM = 16,
DEFAULT_MAX_LIGHTS_PER_RENDER = 256
};
/****************/
/**** SHADER ****/
/****************/
enum PipelineVariant {
PIPELINE_VARIANT_QUAD,
PIPELINE_VARIANT_NINEPATCH,
PIPELINE_VARIANT_PRIMITIVE_TRIANGLES,
PIPELINE_VARIANT_PRIMITIVE_LINES,
PIPELINE_VARIANT_PRIMITIVE_POINTS,
PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES,
PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP,
PIPELINE_VARIANT_ATTRIBUTE_LINES,
PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP,
PIPELINE_VARIANT_ATTRIBUTE_POINTS,
PIPELINE_VARIANT_MAX
};
enum PipelineLightMode {
PIPELINE_LIGHT_MODE_DISABLED,
PIPELINE_LIGHT_MODE_ENABLED,
PIPELINE_LIGHT_MODE_MAX
};
struct PipelineVariants {
RenderPipelineVertexFormatCacheRD variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX];
};
struct {
CanvasShaderRD canvas_shader;
RID default_version;
RID default_version_rd_shader;
RID default_version_rd_shader_light;
RID quad_index_buffer;
RID quad_index_array;
PipelineVariants pipeline_variants;
// default_skeleton uniform set
RID default_skeleton_uniform_buffer;
RID default_skeleton_texture_buffer;
ShaderCompilerRD compiler;
} shader;
struct ShaderData : public RasterizerStorageRD::ShaderData {
enum BlendMode { //used internally
BLEND_MODE_MIX,
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PMALPHA,
BLEND_MODE_DISABLED,
};
enum LightMode {
LIGHT_MODE_NORMAL,
LIGHT_MODE_UNSHADED,
LIGHT_MODE_LIGHT_ONLY
};
bool valid;
RID version;
PipelineVariants pipeline_variants;
String path;
Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
Map<StringName, RID> default_texture_params;
bool uses_screen_texture;
bool uses_material_samplers;
virtual void set_code(const String &p_Code);
virtual void set_default_texture_param(const StringName &p_name, RID p_texture);
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
virtual bool is_param_texture(const StringName &p_param) const;
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual Variant get_default_parameter(const StringName &p_parameter) const;
ShaderData();
virtual ~ShaderData();
};
RasterizerStorageRD::ShaderData *_create_shader_func();
static RasterizerStorageRD::ShaderData *_create_shader_funcs() {
return static_cast<RasterizerCanvasRD *>(singleton)->_create_shader_func();
}
struct MaterialData : public RasterizerStorageRD::MaterialData {
uint64_t last_frame;
ShaderData *shader_data;
RID uniform_buffer;
RID uniform_set;
Vector<RID> texture_cache;
Vector<uint8_t> ubo_data;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual ~MaterialData();
};
RasterizerStorageRD::MaterialData *_create_material_func(ShaderData *p_shader);
static RasterizerStorageRD::MaterialData *_create_material_funcs(RasterizerStorageRD::ShaderData *p_shader) {
return static_cast<RasterizerCanvasRD *>(singleton)->_create_material_func(static_cast<ShaderData *>(p_shader));
}
/**************************/
/**** TEXTURE BINDINGS ****/
/**************************/
// bindings used to render commands,
// cached for performance.
struct TextureBindingKey {
RID texture;
RID normalmap;
RID specular;
RID multimesh;
RS::CanvasItemTextureFilter texture_filter;
RS::CanvasItemTextureRepeat texture_repeat;
bool operator==(const TextureBindingKey &p_key) const {
return texture == p_key.texture && normalmap == p_key.normalmap && specular == p_key.specular && multimesh == p_key.specular && texture_filter == p_key.texture_filter && texture_repeat == p_key.texture_repeat;
}
};
struct TextureBindingKeyHasher {
static _FORCE_INLINE_ uint32_t hash(const TextureBindingKey &p_key) {
uint32_t hash = hash_djb2_one_64(p_key.texture.get_id());
hash = hash_djb2_one_64(p_key.normalmap.get_id(), hash);
hash = hash_djb2_one_64(p_key.specular.get_id(), hash);
hash = hash_djb2_one_64(p_key.multimesh.get_id(), hash);
hash = hash_djb2_one_32(uint32_t(p_key.texture_filter) << 16 | uint32_t(p_key.texture_repeat), hash);
return hash;
}
};
struct TextureBinding {
TextureBindingID id;
TextureBindingKey key;
SelfList<TextureBinding> to_dispose;
uint32_t reference_count;
RID uniform_set;
TextureBinding() :
to_dispose(this) {
reference_count = 0;
}
};
struct {
SelfList<TextureBinding>::List to_dispose_list;
TextureBindingID id_generator;
HashMap<TextureBindingKey, TextureBindingID, TextureBindingKeyHasher> texture_key_bindings;
HashMap<TextureBindingID, TextureBinding *> texture_bindings;
TextureBindingID default_empty;
} bindings;
RID _create_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RenderingServer::CanvasItemTextureFilter p_filter, RenderingServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh);
void _dispose_bindings();
struct {
RS::CanvasItemTextureFilter default_filter;
RS::CanvasItemTextureRepeat default_repeat;
} default_samplers;
/******************/
/**** POLYGONS ****/
/******************/
struct PolygonBuffers {
RD::VertexFormatID vertex_format_id;
RID vertex_buffer;
RID vertex_array;
RID index_buffer;
RID indices;
};
struct {
HashMap<PolygonID, PolygonBuffers> polygons;
PolygonID last_id;
} polygon_buffers;
/********************/
/**** PRIMITIVES ****/
/********************/
struct {
RID index_array[4];
} primitive_arrays;
/*******************/
/**** MATERIALS ****/
/*******************/
/******************/
/**** LIGHTING ****/
/******************/
struct CanvasLight {
RID texture;
struct {
int size;
RID texture;
RID depth;
RID fb;
} shadow;
};
RID_Owner<CanvasLight> canvas_light_owner;
struct ShadowRenderPushConstant {
float projection[16];
float modelview[8];
float direction[2];
float pad[2];
};
struct OccluderPolygon {
RS::CanvasOccluderPolygonCullMode cull_mode;
int point_count;
RID vertex_buffer;
RID vertex_array;
RID index_buffer;
RID index_array;
};
struct LightUniform {
float matrix[8]; //light to texture coordinate matrix
float shadow_matrix[8]; //light to shadow coordinate matrix
float color[4];
float shadow_color[4];
float position[2];
uint32_t flags; //index to light texture
float height;
float shadow_pixel_size;
float pad[3];
};
RID_Owner<OccluderPolygon> occluder_polygon_owner;
struct {
CanvasOcclusionShaderRD shader;
RID shader_version;
RID render_pipelines[3];
RD::VertexFormatID vertex_format;
RD::FramebufferFormatID framebuffer_format;
} shadow_render;
/***************/
/**** STATE ****/
/***************/
//state that does not vary across rendering all items
struct ItemStateData : public Item::CustomData {
struct LightCache {
uint64_t light_version;
Light *light;
};
LightCache light_cache[DEFAULT_MAX_LIGHTS_PER_ITEM];
uint32_t light_cache_count;
RID state_uniform_set_with_light;
RID state_uniform_set;
ItemStateData() {
for (int i = 0; i < DEFAULT_MAX_LIGHTS_PER_ITEM; i++) {
light_cache[i].light_version = 0;
light_cache[i].light = NULL;
}
light_cache_count = 0xFFFFFFFF;
}
~ItemStateData() {
if (state_uniform_set_with_light.is_valid() && RD::get_singleton()->uniform_set_is_valid(state_uniform_set_with_light)) {
RD::get_singleton()->free(state_uniform_set_with_light);
}
if (state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state_uniform_set)) {
RD::get_singleton()->free(state_uniform_set);
}
}
};
struct State {
//state buffer
struct Buffer {
float canvas_transform[16];
float screen_transform[16];
float canvas_normal_transform[16];
float canvas_modulate[4];
float screen_pixel_size[2];
float time;
float pad;
//uint32_t light_count;
//uint32_t pad[3];
};
LightUniform *light_uniforms;
RID lights_uniform_buffer;
RID canvas_state_buffer;
RID shadow_sampler;
uint32_t max_lights_per_render;
uint32_t max_lights_per_item;
double time;
} state;
struct PushConstant {
float world[6];
uint32_t flags;
uint32_t specular_shininess;
union {
//rect
struct {
float modulation[4];
float ninepatch_margins[4];
float dst_rect[4];
float src_rect[4];
float pad[2];
};
//primitive
struct {
float points[6]; // vec2 points[3]
float uvs[6]; // vec2 points[3]
uint32_t colors[6]; // colors encoded as half
};
};
float color_texture_pixel_size[2];
uint32_t lights[4];
};
struct SkeletonUniform {
float skeleton_transform[16];
float skeleton_inverse[16];
};
Item *items[MAX_RENDER_ITEMS];
Size2i _bind_texture_binding(TextureBindingID p_binding, RenderingDevice::DrawListID p_draw_list, uint32_t &flags);
void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants);
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, RID p_screen_uniform_set);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform &p_transform, float *p_mat4);
_FORCE_INLINE_ void _update_specular_shininess(const Color &p_transform, uint32_t *r_ss);
public:
TextureBindingID request_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, RS::CanvasItemTextureFilter p_filter, RS::CanvasItemTextureRepeat p_repeat, RID p_multimesh);
void free_texture_binding(TextureBindingID p_binding);
PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>());
void free_polygon(PolygonID p_polygon);
RID light_create();
void light_set_texture(RID p_rid, RID p_texture);
void light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution);
void light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders);
RID occluder_polygon_create();
void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines);
void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode);
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform);
void canvas_debug_viewport_shadows(Light *p_lights_with_shadow){};
void draw_window_margins(int *p_margins, RID *p_margin_textures) {}
void set_time(double p_time);
void update();
bool free(RID p_rid);
RasterizerCanvasRD(RasterizerStorageRD *p_storage);
~RasterizerCanvasRD();
};
#endif // RASTERIZER_CANVAS_RD_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,457 @@
/*************************************************************************/
/* rasterizer_effects_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef RASTERIZER_EFFECTS_RD_H
#define RASTERIZER_EFFECTS_RD_H
#include "core/math/camera_matrix.h"
#include "servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h"
#include "servers/rendering/rasterizer_rd/shaders/blur.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/bokeh_dof.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/copy.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/cubemap_downsampler.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/cubemap_filter.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/cubemap_roughness.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/luminance_reduce.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/roughness_limiter.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/ssao.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/ssao_minify.glsl.gen.h"
#include "servers/rendering/rasterizer_rd/shaders/tonemap.glsl.gen.h"
#include "servers/rendering_server.h"
class RasterizerEffectsRD {
enum BlurMode {
BLUR_MODE_GAUSSIAN_BLUR,
BLUR_MODE_GAUSSIAN_GLOW,
BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
BLUR_MODE_DOF_NEAR_LOW,
BLUR_MODE_DOF_NEAR_MEDIUM,
BLUR_MODE_DOF_NEAR_HIGH,
BLUR_MODE_DOF_NEAR_MERGE_LOW,
BLUR_MODE_DOF_NEAR_MERGE_MEDIUM,
BLUR_MODE_DOF_NEAR_MERGE_HIGH,
BLUR_MODE_DOF_FAR_LOW,
BLUR_MODE_DOF_FAR_MEDIUM,
BLUR_MODE_DOF_FAR_HIGH,
BLUR_MODE_SSAO_MERGE,
BLUR_MODE_SIMPLY_COPY,
BLUR_MODE_MIPMAP,
BLUR_MODE_MAX,
};
enum {
BLUR_FLAG_HORIZONTAL = (1 << 0),
BLUR_FLAG_USE_BLUR_SECTION = (1 << 1),
BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 2),
BLUR_FLAG_DOF_NEAR_FIRST_TAP = (1 << 3),
BLUR_FLAG_GLOW_FIRST_PASS = (1 << 4),
BLUR_FLAG_FLIP_Y = (1 << 5),
BLUR_COPY_FORCE_LUMINANCE = (1 << 6)
};
struct BlurPushConstant {
float section[4];
float pixel_size[2];
uint32_t flags;
uint32_t pad;
//glow
float glow_strength;
float glow_bloom;
float glow_hdr_threshold;
float glow_hdr_scale;
float glow_exposure;
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_grey;
//dof
float dof_begin;
float dof_end;
float dof_radius;
float dof_pad;
float dof_dir[2];
float camera_z_far;
float camera_z_near;
float ssao_color[4];
};
struct Blur {
BlurPushConstant push_constant;
BlurShaderRD shader;
RID shader_version;
RenderPipelineVertexFormatCacheRD pipelines[BLUR_MODE_MAX];
} blur;
struct CubemapRoughnessPushConstant {
uint32_t face_id;
uint32_t sample_count;
float roughness;
uint32_t use_direct_write;
float face_size;
float pad[3];
};
struct CubemapRoughness {
CubemapRoughnessPushConstant push_constant;
CubemapRoughnessShaderRD shader;
RID shader_version;
RID pipeline;
} roughness;
enum TonemapMode {
TONEMAP_MODE_NORMAL,
TONEMAP_MODE_BICUBIC_GLOW_FILTER,
TONEMAP_MODE_MAX
};
struct TonemapPushConstant {
float bcs[3];
uint32_t use_bcs;
uint32_t use_glow;
uint32_t use_auto_exposure;
uint32_t use_color_correction;
uint32_t tonemapper;
uint32_t glow_texture_size[2];
float glow_intensity;
uint32_t glow_level_flags;
uint32_t glow_mode;
float exposure;
float white;
float auto_exposure_grey;
};
struct Tonemap {
TonemapPushConstant push_constant;
TonemapShaderRD shader;
RID shader_version;
RenderPipelineVertexFormatCacheRD pipelines[TONEMAP_MODE_MAX];
} tonemap;
enum LuminanceReduceMode {
LUMINANCE_REDUCE_READ,
LUMINANCE_REDUCE,
LUMINANCE_REDUCE_WRITE,
LUMINANCE_REDUCE_MAX
};
struct LuminanceReducePushConstant {
int32_t source_size[2];
float max_luminance;
float min_luminance;
float exposure_adjust;
float pad[3];
};
struct LuminanceReduce {
LuminanceReducePushConstant push_constant;
LuminanceReduceShaderRD shader;
RID shader_version;
RID pipelines[LUMINANCE_REDUCE_MAX];
} luminance_reduce;
struct CopyToDPPushConstant {
float bias;
float z_far;
float z_near;
uint32_t z_flip;
};
enum CopyMode {
COPY_MODE_CUBE_TO_DP,
COPY_MODE_MAX
};
struct Copy {
CopyShaderRD shader;
RID shader_version;
RenderPipelineVertexFormatCacheRD pipelines[COPY_MODE_MAX];
} copy;
struct BokehPushConstant {
uint32_t size[2];
float z_far;
float z_near;
uint32_t orthogonal;
float blur_size;
float blur_scale;
uint32_t steps;
uint32_t blur_near_active;
float blur_near_begin;
float blur_near_end;
uint32_t blur_far_active;
float blur_far_begin;
float blur_far_end;
uint32_t second_pass;
uint32_t half_size;
uint32_t use_jitter;
float jitter_seed;
uint32_t pad[2];
};
enum BokehMode {
BOKEH_GEN_BLUR_SIZE,
BOKEH_GEN_BOKEH_BOX,
BOKEH_GEN_BOKEH_HEXAGONAL,
BOKEH_GEN_BOKEH_CIRCULAR,
BOKEH_COMPOSITE,
BOKEH_MAX
};
struct Bokeh {
BokehPushConstant push_constant;
BokehDofShaderRD shader;
RID shader_version;
RID pipelines[BOKEH_MAX];
} bokeh;
enum SSAOMode {
SSAO_MINIFY_FIRST,
SSAO_MINIFY_MIPMAP,
SSAO_GATHER_LOW,
SSAO_GATHER_MEDIUM,
SSAO_GATHER_HIGH,
SSAO_GATHER_ULTRA,
SSAO_GATHER_LOW_HALF,
SSAO_GATHER_MEDIUM_HALF,
SSAO_GATHER_HIGH_HALF,
SSAO_GATHER_ULTRA_HALF,
SSAO_BLUR_PASS,
SSAO_BLUR_PASS_HALF,
SSAO_BLUR_UPSCALE,
SSAO_MAX
};
struct SSAOMinifyPushConstant {
float pixel_size[2];
float z_far;
float z_near;
int32_t source_size[2];
uint32_t orthogonal;
uint32_t pad;
};
struct SSAOGatherPushConstant {
int32_t screen_size[2];
float z_far;
float z_near;
uint32_t orthogonal;
float intensity_div_r6;
float radius;
float bias;
float proj_info[4];
float pixel_size[2];
float proj_scale;
uint32_t pad;
};
struct SSAOBlurPushConstant {
float edge_sharpness;
int32_t filter_scale;
float z_far;
float z_near;
uint32_t orthogonal;
uint32_t pad[3];
int32_t axis[2];
int32_t screen_size[2];
};
struct SSAO {
SSAOMinifyPushConstant minify_push_constant;
SsaoMinifyShaderRD minify_shader;
RID minify_shader_version;
SSAOGatherPushConstant gather_push_constant;
SsaoShaderRD gather_shader;
RID gather_shader_version;
SSAOBlurPushConstant blur_push_constant;
SsaoBlurShaderRD blur_shader;
RID blur_shader_version;
RID pipelines[SSAO_MAX];
} ssao;
struct RoughnessLimiterPushConstant {
int32_t screen_size[2];
float curve;
uint32_t pad;
};
struct RoughnessLimiter {
RoughnessLimiterPushConstant push_constant;
RoughnessLimiterShaderRD shader;
RID shader_version;
RID pipeline;
} roughness_limiter;
struct CubemapDownsamplerPushConstant {
uint32_t face_size;
float pad[3];
};
struct CubemapDownsampler {
CubemapDownsamplerPushConstant push_constant;
CubemapDownsamplerShaderRD shader;
RID shader_version;
RID pipeline;
} cubemap_downsampler;
enum CubemapFilterMode {
FILTER_MODE_HIGH_QUALITY,
FILTER_MODE_LOW_QUALITY,
FILTER_MODE_HIGH_QUALITY_ARRAY,
FILTER_MODE_LOW_QUALITY_ARRAY,
FILTER_MODE_MAX,
};
struct CubemapFilter {
CubemapFilterShaderRD shader;
RID shader_version;
RID pipelines[FILTER_MODE_MAX];
RID uniform_set;
RID image_uniform_set;
RID coefficient_buffer;
bool use_high_quality;
} filter;
struct SkyPushConstant {
float orientation[12];
float proj[4];
float position[3];
float multiplier;
float time;
float pad[3];
};
RID default_sampler;
RID default_mipmap_sampler;
RID index_buffer;
RID index_array;
Map<RID, RID> texture_to_uniform_set_cache;
Map<RID, RID> image_to_uniform_set_cache;
Map<RID, RID> texture_to_compute_uniform_set_cache;
RID _get_uniform_set_from_image(RID p_texture);
RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
public:
//TODO must re-do most of the shaders in compute
void region_copy(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_region);
void copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y = false, bool p_force_luminance = false);
void gaussian_blur(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, const Rect2 &p_region);
void gaussian_glow(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
void cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void make_mipmap(RID p_source_rd_texture, RID p_framebuffer_half, const Vector2 &p_pixel_size);
void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, float p_z_near, float p_z_far, float p_bias, bool p_dp_flip);
void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
void bokeh_dof(RID p_base_texture, RID p_depth_texture, const Size2i &p_base_texture_size, RID p_secondary_texture, RID p_bokeh_texture1, RID p_bokeh_texture2, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
struct TonemapSettings {
bool use_glow = false;
enum GlowMode {
GLOW_MODE_ADD,
GLOW_MODE_SCREEN,
GLOW_MODE_SOFTLIGHT,
GLOW_MODE_REPLACE,
GLOW_MODE_MIX
};
GlowMode glow_mode = GLOW_MODE_ADD;
float glow_intensity = 1.0;
uint32_t glow_level_flags = 0;
Vector2i glow_texture_size;
bool glow_use_bicubic_upscale = false;
RID glow_texture;
RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR;
float exposure = 1.0;
float white = 1.0;
bool use_auto_exposure = false;
float auto_exposure_grey = 0.5;
RID exposure_texture;
bool use_bcs = false;
float brightness = 1.0;
float contrast = 1.0;
float saturation = 1.0;
bool use_color_correction = false;
RID color_correction_texture;
};
void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, const Size2i &p_depth_buffer_size, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao1, bool p_half_size, RID p_ao2, RID p_upscale_buffer, float p_intensity, float p_radius, float p_bias, const CameraMatrix &p_projection, RS::EnvironmentSSAOQuality p_quality, RS::EnvironmentSSAOBlur p_blur, float p_edge_sharpness);
void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array);
void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_lights, RenderPipelineVertexFormatCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position);
RasterizerEffectsRD();
~RasterizerEffectsRD();
};
#endif // !RASTERIZER_EFFECTS_RD_H

View File

@@ -0,0 +1,177 @@
/*************************************************************************/
/* rasterizer_rd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "rasterizer_rd.h"
void RasterizerRD::prepare_for_blitting_render_targets() {
RD::get_singleton()->prepare_screen_for_drawing();
}
void RasterizerRD::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) {
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen);
for (int i = 0; i < p_amount; i++) {
RID texture = storage->render_target_get_texture(p_render_targets[i].render_target);
ERR_CONTINUE(texture.is_null());
RID rd_texture = storage->texture_get_rd_texture(texture);
ERR_CONTINUE(rd_texture.is_null());
if (!render_target_descriptors.has(rd_texture) || !RD::get_singleton()->uniform_set_is_valid(render_target_descriptors[rd_texture])) {
Vector<RD::Uniform> uniforms;
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u.binding = 0;
u.ids.push_back(copy_viewports_sampler);
u.ids.push_back(rd_texture);
uniforms.push_back(u);
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, copy_viewports_rd_shader, 0);
render_target_descriptors[rd_texture] = uniform_set;
}
Size2 screen_size(RD::get_singleton()->screen_get_width(p_screen), RD::get_singleton()->screen_get_height(p_screen));
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_viewports_rd_pipeline);
RD::get_singleton()->draw_list_bind_index_array(draw_list, copy_viewports_rd_array);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0);
float push_constant[4] = {
p_render_targets[i].rect.position.x / screen_size.width,
p_render_targets[i].rect.position.y / screen_size.height,
p_render_targets[i].rect.size.width / screen_size.width,
p_render_targets[i].rect.size.height / screen_size.height,
};
RD::get_singleton()->draw_list_set_push_constant(draw_list, push_constant, 4 * sizeof(float));
RD::get_singleton()->draw_list_draw(draw_list, true);
}
RD::get_singleton()->draw_list_end();
}
void RasterizerRD::begin_frame(double frame_step) {
frame++;
time += frame_step;
canvas->set_time(time);
scene->set_time(time, frame_step);
}
void RasterizerRD::end_frame(bool p_swap_buffers) {
#ifndef _MSC_VER
#warning TODO: likely passa bool to swap buffers to avoid display?
#endif
RD::get_singleton()->swap_buffers(); //probably should pass some bool to avoid display?
}
void RasterizerRD::initialize() {
{ //create framebuffer copy shader
RenderingDevice::ShaderStageData vert;
vert.shader_stage = RenderingDevice::SHADER_STAGE_VERTEX;
vert.spir_v = RenderingDevice::get_singleton()->shader_compile_from_source(RenderingDevice::SHADER_STAGE_VERTEX,
"#version 450\n"
"layout(push_constant, binding = 0, std140) uniform Pos { vec4 dst_rect; } pos;\n"
"layout(location =0) out vec2 uv;\n"
"void main() { \n"
" vec2 base_arr[4] = vec2[](vec2(0.0,0.0),vec2(0.0,1.0),vec2(1.0,1.0),vec2(1.0,0.0));\n"
" uv = base_arr[gl_VertexIndex];\n"
" vec2 vtx = pos.dst_rect.xy+uv*pos.dst_rect.zw;\n"
" gl_Position = vec4(vtx * 2.0 - 1.0,0.0,1.0);\n"
"}\n");
RenderingDevice::ShaderStageData frag;
frag.shader_stage = RenderingDevice::SHADER_STAGE_FRAGMENT;
frag.spir_v = RenderingDevice::get_singleton()->shader_compile_from_source(RenderingDevice::SHADER_STAGE_FRAGMENT,
"#version 450\n"
"layout (location = 0) in vec2 uv;\n"
"layout (location = 0) out vec4 color;\n"
"layout (binding = 0) uniform sampler2D src_rt;\n"
"void main() { color=texture(src_rt,uv); }\n");
Vector<RenderingDevice::ShaderStageData> source;
source.push_back(vert);
source.push_back(frag);
String error;
copy_viewports_rd_shader = RD::get_singleton()->shader_create(source);
if (!copy_viewports_rd_shader.is_valid()) {
print_line("Failed compilation: " + error);
}
}
{ //create index array for copy shader
Vector<uint8_t> pv;
pv.resize(6 * 4);
{
uint8_t *w = pv.ptrw();
int *p32 = (int *)w;
p32[0] = 0;
p32[1] = 1;
p32[2] = 2;
p32[3] = 0;
p32[4] = 2;
p32[5] = 3;
}
copy_viewports_rd_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv);
copy_viewports_rd_array = RD::get_singleton()->index_array_create(copy_viewports_rd_index_buffer, 0, 6);
}
{ //pipeline
copy_viewports_rd_pipeline = RD::get_singleton()->render_pipeline_create(copy_viewports_rd_shader, RD::get_singleton()->screen_get_framebuffer_format(), RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RenderingDevice::PipelineColorBlendState::create_disabled(), 0);
}
{ // sampler
copy_viewports_sampler = RD::get_singleton()->sampler_create(RD::SamplerState());
}
}
ThreadWorkPool RasterizerRD::thread_work_pool;
uint32_t RasterizerRD::frame = 1;
void RasterizerRD::finalize() {
thread_work_pool.finish();
memdelete(scene);
memdelete(canvas);
memdelete(storage);
//only need to erase these, the rest are erased by cascade
RD::get_singleton()->free(copy_viewports_rd_index_buffer);
RD::get_singleton()->free(copy_viewports_rd_shader);
RD::get_singleton()->free(copy_viewports_sampler);
}
RasterizerRD::RasterizerRD() {
thread_work_pool.init();
time = 0;
storage = memnew(RasterizerStorageRD);
canvas = memnew(RasterizerCanvasRD(storage));
scene = memnew(RasterizerSceneHighEndRD(storage));
}

View File

@@ -0,0 +1,95 @@
/*************************************************************************/
/* rasterizer_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef RASTERIZER_RD_H
#define RASTERIZER_RD_H
#include "core/os/os.h"
#include "core/thread_work_pool.h"
#include "servers/rendering/rasterizer.h"
#include "servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h"
#include "servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h"
#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h"
class RasterizerRD : public Rasterizer {
protected:
RasterizerCanvasRD *canvas;
RasterizerStorageRD *storage;
RasterizerSceneHighEndRD *scene;
RID copy_viewports_rd_shader;
RID copy_viewports_rd_pipeline;
RID copy_viewports_rd_index_buffer;
RID copy_viewports_rd_array;
RID copy_viewports_sampler;
Map<RID, RID> render_target_descriptors;
double time;
static uint32_t frame;
public:
RasterizerStorage *get_storage() { return storage; }
RasterizerCanvas *get_canvas() { return canvas; }
RasterizerScene *get_scene() { return scene; }
void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {}
void initialize();
void begin_frame(double frame_step);
void prepare_for_blitting_render_targets();
void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount);
void end_frame(bool p_swap_buffers);
void finalize();
static _ALWAYS_INLINE_ uint64_t get_frame_number() { return frame; }
static Error is_viable() {
return OK;
}
static Rasterizer *_create_current() {
return memnew(RasterizerRD);
}
static void make_current() {
_create_func = _create_current;
}
virtual bool is_low_end() const { return false; }
static ThreadWorkPool thread_work_pool;
RasterizerRD();
~RasterizerRD() {}
};
#endif // RASTERIZER_RD_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,585 @@
/*************************************************************************/
/* rasterizer_scene_high_end_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef RASTERIZER_SCENE_HIGHEND_RD_H
#define RASTERIZER_SCENE_HIGHEND_RD_H
#include "servers/rendering/rasterizer_rd/light_cluster_builder.h"
#include "servers/rendering/rasterizer_rd/rasterizer_scene_rd.h"
#include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h"
#include "servers/rendering/rasterizer_rd/render_pipeline_vertex_format_cache_rd.h"
#include "servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl.gen.h"
class RasterizerSceneHighEndRD : public RasterizerSceneRD {
enum {
SCENE_UNIFORM_SET = 0,
RADIANCE_UNIFORM_SET = 1,
VIEW_DEPENDANT_UNIFORM_SET = 2,
RENDER_BUFFERS_UNIFORM_SET = 3,
TRANSFORMS_UNIFORM_SET = 4,
MATERIAL_UNIFORM_SET = 5
};
/* Scene Shader */
enum ShaderVersion {
SHADER_VERSION_DEPTH_PASS,
SHADER_VERSION_DEPTH_PASS_DP,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
SHADER_VERSION_COLOR_PASS,
SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR,
SHADER_VERSION_VCT_COLOR_PASS,
SHADER_VERSION_VCT_COLOR_PASS_WITH_SEPARATE_SPECULAR,
SHADER_VERSION_LIGHTMAP_COLOR_PASS,
SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR,
SHADER_VERSION_MAX
};
struct {
SceneHighEndShaderRD scene_shader;
ShaderCompilerRD compiler;
} shader;
RasterizerStorageRD *storage;
/* Material */
struct ShaderData : public RasterizerStorageRD::ShaderData {
enum BlendMode { //used internally
BLEND_MODE_MIX,
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
};
enum DepthDraw {
DEPTH_DRAW_DISABLED,
DEPTH_DRAW_OPAQUE,
DEPTH_DRAW_ALWAYS
};
enum DepthTest {
DEPTH_TEST_DISABLED,
DEPTH_TEST_ENABLED
};
enum Cull {
CULL_DISABLED,
CULL_FRONT,
CULL_BACK
};
enum CullVariant {
CULL_VARIANT_NORMAL,
CULL_VARIANT_REVERSED,
CULL_VARIANT_DOUBLE_SIDED,
CULL_VARIANT_MAX
};
bool valid;
RID version;
uint32_t vertex_input_mask;
RenderPipelineVertexFormatCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX];
String path;
Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
Map<StringName, RID> default_texture_params;
DepthDraw depth_draw;
DepthTest depth_test;
bool uses_point_size;
bool uses_alpha;
bool uses_blend_alpha;
bool uses_depth_pre_pass;
bool uses_discard;
bool uses_roughness;
bool uses_normal;
bool unshaded;
bool uses_vertex;
bool uses_sss;
bool uses_screen_texture;
bool uses_depth_texture;
bool uses_normal_texture;
bool uses_time;
bool writes_modelview_or_projection;
bool uses_world_coordinates;
uint64_t last_pass = 0;
uint32_t index = 0;
virtual void set_code(const String &p_Code);
virtual void set_default_texture_param(const StringName &p_name, RID p_texture);
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
virtual bool is_param_texture(const StringName &p_param) const;
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual Variant get_default_parameter(const StringName &p_parameter) const;
ShaderData();
virtual ~ShaderData();
};
RasterizerStorageRD::ShaderData *_create_shader_func();
static RasterizerStorageRD::ShaderData *_create_shader_funcs() {
return static_cast<RasterizerSceneHighEndRD *>(singleton)->_create_shader_func();
}
struct MaterialData : public RasterizerStorageRD::MaterialData {
uint64_t last_frame;
ShaderData *shader_data;
RID uniform_buffer;
RID uniform_set;
Vector<RID> texture_cache;
Vector<uint8_t> ubo_data;
uint64_t last_pass = 0;
uint32_t index = 0;
RID next_pass;
uint8_t priority;
virtual void set_render_priority(int p_priority);
virtual void set_next_pass(RID p_pass);
virtual void update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual ~MaterialData();
};
RasterizerStorageRD::MaterialData *_create_material_func(ShaderData *p_shader);
static RasterizerStorageRD::MaterialData *_create_material_funcs(RasterizerStorageRD::ShaderData *p_shader) {
return static_cast<RasterizerSceneHighEndRD *>(singleton)->_create_material_func(static_cast<ShaderData *>(p_shader));
}
/* Push Constant */
struct PushConstant {
uint32_t index;
uint32_t pad[3];
};
/* Framebuffer */
struct RenderBufferDataHighEnd : public RenderBufferData {
//for rendering, may be MSAAd
RID color;
RID depth;
RID specular;
RID normal_buffer;
RID roughness_buffer;
RID depth_fb;
RID depth_normal_fb;
RID depth_normal_roughness_fb;
RID color_fb;
RID color_specular_fb;
int width, height;
void ensure_specular();
void clear();
virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa);
RID uniform_set;
~RenderBufferDataHighEnd();
};
virtual RenderBufferData *_create_render_buffer_data();
void _allocate_normal_texture(RenderBufferDataHighEnd *rb);
void _allocate_roughness_texture(RenderBufferDataHighEnd *rb);
RID shadow_sampler;
RID render_base_uniform_set;
RID view_dependant_uniform_set;
virtual void _base_uniforms_changed();
void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb);
virtual void _render_buffers_uniform_set_changed(RID p_render_buffers);
virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers);
virtual RID _render_buffers_get_normal_texture(RID p_render_buffers);
void _update_render_base_uniform_set();
void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas);
void _update_render_buffers_uniform_set(RID p_render_buffers);
/* Scene State UBO */
struct ReflectionData { //should always be 128 bytes
float box_extents[3];
float index;
float box_offset[3];
uint32_t mask;
float params[4]; // intensity, 0, interior , boxproject
float ambient[4]; // ambient color, energy
float local_matrix[16]; // up to here for spot and omni, rest is for directional
};
struct LightData {
float position[3];
float inv_radius;
float direction[3];
uint16_t attenuation_energy[2]; //16 bits attenuation, then energy
uint8_t color_specular[4]; //rgb color, a specular (8 bit unorm)
uint16_t cone_attenuation_angle[2]; // attenuation and angle, (16bit float)
uint32_t mask;
uint8_t shadow_color_enabled[4]; //shadow rgb color, a>0.5 enabled (8bit unorm)
float atlas_rect[4]; // in omni, used for atlas uv, in spot, used for projector uv
float shadow_matrix[16];
};
struct DirectionalLightData {
float direction[3];
float energy;
float color[3];
float specular;
float shadow_color[3];
uint32_t mask;
uint32_t blend_splits;
uint32_t shadow_enabled;
float fade_from;
float fade_to;
float shadow_split_offsets[4];
float shadow_matrices[4][16];
};
struct GIProbeData {
float xform[16];
float bounds[3];
float dynamic_range;
float bias;
float normal_bias;
uint32_t blend_ambient;
uint32_t texture_slot;
float anisotropy_strength;
float ao;
float ao_size;
uint32_t pad[1];
};
enum {
INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_SHIFT = 16,
INSTANCE_DATA_FLAGS_MULTIMESH_STRIDE_MASK = 0x7,
INSTANCE_DATA_FLAG_SKELETON = 1 << 19,
};
struct InstanceData {
float transform[16];
float normal_transform[16];
uint32_t flags;
uint32_t instance_ofs; //instance_offset in instancing/skeleton buffer
uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap)
uint32_t mask;
};
struct SceneState {
struct UBO {
float projection_matrix[16];
float inv_projection_matrix[16];
float camera_matrix[16];
float inv_camera_matrix[16];
float viewport_size[2];
float screen_pixel_size[2];
float shadow_z_offset;
float shadow_z_slope_scale;
float time;
float reflection_multiplier;
float ambient_light_color_energy[4];
float ambient_color_sky_mix;
uint32_t use_ambient_light;
uint32_t use_ambient_cubemap;
uint32_t use_reflection_cubemap;
float radiance_inverse_xform[12];
float shadow_atlas_pixel_size[2];
float directional_shadow_pixel_size[2];
uint32_t directional_light_count;
float dual_paraboloid_side;
float z_far;
float z_near;
uint32_t ssao_enabled;
float ssao_light_affect;
float ssao_ao_affect;
uint32_t roughness_limiter_enabled;
float ao_color[4];
};
UBO ubo;
RID uniform_buffer;
ReflectionData *reflections;
uint32_t max_reflections;
RID reflection_buffer;
uint32_t max_reflection_probes_per_instance;
GIProbeData *gi_probes;
uint32_t max_gi_probes;
RID gi_probe_buffer;
uint32_t max_gi_probe_probes_per_instance;
LightData *lights;
uint32_t max_lights;
RID light_buffer;
DirectionalLightData *directional_lights;
uint32_t max_directional_lights;
RID directional_light_buffer;
RID instance_buffer;
InstanceData *instances;
uint32_t max_instances;
bool used_screen_texture = false;
bool used_normal_texture = false;
bool used_depth_texture = false;
bool used_sss = false;
uint32_t current_shader_index = 0;
uint32_t current_material_index = 0;
} scene_state;
/* Render List */
struct RenderList {
int max_elements;
struct Element {
RasterizerScene::InstanceBase *instance;
MaterialData *material;
union {
struct {
//from least significant to most significant in sort, TODO: should be endian swapped on big endian
uint64_t geometry_index : 20;
uint64_t material_index : 15;
uint64_t shader_index : 12;
uint64_t uses_instancing : 1;
uint64_t uses_vct : 1;
uint64_t uses_lightmap : 1;
uint64_t depth_layer : 4;
uint64_t priority : 8;
};
uint64_t sort_key;
};
uint32_t surface_index;
};
Element *base_elements;
Element **elements;
int element_count;
int alpha_element_count;
void clear() {
element_count = 0;
alpha_element_count = 0;
}
//should eventually be replaced by radix
struct SortByKey {
_FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const {
return A->sort_key < B->sort_key;
}
};
void sort_by_key(bool p_alpha) {
SortArray<Element *, SortByKey> sorter;
if (p_alpha) {
sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count);
} else {
sorter.sort(elements, element_count);
}
}
struct SortByDepth {
_FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const {
return A->instance->depth < B->instance->depth;
}
};
void sort_by_depth(bool p_alpha) { //used for shadows
SortArray<Element *, SortByDepth> sorter;
if (p_alpha) {
sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count);
} else {
sorter.sort(elements, element_count);
}
}
struct SortByReverseDepthAndPriority {
_FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const {
uint32_t layer_A = uint32_t(A->priority);
uint32_t layer_B = uint32_t(B->priority);
if (layer_A == layer_B) {
return A->instance->depth > B->instance->depth;
} else {
return layer_A < layer_B;
}
}
};
void sort_by_reverse_depth_and_priority(bool p_alpha) { //used for alpha
SortArray<Element *, SortByReverseDepthAndPriority> sorter;
if (p_alpha) {
sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count);
} else {
sorter.sort(elements, element_count);
}
}
_FORCE_INLINE_ Element *add_element() {
if (element_count + alpha_element_count >= max_elements)
return NULL;
elements[element_count] = &base_elements[element_count];
return elements[element_count++];
}
_FORCE_INLINE_ Element *add_alpha_element() {
if (element_count + alpha_element_count >= max_elements)
return NULL;
int idx = max_elements - alpha_element_count - 1;
elements[idx] = &base_elements[idx];
alpha_element_count++;
return elements[idx];
}
void init() {
element_count = 0;
alpha_element_count = 0;
elements = memnew_arr(Element *, max_elements);
base_elements = memnew_arr(Element, max_elements);
for (int i = 0; i < max_elements; i++)
elements[i] = &base_elements[i]; // assign elements
}
RenderList() {
max_elements = 0;
}
~RenderList() {
memdelete_arr(elements);
memdelete_arr(base_elements);
}
};
RenderList render_list;
static RasterizerSceneHighEndRD *singleton;
uint64_t render_pass;
double time;
RID default_shader;
RID default_material;
RID overdraw_material_shader;
RID overdraw_material;
RID wireframe_material_shader;
RID wireframe_material;
RID default_shader_rd;
RID default_radiance_uniform_set;
RID default_render_buffers_uniform_set;
RID default_vec4_xform_buffer;
RID default_vec4_xform_uniform_set;
LightClusterBuilder cluster_builder;
enum PassMode {
PASS_MODE_COLOR,
PASS_MODE_COLOR_SPECULAR,
PASS_MODE_COLOR_TRANSPARENT,
PASS_MODE_SHADOW,
PASS_MODE_SHADOW_DP,
PASS_MODE_DEPTH,
PASS_MODE_DEPTH_NORMAL,
PASS_MODE_DEPTH_NORMAL_ROUGHNESS,
PASS_MODE_DEPTH_MATERIAL,
};
void _setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers = false);
void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows);
void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment);
void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth);
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set);
_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index);
_FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index);
void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi);
protected:
virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip);
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
public:
virtual void set_time(double p_time, double p_step);
virtual bool free(RID p_rid);
RasterizerSceneHighEndRD(RasterizerStorageRD *p_storage);
~RasterizerSceneHighEndRD();
};
#endif // RASTERIZER_SCENE_HIGHEND_RD_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
/*************************************************************************/
/* render_pipeline_vertex_format_cache_rd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "render_pipeline_vertex_format_cache_rd.h"
#include "core/os/memory.h"
RID RenderPipelineVertexFormatCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id) {
RD::PipelineMultisampleState multisample_state_version = multisample_state;
multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id);
RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, rasterization_state, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags);
ERR_FAIL_COND_V(pipeline.is_null(), RID());
versions = (Version *)memrealloc(versions, sizeof(Version) * (version_count + 1));
versions[version_count].framebuffer_id = p_framebuffer_format_id;
versions[version_count].vertex_id = p_vertex_format_id;
versions[version_count].pipeline = pipeline;
version_count++;
return pipeline;
}
void RenderPipelineVertexFormatCacheRD::_clear() {
if (versions) {
for (uint32_t i = 0; i < version_count; i++) {
//shader may be gone, so this may not be valid
if (RD::get_singleton()->render_pipeline_is_valid(versions[i].pipeline)) {
RD::get_singleton()->free(versions[i].pipeline);
}
}
version_count = 0;
memfree(versions);
versions = NULL;
}
}
void RenderPipelineVertexFormatCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags) {
ERR_FAIL_COND(p_shader.is_null());
_clear();
shader = p_shader;
input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(p_shader);
render_primitive = p_primitive;
rasterization_state = p_rasterization_state;
multisample_state = p_multisample;
depth_stencil_state = p_depth_stencil_state;
blend_state = p_blend_state;
dynamic_state_flags = p_dynamic_state_flags;
}
void RenderPipelineVertexFormatCacheRD::update_shader(RID p_shader) {
ERR_FAIL_COND(p_shader.is_null());
_clear();
setup(p_shader, render_primitive, rasterization_state, multisample_state, depth_stencil_state, blend_state, dynamic_state_flags);
}
void RenderPipelineVertexFormatCacheRD::clear() {
_clear();
shader = RID(); //clear shader
input_mask = 0;
}
RenderPipelineVertexFormatCacheRD::RenderPipelineVertexFormatCacheRD() {
version_count = 0;
versions = NULL;
input_mask = 0;
}
RenderPipelineVertexFormatCacheRD::~RenderPipelineVertexFormatCacheRD() {
_clear();
}

View File

@@ -0,0 +1,96 @@
/*************************************************************************/
/* render_pipeline_vertex_format_cache_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef RENDER_PIPELINE_CACHE_RD_H
#define RENDER_PIPELINE_CACHE_RD_H
#include "core/spin_lock.h"
#include "servers/rendering/rendering_device.h"
class RenderPipelineVertexFormatCacheRD {
SpinLock spin_lock;
RID shader;
uint32_t input_mask;
RD::RenderPrimitive render_primitive;
RD::PipelineRasterizationState rasterization_state;
RD::PipelineMultisampleState multisample_state;
RD::PipelineDepthStencilState depth_stencil_state;
RD::PipelineColorBlendState blend_state;
int dynamic_state_flags;
struct Version {
RD::VertexFormatID vertex_id;
RD::FramebufferFormatID framebuffer_id;
RID pipeline;
};
Version *versions;
uint32_t version_count;
RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id);
void _clear();
public:
void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0);
void update_shader(RID p_shader);
_FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id) {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V_MSG(shader.is_null(), RID(),
"Attempted to use an unused shader variant (shader is null),");
#endif
spin_lock.lock();
RID result;
for (uint32_t i = 0; i < version_count; i++) {
if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id) {
result = versions[i].pipeline;
spin_lock.unlock();
return result;
}
}
result = _generate_version(p_vertex_format_id, p_framebuffer_format_id);
spin_lock.unlock();
return result;
}
_FORCE_INLINE_ uint32_t get_vertex_input_mask() const {
return input_mask;
}
void clear();
RenderPipelineVertexFormatCacheRD();
~RenderPipelineVertexFormatCacheRD();
};
#endif // RENDER_PIPELINE_CACHE_RD_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
/*************************************************************************/
/* shader_compiler_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SHADER_COMPILER_RD_H
#define SHADER_COMPILER_RD_H
#include "core/pair.h"
#include "servers/rendering/shader_language.h"
#include "servers/rendering/shader_types.h"
#include "servers/rendering_server.h"
class ShaderCompilerRD {
public:
struct IdentifierActions {
Map<StringName, Pair<int *, int>> render_mode_values;
Map<StringName, bool *> render_mode_flags;
Map<StringName, bool *> usage_flag_pointers;
Map<StringName, bool *> write_flag_pointers;
Map<StringName, ShaderLanguage::ShaderNode::Uniform> *uniforms;
};
struct GeneratedCode {
Vector<String> defines;
struct Texture {
StringName name;
ShaderLanguage::DataType type;
ShaderLanguage::ShaderNode::Uniform::Hint hint;
ShaderLanguage::TextureFilter filter;
ShaderLanguage::TextureRepeat repeat;
};
Vector<Texture> texture_uniforms;
Vector<uint32_t> uniform_offsets;
uint32_t uniform_total_size;
String uniforms;
String vertex_global;
String vertex;
String fragment_global;
String fragment;
String light;
bool uses_fragment_time;
bool uses_vertex_time;
};
struct DefaultIdentifierActions {
Map<StringName, String> renames;
Map<StringName, String> render_mode_defines;
Map<StringName, String> usage_defines;
Map<StringName, String> custom_samplers;
ShaderLanguage::TextureFilter default_filter;
ShaderLanguage::TextureRepeat default_repeat;
String sampler_array_name;
int base_texture_binding_index = 0;
int texture_layout_set = 0;
String base_uniform_string;
uint32_t base_varying_index = 0;
};
private:
ShaderLanguage parser;
String _get_sampler_name(ShaderLanguage::TextureFilter p_filter, ShaderLanguage::TextureRepeat p_repeat);
void _dump_function_deps(const ShaderLanguage::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, String &r_to_add, Set<StringName> &added);
String _dump_node_code(const ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning);
const ShaderLanguage::ShaderNode *shader;
const ShaderLanguage::FunctionNode *function;
StringName current_func_name;
StringName vertex_name;
StringName fragment_name;
StringName light_name;
StringName time_name;
Set<StringName> texture_functions;
Set<StringName> used_name_defines;
Set<StringName> used_flag_pointers;
Set<StringName> used_rmode_defines;
Set<StringName> internal_functions;
DefaultIdentifierActions actions;
public:
Error compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code);
void initialize(DefaultIdentifierActions p_actions);
ShaderCompilerRD();
};
#endif // SHADERCOMPILERRD_H

View File

@@ -0,0 +1,495 @@
/*************************************************************************/
/* shader_rd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "shader_rd.h"
#include "core/string_builder.h"
#include "rasterizer_rd.h"
#include "servers/rendering/rendering_device.h"
void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) {
name = p_name;
//split vertex and shader code (thank you, shader compiler programmers from you know what company).
if (p_vertex_code) {
String defines_tag = "\nVERSION_DEFINES";
String globals_tag = "\nVERTEX_SHADER_GLOBALS";
String material_tag = "\nMATERIAL_UNIFORMS";
String code_tag = "\nVERTEX_SHADER_CODE";
String code = p_vertex_code;
int cpos = code.find(defines_tag);
if (cpos != -1) {
vertex_codev = code.substr(0, cpos).ascii();
code = code.substr(cpos + defines_tag.length(), code.length());
}
cpos = code.find(material_tag);
if (cpos == -1) {
vertex_code0 = code.ascii();
} else {
vertex_code0 = code.substr(0, cpos).ascii();
code = code.substr(cpos + material_tag.length(), code.length());
cpos = code.find(globals_tag);
if (cpos == -1) {
vertex_code1 = code.ascii();
} else {
vertex_code1 = code.substr(0, cpos).ascii();
String code2 = code.substr(cpos + globals_tag.length(), code.length());
cpos = code2.find(code_tag);
if (cpos == -1) {
vertex_code2 = code2.ascii();
} else {
vertex_code2 = code2.substr(0, cpos).ascii();
vertex_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii();
}
}
}
}
if (p_fragment_code) {
String defines_tag = "\nVERSION_DEFINES";
String globals_tag = "\nFRAGMENT_SHADER_GLOBALS";
String material_tag = "\nMATERIAL_UNIFORMS";
String code_tag = "\nFRAGMENT_SHADER_CODE";
String light_code_tag = "\nLIGHT_SHADER_CODE";
String code = p_fragment_code;
int cpos = code.find(defines_tag);
if (cpos != -1) {
fragment_codev = code.substr(0, cpos).ascii();
code = code.substr(cpos + defines_tag.length(), code.length());
}
cpos = code.find(material_tag);
if (cpos == -1) {
fragment_code0 = code.ascii();
} else {
fragment_code0 = code.substr(0, cpos).ascii();
//print_line("CODE0:\n"+String(fragment_code0.get_data()));
code = code.substr(cpos + material_tag.length(), code.length());
cpos = code.find(globals_tag);
if (cpos == -1) {
fragment_code1 = code.ascii();
} else {
fragment_code1 = code.substr(0, cpos).ascii();
//print_line("CODE1:\n"+String(fragment_code1.get_data()));
String code2 = code.substr(cpos + globals_tag.length(), code.length());
cpos = code2.find(light_code_tag);
if (cpos == -1) {
fragment_code2 = code2.ascii();
} else {
fragment_code2 = code2.substr(0, cpos).ascii();
//print_line("CODE2:\n"+String(fragment_code2.get_data()));
String code3 = code2.substr(cpos + light_code_tag.length(), code2.length());
cpos = code3.find(code_tag);
if (cpos == -1) {
fragment_code3 = code3.ascii();
} else {
fragment_code3 = code3.substr(0, cpos).ascii();
//print_line("CODE3:\n"+String(fragment_code3.get_data()));
fragment_code4 = code3.substr(cpos + code_tag.length(), code3.length()).ascii();
//print_line("CODE4:\n"+String(fragment_code4.get_data()));
}
}
}
}
}
if (p_compute_code) {
is_compute = true;
String defines_tag = "\nVERSION_DEFINES";
String globals_tag = "\nCOMPUTE_SHADER_GLOBALS";
String material_tag = "\nMATERIAL_UNIFORMS";
String code_tag = "\nCOMPUTE_SHADER_CODE";
String code = p_compute_code;
int cpos = code.find(defines_tag);
if (cpos != -1) {
compute_codev = code.substr(0, cpos).ascii();
code = code.substr(cpos + defines_tag.length(), code.length());
}
cpos = code.find(material_tag);
if (cpos == -1) {
compute_code0 = code.ascii();
} else {
compute_code0 = code.substr(0, cpos).ascii();
code = code.substr(cpos + material_tag.length(), code.length());
cpos = code.find(globals_tag);
if (cpos == -1) {
compute_code1 = code.ascii();
} else {
compute_code1 = code.substr(0, cpos).ascii();
String code2 = code.substr(cpos + globals_tag.length(), code.length());
cpos = code2.find(code_tag);
if (cpos == -1) {
compute_code2 = code2.ascii();
} else {
compute_code2 = code2.substr(0, cpos).ascii();
compute_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii();
}
}
}
}
}
RID ShaderRD::version_create() {
//initialize() was never called
ERR_FAIL_COND_V(variant_defines.size() == 0, RID());
Version version;
version.dirty = true;
version.valid = false;
version.initialize_needed = true;
version.variants = NULL;
return version_owner.make_rid(version);
}
void ShaderRD::_clear_version(Version *p_version) {
//clear versions if they exist
if (p_version->variants) {
for (int i = 0; i < variant_defines.size(); i++) {
RD::get_singleton()->free(p_version->variants[i]);
}
memdelete_arr(p_version->variants);
p_version->variants = NULL;
}
}
void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
Vector<RD::ShaderStageData> stages;
String error;
String current_source;
RD::ShaderStage current_stage = RD::SHADER_STAGE_VERTEX;
bool build_ok = true;
if (!is_compute) {
//vertex stage
StringBuilder builder;
builder.append(vertex_codev.get_data()); // version info (if exists)
builder.append("\n"); //make sure defines begin at newline
builder.append(general_defines.get_data());
builder.append(variant_defines[p_variant].get_data());
for (int j = 0; j < p_version->custom_defines.size(); j++) {
builder.append(p_version->custom_defines[j].get_data());
}
builder.append(vertex_code0.get_data()); //first part of vertex
builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
builder.append(vertex_code1.get_data()); //second part of vertex
builder.append(p_version->vertex_globals.get_data()); // vertex globals
builder.append(vertex_code2.get_data()); //third part of vertex
builder.append(p_version->vertex_code.get_data()); // code
builder.append(vertex_code3.get_data()); //fourth of vertex
current_source = builder.as_string();
RD::ShaderStageData stage;
stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_VERTEX, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
if (stage.spir_v.size() == 0) {
build_ok = false;
} else {
stage.shader_stage = RD::SHADER_STAGE_VERTEX;
stages.push_back(stage);
}
}
if (!is_compute && build_ok) {
//fragment stage
current_stage = RD::SHADER_STAGE_FRAGMENT;
StringBuilder builder;
builder.append(fragment_codev.get_data()); // version info (if exists)
builder.append("\n"); //make sure defines begin at newline
builder.append(general_defines.get_data());
builder.append(variant_defines[p_variant].get_data());
for (int j = 0; j < p_version->custom_defines.size(); j++) {
builder.append(p_version->custom_defines[j].get_data());
}
builder.append(fragment_code0.get_data()); //first part of fragment
builder.append(p_version->uniforms.get_data()); //uniforms (same for fragment and fragment)
builder.append(fragment_code1.get_data()); //first part of fragment
builder.append(p_version->fragment_globals.get_data()); // fragment globals
builder.append(fragment_code2.get_data()); //third part of fragment
builder.append(p_version->fragment_light.get_data()); // fragment light
builder.append(fragment_code3.get_data()); //fourth part of fragment
builder.append(p_version->fragment_code.get_data()); // fragment code
builder.append(fragment_code4.get_data()); //fourth part of fragment
current_source = builder.as_string();
RD::ShaderStageData stage;
stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_FRAGMENT, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
if (stage.spir_v.size() == 0) {
build_ok = false;
} else {
stage.shader_stage = RD::SHADER_STAGE_FRAGMENT;
stages.push_back(stage);
}
}
if (is_compute) {
//compute stage
current_stage = RD::SHADER_STAGE_COMPUTE;
StringBuilder builder;
builder.append(compute_codev.get_data()); // version info (if exists)
builder.append("\n"); //make sure defines begin at newline
builder.append(general_defines.get_data());
builder.append(variant_defines[p_variant].get_data());
for (int j = 0; j < p_version->custom_defines.size(); j++) {
builder.append(p_version->custom_defines[j].get_data());
}
builder.append(compute_code0.get_data()); //first part of compute
builder.append(p_version->uniforms.get_data()); //uniforms (same for compute and fragment)
builder.append(compute_code1.get_data()); //second part of compute
builder.append(p_version->compute_globals.get_data()); // compute globals
builder.append(compute_code2.get_data()); //third part of compute
builder.append(p_version->compute_code.get_data()); // code
builder.append(compute_code3.get_data()); //fourth of compute
current_source = builder.as_string();
RD::ShaderStageData stage;
stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
if (stage.spir_v.size() == 0) {
build_ok = false;
} else {
stage.shader_stage = RD::SHADER_STAGE_COMPUTE;
stages.push_back(stage);
}
}
if (!build_ok) {
MutexLock lock(variant_set_mutex); //properly print the errors
ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ").");
ERR_PRINT(error);
#ifdef DEBUG_ENABLED
ERR_PRINT("code:\n" + current_source.get_with_code_lines());
#endif
return;
}
RID shader = RD::get_singleton()->shader_create(stages);
{
MutexLock lock(variant_set_mutex);
p_version->variants[p_variant] = shader;
}
}
void ShaderRD::_compile_version(Version *p_version) {
_clear_version(p_version);
p_version->valid = false;
p_version->dirty = false;
p_version->variants = memnew_arr(RID, variant_defines.size());
#if 1
RasterizerRD::thread_work_pool.do_work(variant_defines.size(), this, &ShaderRD::_compile_variant, p_version);
#else
for (int i = 0; i < variant_defines.size(); i++) {
_compile_variant(i, p_version);
}
#endif
bool all_valid = true;
for (int i = 0; i < variant_defines.size(); i++) {
if (p_version->variants[i].is_null()) {
all_valid = false;
break;
}
}
if (!all_valid) {
//clear versions if they exist
for (int i = 0; i < variant_defines.size(); i++) {
if (!p_version->variants[i].is_null()) {
RD::get_singleton()->free(p_version->variants[i]);
}
}
memdelete_arr(p_version->variants);
p_version->variants = NULL;
return;
}
p_version->valid = true;
}
void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines) {
ERR_FAIL_COND(is_compute);
Version *version = version_owner.getornull(p_version);
ERR_FAIL_COND(!version);
version->vertex_globals = p_vertex_globals.utf8();
version->vertex_code = p_vertex_code.utf8();
version->fragment_light = p_fragment_light.utf8();
version->fragment_globals = p_fragment_globals.utf8();
version->fragment_code = p_fragment_code.utf8();
version->uniforms = p_uniforms.utf8();
version->custom_defines.clear();
for (int i = 0; i < p_custom_defines.size(); i++) {
version->custom_defines.push_back(p_custom_defines[i].utf8());
}
version->dirty = true;
if (version->initialize_needed) {
_compile_version(version);
version->initialize_needed = false;
}
}
void ShaderRD::version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines) {
ERR_FAIL_COND(!is_compute);
Version *version = version_owner.getornull(p_version);
ERR_FAIL_COND(!version);
version->compute_globals = p_compute_globals.utf8();
version->compute_code = p_compute_code.utf8();
version->uniforms = p_uniforms.utf8();
version->custom_defines.clear();
for (int i = 0; i < p_custom_defines.size(); i++) {
version->custom_defines.push_back(p_custom_defines[i].utf8());
}
version->dirty = true;
if (version->initialize_needed) {
_compile_version(version);
version->initialize_needed = false;
}
}
bool ShaderRD::version_is_valid(RID p_version) {
Version *version = version_owner.getornull(p_version);
ERR_FAIL_COND_V(!version, false);
if (version->dirty) {
_compile_version(version);
}
return version->valid;
}
bool ShaderRD::version_free(RID p_version) {
if (version_owner.owns(p_version)) {
Version *version = version_owner.getornull(p_version);
_clear_version(version);
version_owner.free(p_version);
} else {
return false;
}
return true;
}
void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String &p_general_defines) {
ERR_FAIL_COND(variant_defines.size());
ERR_FAIL_COND(p_variant_defines.size() == 0);
general_defines = p_general_defines.utf8();
for (int i = 0; i < p_variant_defines.size(); i++) {
variant_defines.push_back(p_variant_defines[i].utf8());
}
}
ShaderRD::~ShaderRD() {
List<RID> remaining;
version_owner.get_owned_list(&remaining);
if (remaining.size()) {
ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed");
while (remaining.size()) {
version_free(remaining.front()->get());
remaining.pop_front();
}
}
}

View File

@@ -0,0 +1,136 @@
/*************************************************************************/
/* shader_rd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SHADER_RD_H
#define SHADER_RD_H
#include "core/hash_map.h"
#include "core/map.h"
#include "core/os/mutex.h"
#include "core/rid_owner.h"
#include "core/variant.h"
#include <stdio.h>
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
class ShaderRD {
//versions
CharString general_defines;
Vector<CharString> variant_defines;
struct Version {
CharString uniforms;
CharString vertex_globals;
CharString vertex_code;
CharString compute_globals;
CharString compute_code;
CharString fragment_light;
CharString fragment_globals;
CharString fragment_code;
Vector<CharString> custom_defines;
RID *variants; //same size as version defines
bool valid;
bool dirty;
bool initialize_needed;
};
Mutex variant_set_mutex;
void _compile_variant(uint32_t p_variant, Version *p_version);
void _clear_version(Version *p_version);
void _compile_version(Version *p_version);
RID_Owner<Version> version_owner;
CharString fragment_codev; //for version and extensions
CharString fragment_code0;
CharString fragment_code1;
CharString fragment_code2;
CharString fragment_code3;
CharString fragment_code4;
CharString vertex_codev; //for version and extensions
CharString vertex_code0;
CharString vertex_code1;
CharString vertex_code2;
CharString vertex_code3;
bool is_compute = false;
CharString compute_codev; //for version and extensions
CharString compute_code0;
CharString compute_code1;
CharString compute_code2;
CharString compute_code3;
const char *name;
protected:
ShaderRD() {}
void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name);
public:
RID version_create();
void version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines);
void version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines);
_FORCE_INLINE_ RID version_get_shader(RID p_version, int p_variant) {
ERR_FAIL_INDEX_V(p_variant, variant_defines.size(), RID());
Version *version = version_owner.getornull(p_version);
ERR_FAIL_COND_V(!version, RID());
if (version->dirty) {
_compile_version(version);
}
if (!version->valid) {
return RID();
}
return version->variants[p_variant];
}
bool version_is_valid(RID p_version);
bool version_free(RID p_version);
void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = "");
virtual ~ShaderRD();
};
#endif

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python
Import('env')
if 'RD_GLSL' in env['BUILDERS']:
env.RD_GLSL('canvas.glsl');
env.RD_GLSL('canvas_occlusion.glsl');
env.RD_GLSL('blur.glsl');
env.RD_GLSL('cubemap_roughness.glsl');
env.RD_GLSL('cubemap_downsampler.glsl');
env.RD_GLSL('cubemap_filter.glsl');
env.RD_GLSL('scene_high_end.glsl');
env.RD_GLSL('sky.glsl');
env.RD_GLSL('tonemap.glsl');
env.RD_GLSL('copy.glsl');
env.RD_GLSL('giprobe.glsl');
env.RD_GLSL('giprobe_debug.glsl');
env.RD_GLSL('giprobe_sdf.glsl');
env.RD_GLSL('luminance_reduce.glsl');
env.RD_GLSL('bokeh_dof.glsl');
env.RD_GLSL('ssao.glsl');
env.RD_GLSL('ssao_minify.glsl');
env.RD_GLSL('ssao_blur.glsl');
env.RD_GLSL('roughness_limiter.glsl');

View File

@@ -0,0 +1,294 @@
/* clang-format off */
[vertex]
#version 450
VERSION_DEFINES
#include "blur_inc.glsl"
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
if (bool(blur.flags & FLAG_USE_BLUR_SECTION)) {
uv_interp = blur.section.xy + uv_interp * blur.section.zw;
}
gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
if (bool(blur.flags & FLAG_FLIP_Y)) {
uv_interp.y = 1.0 - uv_interp.y;
}
}
/* clang-format off */
[fragment]
#version 450
VERSION_DEFINES
#include "blur_inc.glsl"
layout(location = 0) in vec2 uv_interp;
/* clang-format on */
layout(set = 0, binding = 0) uniform sampler2D source_color;
#ifdef MODE_SSAO_MERGE
layout(set = 1, binding = 0) uniform sampler2D source_ssao;
#endif
#ifdef GLOW_USE_AUTO_EXPOSURE
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
#endif
layout(location = 0) out vec4 frag_color;
//DOF
#if defined(MODE_DOF_FAR_BLUR) || defined(MODE_DOF_NEAR_BLUR)
layout(set = 1, binding = 0) uniform sampler2D dof_source_depth;
#ifdef DOF_NEAR_BLUR_MERGE
layout(set = 2, binding = 0) uniform sampler2D source_dof_original;
#endif
#ifdef DOF_QUALITY_LOW
const int dof_kernel_size = 5;
const int dof_kernel_from = 2;
const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388);
#endif
#ifdef DOF_QUALITY_MEDIUM
const int dof_kernel_size = 11;
const int dof_kernel_from = 5;
const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037);
#endif
#ifdef DOF_QUALITY_HIGH
const int dof_kernel_size = 21;
const int dof_kernel_from = 10;
const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174);
#endif
#endif
void main() {
#ifdef MODE_MIPMAP
vec2 pix_size = blur.pixel_size;
vec4 color = texture(source_color, uv_interp + vec2(-0.5, -0.5) * pix_size);
color += texture(source_color, uv_interp + vec2(0.5, -0.5) * pix_size);
color += texture(source_color, uv_interp + vec2(0.5, 0.5) * pix_size);
color += texture(source_color, uv_interp + vec2(-0.5, 0.5) * pix_size);
frag_color = color / 4.0;
#endif
#ifdef MODE_GAUSSIAN_BLUR
//Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
if (bool(blur.flags & FLAG_HORIZONTAL)) {
vec2 pix_size = blur.pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.214607;
color += texture(source_color, uv_interp + vec2(1.0, 0.0) * pix_size) * 0.189879;
color += texture(source_color, uv_interp + vec2(2.0, 0.0) * pix_size) * 0.131514;
color += texture(source_color, uv_interp + vec2(3.0, 0.0) * pix_size) * 0.071303;
color += texture(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size) * 0.189879;
color += texture(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size) * 0.131514;
color += texture(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size) * 0.071303;
frag_color = color;
} else {
vec2 pix_size = blur.pixel_size;
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.38774;
color += texture(source_color, uv_interp + vec2(0.0, 1.0) * pix_size) * 0.24477;
color += texture(source_color, uv_interp + vec2(0.0, 2.0) * pix_size) * 0.06136;
color += texture(source_color, uv_interp + vec2(0.0, -1.0) * pix_size) * 0.24477;
color += texture(source_color, uv_interp + vec2(0.0, -2.0) * pix_size) * 0.06136;
frag_color = color;
}
#endif
#ifdef MODE_GAUSSIAN_GLOW
//Glow uses larger sigma 1 for a more rounded blur effect
#define GLOW_ADD(m_ofs, m_mult) \
{ \
vec2 ofs = uv_interp + m_ofs * pix_size; \
vec4 c = texture(source_color, ofs) * m_mult; \
if (any(lessThan(ofs, vec2(0.0))) || any(greaterThan(ofs, vec2(1.0)))) { \
c *= 0.0; \
} \
color += c; \
}
if (bool(blur.flags & FLAG_HORIZONTAL)) {
vec2 pix_size = blur.pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938;
GLOW_ADD(vec2(1.0, 0.0), 0.165569);
GLOW_ADD(vec2(2.0, 0.0), 0.140367);
GLOW_ADD(vec2(3.0, 0.0), 0.106595);
GLOW_ADD(vec2(-1.0, 0.0), 0.165569);
GLOW_ADD(vec2(-2.0, 0.0), 0.140367);
GLOW_ADD(vec2(-3.0, 0.0), 0.106595);
color *= blur.glow_strength;
frag_color = color;
} else {
vec2 pix_size = blur.pixel_size;
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.288713;
GLOW_ADD(vec2(0.0, 1.0), 0.233062);
GLOW_ADD(vec2(0.0, 2.0), 0.122581);
GLOW_ADD(vec2(0.0, -1.0), 0.233062);
GLOW_ADD(vec2(0.0, -2.0), 0.122581);
color *= blur.glow_strength;
frag_color = color;
}
#undef GLOW_ADD
if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) {
#ifdef GLOW_USE_AUTO_EXPOSURE
frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_grey;
#endif
frag_color *= blur.glow_exposure;
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap));
}
#endif
#ifdef MODE_DOF_FAR_BLUR
vec4 color_accum = vec4(0.0);
float depth = texture(dof_source_depth, uv_interp, 0.0).r;
depth = depth * 2.0 - 1.0;
if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
depth = ((depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
} else {
depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - depth * (blur.camera_z_far - blur.camera_z_near));
}
float amount = smoothstep(blur.dof_begin, blur.dof_end, depth);
float k_accum = 0.0;
for (int i = 0; i < dof_kernel_size; i++) {
int int_ofs = i - dof_kernel_from;
vec2 tap_uv = uv_interp + blur.dof_dir * float(int_ofs) * amount * blur.dof_radius;
float tap_k = dof_kernel[i];
float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
tap_depth = tap_depth * 2.0 - 1.0;
if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
tap_depth = ((tap_depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
} else {
tap_depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - tap_depth * (blur.camera_z_far - blur.camera_z_near));
}
float tap_amount = mix(smoothstep(blur.dof_begin, blur.dof_end, tap_depth), 1.0, int_ofs == 0);
tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
vec4 tap_color = texture(source_color, tap_uv, 0.0) * tap_k;
k_accum += tap_k * tap_amount;
color_accum += tap_color * tap_amount;
}
if (k_accum > 0.0) {
color_accum /= k_accum;
}
frag_color = color_accum; ///k_accum;
#endif
#ifdef MODE_DOF_NEAR_BLUR
vec4 color_accum = vec4(0.0);
float max_accum = 0.0;
for (int i = 0; i < dof_kernel_size; i++) {
int int_ofs = i - dof_kernel_from;
vec2 tap_uv = uv_interp + blur.dof_dir * float(int_ofs) * blur.dof_radius;
float ofs_influence = max(0.0, 1.0 - float(abs(int_ofs)) / float(dof_kernel_from));
float tap_k = dof_kernel[i];
vec4 tap_color = texture(source_color, tap_uv, 0.0);
float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
tap_depth = tap_depth * 2.0 - 1.0;
if (bool(blur.flags & FLAG_USE_ORTHOGONAL_PROJECTION)) {
tap_depth = ((tap_depth + (blur.camera_z_far + blur.camera_z_near) / (blur.camera_z_far - blur.camera_z_near)) * (blur.camera_z_far - blur.camera_z_near)) / 2.0;
} else {
tap_depth = 2.0 * blur.camera_z_near * blur.camera_z_far / (blur.camera_z_far + blur.camera_z_near - tap_depth * (blur.camera_z_far - blur.camera_z_near));
}
float tap_amount = 1.0 - smoothstep(blur.dof_end, blur.dof_begin, tap_depth);
tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
if (bool(blur.flags & FLAG_DOF_NEAR_FIRST_TAP)) {
tap_color.a = 1.0 - smoothstep(blur.dof_end, blur.dof_begin, tap_depth);
}
max_accum = max(max_accum, tap_amount * ofs_influence);
color_accum += tap_color * tap_k;
}
color_accum.a = max(color_accum.a, sqrt(max_accum));
#ifdef DOF_NEAR_BLUR_MERGE
{
vec4 original = texture(source_dof_original, uv_interp, 0.0);
color_accum = mix(original, color_accum, color_accum.a);
}
#endif
if (bool(blur.flags & FLAG_DOF_NEAR_FIRST_TAP)) {
frag_color = color_accum;
}
#endif
#ifdef MODE_SIMPLE_COPY
vec4 color = texture(source_color, uv_interp, 0.0);
if (bool(blur.flags & FLAG_COPY_FORCE_LUMINANCE)) {
color.rgb = vec3(max(max(color.r, color.g), color.b));
}
frag_color = color;
#endif
#ifdef MODE_SSAO_MERGE
vec4 color = texture(source_color, uv_interp, 0.0);
float ssao = texture(source_ssao, uv_interp, 0.0).r;
frag_color = vec4(mix(color.rgb, color.rgb * mix(blur.ssao_color.rgb, vec3(1.0), ssao), color.a), 1.0);
#endif
}

View File

@@ -0,0 +1,35 @@
#define FLAG_HORIZONTAL (1 << 0)
#define FLAG_USE_BLUR_SECTION (1 << 1)
#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 2)
#define FLAG_DOF_NEAR_FIRST_TAP (1 << 3)
#define FLAG_GLOW_FIRST_PASS (1 << 4)
#define FLAG_FLIP_Y (1 << 5)
#define FLAG_COPY_FORCE_LUMINANCE (1 << 6)
layout(push_constant, binding = 1, std430) uniform Blur {
vec4 section;
vec2 pixel_size;
uint flags;
uint pad;
// Glow.
float glow_strength;
float glow_bloom;
float glow_hdr_threshold;
float glow_hdr_scale;
float glow_exposure;
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_grey;
// DOF.
float dof_begin;
float dof_end;
float dof_radius;
float dof_pad;
vec2 dof_dir;
float camera_z_far;
float camera_z_near;
vec4 ssao_color;
}
blur;

View File

@@ -0,0 +1,258 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
/* clang-format on */
#ifdef MODE_GEN_BLUR_SIZE
layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
#endif
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL) || defined(MODE_BOKEH_CIRCULAR)
layout(set = 1, binding = 0) uniform sampler2D color_texture;
layout(rgba16f, set = 0, binding = 0) uniform restrict writeonly image2D bokeh_image;
#endif
#ifdef MODE_COMPOSITE_BOKEH
layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
#endif
// based on https://www.shadertoy.com/view/Xd3GDl
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 size;
float z_far;
float z_near;
bool orthogonal;
float blur_size;
float blur_scale;
int blur_steps;
bool blur_near_active;
float blur_near_begin;
float blur_near_end;
bool blur_far_active;
float blur_far_begin;
float blur_far_end;
bool second_pass;
bool half_size;
bool use_jitter;
float jitter_seed;
uint pad[2];
}
params;
//used to work around downsampling filter
#define DEPTH_GAP 0.0
#ifdef MODE_GEN_BLUR_SIZE
float get_depth_at_pos(vec2 uv) {
float depth = textureLod(source_depth, uv, 0.0).x;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
return depth;
}
float get_blur_size(float depth) {
if (params.blur_near_active && depth < params.blur_near_begin) {
return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
}
if (params.blur_far_active && depth > params.blur_far_begin) {
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
}
return 0.0;
}
#endif
const float GOLDEN_ANGLE = 2.39996323;
//note: uniform pdf rand [0;1[
float hash12n(vec2 p) {
p = fract(p * vec2(5.3987, 5.4421));
p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
return fract(p.x * p.y * 95.4307);
}
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
dir *= pixel_size;
vec4 color = texture(color_texture, uv);
vec4 accum = color;
float total = 1.0;
float blur_scale = params.blur_size / float(params.blur_steps);
if (params.use_jitter) {
uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
}
for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
if (i == 0) {
continue;
}
float radius = float(i) * blur_scale;
vec2 suv = uv + dir * radius;
radius = abs(radius);
vec4 sample_color = texture(color_texture, suv);
float limit;
if (sample_color.a < color.a) {
limit = abs(sample_color.a);
} else {
limit = abs(color.a);
}
limit -= DEPTH_GAP;
float m = smoothstep(radius - 0.5, radius + 0.5, limit);
accum += mix(color, sample_color, m);
total += 1.0;
}
return accum / total;
}
#endif
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.size))) { //too large, do nothing
return;
}
vec2 pixel_size = 1.0 / vec2(params.size);
vec2 uv = vec2(pos) / vec2(params.size);
#ifdef MODE_GEN_BLUR_SIZE
uv += pixel_size * 0.5;
//precompute size in alpha channel
float depth = get_depth_at_pos(uv);
float size = get_blur_size(depth);
vec4 color = imageLoad(color_image, pos);
color.a = size;
imageStore(color_image, pos, color);
#endif
#ifdef MODE_BOKEH_BOX
//pixel_size*=0.5; //resolution is doubled
if (params.second_pass || !params.half_size) {
uv += pixel_size * 0.5; //half pixel to read centers
} else {
uv += pixel_size * 0.25; //half pixel to read centers from full res
}
vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
imageStore(bokeh_image, pos, color);
#endif
#ifdef MODE_BOKEH_HEXAGONAL
//pixel_size*=0.5; //resolution is doubled
if (params.second_pass || !params.half_size) {
uv += pixel_size * 0.5; //half pixel to read centers
} else {
uv += pixel_size * 0.25; //half pixel to read centers from full res
}
vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
if (params.second_pass) {
dir = normalize(vec2(-1.0, 0.577350269189626));
vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
color.rgb = min(color.rgb, color2.rgb);
color.a = (color.a + color2.a) * 0.5;
}
imageStore(bokeh_image, pos, color);
#endif
#ifdef MODE_BOKEH_CIRCULAR
if (params.half_size) {
pixel_size *= 0.5; //resolution is doubled
}
uv += pixel_size * 0.5; //half pixel to read centers
vec4 color = texture(color_texture, uv);
float accum = 1.0;
float radius = params.blur_scale;
for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
vec2 suv = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
vec4 sample_color = texture(color_texture, suv);
float sample_size = abs(sample_color.a);
if (sample_color.a > color.a) {
sample_size = clamp(sample_size, 0.0, abs(color.a) * 2.0);
}
float m = smoothstep(radius - 0.5, radius + 0.5, sample_size);
color += mix(color / accum, sample_color, m);
accum += 1.0;
radius += params.blur_scale / radius;
}
color /= accum;
imageStore(bokeh_image, pos, color);
#endif
#ifdef MODE_COMPOSITE_BOKEH
uv += pixel_size * 0.5;
vec4 color = imageLoad(color_image, pos);
vec4 bokeh = texture(source_bokeh, uv);
float mix_amount;
if (bokeh.a < color.a) {
mix_amount = min(1.0, max(0.0, max(abs(color.a), abs(bokeh.a)) - DEPTH_GAP));
} else {
mix_amount = min(1.0, max(0.0, abs(color.a) - DEPTH_GAP));
}
color.rgb = mix(color.rgb, bokeh.rgb, mix_amount); //blend between hires and lowres
color.a = 0; //reset alpha
imageStore(color_image, pos, color);
#endif
}

View File

@@ -0,0 +1,584 @@
/* clang-format off */
[vertex]
#version 450
VERSION_DEFINES
#ifdef USE_ATTRIBUTES
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
layout(location = 3) in vec4 color_attrib;
layout(location = 4) in vec2 uv_attrib;
layout(location = 6) in uvec4 bones_attrib;
#endif
#include "canvas_uniforms_inc.glsl"
layout(location = 0) out vec2 uv_interp;
layout(location = 1) out vec4 color_interp;
layout(location = 2) out vec2 vertex_interp;
#ifdef USE_NINEPATCH
layout(location = 3) out vec2 pixel_size_interp;
#endif
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 1, binding = 1, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
/* clang-format off */
VERTEX_SHADER_GLOBALS
/* clang-format on */
void main() {
vec4 instance_custom = vec4(0.0);
#ifdef USE_PRIMITIVE
//weird bug,
//this works
vec2 vertex;
vec2 uv;
vec4 color;
if (gl_VertexIndex == 0) {
vertex = draw_data.points[0];
uv = draw_data.uvs[0];
color = vec4(unpackHalf2x16(draw_data.colors[0]), unpackHalf2x16(draw_data.colors[1]));
} else if (gl_VertexIndex == 1) {
vertex = draw_data.points[1];
uv = draw_data.uvs[1];
color = vec4(unpackHalf2x16(draw_data.colors[2]), unpackHalf2x16(draw_data.colors[3]));
} else {
vertex = draw_data.points[2];
uv = draw_data.uvs[2];
color = vec4(unpackHalf2x16(draw_data.colors[4]), unpackHalf2x16(draw_data.colors[5]));
}
uvec4 bones = uvec4(0, 0, 0, 0);
#elif defined(USE_ATTRIBUTES)
vec2 vertex = vertex_attrib;
vec4 color = color_attrib;
vec2 uv = uv_attrib;
uvec4 bones = bones_attrib;
#else
vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
vec2 vertex_base = vertex_base_arr[gl_VertexIndex];
vec2 uv = draw_data.src_rect.xy + abs(draw_data.src_rect.zw) * ((draw_data.flags & FLAGS_TRANSPOSE_RECT) != 0 ? vertex_base.yx : vertex_base.xy);
vec4 color = draw_data.modulation;
vec2 vertex = draw_data.dst_rect.xy + abs(draw_data.dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data.src_rect.zw, vec2(0.0, 0.0)));
uvec4 bones = uvec4(0, 0, 0, 0);
#endif
mat4 world_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0));
#if 0
if (draw_data.flags & FLAGS_INSTANCING_ENABLED) {
uint offset = draw_data.flags & FLAGS_INSTANCING_STRIDE_MASK;
offset *= gl_InstanceIndex;
mat4 instance_xform = mat4(
vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), 0.0, texelFetch(instancing_buffer, offset + 3)),
vec4(texelFetch(instancing_buffer, offset + 4), texelFetch(instancing_buffer, offset + 5), 0.0, texelFetch(instancing_buffer, offset + 7)),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(0.0, 0.0, 0.0, 1.0));
offset += 8;
if (draw_data.flags & FLAGS_INSTANCING_HAS_COLORS) {
vec4 instance_color;
if (draw_data.flags & FLAGS_INSTANCING_COLOR_8_BIT) {
uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset));
instance_color = unpackUnorm4x8(bits);
offset += 1;
} else {
instance_color = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3));
offser += 4;
}
color *= instance_color;
}
if (draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA) {
if (draw_data.flags & FLAGS_INSTANCING_CUSTOM_DATA_8_BIT) {
uint bits = floatBitsToUint(texelFetch(instancing_buffer, offset));
instance_custom = unpackUnorm4x8(bits);
} else {
instance_custom = vec4(texelFetch(instancing_buffer, offset + 0), texelFetch(instancing_buffer, offset + 1), texelFetch(instancing_buffer, offset + 2), texelFetch(instancing_buffer, offset + 3));
}
}
}
#endif
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(draw_data.flags & FLAGS_USING_PARTICLES)) {
//scale by texture size
vertex /= draw_data.color_texture_pixel_size;
}
#endif
#ifdef USE_POINT_SIZE
float point_size = 1.0;
#endif
{
/* clang-format off */
VERTEX_SHADER_CODE
/* clang-format on */
}
#ifdef USE_NINEPATCH
pixel_size_interp = abs(draw_data.dst_rect.zw) * vertex_base;
#endif
#if !defined(SKIP_TRANSFORM_USED)
vertex = (world_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
color_interp = color;
if (bool(draw_data.flags & FLAGS_USE_PIXEL_SNAP)) {
vertex = floor(vertex + 0.5);
// precision issue on some hardware creates artifacts within texture
// offset uv by a small amount to avoid
uv += 1e-5;
}
#ifdef USE_ATTRIBUTES
#if 0
if (bool(draw_data.flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone
//skeleton transform
ivec4 bone_indicesi = ivec4(bone_indices);
uvec2 tex_ofs = bone_indicesi.x * 2;
mat2x4 m;
m = mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.x;
tex_ofs = bone_indicesi.y * 2;
m += mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.y;
tex_ofs = bone_indicesi.z * 2;
m += mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.z;
tex_ofs = bone_indicesi.w * 2;
m += mat2x4(
texelFetch(skeleton_buffer, tex_ofs + 0),
texelFetch(skeleton_buffer, tex_ofs + 1)) *
bone_weights.w;
mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse;
//outvec = bone_matrix * outvec;
}
#endif
#endif
vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
vertex_interp = vertex;
uv_interp = uv;
gl_Position = canvas_data.screen_transform * vec4(vertex, 0.0, 1.0);
#ifdef USE_POINT_SIZE
gl_PointSize = point_size;
#endif
}
/* clang-format off */
[fragment]
#version 450
VERSION_DEFINES
#include "canvas_uniforms_inc.glsl"
layout(location = 0) in vec2 uv_interp;
/* clang-format on */
layout(location = 1) in vec4 color_interp;
layout(location = 2) in vec2 vertex_interp;
#ifdef USE_NINEPATCH
layout(location = 3) in vec2 pixel_size_interp;
#endif
layout(location = 0) out vec4 frag_color;
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 1, binding = 1, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
/* clang-format off */
FRAGMENT_SHADER_GLOBALS
/* clang-format on */
#ifdef LIGHT_SHADER_CODE_USED
vec4 light_compute(
vec3 light_vertex,
vec3 light_position,
vec3 normal,
vec4 light_color,
float light_energy,
vec4 specular_shininess,
inout vec4 shadow_modulate,
vec2 screen_uv,
vec2 uv,
vec4 color) {
vec4 light = vec4(0.0);
/* clang-format off */
LIGHT_SHADER_CODE
/* clang-format on */
return light;
}
#endif
#ifdef USE_NINEPATCH
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
float tex_size = 1.0 / tex_pixel_size;
if (pixel < margin_begin) {
return pixel * tex_pixel_size;
} else if (pixel >= draw_size - margin_end) {
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else {
if (!bool(draw_data.flags & FLAGS_NINEPACH_DRAW_CENTER)) {
draw_center--;
}
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
if (np_repeat == 0) { // Stretch.
// Convert to ratio.
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
} else if (np_repeat == 1) { // Tile.
// Convert to offset.
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ofs) * tex_pixel_size;
} else if (np_repeat == 2) { // Tile Fit.
// Calculate scale.
float src_area = draw_size - margin_begin - margin_end;
float dst_area = tex_size - margin_begin - margin_end;
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
// Convert to ratio.
float ratio = (pixel - margin_begin) / src_area;
ratio = mod(ratio * scale, 1.0);
// Scale to source texture.
return (margin_begin + ratio * dst_area) * tex_pixel_size;
} else { // Shouldn't happen, but silences compiler warning.
return 0.0;
}
}
}
#endif
void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
vec2 vertex = vertex_interp;
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
#ifdef USE_NINEPATCH
int draw_center = 2;
uv = vec2(
map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(draw_data.flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(draw_data.flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
if (draw_center == 0) {
color.a = 0.0;
}
uv = uv * draw_data.src_rect.zw + draw_data.src_rect.xy; //apply region if needed
#endif
if (bool(draw_data.flags & FLAGS_CLIP_RECT_UV)) {
uv = clamp(uv, draw_data.src_rect.xy, draw_data.src_rect.xy + abs(draw_data.src_rect.zw));
}
#endif
color *= texture(sampler2D(color_texture, texture_sampler), uv);
uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
vec3 normal;
#if defined(NORMAL_USED)
bool normal_used = true;
#else
bool normal_used = false;
#endif
if (normal_used || (light_count > 0 && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
} else {
normal = vec3(0.0, 0.0, 1.0);
}
vec4 specular_shininess;
#if defined(SPECULAR_SHININESS_USED)
bool specular_shininess_used = true;
#else
bool specular_shininess_used = false;
#endif
if (specular_shininess_used || (light_count > 0 && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
}
#if defined(SCREEN_UV_USED)
vec2 screen_uv = gl_FragCoord.xy * canvas_data.screen_pixel_size;
#else
vec2 screen_uv = vec2(0.0);
#endif
vec3 light_vertex = vec3(vertex, 0.0);
vec2 shadow_vertex = vertex;
{
float normal_depth = 1.0;
#if defined(NORMALMAP_USED)
vec3 normal_map = vec3(0.0, 0.0, 1.0);
normal_used = true;
#endif
/* clang-format off */
FRAGMENT_SHADER_CODE
/* clang-format on */
#if defined(NORMALMAP_USED)
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_depth);
#endif
}
if (normal_used) {
//convert by item transform
normal.xy = mat2(normalize(draw_data.world_x), normalize(draw_data.world_y)) * normal.xy;
//convert by canvas transform
normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz);
}
vec4 base_color = color;
if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
color = vec4(0.0); //invisible by default due to using light mask
}
color *= canvas_data.canvas_modulation;
#ifdef USE_LIGHTING
for (uint i = 0; i < MAX_LIGHT_TEXTURES; i++) {
if (i >= light_count) {
break;
}
uint light_base;
if (i < 8) {
if (i < 4) {
light_base = draw_data.lights[0];
} else {
light_base = draw_data.lights[1];
}
} else {
if (i < 12) {
light_base = draw_data.lights[2];
} else {
light_base = draw_data.lights[3];
}
}
light_base >>= (i & 3) * 8;
light_base &= 0xFF;
vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec4 light_color = texture(sampler2D(light_textures[i], texture_sampler), tex_uv);
vec4 light_base_color = light_array.data[light_base].color;
#ifdef LIGHT_SHADER_CODE_USED
vec4 shadow_modulate = vec4(1.0);
vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
light_color.rgb *= light_base_color.rgb;
light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv);
#else
light_color.rgb *= light_base_color.rgb * light_base_color.a;
if (normal_used) {
vec3 light_pos = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
vec3 pos = light_vertex;
vec3 light_vec = normalize(light_pos - pos);
float cNdotL = max(0.0, dot(normal, light_vec));
if (specular_shininess_used) {
//blinn
vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
vec3 half_vec = normalize(view + light_vec);
float cNdotV = max(dot(normal, view), 0.0);
float cNdotH = max(dot(normal, half_vec), 0.0);
float cVdotH = max(dot(view, half_vec), 0.0);
float cLdotH = max(dot(light_vec, half_vec), 0.0);
float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
light_color.rgb = specular_shininess.rgb * light_base_color.rgb * s + light_color.rgb * cNdotL;
} else {
light_color.rgb *= cNdotL;
}
}
#endif
if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
//if outside the light texture, light color is zero
light_color.a = 0.0;
}
if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 pos_norm = normalize(shadow_pos);
vec2 pos_abs = abs(pos_norm);
vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
float tex_ofs;
float distance;
if (pos_rot.y > 0) {
if (pos_rot.x > 0) {
tex_ofs = pos_box.y * 0.125 + 0.125;
distance = shadow_pos.x;
} else {
tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
distance = shadow_pos.y;
}
} else {
if (pos_rot.x < 0) {
tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
distance = -shadow_pos.x;
} else {
tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
distance = -shadow_pos.y;
}
}
//float distance = length(shadow_pos);
float shadow;
uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
vec4 shadow_uv = vec4(tex_ofs, 0.0, distance, 1.0);
if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
shadow = textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv).x;
} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
shadow = 0.0;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 2.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 2.0).x;
shadow /= 5.0;
} else { //PCF13
vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
shadow = 0.0;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 6.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 5.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 4.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 3.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size * 2.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv - shadow_pixel_size).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 2.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 3.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 4.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 5.0).x;
shadow += textureProj(sampler2DShadow(shadow_textures[i], shadow_sampler), shadow_uv + shadow_pixel_size * 6.0).x;
shadow /= 13.0;
}
vec4 shadow_color = light_array.data[light_base].shadow_color;
#ifdef LIGHT_SHADER_CODE_USED
shadow_color *= shadow_modulate;
#endif
light_color = mix(light_color, shadow_color, shadow);
}
uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
switch (blend_mode) {
case LIGHT_FLAGS_BLEND_MODE_ADD: {
color.rgb += light_color.rgb * light_color.a;
} break;
case LIGHT_FLAGS_BLEND_MODE_SUB: {
color.rgb -= light_color.rgb * light_color.a;
} break;
case LIGHT_FLAGS_BLEND_MODE_MIX: {
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
} break;
case LIGHT_FLAGS_BLEND_MODE_MASK: {
light_color.a *= base_color.a;
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
} break;
}
}
#endif
frag_color = color;
}

View File

@@ -0,0 +1,40 @@
/* clang-format off */
[vertex]
#version 450
layout(location = 0) in highp vec3 vertex;
/* clang-format on */
layout(push_constant, binding = 0, std430) uniform Constants {
mat4 projection;
mat2x4 modelview;
vec2 direction;
vec2 pad;
}
constants;
layout(location = 0) out highp float depth;
void main() {
highp vec4 vtx = vec4(vertex, 1.0) * mat4(constants.modelview[0], constants.modelview[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
depth = dot(constants.direction, vtx.xy);
gl_Position = constants.projection * vtx;
}
/* clang-format off */
[fragment]
#version 450
layout(location = 0) in highp float depth;
/* clang-format on */
layout(location = 0) out highp float distance_buf;
void main() {
distance_buf = depth;
}

View File

@@ -0,0 +1,141 @@
#define M_PI 3.14159265359
#define FLAGS_INSTANCING_STRIDE_MASK 0xF
#define FLAGS_INSTANCING_ENABLED (1 << 4)
#define FLAGS_INSTANCING_HAS_COLORS (1 << 5)
#define FLAGS_INSTANCING_COLOR_8BIT (1 << 6)
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 7)
#define FLAGS_INSTANCING_CUSTOM_DATA_8_BIT (1 << 8)
#define FLAGS_CLIP_RECT_UV (1 << 9)
#define FLAGS_TRANSPOSE_RECT (1 << 10)
#define FLAGS_USING_LIGHT_MASK (1 << 11)
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
#define FLAGS_USING_PARTICLES (1 << 13)
#define FLAGS_USE_PIXEL_SNAP (1 << 14)
#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
#define FLAGS_LIGHT_COUNT_SHIFT 20
#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
// In vulkan, sets should always be ordered using the following logic:
// Lower Sets: Sets that change format and layout less often
// Higher sets: Sets that change format and layout very often
// This is because changing a set for another with a different layout or format,
// invalidates all the upper ones.
/* SET0: Draw Primitive */
layout(push_constant, binding = 0, std430) uniform DrawData {
vec2 world_x;
vec2 world_y;
vec2 world_ofs;
uint flags;
uint specular_shininess;
#ifdef USE_PRIMITIVE
vec2 points[3];
vec2 uvs[3];
uint colors[6];
#else
vec4 modulation;
vec4 ninepatch_margins;
vec4 dst_rect; //for built-in rect and UV
vec4 src_rect;
vec2 pad;
#endif
vec2 color_texture_pixel_size;
uint lights[4];
}
draw_data;
// The values passed per draw primitives are cached within it
layout(set = 0, binding = 1) uniform texture2D color_texture;
layout(set = 0, binding = 2) uniform texture2D normal_texture;
layout(set = 0, binding = 3) uniform texture2D specular_texture;
layout(set = 0, binding = 4) uniform sampler texture_sampler;
layout(set = 0, binding = 5) uniform textureBuffer instancing_buffer;
/* SET1: Is reserved for the material */
#ifdef USE_MATERIAL_SAMPLERS
layout(set = 1, binding = 0) uniform sampler material_samplers[12];
#endif
/* SET2: Canvas Item State (including lighting) */
layout(set = 2, binding = 0, std140) uniform CanvasData {
mat4 canvas_transform;
mat4 screen_transform;
mat4 canvas_normal_transform;
vec4 canvas_modulation;
vec2 screen_pixel_size;
float time;
float time_pad;
//uint light_count;
}
canvas_data;
layout(set = 2, binding = 1) uniform textureBuffer skeleton_buffer;
layout(set = 2, binding = 2, std140) uniform SkeletonData {
mat4 skeleton_transform; //in world coordinates
mat4 skeleton_transform_inverse;
}
skeleton_data;
#ifdef USE_LIGHTING
#define LIGHT_FLAGS_BLEND_MASK (3 << 16)
#define LIGHT_FLAGS_BLEND_MODE_ADD (0 << 16)
#define LIGHT_FLAGS_BLEND_MODE_SUB (1 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MIX (2 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MASK (3 << 16)
#define LIGHT_FLAGS_HAS_SHADOW (1 << 20)
#define LIGHT_FLAGS_FILTER_SHIFT 22
#define LIGHT_FLAGS_FILTER_MASK (3 << 22)
#define LIGHT_FLAGS_SHADOW_NEAREST (0 << 22)
#define LIGHT_FLAGS_SHADOW_PCF5 (1 << 22)
#define LIGHT_FLAGS_SHADOW_PCF13 (2 << 22)
struct Light {
mat2x4 texture_matrix; //light to texture coordinate matrix (transposed)
mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed)
vec4 color;
vec4 shadow_color;
vec2 position;
uint flags; //index to light texture
float height;
float shadow_pixel_size;
float pad0;
float pad1;
float pad2;
};
layout(set = 2, binding = 3, std140) uniform LightData {
Light data[MAX_LIGHTS];
}
light_array;
layout(set = 2, binding = 4) uniform texture2D light_textures[MAX_LIGHT_TEXTURES];
layout(set = 2, binding = 5) uniform texture2D shadow_textures[MAX_LIGHT_TEXTURES];
layout(set = 2, binding = 6) uniform sampler shadow_sampler;
#endif
/* SET3: Render Target Data */
#ifdef SCREEN_TEXTURE_USED
layout(set = 3, binding = 0) uniform texture2D screen_texture;
#endif

View File

@@ -0,0 +1,86 @@
/* clang-format off */
[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
}
/* clang-format off */
[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
/* clang-format on */
#ifdef MODE_CUBE_TO_DP
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(push_constant, binding = 0, std430) uniform Params {
float bias;
float z_far;
float z_near;
bool z_flip;
}
params;
layout(location = 0) out float depth_buffer;
#endif
void main() {
#ifdef MODE_CUBE_TO_DP
vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0);
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
normal = normalize(normal);
normal.y = -normal.y; //needs to be flipped to match projection matrix
if (!params.z_flip) {
normal.z = -normal.z;
}
float depth = texture(source_cube, normal).r;
// absolute values for direction cosines, bigger value equals closer to basis axis
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
float depth_fix = 1.0 / dot(normal, unorm);
depth = 2.0 * depth - 1.0;
float linear_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
depth_buffer = (linear_depth * depth_fix + params.bias) / params.z_far;
#endif
}

View File

@@ -0,0 +1,188 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
/* clang-format on */
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
layout(push_constant, binding = 1, std430) uniform Params {
uint face_size;
}
params;
#define M_PI 3.14159265359
void get_dir_0(out vec3 dir, in float u, in float v) {
dir[0] = 1.0;
dir[1] = v;
dir[2] = -u;
}
void get_dir_1(out vec3 dir, in float u, in float v) {
dir[0] = -1.0;
dir[1] = v;
dir[2] = u;
}
void get_dir_2(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = 1.0;
dir[2] = -v;
}
void get_dir_3(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = -1.0;
dir[2] = v;
}
void get_dir_4(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = v;
dir[2] = 1.0;
}
void get_dir_5(out vec3 dir, in float u, in float v) {
dir[0] = -u;
dir[1] = v;
dir[2] = -1.0;
}
float calcWeight(float u, float v) {
float val = u * u + v * v + 1.0;
return val * sqrt(val);
}
void main() {
uvec3 id = gl_GlobalInvocationID;
uint face_size = params.face_size;
if (id.x < face_size && id.y < face_size) {
float inv_face_size = 1.0 / float(face_size);
float u0 = (float(id.x) * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
float u1 = (float(id.x) * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
float v0 = (float(id.y) * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
float v1 = (float(id.y) * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + .125;
}
vec3 dir;
vec4 color;
switch (id.z) {
case 0:
get_dir_0(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_0(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_0(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_0(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 1:
get_dir_1(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_1(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_1(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_1(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 2:
get_dir_2(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_2(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_2(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_2(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 3:
get_dir_3(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_3(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_3(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_3(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 4:
get_dir_4(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_4(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_4(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_4(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
default:
get_dir_5(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_5(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_5(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_5(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
}
imageStore(dest_cubemap, ivec3(id), color);
}
}

View File

@@ -0,0 +1,328 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
#define GROUP_SIZE 64
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
/* clang-format on */
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly imageCube dest_cubemap0;
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly imageCube dest_cubemap1;
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly imageCube dest_cubemap2;
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly imageCube dest_cubemap3;
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly imageCube dest_cubemap4;
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly imageCube dest_cubemap5;
layout(rgba16f, set = 2, binding = 6) uniform restrict writeonly imageCube dest_cubemap6;
#ifdef USE_HIGH_QUALITY
#define NUM_TAPS 32
#else
#define NUM_TAPS 8
#endif
#define BASE_RESOLUTION 128
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
}
data;
#else
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][6] coeffs;
}
data;
#endif
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
switch (face) {
case 0:
dir = vec3(1.0, uv[1], -uv[0]);
break;
case 1:
dir = vec3(-1.0, uv[1], uv[0]);
break;
case 2:
dir = vec3(uv[0], 1.0, -uv[1]);
break;
case 3:
dir = vec3(uv[0], -1.0, uv[1]);
break;
case 4:
dir = vec3(uv[0], uv[1], 1.0);
break;
default:
dir = vec3(-uv[0], uv[1], -1.0);
break;
}
}
void main() {
// INPUT:
// id.x = the linear address of the texel (ignoring face)
// id.y = the face
// -> use to index output texture
// id.x = texel x
// id.y = texel y
// id.z = face
uvec3 id = gl_GlobalInvocationID;
// determine which texel this is
#ifndef USE_TEXTURE_ARRAY
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
int mip_level = 0;
if (id.x < (128 * 128)) {
mip_level = 0;
} else if (id.x < (128 * 128 + 64 * 64)) {
mip_level = 1;
id.x -= (128 * 128);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32)) {
mip_level = 2;
id.x -= (128 * 128 + 64 * 64);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16)) {
mip_level = 3;
id.x -= (128 * 128 + 64 * 64 + 32 * 32);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8)) {
mip_level = 4;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4)) {
mip_level = 5;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2)) {
mip_level = 6;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4);
} else {
return;
}
int res = BASE_RESOLUTION >> mip_level;
#else // Using Texture Arrays so all levels are the same resolution
int res = BASE_RESOLUTION;
int mip_level = int(id.x / (BASE_RESOLUTION * BASE_RESOLUTION));
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
#endif
// determine dir / pos for the texel
vec3 dir, adir, frameZ;
{
id.z = id.y;
id.y = id.x / res;
id.x -= id.y * res;
vec2 uv;
uv.x = (float(id.x) * 2.0 + 1.0) / float(res) - 1.0;
uv.y = -(float(id.y) * 2.0 + 1.0) / float(res) + 1.0;
get_dir(dir, uv, id.z);
frameZ = normalize(dir);
adir = abs(dir);
}
// GGX gather colors
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
switch (axis) {
case 0:
UpVector = vec3(1, 0, 0);
break;
case 1:
UpVector = vec3(0, 1, 0);
break;
default:
UpVector = vec3(0, 0, 1);
break;
}
vec3 frameX = normalize(cross(UpVector, frameZ));
vec3 frameY = cross(frameZ, frameX);
// calculate parametrization for polynomial
float Nx = dir[otherAxis0];
float Ny = dir[otherAxis1];
float Nz = adir[axis];
float NmaxXY = max(abs(Ny), abs(Nx));
Nx /= NmaxXY;
Ny /= NmaxXY;
float theta;
if (Ny < Nx) {
if (Ny <= -0.999)
theta = Nx;
else
theta = Ny;
} else {
if (Ny >= 0.999)
theta = -Nx;
else
theta = -Ny;
}
float phi;
if (Nz <= -0.999)
phi = -NmaxXY;
else if (Nz >= 0.999)
phi = NmaxXY;
else
phi = Nz;
float theta2 = theta * theta;
float phi2 = phi * phi;
// sample
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
#ifdef USE_HIGH_QUALITY
vec4 coeffsDir0[3];
vec4 coeffsDir1[3];
vec4 coeffsDir2[3];
vec4 coeffsLevel[3];
vec4 coeffsWeight[3];
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index];
coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index];
coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index];
coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index];
coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index];
}
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
#else
vec4 coeffsDir0 = data.coeffs[mip_level][0][index];
vec4 coeffsDir1 = data.coeffs[mip_level][1][index];
vec4 coeffsDir2 = data.coeffs[mip_level][2][index];
vec4 coeffsLevel = data.coeffs[mip_level][3][index];
vec4 coeffsWeight = data.coeffs[mip_level][4][index];
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
float sample_level = coeffsLevel[iSubTap];
float sample_weight = coeffsWeight[iSubTap];
#endif
sample_weight *= frameweight;
// adjust for jacobian
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
#ifndef USE_TEXTURE_ARRAY
sample_level += float(mip_level) / 6.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
#endif
// sample cubemap
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
color.w += sample_weight;
}
}
}
}
color /= color.w;
// write color
color.xyz = max(vec3(0.0), color.xyz);
color.w = 1.0;
#ifdef USE_TEXTURE_ARRAY
id.xy *= uvec2(2, 2);
#endif
switch (mip_level) {
case 0:
imageStore(dest_cubemap0, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 1:
imageStore(dest_cubemap1, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 2:
imageStore(dest_cubemap2, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 3:
imageStore(dest_cubemap3, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 4:
imageStore(dest_cubemap4, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 5:
imageStore(dest_cubemap5, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
default:
imageStore(dest_cubemap6, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
}
}

View File

@@ -0,0 +1,147 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
#define GROUP_SIZE 8
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
/* clang-format on */
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
layout(push_constant, binding = 1, std430) uniform Params {
uint face_id;
uint sample_count;
float roughness;
bool use_direct_write;
float face_size;
}
params;
#define M_PI 3.14159265359
vec3 texelCoordToVec(vec2 uv, uint faceID) {
mat3 faceUvVectors[6];
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
// +x
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
// -y
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
// +y
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
// -z
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
// +z
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
return normalize(result);
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) {
float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]
// Compute distribution direction
float Phi = 2.0 * M_PI * Xi.x;
float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
// Convert to spherical direction
vec3 H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 TangentX = normalize(cross(UpVector, N));
vec3 TangentY = cross(N, TangentX);
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
float GGX(float NdotV, float a) {
float k = a / 2.0;
return NdotV / (NdotV * (1.0 - k) + k);
}
// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
float G_Smith(float a, float nDotV, float nDotL) {
return GGX(nDotL, a * a) * GGX(nDotV, a * a);
}
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N) {
return vec2(float(i) / float(N), radicalInverse_VdC(i));
}
void main() {
uvec3 id = gl_GlobalInvocationID;
id.z += params.face_id;
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
vec3 N = texelCoordToVec(uv, id.z);
//vec4 color = color_interp;
if (params.use_direct_write) {
imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
} else {
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
vec2 xi = Hammersley(sampleNum, params.sample_count);
vec3 H = ImportanceSampleGGX(xi, params.roughness, N);
vec3 V = N;
vec3 L = (2.0 * dot(V, H) * H - V);
float ndotl = clamp(dot(N, L), 0.0, 1.0);
if (ndotl > 0.0) {
sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl;
sum.a += ndotl;
}
}
sum /= sum.a;
imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
}
}

View File

@@ -0,0 +1,788 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
#ifdef MODE_DYNAMIC
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#else
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#endif
/* clang-format on */
#ifndef MODE_DYNAMIC
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333, 0.33333, 0.33333)
struct CellChildren {
uint children[8];
};
layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer {
CellChildren data[];
}
cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 2, std430) buffer CellDataBuffer {
CellData data[];
}
cell_data;
#endif // MODE DYNAMIC
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
#define LIGHT_TYPE_SPOT 2
#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING)
struct Light {
uint type;
float energy;
float radius;
float attenuation;
vec3 color;
float spot_angle_radians;
vec3 position;
float spot_attenuation;
vec3 direction;
bool has_shadow;
};
layout(set = 0, binding = 3, std140) uniform Lights {
Light data[MAX_LIGHTS];
}
lights;
#endif // MODE COMPUTE LIGHT
#ifdef MODE_SECOND_BOUNCE
layout(set = 0, binding = 5) uniform texture3D color_texture;
#ifdef MODE_ANISOTROPIC
layout(set = 0, binding = 7) uniform texture3D aniso_pos_texture;
layout(set = 0, binding = 8) uniform texture3D aniso_neg_texture;
#endif // MODE ANISOTROPIC
#endif // MODE_SECOND_BOUNCE
#ifndef MODE_DYNAMIC
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
float emission_scale;
float propagation;
float dynamic_range;
uint light_count;
uint cell_offset;
uint cell_count;
float aniso_strength;
uint pad;
}
params;
layout(set = 0, binding = 4, std430) buffer Outputs {
vec4 data[];
}
outputs;
#endif // MODE DYNAMIC
layout(set = 0, binding = 9) uniform texture3D texture_sdf;
layout(set = 0, binding = 10) uniform sampler texture_sampler;
#ifdef MODE_WRITE_TEXTURE
layout(rgba8, set = 0, binding = 5) uniform restrict writeonly image3D color_tex;
#ifdef MODE_ANISOTROPIC
layout(r16ui, set = 0, binding = 6) uniform restrict writeonly uimage3D aniso_pos_tex;
layout(r16ui, set = 0, binding = 7) uniform restrict writeonly uimage3D aniso_neg_tex;
#endif
#endif
#ifdef MODE_DYNAMIC
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint light_count; //when not lighting
ivec3 x_dir;
float z_base;
ivec3 y_dir;
float z_sign;
ivec3 z_dir;
float pos_multiplier;
ivec2 rect_pos;
ivec2 rect_size;
ivec2 prev_rect_ofs;
ivec2 prev_rect_size;
bool flip_x;
bool flip_y;
float dynamic_range;
bool on_mipmap;
float propagation;
float pad[3];
}
params;
#ifdef MODE_DYNAMIC_LIGHTING
layout(rgba8, set = 0, binding = 5) uniform restrict readonly image2D source_albedo;
layout(rgba8, set = 0, binding = 6) uniform restrict readonly image2D source_normal;
layout(rgba8, set = 0, binding = 7) uniform restrict readonly image2D source_orm;
//layout (set=0,binding=8) uniform texture2D source_depth;
layout(rgba16f, set = 0, binding = 11) uniform restrict image2D emission;
layout(r32f, set = 0, binding = 12) uniform restrict image2D depth;
#endif
#ifdef MODE_DYNAMIC_SHRINK
layout(rgba16f, set = 0, binding = 5) uniform restrict readonly image2D source_light;
layout(r32f, set = 0, binding = 6) uniform restrict readonly image2D source_depth;
#ifdef MODE_DYNAMIC_SHRINK_WRITE
layout(rgba16f, set = 0, binding = 7) uniform restrict writeonly image2D light;
layout(r32f, set = 0, binding = 8) uniform restrict writeonly image2D depth;
#endif // MODE_DYNAMIC_SHRINK_WRITE
#ifdef MODE_DYNAMIC_SHRINK_PLOT
layout(rgba8, set = 0, binding = 11) uniform restrict image3D color_texture;
#ifdef MODE_ANISOTROPIC
layout(r16ui, set = 0, binding = 12) uniform restrict writeonly uimage3D aniso_pos_texture;
layout(r16ui, set = 0, binding = 13) uniform restrict writeonly uimage3D aniso_neg_texture;
#endif // MODE ANISOTROPIC
#endif //MODE_DYNAMIC_SHRINK_PLOT
#endif // MODE_DYNAMIC_SHRINK
//layout (rgba8,set=0,binding=5) uniform restrict writeonly image3D color_tex;
#endif // MODE DYNAMIC
#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING)
float raymarch(float distance, float distance_adv, vec3 from, vec3 direction) {
vec3 cell_size = 1.0 / vec3(params.limits);
float occlusion = 1.0;
while (distance > 0.5) { //use this to avoid precision errors
float advance = texture(sampler3D(texture_sdf, texture_sampler), from * cell_size).r * 255.0 - 1.0;
if (advance < 0.0) {
occlusion = 0.0;
break;
}
occlusion = min(advance, occlusion);
advance = max(distance_adv, advance - mod(advance, distance_adv)); //should always advance in multiples of distance_adv
from += direction * advance;
distance -= advance;
}
return occlusion; //max(0.0,distance);
}
bool compute_light_vector(uint light, vec3 pos, out float attenuation, out vec3 light_pos) {
if (lights.data[light].type == LIGHT_TYPE_DIRECTIONAL) {
light_pos = pos - lights.data[light].direction * length(vec3(params.limits));
attenuation = 1.0;
} else {
light_pos = lights.data[light].position;
float distance = length(pos - light_pos);
if (distance >= lights.data[light].radius) {
return false;
}
attenuation = pow(clamp(1.0 - distance / lights.data[light].radius, 0.0001, 1.0), lights.data[light].attenuation);
if (lights.data[light].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(pos - light_pos);
float angle = acos(dot(rel, lights.data[light].direction));
if (angle > lights.data[light].spot_angle_radians) {
return false;
}
float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1);
attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation);
}
}
return true;
}
float get_normal_advance(vec3 p_normal) {
vec3 normal = p_normal;
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
return 1.0 / dot(normal, unorm);
}
void clip_segment(vec4 plane, vec3 begin, inout vec3 end) {
vec3 segment = begin - end;
float den = dot(plane.xyz, segment);
//printf("den is %i\n",den);
if (den < 0.0001) {
return;
}
float dist = (dot(plane.xyz, begin) - plane.w) / den;
if (dist < 0.0001 || dist > 1.0001) {
return;
}
end = begin + segment * -dist;
}
bool compute_light_at_pos(uint index, vec3 pos, vec3 normal, inout vec3 light, inout vec3 light_dir) {
float attenuation;
vec3 light_pos;
if (!compute_light_vector(index, pos, attenuation, light_pos)) {
return false;
}
light_dir = normalize(pos - light_pos);
if (attenuation < 0.01 || (length(normal) > 0.2 && dot(normal, light_dir) >= 0)) {
return false; //not facing the light, or attenuation is near zero
}
if (lights.data[index].has_shadow) {
float distance_adv = get_normal_advance(light_dir);
vec3 to = pos;
if (length(normal) > 0.2) {
to += normal * distance_adv * 0.51;
} else {
to -= sign(light_dir) * 0.45; //go near the edge towards the light direction to avoid self occlusion
}
//clip
clip_segment(mix(vec4(-1.0, 0.0, 0.0, 0.0), vec4(1.0, 0.0, 0.0, float(params.limits.x - 1)), bvec4(light_dir.x < 0.0)), to, light_pos);
clip_segment(mix(vec4(0.0, -1.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, float(params.limits.y - 1)), bvec4(light_dir.y < 0.0)), to, light_pos);
clip_segment(mix(vec4(0.0, 0.0, -1.0, 0.0), vec4(0.0, 0.0, 1.0, float(params.limits.z - 1)), bvec4(light_dir.z < 0.0)), to, light_pos);
float distance = length(to - light_pos);
if (distance < 0.1) {
return false; // hit
}
distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always
light_pos = to - light_dir * distance;
//from -= sign(light_dir)*0.45; //go near the edge towards the light direction to avoid self occlusion
/*float dist = raymarch(distance,distance_adv,light_pos,light_dir);
if (dist > distance_adv) {
return false;
}
attenuation *= 1.0 - smoothstep(0.1*distance_adv,distance_adv,dist);
*/
float occlusion = raymarch(distance, distance_adv, light_pos, light_dir);
if (occlusion == 0.0) {
return false;
}
attenuation *= occlusion; //1.0 - smoothstep(0.1*distance_adv,distance_adv,dist);
}
light = lights.data[index].color * attenuation * lights.data[index].energy;
return true;
}
#endif // MODE COMPUTE LIGHT
void main() {
#ifndef MODE_DYNAMIC
uint cell_index = gl_GlobalInvocationID.x;
if (cell_index >= params.cell_count) {
return;
}
cell_index += params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21);
vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo);
#endif
/////////////////COMPUTE LIGHT///////////////////////////////
#ifdef MODE_COMPUTE_LIGHT
vec3 pos = vec3(posu) + vec3(0.5);
vec3 emission = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
vec3 normal = unpackSnorm4x8(cell_data.data[cell_index].normal).xyz;
#ifdef MODE_ANISOTROPIC
vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
#else
vec3 accum = vec3(0.0);
#endif
for (uint i = 0; i < params.light_count; i++) {
vec3 light;
vec3 light_dir;
if (!compute_light_at_pos(i, pos, normal.xyz, light, light_dir)) {
continue;
}
light *= albedo.rgb;
#ifdef MODE_ANISOTROPIC
for (uint j = 0; j < 6; j++) {
accum[j] += max(0.0, dot(accum_dirs[j], -light_dir)) * light;
}
#else
if (length(normal) > 0.2) {
accum += max(0.0, dot(normal, -light_dir)) * light;
} else {
//all directions
accum += light;
}
#endif
}
#ifdef MODE_ANISOTROPIC
for (uint i = 0; i < 6; i++) {
vec3 light = accum[i];
if (length(normal) > 0.2) {
light += max(0.0, dot(accum_dirs[i], -normal)) * emission;
} else {
light += emission;
}
outputs.data[cell_index * 6 + i] = vec4(light, 0.0);
}
#else
outputs.data[cell_index] = vec4(accum + emission, 0.0);
#endif
#endif //MODE_COMPUTE_LIGHT
/////////////////SECOND BOUNCE///////////////////////////////
#ifdef MODE_SECOND_BOUNCE
vec3 pos = vec3(posu) + vec3(0.5);
ivec3 ipos = ivec3(posu);
vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal);
#ifdef MODE_ANISOTROPIC
vec3 accum[6];
const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
/*vec3 src_color = texelFetch(sampler3D(color_texture,texture_sampler),ipos,0).rgb * params.dynamic_range;
vec3 src_aniso_pos = texelFetch(sampler3D(aniso_pos_texture,texture_sampler),ipos,0).rgb;
vec3 src_anisp_neg = texelFetch(sampler3D(anisp_neg_texture,texture_sampler),ipos,0).rgb;
accum[0]=src_col * src_aniso_pos.x;
accum[1]=src_col * src_aniso_neg.x;
accum[2]=src_col * src_aniso_pos.y;
accum[3]=src_col * src_aniso_neg.y;
accum[4]=src_col * src_aniso_pos.z;
accum[5]=src_col * src_aniso_neg.z;*/
accum[0] = outputs.data[cell_index * 6 + 0].rgb;
accum[1] = outputs.data[cell_index * 6 + 1].rgb;
accum[2] = outputs.data[cell_index * 6 + 2].rgb;
accum[3] = outputs.data[cell_index * 6 + 3].rgb;
accum[4] = outputs.data[cell_index * 6 + 4].rgb;
accum[5] = outputs.data[cell_index * 6 + 5].rgb;
#else
vec3 accum = outputs.data[cell_index].rgb;
#endif
if (length(normal.xyz) > 0.2) {
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, normal.xyz));
vec3 bitangent = normalize(cross(tangent, normal.xyz));
mat3 normal_mat = mat3(tangent, bitangent, normal.xyz);
#define MAX_CONE_DIRS 6
vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
vec3(0.0, 0.0, 1.0),
vec3(0.866025, 0.0, 0.5),
vec3(0.267617, 0.823639, 0.5),
vec3(-0.700629, 0.509037, 0.5),
vec3(-0.700629, -0.509037, 0.5),
vec3(0.267617, -0.823639, 0.5));
float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
float tan_half_angle = 0.577;
for (int i = 0; i < MAX_CONE_DIRS; i++) {
vec3 direction = normal_mat * cone_dirs[i];
vec4 color = vec4(0.0);
{
float dist = 1.5;
float max_distance = length(vec3(params.limits));
vec3 cell_size = 1.0 / vec3(params.limits);
#ifdef MODE_ANISOTROPIC
vec3 aniso_normal = mix(direction, normal.xyz, params.aniso_strength);
#endif
while (dist < max_distance && color.a < 0.95) {
float diameter = max(1.0, 2.0 * tan_half_angle * dist);
vec3 uvw_pos = (pos + dist * direction) * cell_size;
float half_diameter = diameter * 0.5;
//check if outside, then break
//if ( any(greaterThan(abs(uvw_pos - 0.5),vec3(0.5f + half_diameter * cell_size)) ) ) {
// break;
//}
float log2_diameter = log2(diameter);
vec4 scolor = textureLod(sampler3D(color_texture, texture_sampler), uvw_pos, log2_diameter);
#ifdef MODE_ANISOTROPIC
vec3 aniso_neg = textureLod(sampler3D(aniso_neg_texture, texture_sampler), uvw_pos, log2_diameter).rgb;
vec3 aniso_pos = textureLod(sampler3D(aniso_pos_texture, texture_sampler), uvw_pos, log2_diameter).rgb;
scolor.rgb *= dot(max(vec3(0.0), (aniso_normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-aniso_normal * aniso_neg)), vec3(1.0));
#endif
float a = (1.0 - color.a);
color += a * scolor;
dist += half_diameter;
}
}
color *= cone_weights[i] * vec4(albedo.rgb, 1.0) * params.dynamic_range; //restore range
#ifdef MODE_ANISOTROPIC
for (uint j = 0; j < 6; j++) {
accum[j] += max(0.0, dot(accum_dirs[j], direction)) * color.rgb;
}
#else
accum += color.rgb;
#endif
}
}
#ifdef MODE_ANISOTROPIC
outputs.data[cell_index * 6 + 0] = vec4(accum[0], 0.0);
outputs.data[cell_index * 6 + 1] = vec4(accum[1], 0.0);
outputs.data[cell_index * 6 + 2] = vec4(accum[2], 0.0);
outputs.data[cell_index * 6 + 3] = vec4(accum[3], 0.0);
outputs.data[cell_index * 6 + 4] = vec4(accum[4], 0.0);
outputs.data[cell_index * 6 + 5] = vec4(accum[5], 0.0);
#else
outputs.data[cell_index] = vec4(accum, 0.0);
#endif
#endif // MODE_SECOND_BOUNCE
/////////////////UPDATE MIPMAPS///////////////////////////////
#ifdef MODE_UPDATE_MIPMAPS
{
#ifdef MODE_ANISOTROPIC
vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
#else
vec3 light_accum = vec3(0.0);
#endif
float count = 0.0;
for (uint i = 0; i < 8; i++) {
uint child_index = cell_children.data[cell_index].children[i];
if (child_index == NO_CHILDREN) {
continue;
}
#ifdef MODE_ANISOTROPIC
light_accum[0] += outputs.data[child_index * 6 + 0].rgb;
light_accum[1] += outputs.data[child_index * 6 + 1].rgb;
light_accum[2] += outputs.data[child_index * 6 + 2].rgb;
light_accum[3] += outputs.data[child_index * 6 + 3].rgb;
light_accum[4] += outputs.data[child_index * 6 + 4].rgb;
light_accum[5] += outputs.data[child_index * 6 + 5].rgb;
#else
light_accum += outputs.data[child_index].rgb;
#endif
count += 1.0;
}
float divisor = mix(8.0, count, params.propagation);
#ifdef MODE_ANISOTROPIC
outputs.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0);
outputs.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0);
outputs.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0);
outputs.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0);
outputs.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0);
outputs.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0);
#else
outputs.data[cell_index] = vec4(light_accum / divisor, 0.0);
#endif
}
#endif
///////////////////WRITE TEXTURE/////////////////////////////
#ifdef MODE_WRITE_TEXTURE
{
#ifdef MODE_ANISOTROPIC
vec3 accum_total = vec3(0.0);
accum_total += outputs.data[cell_index * 6 + 0].rgb;
accum_total += outputs.data[cell_index * 6 + 1].rgb;
accum_total += outputs.data[cell_index * 6 + 2].rgb;
accum_total += outputs.data[cell_index * 6 + 3].rgb;
accum_total += outputs.data[cell_index * 6 + 4].rgb;
accum_total += outputs.data[cell_index * 6 + 5].rgb;
float accum_total_energy = max(dot(accum_total, GREY_VEC), 0.00001);
vec3 iso_positive = vec3(dot(outputs.data[cell_index * 6 + 0].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 2].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 4].rgb, GREY_VEC)) / vec3(accum_total_energy);
vec3 iso_negative = vec3(dot(outputs.data[cell_index * 6 + 1].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 3].rgb, GREY_VEC), dot(outputs.data[cell_index * 6 + 5].rgb, GREY_VEC)) / vec3(accum_total_energy);
{
uint aniso_pos = uint(clamp(iso_positive.b * 31.0, 0.0, 31.0));
aniso_pos |= uint(clamp(iso_positive.g * 63.0, 0.0, 63.0)) << 5;
aniso_pos |= uint(clamp(iso_positive.r * 31.0, 0.0, 31.0)) << 11;
imageStore(aniso_pos_tex, ivec3(posu), uvec4(aniso_pos));
}
{
uint aniso_neg = uint(clamp(iso_negative.b * 31.0, 0.0, 31.0));
aniso_neg |= uint(clamp(iso_negative.g * 63.0, 0.0, 63.0)) << 5;
aniso_neg |= uint(clamp(iso_negative.r * 31.0, 0.0, 31.0)) << 11;
imageStore(aniso_neg_tex, ivec3(posu), uvec4(aniso_neg));
}
imageStore(color_tex, ivec3(posu), vec4(accum_total / params.dynamic_range, albedo.a));
#else
imageStore(color_tex, ivec3(posu), vec4(outputs.data[cell_index].rgb / params.dynamic_range, albedo.a));
#endif
}
#endif
///////////////////DYNAMIC LIGHTING/////////////////////////////
#ifdef MODE_DYNAMIC
ivec2 pos_xy = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos_xy, params.rect_size))) {
return; //out of bounds
}
ivec2 uv_xy = pos_xy;
if (params.flip_x) {
uv_xy.x = params.rect_size.x - pos_xy.x - 1;
}
if (params.flip_y) {
uv_xy.y = params.rect_size.y - pos_xy.y - 1;
}
#ifdef MODE_DYNAMIC_LIGHTING
{
float z = params.z_base + imageLoad(depth, uv_xy).x * params.z_sign;
ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(z);
vec3 normal = imageLoad(source_normal, uv_xy).xyz * 2.0 - 1.0;
normal = vec3(params.x_dir) * normal.x * mix(1.0, -1.0, params.flip_x) + vec3(params.y_dir) * normal.y * mix(1.0, -1.0, params.flip_y) - vec3(params.z_dir) * normal.z;
vec4 albedo = imageLoad(source_albedo, uv_xy);
//determine the position in space
vec3 accum = vec3(0.0);
for (uint i = 0; i < params.light_count; i++) {
vec3 light;
vec3 light_dir;
if (!compute_light_at_pos(i, vec3(pos) * params.pos_multiplier, normal, light, light_dir)) {
continue;
}
light *= albedo.rgb;
accum += max(0.0, dot(normal, -light_dir)) * light;
}
accum += imageLoad(emission, uv_xy).xyz;
imageStore(emission, uv_xy, vec4(accum, albedo.a));
imageStore(depth, uv_xy, vec4(z));
}
#endif // MODE DYNAMIC LIGHTING
#ifdef MODE_DYNAMIC_SHRINK
{
vec4 accum = vec4(0.0);
float accum_z = 0.0;
float count = 0.0;
for (int i = 0; i < 4; i++) {
ivec2 ofs = pos_xy * 2 + ivec2(i & 1, i >> 1) - params.prev_rect_ofs;
if (any(lessThan(ofs, ivec2(0))) || any(greaterThanEqual(ofs, params.prev_rect_size))) {
continue;
}
if (params.flip_x) {
ofs.x = params.prev_rect_size.x - ofs.x - 1;
}
if (params.flip_y) {
ofs.y = params.prev_rect_size.y - ofs.y - 1;
}
vec4 light = imageLoad(source_light, ofs);
if (light.a == 0.0) { //ignore empty
continue;
}
accum += light;
float z = imageLoad(source_depth, ofs).x;
accum_z += z * 0.5; //shrink half too
count += 1.0;
}
if (params.on_mipmap) {
accum.rgb /= mix(8.0, count, params.propagation);
accum.a /= 8.0;
} else {
accum /= 4.0;
}
if (count == 0.0) {
accum_z = 0.0; //avoid nan
} else {
accum_z /= count;
}
#ifdef MODE_DYNAMIC_SHRINK_WRITE
imageStore(light, uv_xy, accum);
imageStore(depth, uv_xy, vec4(accum_z));
#endif
#ifdef MODE_DYNAMIC_SHRINK_PLOT
if (accum.a < 0.001) {
return; //do not blit if alpha is too low
}
ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(accum_z);
float z_frac = fract(accum_z);
for (int i = 0; i < 2; i++) {
ivec3 pos3d = pos + abs(params.z_dir) * i;
if (any(lessThan(pos3d, ivec3(0))) || any(greaterThanEqual(pos3d, params.limits))) {
//skip if offlimits
continue;
}
vec4 color_blit = accum * (i == 0 ? 1.0 - z_frac : z_frac);
vec4 color = imageLoad(color_texture, pos3d);
color.rgb *= params.dynamic_range;
#if 0
color.rgb = mix(color.rgb,color_blit.rgb,color_blit.a);
color.a+=color_blit.a;
#else
float sa = 1.0 - color_blit.a;
vec4 result;
result.a = color.a * sa + color_blit.a;
if (result.a == 0.0) {
result = vec4(0.0);
} else {
result.rgb = (color.rgb * color.a * sa + color_blit.rgb * color_blit.a) / result.a;
color = result;
}
#endif
color.rgb /= params.dynamic_range;
imageStore(color_texture, pos3d, color);
//imageStore(color_texture,pos3d,vec4(1,1,1,1));
#ifdef MODE_ANISOTROPIC
//do not care about anisotropy for dynamic objects, just store full lit in all directions
imageStore(aniso_pos_texture, pos3d, uvec4(0xFFFF));
imageStore(aniso_neg_texture, pos3d, uvec4(0xFFFF));
#endif // ANISOTROPIC
}
#endif // MODE_DYNAMIC_SHRINK_PLOT
}
#endif
#endif // MODE DYNAMIC
}

View File

@@ -0,0 +1,208 @@
/* clang-format off */
[vertex]
#version 450
VERSION_DEFINES
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
/* clang-format on */
layout(set = 0, binding = 1, std140) buffer CellDataBuffer {
CellData data[];
}
cell_data;
layout(set = 0, binding = 2) uniform texture3D color_tex;
layout(set = 0, binding = 3) uniform sampler tex_sampler;
#ifdef USE_ANISOTROPY
layout(set = 0, binding = 4) uniform texture3D aniso_pos_tex;
layout(set = 0, binding = 5) uniform texture3D aniso_neg_tex;
#endif
layout(push_constant, binding = 0, std430) uniform Params {
mat4 projection;
uint cell_offset;
float dynamic_range;
float alpha;
uint level;
ivec3 bounds;
uint pad;
}
params;
layout(location = 0) out vec4 color_interp;
void main() {
const vec3 cube_triangles[36] = vec3[](
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, -1.0f, 1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(-1.0f, -1.0f, 1.0f),
vec3(-1.0f, -1.0f, -1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(-1.0f, -1.0f, 1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(1.0f, -1.0f, -1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(1.0f, -1.0f, 1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(1.0f, 1.0f, -1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(-1.0f, 1.0f, -1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(1.0f, 1.0f, 1.0f),
vec3(-1.0f, 1.0f, 1.0f),
vec3(1.0f, -1.0f, 1.0f));
vec3 vertex = cube_triangles[gl_VertexIndex] * 0.5 + 0.5;
#ifdef MODE_DEBUG_LIGHT_FULL
uvec3 posu = uvec3(gl_InstanceIndex % params.bounds.x, (gl_InstanceIndex / params.bounds.x) % params.bounds.y, gl_InstanceIndex / (params.bounds.y * params.bounds.x));
#else
uint cell_index = gl_InstanceIndex + params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21);
#endif
#ifdef MODE_DEBUG_EMISSION
color_interp.xyz = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff, (cell_data.data[cell_index].emission >> 9) & 0x1ff, (cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
#endif
#ifdef MODE_DEBUG_COLOR
color_interp.xyz = unpackUnorm4x8(cell_data.data[cell_index].albedo).xyz;
#endif
#ifdef MODE_DEBUG_LIGHT
#ifdef USE_ANISOTROPY
#define POS_X 0
#define POS_Y 1
#define POS_Z 2
#define NEG_X 3
#define NEG_Y 4
#define NEG_Z 5
const uint triangle_aniso[12] = uint[](
NEG_X,
NEG_Z,
NEG_Y,
NEG_Z,
NEG_X,
NEG_Y,
POS_Z,
POS_X,
POS_X,
POS_Y,
POS_Y,
POS_Z);
color_interp.xyz = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level)).xyz * params.dynamic_range;
vec3 aniso_pos = texelFetch(sampler3D(aniso_pos_tex, tex_sampler), ivec3(posu), int(params.level)).xyz;
vec3 aniso_neg = texelFetch(sampler3D(aniso_neg_tex, tex_sampler), ivec3(posu), int(params.level)).xyz;
uint side = triangle_aniso[gl_VertexIndex / 3];
float strength = 0.0;
switch (side) {
case POS_X: strength = aniso_pos.x; break;
case POS_Y: strength = aniso_pos.y; break;
case POS_Z: strength = aniso_pos.z; break;
case NEG_X: strength = aniso_neg.x; break;
case NEG_Y: strength = aniso_neg.y; break;
case NEG_Z: strength = aniso_neg.z; break;
}
color_interp.xyz *= strength;
#else
color_interp = texelFetch(sampler3D(color_tex, tex_sampler), ivec3(posu), int(params.level));
color_interp.xyz *params.dynamic_range;
#endif
#endif
float scale = (1 << params.level);
gl_Position = params.projection * vec4((vec3(posu) + vertex) * scale, 1.0);
#ifdef MODE_DEBUG_LIGHT_FULL
if (color_interp.a == 0.0) {
gl_Position = vec4(0.0); //force clip and not draw
}
#else
color_interp.a = params.alpha;
#endif
}
/* clang-format off */
[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) in vec4 color_interp;
/* clang-format on */
layout(location = 0) out vec4 frag_color;
void main() {
frag_color = color_interp;
#ifdef MODE_DEBUG_LIGHT_FULL
//there really is no alpha, so use dither
int x = int(gl_FragCoord.x) % 4;
int y = int(gl_FragCoord.y) % 4;
int index = x + y * 4;
float limit = 0.0;
if (x < 8) {
if (index == 0) limit = 0.0625;
if (index == 1) limit = 0.5625;
if (index == 2) limit = 0.1875;
if (index == 3) limit = 0.6875;
if (index == 4) limit = 0.8125;
if (index == 5) limit = 0.3125;
if (index == 6) limit = 0.9375;
if (index == 7) limit = 0.4375;
if (index == 8) limit = 0.25;
if (index == 9) limit = 0.75;
if (index == 10) limit = 0.125;
if (index == 11) limit = 0.625;
if (index == 12) limit = 1.0;
if (index == 13) limit = 0.5;
if (index == 14) limit = 0.875;
if (index == 15) limit = 0.375;
}
if (frag_color.a < limit) {
discard;
}
#endif
}

View File

@@ -0,0 +1,187 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
/* clang-format on */
#define MAX_DISTANCE 100000
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333, 0.33333, 0.33333)
struct CellChildren {
uint children[8];
};
layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer {
CellChildren data[];
}
cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 2, std430) buffer CellDataBuffer {
CellData data[];
}
cell_data;
layout(r8ui, set = 0, binding = 3) uniform restrict writeonly uimage3D sdf_tex;
layout(push_constant, binding = 0, std430) uniform Params {
uint offset;
uint end;
uint pad0;
uint pad1;
}
params;
void main() {
vec3 pos = vec3(gl_GlobalInvocationID);
float closest_dist = 100000.0;
for (uint i = params.offset; i < params.end; i++) {
vec3 posu = vec3(uvec3(cell_data.data[i].position & 0x7FF, (cell_data.data[i].position >> 11) & 0x3FF, cell_data.data[i].position >> 21));
float dist = length(pos - posu);
if (dist < closest_dist) {
closest_dist = dist;
}
}
uint dist_8;
if (closest_dist < 0.0001) { // same cell
dist_8 = 0; //equals to -1
} else {
dist_8 = clamp(uint(closest_dist), 0, 254) + 1; //conservative, 0 is 1, so <1 is considered solid
}
imageStore(sdf_tex, ivec3(gl_GlobalInvocationID), uvec4(dist_8));
//imageStore(sdf_tex,pos,uvec4(pos*2,0));
}
#if 0
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
} params;
float distance_to_aabb(ivec3 pos, ivec3 aabb_pos, ivec3 aabb_size) {
vec3 delta = vec3(max(ivec3(0), max(aabb_pos - pos, pos - (aabb_pos + aabb_size - ivec3(1)))));
return length(delta);
}
void main() {
ivec3 pos = ivec3(gl_GlobalInvocationID);
uint stack[10] = uint[](0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
uint stack_indices[10] = uint[](0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
ivec3 stack_positions[10] = ivec3[](ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0), ivec3(0));
const uint cell_orders[8] = uint[](
0x11f58d1,
0xe2e70a,
0xd47463,
0xbb829c,
0x8d11f5,
0x70ae2e,
0x463d47,
0x29cbb8);
bool cell_found = false;
bool cell_found_exact = false;
ivec3 closest_cell_pos;
float closest_distance = MAX_DISTANCE;
int stack_pos = 0;
while (true) {
uint index = stack_indices[stack_pos] >> 24;
if (index == 8) {
//go up
if (stack_pos == 0) {
break; //done going through octree
}
stack_pos--;
continue;
}
stack_indices[stack_pos] = (stack_indices[stack_pos] & ((1 << 24) - 1)) | ((index + 1) << 24);
uint cell_index = (stack_indices[stack_pos] >> (index * 3)) & 0x7;
uint child_cell = cell_children.data[stack[stack_pos]].children[cell_index];
if (child_cell == NO_CHILDREN) {
continue;
}
ivec3 child_cell_size = params.limits >> (stack_pos + 1);
ivec3 child_cell_pos = stack_positions[stack_pos];
child_cell_pos += mix(ivec3(0), child_cell_size, bvec3(uvec3(index & 1, index & 2, index & 4) != uvec3(0)));
bool is_leaf = stack_pos == (params.stack_size - 2);
if (child_cell_pos == pos && is_leaf) {
//we may actually end up in the exact cell.
//if this happens, just abort
cell_found_exact = true;
break;
}
if (cell_found) {
//discard by distance
float distance = distance_to_aabb(pos, child_cell_pos, child_cell_size);
if (distance >= closest_distance) {
continue; //pointless, just test next child
} else if (is_leaf) {
//closer than what we have AND end of stack, save and continue
closest_cell_pos = child_cell_pos;
closest_distance = distance;
continue;
}
} else if (is_leaf) {
//first solid cell we find, save and continue
closest_distance = distance_to_aabb(pos, child_cell_pos, child_cell_size);
closest_cell_pos = child_cell_pos;
cell_found = true;
continue;
}
bvec3 direction = greaterThan((pos - (child_cell_pos + (child_cell_size >> 1))), ivec3(0));
uint cell_order = 0;
cell_order |= mix(0, 1, direction.x);
cell_order |= mix(0, 2, direction.y);
cell_order |= mix(0, 4, direction.z);
stack[stack_pos + 1] = child_cell;
stack_indices[stack_pos + 1] = cell_orders[cell_order]; //start counting
stack_positions[stack_pos + 1] = child_cell_pos;
stack_pos++; //go up stack
}
uint dist_8;
if (cell_found_exact) {
dist_8 = 0; //equals to -1
} else {
float closest_distance = length(vec3(pos - closest_cell_pos));
dist_8 = clamp(uint(closest_distance), 0, 254) + 1; //conservative, 0 is 1, so <1 is considered solid
}
imageStore(sdf_tex, pos, uvec4(dist_8));
}
#endif

View File

@@ -0,0 +1,335 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
/* clang-format on */
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333, 0.33333, 0.33333)
struct CellChildren {
uint children[8];
};
layout(set = 0, binding = 1, std430) buffer CellChildrenBuffer {
CellChildren data[];
}
cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set = 0, binding = 2, std430) buffer CellDataBuffer {
CellData data[];
}
cell_data;
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
#define LIGHT_TYPE_SPOT 2
#ifdef MODE_COMPUTE_LIGHT
struct Light {
uint type;
float energy;
float radius;
float attenuation;
vec3 color;
float spot_angle_radians;
vec3 position;
float spot_attenuation;
vec3 direction;
bool has_shadow;
};
layout(set = 0, binding = 3, std140) uniform Lights {
Light data[MAX_LIGHTS];
}
lights;
#endif
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
float emission_scale;
float propagation;
float dynamic_range;
uint light_count;
uint cell_offset;
uint cell_count;
uint pad[2];
}
params;
layout(set = 0, binding = 4, std140) uniform Outputs {
vec4 data[];
}
output;
#ifdef MODE_COMPUTE_LIGHT
uint raymarch(float distance, float distance_adv, vec3 from, vec3 direction) {
uint result = NO_CHILDREN;
ivec3 size = ivec3(max(max(params.limits.x, params.limits.y), params.limits.z));
while (distance > -distance_adv) { //use this to avoid precision errors
uint cell = 0;
ivec3 pos = ivec3(from);
if (all(greaterThanEqual(pos, ivec3(0))) && all(lessThan(pos, size))) {
ivec3 ofs = ivec3(0);
ivec3 half_size = size / 2;
for (int i = 0; i < params.stack_size - 1; i++) {
bvec3 greater = greaterThanEqual(pos, ofs + half_size);
ofs += mix(ivec3(0), half_size, greater);
uint child = 0; //wonder if this can be done faster
if (greater.x) {
child |= 1;
}
if (greater.y) {
child |= 2;
}
if (greater.z) {
child |= 4;
}
cell = cell_children.data[cell].children[child];
if (cell == NO_CHILDREN)
break;
half_size >>= ivec3(1);
}
if (cell != NO_CHILDREN) {
return cell; //found cell!
}
}
from += direction * distance_adv;
distance -= distance_adv;
}
return NO_CHILDREN;
}
bool compute_light_vector(uint light, uint cell, vec3 pos, out float attenuation, out vec3 light_pos) {
if (lights.data[light].type == LIGHT_TYPE_DIRECTIONAL) {
light_pos = pos - lights.data[light].direction * length(vec3(params.limits));
attenuation = 1.0;
} else {
light_pos = lights.data[light].position;
float distance = length(pos - light_pos);
if (distance >= lights.data[light].radius) {
return false;
}
attenuation = pow(clamp(1.0 - distance / lights.data[light].radius, 0.0001, 1.0), lights.data[light].attenuation);
if (lights.data[light].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(pos - light_pos);
float angle = acos(dot(rel, lights.data[light].direction));
if (angle > lights.data[light].spot_angle_radians) {
return false;
}
float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1);
attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation);
}
}
return true;
}
float get_normal_advance(vec3 p_normal) {
vec3 normal = p_normal;
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
return 1.0 / dot(normal, unorm);
}
#endif
void main() {
uint cell_index = gl_GlobalInvocationID.x;
if (cell_index >= params.cell_count) {
return;
}
cell_index += params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position & 0x7FF, (cell_data.data[cell_index].position >> 11) & 0x3FF, cell_data.data[cell_index].position >> 21);
vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo);
#ifdef MODE_COMPUTE_LIGHT
vec3 pos = vec3(posu) + vec3(0.5);
vec3 emission = vec3(ivec3(cell_data.data[cell_index].emission & 0x3FF, (cell_data.data[cell_index].emission >> 10) & 0x7FF, cell_data.data[cell_index].emission >> 21)) * params.emission_scale;
vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal);
#ifdef MODE_ANISOTROPIC
vec3 accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
const vec3 accum_dirs[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
#else
vec3 accum = vec3(0.0);
#endif
for (uint i = 0; i < params.light_count; i++) {
float attenuation;
vec3 light_pos;
if (!compute_light_vector(i, cell_index, pos, attenuation, light_pos)) {
continue;
}
vec3 light_dir = pos - light_pos;
float distance = length(light_dir);
light_dir = normalize(light_dir);
if (length(normal.xyz) > 0.2 && dot(normal.xyz, light_dir) >= 0) {
continue; //not facing the light
}
if (lights.data[i].has_shadow) {
float distance_adv = get_normal_advance(light_dir);
distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always
vec3 from = pos - light_dir * distance; //approximate
from -= sign(light_dir) * 0.45; //go near the edge towards the light direction to avoid self occlusion
uint result = raymarch(distance, distance_adv, from, light_dir);
if (result != cell_index) {
continue; //was occluded
}
}
vec3 light = lights.data[i].color * albedo.rgb * attenuation * lights.data[i].energy;
#ifdef MODE_ANISOTROPIC
for (uint j = 0; j < 6; j++) {
accum[j] += max(0.0, dot(accum_dir, -light_dir)) * light + emission;
}
#else
if (length(normal.xyz) > 0.2) {
accum += max(0.0, dot(normal.xyz, -light_dir)) * light + emission;
} else {
//all directions
accum += light + emission;
}
#endif
}
#ifdef MODE_ANISOTROPIC
output.data[cell_index * 6 + 0] = vec4(accum[0], 0.0);
output.data[cell_index * 6 + 1] = vec4(accum[1], 0.0);
output.data[cell_index * 6 + 2] = vec4(accum[2], 0.0);
output.data[cell_index * 6 + 3] = vec4(accum[3], 0.0);
output.data[cell_index * 6 + 4] = vec4(accum[4], 0.0);
output.data[cell_index * 6 + 5] = vec4(accum[5], 0.0);
#else
output.data[cell_index] = vec4(accum, 0.0);
#endif
#endif //MODE_COMPUTE_LIGHT
#ifdef MODE_UPDATE_MIPMAPS
{
#ifdef MODE_ANISOTROPIC
vec3 light_accum[6] = vec3[](vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
#else
vec3 light_accum = vec3(0.0);
#endif
float count = 0.0;
for (uint i = 0; i < 8; i++) {
uint child_index = cell_children.data[cell_index].children[i];
if (child_index == NO_CHILDREN) {
continue;
}
#ifdef MODE_ANISOTROPIC
light_accum[1] += output.data[child_index * 6 + 0].rgb;
light_accum[2] += output.data[child_index * 6 + 1].rgb;
light_accum[3] += output.data[child_index * 6 + 2].rgb;
light_accum[4] += output.data[child_index * 6 + 3].rgb;
light_accum[5] += output.data[child_index * 6 + 4].rgb;
light_accum[6] += output.data[child_index * 6 + 5].rgb;
#else
light_accum += output.data[child_index].rgb;
#endif
count += 1.0;
}
float divisor = mix(8.0, count, params.propagation);
#ifdef MODE_ANISOTROPIC
output.data[cell_index * 6 + 0] = vec4(light_accum[0] / divisor, 0.0);
output.data[cell_index * 6 + 1] = vec4(light_accum[1] / divisor, 0.0);
output.data[cell_index * 6 + 2] = vec4(light_accum[2] / divisor, 0.0);
output.data[cell_index * 6 + 3] = vec4(light_accum[3] / divisor, 0.0);
output.data[cell_index * 6 + 4] = vec4(light_accum[4] / divisor, 0.0);
output.data[cell_index * 6 + 5] = vec4(light_accum[5] / divisor, 0.0);
#else
output.data[cell_index] = vec4(light_accum / divisor, 0.0);
#endif
}
#endif
#ifdef MODE_WRITE_TEXTURE
{
}
#endif
}

View File

@@ -0,0 +1,87 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
/* clang-format on */
shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE];
#ifdef READ_TEXTURE
//use for main texture
layout(set = 0, binding = 0) uniform sampler2D source_texture;
#else
//use for intermediate textures
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_luminance;
#endif
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_luminance;
#ifdef WRITE_LUMINANCE
layout(set = 2, binding = 0) uniform sampler2D prev_luminance;
#endif
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 source_size;
float max_luminance;
float min_luminance;
float exposure_adjust;
float pad[3];
}
params;
void main() {
uint t = gl_LocalInvocationID.y * BLOCK_SIZE + gl_LocalInvocationID.x;
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(lessThan(pos, params.source_size))) {
#ifdef READ_TEXTURE
vec3 v = texelFetch(source_texture, pos, 0).rgb;
tmp_data[t] = max(v.r, max(v.g, v.b));
#else
tmp_data[t] = imageLoad(source_luminance, pos).r;
#endif
} else {
tmp_data[t] = 0.0;
}
groupMemoryBarrier();
barrier();
uint size = (BLOCK_SIZE * BLOCK_SIZE) >> 1;
do {
if (t < size) {
tmp_data[t] += tmp_data[t + size];
}
groupMemoryBarrier();
barrier();
size >>= 1;
} while (size >= 1);
if (t == 0) {
//compute rect size
ivec2 rect_size = min(params.source_size - pos, ivec2(BLOCK_SIZE));
float avg = tmp_data[0] / float(rect_size.x * rect_size.y);
//float avg = tmp_data[0] / float(BLOCK_SIZE*BLOCK_SIZE);
pos /= ivec2(BLOCK_SIZE);
#ifdef WRITE_LUMINANCE
float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous exposure
avg = clamp(prev_lum + (avg - prev_lum) * params.exposure_adjust, params.min_luminance, params.max_luminance);
#endif
imageStore(dest_luminance, pos, vec4(avg));
}
}

View File

@@ -0,0 +1,73 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
/* clang-format on */
layout(set = 0, binding = 0) uniform sampler2D source_normal;
layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_roughness;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
float curve;
uint pad;
}
params;
#define HALF_PI 1.5707963267948966
void main() {
// Pixel being shaded
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.screen_size))) { //too large, do nothing
return;
}
vec3 normal_accum = vec3(0.0);
float accum = 0.0;
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
normal_accum += normalize(texelFetch(source_normal, pos + ivec2(i, j), 0).xyz * 2.0 - 1.0);
accum += 1.0;
}
}
normal_accum /= accum;
float r = length(normal_accum);
float limit;
if (r < 1.0) {
float threshold = 0.4;
/*
//Formula from Filament, does not make sense to me.
float r2 = r * r;
float kappa = (3.0f * r - r * r2) / (1.0f - r2);
float variance = 0.25f / kappa;
limit = sqrt(min(2.0f * variance, threshold * threshold));
//*/
/*
//Formula based on probability distribution graph
float width = acos(max(0.0,r)); // convert to angle (width)
float roughness = pow(width,1.7)*0.854492; //approximate (crappy) formula to convert to roughness
limit = min(sqrt(roughness), threshold); //convert to perceptual roughness and apply threshold
//*/
limit = min(sqrt(pow(acos(max(0.0, r)) / HALF_PI, params.curve)), threshold); //convert to perceptual roughness and apply threshold
//limit = 0.5;
} else {
limit = 0.0;
}
imageStore(dest_roughness, pos, vec4(limit));
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,266 @@
#define M_PI 3.14159265359
#define ROUGHNESS_MAX_LOD 5
layout(push_constant, binding = 0, std430) uniform DrawCall {
uint instance_index;
uint pad[3]; //16 bits minimum size
}
draw_call;
/* Set 0 Scene data that never changes, ever */
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
layout(set = 0, binding = 1) uniform sampler material_samplers[12];
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
layout(set = 0, binding = 3, std140) uniform SceneData {
mat4 projection_matrix;
mat4 inv_projection_matrix;
mat4 camera_matrix;
mat4 inv_camera_matrix;
vec2 viewport_size;
vec2 screen_pixel_size;
//used for shadow mapping only
float z_offset;
float z_slope_scale;
float time;
float reflection_multiplier; // one normally, zero when rendering reflections
vec4 ambient_light_color_energy;
float ambient_color_sky_mix;
bool use_ambient_light;
bool use_ambient_cubemap;
bool use_reflection_cubemap;
mat3 radiance_inverse_xform;
vec2 shadow_atlas_pixel_size;
vec2 directional_shadow_pixel_size;
uint directional_light_count;
float dual_paraboloid_side;
float z_far;
float z_near;
bool ssao_enabled;
float ssao_light_affect;
float ssao_ao_affect;
bool roughness_limiter_enabled;
vec4 ao_color;
#if 0
vec4 ambient_light_color;
vec4 bg_color;
vec4 fog_color_enabled;
vec4 fog_sun_color_amount;
float ambient_energy;
float bg_energy;
#endif
#if 0
vec2 shadow_atlas_pixel_size;
vec2 directional_shadow_pixel_size;
float z_far;
float subsurface_scatter_width;
float ambient_occlusion_affect_light;
float ambient_occlusion_affect_ao_channel;
float opaque_prepass_threshold;
bool fog_depth_enabled;
float fog_depth_begin;
float fog_depth_end;
float fog_density;
float fog_depth_curve;
bool fog_transmit_enabled;
float fog_transmit_curve;
bool fog_height_enabled;
float fog_height_min;
float fog_height_max;
float fog_height_curve;
#endif
}
scene_data;
#define INSTANCE_FLAGS_FORWARD_MASK 0x7
#define INSTANCE_FLAGS_FORWARD_OMNI_LIGHT_SHIFT 3
#define INSTANCE_FLAGS_FORWARD_SPOT_LIGHT_SHIFT 6
#define INSTANCE_FLAGS_FORWARD_DECAL_SHIFT 9
#define INSTANCE_FLAGS_MULTIMESH (1 << 12)
#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13)
#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
#define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15)
#define INSTANCE_FLAGS_MULTIMESH_STRIDE_SHIFT 16
//3 bits of stride
#define INSTANCE_FLAGS_MULTIMESH_STRIDE_MASK 0x7
#define INSTANCE_FLAGS_SKELETON (1 << 19)
struct InstanceData {
mat4 transform;
mat4 normal_transform;
uint flags;
uint instance_ofs; //instance_offset in instancing/skeleton buffer
uint gi_offset; //GI information when using lightmapping (VCT or lightmap)
uint layer_mask;
};
layout(set = 0, binding = 4, std430) buffer Instances {
InstanceData data[];
}
instances;
struct LightData { //this structure needs to be 128 bits
vec3 position;
float inv_radius;
vec3 direction;
uint attenuation_energy; //attenuation
uint color_specular; //rgb color, a specular (8 bit unorm)
uint cone_attenuation_angle; // attenuation and angle, (16bit float)
uint mask;
uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm)
vec4 atlas_rect; //used for shadow atlas uv on omni, and for projection atlas on spot
mat4 shadow_matrix;
};
layout(set = 0, binding = 5, std140) uniform Lights {
LightData data[MAX_LIGHT_DATA_STRUCTS];
}
lights;
struct ReflectionData {
vec3 box_extents;
float index;
vec3 box_offset;
uint mask;
vec4 params; // intensity, 0, interior , boxproject
vec4 ambient; // ambient color, energy
mat4 local_matrix; // up to here for spot and omni, rest is for directional
// notes: for ambientblend, use distance to edge to blend between already existing global environment
};
layout(set = 0, binding = 6, std140) uniform ReflectionProbeData {
ReflectionData data[MAX_REFLECTION_DATA_STRUCTS];
}
reflections;
struct DirectionalLightData {
vec3 direction;
float energy;
vec3 color;
float specular;
vec3 shadow_color;
uint mask;
bool blend_splits;
bool shadow_enabled;
float fade_from;
float fade_to;
vec4 shadow_split_offsets;
mat4 shadow_matrix1;
mat4 shadow_matrix2;
mat4 shadow_matrix3;
mat4 shadow_matrix4;
};
layout(set = 0, binding = 7, std140) uniform DirectionalLights {
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
struct GIProbeData {
mat4 xform;
vec3 bounds;
float dynamic_range;
float bias;
float normal_bias;
bool blend_ambient;
uint texture_slot;
float anisotropy_strength;
float ambient_occlusion;
float ambient_occlusion_size;
uint pad2;
};
layout(set = 0, binding = 8, std140) uniform GIProbes {
GIProbeData data[MAX_GI_PROBES];
}
gi_probes;
layout(set = 0, binding = 9) uniform texture3D gi_probe_textures[MAX_GI_PROBE_TEXTURES];
#define CLUSTER_COUNTER_SHIFT 20
#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1)
#define CLUSTER_COUNTER_MASK 0xfff
layout(set = 0, binding = 10) uniform utexture3D cluster_texture;
layout(set = 0, binding = 11, std430) buffer ClusterData {
uint indices[];
}
cluster_data;
layout(set = 0, binding = 12) uniform texture2D directional_shadow_atlas;
// decal atlas
/* Set 1, Radiance */
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
layout(set = 1, binding = 0) uniform textureCubeArray radiance_cubemap;
#else
layout(set = 1, binding = 0) uniform textureCube radiance_cubemap;
#endif
/* Set 2, Reflection and Shadow Atlases (view dependant) */
layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas;
layout(set = 2, binding = 1) uniform texture2D shadow_atlas;
/* Set 1, Render Buffers */
layout(set = 3, binding = 0) uniform texture2D depth_buffer;
layout(set = 3, binding = 1) uniform texture2D color_buffer;
layout(set = 3, binding = 2) uniform texture2D normal_buffer;
layout(set = 3, binding = 3) uniform texture2D roughness_buffer;
layout(set = 3, binding = 4) uniform texture2D ao_buffer;
/* Set 4 Skeleton & Instancing (Multimesh) */
layout(set = 4, binding = 0, std430) buffer Transforms {
vec4 data[];
}
transforms;
/* Set 5 User Material */

View File

@@ -0,0 +1,181 @@
/* clang-format off */
[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
layout(push_constant, binding = 1, std430) uniform Params {
mat3 orientation;
vec4 proj;
vec4 position_multiplier;
float time;
}
params;
void main() {
vec2 base_arr[4] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(1.0, -1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 1.0, 1.0);
}
/* clang-format off */
[fragment]
#version 450
VERSION_DEFINES
#define M_PI 3.14159265359
layout(location = 0) in vec2 uv_interp;
/* clang-format on */
layout(push_constant, binding = 1, std430) uniform Params {
mat3 orientation;
vec4 proj;
vec4 position_multiplier;
float time; //TODO consider adding vec2 screen res, and float radiance size
}
params;
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
layout(set = 0, binding = 0) uniform sampler material_samplers[12];
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
layout(set = 2, binding = 0) uniform textureCube radiance;
#ifdef USE_CUBEMAP_PASS
layout(set = 2, binding = 1) uniform textureCube half_res;
layout(set = 2, binding = 2) uniform textureCube quarter_res;
#else
layout(set = 2, binding = 1) uniform texture2D half_res;
layout(set = 2, binding = 2) uniform texture2D quarter_res;
#endif
#ifdef USE_CUBEMAP_PASS
#define AT_CUBEMAP_PASS true
#else
#define AT_CUBEMAP_PASS false
#endif
#ifdef USE_HALF_RES_PASS
#define AT_HALF_RES_PASS true
#else
#define AT_HALF_RES_PASS false
#endif
#ifdef USE_QUARTER_RES_PASS
#define AT_QUARTER_RES_PASS true
#else
#define AT_QUARTER_RES_PASS false
#endif
struct DirectionalLightData {
vec3 direction;
float energy;
vec3 color;
bool enabled;
};
layout(set = 3, binding = 0, std140) uniform DirectionalLights {
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
/* clang-format off */
FRAGMENT_SHADER_GLOBALS
/* clang-format on */
layout(location = 0) out vec4 frag_color;
void main() {
vec3 cube_normal;
cube_normal.z = -1.0;
cube_normal.x = (cube_normal.z * (-uv_interp.x - params.proj.x)) / params.proj.y;
cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.proj.z)) / params.proj.w;
cube_normal = mat3(params.orientation) * cube_normal;
cube_normal.z = -cube_normal.z;
cube_normal = normalize(cube_normal);
vec2 uv = uv_interp * 0.5 + 0.5;
vec2 panorama_coords = vec2(atan(cube_normal.x, cube_normal.z), acos(cube_normal.y));
if (panorama_coords.x < 0.0) {
panorama_coords.x += M_PI * 2.0;
}
panorama_coords /= vec2(M_PI * 2.0, M_PI);
vec3 color = vec3(0.0, 0.0, 0.0);
float alpha = 1.0; // Only available to subpasses
vec4 half_res_color = vec4(1.0);
vec4 quarter_res_color = vec4(1.0);
#ifdef USE_CUBEMAP_PASS
float using_cubemap = 1.0;
#ifdef USES_HALF_RES_COLOR
half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal);
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal);
#endif
#else
float using_cubemap = 0.0;
#ifdef USES_HALF_RES_COLOR
half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
#endif
#endif
// unused, just here to make our compiler happy, make sure we don't execute any light code the user adds in..
#ifndef REALLYINCLUDETHIS
{
/* clang-format off */
LIGHT_SHADER_CODE
/* clang-format on */
}
#endif
{
/* clang-format off */
FRAGMENT_SHADER_CODE
/* clang-format on */
}
frag_color.rgb = color * params.position_multiplier.w;
frag_color.a = alpha;
}

View File

@@ -0,0 +1,252 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
/* clang-format on */
#define TWO_PI 6.283185307179586476925286766559
#ifdef SSAO_QUALITY_HIGH
#define NUM_SAMPLES (20)
#endif
#ifdef SSAO_QUALITY_ULTRA
#define NUM_SAMPLES (48)
#endif
#ifdef SSAO_QUALITY_LOW
#define NUM_SAMPLES (8)
#endif
#if !defined(SSAO_QUALITY_LOW) && !defined(SSAO_QUALITY_HIGH) && !defined(SSAO_QUALITY_ULTRA)
#define NUM_SAMPLES (12)
#endif
// If using depth mip levels, the log of the maximum pixel offset before we need to switch to a lower
// miplevel to maintain reasonable spatial locality in the cache
// If this number is too small (< 3), too many taps will land in the same pixel, and we'll get bad variance that manifests as flashing.
// If it is too high (> 5), we'll get bad performance because we're not using the MIP levels effectively
#define LOG_MAX_OFFSET (3)
// This must be less than or equal to the MAX_MIP_LEVEL defined in SSAO.cpp
#define MAX_MIP_LEVEL (4)
// This is the number of turns around the circle that the spiral pattern makes. This should be prime to prevent
// taps from lining up. This particular choice was tuned for NUM_SAMPLES == 9
const int ROTATIONS[] = int[](
1, 1, 2, 3, 2, 5, 2, 3, 2,
3, 3, 5, 5, 3, 4, 7, 5, 5, 7,
9, 8, 5, 5, 7, 7, 7, 8, 5, 8,
11, 12, 7, 10, 13, 8, 11, 8, 7, 14,
11, 11, 13, 12, 13, 19, 17, 13, 11, 18,
19, 11, 11, 14, 17, 21, 15, 16, 17, 18,
13, 17, 11, 17, 19, 18, 25, 18, 19, 19,
29, 21, 19, 27, 31, 29, 21, 18, 17, 29,
31, 31, 23, 18, 25, 26, 25, 23, 19, 34,
19, 27, 21, 25, 39, 29, 17, 21, 27);
/* clang-format on */
//#define NUM_SPIRAL_TURNS (7)
const int NUM_SPIRAL_TURNS = ROTATIONS[NUM_SAMPLES - 1];
layout(set = 0, binding = 0) uniform sampler2D source_depth_mipmaps;
layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
#ifndef USE_HALF_SIZE
layout(set = 2, binding = 0) uniform sampler2D source_depth;
#endif
layout(set = 3, binding = 0) uniform sampler2D source_normal;
layout(push_constant, binding = 1, std430) uniform Params {
ivec2 screen_size;
float z_far;
float z_near;
bool orthogonal;
float intensity_div_r6;
float radius;
float bias;
vec4 proj_info;
vec2 pixel_size;
float proj_scale;
uint pad;
}
params;
vec3 reconstructCSPosition(vec2 S, float z) {
if (params.orthogonal) {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw), z);
} else {
return vec3((S.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
}
}
vec3 getPosition(ivec2 ssP) {
vec3 P;
#ifdef USE_HALF_SIZE
P.z = texelFetch(source_depth_mipmaps, ssP, 0).r;
P.z = -P.z;
#else
P.z = texelFetch(source_depth, ssP, 0).r;
P.z = P.z * 2.0 - 1.0;
if (params.orthogonal) {
P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near));
}
P.z = -P.z;
#endif
// Offset to pixel center
P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
return P;
}
/** Returns a unit vector and a screen-space radius for the tap on a unit disk (the caller should scale by the actual disk radius) */
vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR) {
// Radius relative to ssR
float alpha = (float(sampleNumber) + 0.5) * (1.0 / float(NUM_SAMPLES));
float angle = alpha * (float(NUM_SPIRAL_TURNS) * 6.28) + spinAngle;
ssR = alpha;
return vec2(cos(angle), sin(angle));
}
/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1 */
vec3 getOffsetPosition(ivec2 ssP, float ssR) {
// Derivation:
// mipLevel = floor(log(ssR / MAX_OFFSET));
int mipLevel = clamp(int(floor(log2(ssR))) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
vec3 P;
// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
// Manually clamp to the texture size because texelFetch bypasses the texture unit
ivec2 mipP = clamp(ssP >> mipLevel, ivec2(0), (params.screen_size >> mipLevel) - ivec2(1));
#ifdef USE_HALF_SIZE
P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel).r;
P.z = -P.z;
#else
if (mipLevel < 1) {
//read from depth buffer
P.z = texelFetch(source_depth, mipP, 0).r;
P.z = P.z * 2.0 - 1.0;
if (params.orthogonal) {
P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near));
}
P.z = -P.z;
} else {
//read from mipmaps
P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel - 1).r;
P.z = -P.z;
}
#endif
// Offset to pixel center
P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
return P;
}
/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds
to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling radius \a ssDiskRadius
Note that units of H() in the HPG12 paper are meters, not
unitless. The whole falloff/sampling function is therefore
unitless. In this implementation, we factor out (9 / radius).
Four versions of the falloff function are implemented below
*/
float sampleAO(in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in float p_radius, in int tapIndex, in float randomPatternRotationAngle) {
// Offset on the unit disk, spun for this pixel
float ssR;
vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR);
ssR *= ssDiskRadius;
ivec2 ssP = ivec2(ssR * unitOffset) + ssC;
if (any(lessThan(ssP, ivec2(0))) || any(greaterThanEqual(ssP, params.screen_size))) {
return 0.0;
}
// The occluding point in camera space
vec3 Q = getOffsetPosition(ssP, ssR);
vec3 v = Q - C;
float vv = dot(v, v);
float vn = dot(v, n_C);
const float epsilon = 0.01;
float radius2 = p_radius * p_radius;
// A: From the HPG12 paper
// Note large epsilon to avoid overdarkening within cracks
//return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6;
// B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended]
float f = max(radius2 - vv, 0.0);
return f * f * f * max((vn - params.bias) / (epsilon + vv), 0.0);
// C: Medium contrast (which looks better at high radii), no division. Note that the
// contribution still falls off with radius^2, but we've adjusted the rate in a way that is
// more computationally efficient and happens to be aesthetically pleasing.
// return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0);
// D: Low contrast, no division operation
// return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0);
}
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(ssC, params.screen_size))) { //too large, do nothing
return;
}
// World space point being shaded
vec3 C = getPosition(ssC);
#ifdef USE_HALF_SIZE
vec3 n_C = texelFetch(source_normal, ssC << 1, 0).xyz * 2.0 - 1.0;
#else
vec3 n_C = texelFetch(source_normal, ssC, 0).xyz * 2.0 - 1.0;
#endif
n_C = normalize(n_C);
n_C.y = -n_C.y; //because this code reads flipped
// Hash function used in the HPG12 AlchemyAO paper
float randomPatternRotationAngle = mod(float((3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10), TWO_PI);
// Reconstruct normals from positions. These will lead to 1-pixel black lines
// at depth discontinuities, however the blur will wipe those out so they are not visible
// in the final image.
// Choose the screen-space sample radius
// proportional to the projected area of the sphere
float ssDiskRadius = -params.proj_scale * params.radius;
if (!params.orthogonal) {
ssDiskRadius = -params.proj_scale * params.radius / C.z;
}
float sum = 0.0;
for (int i = 0; i < NUM_SAMPLES; ++i) {
sum += sampleAO(ssC, C, n_C, ssDiskRadius, params.radius, i, randomPatternRotationAngle);
}
float A = max(0.0, 1.0 - sum * params.intensity_div_r6 * (5.0 / float(NUM_SAMPLES)));
imageStore(dest_image, ssC, vec4(A));
}

View File

@@ -0,0 +1,157 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
/* clang-format on */
layout(set = 0, binding = 0) uniform sampler2D source_ssao;
layout(set = 1, binding = 0) uniform sampler2D source_depth;
#ifdef MODE_UPSCALE
layout(set = 2, binding = 0) uniform sampler2D source_depth_mipmaps;
#endif
layout(r8, set = 3, binding = 0) uniform restrict writeonly image2D dest_image;
//////////////////////////////////////////////////////////////////////////////////////////////
// Tunable Parameters:
layout(push_constant, binding = 1, std430) uniform Params {
float edge_sharpness; /** Increase to make depth edges crisper. Decrease to reduce flicker. */
int filter_scale;
float z_far;
float z_near;
bool orthogonal;
uint pad0;
uint pad1;
uint pad2;
ivec2 axis; /** (1, 0) or (0, 1) */
ivec2 screen_size;
}
params;
/** Filter radius in pixels. This will be multiplied by SCALE. */
#define R (4)
//////////////////////////////////////////////////////////////////////////////////////////////
// Gaussian coefficients
const float gaussian[R + 1] =
//float[](0.356642, 0.239400, 0.072410, 0.009869);
//float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0
float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0
//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0
void main() {
// Pixel being shaded
ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(ssC, params.screen_size))) { //too large, do nothing
return;
}
#ifdef MODE_UPSCALE
//closest one should be the same pixel, but check nearby just in case
float depth = texelFetch(source_depth, ssC, 0).r;
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
vec2 pixel_size = 1.0 / vec2(params.screen_size);
vec2 closest_uv = vec2(ssC) * pixel_size + pixel_size * 0.5;
vec2 from_uv = closest_uv;
vec2 ps2 = pixel_size; // * 2.0;
float closest_depth = abs(textureLod(source_depth_mipmaps, closest_uv, 0.0).r - depth);
vec2 offsets[4] = vec2[](vec2(ps2.x, 0), vec2(-ps2.x, 0), vec2(0, ps2.y), vec2(0, -ps2.y));
for (int i = 0; i < 4; i++) {
vec2 neighbour = from_uv + offsets[i];
float neighbour_depth = abs(textureLod(source_depth_mipmaps, neighbour, 0.0).r - depth);
if (neighbour_depth < closest_depth) {
closest_uv = neighbour;
closest_depth = neighbour_depth;
}
}
float visibility = textureLod(source_ssao, closest_uv, 0.0).r;
imageStore(dest_image, ssC, vec4(visibility));
#else
float depth = texelFetch(source_depth, ssC, 0).r;
#ifdef MODE_FULL_SIZE
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
#endif
float depth_divide = 1.0 / params.z_far;
//depth *= depth_divide;
/*
if (depth > params.z_far * 0.999) {
discard; //skybox
}
*/
float sum = texelFetch(source_ssao, ssC, 0).r;
// Base weight for depth falloff. Increase this for more blurriness,
// decrease it for better edge discrimination
float BASE = gaussian[0];
float totalWeight = BASE;
sum *= totalWeight;
ivec2 clamp_limit = params.screen_size - ivec2(1);
for (int r = -R; r <= R; ++r) {
// We already handled the zero case above. This loop should be unrolled and the static branch optimized out,
// so the IF statement has no runtime cost
if (r != 0) {
ivec2 ppos = ssC + params.axis * (r * params.filter_scale);
float value = texelFetch(source_ssao, clamp(ppos, ivec2(0), clamp_limit), 0).r;
ivec2 rpos = clamp(ppos, ivec2(0), clamp_limit);
float temp_depth = texelFetch(source_depth, rpos, 0).r;
#ifdef MODE_FULL_SIZE
temp_depth = temp_depth * 2.0 - 1.0;
if (params.orthogonal) {
temp_depth = ((temp_depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
temp_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - temp_depth * (params.z_far - params.z_near));
}
//temp_depth *= depth_divide;
#endif
// spatial domain: offset gaussian tap
float weight = 0.3 + gaussian[abs(r)];
//weight *= max(0.0, dot(temp_normal, normal));
// range domain (the "bilateral" weight). As depth difference increases, decrease weight.
weight *= max(0.0, 1.0 - params.edge_sharpness * abs(temp_depth - depth));
sum += value * weight;
totalWeight += weight;
}
}
const float epsilon = 0.0001;
float visibility = sum / (totalWeight + epsilon);
imageStore(dest_image, ssC, vec4(visibility));
#endif
}

View File

@@ -0,0 +1,48 @@
/* clang-format off */
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
/* clang-format on */
layout(push_constant, binding = 1, std430) uniform Params {
vec2 pixel_size;
float z_far;
float z_near;
ivec2 source_size;
bool orthogonal;
uint pad;
}
params;
#ifdef MINIFY_START
layout(set = 0, binding = 0) uniform sampler2D source_texture;
#else
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_image;
#endif
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
void main() {
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThan(pos, params.source_size >> 1))) { //too large, do nothing
return;
}
#ifdef MINIFY_START
float depth = texelFetch(source_texture, pos << 1, 0).r * 2.0 - 1.0;
if (params.orthogonal) {
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
} else {
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
}
#else
float depth = imageLoad(source_image, pos << 1).r;
#endif
imageStore(dest_image, pos, vec4(depth));
}

View File

@@ -0,0 +1,305 @@
/* clang-format off */
[vertex]
#version 450
VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
}
/* clang-format off */
[fragment]
#version 450
VERSION_DEFINES
layout(location = 0) in vec2 uv_interp;
/* clang-format on */
layout(set = 0, binding = 0) uniform sampler2D source_color;
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
layout(set = 2, binding = 0) uniform sampler2D source_glow;
layout(set = 3, binding = 0) uniform sampler3D color_correction;
layout(push_constant, binding = 1, std430) uniform Params {
vec3 bcs;
bool use_bcs;
bool use_glow;
bool use_auto_exposure;
bool use_color_correction;
uint tonemapper;
uvec2 glow_texture_size;
float glow_intensity;
uint glow_level_flags;
uint glow_mode;
float exposure;
float white;
float auto_exposure_grey;
}
params;
layout(location = 0) out vec4 frag_color;
#ifdef USE_GLOW_FILTER_BICUBIC
// w0, w1, w2, and w3 are the four cubic B-spline basis functions
float w0(float a) {
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f);
}
float w1(float a) {
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
}
float w2(float a) {
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
}
float w3(float a) {
return (1.0f / 6.0f) * (a * a * a);
}
// g0 and g1 are the two amplitude functions
float g0(float a) {
return w0(a) + w1(a);
}
float g1(float a) {
return w2(a) + w3(a);
}
// h0 and h1 are the two offset functions
float h0(float a) {
return -1.0f + w1(a) / (w0(a) + w1(a));
}
float h1(float a) {
return 1.0f + w3(a) / (w2(a) + w3(a));
}
vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) {
float lod = float(p_lod);
vec2 tex_size = vec2(params.glow_texture_size >> p_lod);
vec2 pixel_size = vec2(1.0f) / tex_size;
uv = uv * tex_size + vec2(0.5f);
vec2 iuv = floor(uv);
vec2 fuv = fract(uv);
float g0x = g0(fuv.x);
float g1x = g1(fuv.x);
float h0x = h0(fuv.x);
float h1x = h1(fuv.x);
float h0y = h0(fuv.y);
float h1y = h1(fuv.y);
vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5f)) * pixel_size;
vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size;
return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
(g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
}
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod)
#else
#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
#endif
vec3 tonemap_filmic(vec3 color, float white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
// has no effect on the curve's general shape or visual properties
const float exposure_bias = 2.0f;
const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
const float B = 0.30f * exposure_bias;
const float C = 0.10f;
const float D = 0.20f;
const float E = 0.01f;
const float F = 0.30f;
vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F;
return color_tonemapped / white_tonemapped;
}
vec3 tonemap_aces(vec3 color, float white) {
const float exposure_bias = 0.85f;
const float A = 2.51f * exposure_bias * exposure_bias;
const float B = 0.03f * exposure_bias;
const float C = 2.43f * exposure_bias * exposure_bias;
const float D = 0.59f * exposure_bias;
const float E = 0.14f;
vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E);
float white_tonemapped = (white * (A * white + B)) / (white * (C * white + D) + E);
return color_tonemapped / white_tonemapped;
}
vec3 tonemap_reinhard(vec3 color, float white) {
return (white * color + color) / (color * white + white);
}
vec3 linear_to_srgb(vec3 color) {
//if going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color
if (params.tonemapper == TONEMAPPER_LINEAR) {
return color;
} else if (params.tonemapper == TONEMAPPER_REINHARD) {
return tonemap_reinhard(color, white);
} else if (params.tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(color, white);
} else { //aces
return tonemap_aces(color, white);
}
}
vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels
vec3 glow = vec3(0.0f);
if (bool(params.glow_level_flags & (1 << 0))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 0).rgb;
}
if (bool(params.glow_level_flags & (1 << 1))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 1).rgb;
}
if (bool(params.glow_level_flags & (1 << 2))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 2).rgb;
}
if (bool(params.glow_level_flags & (1 << 3))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 3).rgb;
}
if (bool(params.glow_level_flags & (1 << 4))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 4).rgb;
}
if (bool(params.glow_level_flags & (1 << 5))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 5).rgb;
}
if (bool(params.glow_level_flags & (1 << 6))) {
glow += GLOW_TEXTURE_SAMPLE(tex, uv, 6).rgb;
}
return glow;
}
#define GLOW_MODE_ADD 0
#define GLOW_MODE_SCREEN 1
#define GLOW_MODE_SOFTLIGHT 2
#define GLOW_MODE_REPLACE 3
#define GLOW_MODE_MIX 4
vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode
if (params.glow_mode == GLOW_MODE_ADD) {
return color + glow;
} else if (params.glow_mode == GLOW_MODE_SCREEN) {
//need color clamping
return max((color + glow) - (color * glow), vec3(0.0));
} else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) {
//need color clamping
glow = glow * vec3(0.5f) + vec3(0.5f);
color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r)));
color.g = (glow.g <= 0.5f) ? (color.g - (1.0f - 2.0f * glow.g) * color.g * (1.0f - color.g)) : (((glow.g > 0.5f) && (color.g <= 0.25f)) ? (color.g + (2.0f * glow.g - 1.0f) * (4.0f * color.g * (4.0f * color.g + 1.0f) * (color.g - 1.0f) + 7.0f * color.g)) : (color.g + (2.0f * glow.g - 1.0f) * (sqrt(color.g) - color.g)));
color.b = (glow.b <= 0.5f) ? (color.b - (1.0f - 2.0f * glow.b) * color.b * (1.0f - color.b)) : (((glow.b > 0.5f) && (color.b <= 0.25f)) ? (color.b + (2.0f * glow.b - 1.0f) * (4.0f * color.b * (4.0f * color.b + 1.0f) * (color.b - 1.0f) + 7.0f * color.b)) : (color.b + (2.0f * glow.b - 1.0f) * (sqrt(color.b) - color.b)));
return color;
} else { //replace
return glow;
}
}
vec3 apply_bcs(vec3 color, vec3 bcs) {
color = mix(vec3(0.0f), color, bcs.x);
color = mix(vec3(0.5f), color, bcs.y);
color = mix(vec3(dot(vec3(1.0f), color) * 0.33333f), color, bcs.z);
return color;
}
vec3 apply_color_correction(vec3 color, sampler3D correction_tex) {
return texture(correction_tex, color).rgb;
}
void main() {
vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb;
// Exposure
if (params.use_auto_exposure) {
color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.auto_exposure_grey;
}
color *= params.exposure;
// Early Tonemap & SRGB Conversion
if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp);
color.rgb = mix(color.rgb, glow, params.glow_intensity);
}
color = apply_tonemapping(color, params.white);
color = linear_to_srgb(color); // regular linear -> SRGB conversion
// Glow
if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity;
// high dynamic range -> SRGB
glow = apply_tonemapping(glow, params.white);
glow = linear_to_srgb(glow);
color = apply_glow(color, glow);
}
// Additional effects
if (params.use_bcs) {
color = apply_bcs(color, params.bcs);
}
if (params.use_color_correction) {
color = apply_color_correction(color, color_correction);
}
frag_color = vec4(color, 1.0f);
}