godot/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
reduz 746dddc067 Replace most uses of Map by HashMap
* Map is unnecessary and inefficient in almost every case.
* Replaced by the new HashMap.
* Renamed Map to RBMap and Set to RBSet for cases that still make sense
  (order matters) but use is discouraged.

There were very few cases where replacing by HashMap was undesired because
keeping the key order was intended.
I tried to keep those (as RBMap) as much as possible, but might have missed
some. Review appreciated!
2022-05-16 10:37:48 +02:00

1920 lines
77 KiB
C++

/*************************************************************************/
/* renderer_scene_sky_rd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "renderer_scene_sky_rd.h"
#include "core/config/project_settings.h"
#include "core/math/math_defs.h"
#include "renderer_scene_render_rd.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_default.h"
////////////////////////////////////////////////////////////////////////////////
// SKY SHADER
void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
//compile
code = p_code;
valid = false;
ubo_size = 0;
uniforms.clear();
if (code.is_empty()) {
return; //just invalid, but no error
}
ShaderCompiler::GeneratedCode gen_code;
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["sky"] = ShaderCompiler::STAGE_FRAGMENT;
uses_time = false;
uses_half_res = false;
uses_quarter_res = false;
uses_position = false;
uses_light = false;
actions.render_mode_flags["use_half_res_pass"] = &uses_half_res;
actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res;
actions.usage_flag_pointers["TIME"] = &uses_time;
actions.usage_flag_pointers["POSITION"] = &uses_position;
actions.usage_flag_pointers["LIGHT0_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_SIZE"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_SIZE"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_SIZE"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_SIZE"] = &uses_light;
actions.uniforms = &uniforms;
// !BAS! Contemplate making `SkyShader sky` accessible from this struct or even part of this struct.
RendererSceneRenderRD *scene_singleton = static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton);
Error err = scene_singleton->sky.sky_shader.compiler.compile(RS::SHADER_SKY, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = scene_singleton->sky.sky_shader.shader.version_create();
}
#if 0
print_line("**compiling shader:");
print_line("**defines:\n");
for (int i = 0; i < gen_code.defines.size(); i++) {
print_line(gen_code.defines[i]);
}
print_line("\n**uniforms:\n" + gen_code.uniforms);
// print_line("\n**vertex_globals:\n" + gen_code.vertex_global);
// print_line("\n**vertex_code:\n" + gen_code.vertex);
print_line("\n**fragment_globals:\n" + gen_code.fragment_global);
print_line("\n**fragment_code:\n" + gen_code.fragment);
print_line("\n**light_code:\n" + gen_code.light);
#endif
scene_singleton->sky.sky_shader.shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
ERR_FAIL_COND(!scene_singleton->sky.sky_shader.shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
//update pipelines
for (int i = 0; i < SKY_VERSION_MAX; i++) {
RD::PipelineDepthStencilState depth_stencil_state;
depth_stencil_state.enable_depth_test = true;
depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
if (scene_singleton->sky.sky_shader.shader.is_variant_enabled(i)) {
RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i);
pipelines[i].setup(shader_variant, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), depth_stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
} else {
pipelines[i].clear();
}
}
valid = true;
}
void RendererSceneSkyRD::SkyShaderData::set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) {
if (!p_texture.is_valid()) {
if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) {
default_texture_params[p_name].erase(p_index);
if (default_texture_params[p_name].is_empty()) {
default_texture_params.erase(p_name);
}
}
} else {
if (!default_texture_params.has(p_name)) {
default_texture_params[p_name] = HashMap<int, RID>();
}
default_texture_params[p_name][p_index] = p_texture;
}
}
void RendererSceneSkyRD::SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
HashMap<int, StringName> order;
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) {
if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
continue;
}
if (E.value.texture_order >= 0) {
order[E.value.texture_order + 100000] = E.key;
} else {
order[E.value.order] = E.key;
}
}
for (const KeyValue<int, StringName> &E : order) {
PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
pi.name = E.value;
p_param_list->push_back(pi);
}
}
void RendererSceneSkyRD::SkyShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const {
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) {
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
continue;
}
RendererMaterialStorage::InstanceShaderParam p;
p.info = ShaderLanguage::uniform_to_property_info(E.value);
p.info.name = E.key; //supply name
p.index = E.value.instance_index;
p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint);
p_param_list->push_back(p);
}
}
bool RendererSceneSkyRD::SkyShaderData::is_param_texture(const StringName &p_param) const {
if (!uniforms.has(p_param)) {
return false;
}
return uniforms[p_param].texture_order >= 0;
}
bool RendererSceneSkyRD::SkyShaderData::is_animated() const {
return false;
}
bool RendererSceneSkyRD::SkyShaderData::casts_shadows() const {
return false;
}
Variant RendererSceneSkyRD::SkyShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
}
RS::ShaderNativeSourceCode RendererSceneSkyRD::SkyShaderData::get_native_source_code() const {
RendererSceneRenderRD *scene_singleton = static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton);
return scene_singleton->sky.sky_shader.shader.version_get_native_source_code(version);
}
RendererSceneSkyRD::SkyShaderData::~SkyShaderData() {
RendererSceneRenderRD *scene_singleton = static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton);
ERR_FAIL_COND(!scene_singleton);
//pipeline variants will clear themselves if shader is gone
if (version.is_valid()) {
scene_singleton->sky.sky_shader.shader.version_free(version);
}
}
////////////////////////////////////////////////////////////////////////////////
// Sky material
bool RendererSceneSkyRD::SkyMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
RendererSceneRenderRD *scene_singleton = static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton);
uniform_set_updated = true;
return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL);
}
RendererSceneSkyRD::SkyMaterialData::~SkyMaterialData() {
free_parameters_uniform_set(uniform_set);
}
////////////////////////////////////////////////////////////////////////////////
// Render sky
static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) {
p_array[0] = p_basis.rows[0][0];
p_array[1] = p_basis.rows[1][0];
p_array[2] = p_basis.rows[2][0];
p_array[3] = 0;
p_array[4] = p_basis.rows[0][1];
p_array[5] = p_basis.rows[1][1];
p_array[6] = p_basis.rows[2][1];
p_array[7] = 0;
p_array[8] = p_basis.rows[0][2];
p_array[9] = p_basis.rows[1][2];
p_array[10] = p_basis.rows[2][2];
p_array[11] = 0;
}
void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier) {
SkyPushConstant sky_push_constant;
memset(&sky_push_constant, 0, sizeof(SkyPushConstant));
for (uint32_t v = 0; v < p_view_count; v++) {
// We only need key components of our projection matrix
sky_push_constant.projections[v][0] = p_projections[v].matrix[2][0];
sky_push_constant.projections[v][1] = p_projections[v].matrix[0][0];
sky_push_constant.projections[v][2] = p_projections[v].matrix[2][1];
sky_push_constant.projections[v][3] = p_projections[v].matrix[1][1];
}
sky_push_constant.position[0] = p_position.x;
sky_push_constant.position[1] = p_position.y;
sky_push_constant.position[2] = p_position.z;
sky_push_constant.multiplier = p_multiplier;
sky_push_constant.time = p_time;
sky_push_constant.luminance_multiplier = p_luminance_multiplier;
store_transform_3x3(p_orientation, sky_push_constant.orientation);
RenderingDevice::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_fb);
RD::DrawListID draw_list = p_list;
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format, false, RD::get_singleton()->draw_list_get_current_pass()));
// Update uniform sets.
{
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0);
if (p_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set.
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1);
}
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2);
// Fog uniform set can be invalidated before drawing, so validate at draw time
if (sky_scene_state.fog_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_scene_state.fog_uniform_set)) {
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.fog_uniform_set, 3);
} else {
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.default_fog_uniform_set, 3);
}
}
RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &sky_push_constant, sizeof(SkyPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
}
////////////////////////////////////////////////////////////////////////////////
// ReflectionData
void RendererSceneSkyRD::ReflectionData::clear_reflection_data() {
layers.clear();
radiance_base_cubemap = RID();
if (downsampled_radiance_cubemap.is_valid()) {
RD::get_singleton()->free(downsampled_radiance_cubemap);
}
downsampled_radiance_cubemap = RID();
downsampled_layer.mipmaps.clear();
coefficient_buffer = RID();
}
void RendererSceneSkyRD::ReflectionData::update_reflection_data(RendererStorageRD *p_storage, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format) {
//recreate radiance and all data
int mipmaps = p_mipmaps;
uint32_t w = p_size, h = p_size;
EffectsRD *effects = p_storage->get_effects();
ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised");
bool prefer_raster_effects = effects->get_prefer_raster_effects();
if (p_use_array) {
int num_layers = p_low_quality ? 8 : p_roughness_layers;
for (int i = 0; i < num_layers; i++) {
ReflectionData::Layer layer;
uint32_t mmw = w;
uint32_t mmh = h;
layer.mipmaps.resize(mipmaps);
layer.views.resize(mipmaps);
for (int j = 0; j < mipmaps; j++) {
ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
mm.size.width = mmw;
mm.size.height = mmh;
for (int k = 0; k < 6; k++) {
mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6 + k, j);
Vector<RID> fbtex;
fbtex.push_back(mm.views[k]);
mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
}
layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + i * 6, j, 1, RD::TEXTURE_SLICE_CUBEMAP);
mmw = MAX(1u, mmw >> 1);
mmh = MAX(1u, mmh >> 1);
}
layers.push_back(layer);
}
} else {
mipmaps = p_low_quality ? 8 : mipmaps;
//regular cubemap, lower quality (aliasing, less memory)
ReflectionData::Layer layer;
uint32_t mmw = w;
uint32_t mmh = h;
layer.mipmaps.resize(mipmaps);
layer.views.resize(mipmaps);
for (int j = 0; j < mipmaps; j++) {
ReflectionData::Layer::Mipmap &mm = layer.mipmaps.write[j];
mm.size.width = mmw;
mm.size.height = mmh;
for (int k = 0; k < 6; k++) {
mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer + k, j);
Vector<RID> fbtex;
fbtex.push_back(mm.views[k]);
mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
}
layer.views.write[j] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, j, 1, RD::TEXTURE_SLICE_CUBEMAP);
mmw = MAX(1u, mmw >> 1);
mmh = MAX(1u, mmh >> 1);
}
layers.push_back(layer);
}
radiance_base_cubemap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0, 1, RD::TEXTURE_SLICE_CUBEMAP);
RD::get_singleton()->set_resource_name(radiance_base_cubemap, "radiance base cubemap");
RD::TextureFormat tf;
tf.format = p_texture_format;
tf.width = 64; // Always 64x64
tf.height = 64;
tf.texture_type = RD::TEXTURE_TYPE_CUBE;
tf.array_layers = 6;
tf.mipmaps = 7;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
downsampled_radiance_cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView());
RD::get_singleton()->set_resource_name(downsampled_radiance_cubemap, "downsampled radiance cubemap");
{
uint32_t mmw = 64;
uint32_t mmh = 64;
downsampled_layer.mipmaps.resize(7);
for (int j = 0; j < downsampled_layer.mipmaps.size(); j++) {
ReflectionData::DownsampleLayer::Mipmap &mm = downsampled_layer.mipmaps.write[j];
mm.size.width = mmw;
mm.size.height = mmh;
mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, 0, j, 1, RD::TEXTURE_SLICE_CUBEMAP);
RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Cubemap Mip " + itos(j) + " ");
if (prefer_raster_effects) {
// we need a framebuffer for each side of our cubemap
for (int k = 0; k < 6; k++) {
mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, k, j);
RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Cubemap Mip: " + itos(j) + " Face: " + itos(k) + " ");
Vector<RID> fbtex;
fbtex.push_back(mm.views[k]);
mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex);
}
}
mmw = MAX(1u, mmw >> 1);
mmh = MAX(1u, mmh >> 1);
}
}
}
void RendererSceneSkyRD::ReflectionData::create_reflection_fast_filter(RendererStorageRD *p_storage, bool p_use_arrays) {
EffectsRD *effects = p_storage->get_effects();
ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised");
bool prefer_raster_effects = effects->get_prefer_raster_effects();
if (prefer_raster_effects) {
RD::get_singleton()->draw_command_begin_label("Downsample radiance map");
for (int k = 0; k < 6; k++) {
effects->cubemap_downsample_raster(radiance_base_cubemap, downsampled_layer.mipmaps[0].framebuffers[k], k, downsampled_layer.mipmaps[0].size);
}
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
for (int k = 0; k < 6; k++) {
effects->cubemap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffers[k], k, downsampled_layer.mipmaps[i].size);
}
}
RD::get_singleton()->draw_command_end_label(); // Downsample Radiance
if (p_use_arrays) {
RD::get_singleton()->draw_command_begin_label("filter radiance map into array heads");
for (int i = 0; i < layers.size(); i++) {
for (int k = 0; k < 6; k++) {
effects->cubemap_filter_raster(downsampled_radiance_cubemap, layers[i].mipmaps[0].framebuffers[k], k, i);
}
}
} else {
RD::get_singleton()->draw_command_begin_label("filter radiance map into mipmaps directly");
for (int j = 0; j < layers[0].mipmaps.size(); j++) {
for (int k = 0; k < 6; k++) {
effects->cubemap_filter_raster(downsampled_radiance_cubemap, layers[0].mipmaps[j].framebuffers[k], k, j);
}
}
}
RD::get_singleton()->draw_command_end_label(); // Filter radiance
} else {
RD::get_singleton()->draw_command_begin_label("Downsample radiance map");
effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size);
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size);
}
RD::get_singleton()->draw_command_end_label(); // Downsample Radiance
Vector<RID> views;
if (p_use_arrays) {
for (int i = 1; i < layers.size(); i++) {
views.push_back(layers[i].views[0]);
}
} else {
for (int i = 1; i < layers[0].views.size(); i++) {
views.push_back(layers[0].views[i]);
}
}
RD::get_singleton()->draw_command_begin_label("Fast filter radiance");
effects->cubemap_filter(downsampled_radiance_cubemap, views, p_use_arrays);
RD::get_singleton()->draw_command_end_label(); // Filter radiance
}
}
void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(RendererStorageRD *p_storage, bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality) {
EffectsRD *effects = p_storage->get_effects();
ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised");
bool prefer_raster_effects = effects->get_prefer_raster_effects();
if (prefer_raster_effects) {
if (p_base_layer == 1) {
RD::get_singleton()->draw_command_begin_label("Downsample radiance map");
for (int k = 0; k < 6; k++) {
effects->cubemap_downsample_raster(radiance_base_cubemap, downsampled_layer.mipmaps[0].framebuffers[k], k, downsampled_layer.mipmaps[0].size);
}
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
for (int k = 0; k < 6; k++) {
effects->cubemap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffers[k], k, downsampled_layer.mipmaps[i].size);
}
}
RD::get_singleton()->draw_command_end_label(); // Downsample Radiance
}
RD::get_singleton()->draw_command_begin_label("High Quality filter radiance");
if (p_use_arrays) {
for (int k = 0; k < 6; k++) {
effects->cubemap_roughness_raster(
downsampled_radiance_cubemap,
layers[p_base_layer].mipmaps[0].framebuffers[k],
k,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers.size() - 1.0),
layers[p_base_layer].mipmaps[0].size.x);
}
} else {
for (int k = 0; k < 6; k++) {
effects->cubemap_roughness_raster(
downsampled_radiance_cubemap,
layers[0].mipmaps[p_base_layer].framebuffers[k],
k,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers[0].mipmaps.size() - 1.0),
layers[0].mipmaps[p_base_layer].size.x);
}
}
} else {
if (p_base_layer == 1) {
RD::get_singleton()->draw_command_begin_label("Downsample radiance map");
effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size);
for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) {
effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size);
}
RD::get_singleton()->draw_command_end_label(); // Downsample Radiance
}
RD::get_singleton()->draw_command_begin_label("High Quality filter radiance");
if (p_use_arrays) {
effects->cubemap_roughness(downsampled_radiance_cubemap, layers[p_base_layer].views[0], p_cube_side, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), layers[p_base_layer].mipmaps[0].size.x);
} else {
effects->cubemap_roughness(
downsampled_radiance_cubemap,
layers[0].views[p_base_layer],
p_cube_side,
p_sky_ggx_samples_quality,
float(p_base_layer) / (layers[0].mipmaps.size() - 1.0),
layers[0].mipmaps[p_base_layer].size.x);
}
}
RD::get_singleton()->draw_command_end_label(); // Filter radiance
}
void RendererSceneSkyRD::ReflectionData::update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end) {
EffectsRD *effects = p_storage->get_effects();
ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised");
bool prefer_raster_effects = effects->get_prefer_raster_effects();
RD::get_singleton()->draw_command_begin_label("Update Radiance Cubemap Array Mipmaps");
for (int i = p_start; i < p_end; i++) {
for (int j = 0; j < layers[i].views.size() - 1; j++) {
RID view = layers[i].views[j];
Size2i size = layers[i].mipmaps[j + 1].size;
if (prefer_raster_effects) {
for (int k = 0; k < 6; k++) {
RID framebuffer = layers[i].mipmaps[j + 1].framebuffers[k];
effects->cubemap_downsample_raster(view, framebuffer, k, size);
}
} else {
RID texture = layers[i].views[j + 1];
effects->cubemap_downsample(view, texture, size);
}
}
}
RD::get_singleton()->draw_command_end_label();
}
////////////////////////////////////////////////////////////////////////////////
// RendererSceneSkyRD::Sky
void RendererSceneSkyRD::Sky::free(RendererStorageRD *p_storage) {
if (radiance.is_valid()) {
RD::get_singleton()->free(radiance);
radiance = RID();
}
reflection.clear_reflection_data();
if (uniform_buffer.is_valid()) {
RD::get_singleton()->free(uniform_buffer);
uniform_buffer = RID();
}
if (half_res_pass.is_valid()) {
RD::get_singleton()->free(half_res_pass);
half_res_pass = RID();
}
if (quarter_res_pass.is_valid()) {
RD::get_singleton()->free(quarter_res_pass);
quarter_res_pass = RID();
}
if (material.is_valid()) {
p_storage->free(material);
material = RID();
}
}
RID RendererSceneSkyRD::Sky::get_textures(RendererStorageRD *p_storage, SkyTextureSetVersion p_version, RID p_default_shader_rd) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
if (texture_uniform_sets[p_version].is_valid() && RD::get_singleton()->uniform_set_is_valid(texture_uniform_sets[p_version])) {
return texture_uniform_sets[p_version];
}
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 0;
if (radiance.is_valid() && p_version <= SKY_TEXTURE_SET_QUARTER_RES) {
u.append_id(radiance);
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
}
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1; // half res
if (half_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_HALF_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) {
if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
u.append_id(reflection.layers[0].views[1]);
} else {
u.append_id(half_res_pass);
}
} else {
if (p_version < SKY_TEXTURE_SET_CUBEMAP) {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_WHITE));
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
}
}
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2; // quarter res
if (quarter_res_pass.is_valid() && p_version != SKY_TEXTURE_SET_QUARTER_RES && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) {
if (p_version >= SKY_TEXTURE_SET_CUBEMAP) {
u.append_id(reflection.layers[0].views[2]);
} else {
u.append_id(quarter_res_pass);
}
} else {
if (p_version < SKY_TEXTURE_SET_CUBEMAP) {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_WHITE));
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
}
}
uniforms.push_back(u);
}
texture_uniform_sets[p_version] = RD::get_singleton()->uniform_set_create(uniforms, p_default_shader_rd, SKY_SET_TEXTURES);
return texture_uniform_sets[p_version];
}
bool RendererSceneSkyRD::Sky::set_radiance_size(int p_radiance_size) {
ERR_FAIL_COND_V(p_radiance_size < 32 || p_radiance_size > 2048, false);
if (radiance_size == p_radiance_size) {
return false;
}
radiance_size = p_radiance_size;
if (mode == RS::SKY_MODE_REALTIME && radiance_size != 256) {
WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
radiance_size = 256;
}
if (radiance.is_valid()) {
RD::get_singleton()->free(radiance);
radiance = RID();
}
reflection.clear_reflection_data();
return true;
}
bool RendererSceneSkyRD::Sky::set_mode(RS::SkyMode p_mode) {
if (mode == p_mode) {
return false;
}
mode = p_mode;
if (mode == RS::SKY_MODE_REALTIME && radiance_size != 256) {
WARN_PRINT("Realtime Skies can only use a radiance size of 256. Radiance size will be set to 256 internally.");
set_radiance_size(256);
}
if (radiance.is_valid()) {
RD::get_singleton()->free(radiance);
radiance = RID();
}
reflection.clear_reflection_data();
return true;
}
bool RendererSceneSkyRD::Sky::set_material(RID p_material) {
if (material == p_material) {
return false;
}
material = p_material;
return true;
}
Ref<Image> RendererSceneSkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, const Size2i &p_size) {
if (radiance.is_valid()) {
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
tf.width = p_size.width;
tf.height = p_size.height;
tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
RID rad_tex = RD::get_singleton()->texture_create(tf, RD::TextureView());
copy_effects->copy_cubemap_to_panorama(radiance, rad_tex, p_size, p_roughness_layers, reflection.layers.size() > 1);
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0);
RD::get_singleton()->free(rad_tex);
Ref<Image> img;
img.instantiate();
img->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data);
for (int i = 0; i < p_size.width; i++) {
for (int j = 0; j < p_size.height; j++) {
Color c = img->get_pixel(i, j);
c.r *= p_energy;
c.g *= p_energy;
c.b *= p_energy;
img->set_pixel(i, j, c);
}
}
return img;
}
return Ref<Image>();
}
////////////////////////////////////////////////////////////////////////////////
// RendererSceneSkyRD
RendererRD::ShaderData *RendererSceneSkyRD::_create_sky_shader_func() {
SkyShaderData *shader_data = memnew(SkyShaderData);
return shader_data;
}
RendererRD::ShaderData *RendererSceneSkyRD::_create_sky_shader_funcs() {
// !BAS! Why isn't _create_sky_shader_func not just static too?
return static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton)->sky._create_sky_shader_func();
};
RendererRD::MaterialData *RendererSceneSkyRD::_create_sky_material_func(SkyShaderData *p_shader) {
SkyMaterialData *material_data = memnew(SkyMaterialData);
material_data->shader_data = p_shader;
//update will happen later anyway so do nothing.
return material_data;
}
RendererRD::MaterialData *RendererSceneSkyRD::_create_sky_material_funcs(RendererRD::ShaderData *p_shader) {
// !BAS! same here, we could just make _create_sky_material_func static?
return static_cast<RendererSceneRenderRD *>(RendererSceneRenderRD::singleton)->sky._create_sky_material_func(static_cast<SkyShaderData *>(p_shader));
};
RendererSceneSkyRD::RendererSceneSkyRD() {
roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
sky_ggx_samples_quality = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
sky_use_cubemap_array = GLOBAL_GET("rendering/reflections/sky_reflections/texture_array_reflections");
}
void RendererSceneSkyRD::init(RendererStorageRD *p_storage) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
storage = p_storage;
{
// Start with the directional lights for the sky
sky_scene_state.max_directional_lights = 4;
uint32_t directional_light_buffer_size = sky_scene_state.max_directional_lights * sizeof(SkyDirectionalLightData);
sky_scene_state.directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights);
sky_scene_state.last_frame_directional_lights = memnew_arr(SkyDirectionalLightData, sky_scene_state.max_directional_lights);
sky_scene_state.last_frame_directional_light_count = sky_scene_state.max_directional_lights + 1;
sky_scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size);
String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_scene_state.max_directional_lights) + "\n";
// Initialize sky
Vector<String> sky_modes;
sky_modes.push_back(""); // Full size
sky_modes.push_back("\n#define USE_HALF_RES_PASS\n"); // Half Res
sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n"); // Quarter res
sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap
sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap
sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap
sky_modes.push_back("\n#define USE_MULTIVIEW\n"); // Full size multiview
sky_modes.push_back("\n#define USE_HALF_RES_PASS\n#define USE_MULTIVIEW\n"); // Half Res multiview
sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n#define USE_MULTIVIEW\n"); // Quarter res multiview
sky_shader.shader.initialize(sky_modes, defines);
if (!RendererCompositorRD::singleton->is_xr_enabled()) {
sky_shader.shader.set_variant_enabled(SKY_VERSION_BACKGROUND_MULTIVIEW, false);
sky_shader.shader.set_variant_enabled(SKY_VERSION_HALF_RES_MULTIVIEW, false);
sky_shader.shader.set_variant_enabled(SKY_VERSION_QUARTER_RES_MULTIVIEW, false);
}
}
// register our shader funds
material_storage->shader_set_data_request_function(RendererRD::SHADER_TYPE_SKY, _create_sky_shader_funcs);
material_storage->material_set_data_request_function(RendererRD::SHADER_TYPE_SKY, _create_sky_material_funcs);
{
ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["COLOR"] = "color";
actions.renames["ALPHA"] = "alpha";
actions.renames["EYEDIR"] = "cube_normal";
actions.renames["POSITION"] = "params.position_multiplier.xyz";
actions.renames["SKY_COORDS"] = "panorama_coords";
actions.renames["SCREEN_UV"] = "uv";
actions.renames["TIME"] = "params.time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
actions.renames["HALF_RES_COLOR"] = "half_res_color";
actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color";
actions.renames["RADIANCE"] = "radiance";
actions.renames["FOG"] = "custom_fog";
actions.renames["LIGHT0_ENABLED"] = "directional_lights.data[0].enabled";
actions.renames["LIGHT0_DIRECTION"] = "directional_lights.data[0].direction_energy.xyz";
actions.renames["LIGHT0_ENERGY"] = "directional_lights.data[0].direction_energy.w";
actions.renames["LIGHT0_COLOR"] = "directional_lights.data[0].color_size.xyz";
actions.renames["LIGHT0_SIZE"] = "directional_lights.data[0].color_size.w";
actions.renames["LIGHT1_ENABLED"] = "directional_lights.data[1].enabled";
actions.renames["LIGHT1_DIRECTION"] = "directional_lights.data[1].direction_energy.xyz";
actions.renames["LIGHT1_ENERGY"] = "directional_lights.data[1].direction_energy.w";
actions.renames["LIGHT1_COLOR"] = "directional_lights.data[1].color_size.xyz";
actions.renames["LIGHT1_SIZE"] = "directional_lights.data[1].color_size.w";
actions.renames["LIGHT2_ENABLED"] = "directional_lights.data[2].enabled";
actions.renames["LIGHT2_DIRECTION"] = "directional_lights.data[2].direction_energy.xyz";
actions.renames["LIGHT2_ENERGY"] = "directional_lights.data[2].direction_energy.w";
actions.renames["LIGHT2_COLOR"] = "directional_lights.data[2].color_size.xyz";
actions.renames["LIGHT2_SIZE"] = "directional_lights.data[2].color_size.w";
actions.renames["LIGHT3_ENABLED"] = "directional_lights.data[3].enabled";
actions.renames["LIGHT3_DIRECTION"] = "directional_lights.data[3].direction_energy.xyz";
actions.renames["LIGHT3_ENERGY"] = "directional_lights.data[3].direction_energy.w";
actions.renames["LIGHT3_COLOR"] = "directional_lights.data[3].color_size.xyz";
actions.renames["LIGHT3_SIZE"] = "directional_lights.data[3].color_size.w";
actions.renames["AT_CUBEMAP_PASS"] = "AT_CUBEMAP_PASS";
actions.renames["AT_HALF_RES_PASS"] = "AT_HALF_RES_PASS";
actions.renames["AT_QUARTER_RES_PASS"] = "AT_QUARTER_RES_PASS";
actions.custom_samplers["RADIANCE"] = "material_samplers[3]";
actions.usage_defines["HALF_RES_COLOR"] = "\n#define USES_HALF_RES_COLOR\n";
actions.usage_defines["QUARTER_RES_COLOR"] = "\n#define USES_QUARTER_RES_COLOR\n";
actions.render_mode_defines["disable_fog"] = "#define DISABLE_FOG\n";
actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = 1;
actions.base_uniform_string = "material.";
actions.base_varying_index = 10;
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
actions.global_buffer_array_variable = "global_variables.data";
sky_shader.compiler.initialize(actions);
}
{
// default material and shader for sky shader
sky_shader.default_shader = material_storage->shader_allocate();
material_storage->shader_initialize(sky_shader.default_shader);
material_storage->shader_set_code(sky_shader.default_shader, R"(
// Default sky shader.
shader_type sky;
void sky() {
COLOR = vec3(0.0);
}
)");
sky_shader.default_material = material_storage->material_allocate();
material_storage->material_initialize(sky_shader.default_material);
material_storage->material_set_shader(sky_shader.default_material, sky_shader.default_shader);
SkyMaterialData *md = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_shader.default_material, RendererRD::SHADER_TYPE_SKY));
sky_shader.default_shader_rd = sky_shader.shader.version_get_shader(md->shader_data->version, SKY_VERSION_BACKGROUND);
sky_scene_state.uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SkySceneState::UBO));
Vector<RD::Uniform> uniforms;
{
Vector<RID> ids;
ids.resize(12);
RID *ids_ptr = ids.ptrw();
ids_ptr[0] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
ids_ptr[1] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
ids_ptr[2] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
ids_ptr[3] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
ids_ptr[4] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
ids_ptr[5] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
ids_ptr[6] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
ids_ptr[7] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
ids_ptr[8] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
ids_ptr[9] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
ids_ptr[10] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
ids_ptr[11] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 0, ids);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.append_id(RendererRD::MaterialStorage::get_singleton()->global_variables_get_storage_buffer());
uniforms.push_back(u);
}
{
RD::Uniform u;
u.binding = 2;
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.append_id(sky_scene_state.uniform_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.binding = 3;
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.append_id(sky_scene_state.directional_light_buffer);
uniforms.push_back(u);
}
sky_scene_state.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_UNIFORMS);
}
{
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.binding = 0;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
RID vfog = texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE);
u.append_id(vfog);
uniforms.push_back(u);
}
sky_scene_state.default_fog_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_FOG);
}
{
// Need defaults for using fog with clear color
sky_scene_state.fog_shader = material_storage->shader_allocate();
material_storage->shader_initialize(sky_scene_state.fog_shader);
material_storage->shader_set_code(sky_scene_state.fog_shader, R"(
// Default clear color sky shader.
shader_type sky;
uniform vec4 clear_color;
void sky() {
COLOR = clear_color.rgb;
}
)");
sky_scene_state.fog_material = material_storage->material_allocate();
material_storage->material_initialize(sky_scene_state.fog_material);
material_storage->material_set_shader(sky_scene_state.fog_material, sky_scene_state.fog_shader);
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 0;
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_WHITE));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_WHITE));
uniforms.push_back(u);
}
sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES);
}
{ //create index array for copy shaders
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;
}
index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv);
index_array = RD::get_singleton()->index_array_create(index_buffer, 0, 6);
}
}
void RendererSceneSkyRD::set_texture_format(RD::DataFormat p_texture_format) {
texture_format = p_texture_format;
}
RendererSceneSkyRD::~RendererSceneSkyRD() {
// cleanup anything created in init...
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
SkyMaterialData *md = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_shader.default_material, RendererRD::SHADER_TYPE_SKY));
sky_shader.shader.version_free(md->shader_data->version);
RD::get_singleton()->free(sky_scene_state.directional_light_buffer);
RD::get_singleton()->free(sky_scene_state.uniform_buffer);
memdelete_arr(sky_scene_state.directional_lights);
memdelete_arr(sky_scene_state.last_frame_directional_lights);
material_storage->shader_free(sky_shader.default_shader);
material_storage->material_free(sky_shader.default_material);
material_storage->shader_free(sky_scene_state.fog_shader);
material_storage->material_free(sky_scene_state.fog_material);
if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.uniform_set)) {
RD::get_singleton()->free(sky_scene_state.uniform_set);
}
if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.default_fog_uniform_set)) {
RD::get_singleton()->free(sky_scene_state.default_fog_uniform_set);
}
if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.fog_only_texture_uniform_set)) {
RD::get_singleton()->free(sky_scene_state.fog_only_texture_uniform_set);
}
RD::get_singleton()->free(index_buffer); //array gets freed as dependency
}
void RendererSceneSkyRD::setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) {
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
ERR_FAIL_COND(!p_env);
SkyMaterialData *material = nullptr;
Sky *sky = get_sky(p_env->sky);
RID sky_material;
SkyShaderData *shader_data = nullptr;
if (sky) {
sky_material = sky_get_material(p_env->sky);
if (sky_material.is_valid()) {
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
if (!material || !material->shader_data->valid) {
material = nullptr;
}
}
if (!material) {
sky_material = sky_shader.default_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
ERR_FAIL_COND(!material);
shader_data = material->shader_data;
ERR_FAIL_COND(!shader_data);
// Invalidate supbass buffers if screen size changes
if (sky->screen_size != p_screen_size) {
sky->screen_size = p_screen_size;
sky->screen_size.x = sky->screen_size.x < 4 ? 4 : sky->screen_size.x;
sky->screen_size.y = sky->screen_size.y < 4 ? 4 : sky->screen_size.y;
if (shader_data->uses_half_res) {
if (sky->half_res_pass.is_valid()) {
RD::get_singleton()->free(sky->half_res_pass);
sky->half_res_pass = RID();
}
invalidate_sky(sky);
}
if (shader_data->uses_quarter_res) {
if (sky->quarter_res_pass.is_valid()) {
RD::get_singleton()->free(sky->quarter_res_pass);
sky->quarter_res_pass = RID();
}
invalidate_sky(sky);
}
}
// Create new subpass buffers if necessary
if ((shader_data->uses_half_res && sky->half_res_pass.is_null()) ||
(shader_data->uses_quarter_res && sky->quarter_res_pass.is_null()) ||
sky->radiance.is_null()) {
invalidate_sky(sky);
update_dirty_skys();
}
if (shader_data->uses_time && p_scene_render->time - sky->prev_time > 0.00001) {
sky->prev_time = p_scene_render->time;
sky->reflection.dirty = true;
RenderingServerDefault::redraw_request();
}
if (material != sky->prev_material) {
sky->prev_material = material;
sky->reflection.dirty = true;
}
if (material->uniform_set_updated) {
material->uniform_set_updated = false;
sky->reflection.dirty = true;
}
if (!p_transform.origin.is_equal_approx(sky->prev_position) && shader_data->uses_position) {
sky->prev_position = p_transform.origin;
sky->reflection.dirty = true;
}
if (shader_data->uses_light) {
sky_scene_state.ubo.directional_light_count = 0;
// Run through the list of lights in the scene and pick out the Directional Lights.
// This can't be done in RenderSceneRenderRD::_setup lights because that needs to be called
// after the depth prepass, but this runs before the depth prepass
for (int i = 0; i < (int)p_lights.size(); i++) {
RendererSceneRenderRD::LightInstance *li = p_scene_render->light_instance_owner.get_or_null(p_lights[i]);
if (!li) {
continue;
}
RID base = li->light;
ERR_CONTINUE(base.is_null());
RS::LightType type = light_storage->light_get_type(base);
if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) {
SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[sky_scene_state.ubo.directional_light_count];
Transform3D light_transform = li->transform;
Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized();
sky_light_data.direction[0] = world_direction.x;
sky_light_data.direction[1] = world_direction.y;
sky_light_data.direction[2] = world_direction.z;
float sign = light_storage->light_is_negative(base) ? -1 : 1;
sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY);
Color linear_col = light_storage->light_get_color(base).srgb_to_linear();
sky_light_data.color[0] = linear_col.r;
sky_light_data.color[1] = linear_col.g;
sky_light_data.color[2] = linear_col.b;
sky_light_data.enabled = true;
float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
if (angular_diameter > 0.0) {
// I know tan(0) is 0, but let's not risk it with numerical precision.
// technically this will keep expanding until reaching the sun, but all we care
// is expand until we reach the radius of the near plane (there can't be more occluders than that)
angular_diameter = Math::tan(Math::deg2rad(angular_diameter));
} else {
angular_diameter = 0.0;
}
sky_light_data.size = angular_diameter;
sky_scene_state.ubo.directional_light_count++;
if (sky_scene_state.ubo.directional_light_count >= sky_scene_state.max_directional_lights) {
break;
}
}
}
// Check whether the directional_light_buffer changes
bool light_data_dirty = false;
// Light buffer is dirty if we have fewer or more lights
// If we have fewer lights, make sure that old lights are disabled
if (sky_scene_state.ubo.directional_light_count != sky_scene_state.last_frame_directional_light_count) {
light_data_dirty = true;
for (uint32_t i = sky_scene_state.ubo.directional_light_count; i < sky_scene_state.max_directional_lights; i++) {
sky_scene_state.directional_lights[i].enabled = false;
}
}
if (!light_data_dirty) {
for (uint32_t i = 0; i < sky_scene_state.ubo.directional_light_count; i++) {
if (sky_scene_state.directional_lights[i].direction[0] != sky_scene_state.last_frame_directional_lights[i].direction[0] ||
sky_scene_state.directional_lights[i].direction[1] != sky_scene_state.last_frame_directional_lights[i].direction[1] ||
sky_scene_state.directional_lights[i].direction[2] != sky_scene_state.last_frame_directional_lights[i].direction[2] ||
sky_scene_state.directional_lights[i].energy != sky_scene_state.last_frame_directional_lights[i].energy ||
sky_scene_state.directional_lights[i].color[0] != sky_scene_state.last_frame_directional_lights[i].color[0] ||
sky_scene_state.directional_lights[i].color[1] != sky_scene_state.last_frame_directional_lights[i].color[1] ||
sky_scene_state.directional_lights[i].color[2] != sky_scene_state.last_frame_directional_lights[i].color[2] ||
sky_scene_state.directional_lights[i].enabled != sky_scene_state.last_frame_directional_lights[i].enabled ||
sky_scene_state.directional_lights[i].size != sky_scene_state.last_frame_directional_lights[i].size) {
light_data_dirty = true;
break;
}
}
}
if (light_data_dirty) {
RD::get_singleton()->buffer_update(sky_scene_state.directional_light_buffer, 0, sizeof(SkyDirectionalLightData) * sky_scene_state.max_directional_lights, sky_scene_state.directional_lights);
SkyDirectionalLightData *temp = sky_scene_state.last_frame_directional_lights;
sky_scene_state.last_frame_directional_lights = sky_scene_state.directional_lights;
sky_scene_state.directional_lights = temp;
sky_scene_state.last_frame_directional_light_count = sky_scene_state.ubo.directional_light_count;
sky->reflection.dirty = true;
}
}
}
//setup fog variables
sky_scene_state.ubo.volumetric_fog_enabled = false;
if (p_render_buffers.is_valid()) {
if (p_scene_render->render_buffers_has_volumetric_fog(p_render_buffers)) {
sky_scene_state.ubo.volumetric_fog_enabled = true;
float fog_end = p_scene_render->render_buffers_get_volumetric_fog_end(p_render_buffers);
if (fog_end > 0.0) {
sky_scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end;
} else {
sky_scene_state.ubo.volumetric_fog_inv_length = 1.0;
}
float fog_detail_spread = p_scene_render->render_buffers_get_volumetric_fog_detail_spread(p_render_buffers); //reverse lookup
if (fog_detail_spread > 0.0) {
sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread;
} else {
sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0;
}
sky_scene_state.fog_uniform_set = p_scene_render->render_buffers_get_volumetric_fog_sky_uniform_set(p_render_buffers);
}
}
sky_scene_state.ubo.z_far = p_projection.get_z_far();
sky_scene_state.ubo.fog_enabled = p_env->fog_enabled;
sky_scene_state.ubo.fog_density = p_env->fog_density;
sky_scene_state.ubo.fog_aerial_perspective = p_env->fog_aerial_perspective;
Color fog_color = p_env->fog_light_color.srgb_to_linear();
float fog_energy = p_env->fog_light_energy;
sky_scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy;
sky_scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy;
sky_scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy;
sky_scene_state.ubo.fog_sun_scatter = p_env->fog_sun_scatter;
RD::get_singleton()->buffer_update(sky_scene_state.uniform_buffer, 0, sizeof(SkySceneState::UBO), &sky_scene_state.ubo);
}
void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
ERR_FAIL_COND(!p_env);
Sky *sky = get_sky(p_env->sky);
ERR_FAIL_COND(!sky);
RID sky_material = sky_get_material(p_env->sky);
SkyMaterialData *material = nullptr;
if (sky_material.is_valid()) {
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
if (!material || !material->shader_data->valid) {
material = nullptr;
}
}
if (!material) {
sky_material = sky_shader.default_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
ERR_FAIL_COND(!material);
SkyShaderData *shader_data = material->shader_data;
ERR_FAIL_COND(!shader_data);
float multiplier = p_env->bg_energy;
bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY;
RS::SkyMode sky_mode = sky->mode;
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
if (shader_data->uses_time || shader_data->uses_position) {
update_single_frame = true;
sky_mode = RS::SKY_MODE_REALTIME;
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
update_single_frame = false;
sky_mode = RS::SKY_MODE_INCREMENTAL;
} else {
update_single_frame = true;
sky_mode = RS::SKY_MODE_QUALITY;
}
}
if (sky->processing_layer == 0 && sky_mode == RS::SKY_MODE_INCREMENTAL) {
// On the first frame after creating sky, rebuild in single frame
update_single_frame = true;
sky_mode = RS::SKY_MODE_QUALITY;
}
int max_processing_layer = sky_use_cubemap_array ? sky->reflection.layers.size() : sky->reflection.layers[0].mipmaps.size();
// Update radiance cubemap
if (sky->reflection.dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) {
static const Vector3 view_normals[6] = {
Vector3(+1, 0, 0),
Vector3(-1, 0, 0),
Vector3(0, +1, 0),
Vector3(0, -1, 0),
Vector3(0, 0, +1),
Vector3(0, 0, -1)
};
static const Vector3 view_up[6] = {
Vector3(0, -1, 0),
Vector3(0, -1, 0),
Vector3(0, 0, +1),
Vector3(0, 0, -1),
Vector3(0, -1, 0),
Vector3(0, -1, 0)
};
CameraMatrix cm;
cm.set_perspective(90, 1, 0.01, 10.0);
CameraMatrix correction;
correction.set_depth_correction(true);
cm = correction * cm;
if (shader_data->uses_quarter_res) {
RD::get_singleton()->draw_command_begin_label("Render Sky to Quarter Res Cubemap");
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES];
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID cubemap_draw_list;
for (int i = 0; i < 6; i++) {
Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd);
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier);
RD::get_singleton()->draw_list_end();
}
RD::get_singleton()->draw_command_end_label();
}
if (shader_data->uses_half_res) {
RD::get_singleton()->draw_command_begin_label("Render Sky to Half Res Cubemap");
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES];
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID cubemap_draw_list;
for (int i = 0; i < 6; i++) {
Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd);
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier);
RD::get_singleton()->draw_list_end();
}
RD::get_singleton()->draw_command_end_label();
}
RD::DrawListID cubemap_draw_list;
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP];
RD::get_singleton()->draw_command_begin_label("Render Sky Cubemap");
for (int i = 0; i < 6; i++) {
Basis local_view = Basis::looking_at(view_normals[i], view_up[i]);
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd);
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
_render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier);
RD::get_singleton()->draw_list_end();
}
RD::get_singleton()->draw_command_end_label();
if (sky_mode == RS::SKY_MODE_REALTIME) {
sky->reflection.create_reflection_fast_filter(storage, sky_use_cubemap_array);
if (sky_use_cubemap_array) {
sky->reflection.update_reflection_mipmaps(storage, 0, sky->reflection.layers.size());
}
} else {
if (update_single_frame) {
for (int i = 1; i < max_processing_layer; i++) {
sky->reflection.create_reflection_importance_sample(storage, sky_use_cubemap_array, 10, i, sky_ggx_samples_quality);
}
if (sky_use_cubemap_array) {
sky->reflection.update_reflection_mipmaps(storage, 0, sky->reflection.layers.size());
}
} else {
if (sky_use_cubemap_array) {
// Multi-Frame so just update the first array level
sky->reflection.update_reflection_mipmaps(storage, 0, 1);
}
}
sky->processing_layer = 1;
}
sky->reflection.dirty = false;
} else {
if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) {
sky->reflection.create_reflection_importance_sample(storage, sky_use_cubemap_array, 10, sky->processing_layer, sky_ggx_samples_quality);
if (sky_use_cubemap_array) {
sky->reflection.update_reflection_mipmaps(storage, sky->processing_layer, sky->processing_layer + 1);
}
sky->processing_layer++;
}
}
}
void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
ERR_FAIL_COND(!p_env);
ERR_FAIL_COND(p_view_count == 0);
ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
Sky *sky = get_sky(p_env->sky);
SkyMaterialData *material = nullptr;
RID sky_material;
RS::EnvironmentBG background = p_env->background;
if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
ERR_FAIL_COND(!sky);
sky_material = sky_get_material(p_env->sky);
if (sky_material.is_valid()) {
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
if (!material || !material->shader_data->valid) {
material = nullptr;
}
}
if (!material) {
sky_material = sky_shader.default_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
}
if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
sky_material = sky_scene_state.fog_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
ERR_FAIL_COND(!material);
SkyShaderData *shader_data = material->shader_data;
ERR_FAIL_COND(!shader_data);
Basis sky_transform = p_env->sky_orientation;
sky_transform.invert();
float multiplier = p_env->bg_energy;
float custom_fov = p_env->sky_custom_fov;
// Camera
CameraMatrix camera;
uint32_t view_count = p_view_count;
const CameraMatrix *projections = p_projections;
if (custom_fov) {
// With custom fov we don't support stereo...
float near_plane = p_projections[0].get_z_near();
float far_plane = p_projections[0].get_z_far();
float aspect = p_projections[0].get_aspect();
camera.set_perspective(custom_fov, aspect, near_plane, far_plane);
view_count = 1;
projections = &camera;
}
sky_transform = p_transform.basis * sky_transform;
if (shader_data->uses_quarter_res) {
PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_QUARTER_RES_MULTIVIEW : SKY_VERSION_QUARTER_RES];
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd);
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
_render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0);
RD::get_singleton()->draw_list_end();
}
if (shader_data->uses_half_res) {
PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_HALF_RES_MULTIVIEW : SKY_VERSION_HALF_RES];
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd);
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
_render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0);
RD::get_singleton()->draw_list_end();
}
PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_BACKGROUND_MULTIVIEW : SKY_VERSION_BACKGROUND];
RID texture_uniform_set;
if (sky) {
texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_BACKGROUND, sky_shader.default_shader_rd);
} else {
texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set;
}
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
_render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0);
RD::get_singleton()->draw_list_end();
}
void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
ERR_FAIL_COND(!p_env);
ERR_FAIL_COND(p_view_count == 0);
ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
Sky *sky = get_sky(p_env->sky);
ERR_FAIL_COND(!sky);
SkyMaterialData *material = nullptr;
RID sky_material;
sky_material = sky_get_material(p_env->sky);
if (sky_material.is_valid()) {
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
if (!material || !material->shader_data->valid) {
material = nullptr;
}
}
if (!material) {
sky_material = sky_shader.default_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
ERR_FAIL_COND(!material);
SkyShaderData *shader_data = material->shader_data;
ERR_FAIL_COND(!shader_data);
Basis sky_transform = p_env->sky_orientation;
sky_transform.invert();
float multiplier = p_env->bg_energy;
float custom_fov = p_env->sky_custom_fov;
// Camera
CameraMatrix camera;
uint32_t view_count = p_view_count;
const CameraMatrix *projections = p_projections;
if (custom_fov) {
// With custom fov we don't support stereo...
float near_plane = p_projections[0].get_z_near();
float far_plane = p_projections[0].get_z_far();
float aspect = p_projections[0].get_aspect();
camera.set_perspective(custom_fov, aspect, near_plane, far_plane);
view_count = 1;
projections = &camera;
}
sky_transform = p_transform.basis * sky_transform;
if (shader_data->uses_quarter_res) {
PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_QUARTER_RES_MULTIVIEW : SKY_VERSION_QUARTER_RES];
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd);
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
_render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier);
RD::get_singleton()->draw_list_end();
}
if (shader_data->uses_half_res) {
PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_HALF_RES_MULTIVIEW : SKY_VERSION_HALF_RES];
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd);
Vector<Color> clear_colors;
clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
_render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier);
RD::get_singleton()->draw_list_end();
}
}
void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
ERR_FAIL_COND(!p_env);
ERR_FAIL_COND(p_view_count == 0);
ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
Sky *sky = get_sky(p_env->sky);
SkyMaterialData *material = nullptr;
RID sky_material;
RS::EnvironmentBG background = p_env->background;
if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
ERR_FAIL_COND(!sky);
sky_material = sky_get_material(p_env->sky);
if (sky_material.is_valid()) {
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
if (!material || !material->shader_data->valid) {
material = nullptr;
}
}
if (!material) {
sky_material = sky_shader.default_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
}
if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
sky_material = sky_scene_state.fog_material;
material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::SHADER_TYPE_SKY));
}
ERR_FAIL_COND(!material);
SkyShaderData *shader_data = material->shader_data;
ERR_FAIL_COND(!shader_data);
Basis sky_transform = p_env->sky_orientation;
sky_transform.invert();
float multiplier = p_env->bg_energy;
float custom_fov = p_env->sky_custom_fov;
// Camera
CameraMatrix camera;
uint32_t view_count = p_view_count;
const CameraMatrix *projections = p_projections;
if (custom_fov) {
// With custom fov we don't support stereo...
float near_plane = p_projections[0].get_z_near();
float far_plane = p_projections[0].get_z_far();
float aspect = p_projections[0].get_aspect();
camera.set_perspective(custom_fov, aspect, near_plane, far_plane);
view_count = 1;
projections = &camera;
}
sky_transform = p_transform.basis * sky_transform;
PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_BACKGROUND_MULTIVIEW : SKY_VERSION_BACKGROUND];
RID texture_uniform_set;
if (sky) {
texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_BACKGROUND, sky_shader.default_shader_rd);
} else {
texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set;
}
_render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier);
}
void RendererSceneSkyRD::invalidate_sky(Sky *p_sky) {
if (!p_sky->dirty) {
p_sky->dirty = true;
p_sky->dirty_list = dirty_sky_list;
dirty_sky_list = p_sky;
}
}
void RendererSceneSkyRD::update_dirty_skys() {
Sky *sky = dirty_sky_list;
while (sky) {
bool texture_set_dirty = false;
//update sky configuration if texture is missing
if (sky->radiance.is_null()) {
int mipmaps = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBAH) + 1;
uint32_t w = sky->radiance_size, h = sky->radiance_size;
int layers = roughness_layers;
if (sky->mode == RS::SKY_MODE_REALTIME) {
layers = 8;
if (roughness_layers != 8) {
WARN_PRINT("When using REALTIME skies, roughness_layers should be set to 8 in the project settings for best quality reflections");
}
}
if (sky_use_cubemap_array) {
//array (higher quality, 6 times more memory)
RD::TextureFormat tf;
tf.array_layers = layers * 6;
tf.format = texture_format;
tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
tf.mipmaps = mipmaps;
tf.width = w;
tf.height = h;
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
sky->reflection.update_reflection_data(storage, sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format);
} else {
//regular cubemap, lower quality (aliasing, less memory)
RD::TextureFormat tf;
tf.array_layers = 6;
tf.format = texture_format;
tf.texture_type = RD::TEXTURE_TYPE_CUBE;
tf.mipmaps = MIN(mipmaps, layers);
tf.width = w;
tf.height = h;
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView());
sky->reflection.update_reflection_data(storage, sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format);
}
texture_set_dirty = true;
}
// Create subpass buffers if they haven't been created already
if (sky->half_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->half_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) {
RD::TextureFormat tformat;
tformat.format = texture_format;
tformat.width = sky->screen_size.x / 2;
tformat.height = sky->screen_size.y / 2;
tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
tformat.texture_type = RD::TEXTURE_TYPE_2D;
sky->half_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView());
Vector<RID> texs;
texs.push_back(sky->half_res_pass);
sky->half_res_framebuffer = RD::get_singleton()->framebuffer_create(texs);
texture_set_dirty = true;
}
if (sky->quarter_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->quarter_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) {
RD::TextureFormat tformat;
tformat.format = texture_format;
tformat.width = sky->screen_size.x / 4;
tformat.height = sky->screen_size.y / 4;
tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
tformat.texture_type = RD::TEXTURE_TYPE_2D;
sky->quarter_res_pass = RD::get_singleton()->texture_create(tformat, RD::TextureView());
Vector<RID> texs;
texs.push_back(sky->quarter_res_pass);
sky->quarter_res_framebuffer = RD::get_singleton()->framebuffer_create(texs);
texture_set_dirty = true;
}
if (texture_set_dirty) {
for (int i = 0; i < SKY_TEXTURE_SET_MAX; i++) {
if (sky->texture_uniform_sets[i].is_valid() && RD::get_singleton()->uniform_set_is_valid(sky->texture_uniform_sets[i])) {
RD::get_singleton()->free(sky->texture_uniform_sets[i]);
sky->texture_uniform_sets[i] = RID();
}
}
}
sky->reflection.dirty = true;
sky->processing_layer = 0;
Sky *next = sky->dirty_list;
sky->dirty_list = nullptr;
sky->dirty = false;
sky = next;
}
dirty_sky_list = nullptr;
}
RID RendererSceneSkyRD::sky_get_material(RID p_sky) const {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND_V(!sky, RID());
return sky->material;
}
RID RendererSceneSkyRD::allocate_sky_rid() {
return sky_owner.allocate_rid();
}
void RendererSceneSkyRD::initialize_sky_rid(RID p_rid) {
sky_owner.initialize_rid(p_rid, Sky());
}
RendererSceneSkyRD::Sky *RendererSceneSkyRD::get_sky(RID p_sky) const {
return sky_owner.get_or_null(p_sky);
}
void RendererSceneSkyRD::free_sky(RID p_sky) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND(!sky);
sky->free(storage);
sky_owner.free(p_sky);
}
void RendererSceneSkyRD::sky_set_radiance_size(RID p_sky, int p_radiance_size) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND(!sky);
if (sky->set_radiance_size(p_radiance_size)) {
invalidate_sky(sky);
}
}
void RendererSceneSkyRD::sky_set_mode(RID p_sky, RS::SkyMode p_mode) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND(!sky);
if (sky->set_mode(p_mode)) {
invalidate_sky(sky);
}
}
void RendererSceneSkyRD::sky_set_material(RID p_sky, RID p_material) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND(!sky);
if (sky->set_material(p_material)) {
invalidate_sky(sky);
}
}
Ref<Image> RendererSceneSkyRD::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND_V(!sky, Ref<Image>());
update_dirty_skys();
return sky->bake_panorama(p_energy, p_bake_irradiance ? roughness_layers : 0, p_size);
}
RID RendererSceneSkyRD::sky_get_radiance_texture_rd(RID p_sky) const {
Sky *sky = get_sky(p_sky);
ERR_FAIL_COND_V(!sky, RID());
return sky->radiance;
}