【光影】使彩色光能穿过小缝隙
前言
使用光影是相对最完美的实现方案,不过需要花点功夫研究别人的
问题的关键
Photon Shaders的彩色光照是体素化的,那么出现光照穿透非完整方块的关键就在于着色器是怎么判断某个方块是完整方块/非完整方块/透明方块等类型
且看源码
#if !defined INCLUDE_LIGHTING_LPV_VOXELIZATION
#define INCLUDE_LIGHTING_LPV_VOXELIZATION
const ivec3 voxel_volume_size = ivec3(VOXEL_VOLUME_SIZE);
#ifdef COLORED_LIGHTS
const float voxelDistance = 32.0;
#endif
vec3 get_voxel_volume_center(vec3 look_direction) {
#if VOXEL_VOLUME_CENTER == VOXEL_VOLUME_CENTER_AHEAD
// Center the voxel volume in front of the player
// Returns the integer offsets towards the center from the scene space origin
// Fraction of the voxel volume size that is behind the player
const float voxelization_fraction_behind_player = 0.15; // blocks
return floor(look_direction * voxel_volume_size * (0.5 - voxelization_fraction_behind_player) * rcp(max_of(abs(look_direction))));
#else
// Voxel volume is centered on the player (origin in scene space)
return vec3(0.0);
#endif
}
vec3 scene_to_voxel_space(vec3 scene_pos) {
vec3 to_center = get_voxel_volume_center(gbufferModelViewInverse[2].xyz);
return scene_pos + fract(cameraPosition) + (0.5 * vec3(voxel_volume_size)) + to_center;
}
vec3 voxel_to_scene_space(vec3 voxel_pos) {
vec3 to_center = get_voxel_volume_center(gbufferModelViewInverse[2].xyz);
return voxel_pos - fract(cameraPosition) - (0.5 * vec3(voxel_volume_size)) - to_center;
}
bool is_inside_voxel_volume(vec3 voxel_pos) {
voxel_pos *= rcp(vec3(voxel_volume_size));
return clamp01(voxel_pos) == voxel_pos;
}
#ifdef PROGRAM_SHADOW
bool is_voxelized(uint block_id, bool vertex_at_grid_corner) {
bool is_terrain = any(equal(ivec4(renderStage), ivec4(MC_RENDER_STAGE_TERRAIN_SOLID, MC_RENDER_STAGE_TERRAIN_TRANSLUCENT, MC_RENDER_STAGE_TERRAIN_CUTOUT, MC_RENDER_STAGE_TERRAIN_CUTOUT_MIPPED)));
bool is_transparent_block =
block_id == 1u || // Water
block_id == 18u || // Transparent metal objects
block_id == 80u; // Miscellaneous transparent
bool is_light_emitting_block = 32u <= block_id && block_id < 64u;
return (vertex_at_grid_corner || is_light_emitting_block) && is_terrain && !is_transparent_block;
}
bvec3 disjunction(bvec3 a, bvec3 b) {
// a || b compiles on Nvidia but apparently not with other vendors
return bvec3(
a.x || b.x,
a.y || b.y,
a.z || b.z
);
}
// Returns true if pos is within `tolerance` of a corner of the unit cube
bool is_corner(vec3 pos, float tolerance) {
return all(disjunction(lessThan(pos, vec3(tolerance)), greaterThan(pos, vec3(1.0 - tolerance))));
}
void update_voxel_map(uint block_id) {
vec3 model_pos = gl_Vertex.xyz + at_midBlock * rcp(64.0);
vec3 view_pos = transform(gl_ModelViewMatrix, model_pos);
vec3 scene_pos = transform(shadowModelViewInverse, view_pos);
vec3 voxel_pos = scene_to_voxel_space(scene_pos);
// Work out whether this vertex is in the lower corner of the block grid
vec3 block_pos = transform(gl_ModelViewMatrix, gl_Vertex.xyz);
block_pos = transform(shadowModelViewInverse, block_pos);
block_pos = fract(block_pos + cameraPosition);
bool vertex_at_grid_corner = is_corner(block_pos, rcp(16.0) - 1e-3);
bool is_voxelized = is_voxelized(block_id, vertex_at_grid_corner);
// Prevent blocks that aren't part of another category in shaders.properties from being treated as air
block_id = max(block_id, 1u);
// Warped and crimson stem emission
uint is_warped_stem = uint(19 <= block_id && block_id < 23);
uint is_crimson_stem = uint(23 <= block_id && block_id < 27);
block_id = block_id * (1u - is_warped_stem) + 46 * is_warped_stem;
block_id = block_id * (1u - is_crimson_stem) + 58 * is_crimson_stem;
// SSS blocks
if (block_id == 5u || // Leaves
block_id == 14u || // Strong SSS
block_id == 15u // Weak SSS
) {
block_id = 79; // light gray tint
}
// Mark transparent light sources
block_id = (vertex_at_grid_corner)
? block_id
: clamp(block_id + 128u, 0u, 255u);
if (is_voxelized && is_inside_voxel_volume(voxel_pos)) {
imageStore(voxel_img, ivec3(voxel_pos), uvec4(block_id, 0u, 0u, 0u));
}
}
#endif
#endif // INCLUDE_LIGHTING_LPV_VOXELIZATION
在开发者所定义的体素空间中,一个体素的大小就是原版一个普通方块的大小
代码中使用 is_corner(block_is, pos) 方法来判断一个方块是否占用了一个体素单位
对于当前方块:
- 如果它没有占用立方体的8个角,可以认为它是一个不完整方块,不作为一个体素加入体素空间,那么光照就可以穿透
- 否则,它被认为是一个完整方块,但还需要
is_voxelized(block_id, vertex_at_grid_corner)进一步判断要不要作为一个体素加入到体素空间中
这就造成了一个很明显的问题:像栅栏这种没有占用立方体8个角的方块,光照可以任意穿透,而像楼梯或半砖这类理应允许光照穿透它们的缝隙的方块却不允许穿透。那么对于littletiles的小方块,可以在一个原版方块大小的空间中任意摆放,但只要有一个小方块占用了立方体其中一个角,都和楼梯/半砖等是同一个下场


左:没有一个角被占用,光照可穿透;右:有一个角被占用,光照穿透不了
解决办法
尝试用半透明方块,如淡灰色玻璃,发现光照能穿透,并且亮度有一定衰减。那么这不是很合适 用于增强灯光在广州塔腰部小缝隙中穿透吗?经过几个小时摸索,上面代码中 block_id 是参考propertiesFiles设计的,其中 block.properties 将各种方块分成了半透明方块和其他类型方块。那么只要把广州塔的柱子方块(quartz_block)改为和light_gray_glass一类就行了
实现效果



左:未修改类型为半透明的效果;中:修改为半透明的效果;右:减少亮度衰减率后的增强效果
现实对比
本文追求的是还原现实中广州塔柱子背光看起来很暗的效果,如果全透就会这样:


浙公网安备 33010602011771号