Implemented environment arrays for skybox reflection and roughness, quality increase is enormous.
This commit is contained in:
parent
d84ed525a9
commit
7263137dba
8 changed files with 294 additions and 107 deletions
|
@ -451,9 +451,10 @@ Basis::operator String() const {
|
|||
}
|
||||
|
||||
Basis::operator Quat() const {
|
||||
#ifdef MATH_CHECKS
|
||||
ERR_FAIL_COND_V(is_rotation() == false, Quat());
|
||||
#endif
|
||||
//commenting this check because precision issues cause it to fail when it shouldn't
|
||||
//#ifdef MATH_CHECKS
|
||||
//ERR_FAIL_COND_V(is_rotation() == false, Quat());
|
||||
//#endif
|
||||
real_t trace = elements[0][0] + elements[1][1] + elements[2][2];
|
||||
real_t temp[4];
|
||||
|
||||
|
|
|
@ -1884,14 +1884,21 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
|
|||
|
||||
if (p_base_env) {
|
||||
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
|
||||
if (storage->config.use_texture_array_environment) {
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, p_base_env);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, p_base_env);
|
||||
}
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, true);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP_ARRAY, storage->config.use_texture_array_environment);
|
||||
} else {
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP_ARRAY, false);
|
||||
}
|
||||
} else {
|
||||
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, false);
|
||||
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP_ARRAY, false);
|
||||
}
|
||||
|
||||
state.cull_front = false;
|
||||
|
|
|
@ -1275,6 +1275,118 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
|
|||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glGenTextures(1, &sky->radiance);
|
||||
|
||||
if (config.use_texture_array_environment) {
|
||||
|
||||
//texture3D
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, sky->radiance);
|
||||
|
||||
GLuint tmp_fb;
|
||||
|
||||
glGenFramebuffers(1, &tmp_fb);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb);
|
||||
|
||||
int size = p_radiance_size;
|
||||
|
||||
int array_level = 6;
|
||||
|
||||
bool use_float = config.hdr_supported;
|
||||
|
||||
GLenum internal_format = use_float ? GL_RGBA16F : GL_RGB10_A2;
|
||||
GLenum format = GL_RGBA;
|
||||
GLenum type = use_float ? GL_HALF_FLOAT : GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internal_format, size, size * 2, array_level, 0, format, type, NULL);
|
||||
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
GLuint tmp_fb2;
|
||||
GLuint tmp_tex;
|
||||
{
|
||||
//generate another one for rendering, as can't read and write from a single texarray it seems
|
||||
glGenFramebuffers(1, &tmp_fb2);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb2);
|
||||
glGenTextures(1, &tmp_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tmp_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size, size * 2, 0, format, type, NULL);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_tex, 0);
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
#ifdef DEBUG_ENABLED
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE);
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int j = 0; j < array_level; j++) {
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb2);
|
||||
|
||||
if (j == 0) {
|
||||
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, false);
|
||||
shaders.cubemap_filter.bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(texture->target, texture->tex_id);
|
||||
} else {
|
||||
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false);
|
||||
shaders.cubemap_filter.bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, sky->radiance);
|
||||
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::SOURCE_ARRAY_INDEX, j - 1); //read from previous to ensure better blur
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
glViewport(0, i * size, size, size);
|
||||
glBindVertexArray(resources.quadie_array);
|
||||
|
||||
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::Z_FLIP, i > 0);
|
||||
shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::ROUGHNESS, j / float(array_level - 1));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tmp_fb);
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, sky->radiance, 0, j);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, tmp_fb2);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glBlitFramebuffer(0, 0, size, size * 2, 0, 0, size, size * 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, false);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_DUAL_PARABOLOID_ARRAY, false);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false);
|
||||
|
||||
//restore ranges
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, sky->radiance);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
|
||||
glDeleteFramebuffers(1, &tmp_fb);
|
||||
glDeleteFramebuffers(1, &tmp_fb2);
|
||||
glDeleteTextures(1, &tmp_tex);
|
||||
|
||||
} else {
|
||||
//regular single texture with mipmaps
|
||||
glBindTexture(GL_TEXTURE_2D, sky->radiance);
|
||||
|
||||
GLuint tmp_fb;
|
||||
|
@ -1315,7 +1427,7 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
|
|||
size = p_radiance_size;
|
||||
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_PANORAMA, true);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true);
|
||||
shaders.cubemap_filter.bind();
|
||||
|
||||
while (mm_level) {
|
||||
|
@ -1343,7 +1455,7 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
|
|||
mm_level--;
|
||||
}
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, false);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_PANORAMA, false);
|
||||
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false);
|
||||
|
||||
//restore ranges
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
|
@ -1356,6 +1468,7 @@ void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_ra
|
|||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
|
||||
glDeleteFramebuffers(1, &tmp_fb);
|
||||
}
|
||||
}
|
||||
|
||||
/* SHADER API */
|
||||
|
@ -6857,6 +6970,7 @@ void RasterizerStorageGLES3::initialize() {
|
|||
frame.current_rt = NULL;
|
||||
config.keep_original_textures = false;
|
||||
config.generate_wireframes = false;
|
||||
config.use_texture_array_environment = GLOBAL_DEF("rendering/quality/texture_array_environments", true);
|
||||
}
|
||||
|
||||
void RasterizerStorageGLES3::finalize() {
|
||||
|
|
|
@ -86,6 +86,8 @@ public:
|
|||
|
||||
bool generate_wireframes;
|
||||
|
||||
bool use_texture_array_environment;
|
||||
|
||||
Set<String> extensions;
|
||||
|
||||
bool keep_original_textures;
|
||||
|
|
|
@ -19,9 +19,16 @@ void main() {
|
|||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
#ifdef USE_PANORAMA
|
||||
#ifdef USE_SOURCE_PANORAMA
|
||||
uniform sampler2D source_panorama; //texunit:0
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
|
||||
uniform sampler2DArray source_dual_paraboloid_array; //texunit:0
|
||||
uniform int source_array_index;
|
||||
#endif
|
||||
|
||||
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA)
|
||||
uniform samplerCube source_cube; //texunit:0
|
||||
#endif
|
||||
|
||||
|
@ -169,7 +176,7 @@ vec2 Hammersley(uint i, uint N) {
|
|||
|
||||
uniform bool z_flip;
|
||||
|
||||
#ifdef USE_PANORAMA
|
||||
#ifdef USE_SOURCE_PANORAMA
|
||||
|
||||
vec4 texturePanorama(vec3 normal,sampler2D pano ) {
|
||||
|
||||
|
@ -189,6 +196,21 @@ vec4 texturePanorama(vec3 normal,sampler2D pano ) {
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
|
||||
|
||||
|
||||
vec4 textureDualParaboloidArray(vec3 normal) {
|
||||
|
||||
vec3 norm = normalize(normal);
|
||||
norm.xy/=1.0+abs(norm.z);
|
||||
norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
|
||||
norm.y+=max(0.0,sign(-norm.z))*0.5;
|
||||
return textureLod(source_dual_paraboloid_array, vec3(norm.xy, float(source_array_index) ), 0.0);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
|
||||
#ifdef USE_DUAL_PARABOLOID
|
||||
|
@ -198,7 +220,7 @@ void main() {
|
|||
N = normalize(N);
|
||||
|
||||
if (!z_flip) {
|
||||
N.y=-N.y; //y is flipped to improve blending between both sides
|
||||
//N.y=-N.y; //y is flipped to improve blending between both sides
|
||||
} else {
|
||||
N.z=-N.z;
|
||||
}
|
||||
|
@ -212,13 +234,24 @@ void main() {
|
|||
|
||||
#ifdef USE_DIRECT_WRITE
|
||||
|
||||
#ifdef USE_PANORAMA
|
||||
#ifdef USE_SOURCE_PANORAMA
|
||||
|
||||
frag_color=vec4(texturePanorama(N,source_panorama).rgb,1.0);
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
|
||||
|
||||
frag_color=vec4(textureDualParaboloidArray(N).rgb,1.0);
|
||||
#endif
|
||||
|
||||
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA)
|
||||
|
||||
frag_color=vec4(texture(N,source_cube).rgb,1.0);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
@ -233,9 +266,16 @@ void main() {
|
|||
float ndotl = clamp(dot(N, L),0.0,1.0);
|
||||
|
||||
if (ndotl>0.0) {
|
||||
#ifdef USE_PANORAMA
|
||||
#ifdef USE_SOURCE_PANORAMA
|
||||
sum.rgb += texturePanorama(H,source_panorama).rgb *ndotl;
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY
|
||||
|
||||
sum.rgb += textureDualParaboloidArray(H).rgb *ndotl;
|
||||
#endif
|
||||
|
||||
#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA)
|
||||
sum.rgb += textureLod(source_cube, H, 0.0).rgb *ndotl;
|
||||
#endif
|
||||
sum.a += ndotl;
|
||||
|
|
|
@ -405,7 +405,6 @@ uniform bool no_ambient_light;
|
|||
|
||||
#ifdef USE_RADIANCE_MAP
|
||||
|
||||
uniform sampler2D radiance_map; //texunit:-2
|
||||
|
||||
|
||||
layout(std140) uniform Radiance { //ubo:2
|
||||
|
@ -415,6 +414,49 @@ layout(std140) uniform Radiance { //ubo:2
|
|||
|
||||
};
|
||||
|
||||
#define RADIANCE_MAX_LOD 5.0
|
||||
|
||||
#ifdef USE_RADIANCE_MAP_ARRAY
|
||||
|
||||
uniform sampler2DArray radiance_map; //texunit:-2
|
||||
|
||||
vec3 textureDualParaboloid(sampler2DArray p_tex, vec3 p_vec,float p_roughness) {
|
||||
|
||||
vec3 norm = normalize(p_vec);
|
||||
norm.xy/=1.0+abs(norm.z);
|
||||
norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
|
||||
|
||||
// we need to lie the derivatives (normg) and assume that DP side is always the same
|
||||
// to get proper texure filtering
|
||||
vec2 normg=norm.xy;
|
||||
norm.y+=max(0.0,sign(norm.z))*0.5;
|
||||
|
||||
// thanks to OpenGL spec using floor(layer + 0.5) for texture arrays,
|
||||
// it's easy to have precision errors using fract() to interpolate layers
|
||||
// as such, using fixed point to ensure it works.
|
||||
|
||||
float index = p_roughness * RADIANCE_MAX_LOD;
|
||||
int indexi = int(index * 256.0);
|
||||
vec3 base = textureGrad(p_tex, vec3(norm.xy, float(indexi/256)),dFdx(normg),dFdy(normg)).xyz;
|
||||
vec3 next = textureGrad(p_tex, vec3(norm.xy, float(indexi/256+1)),dFdx(normg),dFdy(normg)).xyz;
|
||||
return mix(base,next,float(indexi%256)/256.0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
uniform sampler2D radiance_map; //texunit:-2
|
||||
|
||||
vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec,float p_roughness) {
|
||||
|
||||
vec3 norm = normalize(p_vec);
|
||||
norm.xy/=1.0+abs(norm.z);
|
||||
norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25);
|
||||
norm.y+=max(0.0,sign(norm.z))*0.5;
|
||||
return textureLod(p_tex, norm.xy, p_roughness * RADIANCE_MAX_LOD).xyz;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Material Uniforms */
|
||||
|
@ -1231,23 +1273,7 @@ void gi_probes_compute(vec3 pos, vec3 normal, float roughness, inout vec3 out_sp
|
|||
|
||||
#endif
|
||||
|
||||
vec3 textureDualParabolod(sampler2D p_tex, vec3 p_vec,float p_lod) {
|
||||
|
||||
vec3 norm = normalize(p_vec);
|
||||
float y_ofs=0.0;
|
||||
if (norm.z>=0.0) {
|
||||
|
||||
norm.z+=1.0;
|
||||
y_ofs+=0.5;
|
||||
} else {
|
||||
norm.z=1.0 - norm.z;
|
||||
norm.y=-norm.y;
|
||||
}
|
||||
|
||||
norm.xy/=norm.z;
|
||||
norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25+y_ofs);
|
||||
return textureLod(p_tex, norm.xy, p_lod).xyz;
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
|
@ -1387,15 +1413,11 @@ FRAGMENT_SHADER_CODE
|
|||
} else {
|
||||
{
|
||||
|
||||
|
||||
#define RADIANCE_MAX_LOD 5.0
|
||||
float lod = roughness * RADIANCE_MAX_LOD;
|
||||
|
||||
{ //read radiance from dual paraboloid
|
||||
|
||||
vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n);
|
||||
ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz);
|
||||
vec3 radiance = textureDualParabolod(radiance_map,ref_vec,lod) * bg_energy;
|
||||
vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy;
|
||||
specular_light = radiance;
|
||||
|
||||
}
|
||||
|
@ -1407,7 +1429,7 @@ FRAGMENT_SHADER_CODE
|
|||
{
|
||||
|
||||
vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz);
|
||||
vec3 env_ambient=textureDualParabolod(radiance_map,ambient_dir,RADIANCE_MAX_LOD) * bg_energy;
|
||||
vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy;
|
||||
|
||||
ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution);
|
||||
//ambient_light=vec3(0.0,0.0,0.0);
|
||||
|
|
|
@ -340,12 +340,13 @@ void MeshInstance::create_debug_tagents() {
|
|||
mi->set_mesh(am);
|
||||
mi->set_name("DebugTangents");
|
||||
add_child(mi);
|
||||
if (get_parent()) {
|
||||
if (get_parent() == get_tree()->get_edited_scene_root())
|
||||
mi->set_owner(get_parent());
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
if (this == get_tree()->get_edited_scene_root())
|
||||
mi->set_owner(this);
|
||||
else
|
||||
mi->set_owner(get_parent()->get_owner());
|
||||
}
|
||||
mi->set_owner(get_owner());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -818,7 +818,7 @@ float Environment::get_fog_height_curve() const {
|
|||
void Environment::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_background", "mode"), &Environment::set_background);
|
||||
ClassDB::bind_method(D_METHOD("set_sky", "sky:CubeMap"), &Environment::set_sky);
|
||||
ClassDB::bind_method(D_METHOD("set_sky", "sky:Sky"), &Environment::set_sky);
|
||||
ClassDB::bind_method(D_METHOD("set_sky_scale", "scale"), &Environment::set_sky_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &Environment::set_bg_color);
|
||||
ClassDB::bind_method(D_METHOD("set_bg_energy", "energy"), &Environment::set_bg_energy);
|
||||
|
|
Loading…
Reference in a new issue