fix: preserve native sky sprite screen depth
Docs Deploy / Build and Deploy MkDocs (push) Successful in 33s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 33s
This commit is contained in:
7 files changed
+221
-28
No files matched your search
@@ -232,9 +232,10 @@ void main() {
|
|||||||
out_uv = in_uv;
|
out_uv = in_uv;
|
||||||
out_detail_uv = in_detail_uv;
|
out_detail_uv = in_detail_uv;
|
||||||
out_overlay_alpha = in_overlay_alpha;
|
out_overlay_alpha = in_overlay_alpha;
|
||||||
out_fog_factor = native_fog_factor(in_position);
|
|
||||||
int mode = int(round(material.material_mode));
|
int mode = int(round(material.material_mode));
|
||||||
int combiner_mode = mode & 3;
|
int combiner_mode = mode & 3;
|
||||||
|
bool screen_tl = (mode & 8) != 0;
|
||||||
|
out_fog_factor = screen_tl ? 0.0 : native_fog_factor(in_position);
|
||||||
if (combiner_mode == 0) {
|
if (combiner_mode == 0) {
|
||||||
LightingTerms base_terms = native_material_lighting(
|
LightingTerms base_terms = native_material_lighting(
|
||||||
in_position,
|
in_position,
|
||||||
@@ -262,12 +263,14 @@ void main() {
|
|||||||
out_overlay_diffuse = vec3(1.0);
|
out_overlay_diffuse = vec3(1.0);
|
||||||
out_overlay_specular = vec3(0.0);
|
out_overlay_specular = vec3(0.0);
|
||||||
}
|
}
|
||||||
vec4 clip_position = frame.clip_from_world * vec4(in_position, 1.0);
|
vec4 clip_position = screen_tl
|
||||||
|
? vec4(in_position, 1.0)
|
||||||
|
: frame.clip_from_world * vec4(in_position, 1.0);
|
||||||
// Sky far-depth is a Vulkan projection choice for layers authored around
|
// Sky far-depth is a Vulkan projection choice for layers authored around
|
||||||
// the camera. Keep world positions, normals, and UVs intact while placing
|
// the camera. Keep world positions, normals, and UVs intact while placing
|
||||||
// the resulting primitive at the far depth; this does not assert native
|
// the resulting primitive at the far depth; this does not assert native
|
||||||
// D3D depth-bit equivalence.
|
// D3D depth-bit equivalence.
|
||||||
if ((mode & 4) != 0) {
|
if ((mode & 4) != 0 && !screen_tl) {
|
||||||
clip_position.z = clip_position.w;
|
clip_position.z = clip_position.w;
|
||||||
}
|
}
|
||||||
gl_Position = clip_position;
|
gl_Position = clip_position;
|
||||||
|
|||||||
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
use ash::vk;
|
use ash::vk;
|
||||||
use fparkan_render::{LegacyDepthMode, LegacyPipelineState};
|
use fparkan_render::{LegacyDepthMode, LegacyPipelineState};
|
||||||
|
|
||||||
use super::smoke_types::resolve_draw_texture_indices;
|
use super::smoke_types::{resolve_draw_texture_indices, VulkanStaticMaterial};
|
||||||
use super::update_material_texture_descriptor;
|
use super::update_material_texture_descriptor;
|
||||||
use super::{
|
use super::{
|
||||||
create_command_pool, create_frame_sync, create_index_buffer, create_static_mesh_index_buffer,
|
create_command_pool, create_frame_sync, create_index_buffer, create_static_mesh_index_buffer,
|
||||||
@@ -361,18 +361,7 @@ impl VulkanSmokeRenderer {
|
|||||||
create_info
|
create_info
|
||||||
.materials
|
.materials
|
||||||
.iter()
|
.iter()
|
||||||
.map(|material| {
|
.map(VulkanStaticMaterial::encoded_material_mode)
|
||||||
let combiner_mode = if material.lightmap_mode {
|
|
||||||
3.0
|
|
||||||
} else if material.sky_nebula_stars {
|
|
||||||
2.0
|
|
||||||
} else if material.unlit {
|
|
||||||
1.0
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
combiner_mode + if material.sky_far_depth { 4.0 } else { 0.0 }
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
let material_specular = if create_info.materials.is_empty() {
|
let material_specular = if create_info.materials.is_empty() {
|
||||||
|
|||||||
@@ -505,6 +505,33 @@ pub struct VulkanStaticMaterial {
|
|||||||
/// renderer projection choice; it is not a claim about the original D3D
|
/// renderer projection choice; it is not a claim about the original D3D
|
||||||
/// depth-bit encoding.
|
/// depth-bit encoding.
|
||||||
pub sky_far_depth: bool,
|
pub sky_far_depth: bool,
|
||||||
|
/// Push material mode bit 8 and treat positions as Vulkan clip-space
|
||||||
|
/// `XYZRHW` vertices. Their x/y coordinates are converted from viewport
|
||||||
|
/// pixels by the caller and z retains the native `0.999` depth.
|
||||||
|
pub screen_tl: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanStaticMaterial {
|
||||||
|
pub(crate) fn encoded_material_mode(&self) -> f32 {
|
||||||
|
let combiner_mode = if self.screen_tl {
|
||||||
|
1.0
|
||||||
|
} else if self.lightmap_mode {
|
||||||
|
3.0
|
||||||
|
} else if self.sky_nebula_stars {
|
||||||
|
2.0
|
||||||
|
} else if self.unlit {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
combiner_mode
|
||||||
|
+ if self.sky_far_depth && !self.screen_tl {
|
||||||
|
4.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
+ if self.screen_tl { 8.0 } else { 0.0 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One authored RGBA8 mip level accepted by the Vulkan texture upload path.
|
/// One authored RGBA8 mip level accepted by the Vulkan texture upload path.
|
||||||
@@ -1028,6 +1055,7 @@ mod static_mesh_tests {
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: false,
|
sky_far_depth: false,
|
||||||
|
screen_tl: false,
|
||||||
},
|
},
|
||||||
VulkanStaticMaterial {
|
VulkanStaticMaterial {
|
||||||
material_index: 7,
|
material_index: 7,
|
||||||
@@ -1054,9 +1082,19 @@ mod static_mesh_tests {
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: false,
|
sky_far_depth: false,
|
||||||
|
screen_tl: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let mut screen_tl = materials[0].clone();
|
||||||
|
screen_tl.screen_tl = true;
|
||||||
|
screen_tl.sky_far_depth = true;
|
||||||
|
assert_eq!(screen_tl.encoded_material_mode(), 9.0);
|
||||||
|
let mut far_sky = materials[0].clone();
|
||||||
|
far_sky.unlit = true;
|
||||||
|
far_sky.sky_far_depth = true;
|
||||||
|
assert_eq!(far_sky.encoded_material_mode(), 5.0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_draw_texture_indices(&ranges, &materials),
|
resolve_draw_texture_indices(&ranges, &materials),
|
||||||
Ok(vec![1, 0])
|
Ok(vec![1, 0])
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ fn native_specular_reference_uses_squared_power_and_total_overflow_curve() {
|
|||||||
fn shader_computes_radial_fog_per_vertex_and_interpolates_it() {
|
fn shader_computes_radial_fog_per_vertex_and_interpolates_it() {
|
||||||
let vertex = include_str!("../../shaders/triangle.vert");
|
let vertex = include_str!("../../shaders/triangle.vert");
|
||||||
let fragment = include_str!("../../shaders/triangle.frag");
|
let fragment = include_str!("../../shaders/triangle.frag");
|
||||||
assert!(vertex.contains("out_fog_factor = native_fog_factor(in_position);"));
|
assert!(vertex.contains("out_fog_factor = screen_tl ? 0.0 : native_fog_factor(in_position);"));
|
||||||
assert!(fragment.contains("layout(location = 4) in float in_fog_factor;"));
|
assert!(fragment.contains("layout(location = 4) in float in_fog_factor;"));
|
||||||
assert!(!fragment.contains("distance(in_world_position"));
|
assert!(!fragment.contains("distance(in_world_position"));
|
||||||
assert!(!fragment.contains("float fog_factor()"));
|
assert!(!fragment.contains("float fog_factor()"));
|
||||||
|
|||||||
+145
-11
@@ -1261,6 +1261,7 @@ fn append_environment_material(
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: matches!(fallback_row, 0..=2),
|
sky_far_depth: matches!(fallback_row, 0..=2),
|
||||||
|
screen_tl: ENVIRONMENT_SPRITE_ROWS.contains(&fallback_row),
|
||||||
});
|
});
|
||||||
phase_material_indices.push(index);
|
phase_material_indices.push(index);
|
||||||
}
|
}
|
||||||
@@ -1321,6 +1322,7 @@ fn append_environment_gradient_material(
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: true,
|
sky_far_depth: true,
|
||||||
|
screen_tl: false,
|
||||||
});
|
});
|
||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
@@ -1371,6 +1373,7 @@ fn append_shadow_material(materials: &mut Vec<VulkanStaticMaterial>) -> Result<u
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: false,
|
sky_far_depth: false,
|
||||||
|
screen_tl: false,
|
||||||
});
|
});
|
||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
@@ -2648,17 +2651,33 @@ impl EnvironmentGpuScene {
|
|||||||
viewport,
|
viewport,
|
||||||
horizontal_fov(camera, viewport[0] / viewport[1].max(1.0)),
|
horizontal_fov(camera, viewport[0] / viewport[1].max(1.0)),
|
||||||
) {
|
) {
|
||||||
vertices = Some(sprite_quad_vertices_pixels(
|
if ENVIRONMENT_SPRITE_ROWS.contains(&sprite.row) {
|
||||||
camera,
|
if let Some(center_pixels) = projected {
|
||||||
frame.direction,
|
vertices = Some(screen_tl_quad_vertices_pixels(
|
||||||
(camera.far_plane * 0.5).max(camera.near_plane * 2.0),
|
center_pixels,
|
||||||
half_width,
|
half_width,
|
||||||
half_height,
|
half_height,
|
||||||
viewport,
|
viewport,
|
||||||
frame.color,
|
frame.color,
|
||||||
frame.alpha,
|
frame.alpha,
|
||||||
frame.uv,
|
frame.uv,
|
||||||
));
|
));
|
||||||
|
} else {
|
||||||
|
active = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vertices = Some(sprite_quad_vertices_pixels(
|
||||||
|
camera,
|
||||||
|
frame.direction,
|
||||||
|
(camera.far_plane * 0.5).max(camera.near_plane * 2.0),
|
||||||
|
half_width,
|
||||||
|
half_height,
|
||||||
|
viewport,
|
||||||
|
frame.color,
|
||||||
|
frame.alpha,
|
||||||
|
frame.uv,
|
||||||
|
));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
@@ -3083,6 +3102,50 @@ fn sprite_quad_vertices_pixels(
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screen_tl_quad_vertices_pixels(
|
||||||
|
center_pixels: [f32; 2],
|
||||||
|
half_width_pixels: f32,
|
||||||
|
half_height_pixels: f32,
|
||||||
|
viewport: [f32; 2],
|
||||||
|
color: [f32; 3],
|
||||||
|
alpha: f32,
|
||||||
|
uv: [[f32; 2]; 4],
|
||||||
|
) -> [VulkanStaticVertex; 4] {
|
||||||
|
let width = viewport[0].max(1.0);
|
||||||
|
let height = viewport[1].max(1.0);
|
||||||
|
let half_width = half_width_pixels.max(0.0);
|
||||||
|
let half_height = half_height_pixels.max(0.0);
|
||||||
|
let pixel_corners = [
|
||||||
|
[
|
||||||
|
center_pixels[0] - half_width,
|
||||||
|
center_pixels[1] + half_height,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
center_pixels[0] + half_width,
|
||||||
|
center_pixels[1] + half_height,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
center_pixels[0] - half_width,
|
||||||
|
center_pixels[1] - half_height,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
center_pixels[0] + half_width,
|
||||||
|
center_pixels[1] - half_height,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
// Positive-height Vulkan viewports map NDC -Y to top-down screen pixels.
|
||||||
|
let uv = [uv[1], uv[2], uv[0], uv[3]];
|
||||||
|
std::array::from_fn(|index| {
|
||||||
|
let pixel = pixel_corners[index];
|
||||||
|
let position = [
|
||||||
|
pixel[0] * (2.0 / width) - 1.0,
|
||||||
|
pixel[1] * (2.0 / height) - 1.0,
|
||||||
|
0.999,
|
||||||
|
];
|
||||||
|
environment_vertex_with_alpha(position, color, alpha, uv[index])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn precipitation_quad_vertices(
|
fn precipitation_quad_vertices(
|
||||||
kind: PrecipitationKind,
|
kind: PrecipitationKind,
|
||||||
screen: &ScreenBillboard,
|
screen: &ScreenBillboard,
|
||||||
@@ -4305,6 +4368,7 @@ fn static_preview_terrain_base_materials(
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: false,
|
sky_far_depth: false,
|
||||||
|
screen_tl: false,
|
||||||
});
|
});
|
||||||
phase_material_indices.push(usize::from(preview_selector));
|
phase_material_indices.push(usize::from(preview_selector));
|
||||||
}
|
}
|
||||||
@@ -4510,6 +4574,7 @@ fn static_preview_component_materials(
|
|||||||
sky_nebula_stars: false,
|
sky_nebula_stars: false,
|
||||||
lightmap_mode: false,
|
lightmap_mode: false,
|
||||||
sky_far_depth: false,
|
sky_far_depth: false,
|
||||||
|
screen_tl: false,
|
||||||
});
|
});
|
||||||
phase_material_indices.push(usize::from(preview_selector));
|
phase_material_indices.push(usize::from(preview_selector));
|
||||||
}
|
}
|
||||||
@@ -7336,4 +7401,73 @@ mod tests {
|
|||||||
[uv[1], uv[2], uv[0], uv[3]]
|
[uv[1], uv[2], uv[0], uv[3]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_tl_sprite_pixels_use_current_viewport_and_preserve_depth_999() {
|
||||||
|
let uv = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
|
||||||
|
let small = screen_tl_quad_vertices_pixels(
|
||||||
|
[160.0, 120.0],
|
||||||
|
40.0,
|
||||||
|
30.0,
|
||||||
|
[640.0, 480.0],
|
||||||
|
[1.0; 3],
|
||||||
|
1.0,
|
||||||
|
uv,
|
||||||
|
);
|
||||||
|
let resized = screen_tl_quad_vertices_pixels(
|
||||||
|
[320.0, 240.0],
|
||||||
|
80.0,
|
||||||
|
60.0,
|
||||||
|
[1280.0, 960.0],
|
||||||
|
[1.0; 3],
|
||||||
|
1.0,
|
||||||
|
uv,
|
||||||
|
);
|
||||||
|
let wide = screen_tl_quad_vertices_pixels(
|
||||||
|
[640.0, 360.0],
|
||||||
|
100.0,
|
||||||
|
45.0,
|
||||||
|
[1280.0, 720.0],
|
||||||
|
[1.0; 3],
|
||||||
|
1.0,
|
||||||
|
uv,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (small, resized) in small.iter().zip(resized) {
|
||||||
|
for axis in 0..2 {
|
||||||
|
assert!((small.position[axis] - resized.position[axis]).abs() < 1.0e-6);
|
||||||
|
}
|
||||||
|
assert_eq!(small.position[2], 0.999);
|
||||||
|
assert_eq!(resized.position[2], 0.999);
|
||||||
|
}
|
||||||
|
assert_eq!(small[0].uv, [0.0, 1.0]);
|
||||||
|
assert_eq!(small[2].uv, [0.0, 0.0]);
|
||||||
|
for (actual, expected) in [
|
||||||
|
(wide[0].position, [-0.15625, 0.125, 0.999]),
|
||||||
|
(wide[2].position, [-0.15625, -0.125, 0.999]),
|
||||||
|
] {
|
||||||
|
for axis in 0..3 {
|
||||||
|
assert!((actual[axis] - expected[axis]).abs() < 1.0e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_tl_depth_999_is_nearer_than_opaque_geometry_at_320_units() {
|
||||||
|
let camera = FreeFlightCamera {
|
||||||
|
position: [0.0; 3],
|
||||||
|
yaw: 0.0,
|
||||||
|
pitch: 0.0,
|
||||||
|
horizontal_fov: 1.3,
|
||||||
|
near_plane: 0.5,
|
||||||
|
far_plane: 700.0,
|
||||||
|
move_speed: 1.0,
|
||||||
|
};
|
||||||
|
let frame = camera.preview_frame(16.0 / 9.0);
|
||||||
|
let opaque_clip = row_vector_transform([320.0, 0.0, 0.0, 1.0], frame.clip_from_world);
|
||||||
|
let opaque_depth = opaque_clip[2] / opaque_clip[3];
|
||||||
|
|
||||||
|
assert!((opaque_depth - 0.999151_18).abs() < 1.0e-6);
|
||||||
|
assert!(0.999 < opaque_depth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +207,35 @@ packed: [key.colors[10], key.colors[14]],
|
|||||||
`AtmosphereFrame` пока не несёт этот бит обновления, поэтому sampler
|
`AtmosphereFrame` пока не несёт этот бит обновления, поэтому sampler
|
||||||
выдаёт интерполированный цвет при каждом запросе.
|
выдаёт интерполированный цвет при каждом запросе.
|
||||||
|
|
||||||
|
## Экранная геометрия солнца и луны
|
||||||
|
|
||||||
|
`CSun::Render` сначала переводит направление небесного тела в пиксели текущего
|
||||||
|
viewport, затем собирает quad с native XYZRHW-позицией. При положительной высоте
|
||||||
|
viewport Vulkan пиксели переводятся в clip-space так:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
position = [
|
||||||
|
pixel[0] * (2.0 / width) - 1.0,
|
||||||
|
pixel[1] * (2.0 / height) - 1.0,
|
||||||
|
0.999,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
Vertex shader пропускает `clip_from_world` для строк солнца и луны. CPU каждый
|
||||||
|
кадр пересчитывает центр и размер quad по текущему viewport; старые пиксельные
|
||||||
|
координаты не переносятся на новый размер. World-space billboards бликов
|
||||||
|
остаются отдельным путём. Материал экранного quad не освещается, vertex fog
|
||||||
|
отключён, а sky far-depth bit к нему не применяется.
|
||||||
|
|
||||||
|
Native `z = 0.999` не означает, что спрайт гарантированно лежит за любой
|
||||||
|
поверхностью. Vulkan использует `LESS_OR_EQUAL` и не записывает depth для
|
||||||
|
прозрачного ряда. При near `0.5`, far `700` мировая поверхность на расстоянии
|
||||||
|
`320` получает depth
|
||||||
|
`700 / (700 - 0.5) * (1 - 0.5 / 320) ≈ 0.999151`; поэтому экранный `0.999`
|
||||||
|
чуть ближе этой поверхности и проходит depth test перед ней. Это фиксированное
|
||||||
|
native значение сохраняется для совместимости, но само по себе не обещает
|
||||||
|
окклюзию солнца всей геометрией местности.
|
||||||
|
|
||||||
## Что остаётся за владельцем
|
## Что остаётся за владельцем
|
||||||
|
|
||||||
`EnvironmentSystem` не извлекает камеру из renderer, не разрешает материалы
|
`EnvironmentSystem` не извлекает камеру из renderer, не разрешает материалы
|
||||||
|
|||||||
Reference in new issue
Block a user