Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -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()

View File

@@ -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() {}

View File

@@ -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;
}
/** \} */

View File

@@ -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;
}

View File

@@ -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;
}
}
}
}

View File

@@ -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()

View File

@@ -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>();
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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
/** \} */

View File

@@ -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()
/** \} */

View File

@@ -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);
}

View File

@@ -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);
}
/*==========================================================================*/

View File

@@ -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

View File

@@ -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);
}
/** \} */

View File

@@ -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);
}
/** \} */

View File

@@ -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

View File

@@ -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));
}
/** \} */

View File

@@ -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()
/** \} */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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};
}
/** \} */

View File

@@ -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

View File

@@ -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
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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;
}
}

View 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()
/** \} */

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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()
/** \} */

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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];
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}