Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC_GLSL
|
||||
.
|
||||
..
|
||||
|
||||
# For variadic macros
|
||||
../../../blenlib
|
||||
|
||||
../../../gpu
|
||||
../../../gpu/intern
|
||||
../../../gpu/shaders
|
||||
../../../gpu/shaders/common
|
||||
|
||||
# For grease pencil.
|
||||
../../engines/gpencil
|
||||
)
|
||||
|
||||
set(SRC_GLSL_VERT
|
||||
draw_debug_draw_display_vert.glsl
|
||||
|
||||
draw_curves_test.glsl
|
||||
)
|
||||
|
||||
set(SRC_GLSL_FRAG
|
||||
draw_debug_draw_display_frag.glsl
|
||||
)
|
||||
|
||||
set(SRC_GLSL_COMP
|
||||
draw_command_generate_comp.glsl
|
||||
# Failed because of mixed definition of ObjectInfos
|
||||
# draw_resource_finalize_comp.glsl
|
||||
draw_view_finalize_comp.glsl
|
||||
draw_visibility_comp.glsl
|
||||
|
||||
draw_curves_topology_comp.glsl
|
||||
draw_curves_interpolation_comp.glsl
|
||||
draw_curves_length_intercept_comp.glsl
|
||||
|
||||
subdiv_ibo_lines_comp.glsl
|
||||
subdiv_ibo_tris_comp.glsl
|
||||
subdiv_vbo_edge_fac_comp.glsl
|
||||
subdiv_custom_data_interp_comp.glsl
|
||||
subdiv_vbo_sculpt_data_comp.glsl
|
||||
subdiv_vbo_edituv_stretch_angle_comp.glsl
|
||||
subdiv_vbo_edituv_stretch_area_comp.glsl
|
||||
subdiv_vbo_lnor_comp.glsl
|
||||
subdiv_vbo_paint_overlay_flag_comp.glsl
|
||||
subdiv_normals_accumulate_comp.glsl
|
||||
)
|
||||
|
||||
set(SRC_BSL
|
||||
bsl_shader_linting.cc
|
||||
)
|
||||
|
||||
# Compile shaders with shader code.
|
||||
if(WITH_GPU_SHADER_CPP_COMPILATION)
|
||||
# TODO Remove
|
||||
add_definitions(-DUSE_GPU_SHADER_CREATE_INFO)
|
||||
|
||||
compile_sources_as_cpp(draw_cpp_shaders_vert "${SRC_GLSL_VERT}" "GPU_VERTEX_SHADER")
|
||||
compile_sources_as_cpp(draw_cpp_shaders_frag "${SRC_GLSL_FRAG}" "GPU_FRAGMENT_SHADER")
|
||||
compile_sources_as_cpp(draw_cpp_shaders_comp "${SRC_GLSL_COMP}" "GPU_COMPUTE_SHADER")
|
||||
compile_sources_as_cpp(draw_cpp_shaders_bsl "${SRC_BSL}" "")
|
||||
endif()
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Compile shader files as C++ inside one compilation unit to lint syntax and get IDE integration.
|
||||
*/
|
||||
|
||||
#include "draw_model.bsl.hh" /* IWYU pragma: export */
|
||||
#include "draw_view.bsl.hh" /* IWYU pragma: export */
|
||||
|
||||
void main() {}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_shape_lib.glsl"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Axis Aligned Bound Box
|
||||
* \{ */
|
||||
|
||||
struct AABB {
|
||||
float3 min, max;
|
||||
};
|
||||
|
||||
AABB shape_aabb(float3 min, float3 max)
|
||||
{
|
||||
AABB aabb;
|
||||
aabb.min = min;
|
||||
aabb.max = max;
|
||||
return aabb;
|
||||
}
|
||||
|
||||
AABB aabb_init_min_max()
|
||||
{
|
||||
AABB aabb;
|
||||
aabb.min = float3(1.0e30f);
|
||||
aabb.max = float3(-1.0e30f);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
void aabb_merge(AABB &aabb, float3 v)
|
||||
{
|
||||
aabb.min = min(aabb.min, v);
|
||||
aabb.max = max(aabb.max, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is any intersection.
|
||||
*/
|
||||
bool aabb_intersect(AABB a, AABB b)
|
||||
{
|
||||
return all(greaterThanEqual(min(a.max, b.max), max(a.min, b.min)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute intersect intersection volume of \a a and \a b.
|
||||
* Return true if the resulting volume is not empty.
|
||||
*/
|
||||
bool aabb_clip(AABB a, AABB b, AABB &c)
|
||||
{
|
||||
c.min = max(a.min, b.min);
|
||||
c.max = min(a.max, b.max);
|
||||
return all(greaterThanEqual(c.max, c.min));
|
||||
}
|
||||
|
||||
Box aabb_to_box(AABB aabb)
|
||||
{
|
||||
Box box;
|
||||
box.corners[0] = aabb.min;
|
||||
box.corners[1] = float3(aabb.max.x, aabb.min.y, aabb.min.z);
|
||||
box.corners[2] = float3(aabb.max.x, aabb.max.y, aabb.min.z);
|
||||
box.corners[3] = float3(aabb.min.x, aabb.max.y, aabb.min.z);
|
||||
box.corners[4] = float3(aabb.min.x, aabb.min.y, aabb.max.z);
|
||||
box.corners[5] = float3(aabb.max.x, aabb.min.y, aabb.max.z);
|
||||
box.corners[6] = aabb.max;
|
||||
box.corners[7] = float3(aabb.min.x, aabb.max.y, aabb.max.z);
|
||||
return box;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
float linearrgb_to_srgb(float c)
|
||||
{
|
||||
if (c < 0.0031308f) {
|
||||
return (c < 0.0f) ? 0.0f : c * 12.92f;
|
||||
}
|
||||
else {
|
||||
return 1.055f * pow(c, 1.0f / 2.4f) - 0.055f;
|
||||
}
|
||||
}
|
||||
|
||||
float4 texture_read_as_linearrgb(sampler2D tex, bool premultiplied, float2 co)
|
||||
{
|
||||
/* By convention image textures return scene linear colors, but
|
||||
* overlays still assume srgb. */
|
||||
float4 col = texture(tex, co);
|
||||
/* Un-pre-multiply if stored multiplied, since straight alpha is expected by shaders. */
|
||||
if (premultiplied && !(col.a == 0.0f || col.a == 1.0f)) {
|
||||
col.rgb = col.rgb / col.a;
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
float4 texture_read_as_srgb(sampler2D tex, bool premultiplied, float2 co)
|
||||
{
|
||||
float4 col = texture_read_as_linearrgb(tex, premultiplied, co);
|
||||
col.r = linearrgb_to_srgb(col.r);
|
||||
col.g = linearrgb_to_srgb(col.g);
|
||||
col.b = linearrgb_to_srgb(col.b);
|
||||
return col;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Convert DrawPrototype into draw commands.
|
||||
*/
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_command_generate)
|
||||
|
||||
#define atomicAddAndGet(dst, val) (atomicAdd(dst, val) + val)
|
||||
|
||||
/* This is only called by the last thread executed over the group's prototype draws. */
|
||||
void write_draw_call(DrawGroup group, uint group_id)
|
||||
{
|
||||
const bool indexed_draw = group.base_index != -1;
|
||||
|
||||
const uint back_facing_len = group_buf[group_id].back_facing_counter;
|
||||
const uint back_facing_start = group.start * uint(view_len);
|
||||
const uint front_facing_len = group_buf[group_id].front_facing_counter;
|
||||
const uint front_facing_start = (group.start + (group.len - group.front_facing_len)) *
|
||||
uint(view_len);
|
||||
|
||||
/* Back-facing command. */
|
||||
DrawCommand cmd;
|
||||
if (indexed_draw) {
|
||||
DrawCommandIndexed cmd_indexed;
|
||||
cmd_indexed.vertex_len = uint(group.vertex_len);
|
||||
cmd_indexed.instance_len = back_facing_len;
|
||||
cmd_indexed.vertex_first = uint(group.vertex_first);
|
||||
cmd_indexed.base_index = uint(group.base_index);
|
||||
cmd_indexed.instance_first = back_facing_start;
|
||||
cmd.indexed() = cmd_indexed;
|
||||
}
|
||||
else {
|
||||
DrawCommandArray cmd_array;
|
||||
cmd_array.vertex_len = uint(group.vertex_len);
|
||||
cmd_array.instance_len = back_facing_len;
|
||||
cmd_array.vertex_first = uint(group.vertex_first);
|
||||
cmd_array.instance_first = back_facing_start;
|
||||
cmd.array() = cmd_array;
|
||||
}
|
||||
command_buf[group_id * 2 + 0] = cmd;
|
||||
|
||||
/* Front-facing command. */
|
||||
if (indexed_draw) {
|
||||
DrawCommandIndexed cmd_indexed;
|
||||
cmd_indexed.vertex_len = uint(group.vertex_len);
|
||||
cmd_indexed.instance_len = front_facing_len;
|
||||
cmd_indexed.vertex_first = uint(group.vertex_first);
|
||||
cmd_indexed.base_index = uint(group.base_index);
|
||||
cmd_indexed.instance_first = front_facing_start;
|
||||
cmd.indexed() = cmd_indexed;
|
||||
}
|
||||
else {
|
||||
DrawCommandArray cmd_array;
|
||||
cmd_array.vertex_len = uint(group.vertex_len);
|
||||
cmd_array.instance_len = front_facing_len;
|
||||
cmd_array.vertex_first = uint(group.vertex_first);
|
||||
cmd_array.instance_first = front_facing_start;
|
||||
cmd.array() = cmd_array;
|
||||
}
|
||||
command_buf[group_id * 2 + 1] = cmd;
|
||||
|
||||
/* Reset the counters for a next command gen dispatch. Avoids re-sending the whole data just
|
||||
* for this purpose. Only the last thread will execute this so it is thread-safe. */
|
||||
group_buf[group_id].front_facing_counter = 0u;
|
||||
group_buf[group_id].back_facing_counter = 0u;
|
||||
group_buf[group_id].total_counter = 0u;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int proto_id = int(gl_GlobalInvocationID.x);
|
||||
if (proto_id >= prototype_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
DrawPrototype proto = prototype_buf[proto_id];
|
||||
uint group_id = proto.group_id;
|
||||
bool is_inverted = (proto.res_id & 0x80000000u) != 0;
|
||||
uint resource_id = (proto.res_id & 0x7FFFFFFFu);
|
||||
|
||||
/* Visibility test result. */
|
||||
uint visible_instance_len = 0;
|
||||
if (visibility_word_per_draw > 0) {
|
||||
uint visibility_word = resource_id * uint(visibility_word_per_draw);
|
||||
for (int i = 0; i < visibility_word_per_draw; i++, visibility_word++) {
|
||||
/* NOTE: This assumes `proto.instance_len` is 1. */
|
||||
/* TODO: Assert. */
|
||||
visible_instance_len += uint(bitCount(visibility_buf[visibility_word]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((visibility_buf[resource_id / 32u] & (1u << (resource_id % 32u))) != 0) {
|
||||
visible_instance_len = proto.instance_len;
|
||||
}
|
||||
}
|
||||
bool is_visible = visible_instance_len > 0;
|
||||
|
||||
DrawGroup group = group_buf[group_id];
|
||||
|
||||
if (!is_visible) {
|
||||
/* Skip the draw but still count towards the completion. */
|
||||
if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) {
|
||||
write_draw_call(group, group_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint back_facing_len = (group.len - group.front_facing_len) * uint(view_len);
|
||||
uint dst_index = group.start * uint(view_len);
|
||||
if (is_inverted) {
|
||||
uint offset = atomicAdd(group_buf[group_id].back_facing_counter, visible_instance_len);
|
||||
dst_index += offset;
|
||||
if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) {
|
||||
write_draw_call(group, group_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint offset = atomicAdd(group_buf[group_id].front_facing_counter, visible_instance_len);
|
||||
dst_index += back_facing_len + offset;
|
||||
if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) {
|
||||
write_draw_call(group, group_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill resource_id buffer for each instance of this draw. */
|
||||
if (visibility_word_per_draw > 0) {
|
||||
uint visibility_word = resource_id * uint(visibility_word_per_draw);
|
||||
for (int i = 0; i < visibility_word_per_draw; i++, visibility_word++) {
|
||||
uint word = visibility_buf[visibility_word];
|
||||
uint view_index = uint(i) * 32u;
|
||||
while (word != 0u) {
|
||||
if ((word & 1u) != 0u) {
|
||||
if (use_custom_ids) {
|
||||
resource_id_buf[dst_index * 2] = view_index | (resource_id << view_shift);
|
||||
resource_id_buf[dst_index * 2 + 1] = proto.custom_id;
|
||||
}
|
||||
else {
|
||||
resource_id_buf[dst_index] = view_index | (resource_id << view_shift);
|
||||
}
|
||||
dst_index++;
|
||||
}
|
||||
view_index++;
|
||||
word >>= 1u;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint i = dst_index; i < dst_index + visible_instance_len; i++) {
|
||||
if (use_custom_ids) {
|
||||
resource_id_buf[i * 2] = resource_id;
|
||||
resource_id_buf[i * 2 + 1] = proto.custom_id;
|
||||
}
|
||||
else {
|
||||
resource_id_buf[i] = resource_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "draw_attribute_shader_shared.hh"
|
||||
# include "draw_object_infos_infos.hh"
|
||||
#endif
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
# define DRW_HAIR_INFO
|
||||
#endif
|
||||
|
||||
#include "draw_curves_defines.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_topology)
|
||||
LOCAL_GROUP_SIZE(CURVES_PER_THREADGROUP)
|
||||
/* Offsets giving the start and end of the curve. */
|
||||
STORAGE_BUF(0, read, int, evaluated_offsets_buf[])
|
||||
STORAGE_BUF(1, read, uint, curves_cyclic_buf[]) /* Actually bool (1 byte). */
|
||||
STORAGE_BUF(2, write, int, indirection_buf[])
|
||||
PUSH_CONSTANT(int, curves_start)
|
||||
PUSH_CONSTANT(int, curves_count)
|
||||
PUSH_CONSTANT(bool, is_ribbon_topology)
|
||||
PUSH_CONSTANT(bool, use_cyclic)
|
||||
COMPUTE_SOURCE("draw_curves_topology_comp.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_data)
|
||||
LOCAL_GROUP_SIZE(CURVES_PER_THREADGROUP)
|
||||
/* Offsets giving the start and end of the curve. */
|
||||
STORAGE_BUF(EVALUATED_POINT_SLOT, read, int, evaluated_points_by_curve_buf[])
|
||||
STORAGE_BUF(POINTS_BY_CURVES_SLOT, read, int, points_by_curve_buf[])
|
||||
STORAGE_BUF(CURVE_RESOLUTION_SLOT, read, uint, curves_resolution_buf[])
|
||||
STORAGE_BUF(CURVE_TYPE_SLOT, read, uint, curves_type_buf[]) /* Actually int8_t. */
|
||||
STORAGE_BUF(CURVE_CYCLIC_SLOT, read, uint, curves_cyclic_buf[]) /* Actually bool (1 byte). */
|
||||
/* Bezier handles (if needed). */
|
||||
STORAGE_BUF(HANDLES_POS_LEFT_SLOT, read, float, handles_positions_left_buf[])
|
||||
STORAGE_BUF(HANDLES_POS_RIGHT_SLOT, read, float, handles_positions_right_buf[])
|
||||
STORAGE_BUF(BEZIER_OFFSETS_SLOT, read, int, bezier_offsets_buf[])
|
||||
/* Nurbs (alias of other buffers). */
|
||||
// STORAGE_BUF(CURVES_ORDER_SLOT, read, uint, curves_order_buf[]) /* Actually int8_t. */
|
||||
// STORAGE_BUF(BASIS_CACHE_SLOT, read, float, basis_cache_buf[])
|
||||
// STORAGE_BUF(CONTROL_WEIGHTS_SLOT, read, float, control_weights_buf[])
|
||||
// STORAGE_BUF(BASIS_CACHE_OFFSET_SLOT, read, int, basis_cache_offset_buf[])
|
||||
PUSH_CONSTANT(int, curves_start)
|
||||
PUSH_CONSTANT(int, curves_count)
|
||||
PUSH_CONSTANT(bool, use_point_weight)
|
||||
PUSH_CONSTANT(bool, use_cyclic)
|
||||
/** IMPORTANT: For very dumb reasons, on GL the default specialization is compiled and used for
|
||||
* creating the shader interface. If this happens to optimize out some push_constants that are
|
||||
* valid in other specialization, we will never be able to set them. So choose the specialization
|
||||
* that uses all push_constants. */
|
||||
SPECIALIZATION_CONSTANT(int, evaluated_type, 3) /* CURVE_TYPE_NURBS */
|
||||
TYPEDEF_SOURCE("draw_attribute_shader_shared.hh")
|
||||
COMPUTE_SOURCE("draw_curves_interpolation_comp.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_interpolate_position)
|
||||
ADDITIONAL_INFO(draw_curves_data)
|
||||
/* Attributes. */
|
||||
STORAGE_BUF(POINT_POSITIONS_SLOT, read, float, positions_buf[])
|
||||
STORAGE_BUF(POINT_RADII_SLOT, read, float, radii_buf[])
|
||||
/* Outputs. */
|
||||
STORAGE_BUF(EVALUATED_POS_RAD_SLOT, read_write, float4, evaluated_positions_radii_buf[])
|
||||
PUSH_CONSTANT(float4x4, transform)
|
||||
COMPUTE_FUNCTION("evaluate_position_radius")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_interpolate_float4_attribute)
|
||||
ADDITIONAL_INFO(draw_curves_data)
|
||||
STORAGE_BUF(POINT_ATTR_SLOT, read, StoredFloat4, attribute_float4_buf[])
|
||||
STORAGE_BUF(EVALUATED_ATTR_SLOT, read_write, StoredFloat4, evaluated_float4_buf[])
|
||||
COMPUTE_FUNCTION("evaluate_attribute_float4")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_interpolate_float3_attribute)
|
||||
ADDITIONAL_INFO(draw_curves_data)
|
||||
STORAGE_BUF(POINT_ATTR_SLOT, read, StoredFloat3, attribute_float3_buf[])
|
||||
STORAGE_BUF(EVALUATED_ATTR_SLOT, read_write, StoredFloat3, evaluated_float3_buf[])
|
||||
COMPUTE_FUNCTION("evaluate_attribute_float3")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_interpolate_float2_attribute)
|
||||
ADDITIONAL_INFO(draw_curves_data)
|
||||
STORAGE_BUF(POINT_ATTR_SLOT, read, StoredFloat2, attribute_float2_buf[])
|
||||
STORAGE_BUF(EVALUATED_ATTR_SLOT, read_write, StoredFloat2, evaluated_float2_buf[])
|
||||
COMPUTE_FUNCTION("evaluate_attribute_float2")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_interpolate_float_attribute)
|
||||
ADDITIONAL_INFO(draw_curves_data)
|
||||
STORAGE_BUF(POINT_ATTR_SLOT, read, StoredFloat, attribute_float_buf[])
|
||||
STORAGE_BUF(EVALUATED_ATTR_SLOT, read_write, StoredFloat, evaluated_float_buf[])
|
||||
COMPUTE_FUNCTION("evaluate_attribute_float")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_evaluate_length_intercept)
|
||||
LOCAL_GROUP_SIZE(CURVES_PER_THREADGROUP)
|
||||
STORAGE_BUF(EVALUATED_POINT_SLOT, read, int, evaluated_points_by_curve_buf[])
|
||||
STORAGE_BUF(EVALUATED_POS_RAD_SLOT, read, float4, evaluated_positions_radii_buf[])
|
||||
STORAGE_BUF(EVALUATED_TIME_SLOT, read_write, float, evaluated_time_buf[])
|
||||
STORAGE_BUF(CURVES_LENGTH_SLOT, write, float, curves_length_buf[])
|
||||
PUSH_CONSTANT(int, curves_start)
|
||||
PUSH_CONSTANT(int, curves_count)
|
||||
PUSH_CONSTANT(bool, use_cyclic)
|
||||
COMPUTE_FUNCTION("evaluate_length_intercept")
|
||||
COMPUTE_SOURCE("draw_curves_length_intercept_comp.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_test)
|
||||
STORAGE_BUF(0, write, float, result_pos_buf[])
|
||||
STORAGE_BUF(1, write, int4, result_indices_buf[])
|
||||
VERTEX_SOURCE("draw_curves_test.glsl")
|
||||
FRAGMENT_SOURCE("draw_curves_test.glsl")
|
||||
ADDITIONAL_INFO(draw_curves_infos)
|
||||
ADDITIONAL_INFO(draw_curves)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
@@ -0,0 +1,569 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* GPU generated interpolated position and radius. Updated on attribute change.
|
||||
* One thread processes one curve.
|
||||
*
|
||||
* Equivalent of `CurvesGeometry::evaluated_positions()`.
|
||||
*/
|
||||
|
||||
#include "draw_curves_infos.hh"
|
||||
|
||||
#include "gpu_shader_attribute_load_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
#include "gpu_shader_math_safe_lib.glsl"
|
||||
#include "gpu_shader_offset_indices_lib.glsl"
|
||||
|
||||
/* We workaround the lack of function pointers by using different type to overload the attribute
|
||||
* implementation. */
|
||||
struct InterpPosition {
|
||||
/* Position, Radius. */
|
||||
float4 data;
|
||||
};
|
||||
|
||||
/** Input Load. */
|
||||
|
||||
/* Template this function to be able to call it with only no extra argument. */
|
||||
template<typename T> T input_load(int /*point_index*/)
|
||||
{
|
||||
return T(0);
|
||||
}
|
||||
template<> InterpPosition input_load<InterpPosition>(int point_index)
|
||||
{
|
||||
const auto &transform = push_constant_get(draw_curves_interpolate_position, transform);
|
||||
const auto &positions = buffer_get(draw_curves_interpolate_position, positions_buf);
|
||||
InterpPosition interp;
|
||||
interp.data.xyz = gpu_attr_load_float3(positions, int2(3, 0), uint(point_index));
|
||||
interp.data.w = buffer_get(draw_curves_interpolate_position, radii_buf)[point_index];
|
||||
/* Bake object transform for legacy hair particle. */
|
||||
interp.data.xyz = transform_point(transform, interp.data.xyz);
|
||||
return interp;
|
||||
}
|
||||
template<> float4 input_load<float4>(int point_index)
|
||||
{
|
||||
StoredFloat4 data = buffer_get(draw_curves_interpolate_float4_attribute,
|
||||
attribute_float4_buf)[point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
template<> float3 input_load<float3>(int point_index)
|
||||
{
|
||||
StoredFloat3 data = buffer_get(draw_curves_interpolate_float3_attribute,
|
||||
attribute_float3_buf)[point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
template<> float2 input_load<float2>(int point_index)
|
||||
{
|
||||
StoredFloat2 data = buffer_get(draw_curves_interpolate_float2_attribute,
|
||||
attribute_float2_buf)[point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
template<> float input_load<float>(int point_index)
|
||||
{
|
||||
StoredFloat data = buffer_get(draw_curves_interpolate_float_attribute,
|
||||
attribute_float_buf)[point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
|
||||
/** Output Load. */
|
||||
|
||||
/* Template this function to be able to call it with only no extra argument. */
|
||||
template<typename T> T output_load(int /*evaluated_point_index*/)
|
||||
{
|
||||
return T(0);
|
||||
}
|
||||
template<> InterpPosition output_load<InterpPosition>(int evaluated_point_index)
|
||||
{
|
||||
InterpPosition data;
|
||||
data.data = buffer_get(draw_curves_interpolate_position,
|
||||
evaluated_positions_radii_buf)[evaluated_point_index];
|
||||
return data;
|
||||
}
|
||||
template<> float4 output_load<float4>(int evaluated_point_index)
|
||||
{
|
||||
StoredFloat4 data = buffer_get(draw_curves_interpolate_float4_attribute,
|
||||
evaluated_float4_buf)[evaluated_point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
template<> float3 output_load<float3>(int evaluated_point_index)
|
||||
{
|
||||
StoredFloat3 data = buffer_get(draw_curves_interpolate_float3_attribute,
|
||||
evaluated_float3_buf)[evaluated_point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
template<> float2 output_load<float2>(int evaluated_point_index)
|
||||
{
|
||||
StoredFloat2 data = buffer_get(draw_curves_interpolate_float2_attribute,
|
||||
evaluated_float2_buf)[evaluated_point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
template<> float output_load<float>(int evaluated_point_index)
|
||||
{
|
||||
StoredFloat data = buffer_get(draw_curves_interpolate_float_attribute,
|
||||
evaluated_float_buf)[evaluated_point_index];
|
||||
return load_data(data);
|
||||
}
|
||||
|
||||
/** Output Write. */
|
||||
|
||||
void output_write(int evaluated_point_index, InterpPosition interp)
|
||||
{
|
||||
/* Clamp radius to 0 to avoid negative radius due to interpolation. */
|
||||
interp.data.w = max(0.0f, interp.data.w);
|
||||
buffer_get(draw_curves_interpolate_position,
|
||||
evaluated_positions_radii_buf)[evaluated_point_index] = interp.data;
|
||||
}
|
||||
void output_write(int evaluated_point_index, const float4 interp)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_float4_attribute,
|
||||
evaluated_float4_buf)[evaluated_point_index] = as_data(interp);
|
||||
}
|
||||
void output_write(int evaluated_point_index, const float3 interp)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_float3_attribute,
|
||||
evaluated_float3_buf)[evaluated_point_index] = as_data(interp);
|
||||
}
|
||||
void output_write(int evaluated_point_index, const float2 interp)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_float2_attribute,
|
||||
evaluated_float2_buf)[evaluated_point_index] = as_data(interp);
|
||||
}
|
||||
void output_write(int evaluated_point_index, const float interp)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_float_attribute,
|
||||
evaluated_float_buf)[evaluated_point_index] = as_data(interp);
|
||||
}
|
||||
|
||||
/** Output Weighted Add. */
|
||||
|
||||
void output_weighted_add(int evaluated_point_index, float w, const InterpPosition src)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_position,
|
||||
evaluated_positions_radii_buf)[evaluated_point_index] += src.data * w;
|
||||
}
|
||||
|
||||
template<typename InterpType>
|
||||
void output_weighted_add(int evaluated_point_index, float w, const InterpType src)
|
||||
{
|
||||
InterpType dst = output_load<InterpType>(evaluated_point_index);
|
||||
dst += src * w;
|
||||
output_write(evaluated_point_index, dst);
|
||||
}
|
||||
template void output_weighted_add<float4>(int, float, float4);
|
||||
template void output_weighted_add<float3>(int, float, float3);
|
||||
template void output_weighted_add<float2>(int, float, float2);
|
||||
template void output_weighted_add<float>(int, float, float);
|
||||
|
||||
/** Output Mul. */
|
||||
|
||||
template<typename InterpType> void output_mul(int evaluated_point_index, float w)
|
||||
{
|
||||
InterpType dst = output_load<InterpType>(evaluated_point_index);
|
||||
dst *= w;
|
||||
output_write(evaluated_point_index, dst);
|
||||
}
|
||||
template void output_mul<float4>(int, float);
|
||||
template void output_mul<float3>(int, float);
|
||||
template void output_mul<float2>(int, float);
|
||||
template void output_mul<float>(int, float);
|
||||
template<> void output_mul<InterpPosition>(int evaluated_point_index, float w)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_position,
|
||||
evaluated_positions_radii_buf)[evaluated_point_index] *= w;
|
||||
}
|
||||
|
||||
/** Output Set To Zero. */
|
||||
|
||||
template<typename InterpType> void output_set_zero(int evaluated_point_index)
|
||||
{
|
||||
output_write(evaluated_point_index, InterpType(0.0f));
|
||||
}
|
||||
template void output_set_zero<float4>(int);
|
||||
template void output_set_zero<float3>(int);
|
||||
template void output_set_zero<float2>(int);
|
||||
template void output_set_zero<float>(int);
|
||||
template<> void output_set_zero<InterpPosition>(int evaluated_point_index)
|
||||
{
|
||||
buffer_get(draw_curves_interpolate_position,
|
||||
evaluated_positions_radii_buf)[evaluated_point_index] = float4(0.0);
|
||||
}
|
||||
|
||||
/** Mix 4. */
|
||||
|
||||
InterpPosition mix4(
|
||||
InterpPosition v0, InterpPosition v1, InterpPosition v2, InterpPosition v3, float4 w)
|
||||
{
|
||||
v0.data = v0.data * w.x + v1.data * w.y + v2.data * w.z + v3.data * w.w;
|
||||
return v0;
|
||||
}
|
||||
|
||||
template<typename DataT> DataT mix4(DataT v0, DataT v1, DataT v2, DataT v3, float4 w)
|
||||
{
|
||||
v0 = v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w;
|
||||
return v0;
|
||||
}
|
||||
template float4 mix4<float4>(float4, float4, float4, float4, float4);
|
||||
template float3 mix4<float3>(float3, float3, float3, float3, float4);
|
||||
template float2 mix4<float2>(float2, float2, float2, float2, float4);
|
||||
template float mix4<float>(float, float, float, float, float4);
|
||||
|
||||
/** Utilities. */
|
||||
|
||||
bool curve_cyclic_get(int curve_index)
|
||||
{
|
||||
const auto &use_cyclic = push_constant_get(draw_curves_data, use_cyclic);
|
||||
const auto &curves_cyclic_buf = buffer_get(draw_curves_data, curves_cyclic_buf);
|
||||
if (use_cyclic) {
|
||||
return gpu_attr_load_bool(curves_cyclic_buf, curve_index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace catmull_rom {
|
||||
|
||||
float4 calculate_basis(const float parameter)
|
||||
{
|
||||
/* Adapted from Cycles #catmull_rom_basis_eval function. */
|
||||
const float t = parameter;
|
||||
const float s = 1.0f - parameter;
|
||||
return 0.5f * float4(-t * s * s,
|
||||
2.0f + t * t * (3.0f * t - 5.0f),
|
||||
2.0f + s * s * (3.0f * s - 5.0f),
|
||||
-s * t * t);
|
||||
}
|
||||
|
||||
int4 get_points(uint point_id, IndexRange points, const bool cyclic)
|
||||
{
|
||||
int4 point_ids = int(point_id) + int4(-1, +0, +1, +2);
|
||||
if (cyclic) {
|
||||
/* Wrap around. Note the offset by size to avoid modulo with negative values. */
|
||||
point_ids = ((point_ids + points.size()) % points.size());
|
||||
}
|
||||
else {
|
||||
point_ids = clamp(point_ids, int4(0), int4(points.size() - 1));
|
||||
}
|
||||
return points.start() + point_ids;
|
||||
}
|
||||
|
||||
template<typename InterpType>
|
||||
void evaluate_curve(const IndexRange points,
|
||||
const IndexRange evaluated_points,
|
||||
const int curve_index)
|
||||
{
|
||||
const auto &curves_resolution_buf = buffer_get(draw_curves_data, curves_resolution_buf);
|
||||
const uint curve_resolution = curves_resolution_buf[curve_index];
|
||||
const bool is_curve_cyclic = curve_cyclic_get(curve_index);
|
||||
|
||||
const uint evaluated_points_count = uint(evaluated_points.size());
|
||||
for (uint i = 0; i < evaluated_points_count; i++) {
|
||||
const int evaluated_point_id = evaluated_points.start() + int(i);
|
||||
const uint point_id = i / curve_resolution;
|
||||
const float parameter = float(i % curve_resolution) / float(curve_resolution);
|
||||
const float4 weights = calculate_basis(parameter);
|
||||
const int4 point_ids = get_points(point_id, points, is_curve_cyclic);
|
||||
|
||||
InterpType p0 = input_load<InterpType>(point_ids.x);
|
||||
InterpType p1 = input_load<InterpType>(point_ids.y);
|
||||
InterpType p2 = input_load<InterpType>(point_ids.z);
|
||||
InterpType p3 = input_load<InterpType>(point_ids.w);
|
||||
InterpType result = mix4(p0, p1, p2, p3, weights);
|
||||
|
||||
output_write(evaluated_point_id, result);
|
||||
}
|
||||
}
|
||||
|
||||
template void evaluate_curve<InterpPosition>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float2>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float3>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float4>(IndexRange, IndexRange, int);
|
||||
|
||||
} // namespace catmull_rom
|
||||
|
||||
namespace bezier {
|
||||
|
||||
template<typename InterpType> void evaluate_segment(const int2 points, const IndexRange result)
|
||||
{
|
||||
InterpType p0 = input_load<InterpType>(points.x);
|
||||
InterpType p1 = input_load<InterpType>(points.y);
|
||||
|
||||
const float step = 1.0f / float(result.size());
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
output_write(result.start() + i, mix(p0, p1, float(i) * step));
|
||||
}
|
||||
}
|
||||
|
||||
template<> void evaluate_segment<InterpPosition>(const int2 points, const IndexRange result)
|
||||
{
|
||||
const auto &handles_right = buffer_get(draw_curves_interpolate_position,
|
||||
handles_positions_right_buf);
|
||||
const auto &handles_left = buffer_get(draw_curves_interpolate_position,
|
||||
handles_positions_left_buf);
|
||||
|
||||
InterpPosition p0 = input_load<InterpPosition>(points.x);
|
||||
InterpPosition p1 = input_load<InterpPosition>(points.y);
|
||||
|
||||
const float3 point_0 = p0.data.xyz;
|
||||
const float3 point_1 = gpu_attr_load_float3(handles_right, int2(3, 0), uint(points.x));
|
||||
const float3 point_2 = gpu_attr_load_float3(handles_left, int2(3, 0), uint(points.y));
|
||||
const float3 point_3 = p1.data.xyz;
|
||||
|
||||
const float rad_0 = p0.data.w;
|
||||
const float rad_1 = p1.data.w;
|
||||
|
||||
assert(result.size > 0);
|
||||
const float inv_len = 1.0f / float(result.size());
|
||||
const float inv_len_squared = inv_len * inv_len;
|
||||
const float inv_len_cubed = inv_len_squared * inv_len;
|
||||
|
||||
const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
|
||||
const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
|
||||
const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
|
||||
|
||||
float3 q0 = point_0;
|
||||
float3 q1 = rt1 + rt2 + rt3;
|
||||
float3 q2 = 2.0f * rt2 + 6.0f * rt3;
|
||||
float3 q3 = 6.0f * rt3;
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
float rad = mix(rad_0, rad_1, float(i) * inv_len);
|
||||
InterpPosition interp;
|
||||
interp.data = float4(q0, rad);
|
||||
output_write(result.start() + i, interp);
|
||||
q0 += q1;
|
||||
q1 += q2;
|
||||
q2 += q3;
|
||||
}
|
||||
}
|
||||
|
||||
template void evaluate_segment<float>(int2, IndexRange);
|
||||
template void evaluate_segment<float2>(int2, IndexRange);
|
||||
template void evaluate_segment<float3>(int2, IndexRange);
|
||||
template void evaluate_segment<float4>(int2, IndexRange);
|
||||
|
||||
IndexRange per_curve_point_offsets_range(const IndexRange points, const int curve_index)
|
||||
{
|
||||
return {curve_index + points.start(), points.size() + 1};
|
||||
}
|
||||
|
||||
int2 get_points(uint point_id, IndexRange points, const bool cyclic)
|
||||
{
|
||||
int2 point_ids = int(point_id) + int2(+0, +1);
|
||||
if (cyclic) {
|
||||
/* Wrap around. Note the offset by size to avoid modulo with negative values. */
|
||||
point_ids = ((point_ids + points.size()) % points.size());
|
||||
}
|
||||
else {
|
||||
point_ids = clamp(point_ids, int2(0), int2(points.size() - 1));
|
||||
}
|
||||
return points.start() + point_ids;
|
||||
}
|
||||
|
||||
template<typename InterpType>
|
||||
void evaluate_curve(const IndexRange points,
|
||||
const IndexRange evaluated_points,
|
||||
const int curve_index)
|
||||
{
|
||||
const auto &bezier_offsets_buf = buffer_get(draw_curves_data, bezier_offsets_buf);
|
||||
/* Range used for indexing bezier offsets. */
|
||||
const IndexRange offsets = per_curve_point_offsets_range(points, curve_index);
|
||||
const bool is_curve_cyclic = curve_cyclic_get(curve_index);
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
/* Bezier curves can have different number of evaluated segment per curve segment. */
|
||||
const IndexRange segment_range = offset_indices::load_range_from_buffer(bezier_offsets_buf,
|
||||
offsets.start() + i);
|
||||
const IndexRange evaluated_segment_range = evaluated_points.slice(segment_range);
|
||||
const int2 point_ids = get_points(uint(i), points, is_curve_cyclic);
|
||||
|
||||
evaluate_segment<InterpType>(point_ids, evaluated_segment_range);
|
||||
}
|
||||
|
||||
if (is_curve_cyclic) {
|
||||
/* The closing point is not contained inside `bezier_offsets_buf` so we do manual copy. */
|
||||
output_write(evaluated_points.last(), output_load<InterpType>(evaluated_points.first()));
|
||||
}
|
||||
}
|
||||
|
||||
template void evaluate_curve<InterpPosition>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float2>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float3>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float4>(IndexRange, IndexRange, int);
|
||||
|
||||
} // namespace bezier
|
||||
|
||||
template<typename InterpType>
|
||||
void copy_curve_data(const IndexRange points,
|
||||
const IndexRange evaluated_points,
|
||||
const int curve_index)
|
||||
{
|
||||
assert(points.size == evaluated_points.size);
|
||||
const bool is_curve_cyclic = curve_cyclic_get(curve_index);
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
output_write(evaluated_points.start() + i, input_load<InterpType>(points.start() + i));
|
||||
}
|
||||
|
||||
if (is_curve_cyclic) {
|
||||
/* The closing point is not contained inside `bezier_offsets_buf` so we do manual copy. */
|
||||
output_write(evaluated_points.last(), output_load<InterpType>(evaluated_points.first()));
|
||||
}
|
||||
}
|
||||
|
||||
template void copy_curve_data<InterpPosition>(IndexRange, IndexRange, int);
|
||||
template void copy_curve_data<float>(IndexRange, IndexRange, int);
|
||||
template void copy_curve_data<float2>(IndexRange, IndexRange, int);
|
||||
template void copy_curve_data<float3>(IndexRange, IndexRange, int);
|
||||
template void copy_curve_data<float4>(IndexRange, IndexRange, int);
|
||||
|
||||
namespace nurbs {
|
||||
|
||||
template<typename InterpType>
|
||||
void evaluate_curve(const IndexRange points,
|
||||
const IndexRange evaluated_points_padded,
|
||||
const int curve_index)
|
||||
{
|
||||
const auto &use_cyclic = push_constant_get(draw_curves_data, use_cyclic);
|
||||
/* Buffer aliasing to same bind point. We cannot dispatch with different type of curve. */
|
||||
const auto &curves_order_buf = buffer_get(draw_curves_data, curves_resolution_buf);
|
||||
const auto &basis_cache_offset_buf = buffer_get(draw_curves_data, bezier_offsets_buf);
|
||||
|
||||
const int order = int(gpu_attr_load_uchar(curves_order_buf, curve_index));
|
||||
|
||||
const int basis_cache_start = basis_cache_offset_buf[curve_index];
|
||||
const bool invalid = basis_cache_start < 0;
|
||||
|
||||
if (invalid) {
|
||||
copy_curve_data<InterpType>(points, evaluated_points_padded, curve_index);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &use_point_weight = push_constant_get(draw_curves_data, use_point_weight);
|
||||
const bool is_curve_cyclic = curve_cyclic_get(curve_index);
|
||||
|
||||
/* Recover original points range without closing cyclic point. */
|
||||
const IndexRange evaluated_points{evaluated_points_padded.start(),
|
||||
evaluated_points_padded.size() - int(use_cyclic)};
|
||||
|
||||
const int start_indices_range_start = basis_cache_start;
|
||||
const int weights_range_start = basis_cache_start + evaluated_points.size();
|
||||
|
||||
/* Buffer aliasing to same bind point. We cannot dispatch with different type of curve. */
|
||||
const auto &basis_cache_buf = buffer_get(draw_curves_data, handles_positions_left_buf);
|
||||
const auto &control_weights_buf = buffer_get(draw_curves_data, handles_positions_right_buf);
|
||||
|
||||
for (int i = 0; i < evaluated_points.size(); i++) {
|
||||
int evaluated_point_index = evaluated_points.start() + i;
|
||||
/* Equivalent to `attribute_math::DefaultMixer<T> mixer{dst}`. */
|
||||
output_set_zero<InterpType>(evaluated_point_index);
|
||||
float total_weight = 0.0f;
|
||||
|
||||
const IndexRange point_weights{weights_range_start + i * order, order};
|
||||
const int start_index = floatBitsToInt(basis_cache_buf[start_indices_range_start + i]);
|
||||
|
||||
for (int j = 0; j < point_weights.size(); j++) {
|
||||
const int point_index = points.start() + (start_index + j) % points.size();
|
||||
const float point_weight = basis_cache_buf[point_weights.start() + j];
|
||||
const float control_weight = use_point_weight ? control_weights_buf[point_index] : 1.0f;
|
||||
const float weight = point_weight * control_weight;
|
||||
/* Equivalent to `mixer.mix_in()`. */
|
||||
output_weighted_add(evaluated_point_index, weight, input_load<InterpType>(point_index));
|
||||
total_weight += weight;
|
||||
}
|
||||
/* Equivalent to `mixer.finalize()` */
|
||||
output_mul<InterpType>(evaluated_point_index, safe_rcp(total_weight));
|
||||
}
|
||||
|
||||
if (is_curve_cyclic) {
|
||||
/* The closing point is not contained inside the NURBS data structure so we do manual copy. */
|
||||
output_write(evaluated_points_padded.last(),
|
||||
output_load<InterpType>(evaluated_points_padded.first()));
|
||||
}
|
||||
}
|
||||
|
||||
template void evaluate_curve<InterpPosition>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float2>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float3>(IndexRange, IndexRange, int);
|
||||
template void evaluate_curve<float4>(IndexRange, IndexRange, int);
|
||||
|
||||
} // namespace nurbs
|
||||
|
||||
template<typename InterpType> void evaluate_curve()
|
||||
{
|
||||
const auto &use_cyclic = push_constant_get(draw_curves_data, use_cyclic);
|
||||
const auto &curves_count = push_constant_get(draw_curves_data, curves_count);
|
||||
const auto &curves_start = push_constant_get(draw_curves_data, curves_start);
|
||||
const auto &evaluated_type = push_constant_get(draw_curves_data, evaluated_type);
|
||||
const auto &curves_type_buf = buffer_get(draw_curves_data, curves_type_buf);
|
||||
const auto &points_by_curve_buf = buffer_get(draw_curves_data, points_by_curve_buf);
|
||||
const auto &evaluated_points_by_curve_buf = buffer_get(draw_curves_data,
|
||||
evaluated_points_by_curve_buf);
|
||||
|
||||
/* Only for gl_GlobalInvocationID. To be removed. */
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_curves_interpolate_position)
|
||||
|
||||
if (gl_GlobalInvocationID.x >= uint(curves_count)) {
|
||||
return;
|
||||
}
|
||||
int curve_index = int(gl_GlobalInvocationID.x) + curves_start;
|
||||
|
||||
const CurveType curve_type = CurveType(gpu_attr_load_uchar(curves_type_buf, curve_index));
|
||||
if (curve_type != CurveType(evaluated_type)) {
|
||||
return;
|
||||
}
|
||||
IndexRange points = offset_indices::load_range_from_buffer(points_by_curve_buf, curve_index);
|
||||
IndexRange evaluated_points = offset_indices::load_range_from_buffer(
|
||||
evaluated_points_by_curve_buf, curve_index);
|
||||
|
||||
if (use_cyclic) {
|
||||
evaluated_points = IndexRange{evaluated_points.start() + curve_index,
|
||||
evaluated_points.size() + 1};
|
||||
}
|
||||
|
||||
if (CurveType(evaluated_type) == CURVE_TYPE_CATMULL_ROM) {
|
||||
catmull_rom::evaluate_curve<InterpType>(points, evaluated_points, curve_index);
|
||||
}
|
||||
else if (CurveType(evaluated_type) == CURVE_TYPE_BEZIER) {
|
||||
bezier::evaluate_curve<InterpType>(points, evaluated_points, curve_index);
|
||||
}
|
||||
else if (CurveType(evaluated_type) == CURVE_TYPE_NURBS) {
|
||||
nurbs::evaluate_curve<InterpType>(points, evaluated_points, curve_index);
|
||||
}
|
||||
else if (CurveType(evaluated_type) == CURVE_TYPE_POLY) {
|
||||
/* Simple copy. */
|
||||
copy_curve_data<InterpType>(points, evaluated_points, curve_index);
|
||||
}
|
||||
}
|
||||
|
||||
template void evaluate_curve<InterpPosition>();
|
||||
template void evaluate_curve<float>();
|
||||
template void evaluate_curve<float2>();
|
||||
template void evaluate_curve<float3>();
|
||||
template void evaluate_curve<float4>();
|
||||
|
||||
void evaluate_position_radius()
|
||||
{
|
||||
evaluate_curve<InterpPosition>();
|
||||
}
|
||||
|
||||
void evaluate_attribute_float()
|
||||
{
|
||||
evaluate_curve<float>();
|
||||
}
|
||||
|
||||
void evaluate_attribute_float2()
|
||||
{
|
||||
evaluate_curve<float2>();
|
||||
}
|
||||
|
||||
void evaluate_attribute_float3()
|
||||
{
|
||||
evaluate_curve<float3>();
|
||||
}
|
||||
|
||||
void evaluate_attribute_float4()
|
||||
{
|
||||
evaluate_curve<float4>();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* GPU computed length and intercept attribute.
|
||||
* One thread processes one curve.
|
||||
*/
|
||||
|
||||
#include "draw_curves_infos.hh"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_curves_evaluate_length_intercept)
|
||||
|
||||
#include "gpu_shader_attribute_load_lib.glsl"
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_offset_indices_lib.glsl"
|
||||
|
||||
/* Run on the evaluated position and compute the intercept time with the curve and the total curve
|
||||
* length. */
|
||||
void evaluate_length_and_time(const IndexRange evaluated_points, const int curve_index)
|
||||
{
|
||||
const auto &evaluated_positions_radii = buffer_get(draw_curves_evaluate_length_intercept,
|
||||
evaluated_positions_radii_buf);
|
||||
auto &evaluated_time = buffer_get(draw_curves_evaluate_length_intercept, evaluated_time_buf);
|
||||
auto &curves_length = buffer_get(draw_curves_evaluate_length_intercept, curves_length_buf);
|
||||
|
||||
float distance_along_curve = 0.0f;
|
||||
evaluated_time[evaluated_points.first()] = 0.0f;
|
||||
for (int i = 1; i < evaluated_points.size(); i++) {
|
||||
int p = evaluated_points.start() + i;
|
||||
distance_along_curve += distance(evaluated_positions_radii[p].xyz,
|
||||
evaluated_positions_radii[p - 1].xyz);
|
||||
evaluated_time[p] = distance_along_curve;
|
||||
}
|
||||
for (int i = 1; i < evaluated_points.size(); i++) {
|
||||
int p = evaluated_points.start() + i;
|
||||
evaluated_time[p] /= distance_along_curve;
|
||||
}
|
||||
curves_length[curve_index] = distance_along_curve;
|
||||
}
|
||||
|
||||
void evaluate_length_intercept()
|
||||
{
|
||||
if (gl_GlobalInvocationID.x >= uint(curves_count)) {
|
||||
return;
|
||||
}
|
||||
int curve_index = int(gl_GlobalInvocationID.x) + curves_start;
|
||||
|
||||
IndexRange evaluated_points = offset_indices::load_range_from_buffer(
|
||||
evaluated_points_by_curve_buf, curve_index);
|
||||
|
||||
if (use_cyclic) {
|
||||
evaluated_points = IndexRange{evaluated_points.start() + curve_index,
|
||||
evaluated_points.size() + 1};
|
||||
}
|
||||
|
||||
evaluate_length_and_time(evaluated_points, curve_index);
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_object_infos_infos.hh"
|
||||
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_conversion_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
|
||||
/**
|
||||
* Library to create hairs dynamically from control points.
|
||||
* This is less bandwidth intensive than fetching the vertex attributes
|
||||
* but does more ALU work per vertex. This also reduces the amount
|
||||
* of data the CPU has to precompute and transfer for each update.
|
||||
*/
|
||||
|
||||
namespace curves {
|
||||
|
||||
struct Segment {
|
||||
/* Index of this segment. Used to load indirection buffer. */
|
||||
uint id;
|
||||
/* Vertex index inside this segment. */
|
||||
uint v_idx;
|
||||
/* Restart triangle strip if true. Only for cylinder topology. */
|
||||
bool is_end_of_segment;
|
||||
};
|
||||
|
||||
/* Indirection buffer indexing. */
|
||||
Segment segment_get(uint vertex_id)
|
||||
{
|
||||
const auto &drw_curves = buffer_get(draw_curves_infos, drw_curves);
|
||||
|
||||
const bool is_cylinder = drw_curves.half_cylinder_face_count > 1u;
|
||||
Segment segment;
|
||||
segment.id = vertex_id / drw_curves.vertex_per_segment;
|
||||
segment.v_idx = vertex_id % drw_curves.vertex_per_segment;
|
||||
segment.is_end_of_segment = is_cylinder && (segment.v_idx == drw_curves.vertex_per_segment - 1);
|
||||
if (is_cylinder && !segment.is_end_of_segment && (segment.id & 1u) == 0u) {
|
||||
/* The topology is not actually restarted and the winding order changes (because we skip an odd
|
||||
* number of triangles). So we have to manually reverse the winding so that is stays
|
||||
* consistent.
|
||||
*/
|
||||
segment.v_idx ^= 1u;
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
/* Result of interpreting the indirection buffer.
|
||||
* The indirection buffer maps drawn segments back to their curves and curve segments.
|
||||
* This is needed for attribute loading. */
|
||||
struct Indirection {
|
||||
/* Can be equal to INT_MAX with ribbon draw type. */
|
||||
int curve_id;
|
||||
/* Segment ID starting at 0 at curve start. */
|
||||
int curve_segment;
|
||||
/* Restart triangle strip if true. Only for ribbon topology. */
|
||||
bool is_end_of_curve;
|
||||
/* Does these vertices correspond to the last point of a cyclic curve (duplicate of start). */
|
||||
bool is_cyclic_point;
|
||||
};
|
||||
|
||||
Indirection indirection_get(Segment segment)
|
||||
{
|
||||
const auto &curves_indirection_buf = sampler_get(draw_curves, curves_indirection_buf);
|
||||
const auto &drw_curves = buffer_get(draw_curves_infos, drw_curves);
|
||||
|
||||
Indirection ind;
|
||||
ind.is_cyclic_point = false;
|
||||
ind.is_end_of_curve = false;
|
||||
ind.curve_id = 0;
|
||||
|
||||
constexpr int cyclic_endpoint_pivot = INT_MAX / 2;
|
||||
constexpr int end_of_curve = INT_MAX;
|
||||
|
||||
int indirection_value = texelFetch(curves_indirection_buf, int(segment.id)).r;
|
||||
if (indirection_value == end_of_curve) {
|
||||
ind.curve_segment = 0;
|
||||
ind.is_end_of_curve = true;
|
||||
}
|
||||
else if (indirection_value >= 0) {
|
||||
/* This is start of curve. The indirection value is the curve ID. */
|
||||
ind.curve_segment = 0;
|
||||
ind.curve_id = indirection_value;
|
||||
}
|
||||
else if (indirection_value <= -cyclic_endpoint_pivot) {
|
||||
/* This is the last segment of a cyclic curve. The indirection value is the offset to the start
|
||||
* of the curve offset by cyclic_endpoint_pivot. */
|
||||
ind.curve_segment = -indirection_value - cyclic_endpoint_pivot;
|
||||
ind.is_cyclic_point = true;
|
||||
}
|
||||
else {
|
||||
/* This is a normal segment. The indirection value is the offset to the start of the curve. */
|
||||
ind.curve_segment = -indirection_value;
|
||||
}
|
||||
|
||||
if (ind.curve_segment != 0) {
|
||||
ind.curve_id = texelFetch(curves_indirection_buf, int(segment.id) - ind.curve_segment).r;
|
||||
}
|
||||
|
||||
const bool is_cylinder = drw_curves.half_cylinder_face_count > 1u;
|
||||
if (is_cylinder) {
|
||||
bool is_end_of_segment = (segment.v_idx & 1u) != 0u;
|
||||
ind.curve_segment += int(is_end_of_segment);
|
||||
/* Only the end of the segment is to be considered the cyclic point. */
|
||||
if (!is_end_of_segment) {
|
||||
ind.is_cyclic_point = false;
|
||||
}
|
||||
}
|
||||
return ind;
|
||||
}
|
||||
|
||||
int point_id_get(Segment segment, Indirection indirection)
|
||||
{
|
||||
const auto &drw_curves = buffer_get(draw_curves_infos, drw_curves);
|
||||
|
||||
const bool is_cylinder = drw_curves.half_cylinder_face_count > 1u;
|
||||
if (is_cylinder) {
|
||||
return int(segment.id) + indirection.curve_id + int(segment.v_idx & 1u);
|
||||
}
|
||||
return int(segment.id) - indirection.curve_id;
|
||||
}
|
||||
|
||||
float azimuthal_offset_get(Segment segment)
|
||||
{
|
||||
const auto &drw_curves = buffer_get(draw_curves_infos, drw_curves);
|
||||
|
||||
float offset;
|
||||
const bool is_strand = drw_curves.half_cylinder_face_count == 0u;
|
||||
const bool is_cylinder = drw_curves.half_cylinder_face_count > 1u;
|
||||
if (is_strand) {
|
||||
offset = 0.5f;
|
||||
}
|
||||
else if (is_cylinder) {
|
||||
offset = float(segment.v_idx >> 1) / float(drw_curves.half_cylinder_face_count);
|
||||
}
|
||||
else {
|
||||
offset = float(segment.v_idx);
|
||||
}
|
||||
return offset * 2.0f - 1.0f;
|
||||
}
|
||||
|
||||
float4 point_position_and_radius_get(uint point_id)
|
||||
{
|
||||
const auto &curves_pos_rad_buf = buffer_get(draw_curves, curves_pos_rad_buf);
|
||||
|
||||
return texelFetch(curves_pos_rad_buf, int(point_id));
|
||||
}
|
||||
|
||||
float3 point_position_get(uint point_id)
|
||||
{
|
||||
const auto &curves_pos_rad_buf = buffer_get(draw_curves, curves_pos_rad_buf);
|
||||
|
||||
return texelFetch(curves_pos_rad_buf, int(point_id)).rgb;
|
||||
}
|
||||
|
||||
struct Point {
|
||||
/* Position of the evaluated curve point (not the shape / cylinder point). */
|
||||
float3 P;
|
||||
/* Tangent vector going from the root to the tip of the curve. */
|
||||
float3 T;
|
||||
|
||||
float radius;
|
||||
/* Lateral/Azimuthal offset from the center of the curve's width. Range [-1..1]. */
|
||||
float azimuthal_offset;
|
||||
|
||||
int point_id;
|
||||
int curve_id;
|
||||
int curve_segment;
|
||||
};
|
||||
|
||||
/* Return data about the curve point. */
|
||||
Point point_get(uint vertex_id)
|
||||
{
|
||||
Segment segment = segment_get(vertex_id);
|
||||
Indirection indirection = indirection_get(segment);
|
||||
|
||||
Point pt;
|
||||
pt.point_id = point_id_get(segment, indirection);
|
||||
pt.curve_id = indirection.curve_id;
|
||||
pt.curve_segment = indirection.curve_segment;
|
||||
|
||||
float4 pos_rad = point_position_and_radius_get(uint(pt.point_id));
|
||||
|
||||
bool restart_strip = indirection.is_end_of_curve || segment.is_end_of_segment;
|
||||
pt.P = (restart_strip) ? float3(NAN_FLT) : pos_rad.xyz;
|
||||
pt.radius = pos_rad.w;
|
||||
pt.azimuthal_offset = azimuthal_offset_get(segment);
|
||||
|
||||
if (pt.curve_segment == 0) {
|
||||
/* Hair root. */
|
||||
pt.T = point_position_get(uint(pt.point_id + 1)) - pt.P;
|
||||
}
|
||||
else if (indirection.is_cyclic_point) {
|
||||
/* Cyclic end point must match start point. */
|
||||
pt.T = point_position_get(uint(pt.point_id - pt.curve_segment + 1)) - pt.P;
|
||||
}
|
||||
else {
|
||||
pt.T = pt.P - point_position_get(uint(pt.point_id - 1));
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
Point object_to_world(Point pt, float4x4 object_to_world)
|
||||
{
|
||||
pt.P = transform_point(object_to_world, pt.P);
|
||||
pt.T = normalize(transform_direction(object_to_world, pt.T));
|
||||
pt.radius *= length(to_scale(object_to_world)) * M_SQRT1_3;
|
||||
return pt;
|
||||
}
|
||||
|
||||
struct ShapePoint {
|
||||
/* Curve tangent space. */
|
||||
float3 curve_N;
|
||||
float3 curve_T;
|
||||
float3 curve_B;
|
||||
/* Position on the curve shape. */
|
||||
float3 P;
|
||||
/* Shading normal at the position on the curve shape. */
|
||||
float3 N;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the position of the expanded position in world-space.
|
||||
* \arg pt : world space curve point.
|
||||
* \arg V : world space view vector (toward viewer) at `pt.P`.
|
||||
*/
|
||||
ShapePoint shape_point_get(const Point pt, const float3 V)
|
||||
{
|
||||
const bool is_strand = buffer_get(draw_curves_infos, drw_curves).half_cylinder_face_count == 0u;
|
||||
|
||||
ShapePoint shape;
|
||||
/* Shading tangent is inverted because of legacy reason. */
|
||||
/* TODO(fclem): Change user code. */
|
||||
shape.curve_T = -pt.T;
|
||||
shape.curve_B = normalize(cross(pt.T, V));
|
||||
shape.curve_N = cross(shape.curve_B, pt.T);
|
||||
/* Point in curve azimuthal space. */
|
||||
const float2 lP = float2(pt.azimuthal_offset, sin_from_cos(abs(pt.azimuthal_offset)));
|
||||
shape.N = shape.curve_B * lP.x + shape.curve_N * lP.y;
|
||||
shape.P = pt.P + shape.N * (is_strand ? 0.0f : pt.radius);
|
||||
return shape;
|
||||
}
|
||||
|
||||
float get_customdata_float(const int curve_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, curve_id).x;
|
||||
}
|
||||
|
||||
float2 get_customdata_vec2(const int curve_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, curve_id).xy;
|
||||
}
|
||||
|
||||
float3 get_customdata_vec3(const int curve_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, curve_id).xyz;
|
||||
}
|
||||
|
||||
float4 get_customdata_vec4(const int curve_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, curve_id).xyzw;
|
||||
}
|
||||
|
||||
float3 get_curve_root_pos(const int point_id, const int curve_segment)
|
||||
{
|
||||
const auto &curves_pos_rad_buf = buffer_get(draw_curves, curves_pos_rad_buf);
|
||||
|
||||
int curve_start = point_id - curve_segment;
|
||||
return texelFetch(curves_pos_rad_buf, curve_start).xyz;
|
||||
}
|
||||
|
||||
} // namespace curves
|
||||
@@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "draw_curves_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(draw_curves_test)
|
||||
|
||||
#include "draw_curves_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifdef GPU_VERTEX_SHADER
|
||||
curves::Point pt = curves::point_get(uint(gl_VertexID));
|
||||
|
||||
result_pos_buf[gl_VertexID] = pt.P.x;
|
||||
result_indices_buf[gl_VertexID].x = pt.point_id;
|
||||
result_indices_buf[gl_VertexID].y = pt.curve_id;
|
||||
result_indices_buf[gl_VertexID].z = pt.curve_segment;
|
||||
result_indices_buf[gl_VertexID].w = int(pt.azimuthal_offset);
|
||||
|
||||
gl_Position = float4(0);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* GPU generated indirection buffer. Updated on topology change.
|
||||
* One thread processes one curve.
|
||||
*/
|
||||
|
||||
#include "draw_curves_infos.hh"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_curves_topology)
|
||||
|
||||
#include "gpu_shader_attribute_load_lib.glsl"
|
||||
#include "gpu_shader_offset_indices_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
if (gl_GlobalInvocationID.x >= uint(curves_count)) {
|
||||
return;
|
||||
}
|
||||
uint curve_id = gl_GlobalInvocationID.x + uint(curves_start);
|
||||
|
||||
bool is_curve_cyclic = false;
|
||||
if (use_cyclic) {
|
||||
is_curve_cyclic = gpu_attr_load_bool(curves_cyclic_buf, curve_id);
|
||||
}
|
||||
|
||||
IndexRange points = offset_indices::load_range_from_buffer(evaluated_offsets_buf, int(curve_id));
|
||||
int index_start = points.start();
|
||||
int num_segment = points.size();
|
||||
|
||||
if (use_cyclic) {
|
||||
index_start += int(curve_id);
|
||||
num_segment += 1;
|
||||
}
|
||||
|
||||
constexpr int cyclic_endpoint_pivot = INT_MAX / 2;
|
||||
constexpr int end_of_curve = INT_MAX;
|
||||
|
||||
int indirection_index_count = num_segment + (is_ribbon_topology ? 1 : -1);
|
||||
index_start += int(is_ribbon_topology ? curve_id : -curve_id);
|
||||
|
||||
for (int i = 0; i < indirection_index_count; i++) {
|
||||
int value = (i == 0) ? int(curve_id) : -i;
|
||||
|
||||
bool is_restart = false;
|
||||
bool is_cyclic_last_segment = false;
|
||||
|
||||
if (use_cyclic) {
|
||||
if (is_ribbon_topology) {
|
||||
is_cyclic_last_segment = i == indirection_index_count - 2;
|
||||
if (is_curve_cyclic) {
|
||||
is_restart = i == indirection_index_count - 1;
|
||||
}
|
||||
else {
|
||||
is_restart = i >= indirection_index_count - 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
is_cyclic_last_segment = i == indirection_index_count - 1;
|
||||
if (is_curve_cyclic) {
|
||||
is_restart = false;
|
||||
}
|
||||
else {
|
||||
is_restart = i == indirection_index_count - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (is_ribbon_topology) {
|
||||
is_restart = i == indirection_index_count - 1;
|
||||
}
|
||||
else {
|
||||
is_restart = false;
|
||||
}
|
||||
}
|
||||
|
||||
indirection_buf[index_start + i] = is_restart ? end_of_curve :
|
||||
(is_cyclic_last_segment ?
|
||||
value - cyclic_endpoint_pivot :
|
||||
value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Display debug edge list.
|
||||
*/
|
||||
|
||||
#include "draw_debug_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(draw_debug_draw_display)
|
||||
|
||||
/* TODO(fclem): Deduplicate with overlay. */
|
||||
/* edge_start and edge_pos needs to be in the range [0..sizeViewport]. */
|
||||
float4 pack_line_data(float2 frag_co, float2 edge_start, float2 edge_pos)
|
||||
{
|
||||
float2 edge = edge_start - edge_pos;
|
||||
float len = length(edge);
|
||||
if (len > 0.0f) {
|
||||
edge /= len;
|
||||
float2 perp = float2(-edge.y, edge.x);
|
||||
float dist = dot(perp, frag_co - edge_start);
|
||||
/* Add 0.1f to differentiate with cleared pixels. */
|
||||
return float4(perp * 0.5f + 0.5f, dist * 0.25f + 0.5f + 0.1f, 1.0f);
|
||||
}
|
||||
/* Default line if the origin is perfectly aligned with a pixel. */
|
||||
return float4(1.0f, 0.0f, 0.5f + 0.1f, 1.0f);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = final_color;
|
||||
out_line_data = pack_line_data(gl_FragCoord.xy, edge_start, edge_pos);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Display debug edge list.
|
||||
*/
|
||||
|
||||
#include "draw_debug_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(draw_debug_draw_display)
|
||||
|
||||
void main()
|
||||
{
|
||||
int line_id = (gl_VertexID / 2);
|
||||
bool is_provoking_vertex = (gl_VertexID & 1) == 0;
|
||||
/* Skip the first vertex containing header data. */
|
||||
DRWDebugVertPair vert = in_debug_lines_buf[line_id + drw_debug_draw_offset];
|
||||
|
||||
float3 pos = uintBitsToFloat((is_provoking_vertex) ?
|
||||
uint3(vert.pos1_x, vert.pos1_y, vert.pos1_z) :
|
||||
uint3(vert.pos2_x, vert.pos2_y, vert.pos2_z));
|
||||
float4 col = float4((uint4(vert.vert_color) >> uint4(0, 8, 16, 24)) & 0xFFu) / 255.0f;
|
||||
|
||||
/* Lifetime management. */
|
||||
if (is_provoking_vertex && vert.lifetime > 1) {
|
||||
uint vertid = atomicAdd(drw_debug_draw_v_count(out_debug_lines_buf), 2u);
|
||||
if (vertid < DRW_DEBUG_DRAW_VERT_MAX) {
|
||||
uint out_line_id = vertid / 2u;
|
||||
vert.lifetime -= 1;
|
||||
out_debug_lines_buf[out_line_id + drw_debug_draw_offset] = vert;
|
||||
}
|
||||
}
|
||||
|
||||
final_color = col;
|
||||
gl_Position = persmat * float4(pos, 1.0f);
|
||||
|
||||
edge_start = edge_pos = (0.5f * (gl_Position.xy / gl_Position.w) + 0.5f) * size_viewport;
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_debug_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(draw_debug_draw)
|
||||
|
||||
/**
|
||||
* Debugging drawing library
|
||||
*
|
||||
* Quick way to draw debug geometry. All input should be in world space and
|
||||
* will be rendered in the default view. No additional setup required.
|
||||
*/
|
||||
|
||||
#ifdef DRW_DEBUG_DRAW
|
||||
|
||||
/** Global switch option. */
|
||||
bool drw_debug_draw_enable = true;
|
||||
# define drw_debug_default_color float4(1.0f, 0.0f, 0.0f, 1.0f)
|
||||
# define drw_debug_default_lifetime 1
|
||||
# define drw_debug_persistent_lifetime (~0u)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internals
|
||||
* \{ */
|
||||
|
||||
uint drw_debug_start_draw(uint v_needed)
|
||||
{
|
||||
uint vertid = atomicAdd(drw_debug_draw_v_count(drw_debug_lines_buf), v_needed);
|
||||
return vertid;
|
||||
}
|
||||
|
||||
void drw_debug_line(uint &vertid, float3 v1, float3 v2, uint v_color, uint lifetime)
|
||||
{
|
||||
uint out_line_id = vertid / 2u;
|
||||
drw_debug_lines_buf[out_line_id + drw_debug_draw_offset] = debug_line_make(floatBitsToUint(v1.x),
|
||||
floatBitsToUint(v1.y),
|
||||
floatBitsToUint(v1.z),
|
||||
floatBitsToUint(v2.x),
|
||||
floatBitsToUint(v2.y),
|
||||
floatBitsToUint(v2.z),
|
||||
v_color,
|
||||
lifetime);
|
||||
vertid += 2;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name API
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Draw a line.
|
||||
*/
|
||||
void drw_debug_line(float3 v1, float3 v2, float4 v_color, uint lifetime)
|
||||
{
|
||||
if (!drw_debug_draw_enable) {
|
||||
return;
|
||||
}
|
||||
const uint v_needed = 2;
|
||||
uint vertid = drw_debug_start_draw(v_needed);
|
||||
if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
|
||||
drw_debug_line(vertid, v1, v2, debug_color_pack(v_color), lifetime);
|
||||
}
|
||||
}
|
||||
void drw_debug_line(float3 v1, float3 v2, float4 v_color)
|
||||
{
|
||||
drw_debug_line(v1, v2, v_color, drw_debug_default_lifetime);
|
||||
}
|
||||
void drw_debug_line(float3 v1, float3 v2)
|
||||
{
|
||||
drw_debug_line(v1, v2, drw_debug_default_color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a quad contour.
|
||||
*/
|
||||
void drw_debug_quad(float3 v1, float3 v2, float3 v3, float3 v4, float4 v_color, uint lifetime)
|
||||
{
|
||||
if (!drw_debug_draw_enable) {
|
||||
return;
|
||||
}
|
||||
const uint v_needed = 8;
|
||||
uint vertid = drw_debug_start_draw(v_needed);
|
||||
if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
|
||||
uint pcolor = debug_color_pack(v_color);
|
||||
drw_debug_line(vertid, v1, v2, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v2, v3, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v3, v4, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v4, v1, pcolor, lifetime);
|
||||
}
|
||||
}
|
||||
void drw_debug_quad(float3 v1, float3 v2, float3 v3, float3 v4, float4 v_color)
|
||||
{
|
||||
drw_debug_quad(v1, v2, v3, v4, v_color, drw_debug_default_lifetime);
|
||||
}
|
||||
void drw_debug_quad(float3 v1, float3 v2, float3 v3, float3 v4)
|
||||
{
|
||||
drw_debug_quad(v1, v2, v3, v4, drw_debug_default_color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a point as octahedron wireframe.
|
||||
*/
|
||||
void drw_debug_point(float3 p, float radius, float4 v_color, uint lifetime)
|
||||
{
|
||||
if (!drw_debug_draw_enable) {
|
||||
return;
|
||||
}
|
||||
float3 c = float3(radius, -radius, 0);
|
||||
float3 v1 = p + c.xzz;
|
||||
float3 v2 = p + c.zxz;
|
||||
float3 v3 = p + c.yzz;
|
||||
float3 v4 = p + c.zyz;
|
||||
float3 v5 = p + c.zzx;
|
||||
float3 v6 = p + c.zzy;
|
||||
|
||||
const uint v_needed = 12 * 2;
|
||||
uint vertid = drw_debug_start_draw(v_needed);
|
||||
if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
|
||||
uint pcolor = debug_color_pack(v_color);
|
||||
drw_debug_line(vertid, v1, v2, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v2, v3, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v3, v4, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v4, v1, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v1, v5, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v2, v5, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v3, v5, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v4, v5, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v1, v6, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v2, v6, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v3, v6, pcolor, lifetime);
|
||||
drw_debug_line(vertid, v4, v6, pcolor, lifetime);
|
||||
}
|
||||
}
|
||||
void drw_debug_point(float3 p, float radius, float4 v_color)
|
||||
{
|
||||
drw_debug_point(p, radius, v_color, drw_debug_default_lifetime);
|
||||
}
|
||||
void drw_debug_point(float3 p, float radius)
|
||||
{
|
||||
drw_debug_point(p, radius, drw_debug_default_color);
|
||||
}
|
||||
void drw_debug_point(float3 p)
|
||||
{
|
||||
drw_debug_point(p, 0.01f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a sphere wireframe as 3 axes circle.
|
||||
*/
|
||||
void drw_debug_sphere(float3 p, float radius, float4 v_color, uint lifetime)
|
||||
{
|
||||
if (!drw_debug_draw_enable) {
|
||||
return;
|
||||
}
|
||||
const int circle_resolution = 16;
|
||||
const uint v_needed = circle_resolution * 2 * 3;
|
||||
uint vertid = drw_debug_start_draw(v_needed);
|
||||
if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
|
||||
uint pcolor = debug_color_pack(v_color);
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
for (int edge = 0; edge < circle_resolution; edge++) {
|
||||
float angle1 = (2.0f * 3.141592f) * float(edge + 0) / float(circle_resolution);
|
||||
float3 p1 = float3(cos(angle1), sin(angle1), 0.0f) * radius;
|
||||
p1 = float3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]);
|
||||
|
||||
float angle2 = (2.0f * 3.141592f) * float(edge + 1) / float(circle_resolution);
|
||||
float3 p2 = float3(cos(angle2), sin(angle2), 0.0f) * radius;
|
||||
p2 = float3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]);
|
||||
|
||||
drw_debug_line(vertid, p + p1, p + p2, pcolor, lifetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void drw_debug_sphere(float3 p, float radius, float4 v_color)
|
||||
{
|
||||
drw_debug_sphere(p, radius, v_color, drw_debug_default_lifetime);
|
||||
}
|
||||
void drw_debug_sphere(float3 p, float radius)
|
||||
{
|
||||
drw_debug_sphere(p, radius, drw_debug_default_color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a matrix transformation as 3 colored axes.
|
||||
*/
|
||||
void drw_debug_matrix(float4x4 mat, uint lifetime)
|
||||
{
|
||||
float4 p[4] = float4_array(
|
||||
float4(0, 0, 0, 1), float4(1, 0, 0, 1), float4(0, 1, 0, 1), float4(0, 0, 1, 1));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
p[i] = mat * p[i];
|
||||
p[i].xyz /= p[i].w;
|
||||
}
|
||||
drw_debug_line(p[0].xyz, p[0].xyz, float4(1, 0, 0, 1), lifetime);
|
||||
drw_debug_line(p[0].xyz, p[1].xyz, float4(0, 1, 0, 1), lifetime);
|
||||
drw_debug_line(p[0].xyz, p[2].xyz, float4(0, 0, 1, 1), lifetime);
|
||||
}
|
||||
void drw_debug_matrix(float4x4 mat)
|
||||
{
|
||||
drw_debug_matrix(mat, drw_debug_default_lifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a matrix as a 2 units length bounding box, centered on origin.
|
||||
*/
|
||||
void drw_debug_matrix_as_bbox(float4x4 mat, float4 v_color, uint lifetime)
|
||||
{
|
||||
float4 p[8] = float4_array(float4(-1, -1, -1, 1),
|
||||
float4(1, -1, -1, 1),
|
||||
float4(1, 1, -1, 1),
|
||||
float4(-1, 1, -1, 1),
|
||||
float4(-1, -1, 1, 1),
|
||||
float4(1, -1, 1, 1),
|
||||
float4(1, 1, 1, 1),
|
||||
float4(-1, 1, 1, 1));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
p[i] = mat * p[i];
|
||||
p[i].xyz /= p[i].w;
|
||||
}
|
||||
drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, v_color, lifetime);
|
||||
drw_debug_line(p[0].xyz, p[4].xyz, v_color, lifetime);
|
||||
drw_debug_line(p[1].xyz, p[5].xyz, v_color, lifetime);
|
||||
drw_debug_line(p[2].xyz, p[6].xyz, v_color, lifetime);
|
||||
drw_debug_line(p[3].xyz, p[7].xyz, v_color, lifetime);
|
||||
drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, v_color, lifetime);
|
||||
}
|
||||
void drw_debug_matrix_as_bbox(float4x4 mat, float4 v_color)
|
||||
{
|
||||
drw_debug_matrix_as_bbox(mat, v_color, drw_debug_default_lifetime);
|
||||
}
|
||||
void drw_debug_matrix_as_bbox(float4x4 mat)
|
||||
{
|
||||
drw_debug_matrix_as_bbox(mat, drw_debug_default_color);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "draw_shader_shared.hh"
|
||||
#endif
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
# define DRW_DEBUG_DRAW
|
||||
#endif
|
||||
|
||||
#include "draw_defines.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug draw shapes
|
||||
*
|
||||
* Allows to draw lines and points just like the DRW_debug module functions.
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_debug_draw)
|
||||
DEFINE("DRW_DEBUG_DRAW")
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
STORAGE_BUF(DRW_DEBUG_DRAW_SLOT, read_write, DRWDebugVertPair, drw_debug_lines_buf[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface)
|
||||
NO_PERSPECTIVE(float2, edge_pos)
|
||||
FLAT(float2, edge_start)
|
||||
FLAT(float4, final_color)
|
||||
GPU_SHADER_INTERFACE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_debug_draw_display)
|
||||
DO_STATIC_COMPILATION()
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
STORAGE_BUF(DRW_DEBUG_DRAW_SLOT, read, DRWDebugVertPair, in_debug_lines_buf[])
|
||||
STORAGE_BUF(DRW_DEBUG_DRAW_FEEDBACK_SLOT, read_write, DRWDebugVertPair, out_debug_lines_buf[])
|
||||
VERTEX_OUT(draw_debug_draw_display_iface)
|
||||
FRAGMENT_OUT(0, float4, out_color)
|
||||
FRAGMENT_OUT(1, float4, out_line_data)
|
||||
PUSH_CONSTANT(float4x4, persmat)
|
||||
PUSH_CONSTANT(float2, size_viewport)
|
||||
VERTEX_SOURCE("draw_debug_draw_display_vert.glsl")
|
||||
FRAGMENT_SOURCE("draw_debug_draw_display_frag.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Debug drawing of shapes.
|
||||
*/
|
||||
|
||||
#include "draw_debug_draw_lib.glsl"
|
||||
#include "draw_shape_lib.glsl"
|
||||
|
||||
void drw_debug(Box shape, float4 color)
|
||||
{
|
||||
drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
|
||||
drw_debug_line(shape.corners[0], shape.corners[4], color);
|
||||
drw_debug_line(shape.corners[1], shape.corners[5], color);
|
||||
drw_debug_line(shape.corners[2], shape.corners[6], color);
|
||||
drw_debug_line(shape.corners[3], shape.corners[7], color);
|
||||
drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
|
||||
}
|
||||
void drw_debug(Box shape)
|
||||
{
|
||||
drw_debug(shape, drw_debug_default_color);
|
||||
}
|
||||
|
||||
void drw_debug(Frustum shape, float4 color)
|
||||
{
|
||||
drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
|
||||
drw_debug_line(shape.corners[0], shape.corners[4], color);
|
||||
drw_debug_line(shape.corners[1], shape.corners[5], color);
|
||||
drw_debug_line(shape.corners[2], shape.corners[6], color);
|
||||
drw_debug_line(shape.corners[3], shape.corners[7], color);
|
||||
drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
|
||||
}
|
||||
void drw_debug(Frustum shape)
|
||||
{
|
||||
drw_debug(shape, drw_debug_default_color);
|
||||
}
|
||||
|
||||
void drw_debug(Pyramid shape, float4 color)
|
||||
{
|
||||
drw_debug_line(shape.corners[0], shape.corners[1], color);
|
||||
drw_debug_line(shape.corners[0], shape.corners[2], color);
|
||||
drw_debug_line(shape.corners[0], shape.corners[3], color);
|
||||
drw_debug_line(shape.corners[0], shape.corners[4], color);
|
||||
drw_debug_quad(shape.corners[1], shape.corners[2], shape.corners[3], shape.corners[4], color);
|
||||
}
|
||||
void drw_debug(Pyramid shape)
|
||||
{
|
||||
drw_debug(shape, drw_debug_default_color);
|
||||
}
|
||||
|
||||
void drw_debug(Sphere shape, float4 color)
|
||||
{
|
||||
drw_debug_sphere(shape.center, shape.radius, color);
|
||||
}
|
||||
void drw_debug(Sphere shape)
|
||||
{
|
||||
drw_debug(shape, drw_debug_default_color);
|
||||
}
|
||||
@@ -0,0 +1,864 @@
|
||||
/* SPDX-FileCopyrightText: 2014-2015 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2017-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* ---------------------------------------------------------------------------------
|
||||
* File: es3-kepler\FXAA/FXAA3_11.h
|
||||
* SDK Version: v3.00
|
||||
* Email: <gameworks@nvidia.com>
|
||||
* Site: http://developer.nvidia.com/
|
||||
* --------------------------------------------------------------------------------- */
|
||||
|
||||
/* BLENDER MODIFICATIONS:
|
||||
*
|
||||
* - (#B1#) Compute luma on the fly using BT. 709 luma function
|
||||
* - (#B2#) main function instead of #include, due to lack of
|
||||
* ARB_shading_language_include in 3.3
|
||||
* - (#B3#) version and extension directives
|
||||
* - removed "FXAA Console" algorithm support and shader parameters
|
||||
* - removed HLSL support shims
|
||||
* - (#B4#) change luma sampling to compute, not use A channel
|
||||
* (this also removes GATHER4_ALPHA support)
|
||||
* - removed all the console shaders (only remaining algorithm is "FXAA PC
|
||||
* Quality")
|
||||
*
|
||||
* Note that this file doesn't follow the coding style guidelines.
|
||||
*/
|
||||
|
||||
/*============================================================================
|
||||
FXAA QUALITY - TUNING KNOBS
|
||||
------------------------------------------------------------------------------
|
||||
NOTE: the other tuning knobs are now in the shader function inputs!
|
||||
============================================================================*/
|
||||
#ifndef FXAA_QUALITY__PRESET
|
||||
/*
|
||||
* Choose the quality preset.
|
||||
* This needs to be compiled into the shader as it effects code.
|
||||
* Best option to include multiple presets is to
|
||||
* in each shader define the preset, then include this file.
|
||||
*
|
||||
* OPTIONS
|
||||
* -----------------------------------------------------------------------
|
||||
* 10 to 15 - default medium dither (10=fastest, 15=highest quality)
|
||||
* 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality)
|
||||
* 39 - no dither, very expensive
|
||||
*
|
||||
* NOTES
|
||||
* -----------------------------------------------------------------------
|
||||
* 12 = slightly faster than FXAA 3.9f and higher edge quality (default)
|
||||
* 13 = about same speed as FXAA 3.9f and better than 12
|
||||
* 23 = closest to FXAA 3.9f visually and performance wise
|
||||
* _ = the lowest digit is directly related to performance
|
||||
* _ = the highest digit is directly related to style
|
||||
*/
|
||||
# define FXAA_QUALITY__PRESET 12
|
||||
#endif
|
||||
|
||||
/*============================================================================
|
||||
|
||||
FXAA QUALITY - PRESETS
|
||||
|
||||
============================================================================*/
|
||||
|
||||
/*============================================================================
|
||||
FXAA QUALITY - MEDIUM DITHER PRESETS
|
||||
============================================================================*/
|
||||
#if (FXAA_QUALITY__PRESET == 10)
|
||||
# define FXAA_QUALITY__PS 3
|
||||
# define FXAA_QUALITY__P0 1.5f
|
||||
# define FXAA_QUALITY__P1 3.0f
|
||||
# define FXAA_QUALITY__P2 12.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 11)
|
||||
# define FXAA_QUALITY__PS 4
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 3.0f
|
||||
# define FXAA_QUALITY__P3 12.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 12)
|
||||
# define FXAA_QUALITY__PS 5
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 4.0f
|
||||
# define FXAA_QUALITY__P4 12.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 13)
|
||||
# define FXAA_QUALITY__PS 6
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 4.0f
|
||||
# define FXAA_QUALITY__P5 12.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 14)
|
||||
# define FXAA_QUALITY__PS 7
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 4.0f
|
||||
# define FXAA_QUALITY__P6 12.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 15)
|
||||
# define FXAA_QUALITY__PS 8
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 2.0f
|
||||
# define FXAA_QUALITY__P6 4.0f
|
||||
# define FXAA_QUALITY__P7 12.0f
|
||||
#endif
|
||||
|
||||
/*============================================================================
|
||||
FXAA QUALITY - LOW DITHER PRESETS
|
||||
============================================================================*/
|
||||
#if (FXAA_QUALITY__PRESET == 20)
|
||||
# define FXAA_QUALITY__PS 3
|
||||
# define FXAA_QUALITY__P0 1.5f
|
||||
# define FXAA_QUALITY__P1 2.0f
|
||||
# define FXAA_QUALITY__P2 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 21)
|
||||
# define FXAA_QUALITY__PS 4
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 22)
|
||||
# define FXAA_QUALITY__PS 5
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 23)
|
||||
# define FXAA_QUALITY__PS 6
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 24)
|
||||
# define FXAA_QUALITY__PS 7
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 3.0f
|
||||
# define FXAA_QUALITY__P6 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 25)
|
||||
# define FXAA_QUALITY__PS 8
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 2.0f
|
||||
# define FXAA_QUALITY__P6 4.0f
|
||||
# define FXAA_QUALITY__P7 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 26)
|
||||
# define FXAA_QUALITY__PS 9
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 2.0f
|
||||
# define FXAA_QUALITY__P6 2.0f
|
||||
# define FXAA_QUALITY__P7 4.0f
|
||||
# define FXAA_QUALITY__P8 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 27)
|
||||
# define FXAA_QUALITY__PS 10
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 2.0f
|
||||
# define FXAA_QUALITY__P6 2.0f
|
||||
# define FXAA_QUALITY__P7 2.0f
|
||||
# define FXAA_QUALITY__P8 4.0f
|
||||
# define FXAA_QUALITY__P9 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 28)
|
||||
# define FXAA_QUALITY__PS 11
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 2.0f
|
||||
# define FXAA_QUALITY__P6 2.0f
|
||||
# define FXAA_QUALITY__P7 2.0f
|
||||
# define FXAA_QUALITY__P8 2.0f
|
||||
# define FXAA_QUALITY__P9 4.0f
|
||||
# define FXAA_QUALITY__P10 8.0f
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PRESET == 29)
|
||||
# define FXAA_QUALITY__PS 12
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.5f
|
||||
# define FXAA_QUALITY__P2 2.0f
|
||||
# define FXAA_QUALITY__P3 2.0f
|
||||
# define FXAA_QUALITY__P4 2.0f
|
||||
# define FXAA_QUALITY__P5 2.0f
|
||||
# define FXAA_QUALITY__P6 2.0f
|
||||
# define FXAA_QUALITY__P7 2.0f
|
||||
# define FXAA_QUALITY__P8 2.0f
|
||||
# define FXAA_QUALITY__P9 2.0f
|
||||
# define FXAA_QUALITY__P10 4.0f
|
||||
# define FXAA_QUALITY__P11 8.0f
|
||||
#endif
|
||||
|
||||
/*============================================================================
|
||||
FXAA QUALITY - EXTREME QUALITY
|
||||
============================================================================*/
|
||||
#if (FXAA_QUALITY__PRESET == 39)
|
||||
# define FXAA_QUALITY__PS 12
|
||||
# define FXAA_QUALITY__P0 1.0f
|
||||
# define FXAA_QUALITY__P1 1.0f
|
||||
# define FXAA_QUALITY__P2 1.0f
|
||||
# define FXAA_QUALITY__P3 1.0f
|
||||
# define FXAA_QUALITY__P4 1.0f
|
||||
# define FXAA_QUALITY__P5 1.5f
|
||||
# define FXAA_QUALITY__P6 2.0f
|
||||
# define FXAA_QUALITY__P7 2.0f
|
||||
# define FXAA_QUALITY__P8 2.0f
|
||||
# define FXAA_QUALITY__P9 2.0f
|
||||
# define FXAA_QUALITY__P10 4.0f
|
||||
# define FXAA_QUALITY__P11 8.0f
|
||||
#endif
|
||||
|
||||
#define FxaaSat(x) clamp(x, 0.0f, 1.0f)
|
||||
|
||||
#ifdef FXAA_ALPHA
|
||||
|
||||
# define FxaaTexTop(t, p) textureLod(t, p, 0.0f).aaaa
|
||||
# define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0f, o).aaaa
|
||||
# define FxaaLuma(rgba) rgba.a
|
||||
|
||||
#else
|
||||
|
||||
# define FxaaTexTop(t, p) textureLod(t, p, 0.0f)
|
||||
# define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0f, o)
|
||||
|
||||
/* (#B1#) */
|
||||
float FxaaLuma(float4 rgba)
|
||||
{
|
||||
/* NOTE: sqrt because the sampled colors are in a linear color-space!
|
||||
* this approximates a perceptual conversion, which is good enough for the
|
||||
* algorithm */
|
||||
return sqrt(dot(rgba.rgb, float3(0.2126f, 0.7152f, 0.0722f)));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*============================================================================
|
||||
|
||||
FXAA3 QUALITY - PC
|
||||
|
||||
============================================================================*/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float4 FxaaPixelShader(
|
||||
/*
|
||||
* Use no perspective interpolation here (turn off perspective interpolation).
|
||||
* {xy} = center of pixel */
|
||||
float2 pos,
|
||||
/*
|
||||
* Input color texture.
|
||||
* {rgb_} = color in linear or perceptual color space */
|
||||
sampler2D tex,
|
||||
/*
|
||||
* Only used on FXAA Quality.
|
||||
* This must be from a constant/uniform.
|
||||
* {x_} = 1.0/screenWidthInPixels
|
||||
* {_y} = 1.0/screenHeightInPixels */
|
||||
float2 fxaaQualityRcpFrame,
|
||||
/*
|
||||
* Only used on FXAA Quality.
|
||||
* This used to be the FXAA_QUALITY__SUBPIX define.
|
||||
* It is here now to allow easier tuning.
|
||||
* Choose the amount of sub-pixel aliasing removal.
|
||||
* This can effect sharpness.
|
||||
* 1.00 - upper limit (softer)
|
||||
* 0.75 - default amount of filtering
|
||||
* 0.50 - lower limit (sharper, less sub-pixel aliasing removal)
|
||||
* 0.25 - almost off
|
||||
* 0.00 - completely off */
|
||||
float fxaaQualitySubpix,
|
||||
/*
|
||||
* Only used on FXAA Quality.
|
||||
* This used to be the FXAA_QUALITY__EDGE_THRESHOLD define.
|
||||
* It is here now to allow easier tuning.
|
||||
* The minimum amount of local contrast required to apply algorithm.
|
||||
* 0.333 - too little (faster)
|
||||
* 0.250 - low quality
|
||||
* 0.166 - default
|
||||
* 0.125 - high quality
|
||||
* 0.063 - overkill (slower) */
|
||||
float fxaaQualityEdgeThreshold,
|
||||
/*
|
||||
* Only used on FXAA Quality.
|
||||
* This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define.
|
||||
* It is here now to allow easier tuning.
|
||||
* Trims the algorithm from processing darks.
|
||||
* 0.0833 - upper limit (default, the start of visible unfiltered edges)
|
||||
* 0.0625 - high quality (faster)
|
||||
* 0.0312 - visible limit (slower) */
|
||||
float fxaaQualityEdgeThresholdMin)
|
||||
{
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float2 posM;
|
||||
posM.x = pos.x;
|
||||
posM.y = pos.y;
|
||||
float4 rgbyM = FxaaTexTop(tex, posM);
|
||||
float lumaM = FxaaLuma(rgbyM); /* (#B4#) */
|
||||
float lumaS = FxaaLuma(FxaaTexOff(tex, posM, int2(0, 1), fxaaQualityRcpFrame.xy));
|
||||
float lumaE = FxaaLuma(FxaaTexOff(tex, posM, int2(1, 0), fxaaQualityRcpFrame.xy));
|
||||
float lumaN = FxaaLuma(FxaaTexOff(tex, posM, int2(0, -1), fxaaQualityRcpFrame.xy));
|
||||
float lumaW = FxaaLuma(FxaaTexOff(tex, posM, int2(-1, 0), fxaaQualityRcpFrame.xy));
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float maxSM = max(lumaS, lumaM);
|
||||
float minSM = min(lumaS, lumaM);
|
||||
float maxESM = max(lumaE, maxSM);
|
||||
float minESM = min(lumaE, minSM);
|
||||
float maxWN = max(lumaN, lumaW);
|
||||
float minWN = min(lumaN, lumaW);
|
||||
float rangeMax = max(maxWN, maxESM);
|
||||
float rangeMin = min(minWN, minESM);
|
||||
float rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;
|
||||
float range = rangeMax - rangeMin;
|
||||
float rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);
|
||||
bool earlyExit = range < rangeMaxClamped;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
if (earlyExit) {
|
||||
return rgbyM;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float lumaNW = FxaaLuma(FxaaTexOff(tex, posM, int2(-1, -1), fxaaQualityRcpFrame.xy));
|
||||
float lumaSE = FxaaLuma(FxaaTexOff(tex, posM, int2(1, 1), fxaaQualityRcpFrame.xy));
|
||||
float lumaNE = FxaaLuma(FxaaTexOff(tex, posM, int2(1, -1), fxaaQualityRcpFrame.xy));
|
||||
float lumaSW = FxaaLuma(FxaaTexOff(tex, posM, int2(-1, 1), fxaaQualityRcpFrame.xy));
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float lumaNS = lumaN + lumaS;
|
||||
float lumaWE = lumaW + lumaE;
|
||||
float subpixRcpRange = 1.0f / range;
|
||||
float subpixNSWE = lumaNS + lumaWE;
|
||||
float edgeHorz1 = (-2.0f * lumaM) + lumaNS;
|
||||
float edgeVert1 = (-2.0f * lumaM) + lumaWE;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float lumaNESE = lumaNE + lumaSE;
|
||||
float lumaNWNE = lumaNW + lumaNE;
|
||||
float edgeHorz2 = (-2.0f * lumaE) + lumaNESE;
|
||||
float edgeVert2 = (-2.0f * lumaN) + lumaNWNE;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float lumaNWSW = lumaNW + lumaSW;
|
||||
float lumaSWSE = lumaSW + lumaSE;
|
||||
float edgeHorz4 = (abs(edgeHorz1) * 2.0f) + abs(edgeHorz2);
|
||||
float edgeVert4 = (abs(edgeVert1) * 2.0f) + abs(edgeVert2);
|
||||
float edgeHorz3 = (-2.0f * lumaW) + lumaNWSW;
|
||||
float edgeVert3 = (-2.0f * lumaS) + lumaSWSE;
|
||||
float edgeHorz = abs(edgeHorz3) + edgeHorz4;
|
||||
float edgeVert = abs(edgeVert3) + edgeVert4;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float subpixNWSWNESE = lumaNWSW + lumaNESE;
|
||||
float lengthSign = fxaaQualityRcpFrame.x;
|
||||
bool horzSpan = edgeHorz >= edgeVert;
|
||||
float subpixA = subpixNSWE * 2.0f + subpixNWSWNESE;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
if (!horzSpan) {
|
||||
lumaN = lumaW;
|
||||
}
|
||||
if (!horzSpan) {
|
||||
lumaS = lumaE;
|
||||
}
|
||||
if (horzSpan) {
|
||||
lengthSign = fxaaQualityRcpFrame.y;
|
||||
}
|
||||
float subpixB = (subpixA * (1.0f / 12.0f)) - lumaM;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float gradientN = lumaN - lumaM;
|
||||
float gradientS = lumaS - lumaM;
|
||||
float lumaNN = lumaN + lumaM;
|
||||
float lumaSS = lumaS + lumaM;
|
||||
bool pairN = abs(gradientN) >= abs(gradientS);
|
||||
float gradient = max(abs(gradientN), abs(gradientS));
|
||||
if (pairN) {
|
||||
lengthSign = -lengthSign;
|
||||
}
|
||||
float subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float2 posB;
|
||||
posB.x = posM.x;
|
||||
posB.y = posM.y;
|
||||
float2 offNP;
|
||||
offNP.x = (!horzSpan) ? 0.0f : fxaaQualityRcpFrame.x;
|
||||
offNP.y = (horzSpan) ? 0.0f : fxaaQualityRcpFrame.y;
|
||||
if (!horzSpan) {
|
||||
posB.x += lengthSign * 0.5f;
|
||||
}
|
||||
if (horzSpan) {
|
||||
posB.y += lengthSign * 0.5f;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float2 posN;
|
||||
posN.x = posB.x - offNP.x * FXAA_QUALITY__P0;
|
||||
posN.y = posB.y - offNP.y * FXAA_QUALITY__P0;
|
||||
float2 posP;
|
||||
posP.x = posB.x + offNP.x * FXAA_QUALITY__P0;
|
||||
posP.y = posB.y + offNP.y * FXAA_QUALITY__P0;
|
||||
float subpixD = ((-2.0f) * subpixC) + 3.0f;
|
||||
float lumaEndN = FxaaLuma(FxaaTexTop(tex, posN));
|
||||
float subpixE = subpixC * subpixC;
|
||||
float lumaEndP = FxaaLuma(FxaaTexTop(tex, posP));
|
||||
/*--------------------------------------------------------------------------*/
|
||||
if (!pairN) {
|
||||
lumaNN = lumaSS;
|
||||
}
|
||||
float gradientScaled = gradient * 1.0f / 4.0f;
|
||||
float lumaMM = lumaM - lumaNN * 0.5f;
|
||||
float subpixF = subpixD * subpixE;
|
||||
bool lumaMLTZero = lumaMM < 0.0f;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
lumaEndN -= lumaNN * 0.5f;
|
||||
lumaEndP -= lumaNN * 0.5f;
|
||||
bool doneN = abs(lumaEndN) >= gradientScaled;
|
||||
bool doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P1;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P1;
|
||||
}
|
||||
bool doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P1;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P1;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P2;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P2;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P2;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P2;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (FXAA_QUALITY__PS > 3)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P3;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P3;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P3;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P3;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 4)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P4;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P4;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P4;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P4;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 5)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P5;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P5;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P5;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P5;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 6)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P6;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P6;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P6;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P6;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 7)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P7;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P7;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P7;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P7;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 8)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P8;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P8;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P8;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P8;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 9)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P9;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P9;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P9;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P9;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 10)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P10;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P10;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P10;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P10;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 11)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P11;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P11;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P11;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P11;
|
||||
}
|
||||
/*-----------------------------------------------------------------------*/
|
||||
# if (FXAA_QUALITY__PS > 12)
|
||||
if (doneNP) {
|
||||
if (!doneN) {
|
||||
lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
|
||||
}
|
||||
if (!doneN) {
|
||||
lumaEndN = lumaEndN - lumaNN * 0.5f;
|
||||
}
|
||||
if (!doneP) {
|
||||
lumaEndP = lumaEndP - lumaNN * 0.5f;
|
||||
}
|
||||
doneN = abs(lumaEndN) >= gradientScaled;
|
||||
doneP = abs(lumaEndP) >= gradientScaled;
|
||||
if (!doneN) {
|
||||
posN.x -= offNP.x * FXAA_QUALITY__P12;
|
||||
}
|
||||
if (!doneN) {
|
||||
posN.y -= offNP.y * FXAA_QUALITY__P12;
|
||||
}
|
||||
doneNP = (!doneN) || (!doneP);
|
||||
if (!doneP) {
|
||||
posP.x += offNP.x * FXAA_QUALITY__P12;
|
||||
}
|
||||
if (!doneP) {
|
||||
posP.y += offNP.y * FXAA_QUALITY__P12;
|
||||
}
|
||||
/*-----------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*-------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
# endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
#endif
|
||||
/*--------------------------------------------------------------------------*/
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float dstN = posM.x - posN.x;
|
||||
float dstP = posP.x - posM.x;
|
||||
if (!horzSpan) {
|
||||
dstN = posM.y - posN.y;
|
||||
}
|
||||
if (!horzSpan) {
|
||||
dstP = posP.y - posM.y;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
bool goodSpanN = (lumaEndN < 0.0f) != lumaMLTZero;
|
||||
float spanLength = (dstP + dstN);
|
||||
bool goodSpanP = (lumaEndP < 0.0f) != lumaMLTZero;
|
||||
float spanLengthRcp = 1.0f / spanLength;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
bool directionN = dstN < dstP;
|
||||
float dst = min(dstN, dstP);
|
||||
bool goodSpan = directionN ? goodSpanN : goodSpanP;
|
||||
float subpixG = subpixF * subpixF;
|
||||
float pixelOffset = (dst * (-spanLengthRcp)) + 0.5f;
|
||||
float subpixH = subpixG * fxaaQualitySubpix;
|
||||
/*--------------------------------------------------------------------------*/
|
||||
float pixelOffsetGood = goodSpan ? pixelOffset : 0.0f;
|
||||
float pixelOffsetSubpix = max(pixelOffsetGood, subpixH);
|
||||
if (!horzSpan) {
|
||||
posM.x += pixelOffsetSubpix * lengthSign;
|
||||
}
|
||||
if (horzSpan) {
|
||||
posM.y += pixelOffsetSubpix * lengthSign;
|
||||
}
|
||||
return FxaaTexTop(tex, posM);
|
||||
}
|
||||
/*==========================================================================*/
|
||||
@@ -0,0 +1,802 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_object_infos_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(draw_gpencil)
|
||||
|
||||
#include "draw_model_lib.glsl"
|
||||
#include "draw_object_infos_lib.glsl"
|
||||
#include "draw_view_lib.glsl"
|
||||
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
#include "gpu_shader_math_vector_safe_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
#ifndef DRW_GPENCIL_INFO
|
||||
# error Missing additional info draw_gpencil
|
||||
#endif
|
||||
|
||||
#define MITER_LIMIT_TYPE_BEVEL -1.0f
|
||||
#define MITER_LIMIT_TYPE_ROUND -2.0f
|
||||
|
||||
#ifdef GPU_FRAGMENT_SHADER
|
||||
float gpencil_stroke_hardess_mask(float dist, float hardfac)
|
||||
{
|
||||
dist = clamp(1.0f - dist, 0.0f, 1.0f);
|
||||
if (hardfac > 0.999f) {
|
||||
return step(1e-8f, dist);
|
||||
}
|
||||
else {
|
||||
/* Modulate the falloff profile */
|
||||
float hardness = 1.0f - hardfac;
|
||||
dist = pow(dist, mix(0.0f, 10.0f, hardness));
|
||||
return smoothstep(0.0f, 1.0f, dist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Calculate the mask for the pixel in the main segment (1) by using the distance factor to
|
||||
* the center line.
|
||||
*
|
||||
* *====================*
|
||||
* \ /
|
||||
* p1------1>------p2
|
||||
* / \ / \
|
||||
* / *============* \
|
||||
* 0 1
|
||||
* / \
|
||||
* / \
|
||||
* p0 p3
|
||||
*
|
||||
* Segments: 0 (p0->p1)
|
||||
* 1 (p1->p2)
|
||||
* 2 (p2->p3)
|
||||
*
|
||||
* Each point can have a different corner type, stored as p1: miter_limit.x, p2: miter_limit.y
|
||||
*
|
||||
*/
|
||||
float gpencil_stroke_segment_mask(
|
||||
float2 p1, float2 p2, float2 p0, float2 p3, float thickness, float hardfac, float2 miter_limit)
|
||||
{
|
||||
bool both_round = miter_limit.x == MITER_LIMIT_TYPE_ROUND &&
|
||||
miter_limit.y == MITER_LIMIT_TYPE_ROUND;
|
||||
|
||||
bool is_start = distance_squared(p0, p1) < 1e-6;
|
||||
bool is_end = distance_squared(p2, p3) < 1e-6;
|
||||
bool both_ends = is_start && is_end;
|
||||
|
||||
float radius = thickness * 0.5f;
|
||||
float2 pos1 = gl_FragCoord.xy - p1;
|
||||
float2 line1 = p2 - p1;
|
||||
float2 tan1 = orthogonal(line1);
|
||||
float len_sq1 = length_squared(line1);
|
||||
|
||||
/* Calculate the factor along the main segment. */
|
||||
float t1 = dot(pos1, line1) / len_sq1;
|
||||
|
||||
/* The distance factor squared to the main segment. This is clamped and will lead to round
|
||||
* corners. */
|
||||
float dist = length_squared(pos1 - saturate(t1) * line1);
|
||||
|
||||
if (both_round || both_ends) {
|
||||
dist = sqrt(dist) / radius;
|
||||
return gpencil_stroke_hardess_mask(dist, hardfac);
|
||||
}
|
||||
|
||||
float2 line0 = p1 - p0;
|
||||
float2 tan0 = orthogonal(line0);
|
||||
float len_sq0 = length_squared(line0);
|
||||
|
||||
float2 pos2 = gl_FragCoord.xy - p2;
|
||||
float2 line2 = p3 - p2;
|
||||
float2 tan2 = orthogonal(line2);
|
||||
float len_sq2 = length_squared(line2);
|
||||
|
||||
/* Calculate the non-normalized factor along the other segments. */
|
||||
float t0 = dot(pos1, line0);
|
||||
float t2 = dot(pos2, line2);
|
||||
|
||||
/* Normalize all segment directions. */
|
||||
float2 tan_norm0 = tan0 / sqrt(len_sq0);
|
||||
float2 tan_norm1 = tan1 / sqrt(len_sq1);
|
||||
float2 tan_norm2 = tan2 / sqrt(len_sq2);
|
||||
|
||||
/* Get the squared distance to the main segment. */
|
||||
float dist_sq_1 = length_squared(pos1 - t1 * line1);
|
||||
|
||||
/* Check if the pixel is within the corner region between segments 1 and 0. */
|
||||
if (t1 <= 0.0f && t0 >= 0.0f && !is_start && miter_limit.x != MITER_LIMIT_TYPE_ROUND) {
|
||||
if (miter_limit.x == MITER_LIMIT_TYPE_BEVEL) {
|
||||
/* Bevel by cutting with a the half angle line. */
|
||||
float2 bevel_tan = orthogonal(tan_norm0 - tan_norm1);
|
||||
|
||||
dist = dot(pos1, bevel_tan) / dot(tan_norm1, bevel_tan);
|
||||
dist *= dist;
|
||||
}
|
||||
else {
|
||||
/* Continue the main line to get a sharp corner. */
|
||||
dist = dist_sq_1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the pixel is within the corner region between segments 1 and 2. */
|
||||
if (t1 >= 1.0f && t2 <= 0.0f && !is_end && miter_limit.y != MITER_LIMIT_TYPE_ROUND) {
|
||||
if (miter_limit.y == MITER_LIMIT_TYPE_BEVEL) {
|
||||
/* Bevel by cutting with a the half angle line. */
|
||||
float2 bevel_tan = orthogonal(tan_norm2 - tan_norm1);
|
||||
|
||||
dist = dot(pos2, bevel_tan) / dot(tan_norm1, bevel_tan);
|
||||
dist *= dist;
|
||||
}
|
||||
else {
|
||||
/* Continue the main line to get a sharp corner. */
|
||||
dist = dist_sq_1;
|
||||
}
|
||||
}
|
||||
|
||||
dist = sqrt(dist) / radius;
|
||||
return gpencil_stroke_hardess_mask(dist, hardfac);
|
||||
}
|
||||
|
||||
float gpencil_stroke_mask(float2 p1,
|
||||
float2 p2,
|
||||
float2 p0,
|
||||
float2 p3,
|
||||
float2 uv,
|
||||
uint mat_flag,
|
||||
float thickness,
|
||||
float hardfac,
|
||||
float2 miter_limit)
|
||||
{
|
||||
if (flag_test(mat_flag, GP_STROKE_ALIGNMENT)) {
|
||||
/* Dot or Squares. */
|
||||
uv = uv * 2.0 - 1.0;
|
||||
if (flag_test(mat_flag, GP_STROKE_DOTS)) {
|
||||
return gpencil_stroke_hardess_mask(length(uv), hardfac);
|
||||
}
|
||||
else {
|
||||
uv = abs(uv);
|
||||
return gpencil_stroke_hardess_mask(max(uv.x, uv.y), hardfac);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Line mask */
|
||||
return gpencil_stroke_segment_mask(p1, p2, p0, p3, thickness, hardfac, miter_limit);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct PointData {
|
||||
bool cyclical;
|
||||
int mat, stroke_id, point_id, packed_data;
|
||||
};
|
||||
|
||||
PointData decode_ma(int4 ma)
|
||||
{
|
||||
PointData data;
|
||||
|
||||
data.mat = ma.x;
|
||||
data.stroke_id = ma.y;
|
||||
/* Take the absolute because the sign is for cyclical. */
|
||||
data.point_id = abs(ma.z);
|
||||
/* Aspect, UV Rotation and Hardness. */
|
||||
data.packed_data = ma.w;
|
||||
/* Cyclical is stored in the sign of the point index. */
|
||||
data.cyclical = ma.z < 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
float4 ndc_and_radius_to_screen_space(float4 ndc, float radius, float2 viewport_res)
|
||||
{
|
||||
return float4(((ndc.xy / ndc.w) * 0.5f + 0.5f) * viewport_res, ndc.w, radius / ndc.w);
|
||||
}
|
||||
|
||||
float4 screen_space_to_ndc(float4 ss, float2 viewport_res)
|
||||
{
|
||||
return float4((ss.xy / viewport_res - 0.5f) * 2.0f * ss.z, 0.0f, ss.z);
|
||||
}
|
||||
|
||||
float screen_space_to_radius(float4 ss)
|
||||
{
|
||||
return ss.w * ss.z;
|
||||
}
|
||||
|
||||
float2 gpencil_decode_aspect(int packed_data)
|
||||
{
|
||||
float asp = float(uint(packed_data) & 0x1FFu) * (1.0f / 255.0f);
|
||||
return (asp > 1.0f) ? float2(1.0f, (asp - 1.0f)) : float2(asp, 1.0f);
|
||||
}
|
||||
|
||||
float gpencil_decode_uvrot(int packed_data)
|
||||
{
|
||||
uint udata = uint(packed_data);
|
||||
float uvrot = 1e-8f + float((udata & 0x1FE00u) >> 9u) * (1.0f / 255.0f);
|
||||
return ((udata & 0x20000u) != 0u) ? -uvrot : uvrot;
|
||||
}
|
||||
|
||||
float gpencil_decode_hardness(int packed_data)
|
||||
{
|
||||
return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0f / 255.0f);
|
||||
}
|
||||
|
||||
float gpencil_decode_miter_limit(int packed_data)
|
||||
{
|
||||
uint miter_data = (uint(packed_data) & 0xFC000000u) >> 26u;
|
||||
if (miter_data == GP_CORNER_TYPE_ROUND_BITS) {
|
||||
return MITER_LIMIT_TYPE_ROUND;
|
||||
}
|
||||
else if (miter_data == GP_CORNER_TYPE_BEVEL_BITS) {
|
||||
return MITER_LIMIT_TYPE_BEVEL;
|
||||
}
|
||||
float miter_angle = float(miter_data) * (M_PI / GP_CORNER_TYPE_MITER_NUMBER);
|
||||
return cos(miter_angle);
|
||||
}
|
||||
|
||||
float2 gpencil_project_to_screenspace(float4 v, float4 viewport_res)
|
||||
{
|
||||
return ((v.xy / v.w) * 0.5f + 0.5f) * viewport_res.xy;
|
||||
}
|
||||
|
||||
float gpencil_stroke_thickness_modulate(float thickness, float4 viewport_res)
|
||||
{
|
||||
/* Modify stroke thickness by object scale. */
|
||||
thickness = length(to_float3x3(drw_modelmat()) * float3(thickness * M_SQRT1_3));
|
||||
|
||||
/* World space point size. */
|
||||
thickness *= drw_view().winmat[1][1] * viewport_res.y;
|
||||
|
||||
return thickness;
|
||||
}
|
||||
|
||||
#ifdef GPU_VERTEX_SHADER
|
||||
|
||||
int gpencil_stroke_point_id()
|
||||
{
|
||||
return (gl_VertexID & ~GP_IS_STROKE_VERTEX_BIT) >> GP_VERTEX_ID_SHIFT;
|
||||
}
|
||||
|
||||
bool gpencil_is_stroke_vertex()
|
||||
{
|
||||
return flag_test(gl_VertexID, GP_IS_STROKE_VERTEX_BIT);
|
||||
}
|
||||
|
||||
float4 discard_ndc()
|
||||
{
|
||||
/* We set the vertex at the camera origin to generate 0 fragments. */
|
||||
return float4(0.0f, 0.0f, -3e36f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a shape that fully covers the segment.
|
||||
*
|
||||
* Will generate either a trapezoid or a rectangle depending on which has smaller area.
|
||||
*/
|
||||
float4 dot_segment(float2 xy, float4 ss1, float4 ss2, bool is_squares, float4 viewport_res)
|
||||
{
|
||||
float l = 0.0f;
|
||||
float2 local_x = safe_normalize_and_get_length(ss2.xy - ss1.xy, l);
|
||||
float2 local_y = orthogonal(local_x);
|
||||
|
||||
float r1 = ss1.w;
|
||||
float r2 = ss2.w;
|
||||
|
||||
/* Scale each circle up by the diagonal of the square. */
|
||||
if (is_squares) {
|
||||
r1 *= M_SQRT2;
|
||||
r2 *= M_SQRT2;
|
||||
}
|
||||
|
||||
float x = xy.x;
|
||||
float y = xy.y;
|
||||
|
||||
float max_r = max(r1, r2);
|
||||
float a = r2 - r1;
|
||||
float cos_theta = -a / l;
|
||||
float sin_theta = sqrt(1.0f - cos_theta * cos_theta);
|
||||
float tan_half_theta = (1.0f - cos_theta) / sin_theta;
|
||||
|
||||
/* Discard segments that are behind the camera. */
|
||||
if (ss1.z < 0.0f || ss2.z < 0.0f) {
|
||||
return discard_ndc();
|
||||
}
|
||||
|
||||
float2 local = float2(0.0f, 0.0f);
|
||||
|
||||
/* Check if one circle is inside the other. */
|
||||
if (abs(cos_theta) > 1.0f) {
|
||||
if (r1 > r2) {
|
||||
local = xy * r1;
|
||||
}
|
||||
else {
|
||||
local = xy * r2 + float2(l, 0.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Calculate the area of both shapes divided by the width */
|
||||
float area_trapezoid = 0.5f * (r1 / tan_half_theta + r2 * tan_half_theta);
|
||||
float area_rectangle = max_r;
|
||||
|
||||
/* Determent if it is better to use a trapezoid or a rectangle. */
|
||||
if (area_trapezoid < area_rectangle) {
|
||||
if (x == -1.0f) {
|
||||
local.x += -r1;
|
||||
local.y += y * r1 / tan_half_theta;
|
||||
}
|
||||
else {
|
||||
local.x += l + r2;
|
||||
local.y += y * r2 * tan_half_theta;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x == -1.0f) {
|
||||
local.x -= r1;
|
||||
local.y += y * max_r;
|
||||
}
|
||||
else {
|
||||
local.x += l + r2;
|
||||
local.y += y * max_r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float2 ssp = ss1.xy + local.x * local_x + local.y * local_y;
|
||||
|
||||
float z_depth = mix(ss1.z, ss2.z, 0.5f);
|
||||
return screen_space_to_ndc(float4(ssp, z_depth, 0.0f), viewport_res.xy);
|
||||
}
|
||||
|
||||
float2 get_rotation(float4 viewport_res,
|
||||
gpMaterialFlag material_flags,
|
||||
float2 alignment_rot,
|
||||
int4 ma,
|
||||
int4 ma2,
|
||||
float2 line,
|
||||
float2 line_adj,
|
||||
float3 wpos1,
|
||||
float uv_rot,
|
||||
float2 ss1)
|
||||
{
|
||||
uint alignment_mode = material_flags & GP_STROKE_ALIGNMENT;
|
||||
|
||||
/* For one point strokes use object alignment. */
|
||||
if (alignment_mode == GP_STROKE_ALIGNMENT_STROKE && ma.x == -1 && ma2.x == -1) {
|
||||
alignment_mode = GP_STROKE_ALIGNMENT_OBJECT;
|
||||
}
|
||||
|
||||
float2 x_axis;
|
||||
if (alignment_mode == GP_STROKE_ALIGNMENT_STROKE) {
|
||||
x_axis = (ma2.x == -1) ? line_adj : line;
|
||||
}
|
||||
else if (alignment_mode == GP_STROKE_ALIGNMENT_FIXED) {
|
||||
/* Default for no-material drawing. */
|
||||
x_axis = float2(1.0f, 0.0f);
|
||||
}
|
||||
else { /* GP_STROKE_ALIGNMENT_OBJECT */
|
||||
float4 ndc_x = drw_point_world_to_homogenous(wpos1 + drw_modelmat()[0].xyz);
|
||||
float2 ss_x = gpencil_project_to_screenspace(ndc_x, viewport_res);
|
||||
x_axis = safe_normalize(ss_x - ss1);
|
||||
}
|
||||
|
||||
float rot_sin = sqrt(max(0.0f, 1.0f - uv_rot * uv_rot)) * sign(uv_rot);
|
||||
float rot_cos = abs(uv_rot);
|
||||
/* TODO(@fclem): Optimize these 2 matrix multiply into one by only having one rotation angle
|
||||
* and using a cosine approximation. */
|
||||
x_axis = float2x2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis;
|
||||
x_axis = float2x2(alignment_rot.x, -alignment_rot.y, alignment_rot.y, alignment_rot.x) * x_axis;
|
||||
|
||||
return x_axis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value of gl_Position.
|
||||
*
|
||||
* To declare in vertex shader.
|
||||
* in ivec4 ma, ma1, ma2, ma3;
|
||||
* in float4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1;
|
||||
*
|
||||
* All of these attributes are quad loaded the same way
|
||||
* as GL_LINES_ADJACENCY would feed a geometry shader:
|
||||
* - ma reference the previous adjacency point.
|
||||
* - ma1 reference the current line first point.
|
||||
* - ma2 reference the current line second point.
|
||||
* - ma3 reference the next adjacency point.
|
||||
* Note that we are rendering quad instances and not using any index buffer
|
||||
* (except for fills).
|
||||
*
|
||||
* Material : x is material index, y is stroke_id, z is point_id,
|
||||
* w is aspect & rotation & hardness packed.
|
||||
* Position : contains thickness in 4th component.
|
||||
* UV : xy is UV for fills, z is U of stroke, w is strength.
|
||||
*
|
||||
*
|
||||
* WARNING: Max attribute count is actually 14 because OSX OpenGL implementation
|
||||
* considers gl_VertexID and gl_InstanceID as vertex attribute. (see #74536)
|
||||
*/
|
||||
float4 gpencil_vertex(float4 viewport_res,
|
||||
gpMaterialFlag material_flags,
|
||||
float2 alignment_rot,
|
||||
/* World Position. */
|
||||
float3 &out_P,
|
||||
/* World Normal. */
|
||||
float3 &out_N,
|
||||
/* Vertex Color. */
|
||||
float4 &out_color,
|
||||
/* Stroke Strength. */
|
||||
float &out_strength,
|
||||
/* UV coordinates. */
|
||||
float2 &out_uv,
|
||||
/* Screen-Space positions. */
|
||||
float2 &out_sspos_0,
|
||||
float4 &out_sspos_1,
|
||||
float4 &out_sspos_2,
|
||||
float2 &out_sspos_3,
|
||||
/* Object-space accumulated length from the start of the stroke
|
||||
* (x: point 1, y: point 2, z: point density).
|
||||
* NOTE: Z must be already given. */
|
||||
float3 &out_point_length,
|
||||
/* Stroke aspect ratio and rotation direction
|
||||
* (xy: aspect ration, zw: rotation direction). */
|
||||
float4 &out_aspect,
|
||||
/* Stroke thickness and miter limits (x: clamped, y: unclamped,
|
||||
* z: miter limit segment start, w: miter limit segment end). */
|
||||
float4 &out_thickness,
|
||||
/* Stroke hardness. */
|
||||
float &out_hardness)
|
||||
{
|
||||
int stroke_point_id = gpencil_stroke_point_id();
|
||||
|
||||
/* Attribute Loading. */
|
||||
float4 pos = texelFetch(gp_pos_tx, (stroke_point_id - 1) * 3 + 0);
|
||||
float4 pos1 = texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 0);
|
||||
float4 pos2 = texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 0);
|
||||
float4 pos3 = texelFetch(gp_pos_tx, (stroke_point_id + 2) * 3 + 0);
|
||||
int4 ma = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id - 1) * 3 + 1));
|
||||
int4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 1));
|
||||
int4 ma2 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 1));
|
||||
int4 ma3 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 2) * 3 + 1));
|
||||
float4 uv1 = texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 2);
|
||||
float4 uv2 = texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 2);
|
||||
|
||||
float4 col1 = texelFetch(gp_col_tx, (stroke_point_id + 0) * 2 + 0);
|
||||
float4 col2 = texelFetch(gp_col_tx, (stroke_point_id + 1) * 2 + 0);
|
||||
float4 fcol1 = texelFetch(gp_col_tx, (stroke_point_id + 0) * 2 + 1);
|
||||
|
||||
# define thickness1 pos1.w
|
||||
# define thickness2 pos2.w
|
||||
# define strength1 uv1.w
|
||||
# define strength2 uv2.w
|
||||
|
||||
float4 out_ndc;
|
||||
|
||||
if (gpencil_is_stroke_vertex()) {
|
||||
uint placement_mode = material_flags & GP_DOTS_PLACEMENT_MODE;
|
||||
bool is_single_dot = placement_mode == GP_DOTS_PLACEMENT_MODE_COUNT &&
|
||||
out_point_length.z == 1.0f;
|
||||
bool is_dot = flag_test(material_flags, GP_STROKE_ALIGNMENT);
|
||||
bool is_squares = !flag_test(material_flags, GP_STROKE_DOTS);
|
||||
|
||||
bool is_first = (ma.x == -1);
|
||||
bool is_last = (ma3.x == -1);
|
||||
bool is_single = is_first && (ma2.x == -1);
|
||||
|
||||
PointData point_data1 = decode_ma(ma1);
|
||||
PointData point_data2 = decode_ma(ma2);
|
||||
|
||||
/* Join the first and last point if the curve is cyclical. */
|
||||
if (point_data1.cyclical && !is_single) {
|
||||
if (is_first) {
|
||||
/* The first point will have the index of the last point. */
|
||||
PointData point_data = decode_ma(ma);
|
||||
int last_stroke_id = point_data.stroke_id;
|
||||
ma = floatBitsToInt(texelFetch(gp_pos_tx, (last_stroke_id - 2) * 3 + 1));
|
||||
pos = texelFetch(gp_pos_tx, (last_stroke_id - 2) * 3 + 0);
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
PointData point_data3 = decode_ma(ma3);
|
||||
int first_stroke_id = point_data3.stroke_id;
|
||||
ma3 = floatBitsToInt(texelFetch(gp_pos_tx, (first_stroke_id + 2) * 3 + 1));
|
||||
pos3 = texelFetch(gp_pos_tx, (first_stroke_id + 2) * 3 + 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */
|
||||
if (!is_dot && is_single) {
|
||||
is_dot = true;
|
||||
is_squares = false;
|
||||
is_single_dot = true;
|
||||
}
|
||||
|
||||
/* Endpoints, we discard the vertices. */
|
||||
if (!(is_dot && is_single_dot) && ma2.x == -1) {
|
||||
return discard_ndc();
|
||||
}
|
||||
|
||||
/* Avoid using a vertex attribute for quad positioning. */
|
||||
float x = float(gl_VertexID & 1) * 2.0f - 1.0f; /* [-1..1] */
|
||||
float y = float(gl_VertexID & 2) - 1.0f; /* [-1..1] */
|
||||
|
||||
bool use_curr = (is_dot && is_single_dot) || (x == -1.0f);
|
||||
|
||||
float3 wpos0 = transform_point(drw_modelmat(), pos.xyz);
|
||||
float3 wpos1 = transform_point(drw_modelmat(), pos1.xyz);
|
||||
float3 wpos2 = transform_point(drw_modelmat(), pos2.xyz);
|
||||
float3 wpos3 = transform_point(drw_modelmat(), pos3.xyz);
|
||||
float3 wpos_adj = (use_curr) ? wpos0 : wpos3;
|
||||
|
||||
float3 T;
|
||||
if (is_dot && is_single_dot) {
|
||||
/* Shade as facing billboards. */
|
||||
T = drw_view().viewinv[0].xyz;
|
||||
}
|
||||
else if (use_curr && ma.x != -1) {
|
||||
T = wpos1 - wpos_adj;
|
||||
}
|
||||
else {
|
||||
T = wpos2 - wpos1;
|
||||
}
|
||||
T = safe_normalize(T);
|
||||
|
||||
float3 B = cross(T, drw_view().viewinv[2].xyz);
|
||||
out_N = normalize(cross(B, T));
|
||||
|
||||
float4 ndc0 = drw_point_world_to_homogenous(wpos0);
|
||||
float4 ndc1 = drw_point_world_to_homogenous(wpos1);
|
||||
float4 ndc2 = drw_point_world_to_homogenous(wpos2);
|
||||
float4 ndc3 = drw_point_world_to_homogenous(wpos3);
|
||||
|
||||
out_ndc = (use_curr) ? ndc1 : ndc2;
|
||||
out_P = (use_curr) ? wpos1 : wpos2;
|
||||
out_strength = abs((use_curr) ? strength1 : strength2);
|
||||
|
||||
float radius1 = gpencil_stroke_thickness_modulate(thickness1, viewport_res) / 2.0;
|
||||
float radius2 = gpencil_stroke_thickness_modulate(thickness2, viewport_res) / 2.0;
|
||||
|
||||
float2 ss0 = gpencil_project_to_screenspace(ndc0, viewport_res);
|
||||
float4 ss1 = ndc_and_radius_to_screen_space(ndc1, radius1, viewport_res.xy);
|
||||
float4 ss2 = ndc_and_radius_to_screen_space(ndc2, radius2, viewport_res.xy);
|
||||
float2 ss3 = gpencil_project_to_screenspace(ndc3, viewport_res);
|
||||
|
||||
/* Screen-space Lines tangents. */
|
||||
float2 line = safe_normalize(ss2.xy - ss1.xy);
|
||||
float2 line1 = safe_normalize(ss1.xy - ss0);
|
||||
float2 line2 = safe_normalize(ss3 - ss2.xy);
|
||||
float2 line_adj = (use_curr) ? line1 : line2;
|
||||
|
||||
float thickness = abs((use_curr) ? thickness1 : thickness2);
|
||||
thickness = gpencil_stroke_thickness_modulate(thickness, viewport_res);
|
||||
/* The radius attribute can have negative values. Make sure that it's not negative by clamping
|
||||
* to 0. */
|
||||
float clamped_thickness = max(0.0f, thickness);
|
||||
|
||||
out_uv = float2(x, y) * 0.5f + 0.5f;
|
||||
out_hardness = gpencil_decode_hardness(use_curr ? point_data1.packed_data :
|
||||
point_data2.packed_data);
|
||||
|
||||
out_sspos_1 = ss1;
|
||||
if (ma2.x != -1) {
|
||||
out_sspos_2 = ss2;
|
||||
}
|
||||
else {
|
||||
out_sspos_2 = out_sspos_1;
|
||||
}
|
||||
if (ma.x != -1) {
|
||||
out_sspos_0 = ss0;
|
||||
}
|
||||
else {
|
||||
out_sspos_0 = out_sspos_1.xy;
|
||||
}
|
||||
if (ma3.x != -1) {
|
||||
out_sspos_3 = ss3;
|
||||
}
|
||||
else {
|
||||
out_sspos_3 = out_sspos_2.xy;
|
||||
}
|
||||
|
||||
/* Z is already given. */
|
||||
out_point_length.xy = float2(uv1.z, uv2.z);
|
||||
|
||||
if (is_dot && !is_single_dot) {
|
||||
out_thickness.x = clamped_thickness / out_ndc.w;
|
||||
out_thickness.y = thickness / out_ndc.w;
|
||||
|
||||
/* Rotation: Encoded as Cos + Sin sign. */
|
||||
float uv_rot = gpencil_decode_uvrot(point_data1.packed_data);
|
||||
float2 x_axis = get_rotation(viewport_res,
|
||||
material_flags,
|
||||
alignment_rot,
|
||||
ma,
|
||||
ma2,
|
||||
line,
|
||||
line_adj,
|
||||
wpos1,
|
||||
uv_rot,
|
||||
ss1.xy);
|
||||
out_aspect.xy = gpencil_decode_aspect(point_data1.packed_data);
|
||||
/* Invert for vertex shader. */
|
||||
out_aspect.xy = 1.0f / out_aspect.xy;
|
||||
out_aspect.zw = x_axis;
|
||||
|
||||
out_ndc = dot_segment(float2(x, y), ss1, ss2, is_squares, viewport_res);
|
||||
|
||||
out_ndc /= out_ndc.w;
|
||||
out_ndc.z = (use_curr) ? (ndc1.z / ndc1.w) : (ndc2.z / ndc2.w);
|
||||
|
||||
out_uv.x = (use_curr) ? uv1.z : uv2.z;
|
||||
}
|
||||
else if (is_dot && is_single_dot) {
|
||||
/* Rotation: Encoded as Cos + Sin sign. */
|
||||
float uv_rot = gpencil_decode_uvrot(point_data1.packed_data);
|
||||
float2 x_axis = get_rotation(viewport_res,
|
||||
material_flags,
|
||||
alignment_rot,
|
||||
ma,
|
||||
ma2,
|
||||
line,
|
||||
line_adj,
|
||||
wpos1,
|
||||
uv_rot,
|
||||
ss1.xy);
|
||||
/* Rotate 90 degrees counter-clockwise. */
|
||||
float2 y_axis = float2(-x_axis.y, x_axis.x);
|
||||
|
||||
out_aspect.xy = gpencil_decode_aspect(point_data1.packed_data);
|
||||
out_aspect.zw = x_axis;
|
||||
|
||||
x *= out_aspect.x;
|
||||
y *= out_aspect.y;
|
||||
|
||||
/* Invert for vertex shader. */
|
||||
out_aspect.xy = 1.0f / out_aspect.xy;
|
||||
|
||||
out_ndc.xy += (x * x_axis + y * y_axis) * viewport_res.zw * clamped_thickness;
|
||||
out_sspos_0.xy = ss1.xy + x_axis * 0.5f;
|
||||
|
||||
out_thickness.x = (is_squares) ? 1e18f : (clamped_thickness / out_ndc.w);
|
||||
out_thickness.y = (is_squares) ? 1e18f : (thickness / out_ndc.w);
|
||||
out_thickness.z = MITER_LIMIT_TYPE_ROUND;
|
||||
out_thickness.w = MITER_LIMIT_TYPE_ROUND;
|
||||
}
|
||||
else {
|
||||
bool is_stroke_start = (ma.x == -1 && x == -1.0f);
|
||||
bool is_stroke_end = (ma3.x == -1 && x == 1.0f);
|
||||
|
||||
float miter_limit1 = gpencil_decode_miter_limit(point_data1.packed_data);
|
||||
float miter_limit2 = gpencil_decode_miter_limit(point_data2.packed_data);
|
||||
|
||||
float cos_angle1 = -dot(line, line1);
|
||||
float cos_angle2 = -dot(line, line2);
|
||||
|
||||
out_thickness.z = miter_limit1;
|
||||
out_thickness.w = miter_limit2;
|
||||
|
||||
if (cos_angle1 > miter_limit1 && miter_limit1 != MITER_LIMIT_TYPE_ROUND) {
|
||||
out_thickness.z = MITER_LIMIT_TYPE_BEVEL;
|
||||
}
|
||||
if (cos_angle2 > miter_limit2 && miter_limit2 != MITER_LIMIT_TYPE_ROUND) {
|
||||
out_thickness.w = MITER_LIMIT_TYPE_BEVEL;
|
||||
}
|
||||
|
||||
float miter_limit = use_curr ? miter_limit1 : miter_limit2;
|
||||
|
||||
if (miter_limit == MITER_LIMIT_TYPE_BEVEL || miter_limit == MITER_LIMIT_TYPE_ROUND) {
|
||||
miter_limit = 0.5f; /* Default to cos(60) */
|
||||
}
|
||||
|
||||
/* Prevent the limit from becoming to small. */
|
||||
if (miter_limit < 0.5f) {
|
||||
miter_limit = 0.5f;
|
||||
}
|
||||
|
||||
/* Mitter tangent vector. */
|
||||
float2 miter_tan = safe_normalize(line_adj + line);
|
||||
float miter_dot = dot(miter_tan, line_adj);
|
||||
float cos_angle_adj = (use_curr) ? cos_angle1 : cos_angle2;
|
||||
/* Break corners after a certain angle to avoid really thick corners. */
|
||||
bool miter_break = cos_angle_adj > miter_limit;
|
||||
miter_tan = (miter_break || is_stroke_start || is_stroke_end) ? line :
|
||||
(miter_tan / miter_dot);
|
||||
/* Rotate 90 degrees counter-clockwise. */
|
||||
float2 miter = float2(-miter_tan.y, miter_tan.x);
|
||||
|
||||
out_thickness.x = clamped_thickness / out_ndc.w;
|
||||
out_thickness.y = thickness / out_ndc.w;
|
||||
out_aspect = float4(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
|
||||
float2 screen_ofs = miter * y;
|
||||
|
||||
/* Reminder: we packed the cap flag into the sign of strength and thickness sign. */
|
||||
if ((is_stroke_start && strength1 > 0.0f) || (is_stroke_end && thickness1 > 0.0f) ||
|
||||
(miter_break && !is_stroke_start && !is_stroke_end))
|
||||
{
|
||||
screen_ofs += line * x;
|
||||
}
|
||||
|
||||
out_ndc.xy += screen_ofs * viewport_res.zw * clamped_thickness;
|
||||
|
||||
out_uv.x = (use_curr) ? uv1.z : uv2.z;
|
||||
}
|
||||
|
||||
out_color = (use_curr) ? col1 : col2;
|
||||
}
|
||||
else {
|
||||
out_P = transform_point(drw_modelmat(), pos1.xyz);
|
||||
out_ndc = drw_point_world_to_homogenous(out_P);
|
||||
out_uv = uv1.xy;
|
||||
out_thickness.x = 1e18f;
|
||||
out_thickness.y = 1e20f;
|
||||
out_thickness.z = MITER_LIMIT_TYPE_ROUND;
|
||||
out_thickness.w = MITER_LIMIT_TYPE_ROUND;
|
||||
out_hardness = 1.0f;
|
||||
out_aspect = float4(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
out_sspos_0 = float2(0.0f);
|
||||
out_sspos_1 = float4(0.0f);
|
||||
out_sspos_2 = float4(0.0f);
|
||||
out_sspos_3 = float2(0.0f);
|
||||
out_point_length = float3(0.0f);
|
||||
|
||||
/* Flat normal following camera and object bounds. */
|
||||
float3 V = drw_world_incident_vector(drw_modelmat()[3].xyz);
|
||||
float3 N = drw_normal_world_to_object(V);
|
||||
N *= drw_object_infos().orco_mul;
|
||||
N = drw_normal_world_to_object(N);
|
||||
out_N = safe_normalize(N);
|
||||
|
||||
/* Decode fill opacity. */
|
||||
out_color = float4(fcol1.rgb, floor(fcol1.a / 10.0f) / 10000.0f);
|
||||
|
||||
/* We still offset the fills a little to avoid overlaps */
|
||||
out_ndc.z += 0.000002f;
|
||||
}
|
||||
|
||||
# undef thickness1
|
||||
# undef thickness2
|
||||
# undef strength1
|
||||
# undef strength2
|
||||
|
||||
return out_ndc;
|
||||
}
|
||||
|
||||
float4 gpencil_vertex(float4 viewport_res,
|
||||
float3 &out_P,
|
||||
float3 &out_N,
|
||||
float4 &out_color,
|
||||
float &out_strength,
|
||||
float2 &out_uv,
|
||||
float2 &out_sspos_0,
|
||||
float4 &out_sspos_1,
|
||||
float4 &out_sspos_2,
|
||||
float2 &out_sspos_3,
|
||||
float3 &out_point_length,
|
||||
float4 &out_aspect,
|
||||
float4 &out_thickness,
|
||||
float &out_hardness)
|
||||
{
|
||||
return gpencil_vertex(viewport_res,
|
||||
gpMaterialFlag(0u),
|
||||
float2(1.0f, 0.0f),
|
||||
out_P,
|
||||
out_N,
|
||||
out_color,
|
||||
out_strength,
|
||||
out_uv,
|
||||
out_sspos_0,
|
||||
out_sspos_1,
|
||||
out_sspos_2,
|
||||
out_sspos_3,
|
||||
out_point_length,
|
||||
out_aspect,
|
||||
out_thickness,
|
||||
out_hardness);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,545 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Intersection library used for culling.
|
||||
* Results are meant to be conservative.
|
||||
*/
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
#include "draw_shape_lib.glsl"
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_math_safe_lib.glsl"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Plane extraction functions.
|
||||
* \{ */
|
||||
|
||||
/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */
|
||||
float4 isect_plane_setup(float3 p, float3 v1, float3 v2)
|
||||
{
|
||||
float3 normal_to_plane = normalize(cross(v1, v2));
|
||||
return float4(normal_to_plane, -dot(normal_to_plane, p));
|
||||
}
|
||||
|
||||
struct IsectPyramid {
|
||||
float3 corners[5];
|
||||
float4 planes[5];
|
||||
};
|
||||
|
||||
IsectPyramid isect_pyramid_setup(Pyramid shape)
|
||||
{
|
||||
float3 A1 = shape.corners[1] - shape.corners[0];
|
||||
float3 A2 = shape.corners[2] - shape.corners[0];
|
||||
float3 A3 = shape.corners[3] - shape.corners[0];
|
||||
float3 A4 = shape.corners[4] - shape.corners[0];
|
||||
float3 S4 = shape.corners[4] - shape.corners[1];
|
||||
float3 S2 = shape.corners[2] - shape.corners[1];
|
||||
|
||||
IsectPyramid data;
|
||||
data.planes[0] = isect_plane_setup(shape.corners[0], A2, A1);
|
||||
data.planes[1] = isect_plane_setup(shape.corners[0], A3, A2);
|
||||
data.planes[2] = isect_plane_setup(shape.corners[0], A4, A3);
|
||||
data.planes[3] = isect_plane_setup(shape.corners[0], A1, A4);
|
||||
data.planes[4] = isect_plane_setup(shape.corners[1], S2, S4);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
data.corners[i] = shape.corners[i];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
struct IsectBox {
|
||||
float3 corners[8];
|
||||
float4 planes[6];
|
||||
};
|
||||
|
||||
IsectBox isect_box_setup(Box shape)
|
||||
{
|
||||
float3 A1 = shape.corners[1] - shape.corners[0];
|
||||
float3 A3 = shape.corners[3] - shape.corners[0];
|
||||
float3 A4 = shape.corners[4] - shape.corners[0];
|
||||
|
||||
IsectBox data;
|
||||
data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1);
|
||||
data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3);
|
||||
data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4);
|
||||
/* Assumes that the box is actually a box! */
|
||||
data.planes[3] = float4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, shape.corners[6]));
|
||||
data.planes[4] = float4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, shape.corners[6]));
|
||||
data.planes[5] = float4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, shape.corners[6]));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
data.corners[i] = shape.corners[i];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Construct box from 1 corner point + 3 side vectors. */
|
||||
IsectBox isect_box_setup(float3 origin, float3 side_x, float3 side_y, float3 side_z)
|
||||
{
|
||||
IsectBox data;
|
||||
data.corners[0] = origin;
|
||||
data.corners[1] = origin + side_x;
|
||||
data.corners[2] = origin + side_y + side_x;
|
||||
data.corners[3] = origin + side_y;
|
||||
data.corners[4] = data.corners[0] + side_z;
|
||||
data.corners[5] = data.corners[1] + side_z;
|
||||
data.corners[6] = data.corners[2] + side_z;
|
||||
data.corners[7] = data.corners[3] + side_z;
|
||||
|
||||
data.planes[0] = isect_plane_setup(data.corners[0], side_y, side_z);
|
||||
data.planes[1] = isect_plane_setup(data.corners[0], side_x, side_y);
|
||||
data.planes[2] = isect_plane_setup(data.corners[0], side_z, side_x);
|
||||
/* Assumes that the box is actually a box! */
|
||||
data.planes[3] = float4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, data.corners[6]));
|
||||
data.planes[4] = float4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, data.corners[6]));
|
||||
data.planes[5] = float4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, data.corners[6]));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
struct IsectFrustum {
|
||||
float3 corners[8];
|
||||
float4 planes[6];
|
||||
};
|
||||
|
||||
IsectFrustum isect_frustum_setup(Frustum shape)
|
||||
{
|
||||
float3 A1 = shape.corners[1] - shape.corners[0];
|
||||
float3 A3 = shape.corners[3] - shape.corners[0];
|
||||
float3 A4 = shape.corners[4] - shape.corners[0];
|
||||
float3 B5 = shape.corners[5] - shape.corners[6];
|
||||
float3 B7 = shape.corners[7] - shape.corners[6];
|
||||
float3 B2 = shape.corners[2] - shape.corners[6];
|
||||
|
||||
IsectFrustum data;
|
||||
data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1);
|
||||
data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3);
|
||||
data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4);
|
||||
data.planes[3] = isect_plane_setup(shape.corners[6], B7, B5);
|
||||
data.planes[4] = isect_plane_setup(shape.corners[6], B5, B2);
|
||||
data.planes[5] = isect_plane_setup(shape.corners[6], B2, B7);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
data.corners[i] = shape.corners[i];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name View Intersection functions.
|
||||
* \{ */
|
||||
|
||||
#ifdef DRW_VIEW_CULLING_INFO
|
||||
SHADER_LIBRARY_CREATE_INFO(draw_view_culling)
|
||||
|
||||
ViewCullingData drw_view_culling(uint view_id = drw_view_id)
|
||||
{
|
||||
return drw_view_culling_buf[view_id];
|
||||
}
|
||||
|
||||
bool intersect_view(Pyramid pyramid, uint view_id = drw_view_id)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* WORKAROUND: There is a GLSL compiler bug on legacy AMD GPU drivers that returns an incorrect
|
||||
* computation if `drw_view_culling()` is called in both of the loops below (see #143336). */
|
||||
ViewCullingData culling = drw_view_culling(view_id);
|
||||
|
||||
/* Do Pyramid vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(culling.frustum_planes.planes[p], float4(pyramid.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Pyramid planes. */
|
||||
IsectPyramid i_pyramid = isect_pyramid_setup(pyramid);
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], float4(culling.frustum_corners.corners[v].xyz, 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect_view(Box box, uint view_id = drw_view_id)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* WORKAROUND: There is a GLSL compiler bug on legacy AMD GPU drivers that returns an incorrect
|
||||
* computation if `drw_view_culling()` is called in both of the loops below (see #143336). */
|
||||
ViewCullingData culling = drw_view_culling(view_id);
|
||||
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(culling.frustum_planes.planes[p], float4(box.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Box planes. */
|
||||
IsectBox i_box = isect_box_setup(box);
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_box.planes[p], float4(culling.frustum_corners.corners[v].xyz, 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect_view(IsectBox i_box, uint view_id = drw_view_id)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* WORKAROUND: There is a GLSL compiler bug on legacy AMD GPU drivers that returns an incorrect
|
||||
* computation if `drw_view_culling()` is called in both of the loops below (see #143336). */
|
||||
ViewCullingData culling = drw_view_culling(view_id);
|
||||
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(culling.frustum_planes.planes[p], float4(i_box.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_box.planes[p], float4(culling.frustum_corners.corners[v].xyz, 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect_view(Sphere sphere, uint view_id = drw_view_id)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* WORKAROUND: There is a GLSL compiler bug on legacy AMD GPU drivers that returns an incorrect
|
||||
* computation if `drw_view_culling()` is called in both of the loops below (see #143336). */
|
||||
ViewCullingData culling = drw_view_culling(view_id);
|
||||
|
||||
for (int p = 0; p < 6 && intersects; ++p) {
|
||||
float dist_to_plane = dot(culling.frustum_planes.planes[p], float4(sphere.center, 1.0f));
|
||||
if (dist_to_plane < -sphere.radius) {
|
||||
intersects = false;
|
||||
}
|
||||
}
|
||||
/* TODO reject false positive. */
|
||||
return intersects;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Shape vs. Shape Intersection functions.
|
||||
* \{ */
|
||||
|
||||
bool intersect(IsectPyramid i_pyramid, Box box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Pyramid planes. */
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], float4(box.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Pyramid vertices vs Box planes. */
|
||||
IsectBox i_box = isect_box_setup(box);
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(i_box.planes[p], float4(i_pyramid.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect(IsectPyramid i_pyramid, IsectBox i_box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Pyramid planes. */
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], float4(i_box.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Pyramid vertices vs Box planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(i_box.planes[p], float4(i_pyramid.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, Pyramid pyramid)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Pyramid vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 5; ++v) {
|
||||
float test = dot(i_frustum.planes[p], float4(pyramid.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Pyramid planes. */
|
||||
IsectPyramid i_pyramid = isect_pyramid_setup(pyramid);
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_pyramid.planes[p], float4(i_frustum.corners[v].xyz, 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, Box box)
|
||||
{
|
||||
bool intersects = true;
|
||||
|
||||
/* Do Box vertices vs Frustum planes. */
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_frustum.planes[p], float4(box.corners[v], 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
return intersects;
|
||||
}
|
||||
|
||||
/* Now do Frustum vertices vs Box planes. */
|
||||
IsectBox i_box = isect_box_setup(box);
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
bool is_any_vertex_on_positive_side = false;
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
float test = dot(i_box.planes[p], float4(i_frustum.corners[v].xyz, 1.0f));
|
||||
if (test > 0.0f) {
|
||||
is_any_vertex_on_positive_side = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
|
||||
if (all_vertex_on_negative_side) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect(IsectFrustum i_frustum, Sphere sphere)
|
||||
{
|
||||
bool intersects = true;
|
||||
for (int p = 0; p < 6; ++p) {
|
||||
float dist_to_plane = dot(i_frustum.planes[p], float4(sphere.center, 1.0f));
|
||||
if (dist_to_plane < -sphere.radius) {
|
||||
intersects = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect(Cone cone, Sphere sphere)
|
||||
{
|
||||
/**
|
||||
* Following "Improve Tile-based Light Culling with Spherical-sliced Cone"
|
||||
* by Eric Zhang
|
||||
* https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html
|
||||
*/
|
||||
float sphere_distance = length(sphere.center);
|
||||
float sphere_distance_rcp = safe_rcp(sphere_distance);
|
||||
float sphere_sin = saturate(sphere.radius * sphere_distance_rcp);
|
||||
float sphere_cos = sqrt(1.0f - sphere_sin * sphere_sin);
|
||||
float cone_aperture_sin = sqrt(1.0f - cone.angle_cos * cone.angle_cos);
|
||||
|
||||
float cone_sphere_center_cos = dot(sphere.center * sphere_distance_rcp, cone.direction);
|
||||
/* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */
|
||||
float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ?
|
||||
-1.0f :
|
||||
(cone.angle_cos * sphere_cos -
|
||||
cone_aperture_sin * sphere_sin);
|
||||
/* Comparing cosines instead of angles since we are interested
|
||||
* only in the monotonic region [0 .. M_PI / 2]. This saves costly `acos()` calls. */
|
||||
bool intersects = (cone_sphere_center_cos >= cone_sphere_angle_sum_cos);
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool intersect(Circle circle_a, Circle circle_b)
|
||||
{
|
||||
return distance_squared(circle_a.center, circle_b.center) <
|
||||
square(circle_a.radius + circle_b.radius);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,190 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* This should ultimately be rewritten inside a gpu_shader lib with higher quality standard.
|
||||
*/
|
||||
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
#include "gpu_shader_math_vector_reduce_lib.glsl"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Math intersection & projection functions.
|
||||
* \{ */
|
||||
|
||||
float4 plane_from_quad(float3 v0, float3 v1, float3 v2, float3 v3)
|
||||
{
|
||||
float3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3));
|
||||
return float4(nor, -dot(nor, v2));
|
||||
}
|
||||
|
||||
float4 plane_from_tri(float3 v0, float3 v1, float3 v2)
|
||||
{
|
||||
float3 nor = normalize(cross(v2 - v1, v0 - v1));
|
||||
return float4(nor, -dot(nor, v2));
|
||||
}
|
||||
|
||||
float point_plane_projection_dist(float3 line_origin, float3 plane_origin, float3 plane_normal)
|
||||
{
|
||||
return dot(plane_normal, plane_origin - line_origin);
|
||||
}
|
||||
|
||||
float point_line_projection_dist(float2 point, float2 line_origin, float2 line_normal)
|
||||
{
|
||||
return dot(line_normal, line_origin - point);
|
||||
}
|
||||
|
||||
float line_plane_intersect_dist(float3 line_origin,
|
||||
float3 line_direction,
|
||||
float3 plane_origin,
|
||||
float3 plane_normal)
|
||||
{
|
||||
return dot(plane_normal, plane_origin - line_origin) / dot(plane_normal, line_direction);
|
||||
}
|
||||
|
||||
float line_plane_intersect_dist(float3 line_origin, float3 line_direction, float4 plane)
|
||||
{
|
||||
float3 plane_co = plane.xyz * (-plane.w / length_squared(plane.xyz));
|
||||
float3 h = line_origin - plane_co;
|
||||
return -dot(plane.xyz, h) / dot(plane.xyz, line_direction);
|
||||
}
|
||||
|
||||
float3 line_plane_intersect(float3 line_origin,
|
||||
float3 line_direction,
|
||||
float3 plane_origin,
|
||||
float3 plane_normal)
|
||||
{
|
||||
float dist = line_plane_intersect_dist(line_origin, line_direction, plane_origin, plane_normal);
|
||||
return line_origin + line_direction * dist;
|
||||
}
|
||||
|
||||
float3 line_plane_intersect(float3 line_origin, float3 line_direction, float4 plane)
|
||||
{
|
||||
float dist = line_plane_intersect_dist(line_origin, line_direction, plane);
|
||||
return line_origin + line_direction * dist;
|
||||
}
|
||||
|
||||
float line_aligned_plane_intersect_dist(float3 line_origin,
|
||||
float3 line_direction,
|
||||
float3 plane_origin)
|
||||
{
|
||||
/* aligned plane normal */
|
||||
float3 L = plane_origin - line_origin;
|
||||
float disk_dist = length(L);
|
||||
float3 plane_normal = -normalize(L);
|
||||
return -disk_dist / dot(plane_normal, line_direction);
|
||||
}
|
||||
|
||||
float3 line_aligned_plane_intersect(float3 line_origin, float3 line_direction, float3 plane_origin)
|
||||
{
|
||||
float dist = line_aligned_plane_intersect_dist(line_origin, line_direction, plane_origin);
|
||||
if (dist < 0) {
|
||||
/* if intersection is behind we fake the intersection to be
|
||||
* really far and (hopefully) not inside the radius of interest */
|
||||
dist = 1e16f;
|
||||
}
|
||||
return line_origin + line_direction * dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns intersection distance between the unit sphere and the line
|
||||
* with the assumption that \a line_origin is contained in the unit sphere.
|
||||
* It will always returns the farthest intersection.
|
||||
*/
|
||||
float line_unit_sphere_intersect_dist(float3 line_origin, float3 line_direction)
|
||||
{
|
||||
float a = dot(line_direction, line_direction);
|
||||
float b = dot(line_direction, line_origin);
|
||||
float c = dot(line_origin, line_origin) - 1;
|
||||
|
||||
float dist = 1e15f;
|
||||
float determinant = b * b - a * c;
|
||||
if (determinant >= 0) {
|
||||
dist = (sqrt(determinant) - b) / a;
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minimum intersection distance between the unit box and the line
|
||||
* with the assumption that \a line_origin is contained in the unit box.
|
||||
* In other words, it will always returns the farthest intersection.
|
||||
*/
|
||||
float line_unit_box_intersect_dist(float3 line_origin, float3 line_direction)
|
||||
{
|
||||
/* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
||||
*/
|
||||
float3 first_plane = (float3(1.0f) - line_origin) / line_direction;
|
||||
float3 second_plane = (float3(-1.0f) - line_origin) / line_direction;
|
||||
float3 farthest_plane = max(first_plane, second_plane);
|
||||
return reduce_min(farthest_plane);
|
||||
}
|
||||
|
||||
float line_unit_box_intersect_dist_safe(float3 line_origin, float3 line_direction)
|
||||
{
|
||||
float3 safe_line_direction = max(float3(1e-8f), abs(line_direction)) *
|
||||
select(float3(1.0f),
|
||||
-float3(1.0f),
|
||||
lessThan(line_direction, float3(0.0f)));
|
||||
return line_unit_box_intersect_dist(line_origin, safe_line_direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as line_unit_box_intersect_dist but for 2D case.
|
||||
*/
|
||||
float line_unit_square_intersect_dist(float2 line_origin, float2 line_direction)
|
||||
{
|
||||
float2 first_plane = (float2(1.0f) - line_origin) / line_direction;
|
||||
float2 second_plane = (float2(-1.0f) - line_origin) / line_direction;
|
||||
float2 farthest_plane = max(first_plane, second_plane);
|
||||
return reduce_min(farthest_plane);
|
||||
}
|
||||
|
||||
float line_unit_square_intersect_dist_safe(float2 line_origin, float2 line_direction)
|
||||
{
|
||||
float2 safe_line_direction = max(float2(1e-8f), abs(line_direction)) *
|
||||
select(float2(1.0f),
|
||||
-float2(1.0f),
|
||||
lessThan(line_direction, float2(0.0f)));
|
||||
return line_unit_square_intersect_dist(line_origin, safe_line_direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns clipping distance (intersection with the nearest plane) with the given axis-aligned
|
||||
* bound box along \a line_direction.
|
||||
* Safe even if \a line_direction is degenerate.
|
||||
* It assumes that an intersection exists (i.e: that \a line_direction points towards the AABB).
|
||||
*/
|
||||
float line_aabb_clipping_dist(float3 line_origin,
|
||||
float3 line_direction,
|
||||
float3 aabb_min,
|
||||
float3 aabb_max)
|
||||
{
|
||||
float3 safe_dir = select(
|
||||
line_direction, float3(1e-5f), lessThan(abs(line_direction), float3(1e-5f)));
|
||||
float3 dir_inv = 1.0f / safe_dir;
|
||||
|
||||
float3 first_plane = (aabb_min - line_origin) * dir_inv;
|
||||
float3 second_plane = (aabb_max - line_origin) * dir_inv;
|
||||
float3 nearest_plane = min(first_plane, second_plane);
|
||||
return reduce_max(nearest_plane);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Other useful functions.
|
||||
* \{ */
|
||||
|
||||
void make_orthonormal_basis(float3 N, float3 &T, float3 &B)
|
||||
{
|
||||
float3 up_vector = abs(N.z) < 0.99999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f);
|
||||
T = normalize(cross(up_vector, N));
|
||||
B = cross(N, T);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct ID {
|
||||
uint raw_id;
|
||||
|
||||
template<int view_count> uint view_id() const
|
||||
{
|
||||
/** \note This is simply log2(DRW_VIEW_LEN) but making sure it is optimized out. */
|
||||
return raw_id & ~(0xFFFFFFFFu << ((view_count > 32) ? 6 :
|
||||
(view_count > 16) ? 5 :
|
||||
(view_count > 8) ? 4 :
|
||||
(view_count > 4) ? 3 :
|
||||
(view_count > 2) ? 2 :
|
||||
(view_count > 1) ? 1 :
|
||||
0));
|
||||
}
|
||||
|
||||
template<int view_count> uint resource_id() const
|
||||
{
|
||||
return raw_id >> ((view_count > 32) ? 6 :
|
||||
(view_count > 16) ? 5 :
|
||||
(view_count > 8) ? 4 :
|
||||
(view_count > 4) ? 3 :
|
||||
(view_count > 2) ? 2 :
|
||||
(view_count > 1) ? 1 :
|
||||
0);
|
||||
}
|
||||
};
|
||||
|
||||
template uint ID::view_id<1>() const;
|
||||
template uint ID::view_id<64>() const;
|
||||
template uint ID::resource_id<1>() const;
|
||||
template uint ID::resource_id<64>() const;
|
||||
|
||||
struct Resource {
|
||||
[[storage(DRW_RESOURCE_ID_SLOT, read)]] const uint (&res_id_buf)[];
|
||||
|
||||
/* `instance_index` must be the `[[instance_index]]` entry point parameter. */
|
||||
ID get(int instance_index) const
|
||||
{
|
||||
uint raw = res_id_buf[instance_index];
|
||||
return {raw};
|
||||
}
|
||||
};
|
||||
|
||||
struct ResourceCustomID {
|
||||
[[storage(DRW_RESOURCE_ID_SLOT, read)]] const uint2 (&res_id_with_custom_id_buf)[];
|
||||
|
||||
/* `instance_index` must be the `[[instance_index]]` entry point parameter. */
|
||||
ID get(int instance_index) const
|
||||
{
|
||||
return {res_id_with_custom_id_buf[instance_index].x};
|
||||
}
|
||||
|
||||
/* `instance_index` must be the `[[instance_index]]` entry point parameter. */
|
||||
uint get_custom_id(int instance_index) const
|
||||
{
|
||||
return res_id_with_custom_id_buf[instance_index].y;
|
||||
}
|
||||
};
|
||||
|
||||
struct Model {
|
||||
[[storage(DRW_OBJ_MAT_SLOT, read)]] const ObjectMatrices (&drw_matrix_buf)[];
|
||||
|
||||
/* `resource_id` should be the result of `draw::ID::resource_id()` or manually indexed. */
|
||||
ObjectMatrices get(uint resource_id) const
|
||||
{
|
||||
return drw_matrix_buf[resource_id];
|
||||
}
|
||||
};
|
||||
|
||||
struct Infos {
|
||||
[[storage(DRW_OBJ_INFOS_SLOT, read)]] const ObjectInfos (&drw_infos)[];
|
||||
|
||||
/* `resource_id` should be the result of `draw::ID::resource_id()` or manually indexed. */
|
||||
ObjectInfos get(uint resource_id) const
|
||||
{
|
||||
return drw_infos[resource_id];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace draw
|
||||
@@ -0,0 +1,146 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
#include "draw_view_lib.glsl"
|
||||
|
||||
#if !defined(DRAW_MODELMAT_CREATE_INFO) && !defined(GLSL_CPP_STUBS)
|
||||
# error Missing draw_modelmat additional create info on shader create info
|
||||
#endif
|
||||
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
VERTEX_SHADER_CREATE_INFO(draw_resource_id_varying)
|
||||
#elif defined(GPU_FRAGMENT_SHADER)
|
||||
FRAGMENT_SHADER_CREATE_INFO(draw_resource_id_varying)
|
||||
#elif defined(GPU_LIBRARY_SHADER)
|
||||
SHADER_LIBRARY_CREATE_INFO(draw_resource_id_varying)
|
||||
#endif
|
||||
|
||||
uint drw_resource_id_raw()
|
||||
{
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
# ifdef WITH_CUSTOM_IDS
|
||||
uint id = res_id_with_custom_id_buf[gpu_BaseInstance + gl_InstanceID].x;
|
||||
# else
|
||||
uint id = res_id_buf[gpu_BaseInstance + gl_InstanceID];
|
||||
# endif
|
||||
return id;
|
||||
|
||||
#elif (defined(GPU_FRAGMENT_SHADER) || defined(GPU_LIBRARY_SHADER)) && defined(RESOURCE_ID_VARYING)
|
||||
return drw_ResourceID_iface.resource_id;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint drw_resource_id()
|
||||
{
|
||||
return drw_resource_id_raw() >> DRW_VIEW_SHIFT;
|
||||
}
|
||||
|
||||
uint drw_custom_id()
|
||||
{
|
||||
#ifdef WITH_CUSTOM_IDS
|
||||
# if defined(GPU_VERTEX_SHADER)
|
||||
uint inst_id = gpu_BaseInstance + gl_InstanceID;
|
||||
return res_id_with_custom_id_buf[gpu_BaseInstance + gl_InstanceID].y;
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
float4x4 drw_modelmat()
|
||||
{
|
||||
const auto &matrix_buf = buffer_get(draw_modelmat_common, drw_matrix_buf);
|
||||
return matrix_buf[drw_resource_id()].model;
|
||||
}
|
||||
float4x4 drw_modelinv()
|
||||
{
|
||||
const auto &matrix_buf = buffer_get(draw_modelmat_common, drw_matrix_buf);
|
||||
return matrix_buf[drw_resource_id()].model_inverse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usually Normal matrix is `transpose(inverse(ViewMatrix * ModelMatrix))`.
|
||||
*
|
||||
* But since it is slow to multiply matrices we decompose it. Decomposing
|
||||
* inversion and transposition both invert the product order leaving us with
|
||||
* the same original order:
|
||||
* transpose(ViewMatrixInverse) * transpose(ModelMatrixInverse)
|
||||
*
|
||||
* Knowing that the view matrix is orthogonal, the transpose is also the inverse.
|
||||
* NOTE: This is only valid because we are only using the mat3 of the ViewMatrixInverse.
|
||||
* ViewMatrix * transpose(ModelMatrixInverse)
|
||||
*/
|
||||
float3x3 drw_normat()
|
||||
{
|
||||
return transpose(to_float3x3(drw_modelinv()));
|
||||
}
|
||||
float3x3 drw_norinv()
|
||||
{
|
||||
return transpose(to_float3x3(drw_modelmat()));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Normal
|
||||
*
|
||||
* Space conversion helpers for normal vectors.
|
||||
* \{ */
|
||||
|
||||
float3 drw_normal_object_to_world(float3 lN)
|
||||
{
|
||||
return (drw_normat() * lN);
|
||||
}
|
||||
float3 drw_normal_world_to_object(float3 N)
|
||||
{
|
||||
return (drw_norinv() * N);
|
||||
}
|
||||
|
||||
float3 drw_normal_object_to_view(float3 lN)
|
||||
{
|
||||
return (to_float3x3(drw_view().viewmat) * (drw_normat() * lN));
|
||||
}
|
||||
float3 drw_normal_view_to_object(float3 vN)
|
||||
{
|
||||
return (drw_norinv() * (to_float3x3(drw_view().viewinv) * vN));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Normal
|
||||
*
|
||||
* Space conversion helpers for points (coordinates).
|
||||
* \{ */
|
||||
|
||||
float3 drw_point_object_to_world(float3 lP)
|
||||
{
|
||||
return (drw_modelmat() * float4(lP, 1.0f)).xyz;
|
||||
}
|
||||
float3 drw_point_world_to_object(float3 P)
|
||||
{
|
||||
return (drw_modelinv() * float4(P, 1.0f)).xyz;
|
||||
}
|
||||
|
||||
float3 drw_point_object_to_view(float3 lP)
|
||||
{
|
||||
return (drw_view().viewmat * (drw_modelmat() * float4(lP, 1.0f))).xyz;
|
||||
}
|
||||
float3 drw_point_view_to_object(float3 vP)
|
||||
{
|
||||
return (drw_modelinv() * (drw_view().viewinv * float4(vP, 1.0f))).xyz;
|
||||
}
|
||||
|
||||
float4 drw_point_object_to_homogenous(float3 lP)
|
||||
{
|
||||
return (drw_view().winmat * (drw_view().viewmat * (drw_modelmat() * float4(lP, 1.0f))));
|
||||
}
|
||||
float3 drw_point_object_to_ndc(float3 lP)
|
||||
{
|
||||
return drw_perspective_divide(drw_point_object_to_homogenous(lP));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "draw_shader_shared.hh"
|
||||
# include "gpencil_shader_shared.hh"
|
||||
|
||||
# include "draw_view_infos.hh"
|
||||
#endif
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
# define CURVES_SHADER
|
||||
# define DRW_GPENCIL_INFO
|
||||
#endif
|
||||
|
||||
#include "draw_defines.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_volume_infos)
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
DEFINE("VOLUME_INFO_LIB")
|
||||
UNIFORM_BUF_FREQ(DRW_OBJ_DATA_INFO_UBO_SLOT, VolumeInfos, drw_volume, BATCH)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves_infos)
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
UNIFORM_BUF_FREQ(DRW_OBJ_DATA_INFO_UBO_SLOT, CurvesInfos, drw_curves, BATCH)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_layer_attributes)
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
DEFINE("VLATTR_LIB")
|
||||
UNIFORM_BUF_FREQ(DRW_LAYER_ATTR_UBO_SLOT,
|
||||
LayerAttribute,
|
||||
drw_layer_attrs[DRW_RESOURCE_CHUNK_LEN],
|
||||
BATCH)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_object_infos)
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
DEFINE("OBINFO_LIB")
|
||||
STORAGE_BUF(DRW_OBJ_INFOS_SLOT, read, ObjectInfos, drw_infos[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \note Requires draw_object_infos. */
|
||||
GPU_SHADER_CREATE_INFO(draw_object_attributes)
|
||||
DEFINE("OBATTR_LIB")
|
||||
STORAGE_BUF(DRW_OBJ_ATTR_SLOT, read, ObjectAttribute, drw_attrs[])
|
||||
ADDITIONAL_INFO(draw_object_infos)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Geometry Type
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_mesh)
|
||||
ADDITIONAL_INFO(draw_modelmat)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_curves)
|
||||
DEFINE("CURVES_SHADER")
|
||||
DEFINE("DRW_HAIR_INFO")
|
||||
SAMPLER_FREQ(0, samplerBuffer, curves_pos_rad_buf, BATCH)
|
||||
SAMPLER_FREQ(1, isamplerBuffer, curves_indirection_buf, BATCH)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_pointcloud)
|
||||
ADDITIONAL_INFO(draw_object_infos)
|
||||
SAMPLER_FREQ(0, samplerBuffer, ptcloud_pos_rad_tx, BATCH)
|
||||
DEFINE("POINTCLOUD_SHADER")
|
||||
DEFINE("DRW_POINTCLOUD_INFO")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_volume)
|
||||
ADDITIONAL_INFO(draw_modelmat)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_gpencil)
|
||||
TYPEDEF_SOURCE("gpencil_shader_shared.hh")
|
||||
DEFINE("DRW_GPENCIL_INFO")
|
||||
SAMPLER(0, samplerBuffer, gp_pos_tx)
|
||||
SAMPLER(1, samplerBuffer, gp_col_tx)
|
||||
ADDITIONAL_INFO(draw_resource_id_varying)
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
ADDITIONAL_INFO(draw_object_infos)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_object_infos_infos.hh"
|
||||
|
||||
#include "draw_model_lib.glsl"
|
||||
|
||||
#if !defined(OBINFO_LIB) && !defined(GLSL_CPP_STUBS)
|
||||
# error Missing draw_object_infos additional create info on shader create info
|
||||
#endif
|
||||
|
||||
ObjectInfos drw_object_infos()
|
||||
{
|
||||
return buffer_get(draw_object_infos, drw_infos)[drw_resource_id()];
|
||||
}
|
||||
|
||||
/* Convert local coordinates to "Original coordinates" for texture mapping.
|
||||
* This is supposed to only be used if there is no modifier that distort the object.
|
||||
* Otherwise, a geometry attribute should be used instead. */
|
||||
float3 drw_object_orco(float3 lP)
|
||||
{
|
||||
ObjectInfos info = drw_object_infos();
|
||||
return info.orco_add + lP * info.orco_mul;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_object_infos_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(draw_pointcloud)
|
||||
|
||||
#include "draw_defines.hh"
|
||||
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_conversion_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
namespace pointcloud {
|
||||
|
||||
struct Point {
|
||||
float3 P;
|
||||
float radius;
|
||||
/* Point index for attribute loading. */
|
||||
int point_id;
|
||||
/* Position on shape facing the camera. */
|
||||
float3 shape_pos;
|
||||
};
|
||||
|
||||
int point_id_get(uint vert_id)
|
||||
{
|
||||
/* Remove shape indices. */
|
||||
return int(vert_id / uint(DRW_POINTCLOUD_STRIP_TILE_SIZE));
|
||||
}
|
||||
|
||||
/* Return data about the pointcloud point. */
|
||||
Point point_get(uint vert_id)
|
||||
{
|
||||
Point pt;
|
||||
pt.point_id = point_id_get(vert_id);
|
||||
|
||||
auto &buf = sampler_get(draw_pointcloud, ptcloud_pos_rad_tx);
|
||||
float4 pos_rad = texelFetch(buf, pt.point_id);
|
||||
pt.P = pos_rad.xyz;
|
||||
pt.radius = pos_rad.w;
|
||||
pt.shape_pos = float3(NAN_FLT);
|
||||
switch (vert_id % DRW_POINTCLOUD_STRIP_TILE_SIZE) {
|
||||
case 0:
|
||||
pt.shape_pos = float3(-1.0, 0.0, 0.0);
|
||||
break;
|
||||
case 1:
|
||||
pt.shape_pos = float3(0.0, -1.0, 0.0);
|
||||
break;
|
||||
case 2:
|
||||
pt.shape_pos = float3(0.0, 0.0, 1.0);
|
||||
break;
|
||||
case 3:
|
||||
pt.shape_pos = float3(1.0, 0.0, 0.0);
|
||||
break;
|
||||
case 4:
|
||||
pt.shape_pos = float3(0.0, 0.0, 1.0);
|
||||
break;
|
||||
case 5:
|
||||
pt.shape_pos = float3(0.0, 1.0, 0.0);
|
||||
break;
|
||||
case 6:
|
||||
pt.shape_pos = float3(-1.0, 0.0, 0.0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
Point object_to_world(Point pt, float4x4 object_to_world)
|
||||
{
|
||||
pt.P = transform_point(object_to_world, pt.P);
|
||||
pt.radius *= length(to_scale(object_to_world)) * M_SQRT1_3;
|
||||
return pt;
|
||||
}
|
||||
|
||||
struct ShapePoint {
|
||||
/* Position on the shape. */
|
||||
float3 P;
|
||||
/* Shading normal at the position on the shape. */
|
||||
float3 N;
|
||||
};
|
||||
|
||||
float3x3 facing_matrix(const float3 V, const float3 up_axis)
|
||||
{
|
||||
float3x3 facing_mat;
|
||||
facing_mat[2] = V;
|
||||
#ifdef MAT_GEOM_POINTCLOUD
|
||||
if (ptcloud_backface) {
|
||||
facing_mat[2] = -facing_mat[2];
|
||||
}
|
||||
#endif
|
||||
facing_mat[1] = normalize(cross(up_axis, facing_mat[2]));
|
||||
facing_mat[0] = cross(facing_mat[1], facing_mat[2]);
|
||||
return facing_mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the normal of the expanded position in world-space.
|
||||
* \arg pt : world space curve point.
|
||||
* \arg V : world space view vector (toward viewer) at `pt.P`.
|
||||
*/
|
||||
ShapePoint shape_point_get(const Point pt,
|
||||
const float3 V,
|
||||
const float3 up_axis,
|
||||
const eObjectInfoFlag ob_flag)
|
||||
{
|
||||
ShapePoint shape;
|
||||
shape.N = facing_matrix(V, up_axis) * pt.shape_pos;
|
||||
if (flag_test(ob_flag, OBJECT_NEGATIVE_SCALE)) {
|
||||
shape.N = -shape.N;
|
||||
}
|
||||
shape.P = pt.P + shape.N * pt.radius;
|
||||
return shape;
|
||||
}
|
||||
|
||||
float3 get_point_position(const int point_id)
|
||||
{
|
||||
auto &buf = sampler_get(draw_pointcloud, ptcloud_pos_rad_tx);
|
||||
return texelFetch(buf, point_id).xyz;
|
||||
}
|
||||
|
||||
float get_customdata_float(const int point_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, point_id).r;
|
||||
}
|
||||
|
||||
float2 get_customdata_vec2(const int point_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, point_id).rg;
|
||||
}
|
||||
|
||||
float3 get_customdata_vec3(const int point_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, point_id).rgb;
|
||||
}
|
||||
|
||||
float4 get_customdata_vec4(const int point_id, const samplerBuffer cd_buf)
|
||||
{
|
||||
return texelFetch(cd_buf, point_id).rgba;
|
||||
}
|
||||
|
||||
float2 get_barycentric()
|
||||
{
|
||||
/* TODO: To be implemented. */
|
||||
return float2(0.0f);
|
||||
}
|
||||
|
||||
} // namespace pointcloud
|
||||
@@ -0,0 +1,81 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Finish computation of a few draw resource after sync.
|
||||
*/
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
#include "gpu_shader_math_vector_reduce_lib.glsl"
|
||||
#include "gpu_shader_math_vector_safe_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_resource_finalize)
|
||||
|
||||
void main()
|
||||
{
|
||||
uint resource_id = gl_GlobalInvocationID.x;
|
||||
if (resource_id >= uint(resource_len)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float4x4 model_mat = matrix_buf[resource_id].model;
|
||||
ObjectInfos infos = infos_buf[resource_id];
|
||||
ObjectBounds bounds = bounds_buf[resource_id];
|
||||
|
||||
if (drw_bounds_corners_are_valid(bounds)) {
|
||||
/* Convert corners to origin + sides in world space. */
|
||||
float3 p0 = bounds.bounding_corners[0].xyz;
|
||||
float3 p01 = bounds.bounding_corners[1].xyz - p0;
|
||||
float3 p02 = bounds.bounding_corners[2].xyz - p0;
|
||||
float3 p03 = bounds.bounding_corners[3].xyz - p0;
|
||||
/* Avoid flat box. */
|
||||
p01.x = max(p01.x, 1e-4f);
|
||||
p02.y = max(p02.y, 1e-4f);
|
||||
p03.z = max(p03.z, 1e-4f);
|
||||
float3 diagonal = p01 + p02 + p03;
|
||||
float3 center = p0 + diagonal * 0.5f;
|
||||
float min_axis = reduce_min(abs(diagonal));
|
||||
bounds.bounding_sphere.xyz = transform_point(model_mat, center);
|
||||
/* We have to apply scaling to the diagonal. */
|
||||
bounds.bounding_sphere.w = length(transform_direction(model_mat, diagonal)) * 0.5f;
|
||||
bounds._inner_sphere_radius = min_axis;
|
||||
bounds.bounding_corners[0].xyz = transform_point(model_mat, p0);
|
||||
bounds.bounding_corners[1].xyz = transform_direction(model_mat, p01);
|
||||
bounds.bounding_corners[2].xyz = transform_direction(model_mat, p02);
|
||||
bounds.bounding_corners[3].xyz = transform_direction(model_mat, p03);
|
||||
/* Always have correct handedness in the corners vectors. */
|
||||
if (flag_test(infos.flag, OBJECT_NEGATIVE_SCALE)) {
|
||||
bounds.bounding_corners[0].xyz += bounds.bounding_corners[1].xyz;
|
||||
bounds.bounding_corners[1].xyz = -bounds.bounding_corners[1].xyz;
|
||||
}
|
||||
|
||||
/* TODO: Bypass test for very large objects (see #67319). */
|
||||
if (bounds.bounding_sphere.w > 1e12f) {
|
||||
bounds.bounding_sphere.w = -2.0f;
|
||||
}
|
||||
|
||||
/* Bypass culling test for objects that are flattened on one or more axes (see #127774).
|
||||
* Fixing them is too much computation but might be worth doing if a use case for it.
|
||||
* Do not compute the real length to save some instructions. */
|
||||
float3 object_scale = float3(reduce_add(abs(model_mat[0].xyz)),
|
||||
reduce_add(abs(model_mat[1].xyz)),
|
||||
reduce_add(abs(model_mat[2].xyz)));
|
||||
if (any(lessThan(abs(object_scale), float3(1e-10f)))) {
|
||||
bounds.bounding_sphere.w = -2.0f;
|
||||
}
|
||||
|
||||
/* Update bounds. */
|
||||
bounds_buf[resource_id] = bounds;
|
||||
}
|
||||
|
||||
float3 loc = infos.orco_add; /* Box center. */
|
||||
float3 size = infos.orco_mul; /* Box half-extent. */
|
||||
float3 orco_mul = safe_rcp(size * 2.0f);
|
||||
float3 orco_add = (loc - size) * -orco_mul;
|
||||
infos_buf[resource_id].orco_add = orco_add;
|
||||
infos_buf[resource_id].orco_mul = orco_mul;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/**
|
||||
* Geometric shape structures.
|
||||
* Some constructors might seems redundant but are here to make the API cleaner and
|
||||
* allow for more than one constructor per type.
|
||||
*/
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Circle
|
||||
* \{ */
|
||||
|
||||
struct Circle {
|
||||
float2 center;
|
||||
float radius;
|
||||
};
|
||||
|
||||
Circle shape_circle(float2 center, float radius)
|
||||
{
|
||||
Circle circle;
|
||||
circle.center = center;
|
||||
circle.radius = radius;
|
||||
return circle;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Sphere
|
||||
* \{ */
|
||||
|
||||
struct Sphere {
|
||||
float3 center;
|
||||
float radius;
|
||||
};
|
||||
|
||||
Sphere shape_sphere(float3 center, float radius)
|
||||
{
|
||||
Sphere sphere;
|
||||
sphere.center = center;
|
||||
sphere.radius = radius;
|
||||
return sphere;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Box
|
||||
* \{ */
|
||||
|
||||
struct Box {
|
||||
float3 corners[8];
|
||||
};
|
||||
|
||||
/* Construct box from 4 basis points. */
|
||||
Box shape_box(float3 v000, float3 v100, float3 v010, float3 v001)
|
||||
{
|
||||
v100 -= v000;
|
||||
v010 -= v000;
|
||||
v001 -= v000;
|
||||
Box box;
|
||||
box.corners[0] = v000;
|
||||
box.corners[1] = v000 + v100;
|
||||
box.corners[2] = v000 + v010 + v100;
|
||||
box.corners[3] = v000 + v010;
|
||||
box.corners[4] = box.corners[0] + v001;
|
||||
box.corners[5] = box.corners[1] + v001;
|
||||
box.corners[6] = box.corners[2] + v001;
|
||||
box.corners[7] = box.corners[3] + v001;
|
||||
return box;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Square Pyramid
|
||||
* \{ */
|
||||
|
||||
struct Pyramid {
|
||||
/* Apex is the first. Base vertices are in clockwise order from front view. */
|
||||
float3 corners[5];
|
||||
};
|
||||
|
||||
/**
|
||||
* Regular Square Pyramid (can be oblique).
|
||||
* Use this corner order.
|
||||
* (Top-Down View of the pyramid)
|
||||
* <pre>
|
||||
*
|
||||
* Y
|
||||
* |
|
||||
* |
|
||||
* .-----X
|
||||
*
|
||||
* 4-----------3
|
||||
* | \ / |
|
||||
* | \ / |
|
||||
* | 0 |
|
||||
* | / \ |
|
||||
* | / \ |
|
||||
* 1-----------2
|
||||
* </pre>
|
||||
* base_corner_00 is vertex 1
|
||||
* base_corner_01 is vertex 2
|
||||
* base_corner_10 is vertex 4
|
||||
*/
|
||||
Pyramid shape_pyramid(float3 apex,
|
||||
float3 base_corner_00,
|
||||
float3 base_corner_01,
|
||||
float3 base_corner_10)
|
||||
{
|
||||
Pyramid pyramid;
|
||||
pyramid.corners[0] = apex;
|
||||
pyramid.corners[1] = base_corner_00;
|
||||
pyramid.corners[2] = base_corner_01;
|
||||
pyramid.corners[3] = base_corner_10 + (base_corner_01 - base_corner_00);
|
||||
pyramid.corners[4] = base_corner_10;
|
||||
return pyramid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular Square Pyramid.
|
||||
* <pre>
|
||||
*
|
||||
* Y
|
||||
* |
|
||||
* |
|
||||
* .-----X
|
||||
*
|
||||
* 4-----Y-----3
|
||||
* | \ | / |
|
||||
* | \ | / |
|
||||
* | 0-----X
|
||||
* | / \ |
|
||||
* | / \ |
|
||||
* 1-----------2
|
||||
* </pre>
|
||||
* base_center_pos_x is vector from base center to X
|
||||
* base_center_pos_y is vector from base center to Y
|
||||
*/
|
||||
Pyramid shape_pyramid_non_oblique(float3 apex,
|
||||
float3 base_center,
|
||||
float3 base_center_pos_x,
|
||||
float3 base_center_pos_y)
|
||||
{
|
||||
Pyramid pyramid;
|
||||
pyramid.corners[0] = apex;
|
||||
pyramid.corners[1] = base_center - base_center_pos_x - base_center_pos_y;
|
||||
pyramid.corners[2] = base_center + base_center_pos_x - base_center_pos_y;
|
||||
pyramid.corners[3] = base_center + base_center_pos_x + base_center_pos_y;
|
||||
pyramid.corners[4] = base_center - base_center_pos_x + base_center_pos_y;
|
||||
return pyramid;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Frustum
|
||||
* \{ */
|
||||
|
||||
struct Frustum {
|
||||
float3 corners[8];
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this corner order.
|
||||
* <pre>
|
||||
*
|
||||
* Z Y
|
||||
* | /
|
||||
* |/
|
||||
* .-----X
|
||||
* 2----------6
|
||||
* /| /|
|
||||
* / | / |
|
||||
* 1----------5 |
|
||||
* | | | |
|
||||
* | 3-------|--7
|
||||
* | / | /
|
||||
* |/ |/
|
||||
* 0----------4
|
||||
* </pre>
|
||||
*/
|
||||
Frustum shape_frustum(float3 corners[8])
|
||||
{
|
||||
Frustum frustum;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
frustum.corners[i] = corners[i];
|
||||
}
|
||||
return frustum;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Cone
|
||||
* \{ */
|
||||
|
||||
/* Cone at origin with no height. */
|
||||
struct Cone {
|
||||
float3 direction;
|
||||
float angle_cos;
|
||||
};
|
||||
|
||||
Cone shape_cone(float3 direction, float angle_cosine)
|
||||
{
|
||||
return {direction, angle_cosine};
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct View {
|
||||
[[uniform(DRW_VIEW_UBO_SLOT)]] const ViewMatrices (&view_buf)[64];
|
||||
|
||||
/* `view_id` should be the result of `draw::ID::view_id()` or manually indexed view. */
|
||||
ViewMatrices get(uint view_id) const
|
||||
{
|
||||
return view_buf[view_id];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace draw
|
||||
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
void view_clipping_distances([[maybe_unused]] float3 wpos)
|
||||
{
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
VERTEX_SHADER_CREATE_INFO(drw_clipped)
|
||||
/* WORKAROUND(fclem): Allow both legacy and SRT code-path to coexist.
|
||||
* Metal back-end checks for this exact condition to declare gl_ClipDistance and not pay the
|
||||
* price of enabling clip distance. This is something to fix at some point. */
|
||||
# if defined(SRT_CONSTANT_use_clipping) ? (SRT_CONSTANT_use_clipping == 1) : \
|
||||
defined(USE_WORLD_CLIP_PLANES)
|
||||
float4 pos_4d = float4(wpos, 1.0f);
|
||||
gl_ClipDistance[0] = dot(drw_clipping_[0], pos_4d);
|
||||
gl_ClipDistance[1] = dot(drw_clipping_[1], pos_4d);
|
||||
gl_ClipDistance[2] = dot(drw_clipping_[2], pos_4d);
|
||||
gl_ClipDistance[3] = dot(drw_clipping_[3], pos_4d);
|
||||
gl_ClipDistance[4] = dot(drw_clipping_[4], pos_4d);
|
||||
gl_ClipDistance[5] = dot(drw_clipping_[5], pos_4d);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void view_clipping_distances_bypass()
|
||||
{
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
VERTEX_SHADER_CREATE_INFO(drw_clipped)
|
||||
/* WORKAROUND(fclem): Allow both legacy and SRT code-path to coexist.
|
||||
* Metal back-end checks for this exact condition to declare gl_ClipDistance and not pay the
|
||||
* price of enabling clip distance. This is something to fix at some point. */
|
||||
# if defined(SRT_CONSTANT_use_clipping) ? (SRT_CONSTANT_use_clipping == 1) : \
|
||||
defined(USE_WORLD_CLIP_PLANES)
|
||||
gl_ClipDistance[0] = 1.0f;
|
||||
gl_ClipDistance[1] = 1.0f;
|
||||
gl_ClipDistance[2] = 1.0f;
|
||||
gl_ClipDistance[3] = 1.0f;
|
||||
gl_ClipDistance[4] = 1.0f;
|
||||
gl_ClipDistance[5] = 1.0f;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Compute culling data for each views of a given view buffer.
|
||||
*/
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
#include "draw_view_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_transform_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_view_finalize)
|
||||
|
||||
void projmat_dimensions(float4x4 winmat,
|
||||
float &r_left,
|
||||
float &r_right,
|
||||
float &r_bottom,
|
||||
float &r_top,
|
||||
float &r_near,
|
||||
float &r_far)
|
||||
{
|
||||
const bool is_persp = winmat[3][3] == 0.0f;
|
||||
if (is_persp) {
|
||||
float near = winmat[3][2] / (winmat[2][2] - 1.0f);
|
||||
r_left = near * ((winmat[2][0] - 1.0f) / winmat[0][0]);
|
||||
r_right = near * ((winmat[2][0] + 1.0f) / winmat[0][0]);
|
||||
r_bottom = near * ((winmat[2][1] - 1.0f) / winmat[1][1]);
|
||||
r_top = near * ((winmat[2][1] + 1.0f) / winmat[1][1]);
|
||||
r_near = near;
|
||||
r_far = winmat[3][2] / (winmat[2][2] + 1.0f);
|
||||
}
|
||||
else {
|
||||
r_left = (-winmat[3][0] - 1.0f) / winmat[0][0];
|
||||
r_right = (-winmat[3][0] + 1.0f) / winmat[0][0];
|
||||
r_bottom = (-winmat[3][1] - 1.0f) / winmat[1][1];
|
||||
r_top = (-winmat[3][1] + 1.0f) / winmat[1][1];
|
||||
r_near = (winmat[3][2] + 1.0f) / winmat[2][2];
|
||||
r_far = (winmat[3][2] - 1.0f) / winmat[2][2];
|
||||
}
|
||||
}
|
||||
|
||||
void frustum_boundbox_calc(float4x4 winmat, float4x4 viewinv, FrustumCorners &frustum_corners)
|
||||
{
|
||||
float left = 0.0f, right = 0.0f, bottom = 0.0f, top = 0.0f, near = 0.0f, far = 0.0f;
|
||||
bool is_persp = winmat[3][3] == 0.0f;
|
||||
|
||||
projmat_dimensions(winmat, left, right, bottom, top, near, far);
|
||||
|
||||
frustum_corners.corners[0][2] = frustum_corners.corners[3][2] = frustum_corners.corners[7][2] =
|
||||
frustum_corners.corners[4][2] = -near;
|
||||
frustum_corners.corners[0][0] = frustum_corners.corners[3][0] = left;
|
||||
frustum_corners.corners[4][0] = frustum_corners.corners[7][0] = right;
|
||||
frustum_corners.corners[0][1] = frustum_corners.corners[4][1] = bottom;
|
||||
frustum_corners.corners[7][1] = frustum_corners.corners[3][1] = top;
|
||||
|
||||
/* Get the coordinates of the far plane. */
|
||||
if (is_persp) {
|
||||
float sca_far = far / near;
|
||||
left *= sca_far;
|
||||
right *= sca_far;
|
||||
bottom *= sca_far;
|
||||
top *= sca_far;
|
||||
}
|
||||
|
||||
frustum_corners.corners[1][2] = frustum_corners.corners[2][2] = frustum_corners.corners[6][2] =
|
||||
frustum_corners.corners[5][2] = -far;
|
||||
frustum_corners.corners[1][0] = frustum_corners.corners[2][0] = left;
|
||||
frustum_corners.corners[6][0] = frustum_corners.corners[5][0] = right;
|
||||
frustum_corners.corners[1][1] = frustum_corners.corners[5][1] = bottom;
|
||||
frustum_corners.corners[2][1] = frustum_corners.corners[6][1] = top;
|
||||
|
||||
/* Transform into world space. */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
frustum_corners.corners[i].xyz = transform_point(viewinv, frustum_corners.corners[i].xyz);
|
||||
}
|
||||
}
|
||||
|
||||
void planes_from_projmat(float4x4 mat, FrustumPlanes &frustum_planes)
|
||||
{
|
||||
/* References:
|
||||
*
|
||||
* https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/
|
||||
* http://www8.cs.umu.se/kurser/5DV051/HT12/lab/plane_extraction.pdf
|
||||
*/
|
||||
mat = transpose(mat);
|
||||
frustum_planes.planes[0] = mat[3] + mat[0];
|
||||
frustum_planes.planes[1] = mat[3] - mat[0];
|
||||
frustum_planes.planes[2] = mat[3] + mat[1];
|
||||
frustum_planes.planes[3] = mat[3] - mat[1];
|
||||
frustum_planes.planes[4] = mat[3] + mat[2];
|
||||
frustum_planes.planes[5] = mat[3] - mat[2];
|
||||
}
|
||||
|
||||
void frustum_culling_planes_calc(float4x4 winmat, float4x4 viewmat, FrustumPlanes &frustum_planes)
|
||||
{
|
||||
float4x4 persmat = winmat * viewmat;
|
||||
planes_from_projmat(persmat, frustum_planes);
|
||||
|
||||
/* Normalize. */
|
||||
for (int p = 0; p < 6; p++) {
|
||||
frustum_planes.planes[p] /= length(frustum_planes.planes[p].xyz);
|
||||
}
|
||||
}
|
||||
|
||||
float4 frustum_culling_sphere_calc(FrustumCorners frustum_corners)
|
||||
{
|
||||
/* Extract Bounding Sphere */
|
||||
/* TODO(fclem): This is significantly less precise than CPU, but it isn't used in most cases. */
|
||||
|
||||
float4 bsphere;
|
||||
bsphere.xyz = (frustum_corners.corners[0].xyz + frustum_corners.corners[6].xyz) * 0.5f;
|
||||
bsphere.w = 0.0f;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bsphere.w = max(bsphere.w, distance(bsphere.xyz, frustum_corners.corners[i].xyz));
|
||||
}
|
||||
return bsphere;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
drw_view_id = gl_LocalInvocationID.x;
|
||||
|
||||
/* Invalid views are disabled. */
|
||||
if (all(equal(drw_view().viewinv[2].xyz, float3(0.0f)))) {
|
||||
/* Views with negative radius are treated as disabled. */
|
||||
view_culling_buf[drw_view_id].bound_sphere = float4(-1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read frustom_corners from device memory, update, and write back. */
|
||||
FrustumCorners frustum_corners = view_culling_buf[drw_view_id].frustum_corners;
|
||||
frustum_boundbox_calc(drw_view().winmat, drw_view().viewinv, frustum_corners);
|
||||
view_culling_buf[drw_view_id].frustum_corners = frustum_corners;
|
||||
|
||||
/* Read frustum_planes from device memory, update, and write back. */
|
||||
FrustumPlanes frustum_planes = view_culling_buf[drw_view_id].frustum_planes;
|
||||
frustum_culling_planes_calc(drw_view().winmat, drw_view().viewmat, frustum_planes);
|
||||
|
||||
view_culling_buf[drw_view_id].frustum_planes = frustum_planes;
|
||||
view_culling_buf[drw_view_id].bound_sphere = frustum_culling_sphere_calc(frustum_corners);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "draw_defines.hh"
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "draw_command_shared.hh"
|
||||
# include "draw_shader_shared.hh"
|
||||
#endif
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
/* Define stub defines for C++ test compilation. */
|
||||
# define DRAW_VIEW_CREATE_INFO
|
||||
# define DRW_VIEW_CULLING_INFO
|
||||
# define USE_WORLD_CLIP_PLANES
|
||||
|
||||
# define DRW_VIEW_LEN DRW_VIEW_MAX
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Resource ID
|
||||
* This is used to fetch per object data in drw_matrices and other object indexed buffers.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Used if the resource index needs to be passed to the fragment shader.
|
||||
* IMPORTANT: Vertex shader need to write `drw_ResourceID_iface.resource_id` in main().
|
||||
*/
|
||||
GPU_SHADER_NAMED_INTERFACE_INFO(draw_resource_id_iface, drw_ResourceID_iface)
|
||||
FLAT(uint, resource_id)
|
||||
GPU_SHADER_NAMED_INTERFACE_END(drw_ResourceID_iface)
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_resource_id_varying)
|
||||
VERTEX_OUT(draw_resource_id_iface)
|
||||
GEOMETRY_OUT(draw_resource_id_iface)
|
||||
DEFINE("RESOURCE_ID_VARYING")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_resource_id)
|
||||
STORAGE_BUF(DRW_RESOURCE_ID_SLOT, read, uint, res_id_buf[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_resource_with_custom_id)
|
||||
DEFINE("WITH_CUSTOM_IDS")
|
||||
STORAGE_BUF(DRW_RESOURCE_ID_SLOT, read, uint2, res_id_with_custom_id_buf[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw Object Resources
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_modelmat_common)
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
STORAGE_BUF(DRW_OBJ_MAT_SLOT, read, ObjectMatrices, drw_matrix_buf[])
|
||||
DEFINE("DRAW_MODELMAT_CREATE_INFO")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_modelmat)
|
||||
ADDITIONAL_INFO(draw_modelmat_common)
|
||||
ADDITIONAL_INFO(draw_resource_id)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_modelmat_with_custom_id)
|
||||
ADDITIONAL_INFO(draw_modelmat_common)
|
||||
ADDITIONAL_INFO(draw_resource_with_custom_id)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw View
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_view)
|
||||
UNIFORM_BUF_FREQ(DRW_VIEW_UBO_SLOT, ViewMatrices, view_buf[64], PASS)
|
||||
DEFINE("DRAW_VIEW_CREATE_INFO")
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_view_culling)
|
||||
UNIFORM_BUF(DRW_VIEW_CULLING_UBO_SLOT, ViewCullingData, drw_view_culling_buf[64])
|
||||
DEFINE("DRW_VIEW_CULLING_INFO")
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw View
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(drw_clipped)
|
||||
/* TODO(fclem): Move to engine side. */
|
||||
UNIFORM_BUF_FREQ(DRW_CLIPPING_UBO_SLOT, float4, drw_clipping_[6], PASS)
|
||||
BUILTINS(BuiltinBits::CLIP_DISTANCES)
|
||||
DEFINE("USE_WORLD_CLIP_PLANES")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Draw Manager usage
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_resource_finalize)
|
||||
DO_STATIC_COMPILATION()
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
DEFINE("DRAW_FINALIZE_SHADER")
|
||||
LOCAL_GROUP_SIZE(DRW_FINALIZE_GROUP_SIZE)
|
||||
STORAGE_BUF(0, read, ObjectMatrices, matrix_buf[])
|
||||
STORAGE_BUF(1, read_write, ObjectBounds, bounds_buf[])
|
||||
STORAGE_BUF(2, read_write, ObjectInfos, infos_buf[])
|
||||
PUSH_CONSTANT(int, resource_len)
|
||||
COMPUTE_SOURCE("draw_resource_finalize_comp.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_view_finalize)
|
||||
DO_STATIC_COMPILATION()
|
||||
LOCAL_GROUP_SIZE(DRW_VIEW_MAX)
|
||||
DEFINE_VALUE("DRW_VIEW_LEN", STRINGIFY(DRW_VIEW_MAX))
|
||||
STORAGE_BUF(0, read_write, ViewCullingData, view_culling_buf[DRW_VIEW_LEN])
|
||||
COMPUTE_SOURCE("draw_view_finalize_comp.glsl")
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_visibility_compute)
|
||||
DO_STATIC_COMPILATION()
|
||||
LOCAL_GROUP_SIZE(DRW_VISIBILITY_GROUP_SIZE)
|
||||
DEFINE_VALUE("DRW_VIEW_LEN", STRINGIFY(DRW_VIEW_MAX))
|
||||
STORAGE_BUF(0, read, ObjectBounds, bounds_buf[])
|
||||
STORAGE_BUF(1, read_write, uint, visibility_buf[])
|
||||
PUSH_CONSTANT(int, resource_len)
|
||||
PUSH_CONSTANT(int, view_len)
|
||||
PUSH_CONSTANT(int, visibility_word_per_draw)
|
||||
COMPUTE_SOURCE("draw_visibility_comp.glsl")
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
ADDITIONAL_INFO(draw_view_culling)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_command_generate)
|
||||
DO_STATIC_COMPILATION()
|
||||
TYPEDEF_SOURCE("draw_shader_shared.hh")
|
||||
TYPEDEF_SOURCE("draw_command_shared.hh")
|
||||
LOCAL_GROUP_SIZE(DRW_COMMAND_GROUP_SIZE)
|
||||
STORAGE_BUF(0, read_write, DrawGroup, group_buf[])
|
||||
STORAGE_BUF(1, read, uint, visibility_buf[])
|
||||
STORAGE_BUF(2, read, DrawPrototype, prototype_buf[])
|
||||
STORAGE_BUF(3, write, DrawCommand, command_buf[])
|
||||
STORAGE_BUF(DRW_RESOURCE_ID_SLOT, write, uint, resource_id_buf[])
|
||||
PUSH_CONSTANT(int, prototype_len)
|
||||
PUSH_CONSTANT(int, visibility_word_per_draw)
|
||||
PUSH_CONSTANT(int, view_shift)
|
||||
PUSH_CONSTANT(int, view_len)
|
||||
PUSH_CONSTANT(bool, use_custom_ids)
|
||||
COMPUTE_SOURCE("draw_command_generate_comp.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* Stub needs to be after all definitions to avoid conflict with legacy definitions. */
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
/* Make it work for both draw_resource_id and draw_resource_with_custom_id. */
|
||||
# define resource_id_buf uint2(0)
|
||||
#endif
|
||||
@@ -0,0 +1,213 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(draw_view)
|
||||
|
||||
#if !defined(DRAW_VIEW_CREATE_INFO) && !defined(GLSL_CPP_STUBS)
|
||||
#else
|
||||
|
||||
/* Returns the current active view. */
|
||||
ViewMatrices drw_view()
|
||||
{
|
||||
|
||||
return buffer_get(draw_view, view_buf)[drw_view_id];
|
||||
}
|
||||
|
||||
/* Returns true if the current view has a perspective projection matrix. */
|
||||
bool drw_view_is_perspective()
|
||||
{
|
||||
return drw_view().winmat[3][3] == 0.0f;
|
||||
}
|
||||
|
||||
/* Returns the view forward vector, going towards the viewer. */
|
||||
float3 drw_view_forward()
|
||||
{
|
||||
return drw_view().viewinv[2].xyz;
|
||||
}
|
||||
|
||||
/* Returns the view up vector. */
|
||||
float3 drw_view_up()
|
||||
{
|
||||
return drw_view().viewinv[1].xyz;
|
||||
}
|
||||
|
||||
/* Returns the view origin. */
|
||||
float3 drw_view_position()
|
||||
{
|
||||
return drw_view().viewinv[3].xyz;
|
||||
}
|
||||
|
||||
/* Positive Z distance from the view origin. Faster than using `drw_point_world_to_view`. */
|
||||
float drw_view_z_distance(float3 P)
|
||||
{
|
||||
return dot(P - drw_view_position(), -drw_view_forward());
|
||||
}
|
||||
|
||||
/* Returns the projection matrix far clip distance. */
|
||||
float drw_view_far()
|
||||
{
|
||||
if (drw_view_is_perspective()) {
|
||||
return -drw_view().winmat[3][2] / (drw_view().winmat[2][2] + 1.0f);
|
||||
}
|
||||
return -(drw_view().winmat[3][2] - 1.0f) / drw_view().winmat[2][2];
|
||||
}
|
||||
|
||||
/* Returns the projection matrix near clip distance. */
|
||||
float drw_view_near()
|
||||
{
|
||||
if (drw_view_is_perspective()) {
|
||||
return -drw_view().winmat[3][2] / (drw_view().winmat[2][2] - 1.0f);
|
||||
}
|
||||
return -(drw_view().winmat[3][2] + 1.0f) / drw_view().winmat[2][2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world incident vector `V` (going towards the viewer)
|
||||
* from the world position `P` and the current view.
|
||||
*/
|
||||
float3 drw_world_incident_vector(float3 P)
|
||||
{
|
||||
return drw_view_is_perspective() ? normalize(drw_view_position() - P) : drw_view_forward();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view incident vector `vV` (going towards the viewer)
|
||||
* from the view position `vP` and the current view.
|
||||
*/
|
||||
float3 drw_view_incident_vector(float3 vP)
|
||||
{
|
||||
return drw_view_is_perspective() ? normalize(-vP) : float3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform position on screen UV space [0..1] to Normalized Device Coordinate space [-1..1].
|
||||
*/
|
||||
float3 drw_screen_to_ndc(float3 ss_P)
|
||||
{
|
||||
return ss_P * 2.0f - 1.0f;
|
||||
}
|
||||
float2 drw_screen_to_ndc(float2 ss_P)
|
||||
{
|
||||
return ss_P * 2.0f - 1.0f;
|
||||
}
|
||||
float drw_screen_to_ndc(float ss_P)
|
||||
{
|
||||
return ss_P * 2.0f - 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform position in Normalized Device Coordinate [-1..1] to screen UV space [0..1].
|
||||
*/
|
||||
float3 drw_ndc_to_screen(float3 ndc_P)
|
||||
{
|
||||
return ndc_P * 0.5f + 0.5f;
|
||||
}
|
||||
float2 drw_ndc_to_screen(float2 ndc_P)
|
||||
{
|
||||
return ndc_P * 0.5f + 0.5f;
|
||||
}
|
||||
float drw_ndc_to_screen(float ndc_P)
|
||||
{
|
||||
return ndc_P * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Normal
|
||||
* \{ */
|
||||
|
||||
float3 drw_normal_view_to_world(float3 vN)
|
||||
{
|
||||
return (to_float3x3(drw_view().viewinv) * vN);
|
||||
}
|
||||
|
||||
float3 drw_normal_world_to_view(float3 N)
|
||||
{
|
||||
return (to_float3x3(drw_view().viewmat) * N);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Position
|
||||
* \{ */
|
||||
|
||||
float3 drw_perspective_divide(float4 hs_P)
|
||||
{
|
||||
return hs_P.xyz / hs_P.w;
|
||||
}
|
||||
|
||||
float3 drw_point_view_to_world(float3 vP)
|
||||
{
|
||||
return (drw_view().viewinv * float4(vP, 1.0f)).xyz;
|
||||
}
|
||||
float4 drw_point_view_to_homogenous(float3 vP)
|
||||
{
|
||||
return (drw_view().winmat * float4(vP, 1.0f));
|
||||
}
|
||||
float3 drw_point_view_to_ndc(float3 vP)
|
||||
{
|
||||
return drw_perspective_divide(drw_point_view_to_homogenous(vP));
|
||||
}
|
||||
|
||||
float3 drw_point_world_to_view(float3 P)
|
||||
{
|
||||
return (drw_view().viewmat * float4(P, 1.0f)).xyz;
|
||||
}
|
||||
float4 drw_point_world_to_homogenous(float3 P)
|
||||
{
|
||||
return (drw_view().winmat * (drw_view().viewmat * float4(P, 1.0f)));
|
||||
}
|
||||
float3 drw_point_world_to_ndc(float3 P)
|
||||
{
|
||||
return drw_perspective_divide(drw_point_world_to_homogenous(P));
|
||||
}
|
||||
|
||||
float3 drw_point_ndc_to_view(float3 ssP)
|
||||
{
|
||||
return drw_perspective_divide(drw_view().wininv * float4(ssP, 1.0f));
|
||||
}
|
||||
float3 drw_point_ndc_to_world(float3 ssP)
|
||||
{
|
||||
return drw_point_view_to_world(drw_point_ndc_to_view(ssP));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Screen Positions
|
||||
* \{ */
|
||||
|
||||
float3 drw_point_view_to_screen(float3 vP)
|
||||
{
|
||||
return drw_ndc_to_screen(drw_point_view_to_ndc(vP));
|
||||
}
|
||||
float3 drw_point_world_to_screen(float3 vP)
|
||||
{
|
||||
return drw_ndc_to_screen(drw_point_world_to_ndc(vP));
|
||||
}
|
||||
|
||||
float3 drw_point_screen_to_view(float3 ssP)
|
||||
{
|
||||
return drw_point_ndc_to_view(drw_screen_to_ndc(ssP));
|
||||
}
|
||||
float3 drw_point_screen_to_world(float3 ssP)
|
||||
{
|
||||
return drw_point_view_to_world(drw_point_screen_to_view(ssP));
|
||||
}
|
||||
|
||||
float drw_depth_view_to_screen(float v_depth)
|
||||
{
|
||||
return drw_point_view_to_screen(float3(0.0f, 0.0f, v_depth)).z;
|
||||
}
|
||||
float drw_depth_screen_to_view(float ss_depth)
|
||||
{
|
||||
return drw_point_screen_to_view(float3(0.0f, 0.0f, ss_depth)).z;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_view_lib.glsl"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
#include "gpu_shader_math_vector_reduce_lib.glsl"
|
||||
#include "gpu_shader_math_vector_safe_lib.glsl"
|
||||
|
||||
/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/.
|
||||
* This samples the depth buffer 4 time for each direction to get the most correct
|
||||
* implicit normal reconstruction out of the depth buffer. */
|
||||
float3 view_position_derivative_from_depth(sampler2D scene_depth_tx,
|
||||
int2 extent,
|
||||
float2 uv,
|
||||
int2 texel,
|
||||
int2 offset,
|
||||
float3 vP,
|
||||
float depth_center)
|
||||
{
|
||||
float4 H;
|
||||
H.x = texelFetch(scene_depth_tx, texel - offset * 2, 0).r;
|
||||
H.y = texelFetch(scene_depth_tx, texel - offset, 0).r;
|
||||
H.z = texelFetch(scene_depth_tx, texel + offset, 0).r;
|
||||
H.w = texelFetch(scene_depth_tx, texel + offset * 2, 0).r;
|
||||
|
||||
float2 uv_offset = float2(offset) / float2(extent);
|
||||
float2 uv1 = uv - uv_offset * 2.0f;
|
||||
float2 uv2 = uv - uv_offset;
|
||||
float2 uv3 = uv + uv_offset;
|
||||
float2 uv4 = uv + uv_offset * 2.0f;
|
||||
|
||||
/* Fix issue with depth precision. Take even larger diff. */
|
||||
float4 diff = abs(float4(depth_center, H.yzw) - H.x);
|
||||
if (reduce_max(diff) < 2.4e-7f && all(lessThan(diff.xyz, diff.www))) {
|
||||
float3 P1 = drw_point_screen_to_view(float3(uv1, H.x));
|
||||
float3 P3 = drw_point_screen_to_view(float3(uv3, H.w));
|
||||
return 0.25f * (P3 - P1);
|
||||
}
|
||||
/* Simplified (H.xw + 2.0f * (H.yz - H.xw)) - depth_center */
|
||||
float2 deltas = abs((2.0f * H.yz - H.xw) - depth_center);
|
||||
if (deltas.x < deltas.y) {
|
||||
return vP - drw_point_screen_to_view(float3(uv2, H.y));
|
||||
}
|
||||
return drw_point_screen_to_view(float3(uv3, H.z)) - vP;
|
||||
}
|
||||
|
||||
struct SurfaceReconstructResult {
|
||||
/* View position. */
|
||||
float3 vP;
|
||||
/* View geometric normal. */
|
||||
float3 vNg;
|
||||
/* Screen depth [0..1]. Corresponding to the depth buffer value. */
|
||||
float depth;
|
||||
/* True if the pixel has background depth. */
|
||||
bool is_background;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reconstruct surface information from the depth buffer.
|
||||
* Use adjacent pixel info to reconstruct normals.
|
||||
*
|
||||
* \a extent is the valid region of depth_tx.
|
||||
* \a texel is the pixel coordinate [0..extent-1] to reconstruct.
|
||||
*/
|
||||
SurfaceReconstructResult view_reconstruct_from_depth(sampler2D scene_depth_tx,
|
||||
int2 extent,
|
||||
int2 texel)
|
||||
{
|
||||
SurfaceReconstructResult result;
|
||||
result.depth = texelFetch(scene_depth_tx, texel, 0).r;
|
||||
result.is_background = (result.depth == 1.0f);
|
||||
float2 uv = (float2(texel) + float2(0.5f)) / float2(extent);
|
||||
result.vP = drw_point_screen_to_view(float3(uv, result.depth));
|
||||
if (result.is_background) {
|
||||
result.vNg = drw_view_incident_vector(result.vP);
|
||||
return result;
|
||||
}
|
||||
float3 dPdx = view_position_derivative_from_depth(
|
||||
scene_depth_tx, extent, uv, texel, int2(1, 0), result.vP, result.depth);
|
||||
float3 dPdy = view_position_derivative_from_depth(
|
||||
scene_depth_tx, extent, uv, texel, int2(0, 1), result.vP, result.depth);
|
||||
result.vNg = safe_normalize(cross(dPdx, dPdy));
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Compute visibility of each resource bounds for a given view.
|
||||
*/
|
||||
/* TODO(fclem): This could be augmented by a 2 pass occlusion culling system. */
|
||||
|
||||
#include "draw_view_infos.hh"
|
||||
|
||||
#include "draw_intersect_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(draw_visibility_compute)
|
||||
|
||||
void mask_visibility_bit(uint view_id)
|
||||
{
|
||||
if (view_len > 1) {
|
||||
uint index = gl_GlobalInvocationID.x * uint(visibility_word_per_draw) + (view_id / 32u);
|
||||
visibility_buf[index] &= ~(1u << (view_id & 31u));
|
||||
}
|
||||
else {
|
||||
atomicAnd(visibility_buf[gl_WorkGroupID.x], ~(1u << gl_LocalInvocationID.x));
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (int(gl_GlobalInvocationID.x) >= resource_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectBounds bounds = bounds_buf[gl_GlobalInvocationID.x];
|
||||
|
||||
if (drw_bounds_are_valid(bounds)) {
|
||||
IsectBox box = isect_box_setup(bounds.bounding_corners[0].xyz,
|
||||
bounds.bounding_corners[1].xyz,
|
||||
bounds.bounding_corners[2].xyz,
|
||||
bounds.bounding_corners[3].xyz);
|
||||
Sphere bounding_sphere = shape_sphere(bounds.bounding_sphere.xyz, bounds.bounding_sphere.w);
|
||||
Sphere inscribed_sphere = shape_sphere(bounds.bounding_sphere.xyz,
|
||||
bounds._inner_sphere_radius);
|
||||
|
||||
for (uint view_id = 0u; view_id < uint(view_len); view_id++) {
|
||||
if (drw_view_culling(view_id).bound_sphere.w == -1.0f) {
|
||||
/* View disabled. */
|
||||
mask_visibility_bit(view_id);
|
||||
}
|
||||
else if (intersect_view(inscribed_sphere, view_id) == true) {
|
||||
/* Visible. */
|
||||
}
|
||||
else if (intersect_view(bounding_sphere, view_id) == false) {
|
||||
/* Not visible. */
|
||||
mask_visibility_bit(view_id);
|
||||
}
|
||||
else if (intersect_view(box, view_id) == false) {
|
||||
/* Not visible. */
|
||||
mask_visibility_bit(view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Culling is disabled, but we need to mask the bits for disabled views. */
|
||||
for (uint view_id = 0u; view_id < uint(view_len); view_id++) {
|
||||
if (drw_view_culling(view_id).bound_sphere.w == -1.0f) {
|
||||
/* View disabled. */
|
||||
mask_visibility_bit(view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* File that contains the output of `openSubdiv_getGLSLPatchBasisSource`.
|
||||
* The structures here are copy of the latest version only to satisfy building without OSL enabled.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* This file must replaced at runtime. The following content is only a possible implementation. */
|
||||
#pragma runtime_generated
|
||||
|
||||
struct [[host_shared]] OsdPatchParam {
|
||||
int field0;
|
||||
int field1;
|
||||
float sharpness;
|
||||
};
|
||||
|
||||
struct [[host_shared]] OsdPatchArray {
|
||||
int regDesc;
|
||||
int desc;
|
||||
int numPatches;
|
||||
int indexBase;
|
||||
int stride;
|
||||
int primitiveIdBase;
|
||||
};
|
||||
|
||||
struct [[host_shared]] OsdPatchCoord {
|
||||
int arrayIndex;
|
||||
int patchIndex;
|
||||
int vertIndex;
|
||||
float s;
|
||||
float t;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* File that contains dynamic defines based on the actual used GPU backend.
|
||||
*
|
||||
* NOTE: file must be included before `osd_patch_basis.glsl`.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* Ensure the basis code has access to proper backend specification define: it is not guaranteed
|
||||
* that the code provided by OpenSubdiv specifies it. For example, it doesn't for GLSL but it
|
||||
* does for Metal. Additionally, for Metal OpenSubdiv defines OSD_PATCH_BASIS_METAL as 1, so do
|
||||
* the same here to avoid possible warning about value being re-defined. */
|
||||
#ifdef GPU_METAL
|
||||
# define OSD_PATCH_BASIS_METAL 1
|
||||
#else
|
||||
# define OSD_PATCH_BASIS_GLSL
|
||||
#endif
|
||||
@@ -0,0 +1,230 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_custom_data_interp_4d_f32)
|
||||
|
||||
#if defined(DIMENSIONS_1)
|
||||
# define DIMENSIONS 1
|
||||
#elif defined(DIMENSIONS_2)
|
||||
# define DIMENSIONS 2
|
||||
#elif defined(DIMENSIONS_3)
|
||||
# define DIMENSIONS 3
|
||||
#else // defined(DIMENSIONS_4)
|
||||
# define DIMENSIONS 4
|
||||
#endif
|
||||
|
||||
struct Vertex {
|
||||
float vertex_data[DIMENSIONS];
|
||||
};
|
||||
|
||||
void clear(Vertex &v)
|
||||
{
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
v.vertex_data[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Vertex read_vertex(uint index)
|
||||
{
|
||||
Vertex result;
|
||||
#if defined(GPU_COMP_U16)
|
||||
uint base_index = index * 2;
|
||||
if (DIMENSIONS == 4) {
|
||||
uint xy = src_data[base_index];
|
||||
uint zw = src_data[base_index + 1];
|
||||
|
||||
float x = float((xy >> 16) & 0xffff) / 65535.0f;
|
||||
float y = float(xy & 0xffff) / 65535.0f;
|
||||
float z = float((zw >> 16) & 0xffff) / 65535.0f;
|
||||
float w = float(zw & 0xffff) / 65535.0f;
|
||||
|
||||
result.vertex_data[0] = x;
|
||||
result.vertex_data[1] = y;
|
||||
result.vertex_data[2] = z;
|
||||
result.vertex_data[3] = w;
|
||||
}
|
||||
else {
|
||||
/* This case is unsupported for now. */
|
||||
clear(result);
|
||||
}
|
||||
#elif defined(GPU_COMP_I32)
|
||||
uint base_index = index * DIMENSIONS;
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
result.vertex_data[i] = float(src_data[base_index + i]);
|
||||
}
|
||||
#else
|
||||
uint base_index = index * DIMENSIONS;
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
result.vertex_data[i] = src_data[base_index + uint(i)];
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void write_vertex(uint index, Vertex v)
|
||||
{
|
||||
#if defined(GPU_COMP_U16)
|
||||
uint base_index = shader_data.dst_offset + index * 2;
|
||||
if (DIMENSIONS == 4) {
|
||||
uint x = uint(v.vertex_data[0] * 65535.0f);
|
||||
uint y = uint(v.vertex_data[1] * 65535.0f);
|
||||
uint z = uint(v.vertex_data[2] * 65535.0f);
|
||||
uint w = uint(v.vertex_data[3] * 65535.0f);
|
||||
|
||||
uint xy = x << 16 | y;
|
||||
uint zw = z << 16 | w;
|
||||
|
||||
dst_data[base_index] = xy;
|
||||
dst_data[base_index + 1] = zw;
|
||||
}
|
||||
else {
|
||||
/* This case is unsupported for now. */
|
||||
dst_data[base_index] = 0;
|
||||
}
|
||||
#elif defined(GPU_COMP_I32)
|
||||
uint base_index = uint(shader_data.dst_offset) + index * DIMENSIONS;
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
dst_data[base_index + i] = int(round(v.vertex_data[i]));
|
||||
}
|
||||
#else
|
||||
uint base_index = uint(shader_data.dst_offset) + index * DIMENSIONS;
|
||||
# ifdef NORMALIZE
|
||||
float3 value = float3(v.vertex_data[0], v.vertex_data[1], v.vertex_data[2]);
|
||||
value = normalize(value);
|
||||
v.vertex_data[0] = value.x;
|
||||
v.vertex_data[1] = value.y;
|
||||
v.vertex_data[2] = value.z;
|
||||
# endif
|
||||
for (uint i = 0; i < DIMENSIONS; i++) {
|
||||
dst_data[base_index + i] = v.vertex_data[i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vertex interp_vertex(Vertex v0, Vertex v1, Vertex v2, Vertex v3, float2 uv)
|
||||
{
|
||||
Vertex result;
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
float e = mix(v0.vertex_data[i], v1.vertex_data[i], uv.x);
|
||||
float f = mix(v2.vertex_data[i], v3.vertex_data[i], uv.x);
|
||||
result.vertex_data[i] = mix(e, f, uv.y);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_with_weight(Vertex &v0, Vertex v1, float weight)
|
||||
{
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
v0.vertex_data[i] += v1.vertex_data[i] * weight;
|
||||
}
|
||||
}
|
||||
|
||||
Vertex average(Vertex v0, Vertex v1)
|
||||
{
|
||||
Vertex result;
|
||||
for (int i = 0; i < DIMENSIONS; i++) {
|
||||
result.vertex_data[i] = (v0.vertex_data[i] + v1.vertex_data[i]) * 0.5f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint get_vertex_count(uint coarse_face)
|
||||
{
|
||||
uint number_of_patches = face_ptex_offset[coarse_face + 1] - face_ptex_offset[coarse_face];
|
||||
if (number_of_patches == 1) {
|
||||
/* If there is only one patch for the current coarse polygon, then it is a quad. */
|
||||
return 4;
|
||||
}
|
||||
/* Otherwise, the number of patches is the number of vertices. */
|
||||
return number_of_patches;
|
||||
}
|
||||
|
||||
uint get_polygon_corner_index(uint coarse_face, uint patch_index)
|
||||
{
|
||||
uint patch_offset = face_ptex_offset[coarse_face];
|
||||
return patch_index - patch_offset;
|
||||
}
|
||||
|
||||
uint get_loop_start(uint coarse_face)
|
||||
{
|
||||
return extra_coarse_face_data[coarse_face] & shader_data.coarse_face_loopstart_mask;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
/* Find which coarse polygon we came from. */
|
||||
uint coarse_face = coarse_face_index_from_subdiv_quad_index(quad_index,
|
||||
uint(shader_data.coarse_face_count));
|
||||
uint loop_start = get_loop_start(coarse_face);
|
||||
|
||||
/* Find the number of vertices for the coarse polygon. */
|
||||
Vertex v0, v1, v2, v3;
|
||||
clear(v0);
|
||||
clear(v1);
|
||||
clear(v2);
|
||||
clear(v3);
|
||||
|
||||
uint number_of_vertices = get_vertex_count(coarse_face);
|
||||
if (number_of_vertices == 4) {
|
||||
/* Interpolate the src data. */
|
||||
v0 = read_vertex(loop_start + 0);
|
||||
v1 = read_vertex(loop_start + 1);
|
||||
v2 = read_vertex(loop_start + 2);
|
||||
v3 = read_vertex(loop_start + 3);
|
||||
}
|
||||
else {
|
||||
/* Interpolate the src data for the center. */
|
||||
uint loop_end = loop_start + number_of_vertices;
|
||||
Vertex center_value;
|
||||
clear(center_value);
|
||||
|
||||
float weight = 1.0f / float(number_of_vertices);
|
||||
|
||||
for (uint l = loop_start; l < loop_end; l++) {
|
||||
add_with_weight(center_value, read_vertex(l), weight);
|
||||
}
|
||||
|
||||
/* Interpolate between the previous and next corner for the middle values for the edges. */
|
||||
uint patch_index = uint(patch_coords[start_loop_index].patch_index);
|
||||
uint current_coarse_corner = get_polygon_corner_index(coarse_face, patch_index);
|
||||
uint next_coarse_corner = (current_coarse_corner + 1) % number_of_vertices;
|
||||
uint prev_coarse_corner = (current_coarse_corner + number_of_vertices - 1) %
|
||||
number_of_vertices;
|
||||
|
||||
v0 = read_vertex(loop_start + current_coarse_corner);
|
||||
v1 = average(v0, read_vertex(loop_start + next_coarse_corner));
|
||||
v3 = average(v0, read_vertex(loop_start + prev_coarse_corner));
|
||||
|
||||
/* Interpolate between the current value, and the ones for the center and mid-edges. */
|
||||
v2 = center_value;
|
||||
}
|
||||
|
||||
/* Do a linear interpolation of the data based on the UVs for each loop of this subdivided quad.
|
||||
*/
|
||||
for (uint loop_index = start_loop_index; loop_index < start_loop_index + 4; loop_index++) {
|
||||
BlenderPatchCoord co = patch_coords[loop_index];
|
||||
float2 uv = decode_uv(co.encoded_uv);
|
||||
/* NOTE: v2 and v3 are reversed to stay consistent with the interpolation weight on the x-axis:
|
||||
*
|
||||
* v3 +-----+ v2
|
||||
* | |
|
||||
* | |
|
||||
* v0 +-----+ v1
|
||||
*
|
||||
* otherwise, weight would be `1.0f - uv.x` for `v2 <-> v3`, but `uv.x` for `v0 <-> v1`.
|
||||
*/
|
||||
Vertex result = interp_vertex(v0, v1, v3, v2, uv);
|
||||
write_vertex(loop_index, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Create index buffer for lines and loose lines. */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
#ifndef LINES_LOOSE
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_lines)
|
||||
#else
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_lines_loose)
|
||||
#endif
|
||||
|
||||
#ifndef LINES_LOOSE
|
||||
|
||||
bool is_face_hidden(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_hidden_mask) != 0;
|
||||
}
|
||||
|
||||
void emit_line(uint line_offset, uint quad_index, uint start_loop_index, uint corner_index)
|
||||
{
|
||||
uint vertex_index = start_loop_index + corner_index;
|
||||
|
||||
uint coarse_quad_index = coarse_face_index_from_subdiv_quad_index(
|
||||
quad_index, uint(shader_data.coarse_face_count));
|
||||
|
||||
if ((shader_data.use_hide && is_face_hidden(coarse_quad_index)) ||
|
||||
(input_edge_draw_flag[vertex_index] == 0))
|
||||
{
|
||||
output_lines[line_offset + 0] = 0xffffffff;
|
||||
output_lines[line_offset + 1] = 0xffffffff;
|
||||
}
|
||||
else {
|
||||
/* Mod 4 so we loop back at the first vertex on the last loop index (3). */
|
||||
uint next_vertex_index = start_loop_index + (corner_index + 1) % 4;
|
||||
|
||||
output_lines[line_offset + 0] = vertex_index;
|
||||
output_lines[line_offset + 1] = next_vertex_index;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = get_global_invocation_index();
|
||||
if (index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef LINES_LOOSE
|
||||
/* In the loose lines case, we execute for each line, with two vertices per line. */
|
||||
uint line_offset = shader_data.edge_loose_offset + index * 2;
|
||||
uint loop_index = shader_data.num_subdiv_loops + index * 2;
|
||||
|
||||
if (lines_loose_flags[index] != 0) {
|
||||
/* Line is hidden. */
|
||||
output_lines[line_offset] = 0xffffffff;
|
||||
output_lines[line_offset + 1] = 0xffffffff;
|
||||
}
|
||||
else {
|
||||
output_lines[line_offset] = loop_index;
|
||||
output_lines[line_offset + 1] = loop_index + 1;
|
||||
}
|
||||
|
||||
#else
|
||||
/* We execute for each quad, so the start index of the loop is quad_index * 4. */
|
||||
uint start_loop_index = index * 4;
|
||||
/* We execute for each quad, so the start index of the line is quad_index * 8 (with 2 vertices
|
||||
* per line). */
|
||||
uint start_line_index = index * 8;
|
||||
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
emit_line(start_line_index + i * 2u, index, start_loop_index, i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Generate triangle indices from subdivision quads. */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
#ifdef SINGLE_MATERIAL
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_tris_single_material)
|
||||
#else
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_tris_multiple_materials)
|
||||
#endif
|
||||
|
||||
bool is_face_hidden(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_hidden_mask) != 0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint loop_index = quad_index * 4;
|
||||
uint coarse_quad_index = coarse_face_index_from_subdiv_quad_index(
|
||||
quad_index, uint(shader_data.coarse_face_count));
|
||||
|
||||
#ifdef SINGLE_MATERIAL
|
||||
uint triangle_loop_index = quad_index * 6;
|
||||
#else
|
||||
uint mat_offset = face_mat_offset[coarse_quad_index];
|
||||
uint triangle_loop_index = (quad_index + mat_offset) * 6;
|
||||
#endif
|
||||
|
||||
if (shader_data.use_hide && is_face_hidden(coarse_quad_index)) {
|
||||
output_tris[triangle_loop_index + 0] = 0xffffffff;
|
||||
output_tris[triangle_loop_index + 1] = 0xffffffff;
|
||||
output_tris[triangle_loop_index + 2] = 0xffffffff;
|
||||
output_tris[triangle_loop_index + 3] = 0xffffffff;
|
||||
output_tris[triangle_loop_index + 4] = 0xffffffff;
|
||||
output_tris[triangle_loop_index + 5] = 0xffffffff;
|
||||
}
|
||||
else {
|
||||
output_tris[triangle_loop_index + 0] = loop_index + 0;
|
||||
output_tris[triangle_loop_index + 1] = loop_index + 1;
|
||||
output_tris[triangle_loop_index + 2] = loop_index + 2;
|
||||
output_tris[triangle_loop_index + 3] = loop_index + 0;
|
||||
output_tris[triangle_loop_index + 4] = loop_index + 2;
|
||||
output_tris[triangle_loop_index + 5] = loop_index + 3;
|
||||
}
|
||||
}
|
||||
252
blender-5.2.0/source/blender/draw/intern/shaders/subdiv_infos.hh
Normal file
252
blender-5.2.0/source/blender/draw/intern/shaders/subdiv_infos.hh
Normal file
@@ -0,0 +1,252 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
# include "BLI_utildefines_variadic.h"
|
||||
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "draw_subdiv_shader_shared.hh"
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "draw_subdiv_defines.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_base)
|
||||
LOCAL_GROUP_SIZE(SUBDIV_GROUP_SIZE)
|
||||
TYPEDEF_SOURCE("draw_subdiv_shader_shared.hh")
|
||||
UNIFORM_BUF(SHADER_DATA_BUF_SLOT, DRWSubdivUboStorage, shader_data)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_polygon_offset_base)
|
||||
DEFINE("SUBDIV_POLYGON_OFFSET")
|
||||
STORAGE_BUF(SUBDIV_FACE_OFFSET_BUF_SLOT, read, uint, subdiv_face_offset[])
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Loop Normals
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_loop_normals)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(LOOP_NORMALS_POS_SLOT, read, Position, positions[])
|
||||
STORAGE_BUF(LOOP_NORMALS_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
STORAGE_BUF(LOOP_NORMALS_VERT_NORMALS_BUF_SLOT, read, Normal, vert_normals[])
|
||||
STORAGE_BUF(LOOP_NORMALS_VERTEX_LOOP_MAP_BUF_SLOT, read, uint, vert_loop_map[])
|
||||
STORAGE_BUF(LOOP_NORMALS_OUTPUT_LNOR_BUF_SLOT, write, Normal, output_lnor[])
|
||||
COMPUTE_SOURCE("subdiv_vbo_lnor_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Triangle indices
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_tris_single_material)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("SINGLE_MATERIAL")
|
||||
STORAGE_BUF(TRIS_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
STORAGE_BUF(TRIS_OUTPUT_TRIS_BUF_SLOT, write, uint, output_tris[])
|
||||
COMPUTE_SOURCE("subdiv_ibo_tris_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_tris_multiple_materials)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(TRIS_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
STORAGE_BUF(TRIS_OUTPUT_TRIS_BUF_SLOT, write, uint, output_tris[])
|
||||
STORAGE_BUF(TRIS_FACE_MAT_OFFSET, read, uint, face_mat_offset[])
|
||||
COMPUTE_SOURCE("subdiv_ibo_tris_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Line indices
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_lines)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(LINES_INPUT_EDGE_DRAW_FLAG_BUF_SLOT, read, int, input_edge_draw_flag[])
|
||||
STORAGE_BUF(LINES_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
STORAGE_BUF(LINES_OUTPUT_LINES_BUF_SLOT, write, uint, output_lines[])
|
||||
COMPUTE_SOURCE("subdiv_ibo_lines_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_lines_loose)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("LINES_LOOSE")
|
||||
STORAGE_BUF(LINES_OUTPUT_LINES_BUF_SLOT, write, uint, output_lines[])
|
||||
STORAGE_BUF(LINES_LINES_LOOSE_FLAGS, read, uint, lines_loose_flags[])
|
||||
COMPUTE_SOURCE("subdiv_ibo_lines_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Edge data for object mode wireframe
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_edge_fac)
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(EDGE_FAC_POS_BUF_SLOT, read, Position, positions[])
|
||||
STORAGE_BUF(EDGE_FAC_EDGE_DRAW_FLAG_BUF_SLOT, read, uint, input_edge_draw_flag[])
|
||||
STORAGE_BUF(EDGE_FAC_POLY_OTHER_MAP_BUF_SLOT, read, int, input_poly_other_map[])
|
||||
STORAGE_BUF(EDGE_FAC_EDGE_FAC_BUF_SLOT, write, float, output_edge_fac[])
|
||||
COMPUTE_SOURCE("subdiv_vbo_edge_fac_comp.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Custom data
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_custom_data_interp_base)
|
||||
COMPUTE_SOURCE("subdiv_custom_data_interp_comp.glsl")
|
||||
STORAGE_BUF(CUSTOM_DATA_FACE_PTEX_OFFSET_BUF_SLOT, read, uint, face_ptex_offset[])
|
||||
STORAGE_BUF(CUSTOM_DATA_PATCH_COORDS_BUF_SLOT, read, BlenderPatchCoord, patch_coords[])
|
||||
STORAGE_BUF(CUSTOM_DATA_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_data_uint)
|
||||
DEFINE("GPU_COMP_U16")
|
||||
STORAGE_BUF(CUSTOM_DATA_SOURCE_DATA_BUF_SLOT, read, uint, src_data[])
|
||||
STORAGE_BUF(CUSTOM_DATA_DESTINATION_DATA_BUF_SLOT, write, uint, dst_data[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_data_int)
|
||||
DEFINE("GPU_COMP_I32")
|
||||
STORAGE_BUF(CUSTOM_DATA_SOURCE_DATA_BUF_SLOT, read, int, src_data[])
|
||||
STORAGE_BUF(CUSTOM_DATA_DESTINATION_DATA_BUF_SLOT, write, int, dst_data[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_data_float)
|
||||
DEFINE("GPU_COMP_F32")
|
||||
STORAGE_BUF(CUSTOM_DATA_SOURCE_DATA_BUF_SLOT, read, float, src_data[])
|
||||
STORAGE_BUF(CUSTOM_DATA_DESTINATION_DATA_BUF_SLOT, write, float, dst_data[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_dimension_1)
|
||||
DEFINE("DIMENSIONS_1")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_dimension_2)
|
||||
DEFINE("DIMENSIONS_2")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_dimension_3)
|
||||
DEFINE("DIMENSIONS_3")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_dimension_4)
|
||||
DEFINE("DIMENSIONS_4")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_normalize)
|
||||
DEFINE("NORMALIZE")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/* clang-format off */
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_4d_u16, subdiv_custom_data_interp_base, subdiv_data_uint, subdiv_dimension_4)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_1d_i32, subdiv_custom_data_interp_base, subdiv_data_int, subdiv_dimension_1)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_2d_i32, subdiv_custom_data_interp_base, subdiv_data_int, subdiv_dimension_2)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_3d_i32, subdiv_custom_data_interp_base, subdiv_data_int, subdiv_dimension_3)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_4d_i32, subdiv_custom_data_interp_base, subdiv_data_int, subdiv_dimension_4)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_1d_f32, subdiv_custom_data_interp_base, subdiv_data_float, subdiv_dimension_1)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_2d_f32, subdiv_custom_data_interp_base, subdiv_data_float, subdiv_dimension_2)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_3d_f32, subdiv_custom_data_interp_base, subdiv_data_float, subdiv_dimension_3)
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_4d_f32, subdiv_custom_data_interp_base, subdiv_data_float, subdiv_dimension_4)
|
||||
/* clang-format on */
|
||||
|
||||
CREATE_INFO_VARIANT(subdiv_custom_data_interp_3d_f32_normalize,
|
||||
subdiv_custom_data_interp_base,
|
||||
subdiv_data_float,
|
||||
subdiv_dimension_3,
|
||||
subdiv_normalize)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sculpt data
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_sculpt_data)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(SCULPT_DATA_SCULPT_MASK_BUF_SLOT, read, float, sculpt_mask[])
|
||||
STORAGE_BUF(SCULPT_DATA_SCULPT_FACE_SET_COLOR_BUF_SLOT, read, uint, sculpt_face_set_color[])
|
||||
STORAGE_BUF(SCULPT_DATA_SCULPT_DATA_BUF_SLOT, write, SculptData, sculpt_data[])
|
||||
COMPUTE_SOURCE("subdiv_vbo_sculpt_data_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Stretch overlays
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_edituv_stretch_angle)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(STRETCH_ANGLE_POS_BUF_SLOT, read, Position, positions[])
|
||||
STORAGE_BUF(STRETCH_ANGLE_UVS_BUF_SLOT, read, packed_float2, uvs[])
|
||||
STORAGE_BUF(STRETCH_ANGLE_UV_STRETCHES_BUF_SLOT, write, UVStretchAngle, uv_stretches[])
|
||||
COMPUTE_SOURCE("subdiv_vbo_edituv_stretch_angle_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_edituv_stretch_area)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(STRETCH_AREA_COARSE_STRETCH_AREA_BUF_SLOT, read, float, coarse_stretch_area[])
|
||||
STORAGE_BUF(STRETCH_AREA_SUBDIV_STRETCH_AREA_BUF_SLOT, write, float, subdiv_stretch_area[])
|
||||
COMPUTE_SOURCE("subdiv_vbo_edituv_stretch_area_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Normals
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_normals_accumulate)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(NORMALS_ACCUMULATE_POS_BUF_SLOT, read, Position, positions[])
|
||||
STORAGE_BUF(NORMALS_ACCUMULATE_FACE_ADJACENCY_OFFSETS_BUF_SLOT,
|
||||
read,
|
||||
uint,
|
||||
face_adjacency_offsets[])
|
||||
STORAGE_BUF(NORMALS_ACCUMULATE_FACE_ADJACENCY_LISTS_BUF_SLOT, read, uint, face_adjacency_lists[])
|
||||
STORAGE_BUF(NORMALS_ACCUMULATE_VERTEX_LOOP_MAP_BUF_SLOT, read, uint, vert_loop_map[])
|
||||
STORAGE_BUF(NORMALS_ACCUMULATE_NORMALS_BUF_SLOT, write, Normal, vert_normals[])
|
||||
COMPUTE_SOURCE("subdiv_normals_accumulate_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Paint Overlay Flag
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_paint_overlay_flag)
|
||||
DO_STATIC_COMPILATION()
|
||||
STORAGE_BUF(PAINT_OVERLAY_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
STORAGE_BUF(PAINT_OVERLAY_EXTRA_INPUT_VERT_ORIG_INDEX_SLOT, read, int, input_vert_origindex[])
|
||||
STORAGE_BUF(PAINT_OVERLAY_OUTPUT_FLAG_SLOT, write, int, flags[])
|
||||
COMPUTE_SOURCE("subdiv_vbo_paint_overlay_flag_comp.glsl")
|
||||
ADDITIONAL_INFO(subdiv_polygon_offset_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "subdiv_infos.hh"
|
||||
|
||||
#ifdef SUBDIV_POLYGON_OFFSET
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_polygon_offset_base)
|
||||
#else
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_base)
|
||||
#endif
|
||||
|
||||
uint get_global_invocation_index()
|
||||
{
|
||||
uint invocations_per_row = gl_WorkGroupSize.x * gl_NumWorkGroups.x;
|
||||
return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * invocations_per_row;
|
||||
}
|
||||
|
||||
float2 decode_uv(uint encoded_uv)
|
||||
{
|
||||
float u = float((encoded_uv >> 16) & 0xFFFFu) / 65535.0f;
|
||||
float v = float(encoded_uv & 0xFFFFu) / 65535.0f;
|
||||
return float2(u, v);
|
||||
}
|
||||
|
||||
bool is_set(uint i)
|
||||
{
|
||||
/* QuadNode.Child.isSet is the first bit of the bit-field. */
|
||||
return (i & 0x1u) != 0;
|
||||
}
|
||||
|
||||
bool is_leaf(uint i)
|
||||
{
|
||||
/* QuadNode.Child.isLeaf is the second bit of the bit-field. */
|
||||
return (i & 0x2u) != 0;
|
||||
}
|
||||
|
||||
uint get_index(uint i)
|
||||
{
|
||||
/* QuadNode.Child.index is made of the remaining bits. */
|
||||
return (i >> 2) & 0x3FFFFFFFu;
|
||||
}
|
||||
|
||||
float3 subdiv_position_to_float3(Position position)
|
||||
{
|
||||
return float3(position.x, position.y, position.z);
|
||||
}
|
||||
|
||||
void add_newell_cross_v3_v3v3(float3 &n, float3 v_prev, float3 v_curr)
|
||||
{
|
||||
n[0] += (v_prev[1] - v_curr[1]) * (v_prev[2] + v_curr[2]);
|
||||
n[1] += (v_prev[2] - v_curr[2]) * (v_prev[0] + v_curr[0]);
|
||||
n[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]);
|
||||
}
|
||||
|
||||
#define ORIGINDEX_NONE -1
|
||||
|
||||
#ifdef SUBDIV_POLYGON_OFFSET
|
||||
|
||||
/* Given the index of the subdivision quad, return the index of the corresponding coarse polygon.
|
||||
* This uses subdiv_face_offset and since it is a growing list of offsets, we can use binary
|
||||
* search to locate the right index. */
|
||||
uint coarse_face_index_from_subdiv_quad_index(uint subdiv_quad_index, uint coarse_face_count)
|
||||
{
|
||||
uint first = 0;
|
||||
uint last = coarse_face_count;
|
||||
|
||||
while (first != last) {
|
||||
uint middle = (first + last) / 2;
|
||||
|
||||
if (subdiv_face_offset[middle] < subdiv_quad_index) {
|
||||
first = middle + 1;
|
||||
}
|
||||
else {
|
||||
last = middle;
|
||||
}
|
||||
}
|
||||
|
||||
if (first < coarse_face_count && subdiv_face_offset[first] == subdiv_quad_index) {
|
||||
return first;
|
||||
}
|
||||
|
||||
return first - 1;
|
||||
}
|
||||
#else
|
||||
uint coarse_face_index_from_subdiv_quad_index(uint subdiv_quad_index, uint coarse_face_count);
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Accumulate vertex normals from their adjacent faces.
|
||||
*
|
||||
* Accumulated normals needs to be finalized `subdiv_vbo_lnor_comp.glsl`.
|
||||
* to be stored as loops.
|
||||
*/
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_normals_accumulate)
|
||||
|
||||
void find_prev_and_next_vertex_on_face(
|
||||
uint face_index, uint vertex_index, uint &curr, uint &next, uint &prev)
|
||||
{
|
||||
uint start_loop_index = face_index * 4;
|
||||
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
uint subdiv_vert_index = vert_loop_map[start_loop_index + i];
|
||||
|
||||
if (subdiv_vert_index == vertex_index) {
|
||||
curr = i;
|
||||
next = (i + 1) % 4;
|
||||
prev = (i + 4 - 1) % 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint vertex_index = get_global_invocation_index();
|
||||
if (vertex_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint first_adjacent_face_offset = face_adjacency_offsets[vertex_index];
|
||||
uint number_of_adjacent_faces = face_adjacency_offsets[vertex_index + 1] -
|
||||
first_adjacent_face_offset;
|
||||
|
||||
float3 accumulated_normal = float3(0.0f);
|
||||
|
||||
/* For each adjacent face. */
|
||||
for (uint i = 0; i < number_of_adjacent_faces; i++) {
|
||||
uint adjacent_face = face_adjacency_lists[first_adjacent_face_offset + i];
|
||||
uint start_loop_index = adjacent_face * 4;
|
||||
|
||||
/* Compute the face normal using Newell's method. */
|
||||
float3 verts[4];
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
Position position = positions[start_loop_index + j];
|
||||
verts[j] = subdiv_position_to_float3(position);
|
||||
}
|
||||
|
||||
float3 face_normal = float3(0.0f);
|
||||
add_newell_cross_v3_v3v3(face_normal, verts[0], verts[1]);
|
||||
add_newell_cross_v3_v3v3(face_normal, verts[1], verts[2]);
|
||||
add_newell_cross_v3_v3v3(face_normal, verts[2], verts[3]);
|
||||
add_newell_cross_v3_v3v3(face_normal, verts[3], verts[0]);
|
||||
|
||||
/* Accumulate angle weighted normal. */
|
||||
uint curr_vert = 0;
|
||||
uint next_vert = 0;
|
||||
uint prev_vert = 0;
|
||||
find_prev_and_next_vertex_on_face(
|
||||
adjacent_face, vertex_index, curr_vert, next_vert, prev_vert);
|
||||
|
||||
float3 curr_co = verts[curr_vert];
|
||||
float3 prev_co = verts[next_vert];
|
||||
float3 next_co = verts[prev_vert];
|
||||
|
||||
float3 edvec_prev = normalize(prev_co - curr_co);
|
||||
float3 edvec_next = normalize(curr_co - next_co);
|
||||
|
||||
float fac = acos(-dot(edvec_prev, edvec_next));
|
||||
|
||||
accumulated_normal += face_normal * fac;
|
||||
}
|
||||
|
||||
float3 normal = normalize(accumulated_normal);
|
||||
Normal nor;
|
||||
nor.x = normal.x;
|
||||
nor.y = normal.y;
|
||||
nor.z = normal.z;
|
||||
vert_normals[vertex_index] = nor;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "draw_subdiv_shader_shared.hh"
|
||||
|
||||
/* `osd_patch_defines.glsl` must be included before `osd_patch_basis.glsl` */
|
||||
# include "osd_patch_defines.glsl"
|
||||
|
||||
# include "osd_patch_basis.glsl"
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "draw_subdiv_defines.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Patch evaluation
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_patch_evaluation_basis)
|
||||
DEFINE("OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES")
|
||||
TYPEDEF_SOURCE("osd_patch_defines.glsl")
|
||||
TYPEDEF_SOURCE("osd_patch_basis.glsl")
|
||||
COMPUTE_SOURCE("subdiv_patch_evaluation_comp.glsl")
|
||||
STORAGE_BUF(PATCH_EVALUATION_SOURCE_VERTEX_BUFFER_BUF_SLOT, read, float, srcVertexBuffer[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_INPUT_PATCH_HANDLES_BUF_SLOT,
|
||||
read,
|
||||
PatchHandle,
|
||||
input_patch_handles[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_QUAD_NODES_BUF_SLOT, read, QuadNode, quad_nodes[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_PATCH_COORDS_BUF_SLOT, read, BlenderPatchCoord, patch_coords[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_PATCH_ARRAY_BUFFER_BUF_SLOT, read, OsdPatchArray, patchArrayBuffer[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_PATCH_INDEX_BUFFER_BUF_SLOT, read, int, patchIndexBuffer[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_PATCH_PARAM_BUFFER_BUF_SLOT, read, OsdPatchParam, patchParamBuffer[])
|
||||
ADDITIONAL_INFO(subdiv_base)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_patch_evaluation_fdots)
|
||||
DEFINE("FDOTS_EVALUATION")
|
||||
STORAGE_BUF(PATCH_EVALUATION_OUTPUT_FDOTS_VERTEX_BUFFER_BUF_SLOT, write, FDotVert, output_verts[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_OUTPUT_INDICES_BUF_SLOT, write, uint, output_indices[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_EXTRA_COARSE_FACE_DATA_BUF_SLOT, read, uint, extra_coarse_face_data[])
|
||||
ADDITIONAL_INFO(subdiv_patch_evaluation_basis)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_patch_evaluation_verts)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("VERTS_EVALUATION")
|
||||
STORAGE_BUF(PATCH_EVALUATION_OUTPUT_POS_BUF_SLOT, write, Position, positions[])
|
||||
ADDITIONAL_INFO(subdiv_patch_evaluation_basis)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_patch_evaluation_fvar)
|
||||
DO_STATIC_COMPILATION()
|
||||
ADDITIONAL_INFO(subdiv_patch_evaluation_basis)
|
||||
DEFINE("FVAR_EVALUATION")
|
||||
STORAGE_BUF(PATCH_EVALUATION_OUTPUT_FVAR_BUF_SLOT, write, packed_float2, output_fvar[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_patch_evaluation_fdots_normals)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("FDOTS_NORMALS")
|
||||
STORAGE_BUF(PATCH_EVALUATION_OUTPUT_NORMALS_BUF_SLOT, write, FDotNor, output_nors[])
|
||||
ADDITIONAL_INFO(subdiv_patch_evaluation_fdots)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(subdiv_patch_evaluation_verts_orcos)
|
||||
DO_STATIC_COMPILATION()
|
||||
ADDITIONAL_INFO(subdiv_patch_evaluation_verts)
|
||||
DEFINE("ORCO_EVALUATION")
|
||||
STORAGE_BUF(PATCH_EVALUATION_SOURCE_EXTRA_VERTEX_BUFFER_BUF_SLOT,
|
||||
read,
|
||||
float,
|
||||
srcExtraVertexBuffer[])
|
||||
STORAGE_BUF(PATCH_EVALUATION_OUTPUT_ORCOS_BUF_SLOT, write, float4, output_orcos[])
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,381 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_patch_eval_infos.hh"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_patch_evaluation_fdots_normals)
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
float2 read_vec2(int index)
|
||||
{
|
||||
float2 result;
|
||||
result.x = srcVertexBuffer[index * 2];
|
||||
result.y = srcVertexBuffer[index * 2 + 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
float3 read_vec3(int index)
|
||||
{
|
||||
float3 result;
|
||||
result.x = srcVertexBuffer[index * 3];
|
||||
result.y = srcVertexBuffer[index * 3 + 1];
|
||||
result.z = srcVertexBuffer[index * 3 + 2];
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(ORCO_EVALUATION)
|
||||
float3 read_vec3_extra(int index)
|
||||
{
|
||||
float3 result;
|
||||
result.x = srcExtraVertexBuffer[index * 3];
|
||||
result.y = srcExtraVertexBuffer[index * 3 + 1];
|
||||
result.z = srcExtraVertexBuffer[index * 3 + 2];
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
OsdPatchArray GetPatchArray(int arrayIndex)
|
||||
{
|
||||
return patchArrayBuffer[arrayIndex];
|
||||
}
|
||||
|
||||
OsdPatchParam GetPatchParam(int patchIndex)
|
||||
{
|
||||
return patchParamBuffer[patchIndex];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------
|
||||
* Patch Coordinate lookup. Return an #OsdPatchCoord for the given patch_index and UVs.
|
||||
* This code is a port of the #OpenSubdiv PatchMap lookup code.
|
||||
*/
|
||||
|
||||
PatchHandle bogus_patch_handle()
|
||||
{
|
||||
PatchHandle ret;
|
||||
ret.array_index = -1;
|
||||
ret.vertex_index = -1;
|
||||
ret.patch_index = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int transformUVToQuadQuadrant(float median, float &u, float &v)
|
||||
{
|
||||
int uHalf = (u >= median) ? 1 : 0;
|
||||
if (uHalf != 0) {
|
||||
u -= median;
|
||||
}
|
||||
int vHalf = (v >= median) ? 1 : 0;
|
||||
if (vHalf != 0) {
|
||||
v -= median;
|
||||
}
|
||||
return (vHalf << 1) | uHalf;
|
||||
}
|
||||
|
||||
int transformUVToTriQuadrant(float median, float &u, float &v, bool &rotated)
|
||||
{
|
||||
|
||||
if (!rotated) {
|
||||
if (u >= median) {
|
||||
u -= median;
|
||||
return 1;
|
||||
}
|
||||
if (v >= median) {
|
||||
v -= median;
|
||||
return 2;
|
||||
}
|
||||
if ((u + v) >= median) {
|
||||
rotated = true;
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (u < median) {
|
||||
v -= median;
|
||||
return 1;
|
||||
}
|
||||
if (v < median) {
|
||||
u -= median;
|
||||
return 2;
|
||||
}
|
||||
u -= median;
|
||||
v -= median;
|
||||
if ((u + v) < median) {
|
||||
rotated = false;
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PatchHandle find_patch(int face_index, float u, float v)
|
||||
{
|
||||
if (face_index < shader_data.min_patch_face || face_index > shader_data.max_patch_face) {
|
||||
return bogus_patch_handle();
|
||||
}
|
||||
|
||||
QuadNode node = quad_nodes[face_index - shader_data.min_patch_face];
|
||||
|
||||
if (!is_set(node.child[0])) {
|
||||
return bogus_patch_handle();
|
||||
}
|
||||
|
||||
float median = 0.5f;
|
||||
bool tri_rotated = false;
|
||||
|
||||
for (int depth = 0; depth <= shader_data.max_depth; ++depth, median *= 0.5f) {
|
||||
int quadrant = shader_data.patches_are_triangular ?
|
||||
transformUVToTriQuadrant(median, u, v, tri_rotated) :
|
||||
transformUVToQuadQuadrant(median, u, v);
|
||||
|
||||
if (is_leaf(node.child[quadrant])) {
|
||||
return input_patch_handles[get_index(node.child[quadrant])];
|
||||
}
|
||||
|
||||
node = quad_nodes[get_index(node.child[quadrant])];
|
||||
}
|
||||
return bogus_patch_handle();
|
||||
}
|
||||
|
||||
OsdPatchCoord bogus_patch_coord(int face_index, float u, float v)
|
||||
{
|
||||
OsdPatchCoord coord;
|
||||
coord.arrayIndex = 0;
|
||||
coord.patchIndex = face_index;
|
||||
coord.vertIndex = 0;
|
||||
coord.s = u;
|
||||
coord.t = v;
|
||||
return coord;
|
||||
}
|
||||
|
||||
OsdPatchCoord GetPatchCoord(int face_index, float u, float v)
|
||||
{
|
||||
PatchHandle patch_handle = find_patch(face_index, u, v);
|
||||
|
||||
if (patch_handle.array_index == -1) {
|
||||
return bogus_patch_coord(face_index, u, v);
|
||||
}
|
||||
|
||||
OsdPatchCoord coord;
|
||||
coord.arrayIndex = patch_handle.array_index;
|
||||
coord.patchIndex = patch_handle.patch_index;
|
||||
coord.vertIndex = patch_handle.vertex_index;
|
||||
coord.s = u;
|
||||
coord.t = v;
|
||||
return coord;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------
|
||||
* Patch evaluation. Note that the 1st and 2nd derivatives are always computed, although we
|
||||
* only return and use the 1st derivatives if adaptive patches are used. This could
|
||||
* perhaps be optimized.
|
||||
*/
|
||||
|
||||
#if defined(FVAR_EVALUATION)
|
||||
void evaluate_patches_limits(int patch_index, float u, float v, float2 &dst)
|
||||
{
|
||||
OsdPatchCoord coord = GetPatchCoord(patch_index, u, v);
|
||||
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
|
||||
OsdPatchParam param = GetPatchParam(coord.patchIndex);
|
||||
|
||||
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
|
||||
|
||||
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
|
||||
int nPoints = OsdEvaluatePatchBasis(
|
||||
patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
|
||||
|
||||
int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
|
||||
|
||||
for (int cv = 0; cv < nPoints; ++cv) {
|
||||
int index = patchIndexBuffer[indexBase + cv];
|
||||
float2 src_fvar = read_vec2(shader_data.src_offset + index);
|
||||
dst += src_fvar * wP[cv];
|
||||
}
|
||||
}
|
||||
#else
|
||||
void evaluate_patches_limits(
|
||||
int patch_index, float u, float v, float3 &dst, float3 &du, float3 &dv)
|
||||
{
|
||||
OsdPatchCoord coord = GetPatchCoord(patch_index, u, v);
|
||||
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
|
||||
OsdPatchParam param = GetPatchParam(coord.patchIndex);
|
||||
|
||||
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
|
||||
|
||||
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
|
||||
int nPoints = OsdEvaluatePatchBasis(
|
||||
patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
|
||||
|
||||
int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
|
||||
|
||||
for (int cv = 0; cv < nPoints; ++cv) {
|
||||
int index = patchIndexBuffer[indexBase + cv];
|
||||
float3 src_vertex = read_vec3(index);
|
||||
|
||||
dst += src_vertex * wP[cv];
|
||||
du += src_vertex * wDu[cv];
|
||||
dv += src_vertex * wDv[cv];
|
||||
}
|
||||
}
|
||||
|
||||
# if defined(ORCO_EVALUATION)
|
||||
/* Evaluate the patches limits from the extra source vertex buffer. */
|
||||
void evaluate_patches_limits_extra(int patch_index, float u, float v, float3 &dst)
|
||||
{
|
||||
OsdPatchCoord coord = GetPatchCoord(patch_index, u, v);
|
||||
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
|
||||
OsdPatchParam param = GetPatchParam(coord.patchIndex);
|
||||
|
||||
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
|
||||
|
||||
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
|
||||
int nPoints = OsdEvaluatePatchBasis(
|
||||
patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
|
||||
|
||||
int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
|
||||
|
||||
for (int cv = 0; cv < nPoints; ++cv) {
|
||||
int index = patchIndexBuffer[indexBase + cv];
|
||||
float3 src_vertex = read_vec3_extra(index);
|
||||
|
||||
dst += src_vertex * wP[cv];
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------------
|
||||
* Entry point.
|
||||
*/
|
||||
|
||||
#if defined(FVAR_EVALUATION)
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
for (uint loop_index = start_loop_index; loop_index < start_loop_index + 4; loop_index++) {
|
||||
float2 fvar = float2(0.0f);
|
||||
|
||||
BlenderPatchCoord patch_co = patch_coords[loop_index];
|
||||
float2 uv = decode_uv(patch_co.encoded_uv);
|
||||
|
||||
evaluate_patches_limits(patch_co.patch_index, uv.x, uv.y, fvar);
|
||||
output_fvar[shader_data.dst_offset + loop_index] = fvar;
|
||||
}
|
||||
}
|
||||
#elif defined(FDOTS_EVALUATION)
|
||||
bool is_face_selected(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_select_mask) != 0;
|
||||
}
|
||||
|
||||
bool is_face_active(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_active_mask) != 0;
|
||||
}
|
||||
|
||||
float get_face_flag(uint coarse_quad_index)
|
||||
{
|
||||
if (is_face_active(coarse_quad_index)) {
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
if (is_face_selected(coarse_quad_index)) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool is_face_hidden(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_hidden_mask) != 0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each coarse quad. */
|
||||
uint coarse_quad_index = get_global_invocation_index();
|
||||
if (coarse_quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlenderPatchCoord patch_co = patch_coords[coarse_quad_index];
|
||||
float2 uv = decode_uv(patch_co.encoded_uv);
|
||||
|
||||
float3 pos = float3(0.0f);
|
||||
float3 du = float3(0.0f);
|
||||
float3 dv = float3(0.0f);
|
||||
evaluate_patches_limits(patch_co.patch_index, uv.x, uv.y, pos, du, dv);
|
||||
float3 nor = normalize(cross(du, dv));
|
||||
|
||||
FDotVert vert;
|
||||
vert.x = pos.x;
|
||||
vert.y = pos.y;
|
||||
vert.z = pos.z;
|
||||
|
||||
FDotNor fnor;
|
||||
fnor.x = nor.x;
|
||||
fnor.y = nor.y;
|
||||
fnor.z = nor.z;
|
||||
fnor.flag = get_face_flag(coarse_quad_index);
|
||||
|
||||
output_verts[coarse_quad_index] = vert;
|
||||
# ifdef FDOTS_NORMALS
|
||||
output_nors[coarse_quad_index] = fnor;
|
||||
# endif
|
||||
|
||||
if (shader_data.use_hide && is_face_hidden(coarse_quad_index)) {
|
||||
output_indices[coarse_quad_index] = 0xffffffff;
|
||||
}
|
||||
else {
|
||||
output_indices[coarse_quad_index] = coarse_quad_index;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
for (uint loop_index = start_loop_index; loop_index < start_loop_index + 4; loop_index++) {
|
||||
float3 pos = float3(0.0f);
|
||||
float3 du = float3(0.0f);
|
||||
float3 dv = float3(0.0f);
|
||||
|
||||
BlenderPatchCoord patch_co = patch_coords[loop_index];
|
||||
float2 uv = decode_uv(patch_co.encoded_uv);
|
||||
|
||||
evaluate_patches_limits(patch_co.patch_index, uv.x, uv.y, pos, du, dv);
|
||||
|
||||
Position position;
|
||||
position.x = pos.x;
|
||||
position.y = pos.y;
|
||||
position.z = pos.z;
|
||||
positions[loop_index] = position;
|
||||
|
||||
# if defined(ORCO_EVALUATION)
|
||||
pos = float3(0.0f);
|
||||
evaluate_patches_limits_extra(patch_co.patch_index, uv.x, uv.y, pos);
|
||||
|
||||
/* Set w = 0.0f to indicate that this is not a generic attribute.
|
||||
* See comments in `extract_mesh_vbo_orco.cc`. */
|
||||
float4 orco_data = float4(pos, 0.0f);
|
||||
output_orcos[loop_index] = orco_data;
|
||||
# endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Extract edge data for object mode wire frame. */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_edge_fac)
|
||||
|
||||
void write_vec4(uint index, float4 edge_facs)
|
||||
{
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
output_edge_fac[index + i] = edge_facs[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* From extract_mesh_vbo_edge_fac.cc, keep in sync! */
|
||||
float loop_edge_factor_get(float3 fa_no, float3 fb_no)
|
||||
{
|
||||
float cosine = dot(fa_no, fb_no);
|
||||
|
||||
/* Re-scale to the slider range. */
|
||||
float fac = (200 * (cosine - 1.0f)) + 1.0f;
|
||||
|
||||
/* The maximum value (255) is unreachable through the UI. */
|
||||
return clamp(fac, 0.0f, 1.0f) * (254.0f / 255.0f);
|
||||
}
|
||||
|
||||
float compute_line_factor(uint corner_index, float3 face_normal)
|
||||
{
|
||||
if (input_edge_draw_flag[corner_index] == 0) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
int quad_other = input_poly_other_map[corner_index];
|
||||
if (quad_other == -1) {
|
||||
/* Boundary edge or non-manifold. */
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
uint start_corner_index_other = uint(quad_other * 4);
|
||||
Position pos_0 = positions[start_corner_index_other + 0];
|
||||
Position pos_1 = positions[start_corner_index_other + 1];
|
||||
Position pos_2 = positions[start_corner_index_other + 2];
|
||||
float3 v0 = subdiv_position_to_float3(pos_0);
|
||||
float3 v1 = subdiv_position_to_float3(pos_1);
|
||||
float3 v2 = subdiv_position_to_float3(pos_2);
|
||||
float3 face_normal_other = normalize(cross(v1 - v0, v2 - v0));
|
||||
|
||||
return loop_edge_factor_get(face_normal, face_normal_other);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The start index of the loop is quad_index * 4. */
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
/* First compute the face normal, we need it to compute the bihedral edge angle. */
|
||||
Position pos_0 = positions[start_loop_index + 0];
|
||||
Position pos_1 = positions[start_loop_index + 1];
|
||||
Position pos_2 = positions[start_loop_index + 2];
|
||||
float3 v0 = subdiv_position_to_float3(pos_0);
|
||||
float3 v1 = subdiv_position_to_float3(pos_1);
|
||||
float3 v2 = subdiv_position_to_float3(pos_2);
|
||||
float3 face_normal = normalize(cross(v1 - v0, v2 - v0));
|
||||
|
||||
float4 edge_facs = float4(0.0f);
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
edge_facs[i] = compute_line_factor(start_loop_index + i, face_normal);
|
||||
}
|
||||
|
||||
write_vec4(start_loop_index, edge_facs);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_edituv_stretch_angle)
|
||||
|
||||
#define M_PI 3.1415926535897932f
|
||||
#define M_1_PI 0.31830988618379067154f
|
||||
|
||||
/* Adapted from BLI_math_vector.h */
|
||||
float angle_normalized_v3v3(float3 v1, float3 v2)
|
||||
{
|
||||
/* this is the same as acos(dot_v3v3(v1, v2)), but more accurate */
|
||||
bool q = (dot(v1, v2) >= 0.0f);
|
||||
float3 v = (q) ? (v1 - v2) : (v1 + v2);
|
||||
float a = 2.0f * asin(length(v) / 2.0f);
|
||||
return (q) ? a : M_PI - a;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
uint cur_loop_index = start_loop_index + i;
|
||||
uint next_loop_index = start_loop_index + (i + 1) % 4;
|
||||
uint prev_loop_index = start_loop_index + (i + 3) % 4;
|
||||
|
||||
/* Compute 2d edge vectors from UVs. */
|
||||
float2 cur_uv = uvs[uint(shader_data.src_offset) + cur_loop_index];
|
||||
float2 next_uv = uvs[uint(shader_data.src_offset) + next_loop_index];
|
||||
float2 prev_uv = uvs[uint(shader_data.src_offset) + prev_loop_index];
|
||||
|
||||
float2 norm_uv_edge0 = normalize(prev_uv - cur_uv);
|
||||
float2 norm_uv_edge1 = normalize(cur_uv - next_uv);
|
||||
|
||||
/* Compute 3d edge vectors from positions. */
|
||||
Position cur_position = positions[cur_loop_index];
|
||||
float3 cur_pos = subdiv_position_to_float3(cur_position);
|
||||
Position next_position = positions[next_loop_index];
|
||||
float3 next_pos = subdiv_position_to_float3(next_position);
|
||||
Position prev_position = positions[prev_loop_index];
|
||||
float3 prev_pos = subdiv_position_to_float3(prev_position);
|
||||
|
||||
float3 norm_pos_edge0 = normalize(prev_pos - cur_pos);
|
||||
float3 norm_pos_edge1 = normalize(cur_pos - next_pos);
|
||||
|
||||
/* Compute stretches, this logic is adapted from #edituv_get_edituv_stretch_angle.
|
||||
* Keep in sync! */
|
||||
UVStretchAngle stretch;
|
||||
stretch.uv_angle0 = atan(norm_uv_edge0.y, norm_uv_edge0.x) * M_1_PI;
|
||||
stretch.uv_angle1 = atan(norm_uv_edge1.y, norm_uv_edge1.x) * M_1_PI;
|
||||
stretch.angle = angle_normalized_v3v3(norm_pos_edge0, norm_pos_edge1) * M_1_PI;
|
||||
|
||||
uv_stretches[cur_loop_index] = stretch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_edituv_stretch_area)
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The start index of the loop is quad_index * 4. */
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
uint coarse_quad_index = coarse_face_index_from_subdiv_quad_index(
|
||||
quad_index, uint(shader_data.coarse_face_count));
|
||||
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
subdiv_stretch_area[start_loop_index + i] = coarse_stretch_area[coarse_quad_index];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_loop_normals)
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The start index of the loop is quad_index * 4. */
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
uint coarse_quad_index = coarse_face_index_from_subdiv_quad_index(
|
||||
quad_index, uint(shader_data.coarse_face_count));
|
||||
|
||||
if ((extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_smooth_mask) != 0) {
|
||||
/* Face is smooth, use vertex normals. */
|
||||
for (uint i = 0; i < 4u; i++) {
|
||||
uint subdiv_vert_index = vert_loop_map[start_loop_index + i];
|
||||
Normal vert_normal = vert_normals[subdiv_vert_index];
|
||||
|
||||
Normal normal;
|
||||
normal.x = vert_normal.x;
|
||||
normal.y = vert_normal.y;
|
||||
normal.z = vert_normal.z;
|
||||
|
||||
output_lnor[start_loop_index + i] = normal;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Position pos_0 = positions[start_loop_index + 0];
|
||||
Position pos_1 = positions[start_loop_index + 1];
|
||||
Position pos_2 = positions[start_loop_index + 2];
|
||||
Position pos_3 = positions[start_loop_index + 3];
|
||||
float3 v0 = subdiv_position_to_float3(pos_0);
|
||||
float3 v1 = subdiv_position_to_float3(pos_1);
|
||||
float3 v2 = subdiv_position_to_float3(pos_2);
|
||||
float3 v3 = subdiv_position_to_float3(pos_3);
|
||||
|
||||
float3 face_normal = float3(0.0f);
|
||||
add_newell_cross_v3_v3v3(face_normal, v0, v1);
|
||||
add_newell_cross_v3_v3v3(face_normal, v1, v2);
|
||||
add_newell_cross_v3_v3v3(face_normal, v2, v3);
|
||||
add_newell_cross_v3_v3v3(face_normal, v3, v0);
|
||||
|
||||
face_normal = normalize(face_normal);
|
||||
|
||||
Normal normal;
|
||||
normal.x = face_normal.x;
|
||||
normal.y = face_normal.y;
|
||||
normal.z = face_normal.z;
|
||||
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
output_lnor[start_loop_index + i] = normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_paint_overlay_flag)
|
||||
|
||||
bool is_face_selected(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_select_mask) != 0;
|
||||
}
|
||||
|
||||
bool is_face_hidden(uint coarse_quad_index)
|
||||
{
|
||||
return (extra_coarse_face_data[coarse_quad_index] & shader_data.coarse_face_hidden_mask) != 0;
|
||||
}
|
||||
|
||||
/* Flag for paint mode overlay and normals drawing in edit-mode. */
|
||||
int get_loop_flag(uint coarse_quad_index, int vert_origindex)
|
||||
{
|
||||
if (is_face_hidden(coarse_quad_index) || (shader_data.is_edit_mode && vert_origindex == -1)) {
|
||||
return -1;
|
||||
}
|
||||
if (is_face_selected(coarse_quad_index)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The start index of the loop is quad_index * 4. */
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
uint coarse_quad_index = coarse_face_index_from_subdiv_quad_index(
|
||||
quad_index, uint(shader_data.coarse_face_count));
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
int origindex = input_vert_origindex[start_loop_index + i];
|
||||
int flag = get_loop_flag(coarse_quad_index, origindex);
|
||||
|
||||
flags[start_loop_index + i] = flag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "subdiv_lib.glsl"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(subdiv_sculpt_data)
|
||||
|
||||
void main()
|
||||
{
|
||||
/* We execute for each quad. */
|
||||
uint quad_index = get_global_invocation_index();
|
||||
if (quad_index >= shader_data.total_dispatch_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint start_loop_index = quad_index * 4;
|
||||
|
||||
for (uint loop_index = start_loop_index; loop_index < start_loop_index + 4; loop_index++) {
|
||||
SculptData data;
|
||||
data.face_set_color = sculpt_face_set_color[loop_index];
|
||||
|
||||
if (shader_data.has_sculpt_mask) {
|
||||
data.mask = sculpt_mask[loop_index];
|
||||
}
|
||||
else {
|
||||
data.mask = 0.0f;
|
||||
}
|
||||
|
||||
sculpt_data[loop_index] = data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user