Skip to content

Commit

Permalink
Implement new CPU lightmapper
Browse files Browse the repository at this point in the history
Completely re-write the lightmap generation code:
- Follow the general lightmapper code structure from 4.0.
- Use proper path tracing to compute the global illumination.
- Use atlassing to merge all lightmaps into a single texture (done by @RandomShaper)
- Use OpenImageDenoiser to improve the generated lightmaps.
- Take into account alpha transparency in material textures.
- Allow baking environment lighting.
- Add bicubic lightmap filtering.

There is some minor compatibility breakage in some properties and methods
in BakedLightmap, but lightmaps generated in previous engine versions
should work fine out of the box.

The scene importer has been changed to generate `.unwrap_cache` files
next to the imported scene files. These files *SHOULD* be added to any
version control system as they guarantee there won't be differences when
re-importing the scene from other OSes or engine versions.

This work started as a Google Summer of Code project; Was later funded by IMVU for a good amount of progress;
Was then finished and polished by me on my free time.

Co-authored-by: Pedro J. Estébanez <[email protected]>
  • Loading branch information
JFonS and RandomShaper committed Jan 14, 2021
1 parent a80e4a6 commit 112b416
Show file tree
Hide file tree
Showing 48 changed files with 4,847 additions and 1,598 deletions.
35 changes: 35 additions & 0 deletions core/math/geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "core/print_string.h"
#include "thirdparty/misc/clipper.hpp"
#include "thirdparty/misc/triangulator.h"
#define STB_RECT_PACK_IMPLEMENTATION
#include "thirdparty/stb_rect_pack/stb_rect_pack.h"

#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON.

Expand Down Expand Up @@ -1224,3 +1226,36 @@ Vector<Vector3> Geometry::compute_convex_mesh_points(const Plane *p_planes, int

return points;
}

Vector<Geometry::PackRectsResult> Geometry::partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size) {

Vector<stbrp_node> nodes;
nodes.resize(p_atlas_size.width);
zeromem(nodes.ptrw(), sizeof(stbrp_node) * nodes.size());

stbrp_context context;
stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width);

Vector<stbrp_rect> rects;
rects.resize(p_sizes.size());

for (int i = 0; i < p_sizes.size(); i++) {
rects.write[i].id = i;
rects.write[i].w = p_sizes[i].width;
rects.write[i].h = p_sizes[i].height;
rects.write[i].x = 0;
rects.write[i].y = 0;
rects.write[i].was_packed = 0;
}

stbrp_pack_rects(&context, rects.ptrw(), rects.size());

Vector<PackRectsResult> ret;
ret.resize(p_sizes.size());

for (int i = 0; i < p_sizes.size(); i++) {
ret.write[rects[i].id] = { rects[i].x, rects[i].y, static_cast<bool>(rects[i].was_packed) };
}

return ret;
}
28 changes: 28 additions & 0 deletions core/math/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,27 @@ class Geometry {
return (cn.cross(an) > 0) == orientation;
}

static Vector3 barycentric_coordinates_2d(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
// http://www.blackpawn.com/texts/pointinpoly/
Vector2 v0 = c - a;
Vector2 v1 = b - a;
Vector2 v2 = s - a;

// Compute dot products
double dot00 = v0.dot(v0);
double dot01 = v0.dot(v1);
double dot02 = v0.dot(v2);
double dot11 = v1.dot(v1);
double dot12 = v1.dot(v2);

// Compute barycentric coordinates
double invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
double b2 = (dot11 * dot02 - dot01 * dot12) * invDenom;
double b1 = (dot00 * dot12 - dot01 * dot02) * invDenom;
double b0 = 1.0f - b2 - b1;
return Vector3(b0, b1, b2);
}

static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 *p_segment) {

Vector2 p = p_point - p_segment[0];
Expand Down Expand Up @@ -1014,6 +1035,13 @@ class Geometry {

static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size);

struct PackRectsResult {
int x;
int y;
bool packed;
};
static Vector<PackRectsResult> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size);

static Vector<Vector3> compute_convex_mesh_points(const Plane *p_planes, int p_plane_count);

private:
Expand Down
5 changes: 5 additions & 0 deletions drivers/gles2/rasterizer_scene_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2572,6 +2572,9 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,

if (rebind_lightmap && lightmap) {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_ENERGY, lightmap_energy);
if (storage->config.use_lightmap_filter_bicubic) {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_TEXTURE_SIZE, Vector2(lightmap->width, lightmap->height));
}
}

state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
Expand Down Expand Up @@ -4053,6 +4056,8 @@ void RasterizerSceneGLES2::initialize() {
}

void RasterizerSceneGLES2::iteration() {
storage->config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling");
state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_FILTER_BICUBIC, storage->config.use_lightmap_filter_bicubic);
shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode")));
}

Expand Down
1 change: 1 addition & 0 deletions drivers/gles2/rasterizer_storage_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6299,6 +6299,7 @@ void RasterizerStorageGLES2::initialize() {

config.force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
config.use_fast_texture_filter = GLOBAL_GET("rendering/quality/filters/use_nearest_mipmap_filter");
config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling");
}

void RasterizerStorageGLES2::finalize() {
Expand Down
1 change: 1 addition & 0 deletions drivers/gles2/rasterizer_storage_gles2.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
bool shrink_textures_x2;
bool use_fast_texture_filter;
bool use_skeleton_software;
bool use_lightmap_filter_bicubic;

int max_vertex_texture_image_units;
int max_texture_image_units;
Expand Down
70 changes: 69 additions & 1 deletion drivers/gles2/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,74 @@ void reflection_process(samplerCube reflection_map,
#ifdef USE_LIGHTMAP
uniform mediump sampler2D lightmap; //texunit:-4
uniform mediump float lightmap_energy;

#ifdef USE_LIGHTMAP_FILTER_BICUBIC
uniform mediump vec2 lightmap_texture_size;

// w0, w1, w2, and w3 are the four cubic B-spline basis functions
float w0(float a) {
return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0);
}

float w1(float a) {
return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0);
}

float w2(float a) {
return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0);
}

float w3(float a) {
return (1.0 / 6.0) * (a * a * a);
}

// g0 and g1 are the two amplitude functions
float g0(float a) {
return w0(a) + w1(a);
}

float g1(float a) {
return w2(a) + w3(a);
}

// h0 and h1 are the two offset functions
float h0(float a) {
return -1.0 + w1(a) / (w0(a) + w1(a));
}

float h1(float a) {
return 1.0 + w3(a) / (w2(a) + w3(a));
}

vec4 texture2D_bicubic(sampler2D tex, vec2 uv) {
vec2 texel_size = vec2(1.0) / lightmap_texture_size;

uv = uv * lightmap_texture_size + vec2(0.5);

vec2 iuv = floor(uv);
vec2 fuv = fract(uv);

float g0x = g0(fuv.x);
float g1x = g1(fuv.x);
float h0x = h0(fuv.x);
float h1x = h1(fuv.x);
float h0y = h0(fuv.y);
float h1y = h1(fuv.y);

vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size;
vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size;
vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size;
vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size;

return (g0(fuv.y) * (g0x * texture2D(tex, p0) + g1x * texture2D(tex, p1))) +
(g1(fuv.y) * (g0x * texture2D(tex, p2) + g1x * texture2D(tex, p3)));
}
#define LIGHTMAP_TEXTURE_SAMPLE(m_tex, m_uv) texture2D_bicubic(m_tex, m_uv)

#else //!USE_LIGHTMAP_FILTER_BICUBIC
#define LIGHTMAP_TEXTURE_SAMPLE(m_tex, m_uv) texture2D(m_tex, m_uv)

#endif //USE_LIGHTMAP_FILTER_BICUBIC
#endif

#ifdef USE_LIGHTMAP_CAPTURE
Expand Down Expand Up @@ -1661,7 +1729,7 @@ FRAGMENT_SHADER_CODE

#ifdef USE_LIGHTMAP
//ambient light will come entirely from lightmap is lightmap is used
ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy;
ambient_light = LIGHTMAP_TEXTURE_SAMPLE(lightmap, uv2_interp).rgb * lightmap_energy;
#endif

#ifdef USE_LIGHTMAP_CAPTURE
Expand Down
20 changes: 19 additions & 1 deletion drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1949,7 +1949,17 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform

if (lightmap && capture) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 9);
glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
if (e->instance->lightmap_slice == -1) {
glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
} else {
glBindTexture(GL_TEXTURE_2D_ARRAY, lightmap->tex_id);
state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_LAYER, e->instance->lightmap_slice);
}
const Rect2 &uvr = e->instance->lightmap_uv_rect;
state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_UV_RECT, Color(uvr.get_position().x, uvr.get_position().y, uvr.get_size().x, uvr.get_size().y));
if (storage->config.use_lightmap_filter_bicubic) {
state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, Vector2(lightmap->width, lightmap->height));
}
state.scene_shader.set_uniform(SceneShaderGLES3::LIGHTMAP_ENERGY, capture->energy);
}
}
Expand Down Expand Up @@ -2080,6 +2090,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_LAYERED, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, false);

Expand All @@ -2088,6 +2099,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_

state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, e->instance->gi_probe_instances.size() > 0);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, e->instance->lightmap.is_valid() && e->instance->gi_probe_instances.size() == 0);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_LAYERED, e->instance->lightmap_slice != -1);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, !e->instance->lightmap_capture_data.empty() && !e->instance->lightmap.is_valid() && e->instance->gi_probe_instances.size() == 0);

state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS, false);
Expand Down Expand Up @@ -2258,6 +2270,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
state.scene_shader.set_conditional(SceneShaderGLES3::SHADOW_MODE_PCF_13, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_GI_PROBES, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_LAYERED, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_CAPTURE, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_CONTACT_SHADOWS, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_VERTEX_LIGHTING, false);
Expand Down Expand Up @@ -2392,6 +2405,9 @@ void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::G

if (e->instance->lightmap.is_valid()) {
e->sort_key |= SORT_KEY_LIGHTMAP_FLAG;
if (e->instance->lightmap_slice != -1) {
e->sort_key |= SORT_KEY_LIGHTMAP_LAYERED_FLAG;
}
}

if (!e->instance->lightmap_capture_data.empty()) {
Expand Down Expand Up @@ -5337,6 +5353,8 @@ void RasterizerSceneGLES3::iteration() {
subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/quality")));
subsurface_scatter_size = GLOBAL_GET("rendering/quality/subsurface_scattering/scale");

storage->config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling");
state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_FILTER_BICUBIC, storage->config.use_lightmap_filter_bicubic);
state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET("rendering/quality/voxel_cone_tracing/high_quality"));
}

Expand Down
9 changes: 5 additions & 4 deletions drivers/gles3/rasterizer_scene_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -680,14 +680,15 @@ class RasterizerSceneGLES3 : public RasterizerScene {
SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT = 52,
SORT_KEY_OPAQUE_DEPTH_LAYER_MASK = 0xF,
//64 bits unsupported in MSVC
#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 49)
#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 48)
#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 47)
#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 50)
#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 49)
#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 48)
#define SORT_KEY_LIGHTMAP_LAYERED_FLAG (uint64_t(1) << 47)
#define SORT_KEY_LIGHTMAP_FLAG (uint64_t(1) << 46)
#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 45)
#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 44)
SORT_KEY_SHADING_SHIFT = 44,
SORT_KEY_SHADING_MASK = 63,
SORT_KEY_SHADING_MASK = 127,
//44-28 material index
SORT_KEY_MATERIAL_INDEX_SHIFT = 28,
//28-8 geometry index
Expand Down
2 changes: 2 additions & 0 deletions drivers/gles3/rasterizer_storage_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8555,6 +8555,8 @@ void RasterizerStorageGLES3::initialize() {

String renderer = (const char *)glGetString(GL_RENDERER);

config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling");

config.use_depth_prepass = bool(GLOBAL_GET("rendering/quality/depth_prepass/enable"));
if (config.use_depth_prepass) {

Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/rasterizer_storage_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
bool shrink_textures_x2;
bool use_fast_texture_filter;
bool use_anisotropic_filter;
bool use_lightmap_filter_bicubic;

bool s3tc_supported;
bool latc_supported;
Expand Down
Loading

0 comments on commit 112b416

Please sign in to comment.