Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion samples/core/editors/unit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ function UnitBox:draw()
end
end

function UnitBox:set_light(type, range, intensity, angle, cr, cg, cb, bias, cast_shadows)
function UnitBox:set_light(type, range, intensity, angle, cr, cg, cb, bias, cast_shadows, cookie, cookie_scale, cookie_x, cookie_y)
local nv, nq, nm = Device.temp_count()
local light = RenderWorld.light_instance(self._rw, self._unit_id)
if light then
Expand All @@ -505,6 +505,8 @@ function UnitBox:set_light(type, range, intensity, angle, cr, cg, cb, bias, cast
RenderWorld.light_set_spot_angle(self._rw, light, angle)
RenderWorld.light_set_shadow_bias(self._rw, light, bias)
RenderWorld.light_set_cast_shadows(self._rw, light, cast_shadows)
RenderWorld.light_set_cookie(self._rw, light, cookie)
RenderWorld.light_set_cookie_transform(self._rw, light, Vector3(cookie_scale, cookie_x, cookie_y))
end
Device.set_temp_count(nv, nq, nm)
end
Expand Down
91 changes: 73 additions & 18 deletions samples/core/shaders/lighting.shader
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ bgfx_shaders = {

code = """
#if !defined(NO_LIGHT)
# define LIGHT_SIZE 22 // In vec4 units.
# define LIGHT_SIZE 25 // In vec4 units.
# define MAX_NUM_LIGHTS 32
# define MAX_NUM_CASCADES 4
uniform vec4 u_lights_num; // num_dir, num_omni, num_spot
Expand All @@ -26,6 +26,7 @@ bgfx_shaders = {
# define local_lights_shadow_map_samples u_shadow_map_params[1].y
SAMPLER2DSHADOW(u_cascaded_shadow_map, 10);
SAMPLER2DSHADOW(u_local_lights_shadow_map, 11);
SAMPLER2D(u_lights_cookie_atlas, 13);
uniform vec4 u_local_lights_params;
# define local_lights_distance_culling u_local_lights_params.x
# define local_lights_distance_culling_fade u_local_lights_params.y
Expand Down Expand Up @@ -189,6 +190,21 @@ bgfx_shaders = {
;
}

vec3 sample_light_cookie(vec4 rect, vec2 uv, vec4 transform, float center, float flip_y)
{
uv = (uv - center) / max(transform.x, 0.001) + center + transform.yz;
uv.y = flip_y > 0.0 ? 1.0 - uv.y : uv.y;
vec2 atlas_uv = mix(rect.xy, rect.zw, fract(uv));
return texture2D(u_lights_cookie_atlas, atlas_uv).rgb;
}

vec2 equirectangular_uv(vec3 direction)
{
return vec2(atan2(direction.x, direction.z) * (0.5 / M_PI) + 0.5
, acos(clamp(direction.y, -1.0, 1.0)) / M_PI
);
}

vec4 lights_data(int offset)
{
float u = (float(offset) + 0.5) / float(LIGHT_SIZE * MAX_NUM_LIGHTS);
Expand Down Expand Up @@ -231,7 +247,7 @@ bgfx_shaders = {
vec3 sun_color = vec3(1, 1, 1);

if (num_dir > 0) {
// Brightest directional light (index == 0) generates cascaded shadow maps.
// Brightest directional light (index == 0) generates cascaded shadow maps and supports a cookie.
vec3 light_color = lights_data(loffset + 0).rgb;
float intensity = lights_data(loffset + 0).w;
vec3 direction = lights_data(loffset + 2).xyz;
Expand All @@ -248,6 +264,11 @@ bgfx_shaders = {
float atlas_size = lights_data(loffset + 21).x;
float shadow_bias = lights_data(loffset + 21).y;
float cast_shadow = lights_data(loffset + 21).z;
vec4 cookie_rect = lights_data(loffset + 22);
vec4 cookie_up = lights_data(loffset + 23);
// .x is the scale (world-space size in meters of one cookie
// tile), .yz is the (x, y) offset of the cookie pattern.
vec4 cookie_transform = lights_data(loffset + 24);
loffset += LIGHT_SIZE;

sun_color = light_color;
Expand All @@ -263,6 +284,14 @@ bgfx_shaders = {
, f0
);

if (cookie_rect.z > 0.0) {
vec3 cookie_right = cross(direction, cookie_up.xyz);
vec2 cookie_uv = vec2(dot(shadow_local.xyz, cookie_right)
, dot(shadow_local.xyz, cookie_up.xyz)
);
local_radiance *= sample_light_cookie(cookie_rect, cookie_uv, cookie_transform, 0.0, cookie_up.w);
}

if (cast_shadow == 1.0 && receive_shadow) {
vec3 shadow_world = shadow_local.xyz;
vec3 atlas_offset0 = vec3(atlas_u.x , atlas_v.x , atlas_size);
Expand Down Expand Up @@ -337,7 +366,11 @@ bgfx_shaders = {
float intensity = lights_data(loffset + 0).w;
vec3 position = lights_data(loffset + 1).xyz;
float range = lights_data(loffset + 1).w;
vec3 direction = lights_data(loffset + 2).xyz;
float cast_shadow = lights_data(loffset + 21).z;
vec4 cookie_rect = lights_data(loffset + 22);
vec4 cookie_up = lights_data(loffset + 23);
vec4 cookie_transform = lights_data(loffset + 24);
vec3 light_pos = mul(position, tbn);

vec3 local_radiance = calc_omni_light(n
Expand All @@ -353,6 +386,17 @@ bgfx_shaders = {
, f0
);

if (cookie_rect.z > 0.0) {
vec3 to_frag = normalize(shadow_local.xyz - position);
vec3 cookie_right = cross(direction, cookie_up.xyz);
vec3 cookie_direction = vec3(dot(to_frag, cookie_right)
, dot(to_frag, cookie_up.xyz)
, dot(to_frag, direction)
);
vec2 cookie_uv = equirectangular_uv(cookie_direction);
local_radiance *= sample_light_cookie(cookie_rect, cookie_uv, cookie_transform, 0.5, cookie_up.w);
}

if (cast_shadow == 1.0 && receive_shadow) {
// Tetrahedron normals.
CONST(vec3 bn) = vec3( 0.0f, 0.81649661f, -0.57735026f);
Expand Down Expand Up @@ -444,6 +488,8 @@ bgfx_shaders = {
vec3 direction = lights_data(loffset + 2).xyz;
float spot_angle = lights_data(loffset + 2).w;
float cast_shadow = lights_data(loffset + 21).z;
vec4 cookie_rect = lights_data(loffset + 22);
vec4 cookie_transform = lights_data(loffset + 24);

vec3 local_radiance = calc_spot_light(n
, v
Expand All @@ -460,28 +506,37 @@ bgfx_shaders = {
, f0
);

if (cast_shadow == 1.0 && receive_shadow) {
if (cookie_rect.z > 0.0 || (cast_shadow == 1.0 && receive_shadow)) {
mat4 mvp = mtxFromCols(lights_data(loffset + 3)
, lights_data(loffset + 4)
, lights_data(loffset + 5)
, lights_data(loffset + 6)
);
vec4 atlas_u = lights_data(loffset + 19);
vec4 atlas_v = lights_data(loffset + 20);
float atlas_size = lights_data(loffset + 21).x;
float shadow_bias = lights_data(loffset + 21).y;
vec3 atlas_offset = vec3(atlas_u.x, atlas_v.x, atlas_size);
vec4 atlas_shadow_pos0 = atlas_shadow_coord(mul(mvp, shadow_local), atlas_offset);
vec4 light_clip = mul(mvp, shadow_local);

if (shadow_coord_inside_atlas_tile(atlas_shadow_pos0, atlas_offset)) {
local_radiance *= shadow(u_local_lights_shadow_map
, atlas_shadow_pos0
, shadow_bias
, local_lights_sm_texel_size
, local_lights_shadow_map_samples
);
} else {
local_radiance *= 0.0;
if (cookie_rect.z > 0.0) {
vec2 cookie_uv = light_clip.xy / light_clip.w;
local_radiance *= sample_light_cookie(cookie_rect, cookie_uv, cookie_transform, 0.5, 0.0);
}

if (cast_shadow == 1.0 && receive_shadow) {
vec4 atlas_u = lights_data(loffset + 19);
vec4 atlas_v = lights_data(loffset + 20);
float atlas_size = lights_data(loffset + 21).x;
float shadow_bias = lights_data(loffset + 21).y;
vec3 atlas_offset = vec3(atlas_u.x, atlas_v.x, atlas_size);
vec4 atlas_shadow_pos0 = atlas_shadow_coord(light_clip, atlas_offset);

if (shadow_coord_inside_atlas_tile(atlas_shadow_pos0, atlas_offset)) {
local_radiance *= shadow(u_local_lights_shadow_map
, atlas_shadow_pos0
, shadow_bias
, local_lights_sm_texel_size
, local_lights_shadow_map_samples
);
} else {
local_radiance *= 0.0;
}
}
}

Expand Down
128 changes: 128 additions & 0 deletions src/device/pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
* SPDX-License-Identifier: MIT
*/

#include "core/memory/allocator.h"
#include "core/memory/globals.h"
#include "core/strings/string_id.inl"
#include "core/types.h"
#include "device/pipeline.h"
#include "world/shader_manager.h"
#include "core/math/matrix4x4.inl"
#include <bx/math.h>
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb_rect_pack.h>

namespace crown
{
Expand Down Expand Up @@ -178,6 +182,12 @@ Pipeline::Pipeline(ShaderManager &sm)
, _sun_shadow_map_frame_buffer(BGFX_INVALID_HANDLE)
, _local_lights_shadow_map_texture(BGFX_INVALID_HANDLE)
, _local_lights_shadow_map_frame_buffer(BGFX_INVALID_HANDLE)
, _u_lights_cookie_atlas(BGFX_INVALID_HANDLE)
, _lights_cookie_atlas_texture(BGFX_INVALID_HANDLE)
, _lights_cookie_atlas_frame_buffer(BGFX_INVALID_HANDLE)
, _lights_cookie_atlas_supported(false)
, _lights_cookie_atlas_packer(NULL)
, _lights_cookie_atlas_packer_nodes(NULL)
, _bloom_map(BGFX_INVALID_HANDLE)
, _map_pixel_size(BGFX_INVALID_HANDLE)
, _bloom_params(BGFX_INVALID_HANDLE)
Expand Down Expand Up @@ -285,6 +295,39 @@ void Pipeline::create(u16 width, u16 height, const RenderSettings &render_settin
_fog_data = bgfx::createUniform("u_fog_data", bgfx::UniformType::Vec4, 2);
_lighting_params = bgfx::createUniform("u_lighting_params", bgfx::UniformType::Vec4);

_lights_cookie_atlas_supported = bgfx::isTextureValid(0
, false
, 1
, bgfx::TextureFormat::RGBA8
, BGFX_TEXTURE_RT
);
_u_lights_cookie_atlas = bgfx::createUniform("u_lights_cookie_atlas", bgfx::UniformType::Sampler);
if (_lights_cookie_atlas_supported) {
const u16 atlas_w = (u16)_render_settings.lights_cookie_atlas_size.x;
const u16 atlas_h = (u16)_render_settings.lights_cookie_atlas_size.y;
_lights_cookie_atlas_texture = bgfx::createTexture2D(atlas_w
, atlas_h
, false
, 1
, bgfx::TextureFormat::RGBA8
, BGFX_TEXTURE_RT | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP
);
_lights_cookie_atlas_frame_buffer = bgfx::createFrameBuffer(1, &_lights_cookie_atlas_texture);
_lights_cookie_atlas_packer = (stbrp_context *)default_allocator().allocate(sizeof(stbrp_context));
_lights_cookie_atlas_packer_nodes = (stbrp_node *)default_allocator().allocate(sizeof(stbrp_node) * atlas_w);
} else {
// Keep the sampler valid on renderers without RGBA8 render targets.
const u32 pixel = 0x00000000;
_lights_cookie_atlas_texture = bgfx::createTexture2D(1
, 1
, false
, 1
, bgfx::TextureFormat::RGBA8
, BGFX_TEXTURE_NONE
, bgfx::copy(&pixel, sizeof(pixel))
);
}

_bloom_map = bgfx::createUniform("s_bloom_map", bgfx::UniformType::Sampler);
_map_pixel_size = bgfx::createUniform("u_map_pixel_size", bgfx::UniformType::Vec4);
_bloom_params = bgfx::createUniform("u_bloom_params", bgfx::UniformType::Vec4);
Expand Down Expand Up @@ -316,6 +359,21 @@ void Pipeline::destroy()
bgfx::destroy(_lights_num);
_lights_num = BGFX_INVALID_HANDLE;

// Destroy light cookie resources.
bgfx::destroy(_u_lights_cookie_atlas);
_u_lights_cookie_atlas = BGFX_INVALID_HANDLE;
if (bgfx::isValid(_lights_cookie_atlas_frame_buffer))
bgfx::destroy(_lights_cookie_atlas_frame_buffer);
_lights_cookie_atlas_frame_buffer = BGFX_INVALID_HANDLE;
bgfx::destroy(_lights_cookie_atlas_texture);
_lights_cookie_atlas_texture = BGFX_INVALID_HANDLE;
if (_lights_cookie_atlas_packer != NULL) {
default_allocator().deallocate(_lights_cookie_atlas_packer);
default_allocator().deallocate(_lights_cookie_atlas_packer_nodes);
_lights_cookie_atlas_packer = NULL;
_lights_cookie_atlas_packer_nodes = NULL;
}

// Destroy local-lights shadow map resources.
bgfx::destroy(_u_local_lights_params);
_u_local_lights_params = BGFX_INVALID_HANDLE;
Expand Down Expand Up @@ -619,6 +677,24 @@ void Pipeline::reset(u16 width, u16 height)
} else if (id >= View::SM_LOCAL_0 && id < View::SM_LOCAL_LAST) {
view_name = "sm_local_lights";
bgfx::setViewFrameBuffer(id, _local_lights_shadow_map_frame_buffer);
} else if (id == View::LOCAL_LIGHTS_COOKIE_ATLAS_CLEAR) {
view_name = "lights_cookie_atlas_clear";
if (_lights_cookie_atlas_supported) {
bgfx::setViewFrameBuffer(id, _lights_cookie_atlas_frame_buffer);
bgfx::setViewRect(id
, 0
, 0
, (u16)_render_settings.lights_cookie_atlas_size.x
, (u16)_render_settings.lights_cookie_atlas_size.y
);
bgfx::setViewClear(id, BGFX_CLEAR_COLOR, 0x00000000, 1.0f, 0);
}
} else if (id >= View::LOCAL_LIGHTS_COOKIE_ATLAS_0 && id < View::LOCAL_LIGHTS_COOKIE_ATLAS_LAST) {
view_name = "lights_cookie_atlas";
if (_lights_cookie_atlas_supported) {
bgfx::setViewFrameBuffer(id, _lights_cookie_atlas_frame_buffer);
bgfx::setViewTransform(id, NULL, ortho);
}
} else if (id == View::LIGHTS) {
view_name = "lights_data";
} else if (id == View::MESH) {
Expand Down Expand Up @@ -971,6 +1047,58 @@ void Pipeline::render(u16 width, u16 height, const Matrix4x4 &view, const Matrix
bgfx::submit(View::BLIT, _blit_shader.program);
}

void Pipeline::begin_light_cookie_atlas()
{
if (!_lights_cookie_atlas_supported)
return;

const u16 width = (u16)_render_settings.lights_cookie_atlas_size.x;
const u16 height = (u16)_render_settings.lights_cookie_atlas_size.y;
stbrp_init_target(_lights_cookie_atlas_packer
, width
, height
, _lights_cookie_atlas_packer_nodes
, width
);
bgfx::touch(View::LOCAL_LIGHTS_COOKIE_ATLAS_CLEAR);
}

Vector4 Pipeline::add_light_cookie(u16 &view, bgfx::TextureHandle texture, u16 width, u16 height)
{
if (!_lights_cookie_atlas_supported)
return VECTOR4_ZERO;

stbrp_rect packed = { 0, width, height, 0, 0, 0 };
stbrp_pack_rects(_lights_cookie_atlas_packer, &packed, 1);
if (!packed.was_packed)
return VECTOR4_ZERO;

CE_ASSERT(view < View::LOCAL_LIGHTS_COOKIE_ATLAS_LAST, "Too many light cookies");
bgfx::setViewRect(view, packed.x, packed.y, width, height);
const u32 sampler_flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP;
bgfx::setTexture(0, _color_map, texture, sampler_flags);
screenSpaceQuad(width, height, 0.0f, bgfx::getCaps()->originBottomLeft);
bgfx::setState(_blit_shader.state);
bgfx::submit(view++, _blit_shader.program);

const f32 atlas_w = _render_settings.lights_cookie_atlas_size.x;
const f32 atlas_h = _render_settings.lights_cookie_atlas_size.y;
const f32 min_v = bgfx::getCaps()->originBottomLeft
? atlas_h - f32(packed.y) - f32(height) + 0.5f
: f32(packed.y) + 0.5f
;
const f32 max_v = bgfx::getCaps()->originBottomLeft
? atlas_h - f32(packed.y) - 0.5f
: f32(packed.y) + f32(height) - 0.5f
;
return {
(f32(packed.x) + 0.5f) / atlas_w,
min_v / atlas_h,
(f32(packed.x) + f32(width) - 0.5f) / atlas_w,
max_v / atlas_h
};
}

void Pipeline::reload_shaders(const ShaderResource *old_resource, const ShaderResource *new_resource)
{
CE_UNUSED_2(old_resource, new_resource);
Expand Down
Loading