Add Chromium-only Blender WebEngine parity work
This commit is contained in:
114
blender-5.2.0/source/blender/gpu/shaders/CMakeLists.txt
Normal file
114
blender-5.2.0/source/blender/gpu/shaders/CMakeLists.txt
Normal file
@@ -0,0 +1,114 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC_GLSL
|
||||
.
|
||||
..
|
||||
../intern
|
||||
|
||||
# For variadic macros
|
||||
../../blenlib
|
||||
|
||||
common
|
||||
infos
|
||||
)
|
||||
|
||||
set(SRC_GLSL_VERT
|
||||
gpu_shader_2D_area_borders_vert.glsl
|
||||
gpu_shader_2D_image_rect_vert.glsl
|
||||
gpu_shader_2D_image_vert.glsl
|
||||
gpu_shader_2D_node_socket_vert.glsl
|
||||
gpu_shader_2D_point_uniform_size_aa_vert.glsl
|
||||
gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl
|
||||
gpu_shader_2D_point_varying_size_varying_color_vert.glsl
|
||||
gpu_shader_3D_polyline_vert.glsl
|
||||
gpu_shader_2D_vert.glsl
|
||||
gpu_shader_2D_widget_shadow_vert.glsl
|
||||
gpu_shader_3D_clipped_uniform_color_vert.glsl
|
||||
gpu_shader_3D_flat_color_vert.glsl
|
||||
gpu_shader_3D_image_vert.glsl
|
||||
gpu_shader_3D_line_dashed_uniform_color_vert.glsl
|
||||
gpu_shader_3D_normal_vert.glsl
|
||||
gpu_shader_3D_point_uniform_size_aa_vert.glsl
|
||||
gpu_shader_3D_point_varying_size_varying_color_vert.glsl
|
||||
gpu_shader_3D_point_flat_color_vert.glsl
|
||||
gpu_shader_3D_smooth_color_vert.glsl
|
||||
gpu_shader_display_fallback_vert.glsl
|
||||
gpu_shader_gpencil_stroke_vert.glsl
|
||||
gpu_shader_icon_multi_vert.glsl
|
||||
gpu_shader_icon_vert.glsl
|
||||
gpu_shader_keyframe_shape_vert.glsl
|
||||
gpu_shader_sequencer_strips_vert.glsl
|
||||
gpu_shader_sequencer_thumbs_vert.glsl
|
||||
gpu_shader_text_vert.glsl
|
||||
)
|
||||
|
||||
set(SRC_GLSL_FRAG
|
||||
gpu_shader_2D_area_borders_frag.glsl
|
||||
gpu_shader_2D_line_dashed_frag.glsl
|
||||
gpu_shader_2D_node_socket_frag.glsl
|
||||
gpu_shader_2D_widget_shadow_frag.glsl
|
||||
gpu_shader_3D_polyline_frag.glsl
|
||||
gpu_shader_3D_smooth_color_frag.glsl
|
||||
gpu_shader_checker_frag.glsl
|
||||
gpu_shader_depth_only_frag.glsl
|
||||
gpu_shader_diag_stripes_frag.glsl
|
||||
gpu_shader_display_fallback_frag.glsl
|
||||
gpu_shader_flat_color_frag.glsl
|
||||
gpu_shader_gpencil_stroke_frag.glsl
|
||||
gpu_shader_icon_frag.glsl
|
||||
gpu_shader_image_color_frag.glsl
|
||||
gpu_shader_image_desaturate_frag.glsl
|
||||
gpu_shader_image_frag.glsl
|
||||
gpu_shader_image_overlays_merge_frag.glsl
|
||||
gpu_shader_image_overlays_stereo_merge_frag.glsl
|
||||
gpu_shader_image_shuffle_color_frag.glsl
|
||||
gpu_shader_keyframe_shape_frag.glsl
|
||||
gpu_shader_point_uniform_color_aa_frag.glsl
|
||||
gpu_shader_point_uniform_color_outline_aa_frag.glsl
|
||||
gpu_shader_point_varying_color_frag.glsl
|
||||
gpu_shader_sequencer_scope_frag.glsl
|
||||
gpu_shader_sequencer_strips_frag.glsl
|
||||
gpu_shader_sequencer_thumbs_frag.glsl
|
||||
gpu_shader_sequencer_zebra_frag.glsl
|
||||
gpu_shader_simple_lighting_frag.glsl
|
||||
gpu_shader_text_frag.glsl
|
||||
gpu_shader_uniform_color_frag.glsl
|
||||
)
|
||||
|
||||
set(SRC_GLSL_COMP
|
||||
gpu_shader_sequencer_scope_comp.glsl
|
||||
# TODO rename them properly to enable compilation.
|
||||
# gpu_shader_index_2d_array_lines.glsl
|
||||
# gpu_shader_index_2d_array_points.glsl
|
||||
# gpu_shader_index_2d_array_tris.glsl
|
||||
)
|
||||
|
||||
set(SRC_VULKAN_GLSL_COMP
|
||||
# TODO incorrect path, if corrected fails c++ compile
|
||||
# vk_backbuffer_blit_comp.glsl
|
||||
)
|
||||
|
||||
set(SRC_BSL
|
||||
bsl_shader_linting.cc
|
||||
)
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND SRC_GLSL_COMP ${SRC_VULKAN_GLSL_COMP})
|
||||
endif()
|
||||
|
||||
set(SRC_GLSL_LIB
|
||||
common/gpu_shader_print_lib.glsl
|
||||
)
|
||||
|
||||
# Compile shaders with shader code.
|
||||
if(WITH_GPU_SHADER_CPP_COMPILATION)
|
||||
compile_sources_as_cpp(gpu_cpp_shaders_vert "${SRC_GLSL_VERT}" "GPU_VERTEX_SHADER")
|
||||
compile_sources_as_cpp(gpu_cpp_shaders_frag "${SRC_GLSL_FRAG}" "GPU_FRAGMENT_SHADER")
|
||||
compile_sources_as_cpp(gpu_cpp_shaders_comp "${SRC_GLSL_COMP}" "GPU_COMPUTE_SHADER")
|
||||
compile_sources_as_cpp(gpu_cpp_shaders_bsl "${SRC_GLSL_COMP}" "")
|
||||
# Only enable to make sure they compile on their own.
|
||||
# Otherwise it creates a warning about `pragma once`.
|
||||
# compile_sources_as_cpp(gpu_cpp_shaders_lib "${SRC_GLSL_LIB}" "GPU_LIBRARY_SHADER")
|
||||
endif()
|
||||
@@ -0,0 +1,13 @@
|
||||
/* 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 "gpu_shader_2D_nodelink.bsl.hh" /* IWYU pragma: export */
|
||||
#include "gpu_shader_2D_update_mipmaps.bsl.hh" /* IWYU pragma: export */
|
||||
#include "gpu_shader_2D_widget_base.bsl.hh" /* IWYU pragma: export */
|
||||
|
||||
void main() {}
|
||||
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/**
|
||||
* Library to read packed vertex buffer data of a `gpu::Batch` using a SSBO rather than using input
|
||||
* assembly. It is **not** needed to use these macros if the data is known to be aligned and
|
||||
* contiguous. Arrays of any 4-byte component vector except 3 component vectors do not need this.
|
||||
*
|
||||
* Implemented as macros to avoid compiler differences with buffer qualifiers.
|
||||
*/
|
||||
|
||||
/** Returns index in the first component. Needed for non trivially packed data. */
|
||||
uint gpu_attr_load_index(uint vertex_index, int2 stride_and_offset)
|
||||
{
|
||||
return vertex_index * uint(stride_and_offset.x) + uint(stride_and_offset.y);
|
||||
}
|
||||
|
||||
float4 gpu_attr_decode_1010102_snorm(uint in_data)
|
||||
{
|
||||
/* TODO(fclem): Improve this. */
|
||||
uint4 v_data = uint4(in_data) >> uint4(0, 10, 20, 30);
|
||||
bool4 v_sign = greaterThan(v_data & uint4(0x3FF, 0x3FF, 0x3FF, 0x3),
|
||||
uint4(0x1FF, 0x1FF, 0x1FF, 0x1));
|
||||
uint4 v_data_u = floatBitsToUint(mix(uintBitsToFloat(v_data), uintBitsToFloat(~v_data), v_sign));
|
||||
float4 mag = float4(v_data_u & uint4(0x1FF, 0x1FF, 0x1FF, 0x1)) /
|
||||
float4(0x1FF, 0x1FF, 0x1FF, 0x1);
|
||||
return mix(mag, -mag, v_sign);
|
||||
}
|
||||
|
||||
float4 gpu_attr_decode_short4_to_float4_snorm(uint data0, uint data1)
|
||||
{
|
||||
/* TODO(fclem): Improve this. */
|
||||
uint4 v_data = uint4(data0, data0 >> 16u, data1, data1 >> 16u);
|
||||
bool4 v_sign = greaterThan(v_data & uint4(0xFFFF), uint4(0x7FFF));
|
||||
uint4 v_data_u = floatBitsToUint(mix(uintBitsToFloat(v_data), uintBitsToFloat(~v_data), v_sign));
|
||||
float4 mag = float4(v_data_u & 0x7FFFu) / float(0x7FFF);
|
||||
return mix(mag, -mag, v_sign);
|
||||
}
|
||||
|
||||
uint4 gpu_attr_decode_uchar4_to_uint4(uint in_data)
|
||||
{
|
||||
return (uint4(in_data) >> uint4(0, 8, 16, 24)) & uint4(0xFF);
|
||||
}
|
||||
|
||||
/* TODO(fclem): Once the stride and offset are made obsolete, we can think of wrapping vec3 into
|
||||
* structs of floats as they do not have the 16byte alignment restriction. */
|
||||
|
||||
#define gpu_attr_load_triplet(_type, _data, _stride_and_offset, _i) \
|
||||
_type(_data[gpu_attr_load_index(_i, _stride_and_offset) + 0], \
|
||||
_data[gpu_attr_load_index(_i, _stride_and_offset) + 1], \
|
||||
_data[gpu_attr_load_index(_i, _stride_and_offset) + 2])
|
||||
#define gpu_attr_load_tuple(_type, _data, _stride_and_offset, _i) \
|
||||
_type(_data[gpu_attr_load_index(_i, _stride_and_offset) + 0], \
|
||||
_data[gpu_attr_load_index(_i, _stride_and_offset) + 1])
|
||||
|
||||
/* Assumes _data is declared as an array of float. */
|
||||
#define gpu_attr_load_float3(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_load_triplet(float3, _data, _stride_and_offset, _i)
|
||||
#define gpu_attr_load_float2(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_load_tuple(float2, _data, _stride_and_offset, _i)
|
||||
/* Assumes _data is declared as an array of uint. */
|
||||
#define gpu_attr_load_uint3(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_load_triplet(int3, _data, _stride_and_offset, _i)
|
||||
#define gpu_attr_load_uint2(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_load_tuple(int2, _data, _stride_and_offset, _i)
|
||||
/* Assumes _data is declared as an array of int. */
|
||||
#define gpu_attr_load_int3(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_load_triplet(uint3, _data, _stride_and_offset, _i)
|
||||
#define gpu_attr_load_int2(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_load_tuple(uint2, _data, _stride_and_offset, _i)
|
||||
|
||||
/* Assumes _data is declared as an array of uint. */
|
||||
#define gpu_attr_load_uint_1010102_snorm(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_decode_1010102_snorm(_data[gpu_attr_load_index(_i, _stride_and_offset)])
|
||||
|
||||
/* TODO(fclem): Once the stride and offset are made obsolete, we can think of wrapping short4 into
|
||||
* structs of uint as they do not have the 16byte alignment restriction. */
|
||||
|
||||
/* Assumes _data is declared as an array of uint. */
|
||||
#define gpu_attr_load_short4_snorm(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_decode_short4_to_float4_snorm(_data[gpu_attr_load_index(_i, _stride_and_offset) + 0], \
|
||||
_data[gpu_attr_load_index(_i, _stride_and_offset) + 1])
|
||||
/* Assumes _data is declared as an array of uint. */
|
||||
#define gpu_attr_load_uchar4(_data, _stride_and_offset, _i) \
|
||||
gpu_attr_decode_uchar4_to_uint4(_data[gpu_attr_load_index(_i, _stride_and_offset)])
|
||||
/* Assumes _data is declared as an array of uint. */
|
||||
#define gpu_attr_load_uchar(_data, _i) \
|
||||
gpu_attr_decode_uchar4_to_uint4( \
|
||||
_data[gpu_attr_load_index(uint(_i) >> 2u, int2(1, 0))])[uint(_i) & 3u]
|
||||
/* Assumes _data is declared as an array of uint. */
|
||||
#define gpu_attr_load_bool(_data, _i) (gpu_attr_load_uchar(_data, _i) != 0u)
|
||||
@@ -0,0 +1,77 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/** \param f: Offset from texel center in pixel space. */
|
||||
void cubic_bspline_coefficients(float2 f, float2 &w0, float2 &w1, float2 &w2, float2 &w3)
|
||||
{
|
||||
float2 f2 = f * f;
|
||||
float2 f3 = f2 * f;
|
||||
/* Optimized formulae for cubic B-Spline coefficients. */
|
||||
w3 = f3 / 6.0f;
|
||||
w0 = -w3 + f2 * 0.5f - f * 0.5f + 1.0f / 6.0f;
|
||||
w1 = f3 * 0.5f - f2 * 1.0f + 2.0f / 3.0f;
|
||||
w2 = 1.0f - w0 - w1 - w3;
|
||||
}
|
||||
|
||||
/* Samples the given 2D sampler at the given coordinates using Bicubic interpolation. This function
|
||||
* uses an optimized algorithm which assumes a linearly filtered sampler, so the caller needs to
|
||||
* take that into account when setting up the sampler. */
|
||||
float4 texture_bicubic(sampler2D sampler_2d, float2 coordinates)
|
||||
{
|
||||
float2 texture_size = float2(textureSize(sampler_2d, 0).xy);
|
||||
coordinates.xy *= texture_size;
|
||||
|
||||
float2 w0, w1, w2, w3;
|
||||
float2 texel_center = floor(coordinates.xy - 0.5f) + 0.5f;
|
||||
cubic_bspline_coefficients(coordinates.xy - texel_center, w0, w1, w2, w3);
|
||||
|
||||
#if 1 /* Optimized version using 4 filtered taps. */
|
||||
float2 s0 = w0 + w1;
|
||||
float2 s1 = w2 + w3;
|
||||
|
||||
float2 f0 = w1 / (w0 + w1);
|
||||
float2 f1 = w3 / (w2 + w3);
|
||||
|
||||
float4 sampling_coordinates;
|
||||
sampling_coordinates.xy = texel_center - 1.0f + f0;
|
||||
sampling_coordinates.zw = texel_center + 1.0f + f1;
|
||||
|
||||
sampling_coordinates /= texture_size.xyxy;
|
||||
|
||||
float4 sampled_color = textureLod(sampler_2d, sampling_coordinates.xy, 0.0f) * s0.x * s0.y;
|
||||
sampled_color += textureLod(sampler_2d, sampling_coordinates.zy, 0.0f) * s1.x * s0.y;
|
||||
sampled_color += textureLod(sampler_2d, sampling_coordinates.xw, 0.0f) * s0.x * s1.y;
|
||||
sampled_color += textureLod(sampler_2d, sampling_coordinates.zw, 0.0f) * s1.x * s1.y;
|
||||
|
||||
return sampled_color;
|
||||
|
||||
#else /* Reference brute-force 16 taps. */
|
||||
float4 color = texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, -1.0f)), 0) * w0.x *
|
||||
w0.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, -1.0f)), 0) * w1.x * w0.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, -1.0f)), 0) * w2.x * w0.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, -1.0f)), 0) * w3.x * w0.y;
|
||||
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, 0.0f)), 0) * w0.x * w1.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, 0.0f)), 0) * w1.x * w1.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, 0.0f)), 0) * w2.x * w1.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, 0.0f)), 0) * w3.x * w1.y;
|
||||
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, 1.0f)), 0) * w0.x * w2.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, 1.0f)), 0) * w1.x * w2.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, 1.0f)), 0) * w2.x * w2.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, 1.0f)), 0) * w3.x * w2.y;
|
||||
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(-1.0f, 2.0f)), 0) * w0.x * w3.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(0.0f, 2.0f)), 0) * w1.x * w3.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(1.0f, 2.0f)), 0) * w2.x * w3.y;
|
||||
color += texelFetch(sampler_2d, int2(texel_center + float2(2.0f, 2.0f)), 0) * w3.x * w3.y;
|
||||
|
||||
return color;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
[[node]]
|
||||
void valtorgb_opti_constant(
|
||||
float fac, float edge, float4 color1, float4 color2, float4 &outcol, float &outalpha)
|
||||
{
|
||||
outcol = (fac > edge) ? color2 : color1;
|
||||
outalpha = outcol.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void valtorgb_opti_linear(
|
||||
float fac, float2 mulbias, float4 color1, float4 color2, float4 &outcol, float &outalpha)
|
||||
{
|
||||
fac = clamp(fac * mulbias.x + mulbias.y, 0.0f, 1.0f);
|
||||
outcol = mix(color1, color2, fac);
|
||||
outalpha = outcol.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void valtorgb_opti_ease(
|
||||
float fac, float2 mulbias, float4 color1, float4 color2, float4 &outcol, float &outalpha)
|
||||
{
|
||||
fac = clamp(fac * mulbias.x + mulbias.y, 0.0f, 1.0f);
|
||||
fac = fac * fac * (3.0f - 2.0f * fac);
|
||||
outcol = mix(color1, color2, fac);
|
||||
outalpha = outcol.a;
|
||||
}
|
||||
|
||||
/* Color maps are stored in texture samplers, so ensure that the coordinate evaluates the sampler
|
||||
* at the center of the pixels, because samplers are evaluated using linear interpolation. Given
|
||||
* the coordinate in the [0, 1] range. */
|
||||
float compute_color_map_coordinate(float coordinate)
|
||||
{
|
||||
/* Color maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale
|
||||
* down such that the normalized coordinate 1.0 corresponds to the center of the last pixel. */
|
||||
constexpr float sampler_resolution = 257.0f;
|
||||
constexpr float sampler_offset = 0.5f / sampler_resolution;
|
||||
constexpr float sampler_scale = 1.0f - (1.0f / sampler_resolution);
|
||||
return coordinate * sampler_scale + sampler_offset;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void valtorgb(float fac, sampler1DArray colormap, float layer, float4 &outcol, float &outalpha)
|
||||
{
|
||||
outcol = texture(colormap, float2(compute_color_map_coordinate(fac), layer));
|
||||
outalpha = outcol.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void valtorgb_nearest(
|
||||
float fac, sampler1DArray colormap, float layer, float4 &outcol, float &outalpha)
|
||||
{
|
||||
fac = clamp(fac, 0.0f, 1.0f);
|
||||
outcol = texelFetch(colormap, int2(fac * (textureSize(colormap, 0).x - 1), layer), 0);
|
||||
outalpha = outcol.a;
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
[[node]]
|
||||
void rgb_to_hsv(float4 rgb, float4 &outcol)
|
||||
{
|
||||
float cmax, cmin, h, s, v, cdelta;
|
||||
float3 c;
|
||||
|
||||
cmax = max(rgb[0], max(rgb[1], rgb[2]));
|
||||
cmin = min(rgb[0], min(rgb[1], rgb[2]));
|
||||
cdelta = cmax - cmin;
|
||||
|
||||
v = cmax;
|
||||
if (cmax != 0.0f) {
|
||||
s = cdelta / cmax;
|
||||
}
|
||||
else {
|
||||
s = 0.0f;
|
||||
h = 0.0f;
|
||||
}
|
||||
|
||||
if (s == 0.0f) {
|
||||
h = 0.0f;
|
||||
}
|
||||
else {
|
||||
c = (float3(cmax) - rgb.xyz) / cdelta;
|
||||
|
||||
if (rgb.x == cmax) {
|
||||
h = c[2] - c[1];
|
||||
}
|
||||
else if (rgb.y == cmax) {
|
||||
h = 2.0f + c[0] - c[2];
|
||||
}
|
||||
else {
|
||||
h = 4.0f + c[1] - c[0];
|
||||
}
|
||||
|
||||
h /= 6.0f;
|
||||
|
||||
if (h < 0.0f) {
|
||||
h += 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
outcol = float4(h, s, v, rgb.w);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void hsv_to_rgb(float4 hsv, float4 &outcol)
|
||||
{
|
||||
float i, f, p, q, t, h, s, v;
|
||||
float3 rgb;
|
||||
|
||||
h = hsv[0];
|
||||
s = hsv[1];
|
||||
v = hsv[2];
|
||||
|
||||
if (s == 0.0f) {
|
||||
rgb = float3(v, v, v);
|
||||
}
|
||||
else {
|
||||
if (h == 1.0f) {
|
||||
h = 0.0f;
|
||||
}
|
||||
|
||||
h *= 6.0f;
|
||||
i = floor(h);
|
||||
f = h - i;
|
||||
rgb = float3(f, f, f);
|
||||
p = v * (1.0f - s);
|
||||
q = v * (1.0f - (s * f));
|
||||
t = v * (1.0f - (s * (1.0f - f)));
|
||||
|
||||
if (i == 0.0f) {
|
||||
rgb = float3(v, t, p);
|
||||
}
|
||||
else if (i == 1.0f) {
|
||||
rgb = float3(q, v, p);
|
||||
}
|
||||
else if (i == 2.0f) {
|
||||
rgb = float3(p, v, t);
|
||||
}
|
||||
else if (i == 3.0f) {
|
||||
rgb = float3(p, q, v);
|
||||
}
|
||||
else if (i == 4.0f) {
|
||||
rgb = float3(t, p, v);
|
||||
}
|
||||
else {
|
||||
rgb = float3(v, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
outcol = float4(rgb, hsv.w);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void rgb_to_hsl(float4 rgb, float4 &outcol)
|
||||
{
|
||||
float cmax, cmin, h, s, l;
|
||||
|
||||
cmax = max(rgb[0], max(rgb[1], rgb[2]));
|
||||
cmin = min(rgb[0], min(rgb[1], rgb[2]));
|
||||
l = min(1.0f, (cmax + cmin) / 2.0f);
|
||||
|
||||
if (cmax == cmin) {
|
||||
h = s = 0.0f; /* achromatic */
|
||||
}
|
||||
else {
|
||||
float cdelta = cmax - cmin;
|
||||
s = l > 0.5f ? cdelta / (2.0f - cmax - cmin) : cdelta / (cmax + cmin);
|
||||
if (cmax == rgb[0]) {
|
||||
h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0f : 0.0f);
|
||||
}
|
||||
else if (cmax == rgb[1]) {
|
||||
h = (rgb[2] - rgb[0]) / cdelta + 2.0f;
|
||||
}
|
||||
else {
|
||||
h = (rgb[0] - rgb[1]) / cdelta + 4.0f;
|
||||
}
|
||||
}
|
||||
h /= 6.0f;
|
||||
|
||||
outcol = float4(h, s, l, rgb.w);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void hsl_to_rgb(float4 hsl, float4 &outcol)
|
||||
{
|
||||
float nr, ng, nb, chroma, h, s, l;
|
||||
|
||||
h = hsl[0];
|
||||
s = hsl[1];
|
||||
l = hsl[2];
|
||||
|
||||
nr = abs(h * 6.0f - 3.0f) - 1.0f;
|
||||
ng = 2.0f - abs(h * 6.0f - 2.0f);
|
||||
nb = 2.0f - abs(h * 6.0f - 4.0f);
|
||||
|
||||
nr = clamp(nr, 0.0f, 1.0f);
|
||||
nb = clamp(nb, 0.0f, 1.0f);
|
||||
ng = clamp(ng, 0.0f, 1.0f);
|
||||
|
||||
chroma = (1.0f - abs(2.0f * l - 1.0f)) * s;
|
||||
|
||||
outcol = float4(
|
||||
(nr - 0.5f) * chroma + l, (ng - 0.5f) * chroma + l, (nb - 0.5f) * chroma + l, hsl.w);
|
||||
}
|
||||
|
||||
/* ** YCCA to RGBA ** */
|
||||
|
||||
[[node]]
|
||||
void ycca_to_rgba_itu_601(float4 ycca, float4 &color)
|
||||
{
|
||||
ycca.xyz *= 255.0f;
|
||||
ycca.xyz -= float3(16.0f, 128.0f, 128.0f);
|
||||
color.rgb = float3x3(1.164f, 1.164f, 1.164f, 0.0f, -0.392f, 2.017f, 1.596f, -0.813f, 0.0f) *
|
||||
ycca.xyz;
|
||||
color.rgb /= 255.0f;
|
||||
color.a = ycca.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void ycca_to_rgba_itu_709(float4 ycca, float4 &color)
|
||||
{
|
||||
ycca.xyz *= 255.0f;
|
||||
ycca.xyz -= float3(16.0f, 128.0f, 128.0f);
|
||||
color.rgb = float3x3(1.164f, 1.164f, 1.164f, 0.0f, -0.213f, 2.115f, 1.793f, -0.534f, 0.0f) *
|
||||
ycca.xyz;
|
||||
color.rgb /= 255.0f;
|
||||
color.a = ycca.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void ycca_to_rgba_jpeg(float4 ycca, float4 &color)
|
||||
{
|
||||
ycca.xyz *= 255.0f;
|
||||
color.rgb = float3x3(1.0f, 1.0f, 1.0f, 0.0f, -0.34414f, 1.772f, 1.402f, -0.71414f, 0.0f) *
|
||||
ycca.xyz;
|
||||
color.rgb += float3(-179.456f, 135.45984f, -226.816f);
|
||||
color.rgb /= 255.0f;
|
||||
color.a = ycca.a;
|
||||
}
|
||||
|
||||
/* ** RGBA to YCCA ** */
|
||||
|
||||
[[node]]
|
||||
void rgba_to_ycca_itu_601(float4 rgba, float4 &ycca)
|
||||
{
|
||||
rgba.rgb *= 255.0f;
|
||||
ycca.xyz = float3x3(0.257f, -0.148f, 0.439f, 0.504f, -0.291f, -0.368f, 0.098f, 0.439f, -0.071f) *
|
||||
rgba.rgb;
|
||||
ycca.xyz += float3(16.0f, 128.0f, 128.0f);
|
||||
ycca.xyz /= 255.0f;
|
||||
ycca.a = rgba.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void rgba_to_ycca_itu_709(float4 rgba, float4 &ycca)
|
||||
{
|
||||
rgba.rgb *= 255.0f;
|
||||
ycca.xyz = float3x3(0.183f, -0.101f, 0.439f, 0.614f, -0.338f, -0.399f, 0.062f, 0.439f, -0.040f) *
|
||||
rgba.rgb;
|
||||
ycca.xyz += float3(16.0f, 128.0f, 128.0f);
|
||||
ycca.xyz /= 255.0f;
|
||||
ycca.a = rgba.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void rgba_to_ycca_jpeg(float4 rgba, float4 &ycca)
|
||||
{
|
||||
rgba.rgb *= 255.0f;
|
||||
ycca.xyz = float3x3(
|
||||
0.299f, -0.16874f, 0.5f, 0.587f, -0.33126f, -0.41869f, 0.114f, 0.5f, -0.08131f) *
|
||||
rgba.rgb;
|
||||
ycca.xyz += float3(0.0f, 128.0f, 128.0f);
|
||||
ycca.xyz /= 255.0f;
|
||||
ycca.a = rgba.a;
|
||||
}
|
||||
|
||||
/* ** YUVA to RGBA ** */
|
||||
|
||||
[[node]]
|
||||
void yuva_to_rgba_itu_709(float4 yuva, float4 &color)
|
||||
{
|
||||
color.rgb = float3x3(1.0f, 1.0f, 1.0f, 0.0f, -0.21482f, 2.12798f, 1.28033f, -0.38059f, 0.0f) *
|
||||
yuva.xyz;
|
||||
color.a = yuva.a;
|
||||
}
|
||||
|
||||
/* ** RGBA to YUVA ** */
|
||||
|
||||
[[node]]
|
||||
void rgba_to_yuva_itu_709(float4 rgba, float4 &yuva)
|
||||
{
|
||||
yuva.xyz =
|
||||
float3x3(
|
||||
0.2126f, -0.09991f, 0.615f, 0.7152f, -0.33609f, -0.55861f, 0.0722f, 0.436f, -0.05639f) *
|
||||
rgba.rgb;
|
||||
yuva.a = rgba.a;
|
||||
}
|
||||
|
||||
/* ** Alpha Handling ** */
|
||||
|
||||
[[node]]
|
||||
void color_alpha_clear(float4 color, float4 &result)
|
||||
{
|
||||
result = float4(color.rgb, 1.0f);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void color_alpha_premultiply(float4 color, float4 &result)
|
||||
{
|
||||
result = float4(color.rgb * color.a, color.a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void color_alpha_unpremultiply(float4 color, float4 &result)
|
||||
{
|
||||
if (color.a == 0.0f || color.a == 1.0f) {
|
||||
result = color;
|
||||
}
|
||||
else {
|
||||
result = float4(color.rgb / color.a, color.a);
|
||||
}
|
||||
}
|
||||
|
||||
float linear_rgb_to_srgb(float color)
|
||||
{
|
||||
if (color < 0.0031308f) {
|
||||
return (color < 0.0f) ? 0.0f : color * 12.92f;
|
||||
}
|
||||
|
||||
return 1.055f * pow(color, 1.0f / 2.4f) - 0.055f;
|
||||
}
|
||||
|
||||
float3 linear_rgb_to_srgb(float3 color)
|
||||
{
|
||||
return float3(
|
||||
linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b));
|
||||
}
|
||||
|
||||
float srgb_to_linear_rgb(float color)
|
||||
{
|
||||
if (color < 0.04045f) {
|
||||
return (color < 0.0f) ? 0.0f : color * (1.0f / 12.92f);
|
||||
}
|
||||
|
||||
return pow((color + 0.055f) * (1.0f / 1.055f), 2.4f);
|
||||
}
|
||||
|
||||
float3 srgb_to_linear_rgb(float3 color)
|
||||
{
|
||||
return float3(
|
||||
srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b));
|
||||
}
|
||||
|
||||
float get_luminance(float3 color, float3 luminance_coefficients)
|
||||
{
|
||||
return dot(color, luminance_coefficients);
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
float4 white_balance(float4 color, float4 black_level, float4 white_level)
|
||||
{
|
||||
float4 range = max(white_level - black_level, float4(1e-5f));
|
||||
return (color - black_level) / range;
|
||||
}
|
||||
|
||||
float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope)
|
||||
{
|
||||
if (parameter < 0.0f) {
|
||||
return value + parameter * start_slope;
|
||||
}
|
||||
|
||||
if (parameter > 1.0f) {
|
||||
return value + (parameter - 1.0f) * end_slope;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Same as extrapolate_if_needed but vectorized. */
|
||||
float3 extrapolate_if_needed(float3 parameters,
|
||||
float3 values,
|
||||
float3 start_slopes,
|
||||
float3 end_slopes)
|
||||
{
|
||||
float3 end_or_zero_slopes = mix(float3(0.0f), end_slopes, greaterThan(parameters, float3(1.0f)));
|
||||
float3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, float3(0.0f)));
|
||||
parameters = parameters - mix(float3(0.0f), float3(1.0f), greaterThan(parameters, float3(1.0f)));
|
||||
return values + parameters * slopes;
|
||||
}
|
||||
|
||||
/* Curve maps are stored in texture samplers, so ensure that the parameters evaluate the sampler at
|
||||
* the center of the pixels, because samplers are evaluated using linear interpolation. Given the
|
||||
* parameter in the [0, 1] range. */
|
||||
float compute_curve_map_coordinates(float parameter)
|
||||
{
|
||||
/* Curve maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale
|
||||
* down such that the normalized parameter 1.0 corresponds to the center of the last pixel. */
|
||||
float sampler_offset = 0.5f / 257.0f;
|
||||
float sampler_scale = 1.0f - (1.0f / 257.0f);
|
||||
return parameter * sampler_scale + sampler_offset;
|
||||
}
|
||||
|
||||
/* Same as compute_curve_map_coordinates but vectorized. */
|
||||
float3 compute_curve_map_coordinates(float3 parameters)
|
||||
{
|
||||
float sampler_offset = 0.5f / 257.0f;
|
||||
float sampler_scale = 1.0f - (1.0f / 257.0f);
|
||||
return parameters * sampler_scale + sampler_offset;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_combined_rgb(float factor,
|
||||
float4 color,
|
||||
float4 black_level,
|
||||
float4 white_level,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float4 range_minimums,
|
||||
float4 range_dividers,
|
||||
float4 start_slopes,
|
||||
float4 end_slopes,
|
||||
float4 &result)
|
||||
{
|
||||
float4 balanced = white_balance(color, black_level, white_level);
|
||||
|
||||
/* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
|
||||
* UI. The channels are first normalized into the [0, 1] range. */
|
||||
float3 parameters = (balanced.rgb - range_minimums.aaa) * range_dividers.aaa;
|
||||
float3 coordinates = compute_curve_map_coordinates(parameters);
|
||||
result.r = texture(curve_map, float2(coordinates.x, layer)).a;
|
||||
result.g = texture(curve_map, float2(coordinates.y, layer)).a;
|
||||
result.b = texture(curve_map, float2(coordinates.z, layer)).a;
|
||||
|
||||
/* Then, extrapolate if needed. */
|
||||
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa);
|
||||
|
||||
/* Then, evaluate each channel on its curve map. The channels are first normalized into the
|
||||
* [0, 1] range. */
|
||||
parameters = (result.rgb - range_minimums.rgb) * range_dividers.rgb;
|
||||
coordinates = compute_curve_map_coordinates(parameters);
|
||||
result.r = texture(curve_map, float2(coordinates.r, layer)).r;
|
||||
result.g = texture(curve_map, float2(coordinates.g, layer)).g;
|
||||
result.b = texture(curve_map, float2(coordinates.b, layer)).b;
|
||||
|
||||
/* Then, extrapolate again if needed. */
|
||||
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb);
|
||||
|
||||
result.a = color.a;
|
||||
|
||||
result = mix(color, result, factor);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_combined_rgb_compositor(float4 color,
|
||||
float factor,
|
||||
float4 black_level,
|
||||
float4 white_level,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float4 range_minimums,
|
||||
float4 range_dividers,
|
||||
float4 start_slopes,
|
||||
float4 end_slopes,
|
||||
float4 &result)
|
||||
{
|
||||
curves_combined_rgb(factor,
|
||||
color,
|
||||
black_level,
|
||||
white_level,
|
||||
curve_map,
|
||||
layer,
|
||||
range_minimums,
|
||||
range_dividers,
|
||||
start_slopes,
|
||||
end_slopes,
|
||||
result);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_combined_only(float factor,
|
||||
float4 color,
|
||||
float4 black_level,
|
||||
float4 white_level,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float range_minimum,
|
||||
float range_divider,
|
||||
float start_slope,
|
||||
float end_slope,
|
||||
float4 &result)
|
||||
{
|
||||
float4 balanced = white_balance(color, black_level, white_level);
|
||||
|
||||
/* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
|
||||
* UI. The channels are first normalized into the [0, 1] range. */
|
||||
float3 parameters = (balanced.rgb - float3(range_minimum)) * float3(range_divider);
|
||||
float3 coordinates = compute_curve_map_coordinates(parameters);
|
||||
result.r = texture(curve_map, float2(coordinates.x, layer)).a;
|
||||
result.g = texture(curve_map, float2(coordinates.y, layer)).a;
|
||||
result.b = texture(curve_map, float2(coordinates.z, layer)).a;
|
||||
|
||||
/* Then, extrapolate if needed. */
|
||||
result.rgb = extrapolate_if_needed(
|
||||
parameters, result.rgb, float3(start_slope), float3(end_slope));
|
||||
|
||||
result.a = color.a;
|
||||
|
||||
result = mix(color, result, factor);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_combined_only_compositor(float4 color,
|
||||
float factor,
|
||||
float4 black_level,
|
||||
float4 white_level,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float range_minimum,
|
||||
float range_divider,
|
||||
float start_slope,
|
||||
float end_slope,
|
||||
float4 &result)
|
||||
{
|
||||
curves_combined_only(factor,
|
||||
color,
|
||||
black_level,
|
||||
white_level,
|
||||
curve_map,
|
||||
layer,
|
||||
range_minimum,
|
||||
range_divider,
|
||||
start_slope,
|
||||
end_slope,
|
||||
result);
|
||||
}
|
||||
|
||||
/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve
|
||||
* the hue of the colors as much as possible. To understand why this might be a problem, consider
|
||||
* the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the
|
||||
* color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity,
|
||||
* which is what film-like tone curves tries to avoid.
|
||||
*
|
||||
* First, the channels with the lowest and highest values are identified and evaluated at the
|
||||
* curve. Then, the third channel---the median---is computed while maintaining the original hue of
|
||||
* the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming
|
||||
* the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the
|
||||
* hue, the equation is:
|
||||
*
|
||||
* hue = (median - min) / (max - min) [1]
|
||||
*
|
||||
* Since we have the new values for the minimum and maximum after evaluating at the curve, we also
|
||||
* have:
|
||||
*
|
||||
* hue = (new_median - new_min) / (new_max - new_min) [2]
|
||||
*
|
||||
* Since we want the hue to be equivalent, by equating [1] and [2] and rearranging:
|
||||
*
|
||||
* (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min)
|
||||
* new_median - new_min = (new_max - new_min) * (median - min) / (max - min)
|
||||
* new_median = new_min + (new_max - new_min) * (median - min) / (max - min)
|
||||
* new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED]
|
||||
*
|
||||
* Which gives us the median color that preserves the hue. More intuitively, the median is computed
|
||||
* such that the change in the distance from the median to the minimum is proportional to the
|
||||
* change in the distance from the minimum to the maximum. Finally, each of the new minimum,
|
||||
* maximum, and median values are written to the color channel that they were originally extracted
|
||||
* from. */
|
||||
[[node]]
|
||||
void curves_film_like(float factor,
|
||||
float4 color,
|
||||
float4 black_level,
|
||||
float4 white_level,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float range_minimum,
|
||||
float range_divider,
|
||||
float start_slope,
|
||||
float end_slope,
|
||||
float4 &result)
|
||||
{
|
||||
float4 balanced = white_balance(color, black_level, white_level);
|
||||
|
||||
/* Find the maximum, minimum, and median of the color channels. */
|
||||
float minimum = min(balanced.r, min(balanced.g, balanced.b));
|
||||
float maximum = max(balanced.r, max(balanced.g, balanced.b));
|
||||
float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g)));
|
||||
|
||||
/* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined
|
||||
* curve in the UI. The channels are first normalized into the [0, 1] range. */
|
||||
float min_parameter = (minimum - range_minimum) * range_divider;
|
||||
float max_parameter = (maximum - range_minimum) * range_divider;
|
||||
float min_coordinates = compute_curve_map_coordinates(min_parameter);
|
||||
float max_coordinates = compute_curve_map_coordinates(max_parameter);
|
||||
float new_min = texture(curve_map, float2(min_coordinates, layer)).a;
|
||||
float new_max = texture(curve_map, float2(max_coordinates, layer)).a;
|
||||
|
||||
/* Then, extrapolate if needed. */
|
||||
new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope);
|
||||
new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope);
|
||||
|
||||
/* Compute the new median using the ratio between the new and the original range. */
|
||||
float scaling_ratio = (new_max - new_min) / (maximum - minimum);
|
||||
float new_median = new_min + (median - minimum) * scaling_ratio;
|
||||
|
||||
/* Write each value to its original channel. */
|
||||
bool3 channel_is_min = equal(balanced.rgb, float3(minimum));
|
||||
float3 median_or_min = mix(float3(new_median), float3(new_min), channel_is_min);
|
||||
bool3 channel_is_max = equal(balanced.rgb, float3(maximum));
|
||||
result.rgb = mix(median_or_min, float3(new_max), channel_is_max);
|
||||
|
||||
result.a = color.a;
|
||||
|
||||
result = mix(color, result, clamp(factor, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_film_like_compositor(float4 color,
|
||||
float factor,
|
||||
float4 black_level,
|
||||
float4 white_level,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float range_minimum,
|
||||
float range_divider,
|
||||
float start_slope,
|
||||
float end_slope,
|
||||
float4 &result)
|
||||
{
|
||||
curves_film_like(factor,
|
||||
color,
|
||||
black_level,
|
||||
white_level,
|
||||
curve_map,
|
||||
layer,
|
||||
range_minimum,
|
||||
range_divider,
|
||||
start_slope,
|
||||
end_slope,
|
||||
result);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_vector(float3 vector,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float3 range_minimums,
|
||||
float3 range_dividers,
|
||||
float3 start_slopes,
|
||||
float3 end_slopes,
|
||||
float3 &result)
|
||||
{
|
||||
/* Evaluate each component on its curve map.
|
||||
* The components are first normalized into the [0, 1] range. */
|
||||
float3 parameters = (vector - range_minimums) * range_dividers;
|
||||
float3 coordinates = compute_curve_map_coordinates(parameters);
|
||||
result.x = texture(curve_map, float2(coordinates.x, layer)).x;
|
||||
result.y = texture(curve_map, float2(coordinates.y, layer)).y;
|
||||
result.z = texture(curve_map, float2(coordinates.z, layer)).z;
|
||||
|
||||
/* Then, extrapolate if needed. */
|
||||
result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_vector_mixed(float factor,
|
||||
float3 vector,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float3 range_minimums,
|
||||
float3 range_dividers,
|
||||
float3 start_slopes,
|
||||
float3 end_slopes,
|
||||
float3 &result)
|
||||
{
|
||||
curves_vector(
|
||||
vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result);
|
||||
result = mix(vector, result, factor);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_float(float value,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float range_minimum,
|
||||
float range_divider,
|
||||
float start_slope,
|
||||
float end_slope,
|
||||
float &result)
|
||||
{
|
||||
/* Evaluate the normalized value on the first curve map. */
|
||||
float parameter = (value - range_minimum) * range_divider;
|
||||
float coordinates = compute_curve_map_coordinates(parameter);
|
||||
result = texture(curve_map, float2(coordinates, layer)).x;
|
||||
|
||||
/* Then, extrapolate if needed. */
|
||||
result = extrapolate_if_needed(parameter, result, start_slope, end_slope);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void curves_float_mixed(float factor,
|
||||
float value,
|
||||
sampler1DArray curve_map,
|
||||
const float layer,
|
||||
float range_minimum,
|
||||
float range_divider,
|
||||
float start_slope,
|
||||
float end_slope,
|
||||
float &result)
|
||||
{
|
||||
curves_float(
|
||||
value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result);
|
||||
result = mix(value, result, factor);
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* ***** Jenkins Lookup3 Hash Functions ***** */
|
||||
|
||||
/* Source: http://burtleburtle.net/bob/c/lookup3.c */
|
||||
|
||||
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
|
||||
|
||||
#define mix(a, b, c) \
|
||||
{ \
|
||||
a -= c; \
|
||||
a ^= rot(c, 4); \
|
||||
c += b; \
|
||||
b -= a; \
|
||||
b ^= rot(a, 6); \
|
||||
a += c; \
|
||||
c -= b; \
|
||||
c ^= rot(b, 8); \
|
||||
b += a; \
|
||||
a -= c; \
|
||||
a ^= rot(c, 16); \
|
||||
c += b; \
|
||||
b -= a; \
|
||||
b ^= rot(a, 19); \
|
||||
a += c; \
|
||||
c -= b; \
|
||||
c ^= rot(b, 4); \
|
||||
b += a; \
|
||||
}
|
||||
|
||||
#define final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; \
|
||||
c -= rot(b, 14); \
|
||||
a ^= c; \
|
||||
a -= rot(c, 11); \
|
||||
b ^= a; \
|
||||
b -= rot(a, 25); \
|
||||
c ^= b; \
|
||||
c -= rot(b, 16); \
|
||||
a ^= c; \
|
||||
a -= rot(c, 4); \
|
||||
b ^= a; \
|
||||
b -= rot(a, 14); \
|
||||
c ^= b; \
|
||||
c -= rot(b, 24); \
|
||||
}
|
||||
|
||||
uint hash_uint(uint kx)
|
||||
{
|
||||
uint a, b, c;
|
||||
a = b = c = 0xdeadbeefu + (1u << 2u) + 13u;
|
||||
|
||||
a += kx;
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
uint hash_uint2(uint kx, uint ky)
|
||||
{
|
||||
uint a, b, c;
|
||||
a = b = c = 0xdeadbeefu + (2u << 2u) + 13u;
|
||||
|
||||
b += ky;
|
||||
a += kx;
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
uint hash_uint3(uint kx, uint ky, uint kz)
|
||||
{
|
||||
uint a, b, c;
|
||||
a = b = c = 0xdeadbeefu + (3u << 2u) + 13u;
|
||||
|
||||
c += kz;
|
||||
b += ky;
|
||||
a += kx;
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
uint hash_uint4(uint kx, uint ky, uint kz, uint kw)
|
||||
{
|
||||
uint a, b, c;
|
||||
a = b = c = 0xdeadbeefu + (4u << 2u) + 13u;
|
||||
|
||||
a += kx;
|
||||
b += ky;
|
||||
c += kz;
|
||||
mix(a, b, c);
|
||||
|
||||
a += kw;
|
||||
final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#undef rot
|
||||
#undef final
|
||||
#undef mix
|
||||
|
||||
uint hash_int(int kx)
|
||||
{
|
||||
return hash_uint(uint(kx));
|
||||
}
|
||||
|
||||
uint hash_int2(int kx, int ky)
|
||||
{
|
||||
return hash_uint2(uint(kx), uint(ky));
|
||||
}
|
||||
|
||||
uint hash_int3(int kx, int ky, int kz)
|
||||
{
|
||||
return hash_uint3(uint(kx), uint(ky), uint(kz));
|
||||
}
|
||||
|
||||
uint hash_int4(int kx, int ky, int kz, int kw)
|
||||
{
|
||||
return hash_uint4(uint(kx), uint(ky), uint(kz), uint(kw));
|
||||
}
|
||||
|
||||
/* PCG 2D, 3D and 4D hash functions,
|
||||
* from "Hash Functions for GPU Rendering" JCGT 2020
|
||||
* https://jcgt.org/published/0009/03/02/
|
||||
*
|
||||
* Slightly modified to only use signed integers,
|
||||
* so that they can also be implemented in OSL. */
|
||||
|
||||
int2 hash_pcg2d_i(int2 v)
|
||||
{
|
||||
v = v * 1664525 + 1013904223;
|
||||
v.x += v.y * 1664525;
|
||||
v.y += v.x * 1664525;
|
||||
v = v ^ (v >> 16);
|
||||
v.x += v.y * 1664525;
|
||||
v.y += v.x * 1664525;
|
||||
return v;
|
||||
}
|
||||
|
||||
int3 hash_pcg3d_i(int3 v)
|
||||
{
|
||||
v = v * 1664525 + 1013904223;
|
||||
v.x += v.y * v.z;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
v = v ^ (v >> 16);
|
||||
v.x += v.y * v.z;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
return v;
|
||||
}
|
||||
|
||||
int4 hash_pcg4d_i(int4 v)
|
||||
{
|
||||
v = v * 1664525 + 1013904223;
|
||||
v.x += v.y * v.w;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
v.w += v.y * v.z;
|
||||
v = v ^ (v >> 16);
|
||||
v.x += v.y * v.w;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
v.w += v.y * v.z;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Hashing uint or uint[234] into a float in the range [0, 1]. */
|
||||
|
||||
float hash_uint_to_float(uint kx)
|
||||
{
|
||||
return float(hash_uint(kx)) / float(0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
float hash_uint2_to_float(uint kx, uint ky)
|
||||
{
|
||||
return float(hash_uint2(kx, ky)) / float(0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
float hash_uint3_to_float(uint kx, uint ky, uint kz)
|
||||
{
|
||||
return float(hash_uint3(kx, ky, kz)) / float(0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
float hash_uint4_to_float(uint kx, uint ky, uint kz, uint kw)
|
||||
{
|
||||
return float(hash_uint4(kx, ky, kz, kw)) / float(0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
/* Hashing float or vec[234] into a float in the range [0, 1]. */
|
||||
|
||||
float hash_float_to_float(float k)
|
||||
{
|
||||
return hash_uint_to_float(floatBitsToUint(k));
|
||||
}
|
||||
|
||||
float hash_vec2_to_float(float2 k)
|
||||
{
|
||||
return hash_uint2_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y));
|
||||
}
|
||||
|
||||
float hash_vec3_to_float(float3 k)
|
||||
{
|
||||
return hash_uint3_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z));
|
||||
}
|
||||
|
||||
float hash_vec4_to_float(float4 k)
|
||||
{
|
||||
return hash_uint4_to_float(
|
||||
floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z), floatBitsToUint(k.w));
|
||||
}
|
||||
|
||||
/* Hashing vec[234] into vec[234] of components in the range [0, 1]. */
|
||||
|
||||
float2 hash_vec2_to_vec2(float2 k)
|
||||
{
|
||||
return float2(hash_vec2_to_float(k), hash_vec3_to_float(float3(k, 1.0f)));
|
||||
}
|
||||
|
||||
float3 hash_vec3_to_vec3(float3 k)
|
||||
{
|
||||
return float3(hash_vec3_to_float(k),
|
||||
hash_vec4_to_float(float4(k, 1.0f)),
|
||||
hash_vec4_to_float(float4(k, 2.0f)));
|
||||
}
|
||||
|
||||
float4 hash_vec4_to_vec4(float4 k)
|
||||
{
|
||||
return float4(hash_vec4_to_float(k.xyzw),
|
||||
hash_vec4_to_float(k.wxyz),
|
||||
hash_vec4_to_float(k.zwxy),
|
||||
hash_vec4_to_float(k.yzwx));
|
||||
}
|
||||
|
||||
/* Hashing a number of integers into floats in [0..1] range. */
|
||||
|
||||
float2 hash_int2_to_vec2(int2 k)
|
||||
{
|
||||
int2 h = hash_pcg2d_i(k);
|
||||
return float2(h & 0x7fffffff) * (1.0 / float(0x7fffffff));
|
||||
}
|
||||
|
||||
float3 hash_int3_to_vec3(int3 k)
|
||||
{
|
||||
int3 h = hash_pcg3d_i(k);
|
||||
return float3(h & 0x7fffffff) * (1.0 / float(0x7fffffff));
|
||||
}
|
||||
|
||||
float4 hash_int4_to_vec4(int4 k)
|
||||
{
|
||||
int4 h = hash_pcg4d_i(k);
|
||||
return float4(h & 0x7fffffff) * (1.0 / float(0x7fffffff));
|
||||
}
|
||||
|
||||
float3 hash_int2_to_vec3(int2 k)
|
||||
{
|
||||
return hash_int3_to_vec3(int3(k.x, k.y, 0));
|
||||
}
|
||||
|
||||
float3 hash_int4_to_vec3(int4 k)
|
||||
{
|
||||
return hash_int4_to_vec4(k).xyz;
|
||||
}
|
||||
|
||||
/* Hashing float or vec[234] into vec3 of components in range [0, 1]. */
|
||||
|
||||
float3 hash_float_to_vec3(float k)
|
||||
{
|
||||
return float3(hash_float_to_float(k),
|
||||
hash_vec2_to_float(float2(k, 1.0f)),
|
||||
hash_vec2_to_float(float2(k, 2.0f)));
|
||||
}
|
||||
|
||||
float3 hash_vec2_to_vec3(float2 k)
|
||||
{
|
||||
return float3(hash_vec2_to_float(k),
|
||||
hash_vec3_to_float(float3(k, 1.0f)),
|
||||
hash_vec3_to_float(float3(k, 2.0f)));
|
||||
}
|
||||
|
||||
float3 hash_vec4_to_vec3(float4 k)
|
||||
{
|
||||
return float3(
|
||||
hash_vec4_to_float(k.xyzw), hash_vec4_to_float(k.zxwy), hash_vec4_to_float(k.wzyx));
|
||||
}
|
||||
|
||||
/* Hashing float or vec[234] into vec2 of components in range [0, 1]. */
|
||||
|
||||
float2 hash_float_to_vec2(float k)
|
||||
{
|
||||
return float2(hash_float_to_float(k), hash_vec2_to_float(float2(k, 1.0f)));
|
||||
}
|
||||
|
||||
float2 hash_vec3_to_vec2(float3 k)
|
||||
{
|
||||
return float2(hash_vec3_to_float(k.xyz), hash_vec3_to_float(k.zxy));
|
||||
}
|
||||
|
||||
float2 hash_vec4_to_vec2(float4 k)
|
||||
{
|
||||
return float2(hash_vec4_to_float(k.xyzw), hash_vec4_to_float(k.zxwy));
|
||||
}
|
||||
|
||||
/* Other Hash Functions */
|
||||
|
||||
float integer_noise(int n)
|
||||
{
|
||||
/* Integer bit-shifts for these calculations can cause precision problems on macOS.
|
||||
* Using uint resolves these issues. */
|
||||
uint nn;
|
||||
nn = (uint(n) + 1013u) & 0x7fffffffu;
|
||||
nn = (nn >> 13u) ^ nn;
|
||||
nn = (uint(nn * (nn * nn * 60493u + 19990303u)) + 1376312589u) & 0x7fffffffu;
|
||||
return 0.5f * (float(nn) / 1073741824.0f);
|
||||
}
|
||||
|
||||
float wang_hash_noise(uint s)
|
||||
{
|
||||
s = (s ^ 61u) ^ (s >> 16u);
|
||||
s *= 9u;
|
||||
s = s ^ (s >> 4u);
|
||||
s *= 0x27d4eb2du;
|
||||
s = s ^ (s >> 15u);
|
||||
|
||||
return fract(float(s) / 4294967296.0f);
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_math_safe_lib.glsl"
|
||||
|
||||
[[node]]
|
||||
void math_add(float a, float b, float c, float &result)
|
||||
{
|
||||
result = a + b;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_subtract(float a, float b, float c, float &result)
|
||||
{
|
||||
result = a - b;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_multiply(float a, float b, float c, float &result)
|
||||
{
|
||||
result = a * b;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_divide(float a, float b, float c, float &result)
|
||||
{
|
||||
result = safe_divide(a, b);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_power(float a, float b, float c, float &result)
|
||||
{
|
||||
if (a >= 0.0f) {
|
||||
result = compatible_pow(a, b);
|
||||
}
|
||||
else {
|
||||
float fraction = mod(abs(b), 1.0f);
|
||||
if (fraction > 0.999f || fraction < 0.001f) {
|
||||
result = compatible_pow(a, floor(b + 0.5f));
|
||||
}
|
||||
else {
|
||||
result = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_logarithm(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (a > 0.0f && b > 0.0f) ? log2(a) / log2(b) : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_sqrt(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (a > 0.0f) ? sqrt(a) : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_inversesqrt(float a, float b, float c, float &result)
|
||||
{
|
||||
result = inversesqrt(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_absolute(float a, float b, float c, float &result)
|
||||
{
|
||||
result = abs(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_radians(float a, float b, float c, float &result)
|
||||
{
|
||||
result = radians(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_degrees(float a, float b, float c, float &result)
|
||||
{
|
||||
result = degrees(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_minimum(float a, float b, float c, float &result)
|
||||
{
|
||||
result = min(a, b);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_maximum(float a, float b, float c, float &result)
|
||||
{
|
||||
result = max(a, b);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_less_than(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (a < b) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_greater_than(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (a > b) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_round(float a, float b, float c, float &result)
|
||||
{
|
||||
result = floor(a + 0.5f);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_floor(float a, float b, float c, float &result)
|
||||
{
|
||||
result = floor(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_ceil(float a, float b, float c, float &result)
|
||||
{
|
||||
result = ceil(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_fraction(float a, float b, float c, float &result)
|
||||
{
|
||||
result = a - floor(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_modulo(float a, float b, float c, float &result)
|
||||
{
|
||||
result = compatible_mod(a, b);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_floored_modulo(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (b != 0.0f) ? a - floor(a / b) * b : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_trunc(float a, float b, float c, float &result)
|
||||
{
|
||||
result = trunc(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_snap(float a, float b, float c, float &result)
|
||||
{
|
||||
result = floor(safe_divide(a, b)) * b;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_pingpong(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (b != 0.0f) ? abs(fract((a - b) / (b * 2.0f)) * b * 2.0f - b) : 0.0f;
|
||||
}
|
||||
|
||||
/* Adapted from GODOT-engine math_funcs.h. */
|
||||
[[node]]
|
||||
void math_wrap(float a, float b, float c, float &result)
|
||||
{
|
||||
result = wrap(a, b, c);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_sine(float a, float b, float c, float &result)
|
||||
{
|
||||
result = sin(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_cosine(float a, float b, float c, float &result)
|
||||
{
|
||||
result = cos(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_tangent(float a, float b, float c, float &result)
|
||||
{
|
||||
result = tan(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_sinh(float a, float b, float c, float &result)
|
||||
{
|
||||
result = sinh(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_cosh(float a, float b, float c, float &result)
|
||||
{
|
||||
result = cosh(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_tanh(float a, float b, float c, float &result)
|
||||
{
|
||||
result = tanh(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_arcsine(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (a <= 1.0f && a >= -1.0f) ? asin(a) : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_arccosine(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (a <= 1.0f && a >= -1.0f) ? acos(a) : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_arctangent(float a, float b, float c, float &result)
|
||||
{
|
||||
result = atan(a);
|
||||
}
|
||||
|
||||
/* The behavior of `atan2(0, 0)` is undefined on many platforms, to ensure consistent behavior, we
|
||||
* return 0 in this case. See !126951. */
|
||||
[[node]]
|
||||
void math_arctan2(float a, float b, float c, float &result)
|
||||
{
|
||||
result = ((a == 0.0f && b == 0.0f) ? 0.0f : atan(a, b));
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_sign(float a, float b, float c, float &result)
|
||||
{
|
||||
result = sign(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_exponent(float a, float b, float c, float &result)
|
||||
{
|
||||
result = exp(a);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_compare(float a, float b, float c, float &result)
|
||||
{
|
||||
result = (abs(a - b) <= max(c, 1e-5f)) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_multiply_add(float a, float b, float c, float &result)
|
||||
{
|
||||
result = a * b + c;
|
||||
}
|
||||
|
||||
/* See: https://www.iquilezles.org/www/articles/smin/smin.htm. */
|
||||
[[node]]
|
||||
void math_smoothmin(float a, float b, float c, float &result)
|
||||
{
|
||||
if (c != 0.0f) {
|
||||
float h = max(c - abs(a - b), 0.0f) / c;
|
||||
result = min(a, b) - h * h * h * c * (1.0f / 6.0f);
|
||||
}
|
||||
else {
|
||||
result = min(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void math_smoothmax(float a, float b, float c, float &result)
|
||||
{
|
||||
math_smoothmin(-a, -b, c, result);
|
||||
result = -result;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Fix dependency hell one EEVEE legacy is removed. */
|
||||
float math_reduce_max(float3 a)
|
||||
{
|
||||
return max(a.x, max(a.y, a.z));
|
||||
}
|
||||
|
||||
float math_average(float3 a)
|
||||
{
|
||||
return (a.x + a.y + a.z) * (1.0f / 3.0f);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
[[node]]
|
||||
void invert_z(float3 v, float3 &outv)
|
||||
{
|
||||
v.z = -v.z;
|
||||
outv = v;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void vector_normalize(float3 normal, float3 &outnormal)
|
||||
{
|
||||
/* Match the safe normalize function in Cycles by defaulting to float3(0.0f) */
|
||||
float length_sqr = dot(normal, normal);
|
||||
outnormal = (length_sqr > 1e-35f) ? normal * inversesqrt(length_sqr) : float3(0.0f);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void vector_copy(float3 normal, float3 &outnormal)
|
||||
{
|
||||
outnormal = normal;
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "gpu_shader_common_color_utils.glsl"
|
||||
|
||||
[[node]]
|
||||
void mix_blend(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = mix(col1, col2, fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_add(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = mix(col1, col1 + col2, fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_mult(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = mix(col1, col1 * col2, fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_screen(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = float4(1.0f) - (float4(facm) + fac * (float4(1.0f) - col2)) * (float4(1.0f) - col1);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_overlay(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = col1;
|
||||
|
||||
if (outcol.r < 0.5f) {
|
||||
outcol.r *= facm + 2.0f * fac * col2.r;
|
||||
}
|
||||
else {
|
||||
outcol.r = 1.0f - (facm + 2.0f * fac * (1.0f - col2.r)) * (1.0f - outcol.r);
|
||||
}
|
||||
|
||||
if (outcol.g < 0.5f) {
|
||||
outcol.g *= facm + 2.0f * fac * col2.g;
|
||||
}
|
||||
else {
|
||||
outcol.g = 1.0f - (facm + 2.0f * fac * (1.0f - col2.g)) * (1.0f - outcol.g);
|
||||
}
|
||||
|
||||
if (outcol.b < 0.5f) {
|
||||
outcol.b *= facm + 2.0f * fac * col2.b;
|
||||
}
|
||||
else {
|
||||
outcol.b = 1.0f - (facm + 2.0f * fac * (1.0f - col2.b)) * (1.0f - outcol.b);
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_sub(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = mix(col1, col1 - col2, fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_div(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = float4(float3(0.0f), col1.a);
|
||||
|
||||
if (col2.r != 0.0f) {
|
||||
outcol.r = facm * col1.r + fac * col1.r / col2.r;
|
||||
}
|
||||
if (col2.g != 0.0f) {
|
||||
outcol.g = facm * col1.g + fac * col1.g / col2.g;
|
||||
}
|
||||
if (col2.b != 0.0f) {
|
||||
outcol.b = facm * col1.b + fac * col1.b / col2.b;
|
||||
}
|
||||
}
|
||||
|
||||
/* A variant of mix_div that fallback to the first color upon zero division. */
|
||||
[[node]]
|
||||
void mix_div_fallback(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = col1;
|
||||
|
||||
if (col2.r != 0.0f) {
|
||||
outcol.r = facm * outcol.r + fac * outcol.r / col2.r;
|
||||
}
|
||||
if (col2.g != 0.0f) {
|
||||
outcol.g = facm * outcol.g + fac * outcol.g / col2.g;
|
||||
}
|
||||
if (col2.b != 0.0f) {
|
||||
outcol.b = facm * outcol.b + fac * outcol.b / col2.b;
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_diff(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = mix(col1, abs(col1 - col2), fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_exclusion(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = max(mix(col1, col1 + col2 - 2.0f * col1 * col2, fac), 0.0f);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_dark(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_light(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_dodge(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = col1;
|
||||
|
||||
if (outcol.r != 0.0f) {
|
||||
float tmp = 1.0f - fac * col2.r;
|
||||
if (tmp <= 0.0f) {
|
||||
outcol.r = 1.0f;
|
||||
}
|
||||
else if ((tmp = outcol.r / tmp) > 1.0f) {
|
||||
outcol.r = 1.0f;
|
||||
}
|
||||
else {
|
||||
outcol.r = tmp;
|
||||
}
|
||||
}
|
||||
if (outcol.g != 0.0f) {
|
||||
float tmp = 1.0f - fac * col2.g;
|
||||
if (tmp <= 0.0f) {
|
||||
outcol.g = 1.0f;
|
||||
}
|
||||
else if ((tmp = outcol.g / tmp) > 1.0f) {
|
||||
outcol.g = 1.0f;
|
||||
}
|
||||
else {
|
||||
outcol.g = tmp;
|
||||
}
|
||||
}
|
||||
if (outcol.b != 0.0f) {
|
||||
float tmp = 1.0f - fac * col2.b;
|
||||
if (tmp <= 0.0f) {
|
||||
outcol.b = 1.0f;
|
||||
}
|
||||
else if ((tmp = outcol.b / tmp) > 1.0f) {
|
||||
outcol.b = 1.0f;
|
||||
}
|
||||
else {
|
||||
outcol.b = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_burn(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float tmp, facm = 1.0f - fac;
|
||||
|
||||
outcol = col1;
|
||||
|
||||
tmp = facm + fac * col2.r;
|
||||
if (tmp <= 0.0f) {
|
||||
outcol.r = 0.0f;
|
||||
}
|
||||
else if ((tmp = (1.0f - (1.0f - outcol.r) / tmp)) < 0.0f) {
|
||||
outcol.r = 0.0f;
|
||||
}
|
||||
else if (tmp > 1.0f) {
|
||||
outcol.r = 1.0f;
|
||||
}
|
||||
else {
|
||||
outcol.r = tmp;
|
||||
}
|
||||
|
||||
tmp = facm + fac * col2.g;
|
||||
if (tmp <= 0.0f) {
|
||||
outcol.g = 0.0f;
|
||||
}
|
||||
else if ((tmp = (1.0f - (1.0f - outcol.g) / tmp)) < 0.0f) {
|
||||
outcol.g = 0.0f;
|
||||
}
|
||||
else if (tmp > 1.0f) {
|
||||
outcol.g = 1.0f;
|
||||
}
|
||||
else {
|
||||
outcol.g = tmp;
|
||||
}
|
||||
|
||||
tmp = facm + fac * col2.b;
|
||||
if (tmp <= 0.0f) {
|
||||
outcol.b = 0.0f;
|
||||
}
|
||||
else if ((tmp = (1.0f - (1.0f - outcol.b) / tmp)) < 0.0f) {
|
||||
outcol.b = 0.0f;
|
||||
}
|
||||
else if (tmp > 1.0f) {
|
||||
outcol.b = 1.0f;
|
||||
}
|
||||
else {
|
||||
outcol.b = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_hue(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = col1;
|
||||
|
||||
float4 hsv, hsv2, tmp;
|
||||
rgb_to_hsv(col2, hsv2);
|
||||
|
||||
if (hsv2.y != 0.0f) {
|
||||
rgb_to_hsv(outcol, hsv);
|
||||
hsv.x = hsv2.x;
|
||||
hsv_to_rgb(hsv, tmp);
|
||||
|
||||
outcol = mix(outcol, tmp, fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_sat(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = col1;
|
||||
|
||||
float4 hsv, hsv2;
|
||||
rgb_to_hsv(outcol, hsv);
|
||||
|
||||
if (hsv.y != 0.0f) {
|
||||
rgb_to_hsv(col2, hsv2);
|
||||
|
||||
hsv.y = facm * hsv.y + fac * hsv2.y;
|
||||
hsv_to_rgb(hsv, outcol);
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_val(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
float4 hsv, hsv2;
|
||||
rgb_to_hsv(col1, hsv);
|
||||
rgb_to_hsv(col2, hsv2);
|
||||
|
||||
hsv.z = facm * hsv.z + fac * hsv2.z;
|
||||
hsv_to_rgb(hsv, outcol);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_color(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
outcol = col1;
|
||||
|
||||
float4 hsv, hsv2, tmp;
|
||||
rgb_to_hsv(col2, hsv2);
|
||||
|
||||
if (hsv2.y != 0.0f) {
|
||||
rgb_to_hsv(outcol, hsv);
|
||||
hsv.x = hsv2.x;
|
||||
hsv.y = hsv2.y;
|
||||
hsv_to_rgb(hsv, tmp);
|
||||
|
||||
outcol = mix(outcol, tmp, fac);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_soft(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
float facm = 1.0f - fac;
|
||||
|
||||
float4 one = float4(1.0f);
|
||||
float4 scr = one - (one - col2) * (one - col1);
|
||||
outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void mix_linear(float fac, float4 col1, float4 col2, float4 &outcol)
|
||||
{
|
||||
outcol = col1 + fac * (2.0f * (col2 - float4(0.5f)));
|
||||
outcol.a = col1.a;
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void clamp_color(float4 vec, const float4 min, const float4 max, float4 &out_vec)
|
||||
{
|
||||
out_vec = clamp(vec, min, max);
|
||||
}
|
||||
|
||||
[[node]]
|
||||
void multiply_by_alpha(float factor, float4 color, float &result)
|
||||
{
|
||||
result = factor * color.a;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/*
|
||||
* For debugging purpose mainly.
|
||||
* From https://www.shadertoy.com/view/4dsSzr
|
||||
* By Morgan McGuire @morgan3d, http://graphicscodex.com
|
||||
* Reuse permitted under the BSD license.
|
||||
*/
|
||||
float3 neon_gradient(float t)
|
||||
{
|
||||
float tt = abs(0.43f - t) * 1.7f;
|
||||
return clamp(float3(t * 1.3f + 0.1f, tt * tt, (1.0f - t) * 1.7f), 0.0f, 1.0f);
|
||||
}
|
||||
float3 heatmap_gradient(float t)
|
||||
{
|
||||
float a = pow(t, 1.5f) * 0.8f + 0.2f;
|
||||
float b = smoothstep(0.0f, 0.35f, t) + t * 0.5f;
|
||||
float c = smoothstep(0.5f, 1.0f, t);
|
||||
float d = max(1.0f - t * 1.7f, t * 7.0f - 6.0f);
|
||||
return clamp(a * float3(b, c, d), float3(0.0f), float3(1.0f));
|
||||
}
|
||||
float3 hue_gradient(float t)
|
||||
{
|
||||
float3 p = abs(fract(t + float3(1.0f, 2.0f / 3.0f, 1.0f / 3.0f)) * 6.0f - 3.0f);
|
||||
return (clamp(p - 1.0f, 0.0f, 1.0f));
|
||||
}
|
||||
float3 green_to_red_gradient(float t)
|
||||
{
|
||||
return mix(float3(0.0f, 1.0f, 0.0f), float3(1.0f, 0.0f, 0.0f), t);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2015-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* Position vertices of a triangle to cover the whole screen. */
|
||||
void fullscreen_vertex(int vertex_id, float4 &out_position)
|
||||
{
|
||||
int v = vertex_id % 3;
|
||||
float x = -1.0f + float((v & 1) << 2);
|
||||
float y = -1.0f + float((v & 2) << 1);
|
||||
out_position = float4(x, y, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void fullscreen_vertex(int vertex_id, float4 &out_position, float2 &out_uv)
|
||||
{
|
||||
fullscreen_vertex(vertex_id, out_position);
|
||||
out_uv = (out_position.xy + 1.0f) * 0.5f;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-FileCopyrightText: 2015-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_fullscreen_infos.hh"
|
||||
#include "gpu_shader_sequencer_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_fullscreen)
|
||||
|
||||
#include "gpu_shader_fullscreen_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
fullscreen_vertex(gl_VertexID, gl_Position, screen_uv);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infos/gpu_index_load_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(gpu_index_buffer_load)
|
||||
|
||||
/**
|
||||
* Library to read the index buffer of a `gpu::Batch` using a SSBO rather than using `gl_VertexID`.
|
||||
* This is required for primitive expansion without geometry shader.
|
||||
* It is **not** needed if it is guaranteed that the processed `gpu::Batch` will not use any index
|
||||
* buffer.
|
||||
*/
|
||||
|
||||
#ifndef WORKAROUND_INDEX_LOAD_INCLUDE
|
||||
# ifndef GPU_INDEX_LOAD
|
||||
# error Missing gpu_index_buffer_load create info dependency
|
||||
# endif
|
||||
|
||||
/**
|
||||
* Returns the resolved index after index buffer (a.k.a. element buffer) indirection.
|
||||
*/
|
||||
uint gpu_index_load(uint element_index)
|
||||
{
|
||||
if (gpu_index_no_buffer) {
|
||||
return element_index;
|
||||
}
|
||||
|
||||
uint raw_index = gpu_index_buf[gpu_index_16bit ? element_index >> 1u : element_index];
|
||||
|
||||
if (gpu_index_16bit) {
|
||||
raw_index = ((element_index & 1u) == 0u) ? (raw_index & 0xFFFFu) : (raw_index >> 16u);
|
||||
}
|
||||
|
||||
return raw_index + uint(gpu_index_base_index);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* WORKAROUND: Workaround include order hell. */
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
#elif defined(GPU_SHADER)
|
||||
# define static
|
||||
#endif
|
||||
|
||||
struct IndexRange {
|
||||
int start_;
|
||||
int size_;
|
||||
|
||||
static IndexRange from_begin_end(int begin, int end)
|
||||
{
|
||||
return {begin, end - begin};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first element in the range.
|
||||
*/
|
||||
int first() const
|
||||
{
|
||||
return this->start_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first element in the range. The returned value is undefined when the range is empty.
|
||||
*/
|
||||
int start() const
|
||||
{
|
||||
return this->start_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nth last element in the range.
|
||||
*/
|
||||
int last(int n = 0) const
|
||||
{
|
||||
return this->start_ + this->size_ - 1 - n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of numbers in the range.
|
||||
*/
|
||||
int size() const
|
||||
{
|
||||
return this->size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new range, that contains a sub-interval of the current one.
|
||||
*/
|
||||
IndexRange slice(int start, int size) const
|
||||
{
|
||||
int new_start = this->start_ + start;
|
||||
return {new_start, size};
|
||||
}
|
||||
IndexRange slice(IndexRange range) const
|
||||
{
|
||||
return this->slice(range.start(), range.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the range forward or backward within the larger array. The amount may be negative,
|
||||
* but its absolute value cannot be greater than the existing start of the range.
|
||||
*/
|
||||
IndexRange shift(int n) const
|
||||
{
|
||||
return {this->start_ + n, this->size_};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
|
||||
struct AngleRadian {
|
||||
/* Note that value is public because of the lack of casting operator in GLSL. */
|
||||
float angle;
|
||||
|
||||
static AngleRadian identity()
|
||||
{
|
||||
return {0};
|
||||
}
|
||||
|
||||
static AngleRadian from_degree(float angle_degree)
|
||||
{
|
||||
return {angle_degree * (M_PI / 180.0f)};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
struct AxisAngle {
|
||||
float3 axis;
|
||||
float angle;
|
||||
|
||||
static AxisAngle identity()
|
||||
{
|
||||
return {float3(0, 1, 0), 0};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* `powf` is really slow for raising to integer powers. */
|
||||
|
||||
float pow2f(float x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
float pow3f(float x)
|
||||
{
|
||||
return x * x * x;
|
||||
}
|
||||
float pow4f(float x)
|
||||
{
|
||||
return pow2f(pow2f(x));
|
||||
}
|
||||
float pow5f(float x)
|
||||
{
|
||||
return pow4f(x) * x;
|
||||
}
|
||||
float pow6f(float x)
|
||||
{
|
||||
return pow2f(pow3f(x));
|
||||
}
|
||||
float pow7f(float x)
|
||||
{
|
||||
return pow6f(x) * x;
|
||||
}
|
||||
float pow8f(float x)
|
||||
{
|
||||
return pow2f(pow4f(x));
|
||||
}
|
||||
|
||||
float square(float v)
|
||||
{
|
||||
return v * v;
|
||||
}
|
||||
float2 square(float2 v)
|
||||
{
|
||||
return v * v;
|
||||
}
|
||||
float3 square(float3 v)
|
||||
{
|
||||
return v * v;
|
||||
}
|
||||
float4 square(float4 v)
|
||||
{
|
||||
return v * v;
|
||||
}
|
||||
|
||||
float hypot(float x, float y)
|
||||
{
|
||||
return sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
/* Declared as _atan2 to prevent errors with `WITH_GPU_SHADER_CPP_COMPILATION` on VS2019 due
|
||||
* to `corecrt_math` conflicting functions. */
|
||||
|
||||
float _atan2(float y, float x)
|
||||
{
|
||||
return atan(y, x);
|
||||
}
|
||||
#define atan2 _atan2
|
||||
|
||||
/**
|
||||
* Returns \a a if it is a multiple of \a b or the next multiple or \a b after \b a .
|
||||
* In other words, it is equivalent to `divide_ceil(a, b) * b`.
|
||||
* It is undefined if \a a is negative or \b b is not strictly positive.
|
||||
*/
|
||||
int ceil_to_multiple(int a, int b)
|
||||
{
|
||||
return ((a + b - 1) / b) * b;
|
||||
}
|
||||
uint ceil_to_multiple(uint a, uint b)
|
||||
{
|
||||
return ((a + b - 1u) / b) * b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Integer division that returns the ceiling, instead of flooring like normal C division.
|
||||
* It is undefined if \a a is negative or \b b is not strictly positive.
|
||||
*/
|
||||
int divide_ceil(int a, int b)
|
||||
{
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
uint divide_ceil(uint a, uint b)
|
||||
{
|
||||
return (a + b - 1u) / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component wise, use vector to replace min if it is smaller and max if bigger.
|
||||
*/
|
||||
void min_max(float value, float &min_v, float &max_v)
|
||||
{
|
||||
min_v = min(value, min_v);
|
||||
max_v = max(value, max_v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the difference between`a` and `b` is below the `epsilon` value.
|
||||
*/
|
||||
bool is_equal(float a, float b, const float epsilon)
|
||||
{
|
||||
return abs(a - b) <= epsilon;
|
||||
}
|
||||
|
||||
float sin_from_cos(float c)
|
||||
{
|
||||
return sqrt(max(0.0f, 1.0f - square(c)));
|
||||
}
|
||||
|
||||
float cos_from_sin(float s)
|
||||
{
|
||||
return sqrt(max(0.0f, 1.0f - square(s)));
|
||||
}
|
||||
|
||||
float cos_from_tan(float t)
|
||||
{
|
||||
return inversesqrt(1.0f + square(t));
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define M_PI 3.14159265358979323846f /* pi */
|
||||
#define M_TAU 6.28318530717958647692f /* tau = 2*pi */
|
||||
#define M_PI_2 1.57079632679489661923f /* pi/2 */
|
||||
#define M_PI_4 0.78539816339744830962f /* pi/4 */
|
||||
#define M_SQRT2 1.41421356237309504880f /* sqrt(2) */
|
||||
#define M_SQRT1_2 0.70710678118654752440f /* 1/sqrt(2) */
|
||||
#define M_SQRT3 1.73205080756887729352f /* sqrt(3) */
|
||||
#define M_SQRT1_3 0.57735026918962576450f /* 1/sqrt(3) */
|
||||
#define M_1_PI 0.318309886183790671538f /* 1/pi */
|
||||
#define M_E 2.7182818284590452354f /* e */
|
||||
#define M_LOG2E 1.4426950408889634074f /* log_2 e */
|
||||
#define M_LOG10E 0.43429448190325182765f /* log_10 e */
|
||||
#define M_LN2 0.69314718055994530942f /* log_e 2 */
|
||||
#define M_LN10 2.30258509299404568402f /* log_e 10 */
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
struct EulerXYZ {
|
||||
float x, y, z;
|
||||
|
||||
static EulerXYZ from_float3(float3 eul)
|
||||
{
|
||||
return {eul.x, eul.y, eul.z};
|
||||
}
|
||||
|
||||
static EulerXYZ identity()
|
||||
{
|
||||
return {0, 0, 0};
|
||||
}
|
||||
|
||||
float3 as_float3() const
|
||||
{
|
||||
return float3(this->x, this->y, this->z);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
|
||||
/* [Drobot2014a] Low Level Optimizations for GCN. */
|
||||
float sqrt_fast(float v)
|
||||
{
|
||||
return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
|
||||
}
|
||||
float2 sqrt_fast(float2 v)
|
||||
{
|
||||
return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
|
||||
}
|
||||
|
||||
/* [Eberly2014] GPGPU Programming for Games and Science. */
|
||||
float acos_fast(float v)
|
||||
{
|
||||
float res = -0.156583f * abs(v) + M_PI_2;
|
||||
res *= sqrt_fast(1.0f - abs(v));
|
||||
return (v >= 0) ? res : M_PI - res;
|
||||
}
|
||||
float2 acos_fast(float2 v)
|
||||
{
|
||||
float2 res = -0.156583f * abs(v) + M_PI_2;
|
||||
res *= sqrt_fast(1.0f - abs(v));
|
||||
v.x = (v.x >= 0) ? res.x : M_PI - res.x;
|
||||
v.y = (v.y >= 0) ? res.y : M_PI - res.y;
|
||||
return v;
|
||||
}
|
||||
|
||||
float atan_fast(float x)
|
||||
{
|
||||
float a = abs(x);
|
||||
float k = a > 1.0f ? (1.0f / a) : a;
|
||||
float s = 1.0f - (1.0f - k); /* Crush denormals. */
|
||||
float t = s * s;
|
||||
/* http://mathforum.org/library/drmath/view/62672.html
|
||||
* Examined 4278190080 values of atan:
|
||||
* 2.36864877 avg ULP diff, 302 max ULP, 6.55651e-06f max error // (with denormals)
|
||||
* Examined 4278190080 values of atan:
|
||||
* 171160502 avg ULP diff, 855638016 max ULP, 6.55651e-06f max error // (crush denormals)
|
||||
*/
|
||||
float r = s * fma(0.43157974f, t, 1.0f) / fma(fma(0.05831938f, t, 0.76443945f), t, 1.0f);
|
||||
if (a > 1.0f) {
|
||||
r = 1.57079632679489661923f - r;
|
||||
}
|
||||
return (x < 0.0f) ? -r : r;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Normalize
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns the adjoint of the matrix (also known as adjugate matrix).
|
||||
*/
|
||||
float2x2 adjoint(float2x2 mat)
|
||||
{
|
||||
float2x2 adj = float2x2(0.0f);
|
||||
for (int c = 0; c < 2; c++) {
|
||||
for (int r = 0; r < 2; r++) {
|
||||
/* Copy other cells except the "cross" to compute the determinant. */
|
||||
float tmp = 0.0f;
|
||||
for (int m_c = 0; m_c < 2; m_c++) {
|
||||
for (int m_r = 0; m_r < 2; m_r++) {
|
||||
if (m_c != c && m_r != r) {
|
||||
tmp = mat[m_c][m_r];
|
||||
}
|
||||
}
|
||||
}
|
||||
float minor = tmp;
|
||||
/* Transpose directly to get the adjugate. Swap destination row and col. */
|
||||
adj[r][c] = (((c + r) & 1) != 0) ? -minor : minor;
|
||||
}
|
||||
}
|
||||
return adj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adjoint of the matrix (also known as adjugate matrix).
|
||||
*/
|
||||
float3x3 adjoint(float3x3 mat)
|
||||
{
|
||||
float3x3 adj = float3x3(0.0f);
|
||||
for (int c = 0; c < 3; c++) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
/* Copy other cells except the "cross" to compute the determinant. */
|
||||
float2x2 tmp = float2x2(0.0f);
|
||||
for (int m_c = 0; m_c < 3; m_c++) {
|
||||
for (int m_r = 0; m_r < 3; m_r++) {
|
||||
if (m_c != c && m_r != r) {
|
||||
int d_c = (m_c < c) ? m_c : (m_c - 1);
|
||||
int d_r = (m_r < r) ? m_r : (m_r - 1);
|
||||
tmp[d_c][d_r] = mat[m_c][m_r];
|
||||
}
|
||||
}
|
||||
}
|
||||
float minor = determinant(tmp);
|
||||
/* Transpose directly to get the adjugate. Swap destination row and col. */
|
||||
adj[r][c] = (((c + r) & 1) != 0) ? -minor : minor;
|
||||
}
|
||||
}
|
||||
return adj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adjoint of the matrix (also known as adjugate matrix).
|
||||
*/
|
||||
float4x4 adjoint(float4x4 mat)
|
||||
{
|
||||
float4x4 adj = float4x4(0.0f);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
for (int r = 0; r < 4; r++) {
|
||||
/* Copy other cells except the "cross" to compute the determinant. */
|
||||
float3x3 tmp = float3x3(0.0f);
|
||||
for (int m_c = 0; m_c < 4; m_c++) {
|
||||
for (int m_r = 0; m_r < 4; m_r++) {
|
||||
if (m_c != c && m_r != r) {
|
||||
int d_c = (m_c < c) ? m_c : (m_c - 1);
|
||||
int d_r = (m_r < r) ? m_r : (m_r - 1);
|
||||
tmp[d_c][d_r] = mat[m_c][m_r];
|
||||
}
|
||||
}
|
||||
}
|
||||
float minor = determinant(tmp);
|
||||
/* Transpose directly to get the adjugate. Swap destination row and col. */
|
||||
adj[r][c] = (((c + r) & 1) != 0) ? -minor : minor;
|
||||
}
|
||||
}
|
||||
return adj;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,214 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_vector_compare_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compare / Test
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns true if all of the matrices components are strictly equal to 0.
|
||||
*/
|
||||
bool is_zero(float3x3 a)
|
||||
{
|
||||
if (is_zero(a[0])) {
|
||||
if (is_zero(a[1])) {
|
||||
if (is_zero(a[2])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns true if all of the matrices components are strictly equal to 0.
|
||||
*/
|
||||
bool is_zero(float4x4 a)
|
||||
{
|
||||
if (is_zero(a[0])) {
|
||||
if (is_zero(a[1])) {
|
||||
if (is_zero(a[2])) {
|
||||
if (is_zero(a[3])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if matrix has inverted handedness.
|
||||
*/
|
||||
bool is_negative(float3x3 mat)
|
||||
{
|
||||
return determinant(mat) < 0.0f;
|
||||
}
|
||||
/**
|
||||
* Returns true if matrix has inverted handedness.
|
||||
*
|
||||
* \note It doesn't use determinant(mat4x4) as only the 3x3 components are needed
|
||||
* when the matrix is used as a transformation to represent location/scale/rotation.
|
||||
*/
|
||||
bool is_negative(float4x4 mat)
|
||||
{
|
||||
return is_negative(to_float3x3(mat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if matrices are equal within the given epsilon.
|
||||
*/
|
||||
bool is_equal(float2x2 a, float2x2 b, float epsilon)
|
||||
{
|
||||
if (is_equal(a[0], b[0], epsilon)) {
|
||||
if (is_equal(a[1], b[1], epsilon)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns true if matrices are equal within the given epsilon.
|
||||
*/
|
||||
bool is_equal(float3x3 a, float3x3 b, float epsilon)
|
||||
{
|
||||
if (is_equal(a[0], b[0], epsilon)) {
|
||||
if (is_equal(a[1], b[1], epsilon)) {
|
||||
if (is_equal(a[2], b[2], epsilon)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns true if matrices are equal within the given epsilon.
|
||||
*/
|
||||
bool is_equal(float4x4 a, float4x4 b, float epsilon)
|
||||
{
|
||||
if (is_equal(a[0], b[0], epsilon)) {
|
||||
if (is_equal(a[1], b[1], epsilon)) {
|
||||
if (is_equal(a[2], b[2], epsilon)) {
|
||||
if (is_equal(a[3], b[3], epsilon)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Test if the X, Y and Z axes are perpendicular with each other.
|
||||
*/
|
||||
bool is_orthogonal(float3x3 mat)
|
||||
{
|
||||
if (abs(dot(mat[0], mat[1])) > 1e-5f) {
|
||||
return false;
|
||||
}
|
||||
if (abs(dot(mat[1], mat[2])) > 1e-5f) {
|
||||
return false;
|
||||
}
|
||||
if (abs(dot(mat[2], mat[0])) > 1e-5f) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Test if the X, Y and Z axes are perpendicular with each other.
|
||||
*/
|
||||
bool is_orthogonal(float4x4 mat)
|
||||
{
|
||||
return is_orthogonal(to_float3x3(mat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the X, Y and Z axes are perpendicular with each other and unit length.
|
||||
*/
|
||||
bool is_orthonormal(float3x3 mat)
|
||||
{
|
||||
if (!is_orthogonal(mat)) {
|
||||
return false;
|
||||
}
|
||||
if (abs(dot(mat[0], mat[0]) - 1.0f) > 1e-5f) {
|
||||
return false;
|
||||
}
|
||||
if (abs(dot(mat[1], mat[1]) - 1.0f) > 1e-5f) {
|
||||
return false;
|
||||
}
|
||||
if (abs(dot(mat[2], mat[2]) - 1.0f) > 1e-5f) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Test if the X, Y and Z axes are perpendicular with each other and unit length.
|
||||
*/
|
||||
bool is_orthonormal(float4x4 mat)
|
||||
{
|
||||
return is_orthonormal(to_float3x3(mat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the X, Y and Z axes are perpendicular with each other and the same length.
|
||||
*/
|
||||
bool is_uniformly_scaled(float3x3 mat)
|
||||
{
|
||||
if (!is_orthogonal(mat)) {
|
||||
return false;
|
||||
}
|
||||
constexpr float eps = 1e-7f;
|
||||
float x = dot(mat[0], mat[0]);
|
||||
float y = dot(mat[1], mat[1]);
|
||||
float z = dot(mat[2], mat[2]);
|
||||
return (abs(x - y) < eps) && abs(x - z) < eps;
|
||||
}
|
||||
/**
|
||||
* Test if the X, Y and Z axes are perpendicular with each other and the same length.
|
||||
*/
|
||||
bool is_uniformly_scaled(float4x4 mat)
|
||||
{
|
||||
return is_uniformly_scaled(to_float3x3(mat));
|
||||
}
|
||||
|
||||
/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
|
||||
bool is_unit_scale(float4x4 m)
|
||||
{
|
||||
if (is_unit_scale(m[0])) {
|
||||
if (is_unit_scale(m[1])) {
|
||||
if (is_unit_scale(m[2])) {
|
||||
if (is_unit_scale(m[3])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
|
||||
bool is_unit_scale(float3x3 m)
|
||||
{
|
||||
if (is_unit_scale(m[0])) {
|
||||
if (is_unit_scale(m[1])) {
|
||||
if (is_unit_scale(m[2])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* Returns true if each individual columns are unit scaled. Mainly for assert usage. */
|
||||
bool is_unit_scale(float2x2 m)
|
||||
{
|
||||
if (is_unit_scale(m[0])) {
|
||||
if (is_unit_scale(m[1])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,346 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_angle_lib.glsl"
|
||||
#include "gpu_shader_math_axis_angle_lib.glsl"
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_math_euler_lib.glsl"
|
||||
#include "gpu_shader_math_quaternion_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Static constructors
|
||||
* \{ */
|
||||
|
||||
float2x2 mat2x2_diagonal(float v)
|
||||
{
|
||||
return float2x2(float2(v, 0.0f), float2(0.0f, v));
|
||||
}
|
||||
float3x3 mat3x3_diagonal(float v)
|
||||
{
|
||||
return float3x3(float3(v, 0.0f, 0.0f), float3(0.0f, v, 0.0f), float3(0.0f, 0.0f, v));
|
||||
}
|
||||
float4x4 mat4x4_diagonal(float v)
|
||||
{
|
||||
return float4x4(float4(v, 0.0f, 0.0f, 0.0f),
|
||||
float4(0.0f, v, 0.0f, 0.0f),
|
||||
float4(0.0f, 0.0f, v, 0.0f),
|
||||
float4(0.0f, 0.0f, 0.0f, v));
|
||||
}
|
||||
|
||||
float2x2 mat2x2_all(float v)
|
||||
{
|
||||
return float2x2(float2(v), float2(v));
|
||||
}
|
||||
float3x3 mat3x3_all(float v)
|
||||
{
|
||||
return float3x3(float3(v), float3(v), float3(v));
|
||||
}
|
||||
float4x4 mat4x4_all(float v)
|
||||
{
|
||||
return float4x4(float4(v), float4(v), float4(v), float4(v));
|
||||
}
|
||||
|
||||
float2x2 mat2x2_zero()
|
||||
{
|
||||
return mat2x2_all(0.0f);
|
||||
}
|
||||
float3x3 mat3x3_zero()
|
||||
{
|
||||
return mat3x3_all(0.0f);
|
||||
}
|
||||
float4x4 mat4x4_zero()
|
||||
{
|
||||
return mat4x4_all(0.0f);
|
||||
}
|
||||
|
||||
float2x2 mat2x2_identity()
|
||||
{
|
||||
return mat2x2_diagonal(1.0f);
|
||||
}
|
||||
float3x3 mat3x3_identity()
|
||||
{
|
||||
return mat3x3_diagonal(1.0f);
|
||||
}
|
||||
float4x4 mat4x4_identity()
|
||||
{
|
||||
return mat4x4_diagonal(1.0f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Init helpers.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Create a translation only matrix. Matrix dimensions should be at least 4 col x 3 row.
|
||||
*/
|
||||
float4x4 from_location(float3 location)
|
||||
{
|
||||
float4x4 ret = float4x4(1.0f);
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a matrix whose diagonal is defined by the given scale vector.
|
||||
*/
|
||||
float2x2 from_scale(float2 scale)
|
||||
{
|
||||
float2x2 ret = float2x2(0.0f);
|
||||
ret[0][0] = scale[0];
|
||||
ret[1][1] = scale[1];
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a matrix whose diagonal is defined by the given scale vector.
|
||||
*/
|
||||
float3x3 from_scale(float3 scale)
|
||||
{
|
||||
float3x3 ret = float3x3(0.0f);
|
||||
ret[0][0] = scale[0];
|
||||
ret[1][1] = scale[1];
|
||||
ret[2][2] = scale[2];
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a matrix whose diagonal is defined by the given scale vector.
|
||||
*/
|
||||
float4x4 from_scale(float4 scale)
|
||||
{
|
||||
float4x4 ret = float4x4(0.0f);
|
||||
ret[0][0] = scale[0];
|
||||
ret[1][1] = scale[1];
|
||||
ret[2][2] = scale[2];
|
||||
ret[3][3] = scale[3];
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rotation only matrix.
|
||||
*/
|
||||
float2x2 from_rotation(AngleRadian rotation)
|
||||
{
|
||||
float c = cos(rotation.angle);
|
||||
float s = sin(rotation.angle);
|
||||
return float2x2(c, -s, s, c);
|
||||
}
|
||||
/**
|
||||
* Create a rotation only matrix.
|
||||
*/
|
||||
float3x3 from_rotation(EulerXYZ rotation)
|
||||
{
|
||||
float ci = cos(rotation.x);
|
||||
float cj = cos(rotation.y);
|
||||
float ch = cos(rotation.z);
|
||||
float si = sin(rotation.x);
|
||||
float sj = sin(rotation.y);
|
||||
float sh = sin(rotation.z);
|
||||
float cc = ci * ch;
|
||||
float cs = ci * sh;
|
||||
float sc = si * ch;
|
||||
float ss = si * sh;
|
||||
|
||||
float3x3 mat;
|
||||
mat[0][0] = cj * ch;
|
||||
mat[1][0] = sj * sc - cs;
|
||||
mat[2][0] = sj * cc + ss;
|
||||
|
||||
mat[0][1] = cj * sh;
|
||||
mat[1][1] = sj * ss + cc;
|
||||
mat[2][1] = sj * cs - sc;
|
||||
|
||||
mat[0][2] = -sj;
|
||||
mat[1][2] = cj * si;
|
||||
mat[2][2] = cj * ci;
|
||||
return mat;
|
||||
}
|
||||
/**
|
||||
* Create a rotation only matrix.
|
||||
*/
|
||||
float3x3 from_rotation(Quaternion rotation)
|
||||
{
|
||||
/* NOTE: Should be double but support isn't native on most GPUs. */
|
||||
float q0 = M_SQRT2 * float(rotation.x);
|
||||
float q1 = M_SQRT2 * float(rotation.y);
|
||||
float q2 = M_SQRT2 * float(rotation.z);
|
||||
float q3 = M_SQRT2 * float(rotation.w);
|
||||
|
||||
float qda = q0 * q1;
|
||||
float qdb = q0 * q2;
|
||||
float qdc = q0 * q3;
|
||||
float qaa = q1 * q1;
|
||||
float qab = q1 * q2;
|
||||
float qac = q1 * q3;
|
||||
float qbb = q2 * q2;
|
||||
float qbc = q2 * q3;
|
||||
float qcc = q3 * q3;
|
||||
|
||||
float3x3 mat;
|
||||
mat[0][0] = float(1.0f - qbb - qcc);
|
||||
mat[0][1] = float(qdc + qab);
|
||||
mat[0][2] = float(-qdb + qac);
|
||||
|
||||
mat[1][0] = float(-qdc + qab);
|
||||
mat[1][1] = float(1.0f - qaa - qcc);
|
||||
mat[1][2] = float(qda + qbc);
|
||||
|
||||
mat[2][0] = float(qdb + qac);
|
||||
mat[2][1] = float(-qda + qbc);
|
||||
mat[2][2] = float(1.0f - qaa - qbb);
|
||||
return mat;
|
||||
}
|
||||
/**
|
||||
* Create a rotation only matrix.
|
||||
*/
|
||||
float3x3 from_rotation(AxisAngle rotation)
|
||||
{
|
||||
float angle_sin = sin(rotation.angle);
|
||||
float angle_cos = cos(rotation.angle);
|
||||
float3 axis = rotation.axis;
|
||||
|
||||
float ico = (float(1) - angle_cos);
|
||||
float3 nsi = axis * angle_sin;
|
||||
|
||||
float3 n012 = (axis * axis) * ico;
|
||||
float n_01 = (axis[0] * axis[1]) * ico;
|
||||
float n_02 = (axis[0] * axis[2]) * ico;
|
||||
float n_12 = (axis[1] * axis[2]) * ico;
|
||||
|
||||
float3x3 mat = from_scale(n012 + angle_cos);
|
||||
mat[0][1] = n_01 + nsi[2];
|
||||
mat[0][2] = n_02 - nsi[1];
|
||||
mat[1][0] = n_01 - nsi[2];
|
||||
mat[1][2] = n_12 + nsi[0];
|
||||
mat[2][0] = n_02 + nsi[1];
|
||||
mat[2][1] = n_12 - nsi[0];
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transform matrix with rotation and scale applied in this order.
|
||||
*/
|
||||
float3x3 from_rot_scale(EulerXYZ rotation, float3 scale)
|
||||
{
|
||||
return from_rotation(rotation) * from_scale(scale);
|
||||
}
|
||||
/**
|
||||
* Create a transform matrix with rotation and scale applied in this order.
|
||||
*/
|
||||
float3x3 from_rot_scale(Quaternion rotation, float3 scale)
|
||||
{
|
||||
return from_rotation(rotation) * from_scale(scale);
|
||||
}
|
||||
/**
|
||||
* Create a transform matrix with rotation and scale applied in this order.
|
||||
*/
|
||||
float3x3 from_rot_scale(AxisAngle rotation, float3 scale)
|
||||
{
|
||||
return from_rotation(rotation) * from_scale(scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transform matrix with translation and rotation applied in this order.
|
||||
*/
|
||||
float4x4 from_loc_rot(float3 location, EulerXYZ rotation)
|
||||
{
|
||||
float4x4 ret = to_float4x4(from_rotation(rotation));
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a transform matrix with translation and rotation applied in this order.
|
||||
*/
|
||||
float4x4 from_loc_rot(float3 location, Quaternion rotation)
|
||||
{
|
||||
float4x4 ret = to_float4x4(from_rotation(rotation));
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a transform matrix with translation and rotation applied in this order.
|
||||
*/
|
||||
float4x4 from_loc_rot(float3 location, AxisAngle rotation)
|
||||
{
|
||||
float4x4 ret = to_float4x4(from_rotation(rotation));
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transform matrix with translation, rotation and scale applied in this order.
|
||||
*/
|
||||
float4x4 from_loc_rot_scale(float3 location, EulerXYZ rotation, float3 scale)
|
||||
{
|
||||
float4x4 ret = to_float4x4(from_rot_scale(rotation, scale));
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a transform matrix with translation, rotation and scale applied in this order.
|
||||
*/
|
||||
float4x4 from_loc_rot_scale(float3 location, Quaternion rotation, float3 scale)
|
||||
{
|
||||
float4x4 ret = to_float4x4(from_rot_scale(rotation, scale));
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Create a transform matrix with translation, rotation and scale applied in this order.
|
||||
*/
|
||||
float4x4 from_loc_rot_scale(float3 location, AxisAngle rotation, float3 scale)
|
||||
{
|
||||
float4x4 ret = to_float4x4(from_rot_scale(rotation, scale));
|
||||
ret[3].xyz = location;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a 2D rotation matrix with the angle that the given direction makes with the x axis.
|
||||
* Assumes the direction vector is normalized.
|
||||
*/
|
||||
float2x2 from_direction(float2 direction)
|
||||
{
|
||||
float cos_angle = direction.x;
|
||||
float sin_angle = direction.y;
|
||||
return float2x2(cos_angle, sin_angle, -sin_angle, cos_angle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rotation matrix from 2 basis vectors.
|
||||
* The matrix determinant is given to be positive and it can be converted to other rotation types.
|
||||
* \note `forward` and `up` must be normalized.
|
||||
*/
|
||||
// mat3x3 from_normalized_axis_data(vec3 forward, vec3 up); /* TODO. */
|
||||
|
||||
/**
|
||||
* Create a transform matrix with translation and rotation from 2 basis vectors and a translation.
|
||||
* \note `forward` and `up` must be normalized.
|
||||
*/
|
||||
// mat4x4 from_normalized_axis_data(vec3 location, vec3 forward, vec3 up); /* TODO. */
|
||||
|
||||
/**
|
||||
* Create a rotation matrix from only one \a up axis.
|
||||
* The other axes are chosen to always be orthogonal. The resulting matrix is a basis matrix.
|
||||
* \note `up` must be normalized.
|
||||
* \note This can be used to create a tangent basis from a normal vector.
|
||||
* \note The output of this function is not given to be same across blender version. Prefer using
|
||||
* `from_orthonormal_axes` for more stable output.
|
||||
*/
|
||||
float3x3 from_up_axis(float3 up)
|
||||
{
|
||||
/* Duff, Tom, et al. "Building an orthonormal basis, revisited." JCGT 6.1 (2017). */
|
||||
float z_sign = up.z >= 0.0f ? 1.0f : -1.0f;
|
||||
float a = -1.0f / (z_sign + up.z);
|
||||
float b = up.x * up.y * a;
|
||||
|
||||
float3x3 basis;
|
||||
basis[0] = float3(1.0f + z_sign * square(up.x) * a, z_sign * b, -z_sign * up.x);
|
||||
basis[1] = float3(b, z_sign + square(up.y) * a, -up.y);
|
||||
basis[2] = up;
|
||||
return basis;
|
||||
}
|
||||
/** \} */
|
||||
@@ -0,0 +1,49 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_math_euler_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_compare_lib.glsl"
|
||||
#include "gpu_shader_math_quaternion_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Conversion function.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Extract the absolute 3d scale from a transform matrix.
|
||||
*/
|
||||
float3 to_scale(float3x3 mat)
|
||||
{
|
||||
return float3(length(mat[0]), length(mat[1]), length(mat[2]));
|
||||
}
|
||||
/**
|
||||
* Extract the absolute 3d scale from a transform matrix.
|
||||
*/
|
||||
float3 to_scale(float4x4 mat)
|
||||
{
|
||||
return to_scale(to_float3x3(mat));
|
||||
}
|
||||
/**
|
||||
* Extract the absolute 3d scale from a transform matrix.
|
||||
*/
|
||||
template<typename MatT, bool allow_negative_scale> float3 to_scale(MatT mat)
|
||||
{
|
||||
float3 result = to_scale(mat);
|
||||
if (allow_negative_scale) {
|
||||
if (is_negative(mat)) {
|
||||
result = -result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template float3 to_scale<float3x3, true>(float3x3 mat);
|
||||
template float3 to_scale<float3x3, false>(float3x3 mat);
|
||||
template float3 to_scale<float4x4, true>(float4x4 mat);
|
||||
template float3 to_scale<float4x4, false>(float4x4 mat);
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_matrix_construct_lib.glsl"
|
||||
#include "gpu_shader_math_quaternion_lib.glsl"
|
||||
#include "gpu_shader_math_rotation_conversion_lib.glsl"
|
||||
#include "gpu_shader_math_rotation_lib.glsl"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Interpolate
|
||||
* \{ */
|
||||
|
||||
#if 0 /* TODO(@fclem): Implement */
|
||||
/**
|
||||
* Naive interpolation implementation, faster than polar decomposition
|
||||
*
|
||||
* \note This code is about five times faster than the polar decomposition.
|
||||
* However, it gives un-expected results even with non-uniformly scaled matrices,
|
||||
* see #46418 for an example.
|
||||
*
|
||||
* \param a: Input matrix which is totally effective with `t = 0.0`.
|
||||
* \param b: Input matrix which is totally effective with `t = 1.0`.
|
||||
* \param t: Interpolation factor.
|
||||
*/
|
||||
float3x3 interpolate_fast(float3x3 a, float3x3 b, float t);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Naive transform matrix interpolation,
|
||||
* based on naive-decomposition-based interpolation from #interpolate_fast<T, 3, 3>.
|
||||
*/
|
||||
float4x4 interpolate_fast(float4x4 a, float4x4 b, float t)
|
||||
{
|
||||
float3 a_loc, b_loc;
|
||||
float3 a_scale, b_scale;
|
||||
Quaternion a_quat, b_quat;
|
||||
to_loc_rot_scale(a, a_loc, a_quat, a_scale);
|
||||
to_loc_rot_scale(b, b_loc, b_quat, b_scale);
|
||||
|
||||
float3 location = interpolate(a_loc, b_loc, t);
|
||||
float3 scale = interpolate(a_scale, b_scale, t);
|
||||
Quaternion rotation = interpolate(a_quat, b_quat, t);
|
||||
return from_loc_rot_scale(location, rotation, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute Moore-Penrose pseudo inverse of matrix.
|
||||
* Singular values below epsilon are ignored for stability (truncated SVD).
|
||||
*/
|
||||
/* TODO(fclem): Implement */
|
||||
// mat4x4 pseudo_invert(mat4x4 mat, float epsilon); /* Not implemented. Too complex to port. */
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,136 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Matrix Operations
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Flip the matrix across its diagonal. Also flips dimensions for non square matrices.
|
||||
*/
|
||||
// float3x3 transpose(float3x3 mat); /* Built-In using shading languages. */
|
||||
|
||||
/**
|
||||
* Returns the determinant of the matrix.
|
||||
* It can be interpreted as the signed volume (or area) of the unit cube after transformation.
|
||||
*/
|
||||
// float determinant(float3x3 mat); /* Built-In using shading languages. */
|
||||
|
||||
/**
|
||||
* Returns the inverse of a square matrix or zero matrix on failure.
|
||||
* \a r_success is optional and set to true if the matrix was inverted successfully.
|
||||
*/
|
||||
float2x2 invert(float2x2 mat)
|
||||
{
|
||||
return inverse(mat);
|
||||
}
|
||||
/**
|
||||
* Returns the inverse of a square matrix or zero matrix on failure.
|
||||
* \a r_success is optional and set to true if the matrix was inverted successfully.
|
||||
*/
|
||||
float3x3 invert(float3x3 mat)
|
||||
{
|
||||
return inverse(mat);
|
||||
}
|
||||
/**
|
||||
* Returns the inverse of a square matrix or zero matrix on failure.
|
||||
* \a r_success is optional and set to true if the matrix was inverted successfully.
|
||||
*/
|
||||
float4x4 invert(float4x4 mat)
|
||||
{
|
||||
return inverse(mat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inverse of a square matrix or zero matrix on failure.
|
||||
* \a r_success is optional and set to true if the matrix was inverted successfully.
|
||||
*/
|
||||
float2x2 invert(float2x2 mat, bool &r_success)
|
||||
{
|
||||
r_success = determinant(mat) != 0.0f;
|
||||
return r_success ? inverse(mat) : float2x2(0.0f);
|
||||
}
|
||||
/**
|
||||
* Returns the inverse of a square matrix or zero matrix on failure.
|
||||
* \a r_success is optional and set to true if the matrix was inverted successfully.
|
||||
*/
|
||||
float3x3 invert(float3x3 mat, bool &r_success)
|
||||
{
|
||||
r_success = determinant(mat) != 0.0f;
|
||||
return r_success ? inverse(mat) : float3x3(0.0f);
|
||||
}
|
||||
/**
|
||||
* Returns the inverse of a square matrix or zero matrix on failure.
|
||||
* \a r_success is optional and set to true if the matrix was inverted successfully.
|
||||
*/
|
||||
float4x4 invert(float4x4 mat, bool &r_success)
|
||||
{
|
||||
r_success = determinant(mat) != 0.0f;
|
||||
return r_success ? inverse(mat) : float4x4(0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to `mat * from_location(translation)` but with fewer operation.
|
||||
*/
|
||||
float4x4 translate(float4x4 mat, float3 translation)
|
||||
{
|
||||
mat[3].xyz += translation[0] * mat[0].xyz;
|
||||
mat[3].xyz += translation[1] * mat[1].xyz;
|
||||
mat[3].xyz += translation[2] * mat[2].xyz;
|
||||
return mat;
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_location(translation)` but with fewer operation.
|
||||
*/
|
||||
float4x4 translate(float4x4 mat, float2 translation)
|
||||
{
|
||||
mat[3].xyz += translation[0] * mat[0].xyz;
|
||||
mat[3].xyz += translation[1] * mat[1].xyz;
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
|
||||
*/
|
||||
float3x3 scale(float3x3 mat, float2 scale)
|
||||
{
|
||||
mat[0] *= scale[0];
|
||||
mat[1] *= scale[1];
|
||||
return mat;
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
|
||||
*/
|
||||
float3x3 scale(float3x3 mat, float3 scale)
|
||||
{
|
||||
mat[0] *= scale[0];
|
||||
mat[1] *= scale[1];
|
||||
mat[2] *= scale[2];
|
||||
return mat;
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
|
||||
*/
|
||||
float4x4 scale(float4x4 mat, float2 scale)
|
||||
{
|
||||
mat[0] *= scale[0];
|
||||
mat[1] *= scale[1];
|
||||
return mat;
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_scale(scale)` but with fewer operation.
|
||||
*/
|
||||
float4x4 scale(float4x4 mat, float3 scale)
|
||||
{
|
||||
mat[0] *= scale[0];
|
||||
mat[1] *= scale[1];
|
||||
mat[2] *= scale[2];
|
||||
return mat;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,209 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Normalize
|
||||
* \{ */
|
||||
|
||||
/* Needs to be defined for correct overloading. */
|
||||
#if defined(GPU_OPENGL) || defined(GPU_METAL)
|
||||
float2 normalize(float2 a)
|
||||
{
|
||||
return a * inversesqrt(length_squared(a));
|
||||
}
|
||||
float3 normalize(float3 a)
|
||||
{
|
||||
return a * inversesqrt(length_squared(a));
|
||||
}
|
||||
float4 normalize(float4 a)
|
||||
{
|
||||
return a * inversesqrt(length_squared(a));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Normalize each column of the matrix individually.
|
||||
*/
|
||||
#if 0 /* Remove unused variants as they are slow down compilation. */
|
||||
float2x2 normalize(float2x2 mat)
|
||||
{
|
||||
float2x2 ret;
|
||||
ret[0] = normalize(mat[0].xy);
|
||||
ret[1] = normalize(mat[1].xy);
|
||||
return ret;
|
||||
}
|
||||
float2x3 normalize(float2x3 mat)
|
||||
{
|
||||
float2x3 ret;
|
||||
ret[0] = normalize(mat[0].xyz);
|
||||
ret[1] = normalize(mat[1].xyz);
|
||||
return ret;
|
||||
}
|
||||
float2x4 normalize(float2x4 mat)
|
||||
{
|
||||
float2x4 ret;
|
||||
ret[0] = normalize(mat[0].xyzw);
|
||||
ret[1] = normalize(mat[1].xyzw);
|
||||
return ret;
|
||||
}
|
||||
float3x2 normalize(float3x2 mat)
|
||||
{
|
||||
float3x2 ret;
|
||||
ret[0] = normalize(mat[0].xy);
|
||||
ret[1] = normalize(mat[1].xy);
|
||||
ret[2] = normalize(mat[2].xy);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
float3x3 normalize(float3x3 mat)
|
||||
{
|
||||
float3x3 ret;
|
||||
ret[0] = normalize(mat[0].xyz);
|
||||
ret[1] = normalize(mat[1].xyz);
|
||||
ret[2] = normalize(mat[2].xyz);
|
||||
return ret;
|
||||
}
|
||||
#if 0 /* Remove unused variants as they are slow down compilation. */
|
||||
float3x4 normalize(float3x4 mat)
|
||||
{
|
||||
float3x4 ret;
|
||||
ret[0] = normalize(mat[0].xyzw);
|
||||
ret[1] = normalize(mat[1].xyzw);
|
||||
ret[2] = normalize(mat[2].xyzw);
|
||||
return ret;
|
||||
}
|
||||
float4x2 normalize(float4x2 mat)
|
||||
{
|
||||
float4x2 ret;
|
||||
ret[0] = normalize(mat[0].xy);
|
||||
ret[1] = normalize(mat[1].xy);
|
||||
ret[2] = normalize(mat[2].xy);
|
||||
ret[3] = normalize(mat[3].xy);
|
||||
return ret;
|
||||
}
|
||||
float4x3 normalize(float4x3 mat)
|
||||
{
|
||||
float4x3 ret;
|
||||
ret[0] = normalize(mat[0].xyz);
|
||||
ret[1] = normalize(mat[1].xyz);
|
||||
ret[2] = normalize(mat[2].xyz);
|
||||
ret[3] = normalize(mat[3].xyz);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
float4x4 normalize(float4x4 mat)
|
||||
{
|
||||
float4x4 ret;
|
||||
ret[0] = normalize(mat[0].xyzw);
|
||||
ret[1] = normalize(mat[1].xyzw);
|
||||
ret[2] = normalize(mat[2].xyzw);
|
||||
ret[3] = normalize(mat[3].xyzw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize each column of the matrix individually.
|
||||
* Return the length of each column vector.
|
||||
*/
|
||||
#if 0 /* Remove unused variants as they are slow down compilation. */
|
||||
float2x2 normalize_and_get_size(float2x2 mat, float2 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f;
|
||||
float2x2 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
r_size = float2(size_x, size_y);
|
||||
return ret;
|
||||
}
|
||||
float2x3 normalize_and_get_size(float2x3 mat, float2 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f;
|
||||
float2x3 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
r_size = float2(size_x, size_y);
|
||||
return ret;
|
||||
}
|
||||
float2x4 normalize_and_get_size(float2x4 mat, float2 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f;
|
||||
float2x4 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
r_size = float2(size_x, size_y);
|
||||
return ret;
|
||||
}
|
||||
float3x2 normalize_and_get_size(float3x2 mat, float3 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f;
|
||||
float3x2 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
ret[2] = normalize_and_get_length(mat[2], size_z);
|
||||
r_size = float3(size_x, size_y, size_z);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
float3x3 normalize_and_get_size(float3x3 mat, float3 &r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f;
|
||||
float3x3 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
ret[2] = normalize_and_get_length(mat[2], size_z);
|
||||
r_size = float3(size_x, size_y, size_z);
|
||||
return ret;
|
||||
}
|
||||
#if 0 /* Remove unused variants as they are slow down compilation. */
|
||||
float3x4 normalize_and_get_size(float3x4 mat, float3 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f;
|
||||
float3x4 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
ret[2] = normalize_and_get_length(mat[2], size_z);
|
||||
r_size = float3(size_x, size_y, size_z);
|
||||
return ret;
|
||||
}
|
||||
float4x2 normalize_and_get_size(float4x2 mat, float4 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f, size_w = 0.0f;
|
||||
float4x2 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
ret[2] = normalize_and_get_length(mat[2], size_z);
|
||||
ret[3] = normalize_and_get_length(mat[3], size_w);
|
||||
r_size = float4(size_x, size_y, size_z, size_w);
|
||||
return ret;
|
||||
}
|
||||
float4x3 normalize_and_get_size(float4x3 mat, float4 & r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f, size_w = 0.0f;
|
||||
float4x3 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
ret[2] = normalize_and_get_length(mat[2], size_z);
|
||||
ret[3] = normalize_and_get_length(mat[3], size_w);
|
||||
r_size = float4(size_x, size_y, size_z, size_w);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
float4x4 normalize_and_get_size(float4x4 mat, float4 &r_size)
|
||||
{
|
||||
float size_x = 0.0f, size_y = 0.0f, size_z = 0.0f, size_w = 0.0f;
|
||||
float4x4 ret;
|
||||
ret[0] = normalize_and_get_length(mat[0], size_x);
|
||||
ret[1] = normalize_and_get_length(mat[1], size_y);
|
||||
ret[2] = normalize_and_get_length(mat[2], size_z);
|
||||
ret[3] = normalize_and_get_length(mat[3], size_w);
|
||||
r_size = float4(size_x, size_y, size_z, size_w);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Projection Matrices.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \brief Create an orthographic projection matrix using OpenGL coordinate convention:
|
||||
* Maps each axis range to [-1..1] range for all axes.
|
||||
* The resulting matrix can be used with either #project_point or #transform_point.
|
||||
*/
|
||||
float4x4 projection_orthographic(
|
||||
float left, float right, float bottom, float top, float near_clip, float far_clip)
|
||||
{
|
||||
float x_delta = right - left;
|
||||
float y_delta = top - bottom;
|
||||
float z_delta = far_clip - near_clip;
|
||||
|
||||
float4x4 mat = float4x4(1.0f);
|
||||
if (x_delta != 0.0f && y_delta != 0.0f && z_delta != 0.0f) {
|
||||
mat[0][0] = 2.0f / x_delta;
|
||||
mat[3][0] = -(right + left) / x_delta;
|
||||
mat[1][1] = 2.0f / y_delta;
|
||||
mat[3][1] = -(top + bottom) / y_delta;
|
||||
mat[2][2] = -2.0f / z_delta; /* NOTE: negate Z. */
|
||||
mat[3][2] = -(far_clip + near_clip) / z_delta;
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a perspective projection matrix using OpenGL coordinate convention:
|
||||
* Maps each axis range to [-1..1] range for all axes.
|
||||
* `left`, `right`, `bottom`, `top` are frustum side distances at `z=near_clip`.
|
||||
* The resulting matrix can be used with #project_point.
|
||||
*/
|
||||
float4x4 projection_perspective(
|
||||
float left, float right, float bottom, float top, float near_clip, float far_clip)
|
||||
{
|
||||
float x_delta = right - left;
|
||||
float y_delta = top - bottom;
|
||||
float z_delta = far_clip - near_clip;
|
||||
|
||||
float4x4 mat = float4x4(1.0f);
|
||||
if (x_delta != 0.0f && y_delta != 0.0f && z_delta != 0.0f) {
|
||||
mat[0][0] = near_clip * 2.0f / x_delta;
|
||||
mat[1][1] = near_clip * 2.0f / y_delta;
|
||||
mat[2][0] = (right + left) / x_delta; /* NOTE: negate Z. */
|
||||
mat[2][1] = (top + bottom) / y_delta;
|
||||
mat[2][2] = -(far_clip + near_clip) / z_delta;
|
||||
mat[2][3] = -1.0f;
|
||||
mat[3][2] = (-2.0f * near_clip * far_clip) / z_delta;
|
||||
mat[3][3] = 0.0f;
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a perspective projection matrix using OpenGL coordinate convention:
|
||||
* Maps each axis range to [-1..1] range for all axes.
|
||||
* Uses field of view angles instead of plane distances.
|
||||
* The resulting matrix can be used with #project_point.
|
||||
*/
|
||||
float4x4 projection_perspective_fov(float angle_left,
|
||||
float angle_right,
|
||||
float angle_bottom,
|
||||
float angle_top,
|
||||
float near_clip,
|
||||
float far_clip)
|
||||
{
|
||||
float4x4 mat = projection_perspective(
|
||||
tan(angle_left), tan(angle_right), tan(angle_bottom), tan(angle_top), near_clip, far_clip);
|
||||
mat[0][0] /= near_clip;
|
||||
mat[1][1] /= near_clip;
|
||||
return mat;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform function.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Transform a 3d point using a 3x3 matrix (rotation & scale).
|
||||
*/
|
||||
float3 transform_point(float3x3 mat, float3 point)
|
||||
{
|
||||
return mat * point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a 3d point using a 4x4 matrix (location & rotation & scale).
|
||||
*/
|
||||
float3 transform_point(float4x4 mat, float3 point)
|
||||
{
|
||||
return (mat * float4(point, 1.0f)).xyz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a 2d point using a 3x3 matrix (location & rotation & scale).
|
||||
*/
|
||||
float2 transform_point(float3x3 mat, float2 point)
|
||||
{
|
||||
return (mat * float3(point, 1.0f)).xy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a 3d direction vector using a 3x3 matrix (rotation & scale).
|
||||
*/
|
||||
float3 transform_direction(float3x3 mat, float3 direction)
|
||||
{
|
||||
return mat * direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a 3d direction vector using a 4x4 matrix (rotation & scale).
|
||||
*/
|
||||
float3 transform_direction(float4x4 mat, float3 direction)
|
||||
{
|
||||
return to_float3x3(mat) * direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Project a point using a matrix (location & rotation & scale & perspective divide).
|
||||
*/
|
||||
float2 project_point(float3x3 mat, float2 point)
|
||||
{
|
||||
float3 tmp = mat * float3(point, 1.0f);
|
||||
/* Absolute value to not flip the frustum upside down behind the camera. */
|
||||
return tmp.xy / abs(tmp.z);
|
||||
}
|
||||
/**
|
||||
* Project a point using a matrix (location & rotation & scale & perspective divide).
|
||||
*/
|
||||
float3 project_point(float4x4 mat, float3 point)
|
||||
{
|
||||
float4 tmp = mat * float4(point, 1.0f);
|
||||
/* Absolute value to not flip the frustum upside down behind the camera. */
|
||||
return tmp.xyz / abs(tmp.w);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
struct Quaternion {
|
||||
float x, y, z, w;
|
||||
|
||||
static Quaternion identity()
|
||||
{
|
||||
return {1, 0, 0, 0};
|
||||
}
|
||||
|
||||
float4 as_float4() const
|
||||
{
|
||||
return float4(this->x, this->y, this->z, this->w);
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Quaternion Math
|
||||
* \{ */
|
||||
|
||||
Quaternion math_quaternion_multiply(Quaternion a, Quaternion b)
|
||||
{
|
||||
Quaternion result;
|
||||
result.x = a.x * b.x - a.y * b.y - a.z * b.z - a.w * b.w;
|
||||
result.y = a.x * b.y + a.y * b.x + a.z * b.w - a.w * b.z;
|
||||
result.z = a.x * b.z - a.y * b.w + a.z * b.x + a.w * b.y;
|
||||
result.w = a.x * b.w + a.y * b.z - a.z * b.y + a.w * b.x;
|
||||
return result;
|
||||
}
|
||||
|
||||
Quaternion quaternion_conjugate(Quaternion q)
|
||||
{
|
||||
return {q.x, -q.y, -q.z, -q.w};
|
||||
}
|
||||
|
||||
float3 transform_point_by_quaternion(Quaternion q, float3 v)
|
||||
{
|
||||
const Quaternion v_quat = {0.0f, v.x, v.y, v.z};
|
||||
const Quaternion result = math_quaternion_multiply(math_quaternion_multiply(q, v_quat),
|
||||
quaternion_conjugate(q));
|
||||
return float3(result.y, result.z, result.w);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,396 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_axis_angle_lib.glsl"
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_math_euler_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_conversion_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_normalize_lib.glsl"
|
||||
#include "gpu_shader_math_quaternion_lib.glsl"
|
||||
#include "gpu_shader_math_vector_compare_lib.glsl"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
namespace detail {
|
||||
|
||||
Quaternion normalized_to_quat_fast(float3x3 mat)
|
||||
{
|
||||
/* Caller must ensure matrices aren't negative for valid results, see: #24291, #94231. */
|
||||
Quaternion q;
|
||||
|
||||
/* Method outlined by Mike Day, ref: https://math.stackexchange.com/a/3183435/220949
|
||||
* with an additional `sqrtf(..)` for higher precision result.
|
||||
* Removing the `sqrt` causes tests to fail unless the precision is set to 1e-6f or larger. */
|
||||
|
||||
if (mat[2][2] < 0.0f) {
|
||||
if (mat[0][0] > mat[1][1]) {
|
||||
float trace = 1.0f + mat[0][0] - mat[1][1] - mat[2][2];
|
||||
float s = 2.0f * sqrt(trace);
|
||||
if (mat[1][2] < mat[2][1]) {
|
||||
/* Ensure W is non-negative for a canonical result. */
|
||||
s = -s;
|
||||
}
|
||||
q.y = 0.25f * s;
|
||||
s = 1.0f / s;
|
||||
q.x = (mat[1][2] - mat[2][1]) * s;
|
||||
q.z = (mat[0][1] + mat[1][0]) * s;
|
||||
q.w = (mat[2][0] + mat[0][2]) * s;
|
||||
if ((trace == 1.0f) && (q.x == 0.0f && q.z == 0.0f && q.w == 0.0f)) {
|
||||
/* Avoids the need to normalize the degenerate case. */
|
||||
q.y = 1.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float trace = 1.0f - mat[0][0] + mat[1][1] - mat[2][2];
|
||||
float s = 2.0f * sqrt(trace);
|
||||
if (mat[2][0] < mat[0][2]) {
|
||||
/* Ensure W is non-negative for a canonical result. */
|
||||
s = -s;
|
||||
}
|
||||
q.z = 0.25f * s;
|
||||
s = 1.0f / s;
|
||||
q.x = (mat[2][0] - mat[0][2]) * s;
|
||||
q.y = (mat[0][1] + mat[1][0]) * s;
|
||||
q.w = (mat[1][2] + mat[2][1]) * s;
|
||||
if ((trace == 1.0f) && (q.x == 0.0f && q.y == 0.0f && q.w == 0.0f)) {
|
||||
/* Avoids the need to normalize the degenerate case. */
|
||||
q.z = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mat[0][0] < -mat[1][1]) {
|
||||
float trace = 1.0f - mat[0][0] - mat[1][1] + mat[2][2];
|
||||
float s = 2.0f * sqrt(trace);
|
||||
if (mat[0][1] < mat[1][0]) {
|
||||
/* Ensure W is non-negative for a canonical result. */
|
||||
s = -s;
|
||||
}
|
||||
q.w = 0.25f * s;
|
||||
s = 1.0f / s;
|
||||
q.x = (mat[0][1] - mat[1][0]) * s;
|
||||
q.y = (mat[2][0] + mat[0][2]) * s;
|
||||
q.z = (mat[1][2] + mat[2][1]) * s;
|
||||
if ((trace == 1.0f) && (q.x == 0.0f && q.y == 0.0f && q.z == 0.0f)) {
|
||||
/* Avoids the need to normalize the degenerate case. */
|
||||
q.w = 1.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* NOTE(@ideasman42): A zero matrix will fall through to this block,
|
||||
* needed so a zero scaled matrices to return a quaternion without rotation, see: #101848. */
|
||||
float trace = 1.0f + mat[0][0] + mat[1][1] + mat[2][2];
|
||||
float s = 2.0f * sqrt(trace);
|
||||
q.x = 0.25f * s;
|
||||
s = 1.0f / s;
|
||||
q.y = (mat[1][2] - mat[2][1]) * s;
|
||||
q.z = (mat[2][0] - mat[0][2]) * s;
|
||||
q.w = (mat[0][1] - mat[1][0]) * s;
|
||||
if ((trace == 1.0f) && (q.y == 0.0f && q.z == 0.0f && q.w == 0.0f)) {
|
||||
/* Avoids the need to normalize the degenerate case. */
|
||||
q.x = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
Quaternion normalized_to_quat_with_checks(float3x3 mat)
|
||||
{
|
||||
float det = determinant(mat);
|
||||
if (!isfinite(det)) {
|
||||
return Quaternion::identity();
|
||||
}
|
||||
if (det < 0.0f) {
|
||||
return normalized_to_quat_fast(-mat);
|
||||
}
|
||||
return normalized_to_quat_fast(mat);
|
||||
}
|
||||
|
||||
void normalized_to_eul2(float3x3 mat, EulerXYZ &eul1, EulerXYZ &eul2)
|
||||
{
|
||||
float cy = hypot(mat[0][0], mat[0][1]);
|
||||
if (cy > 16.0f * FLT_EPSILON) {
|
||||
eul1.x = atan2(mat[1][2], mat[2][2]);
|
||||
eul1.y = atan2(-mat[0][2], cy);
|
||||
eul1.z = atan2(mat[0][1], mat[0][0]);
|
||||
|
||||
eul2.x = atan2(-mat[1][2], -mat[2][2]);
|
||||
eul2.y = atan2(-mat[0][2], -cy);
|
||||
eul2.z = atan2(-mat[0][1], -mat[0][0]);
|
||||
}
|
||||
else {
|
||||
eul1.x = atan2(-mat[2][1], mat[1][1]);
|
||||
eul1.y = atan2(-mat[0][2], cy);
|
||||
eul1.z = 0.0f;
|
||||
|
||||
eul2 = eul1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Quaternion Functions
|
||||
* \{ */
|
||||
|
||||
Quaternion to_quaternion(EulerXYZ eul)
|
||||
{
|
||||
float ti = eul.x * 0.5f;
|
||||
float tj = eul.y * 0.5f;
|
||||
float th = eul.z * 0.5f;
|
||||
float ci = cos(ti);
|
||||
float cj = cos(tj);
|
||||
float ch = cos(th);
|
||||
float si = sin(ti);
|
||||
float sj = sin(tj);
|
||||
float sh = sin(th);
|
||||
float cc = ci * ch;
|
||||
float cs = ci * sh;
|
||||
float sc = si * ch;
|
||||
float ss = si * sh;
|
||||
|
||||
Quaternion quat;
|
||||
quat.x = cj * cc + sj * ss;
|
||||
quat.y = cj * sc - sj * cs;
|
||||
quat.z = cj * ss + sj * cc;
|
||||
quat.w = cj * cs - sj * sc;
|
||||
return quat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract quaternion rotation from transform matrix.
|
||||
* \note normalized is set to false by default.
|
||||
*/
|
||||
Quaternion to_quaternion(float3x3 mat)
|
||||
{
|
||||
return detail::normalized_to_quat_with_checks(normalize(mat));
|
||||
}
|
||||
/**
|
||||
* Extract quaternion rotation from transform matrix.
|
||||
* \note normalized is set to false by default.
|
||||
*/
|
||||
Quaternion to_quaternion(float3x3 mat, const bool normalized)
|
||||
{
|
||||
if (!normalized) {
|
||||
mat = normalize(mat);
|
||||
}
|
||||
return to_quaternion(mat);
|
||||
}
|
||||
/**
|
||||
* Extract quaternion rotation from transform matrix.
|
||||
* \note normalized is set to false by default.
|
||||
*/
|
||||
Quaternion to_quaternion(float4x4 mat)
|
||||
{
|
||||
return to_quaternion(to_float3x3(mat));
|
||||
}
|
||||
/**
|
||||
* Extract quaternion rotation from transform matrix.
|
||||
* \note normalized is set to false by default.
|
||||
*/
|
||||
Quaternion to_quaternion(float4x4 mat, const bool normalized)
|
||||
{
|
||||
return to_quaternion(to_float3x3(mat), normalized);
|
||||
}
|
||||
|
||||
Quaternion to_quaternion(AxisAngle axis_angle)
|
||||
{
|
||||
float angle_cos = cos(axis_angle.angle);
|
||||
/** Using half angle identities: sin(angle / 2) = sqrt((1 - angle_cos) / 2) */
|
||||
float sine = sqrt(0.5f - angle_cos * 0.5f);
|
||||
float cosine = sqrt(0.5f + angle_cos * 0.5f);
|
||||
|
||||
/* TODO(fclem): Optimize. */
|
||||
float angle_sin = sin(axis_angle.angle);
|
||||
if (angle_sin < 0.0f) {
|
||||
sine = -sine;
|
||||
}
|
||||
|
||||
Quaternion quat;
|
||||
quat.x = cosine;
|
||||
quat.y = axis_angle.axis.x * sine;
|
||||
quat.z = axis_angle.axis.y * sine;
|
||||
quat.w = axis_angle.axis.z * sine;
|
||||
return quat;
|
||||
}
|
||||
|
||||
Quaternion to_quaternion(float3 axis, float angle)
|
||||
{
|
||||
if (is_zero(axis)) {
|
||||
return Quaternion::identity();
|
||||
}
|
||||
AxisAngle aa;
|
||||
aa.axis = normalize(axis);
|
||||
aa.angle = angle;
|
||||
return to_quaternion(aa);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Euler Functions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Extract euler rotation from transform matrix.
|
||||
* \return the rotation with the smallest values from the potential candidates.
|
||||
*/
|
||||
EulerXYZ to_euler(float3x3 mat, const bool normalized)
|
||||
{
|
||||
if (!normalized) {
|
||||
mat = normalize(mat);
|
||||
}
|
||||
EulerXYZ eul1, eul2;
|
||||
detail::normalized_to_eul2(mat, eul1, eul2);
|
||||
/* Return best, which is just the one with lowest values it in. */
|
||||
return (length_manhattan(eul1.as_float3()) > length_manhattan(eul2.as_float3())) ? eul2 : eul1;
|
||||
}
|
||||
/**
|
||||
* Extract euler rotation from transform matrix.
|
||||
* \return the rotation with the smallest values from the potential candidates.
|
||||
*/
|
||||
EulerXYZ to_euler(float3x3 mat)
|
||||
{
|
||||
return to_euler(mat, true);
|
||||
}
|
||||
/**
|
||||
* Extract euler rotation from transform matrix.
|
||||
* \return the rotation with the smallest values from the potential candidates.
|
||||
*/
|
||||
EulerXYZ to_euler(float4x4 mat, const bool normalized)
|
||||
{
|
||||
return to_euler(to_float3x3(mat), normalized);
|
||||
}
|
||||
/**
|
||||
* Extract euler rotation from transform matrix.
|
||||
* \return the rotation with the smallest values from the potential candidates.
|
||||
*/
|
||||
EulerXYZ to_euler(float4x4 mat)
|
||||
{
|
||||
return to_euler(to_float3x3(mat));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Axis Angle Functions
|
||||
* \{ */
|
||||
|
||||
AxisAngle to_axis_angle(Quaternion quat)
|
||||
{
|
||||
/* Calculate angle/2, and sin(angle/2). */
|
||||
float ha = acos(quat.x);
|
||||
float si = sin(ha);
|
||||
|
||||
/* From half-angle to angle. */
|
||||
float angle = ha * 2;
|
||||
/* Prevent division by zero for axis conversion. */
|
||||
if (abs(si) < 0.0005f) {
|
||||
si = 1.0f;
|
||||
}
|
||||
|
||||
float3 axis = float3(quat.y, quat.z, quat.w) / si;
|
||||
if (is_zero(axis)) {
|
||||
axis[1] = 1.0f;
|
||||
}
|
||||
return {axis, angle};
|
||||
}
|
||||
|
||||
AxisAngle to_axis_angle(EulerXYZ eul)
|
||||
{
|
||||
/* Use quaternions as intermediate representation for now... */
|
||||
return to_axis_angle(to_quaternion(eul));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Matrices Functions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Decompose a matrix into location, rotation, and scale components.
|
||||
* \tparam allow_negative_scale: if true, will compute determinant to know if matrix is negative.
|
||||
* Rotation and scale values will be flipped if it is negative.
|
||||
* This is a costly operation so it is disabled by default.
|
||||
*/
|
||||
void to_rot_scale(float3x3 mat, EulerXYZ &r_rotation, float3 &r_scale)
|
||||
{
|
||||
r_scale = to_scale(mat);
|
||||
r_rotation = to_euler(mat, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompose a matrix into location, rotation, and scale components.
|
||||
* \tparam allow_negative_scale: if true, will compute determinant to know if matrix is negative.
|
||||
* Rotation and scale values will be flipped if it is negative.
|
||||
* This is a costly operation so it is disabled by default.
|
||||
*/
|
||||
void to_rot_scale(float3x3 mat,
|
||||
EulerXYZ &r_rotation,
|
||||
float3 &r_scale,
|
||||
const bool allow_negative_scale)
|
||||
{
|
||||
float3x3 normalized_mat = normalize_and_get_size(mat, r_scale);
|
||||
if (allow_negative_scale) {
|
||||
if (is_negative(normalized_mat)) {
|
||||
normalized_mat = -normalized_mat;
|
||||
r_scale = -r_scale;
|
||||
}
|
||||
}
|
||||
r_rotation = to_euler(normalized_mat, true);
|
||||
}
|
||||
void to_rot_scale(float3x3 mat, Quaternion &r_rotation, float3 &r_scale)
|
||||
{
|
||||
r_scale = to_scale(mat);
|
||||
r_rotation = to_quaternion(mat, true);
|
||||
}
|
||||
void to_rot_scale(float3x3 mat,
|
||||
Quaternion &r_rotation,
|
||||
float3 &r_scale,
|
||||
const bool allow_negative_scale)
|
||||
{
|
||||
float3x3 normalized_mat = normalize_and_get_size(mat, r_scale);
|
||||
if (allow_negative_scale) {
|
||||
if (is_negative(normalized_mat)) {
|
||||
normalized_mat = -normalized_mat;
|
||||
r_scale = -r_scale;
|
||||
}
|
||||
}
|
||||
r_rotation = to_quaternion(normalized_mat, true);
|
||||
}
|
||||
|
||||
void to_loc_rot_scale(float4x4 mat, float3 &r_location, EulerXYZ &r_rotation, float3 &r_scale)
|
||||
{
|
||||
r_location = mat[3].xyz;
|
||||
to_rot_scale(to_float3x3(mat), r_rotation, r_scale);
|
||||
}
|
||||
void to_loc_rot_scale(float4x4 mat,
|
||||
float3 &r_location,
|
||||
EulerXYZ &r_rotation,
|
||||
float3 &r_scale,
|
||||
const bool allow_negative_scale)
|
||||
{
|
||||
r_location = mat[3].xyz;
|
||||
to_rot_scale(to_float3x3(mat), r_rotation, r_scale, allow_negative_scale);
|
||||
}
|
||||
void to_loc_rot_scale(float4x4 mat, float3 &r_location, Quaternion &r_rotation, float3 &r_scale)
|
||||
{
|
||||
r_location = mat[3].xyz;
|
||||
to_rot_scale(to_float3x3(mat), r_rotation, r_scale);
|
||||
}
|
||||
void to_loc_rot_scale(float4x4 mat,
|
||||
float3 &r_location,
|
||||
Quaternion &r_rotation,
|
||||
float3 &r_scale,
|
||||
const bool allow_negative_scale)
|
||||
{
|
||||
r_location = mat[3].xyz;
|
||||
to_rot_scale(to_float3x3(mat), r_rotation, r_scale, allow_negative_scale);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,153 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_axis_angle_lib.glsl"
|
||||
#include "gpu_shader_math_euler_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_construct_lib.glsl"
|
||||
#include "gpu_shader_math_quaternion_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Interpolate
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Generic function for implementing slerp
|
||||
* (quaternions and spherical vector coords).
|
||||
*
|
||||
* \param t: factor in [0..1]
|
||||
* \param cosom: dot product from normalized vectors/quaternions.
|
||||
* \return calculated weights.
|
||||
*/
|
||||
float2 interpolate_dot_slerp(float t, float cosom)
|
||||
{
|
||||
float2 w = float2(1.0f - t, t);
|
||||
/* Within [-1..1] range, avoid aligned axis. */
|
||||
constexpr float eps = 1e-4f;
|
||||
if (abs(cosom) < 1.0f - eps) {
|
||||
float omega = acos(cosom);
|
||||
w = sin(w * omega) / sin(omega);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
Quaternion interpolate(Quaternion a, Quaternion b, float t)
|
||||
{
|
||||
float4 quat = a.as_float4();
|
||||
float cosom = dot(a.as_float4(), b.as_float4());
|
||||
/* Rotate around shortest angle. */
|
||||
if (cosom < 0.0f) {
|
||||
cosom = -cosom;
|
||||
quat = -quat;
|
||||
}
|
||||
float2 w = interpolate_dot_slerp(t, cosom);
|
||||
quat = w.x * quat + w.y * b.as_float4();
|
||||
return Quaternion{UNPACK4(quat)};
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Rotate
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
|
||||
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
|
||||
*/
|
||||
float3x3 rotate(float3x3 mat, AxisAngle rotation)
|
||||
{
|
||||
float3x3 result;
|
||||
/* axis_vec is given to be normalized. */
|
||||
if (rotation.axis.x == 1.0f) {
|
||||
float angle_cos = cos(rotation.angle);
|
||||
float angle_sin = sin(rotation.angle);
|
||||
for (int c = 0; c < 3; c++) {
|
||||
result[0][c] = mat[0][c];
|
||||
result[1][c] = angle_cos * mat[1][c] + angle_sin * mat[2][c];
|
||||
result[2][c] = -angle_sin * mat[1][c] + angle_cos * mat[2][c];
|
||||
}
|
||||
}
|
||||
else if (rotation.axis.y == 1.0f) {
|
||||
float angle_cos = cos(rotation.angle);
|
||||
float angle_sin = sin(rotation.angle);
|
||||
for (int c = 0; c < 3; c++) {
|
||||
result[0][c] = angle_cos * mat[0][c] - angle_sin * mat[2][c];
|
||||
result[1][c] = mat[1][c];
|
||||
result[2][c] = angle_sin * mat[0][c] + angle_cos * mat[2][c];
|
||||
}
|
||||
}
|
||||
else if (rotation.axis.z == 1.0f) {
|
||||
float angle_cos = cos(rotation.angle);
|
||||
float angle_sin = sin(rotation.angle);
|
||||
for (int c = 0; c < 3; c++) {
|
||||
result[0][c] = angle_cos * mat[0][c] + angle_sin * mat[1][c];
|
||||
result[1][c] = -angle_sin * mat[0][c] + angle_cos * mat[1][c];
|
||||
result[2][c] = mat[2][c];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Un-optimized case. Arbitrary rotation. */
|
||||
result = mat * from_rotation(rotation);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
|
||||
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
|
||||
*/
|
||||
float3x3 rotate(float3x3 mat, EulerXYZ rotation)
|
||||
{
|
||||
AxisAngle axis_angle;
|
||||
if (rotation.y == 0.0f && rotation.z == 0.0f) {
|
||||
axis_angle = AxisAngle{float3(1.0f, 0.0f, 0.0f), rotation.x};
|
||||
}
|
||||
else if (rotation.x == 0.0f && rotation.z == 0.0f) {
|
||||
axis_angle = AxisAngle{float3(0.0f, 1.0f, 0.0f), rotation.y};
|
||||
}
|
||||
else if (rotation.x == 0.0f && rotation.y == 0.0f) {
|
||||
axis_angle = AxisAngle{float3(0.0f, 0.0f, 1.0f), rotation.z};
|
||||
}
|
||||
else {
|
||||
/* Un-optimized case. Arbitrary rotation. */
|
||||
return mat * from_rotation(rotation);
|
||||
}
|
||||
return rotate(mat, axis_angle);
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
|
||||
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
|
||||
*/
|
||||
float4x4 rotate(float4x4 mat, AxisAngle rotation)
|
||||
{
|
||||
float4x4 result = to_float4x4(rotate(to_float3x3(mat), rotation));
|
||||
result[0][3] = mat[0][3];
|
||||
result[1][3] = mat[1][3];
|
||||
result[2][3] = mat[2][3];
|
||||
result[3][0] = mat[3][0];
|
||||
result[3][1] = mat[3][1];
|
||||
result[3][2] = mat[3][2];
|
||||
result[3][3] = mat[3][3];
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Equivalent to `mat * from_rotation(rotation)` but with fewer operation.
|
||||
* Optimized for rotation on basis vector (i.e: AxisAngle({1, 0, 0}, 0.2f)).
|
||||
*/
|
||||
float4x4 rotate(float4x4 mat, EulerXYZ rotation)
|
||||
{
|
||||
float4x4 result = to_float4x4(rotate(to_float3x3(mat), rotation));
|
||||
result[0][3] = mat[0][3];
|
||||
result[1][3] = mat[1][3];
|
||||
result[2][3] = mat[2][3];
|
||||
result[3][0] = mat[3][0];
|
||||
result[3][1] = mat[3][1];
|
||||
result[3][2] = mat[3][2];
|
||||
result[3][3] = mat[3][3];
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,129 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Safe Math Functions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Safe `a` modulo `b`.
|
||||
* If `b` equal 0 the result will be 0.
|
||||
*/
|
||||
float safe_mod(float a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? mod(a, b) : 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe divide `a` by `b`.
|
||||
* If `b` equal 0 the result will be 0.
|
||||
*/
|
||||
float safe_divide(float a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? (a / b) : 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe reciprocal function. Returns `1/a`.
|
||||
* If `a` equal 0 the result will be 0.
|
||||
*/
|
||||
float safe_rcp(float a)
|
||||
{
|
||||
return (a != 0.0f) ? (1.0f / a) : 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe square root function. Returns `sqrt(a)`.
|
||||
* If `a` is less or equal to 0 then the result will be 0.
|
||||
*/
|
||||
float safe_sqrt(float a)
|
||||
{
|
||||
return sqrt(max(0.0f, a));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe `arccosine` function. Returns `acos(a)`.
|
||||
* If `a` is greater than 1, returns 0.
|
||||
* If `a` is less than -1, returns PI.
|
||||
*/
|
||||
float safe_acos(float a)
|
||||
{
|
||||
if (a <= -1.0f) {
|
||||
return M_PI;
|
||||
}
|
||||
else if (a >= 1.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
return acos(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of pow that returns a fallback value if the computation is undefined. From the spec:
|
||||
* The result is undefined if x < 0 or if x = 0 and y is less than or equal 0.
|
||||
*/
|
||||
float fallback_pow(float x, float y, float fallback)
|
||||
{
|
||||
if (x < 0.0f || (x == 0.0f && y <= 0.0f)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return pow(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of pow that behaves similar to C++ std::pow.
|
||||
*/
|
||||
float compatible_pow(float x, float y)
|
||||
{
|
||||
if (y == 0.0f) { /* x^0 -> 1, including 0^0 */
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* GLSL pow doesn't accept negative x. */
|
||||
if (x < 0.0f) {
|
||||
if (mod(-y, 2.0f) == 0.0f) {
|
||||
return pow(-x, y);
|
||||
}
|
||||
else {
|
||||
return -pow(-x, y);
|
||||
}
|
||||
}
|
||||
else if (x == 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return pow(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float compatible_mod(float a, float b)
|
||||
{
|
||||
if (b != 0.0f) {
|
||||
int N = int(a / b);
|
||||
return a - N * b;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given value a to fall within the range [b, c].
|
||||
*/
|
||||
float wrap(float a, float b, float c)
|
||||
{
|
||||
float range = b - c;
|
||||
/* Avoid discrepancy on some hardware due to floating point accuracy and fast math. */
|
||||
float s = (a != b) ? floor((a - c) / range) : 1.0f;
|
||||
return (range != 0.0f) ? a - range * s : c;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Comparison
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Return true if all components is equal to zero.
|
||||
*/
|
||||
template<typename VecT> bool is_zero(VecT vec)
|
||||
{
|
||||
return all(equal(vec, VecT(0.0f)));
|
||||
}
|
||||
template bool is_zero<float2>(float2);
|
||||
template bool is_zero<float3>(float3);
|
||||
template bool is_zero<float4>(float4);
|
||||
|
||||
/**
|
||||
* Return true if any component is equal to zero.
|
||||
*/
|
||||
template<typename VecT> bool is_any_zero(VecT vec)
|
||||
{
|
||||
return any(equal(vec, VecT(0.0f)));
|
||||
}
|
||||
template bool is_any_zero<float2>(float2);
|
||||
template bool is_any_zero<float3>(float3);
|
||||
template bool is_any_zero<float4>(float4);
|
||||
|
||||
/**
|
||||
* Return true if the difference between`a` and `b` is below the `epsilon` value.
|
||||
*/
|
||||
template<typename VecT> bool is_equal(VecT a, VecT b, float epsilon)
|
||||
{
|
||||
return all(lessThanEqual(abs(a - b), VecT(epsilon)));
|
||||
}
|
||||
template bool is_equal<float2>(float2, float2, float);
|
||||
template bool is_equal<float3>(float3, float3, float);
|
||||
template bool is_equal<float4>(float4, float4, float);
|
||||
|
||||
/**
|
||||
* Return true if the deference between`a` and `b` is below the `epsilon` value.
|
||||
* Epsilon value is scaled by magnitude of `a` before comparison.
|
||||
*/
|
||||
template<typename VecT, int dim>
|
||||
bool almost_equal_relative(VecT a, VecT b, const float epsilon_factor)
|
||||
{
|
||||
for (int i = 0; i < dim; i++) {
|
||||
if (abs(a[i] - b[i]) > epsilon_factor * abs(a[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template bool almost_equal_relative<float2, 2>(float2 a, float2 b, const float epsilon_factor);
|
||||
template bool almost_equal_relative<float3, 3>(float3 a, float3 b, const float epsilon_factor);
|
||||
template bool almost_equal_relative<float4, 4>(float4 a, float4 b, const float epsilon_factor);
|
||||
|
||||
/* Checks are flipped so NAN doesn't assert because we're making sure the value was
|
||||
* normalized and in the case we don't want NAN to be raising asserts since there
|
||||
* is nothing to be done in that case. */
|
||||
template<typename VecT> bool is_unit_scale(VecT v)
|
||||
{
|
||||
constexpr float assert_unit_epsilon = 0.0002f;
|
||||
float test_unit = dot(v, v);
|
||||
return (!(abs(test_unit - 1.0f) >= assert_unit_epsilon) ||
|
||||
!(abs(test_unit) >= assert_unit_epsilon));
|
||||
}
|
||||
template bool is_unit_scale<float2>(float2);
|
||||
template bool is_unit_scale<float3>(float3);
|
||||
template bool is_unit_scale<float4>(float4);
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,179 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/**
|
||||
* Returns \a a if it is a multiple of \a b or the next multiple or \a b after \b a .
|
||||
* In other words, it is equivalent to `divide_ceil(a, b) * b`.
|
||||
* It is undefined if \a a is negative or \b b is not strictly positive.
|
||||
*/
|
||||
template<typename VecT> VecT ceil_to_multiple(VecT a, VecT b)
|
||||
{
|
||||
return ((a + b - VecT(1)) / b) * b;
|
||||
}
|
||||
template int2 ceil_to_multiple<int2>(int2, int2);
|
||||
template int3 ceil_to_multiple<int3>(int3, int3);
|
||||
template int4 ceil_to_multiple<int4>(int4, int4);
|
||||
template uint2 ceil_to_multiple<uint2>(uint2, uint2);
|
||||
template uint3 ceil_to_multiple<uint3>(uint3, uint3);
|
||||
template uint4 ceil_to_multiple<uint4>(uint4, uint4);
|
||||
|
||||
/**
|
||||
* Integer division that returns the ceiling, instead of flooring like normal C division.
|
||||
* It is undefined if \a a is negative or \b b is not strictly positive.
|
||||
*/
|
||||
template<typename VecT> VecT divide_ceil(VecT a, VecT b)
|
||||
{
|
||||
return (a + b - VecT(1)) / b;
|
||||
}
|
||||
template int2 divide_ceil<int2>(int2, int2);
|
||||
template int3 divide_ceil<int3>(int3, int3);
|
||||
template int4 divide_ceil<int4>(int4, int4);
|
||||
template uint2 divide_ceil<uint2>(uint2, uint2);
|
||||
template uint3 divide_ceil<uint3>(uint3, uint3);
|
||||
template uint4 divide_ceil<uint4>(uint4, uint4);
|
||||
|
||||
/**
|
||||
* Component wise, use vector to replace min if it is smaller and max if bigger.
|
||||
*/
|
||||
template<typename VecT> void min_max(VecT vector, VecT &min_v, VecT &max_v)
|
||||
{
|
||||
min_v = min(vector, min_v);
|
||||
max_v = max(vector, max_v);
|
||||
}
|
||||
template void min_max<float2>(float2, float2 &, float2 &);
|
||||
template void min_max<float3>(float3, float3 &, float3 &);
|
||||
template void min_max<float4>(float4, float4 &, float4 &);
|
||||
|
||||
/**
|
||||
* Return the manhattan length of `a`.
|
||||
* This is also the sum of the absolute value of all components.
|
||||
*/
|
||||
template<typename VecT> float length_manhattan(VecT a)
|
||||
{
|
||||
return dot(abs(a), VecT(1));
|
||||
}
|
||||
template float length_manhattan<float2>(float2);
|
||||
template float length_manhattan<float3>(float3);
|
||||
template float length_manhattan<float4>(float4);
|
||||
|
||||
/**
|
||||
* Return the length squared of `a`.
|
||||
*/
|
||||
template<typename VecT> float length_squared(VecT a)
|
||||
{
|
||||
return dot(a, a);
|
||||
}
|
||||
template float length_squared<float2>(float2);
|
||||
template float length_squared<float3>(float3);
|
||||
template float length_squared<float4>(float4);
|
||||
|
||||
/**
|
||||
* Return the manhattan distance between `a` and `b`.
|
||||
*/
|
||||
template<typename VecT> float distance_manhattan(VecT a, VecT b)
|
||||
{
|
||||
return length_manhattan(a - b);
|
||||
}
|
||||
template float distance_manhattan<float2>(float2, float2);
|
||||
template float distance_manhattan<float3>(float3, float3);
|
||||
template float distance_manhattan<float4>(float4, float4);
|
||||
|
||||
/**
|
||||
* Return the squared distance between `a` and `b`.
|
||||
*/
|
||||
template<typename VecT> float distance_squared(VecT a, VecT b)
|
||||
{
|
||||
return length_squared(a - b);
|
||||
}
|
||||
template float distance_squared<float2>(float2, float2);
|
||||
template float distance_squared<float3>(float3, float3);
|
||||
template float distance_squared<float4>(float4, float4);
|
||||
|
||||
/**
|
||||
* Return normalized version of the `vector` and its length.
|
||||
*/
|
||||
template<typename VecT> VecT normalize_and_get_length(VecT vector, float &out_length)
|
||||
{
|
||||
out_length = length_squared(vector);
|
||||
constexpr float threshold = 1e-35f;
|
||||
if (out_length > threshold) {
|
||||
out_length = sqrt(out_length);
|
||||
return vector / out_length;
|
||||
}
|
||||
/* Either the vector is small or one of its values contained `nan`. */
|
||||
out_length = 0.0f;
|
||||
return VecT(0.0f);
|
||||
}
|
||||
template float2 normalize_and_get_length<float2>(float2, float &);
|
||||
template float3 normalize_and_get_length<float3>(float3, float &);
|
||||
template float4 normalize_and_get_length<float4>(float4, float &);
|
||||
|
||||
/**
|
||||
* Per component linear interpolation.
|
||||
*/
|
||||
template<typename VecT> VecT interpolate(VecT a, VecT b, float t)
|
||||
{
|
||||
return mix(a, b, t);
|
||||
}
|
||||
template float2 interpolate<float2>(float2, float2, float);
|
||||
template float3 interpolate<float3>(float3, float3, float);
|
||||
template float4 interpolate<float4>(float4, float4, float);
|
||||
|
||||
/**
|
||||
* Return half-way point between `a` and `b`.
|
||||
*/
|
||||
template<typename VecT> VecT midpoint(VecT a, VecT b)
|
||||
{
|
||||
return (a + b) * 0.5f;
|
||||
}
|
||||
template float2 midpoint<float2>(float2, float2);
|
||||
template float3 midpoint<float3>(float3, float3);
|
||||
template float4 midpoint<float4>(float4, float4);
|
||||
|
||||
/**
|
||||
* Return the index of the component with the greatest absolute value.
|
||||
*/
|
||||
int dominant_axis(float3 a)
|
||||
{
|
||||
float3 b = abs(a);
|
||||
return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a perpendicular vector to \a v.
|
||||
* \note Returned vector is always rotated 90 degrees counter clock wise.
|
||||
*/
|
||||
template<typename VecT> VecT orthogonal(VecT v)
|
||||
{
|
||||
return VecT(-v.y, v.x);
|
||||
}
|
||||
template int2 orthogonal<int2>(int2);
|
||||
template float2 orthogonal<float2>(float2);
|
||||
|
||||
/**
|
||||
* Calculates a perpendicular vector to \a v.
|
||||
* \note Returned vector can be in any perpendicular direction.
|
||||
* \note Returned vector might not the same length as \a v.
|
||||
*/
|
||||
template<> float3 orthogonal<float3>(float3 v)
|
||||
{
|
||||
switch (dominant_axis(v)) {
|
||||
default:
|
||||
case 0:
|
||||
return float3(-v.y - v.z, v.x, v.x);
|
||||
case 1:
|
||||
return float3(v.y, -v.x - v.z, v.y);
|
||||
case 2:
|
||||
return float3(v.z, v.z, -v.x - v.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `vector` if `incident` and `reference` are pointing in the same direction.
|
||||
*/
|
||||
// float2 faceforward(float2 vector, float2 incident, float2 reference); /* Built-in GLSL. */
|
||||
@@ -0,0 +1,128 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Comparison
|
||||
* \{ */
|
||||
|
||||
float reduce_max(float2 a)
|
||||
{
|
||||
return max(a.x, a.y);
|
||||
}
|
||||
float reduce_max(float3 a)
|
||||
{
|
||||
return max(a.x, max(a.y, a.z));
|
||||
}
|
||||
float reduce_max(float4 a)
|
||||
{
|
||||
return max(max(a.x, a.y), max(a.z, a.w));
|
||||
}
|
||||
int reduce_max(int2 a)
|
||||
{
|
||||
return max(a.x, a.y);
|
||||
}
|
||||
int reduce_max(int3 a)
|
||||
{
|
||||
return max(a.x, max(a.y, a.z));
|
||||
}
|
||||
int reduce_max(int4 a)
|
||||
{
|
||||
return max(max(a.x, a.y), max(a.z, a.w));
|
||||
}
|
||||
|
||||
float reduce_min(float2 a)
|
||||
{
|
||||
return min(a.x, a.y);
|
||||
}
|
||||
float reduce_min(float3 a)
|
||||
{
|
||||
return min(a.x, min(a.y, a.z));
|
||||
}
|
||||
float reduce_min(float4 a)
|
||||
{
|
||||
return min(min(a.x, a.y), min(a.z, a.w));
|
||||
}
|
||||
int reduce_min(int2 a)
|
||||
{
|
||||
return min(a.x, a.y);
|
||||
}
|
||||
int reduce_min(int3 a)
|
||||
{
|
||||
return min(a.x, min(a.y, a.z));
|
||||
}
|
||||
int reduce_min(int4 a)
|
||||
{
|
||||
return min(min(a.x, a.y), min(a.z, a.w));
|
||||
}
|
||||
|
||||
float reduce_add(float2 a)
|
||||
{
|
||||
return a.x + a.y;
|
||||
}
|
||||
float reduce_add(float3 a)
|
||||
{
|
||||
return a.x + a.y + a.z;
|
||||
}
|
||||
float reduce_add(float4 a)
|
||||
{
|
||||
return a.x + a.y + a.z + a.w;
|
||||
}
|
||||
int reduce_add(int2 a)
|
||||
{
|
||||
return a.x + a.y;
|
||||
}
|
||||
int reduce_add(int3 a)
|
||||
{
|
||||
return a.x + a.y + a.z;
|
||||
}
|
||||
int reduce_add(int4 a)
|
||||
{
|
||||
return a.x + a.y + a.z + a.w;
|
||||
}
|
||||
|
||||
#if 0 /* Remove unused variants as they are slow down compilation. */
|
||||
float reduce_mul(float2 a)
|
||||
{
|
||||
return a.x * a.y;
|
||||
}
|
||||
float reduce_mul(float3 a)
|
||||
{
|
||||
return a.x * a.y * a.z;
|
||||
}
|
||||
float reduce_mul(float4 a)
|
||||
{
|
||||
return a.x * a.y * a.z * a.w;
|
||||
}
|
||||
int reduce_mul(int2 a)
|
||||
{
|
||||
return a.x * a.y;
|
||||
}
|
||||
int reduce_mul(int3 a)
|
||||
{
|
||||
return a.x * a.y * a.z;
|
||||
}
|
||||
int reduce_mul(int4 a)
|
||||
{
|
||||
return a.x * a.y * a.z * a.w;
|
||||
}
|
||||
#endif
|
||||
|
||||
float average(float2 a)
|
||||
{
|
||||
return reduce_add(a) * (1.0f / 2.0f);
|
||||
}
|
||||
float average(float3 a)
|
||||
{
|
||||
return reduce_add(a) * (1.0f / 3.0f);
|
||||
}
|
||||
float average(float4 a)
|
||||
{
|
||||
return reduce_add(a) * (1.0f / 4.0f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,246 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_math_safe_lib.glsl"
|
||||
|
||||
/**
|
||||
* Safe `a` modulo `b`.
|
||||
* If `b` equal 0 the result will be 0.
|
||||
*/
|
||||
template<typename VecT> VecT safe_mod(VecT a, VecT b)
|
||||
{
|
||||
return select(VecT(0), mod(a, b), notEqual(b, VecT(0)));
|
||||
}
|
||||
template float2 safe_mod<float2>(float2, float2);
|
||||
template float3 safe_mod<float3>(float3, float3);
|
||||
template float4 safe_mod<float4>(float4, float4);
|
||||
|
||||
/**
|
||||
* Safe `a` modulo `b`.
|
||||
* If `b` equal 0 the result will be 0.
|
||||
*/
|
||||
float2 safe_mod(float2 a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? mod(a, float2(b)) : float2(0);
|
||||
}
|
||||
float3 safe_mod(float3 a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? mod(a, float3(b)) : float3(0);
|
||||
}
|
||||
float4 safe_mod(float4 a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? mod(a, float4(b)) : float4(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float2 compatible_mod(float2 a, float b)
|
||||
{
|
||||
return float2(compatible_mod(a.x, b), compatible_mod(a.y, b));
|
||||
}
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float3 compatible_mod(float3 a, float b)
|
||||
{
|
||||
return float3(compatible_mod(a.x, b), compatible_mod(a.y, b), compatible_mod(a.z, b));
|
||||
}
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float4 compatible_mod(float4 a, float b)
|
||||
{
|
||||
return float4(compatible_mod(a.x, b),
|
||||
compatible_mod(a.y, b),
|
||||
compatible_mod(a.z, b),
|
||||
compatible_mod(a.w, b));
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float2 compatible_mod(float2 a, float2 b)
|
||||
{
|
||||
return float2(compatible_mod(a.x, b.x), compatible_mod(a.y, b.y));
|
||||
}
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float3 compatible_mod(float3 a, float3 b)
|
||||
{
|
||||
return float3(compatible_mod(a.x, b.x), compatible_mod(a.y, b.y), compatible_mod(a.z, b.z));
|
||||
}
|
||||
/**
|
||||
* A version of mod that behaves similar to C++ `std::modf`, and is safe such that it returns 0
|
||||
* when b is also 0.
|
||||
*/
|
||||
float4 compatible_mod(float4 a, float4 b)
|
||||
{
|
||||
return float4(compatible_mod(a.x, b.x),
|
||||
compatible_mod(a.y, b.y),
|
||||
compatible_mod(a.z, b.z),
|
||||
compatible_mod(a.w, b.w));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe divide `a` by `b`.
|
||||
* If `b` equal 0 the result will be 0.
|
||||
*/
|
||||
template<typename VecT> VecT safe_divide(VecT a, VecT b)
|
||||
{
|
||||
return select(VecT(0), a / b, notEqual(b, VecT(0)));
|
||||
}
|
||||
template float2 safe_divide<float2>(float2, float2);
|
||||
template float3 safe_divide<float3>(float3, float3);
|
||||
template float4 safe_divide<float4>(float4, float4);
|
||||
|
||||
/* NOTE: Cannot overload templates. */
|
||||
|
||||
/**
|
||||
* Safe divide `a` by `b`.
|
||||
* If `b` equal 0 the result will be 0.
|
||||
*/
|
||||
float2 safe_divide(float2 a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? (a / b) : float2(0);
|
||||
}
|
||||
float3 safe_divide(float3 a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? (a / b) : float3(0);
|
||||
}
|
||||
float4 safe_divide(float4 a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? (a / b) : float4(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return normalized version of the `vector` or a default normalized vector if `vector` is invalid.
|
||||
*/
|
||||
template<typename VecT> VecT safe_normalize_and_get_length(VecT vector, float &out_length)
|
||||
{
|
||||
float length_squared = dot(vector, vector);
|
||||
constexpr float threshold = 1e-35f;
|
||||
if (length_squared > threshold) {
|
||||
out_length = sqrt(length_squared);
|
||||
return vector / out_length;
|
||||
}
|
||||
/* Either the vector is small or one of its values contained `nan`. */
|
||||
out_length = 0.0f;
|
||||
VecT result = VecT(0.0f);
|
||||
result[0] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
template float2 safe_normalize_and_get_length<float2>(float2, float &);
|
||||
template float3 safe_normalize_and_get_length<float3>(float3, float &);
|
||||
template float4 safe_normalize_and_get_length<float4>(float4, float &);
|
||||
|
||||
/**
|
||||
* Return normalized version of the `vector` or a default normalized vector if `vector` is invalid.
|
||||
*/
|
||||
template<typename VecT> VecT safe_normalize(VecT vector)
|
||||
{
|
||||
float unused_length = 0.0f;
|
||||
return safe_normalize_and_get_length(vector, unused_length);
|
||||
}
|
||||
template float2 safe_normalize<float2>(float2);
|
||||
template float3 safe_normalize<float3>(float3);
|
||||
template float4 safe_normalize<float4>(float4);
|
||||
|
||||
/**
|
||||
* Return normalized version of the `vector` or `fallback` vector if `vector` is invalid.
|
||||
*/
|
||||
template<typename VecT> VecT normalize_fallback(VecT vector, VecT fallback)
|
||||
{
|
||||
float length_squared = dot(vector, vector);
|
||||
constexpr float threshold = 1e-35f;
|
||||
if (length_squared > threshold) {
|
||||
return vector * inversesqrt(length_squared);
|
||||
}
|
||||
/* Either the vector is small or one of its values contained `nan`. */
|
||||
return fallback;
|
||||
}
|
||||
template float2 normalize_fallback<float2>(float2, float2);
|
||||
template float3 normalize_fallback<float3>(float3, float3);
|
||||
template float4 normalize_fallback<float4>(float4, float4);
|
||||
|
||||
/**
|
||||
* Safe reciprocal function. Returns `1/a`.
|
||||
* If `a` equal 0 the result will be 0.
|
||||
*/
|
||||
template<typename VecT> VecT safe_rcp(VecT a)
|
||||
{
|
||||
return select(VecT(0.0f), (1.0f / a), notEqual(a, VecT(0.0f)));
|
||||
}
|
||||
template float2 safe_rcp<float2>(float2);
|
||||
template float3 safe_rcp<float3>(float3);
|
||||
template float4 safe_rcp<float4>(float4);
|
||||
|
||||
/**
|
||||
* A version of pow that returns a fallback value if the computation is undefined. From the spec:
|
||||
* The result is undefined if x < 0 or if x = 0 and y is less than or equal 0.
|
||||
*/
|
||||
float2 fallback_pow(float2 a, float b, float2 fallback)
|
||||
{
|
||||
return float2(fallback_pow(a.x, b, fallback.x), fallback_pow(a.y, b, fallback.y));
|
||||
}
|
||||
float3 fallback_pow(float3 a, float b, float3 fallback)
|
||||
{
|
||||
return float3(fallback_pow(a.x, b, fallback.x),
|
||||
fallback_pow(a.y, b, fallback.y),
|
||||
fallback_pow(a.z, b, fallback.z));
|
||||
}
|
||||
float4 fallback_pow(float4 a, float b, float4 fallback)
|
||||
{
|
||||
return float4(fallback_pow(a.x, b, fallback.x),
|
||||
fallback_pow(a.y, b, fallback.y),
|
||||
fallback_pow(a.z, b, fallback.z),
|
||||
fallback_pow(a.w, b, fallback.w));
|
||||
}
|
||||
|
||||
float2 compatible_pow(float2 a, float2 b)
|
||||
{
|
||||
return float2(compatible_pow(a.x, b.x), compatible_pow(a.y, b.y));
|
||||
}
|
||||
float3 compatible_pow(float3 a, float3 b)
|
||||
{
|
||||
return float3(compatible_pow(a.x, b.x), compatible_pow(a.y, b.y), compatible_pow(a.z, b.z));
|
||||
}
|
||||
float4 compatible_pow(float4 a, float4 b)
|
||||
{
|
||||
return float4(compatible_pow(a.x, b.x),
|
||||
compatible_pow(a.y, b.y),
|
||||
compatible_pow(a.z, b.z),
|
||||
compatible_pow(a.w, b.w));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given value a to fall within the range [b, c].
|
||||
*/
|
||||
float2 wrap(float2 a, float2 b, float2 c)
|
||||
{
|
||||
return float2(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y));
|
||||
}
|
||||
/**
|
||||
* Wrap the given value a to fall within the range [b, c].
|
||||
*/
|
||||
float3 wrap(float3 a, float3 b, float3 c)
|
||||
{
|
||||
return float3(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z));
|
||||
}
|
||||
/**
|
||||
* Wrap the given value a to fall within the range [b, c].
|
||||
*/
|
||||
float4 wrap(float4 a, float4 b, float4 c)
|
||||
{
|
||||
return float4(
|
||||
wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z), wrap(a.w, b.w, c.w));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "gpu_shader_index_range_lib.glsl"
|
||||
|
||||
/**
|
||||
* See `OffsetIndices` C++ definition for formal definition.
|
||||
*
|
||||
* OffsetIndices cannot be implemented on GPU because of the lack of operator overloading and
|
||||
* buffer reference in GLSL. So we simply interpret a given integer buffer as a `OffsetIndices`
|
||||
* buffer and load a specific item as a range.
|
||||
*/
|
||||
namespace offset_indices {
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
/* Equivalent of `IndexRange OffsetIndices<int>operator[]`.
|
||||
* Implementation for C++ compilation. */
|
||||
inline static IndexRange load_range_from_buffer(const int (&buf)[], int i)
|
||||
{
|
||||
return IndexRange::from_begin_end(buf[i], buf[i + 1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace offset_indices
|
||||
|
||||
/* Shader implementation because of missing buffer reference as argument in GLSL. */
|
||||
#define offset_indices_load_range_from_buffer(buf_, i_) \
|
||||
IndexRange::from_begin_end(buf_[i_], buf_[i_ + 1])
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infos/gpu_shader_print_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(gpu_print)
|
||||
|
||||
uint print_data(uint offset, uint data)
|
||||
{
|
||||
if (offset < GPU_SHADER_PRINTF_MAX_CAPACITY) {
|
||||
gpu_print_buf[offset] = data;
|
||||
}
|
||||
return offset + 1u;
|
||||
}
|
||||
|
||||
uint print_data(uint offset, string_t data)
|
||||
{
|
||||
return print_data(offset, as_uint(data));
|
||||
}
|
||||
|
||||
uint print_data(uint offset, int data)
|
||||
{
|
||||
return print_data(offset, uint(data));
|
||||
}
|
||||
|
||||
uint print_data(uint offset, float data)
|
||||
{
|
||||
return print_data(offset, floatBitsToUint(data));
|
||||
}
|
||||
|
||||
uint print_start(const uint data_len)
|
||||
{
|
||||
/* Add one to skip the length stored in the first element of the buffer. */
|
||||
return atomicAdd(gpu_print_buf[0], data_len) + 1u;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* General purpose 3D ray. */
|
||||
struct Ray {
|
||||
packed_float3 direction;
|
||||
float max_time;
|
||||
packed_float3 origin;
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/**
|
||||
* Ray offset to avoid self intersection.
|
||||
*
|
||||
* This can be used to compute a modified ray start position for rays leaving from a surface.
|
||||
* From:
|
||||
* "A Fast and Robust Method for Avoiding Self-Intersection"
|
||||
* Ray Tracing Gems, chapter 6.
|
||||
*/
|
||||
float3 offset_ray(float3 P, float3 Ng)
|
||||
{
|
||||
constexpr float origin = 1.0f / 32.0f;
|
||||
constexpr float float_scale = 1.0f / 65536.0f;
|
||||
constexpr float int_scale = 256.0f;
|
||||
|
||||
int3 of_i = int3(int_scale * Ng);
|
||||
of_i = int3((P.x < 0.0f) ? -of_i.x : of_i.x,
|
||||
(P.y < 0.0f) ? -of_i.y : of_i.y,
|
||||
(P.z < 0.0f) ? -of_i.z : of_i.z);
|
||||
float3 P_i = intBitsToFloat(floatBitsToInt(P) + of_i);
|
||||
|
||||
float3 uf = P + float_scale * Ng;
|
||||
return float3((abs(P.x) < origin) ? uf.x : P_i.x,
|
||||
(abs(P.y) < origin) ? uf.y : P_i.y,
|
||||
(abs(P.z) < origin) ? uf.z : P_i.z);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infos/gpu_shader_sequencer_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(gpu_shader_sequencer_strips)
|
||||
|
||||
/* Signed distance to rounded box, centered at origin.
|
||||
* Reference: https://iquilezles.org/articles/distfunctions2d/ */
|
||||
float sdf_rounded_box(float2 pos, float2 size, float radius)
|
||||
{
|
||||
float2 q = abs(pos) - size + radius;
|
||||
return min(max(q.x, q.y), 0.0f) + length(max(q, 0.0f)) - radius;
|
||||
}
|
||||
|
||||
void strip_box(float left,
|
||||
float right,
|
||||
float bottom,
|
||||
float top,
|
||||
float2 pos,
|
||||
float2 &r_pos1,
|
||||
float2 &r_pos2,
|
||||
float2 &r_size,
|
||||
float2 &r_center,
|
||||
float2 &r_pos,
|
||||
float &r_radius)
|
||||
{
|
||||
/* Snap to pixel grid coordinates, so that outline/border is non-fractional
|
||||
* pixel sizes. */
|
||||
r_pos1 = round(float2(left, bottom));
|
||||
r_pos2 = round(float2(right, top));
|
||||
/* Make sure strip is at least 1px wide. */
|
||||
r_pos2.x = max(r_pos2.x, r_pos1.x + 1.0f);
|
||||
r_size = (r_pos2 - r_pos1) * 0.5f;
|
||||
r_center = (r_pos1 + r_pos2) * 0.5f;
|
||||
r_pos = round(pos);
|
||||
|
||||
r_radius = context_data.round_radius;
|
||||
if (r_radius > r_size.x) {
|
||||
r_radius = 0.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/**
|
||||
* Software implementation of encoding and decoding of shared exponent texture as described by the
|
||||
* OpenGL extension EXT_texture_shared_exponent Appendix
|
||||
* https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt
|
||||
*
|
||||
* This allows to read and write the RGB9_E5 format in a R32UI texture without explicit support on
|
||||
* the hardware for this type. However, filtering is not supported in this case.
|
||||
*/
|
||||
|
||||
#define RGB9E5_EXPONENT_BITS 5
|
||||
#define RGB9E5_MANTISSA_BITS 9
|
||||
#define RGB9E5_EXP_BIAS 15
|
||||
#define RGB9E5_MAX_VALID_BIASED_EXP 31
|
||||
|
||||
#define MAX_RGB9E5_EXP (RGB9E5_MAX_VALID_BIASED_EXP - RGB9E5_EXP_BIAS)
|
||||
#define RGB9E5_MANTISSA_VALUES (1 << RGB9E5_MANTISSA_BITS)
|
||||
#define MAX_RGB9E5_MANTISSA (RGB9E5_MANTISSA_VALUES - 1)
|
||||
|
||||
int rgb9e5_floor_log2(float x)
|
||||
{
|
||||
/* Ok, rgb9e5_floor_log2 is not correct for the denorm and zero values, but we
|
||||
* are going to do a max of this value with the minimum rgb9e5 exponent
|
||||
* that will hide these problem cases. */
|
||||
int biased_exponent = floatBitsToInt(x) >> 23;
|
||||
return biased_exponent - 127;
|
||||
}
|
||||
|
||||
float rgb9e5_exponent_factor(int exponent)
|
||||
{
|
||||
/* This pow function could be replaced by a table. There is only 32 values. */
|
||||
return exp2(float(exponent - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS));
|
||||
}
|
||||
|
||||
struct rgb9e5_t {
|
||||
uint exp_shared;
|
||||
uint3 mantissa;
|
||||
};
|
||||
|
||||
rgb9e5_t rgb9e5_from_float3(float3 color)
|
||||
{
|
||||
constexpr float max_rgb9e5 = float(0xFF80u);
|
||||
color = clamp(color, 0.0f, max_rgb9e5);
|
||||
|
||||
float max_component = max(max(color.r, color.g), color.b);
|
||||
int log2_floored = rgb9e5_floor_log2(max_component);
|
||||
int exp_shared = max(-RGB9E5_EXP_BIAS - 1, log2_floored) + (1 + RGB9E5_EXP_BIAS);
|
||||
float denom = rgb9e5_exponent_factor(exp_shared);
|
||||
int maxm = int(max_component / denom + 0.5f);
|
||||
if (maxm == MAX_RGB9E5_MANTISSA + 1) {
|
||||
denom *= 2.0f;
|
||||
exp_shared += 1;
|
||||
}
|
||||
|
||||
rgb9e5_t result;
|
||||
result.exp_shared = uint(exp_shared);
|
||||
result.mantissa = uint3(color / denom + 0.5f);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint rgb9e5_encode(float3 color)
|
||||
{
|
||||
rgb9e5_t result = rgb9e5_from_float3(color);
|
||||
result.exp_shared <<= RGB9E5_MANTISSA_BITS * 3;
|
||||
result.mantissa <<= RGB9E5_MANTISSA_BITS * uint3(0, 1, 2);
|
||||
return result.mantissa.r | result.mantissa.g | result.mantissa.b | result.exp_shared;
|
||||
}
|
||||
|
||||
float3 rgb9e5_decode(uint data)
|
||||
{
|
||||
int exp_shared = int(data >> (RGB9E5_MANTISSA_BITS * 3));
|
||||
uint3 mantissa = (uint3(data) >> (RGB9E5_MANTISSA_BITS * uint3(0, 1, 2))) &
|
||||
uint(MAX_RGB9E5_MANTISSA);
|
||||
return float3(mantissa) * rgb9e5_exponent_factor(exp_shared);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_math_vector_compare_lib.glsl"
|
||||
|
||||
#include "GPU_shader_shared.hh"
|
||||
|
||||
/* clang-format off */
|
||||
#ifndef GPU_METAL
|
||||
bool is_integer(bool /*v*/) { return true; }
|
||||
#endif
|
||||
bool is_integer(uint /*v*/) { return true; }
|
||||
bool is_integer(int /*v*/) { return true; }
|
||||
bool is_integer(float /*v*/) { return false; }
|
||||
bool is_integer(int2 /*v*/) { return true; }
|
||||
bool is_integer(int3 /*v*/) { return true; }
|
||||
bool is_integer(int4 /*v*/) { return true; }
|
||||
bool is_integer(uint2 /*v*/) { return true; }
|
||||
bool is_integer(uint3 /*v*/) { return true; }
|
||||
bool is_integer(uint4 /*v*/) { return true; }
|
||||
bool is_integer(float2 /*v*/) { return false; }
|
||||
bool is_integer(float3 /*v*/) { return false; }
|
||||
bool is_integer(float4 /*v*/) { return false; }
|
||||
bool is_integer(float2x2 /*v*/) { return false; }
|
||||
bool is_integer(float2x3 /*v*/) { return false; }
|
||||
bool is_integer(float2x4 /*v*/) { return false; }
|
||||
bool is_integer(float3x2 /*v*/) { return false; }
|
||||
bool is_integer(float3x3 /*v*/) { return false; }
|
||||
bool is_integer(float3x4 /*v*/) { return false; }
|
||||
bool is_integer(float4x2 /*v*/) { return false; }
|
||||
bool is_integer(float4x3 /*v*/) { return false; }
|
||||
bool is_integer(float4x4 /*v*/) { return false; }
|
||||
|
||||
int mat_row_len(float2x2 /*v*/) { return 2; }
|
||||
int mat_row_len(float2x3 /*v*/) { return 3; }
|
||||
int mat_row_len(float2x4 /*v*/) { return 4; }
|
||||
int mat_row_len(float3x2 /*v*/) { return 2; }
|
||||
int mat_row_len(float3x3 /*v*/) { return 3; }
|
||||
int mat_row_len(float3x4 /*v*/) { return 4; }
|
||||
int mat_row_len(float4x2 /*v*/) { return 2; }
|
||||
int mat_row_len(float4x3 /*v*/) { return 3; }
|
||||
int mat_row_len(float4x4 /*v*/) { return 4; }
|
||||
|
||||
int mat_col_len(float2x2 /*v*/) { return 2; }
|
||||
int mat_col_len(float2x3 /*v*/) { return 2; }
|
||||
int mat_col_len(float2x4 /*v*/) { return 2; }
|
||||
int mat_col_len(float3x2 /*v*/) { return 3; }
|
||||
int mat_col_len(float3x3 /*v*/) { return 3; }
|
||||
int mat_col_len(float3x4 /*v*/) { return 3; }
|
||||
int mat_col_len(float4x2 /*v*/) { return 4; }
|
||||
int mat_col_len(float4x3 /*v*/) { return 4; }
|
||||
int mat_col_len(float4x4 /*v*/) { return 4; }
|
||||
int mat_col_len(int2 /*v*/) { return 2; }
|
||||
int mat_col_len(int3 /*v*/) { return 3; }
|
||||
int mat_col_len(int4 /*v*/) { return 4; }
|
||||
int mat_col_len(uint2 /*v*/) { return 2; }
|
||||
int mat_col_len(uint3 /*v*/) { return 3; }
|
||||
int mat_col_len(uint4 /*v*/) { return 4; }
|
||||
int mat_col_len(float2 /*v*/) { return 2; }
|
||||
int mat_col_len(float3 /*v*/) { return 3; }
|
||||
int mat_col_len(float4 /*v*/) { return 4; }
|
||||
|
||||
#ifndef GPU_METAL
|
||||
uint to_type(bool /*v*/) { return TEST_TYPE_BOOL; }
|
||||
#endif
|
||||
uint to_type(uint /*v*/) { return TEST_TYPE_UINT; }
|
||||
uint to_type(int /*v*/) { return TEST_TYPE_INT; }
|
||||
uint to_type(float /*v*/) { return TEST_TYPE_FLOAT; }
|
||||
uint to_type(int2 /*v*/) { return TEST_TYPE_IVEC2; }
|
||||
uint to_type(int3 /*v*/) { return TEST_TYPE_IVEC3; }
|
||||
uint to_type(int4 /*v*/) { return TEST_TYPE_IVEC4; }
|
||||
uint to_type(uint2 /*v*/) { return TEST_TYPE_UVEC2; }
|
||||
uint to_type(uint3 /*v*/) { return TEST_TYPE_UVEC3; }
|
||||
uint to_type(uint4 /*v*/) { return TEST_TYPE_UVEC4; }
|
||||
uint to_type(float2 /*v*/) { return TEST_TYPE_VEC2; }
|
||||
uint to_type(float3 /*v*/) { return TEST_TYPE_VEC3; }
|
||||
uint to_type(float4 /*v*/) { return TEST_TYPE_VEC4; }
|
||||
uint to_type(float2x2 /*v*/) { return TEST_TYPE_MAT2X2; }
|
||||
uint to_type(float2x3 /*v*/) { return TEST_TYPE_MAT2X3; }
|
||||
uint to_type(float2x4 /*v*/) { return TEST_TYPE_MAT2X4; }
|
||||
uint to_type(float3x2 /*v*/) { return TEST_TYPE_MAT3X2; }
|
||||
uint to_type(float3x3 /*v*/) { return TEST_TYPE_MAT3X3; }
|
||||
uint to_type(float3x4 /*v*/) { return TEST_TYPE_MAT3X4; }
|
||||
uint to_type(float4x2 /*v*/) { return TEST_TYPE_MAT4X2; }
|
||||
uint to_type(float4x3 /*v*/) { return TEST_TYPE_MAT4X3; }
|
||||
uint to_type(float4x4 /*v*/) { return TEST_TYPE_MAT4X4; }
|
||||
/* clang-format on */
|
||||
|
||||
#define WRITE_MATRIX(v) \
|
||||
TestOutputRawData raw; \
|
||||
for (int c = 0; c < mat_col_len(v); c++) { \
|
||||
for (int r = 0; r < mat_row_len(v); r++) { \
|
||||
raw.data[c * mat_row_len(v) + r] = floatBitsToUint(v[c][r]); \
|
||||
} \
|
||||
} \
|
||||
return raw;
|
||||
|
||||
#define WRITE_FLOAT_VECTOR(v) \
|
||||
TestOutputRawData raw; \
|
||||
for (int c = 0; c < mat_col_len(v); c++) { \
|
||||
raw.data[c] = floatBitsToUint(v[c]); \
|
||||
} \
|
||||
return raw;
|
||||
|
||||
#define WRITE_INT_VECTOR(v) \
|
||||
TestOutputRawData raw; \
|
||||
for (int c = 0; c < mat_col_len(v); c++) { \
|
||||
raw.data[c] = uint(v[c]); \
|
||||
} \
|
||||
return raw;
|
||||
|
||||
#define WRITE_FLOAT_SCALAR(v) \
|
||||
TestOutputRawData raw; \
|
||||
raw.data[0] = floatBitsToUint(v); \
|
||||
return raw;
|
||||
|
||||
#define WRITE_INT_SCALAR(v) \
|
||||
TestOutputRawData raw; \
|
||||
raw.data[0] = uint(v); \
|
||||
return raw;
|
||||
|
||||
/* clang-format off */
|
||||
#ifndef GPU_METAL
|
||||
TestOutputRawData as_raw_data(bool v) { WRITE_INT_SCALAR(v); }
|
||||
#endif
|
||||
TestOutputRawData as_raw_data(uint v) { WRITE_INT_SCALAR(v); }
|
||||
TestOutputRawData as_raw_data(int v) { WRITE_INT_SCALAR(v); }
|
||||
TestOutputRawData as_raw_data(float v) { WRITE_FLOAT_SCALAR(v); }
|
||||
TestOutputRawData as_raw_data(int2 v) { WRITE_INT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(int3 v) { WRITE_INT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(int4 v) { WRITE_INT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(uint2 v) { WRITE_INT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(uint3 v) { WRITE_INT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(uint4 v) { WRITE_INT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(float2 v) { WRITE_FLOAT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(float3 v) { WRITE_FLOAT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(float4 v) { WRITE_FLOAT_VECTOR(v); }
|
||||
TestOutputRawData as_raw_data(float2x2 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float2x3 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float2x4 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float3x2 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float3x3 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float3x4 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float4x2 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float4x3 v) { WRITE_MATRIX(v); }
|
||||
TestOutputRawData as_raw_data(float4x4 v) { WRITE_MATRIX(v); }
|
||||
/* clang-format on */
|
||||
|
||||
#ifdef GPU_METAL
|
||||
/* Vector comparison in MSL return a `bvec`. Collapse it like in GLSL. */
|
||||
# define COLLAPSE_BOOL(OP) bool(all(OP))
|
||||
#else
|
||||
# undef COLLAPSE_BOOL /* Silence warning caused by define grepping in info files. */
|
||||
# define COLLAPSE_BOOL(OP) (OP)
|
||||
#endif
|
||||
|
||||
#define EXPECT_OP(OP, val1, val2) \
|
||||
test_output(as_raw_data(val1), as_raw_data(val2), COLLAPSE_BOOL(OP), to_type(val1))
|
||||
|
||||
#define EXPECT_EQ(result, expect) EXPECT_OP((result) == (expect), result, expect)
|
||||
#define EXPECT_NE(result, expect) EXPECT_OP((result) != (expect), result, expect)
|
||||
#define EXPECT_LE(result, expect) EXPECT_OP((result) <= (expect), result, expect)
|
||||
#define EXPECT_LT(result, expect) EXPECT_OP((result) < (expect), result, expect)
|
||||
#define EXPECT_GE(result, expect) EXPECT_OP((result) >= (expect), result, expect)
|
||||
#define EXPECT_GT(result, expect) EXPECT_OP((result) > (expect), result, expect)
|
||||
|
||||
#define EXPECT_TRUE(result) EXPECT_OP(result, result, true)
|
||||
#define EXPECT_FALSE(result) EXPECT_OP(!result, result, false)
|
||||
|
||||
#define EXPECT_NEAR(result, expect, threshold) \
|
||||
EXPECT_OP(is_equal(result, expect, threshold), result, expect)
|
||||
|
||||
struct ShaderTestOutput {
|
||||
[[storage(0, write)]] TestOutput (&out_test)[];
|
||||
};
|
||||
|
||||
#define TEST(a, b) if (true)
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
bool tiled_image_lookup(float3 &co, sampler2DArray ima, sampler1DArray map)
|
||||
{
|
||||
float2 tile_pos = floor(co.xy);
|
||||
|
||||
if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) {
|
||||
return false;
|
||||
}
|
||||
float tile = 10 * tile_pos.y + tile_pos.x;
|
||||
if (tile >= textureSize(map, 0).x) {
|
||||
return false;
|
||||
}
|
||||
/* Fetch tile information. */
|
||||
float tile_layer = texelFetch(map, int2(tile, 0), 0).x;
|
||||
if (tile_layer < 0) {
|
||||
return false;
|
||||
}
|
||||
float4 tile_info = texelFetch(map, int2(tile, 1), 0);
|
||||
|
||||
co = float3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#ifndef FLT_MAX
|
||||
# define FLT_MAX uintBitsToFloat(0x7F7FFFFFu)
|
||||
# define FLT_MIN uintBitsToFloat(0x00800000u)
|
||||
# define FLT_EPSILON 1.192092896e-07F
|
||||
#endif
|
||||
#ifndef SHRT_MAX
|
||||
# define SHRT_MAX 0x00007FFF
|
||||
# define INT_MAX 0x7FFFFFFF
|
||||
# define USHRT_MAX 0x0000FFFFu
|
||||
# define UINT_MAX 0xFFFFFFFFu
|
||||
#endif
|
||||
#define NAN_FLT uintBitsToFloat(0x7FC00000u)
|
||||
#define FLT_11_MAX uintBitsToFloat(0x477E0000)
|
||||
#define FLT_10_MAX uintBitsToFloat(0x477C0000)
|
||||
#define FLT_11_11_10_MAX float3(FLT_11_MAX, FLT_11_MAX, FLT_10_MAX)
|
||||
|
||||
#define UNPACK2(a) (a)[0], (a)[1]
|
||||
#define UNPACK3(a) (a)[0], (a)[1], (a)[2]
|
||||
#define UNPACK4(a) (a)[0], (a)[1], (a)[2], (a)[3]
|
||||
|
||||
/**
|
||||
* Clamp input into [0..1] range.
|
||||
*/
|
||||
#define saturate(a) clamp(a, 0.0f, 1.0f)
|
||||
|
||||
#define isfinite(a) (!isinf(a) && !isnan(a))
|
||||
|
||||
/* clang-format off */
|
||||
#define in_range_inclusive(val, min_v, max_v) (all(greaterThanEqual(val, min_v)) && all(lessThanEqual(val, max_v)))
|
||||
#define in_range_exclusive(val, min_v, max_v) (all(greaterThan(val, min_v)) && all(lessThan(val, max_v)))
|
||||
#define in_texture_range(texel, tex) (all(greaterThanEqual(texel, int2(0))) && all(lessThan(texel, textureSize(tex, 0).xy)))
|
||||
#define in_image_range(texel, tex) (all(greaterThanEqual(texel, int2(0))) && all(lessThan(texel, imageSize(tex).xy)))
|
||||
|
||||
#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(weights[0] + weights[1] + weights[2] + weights[3]))
|
||||
#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(weights[0] + weights[1] + weights[2] + weights[3]))
|
||||
/* clang-format on */
|
||||
|
||||
bool flag_test(uint flag, uint val)
|
||||
{
|
||||
return (flag & val) != 0u;
|
||||
}
|
||||
bool flag_test(int flag, uint val)
|
||||
{
|
||||
return flag_test(uint(flag), val);
|
||||
}
|
||||
bool flag_test(int flag, int val)
|
||||
{
|
||||
return (flag & val) != 0;
|
||||
}
|
||||
|
||||
void set_flag_from_test(uint &value, bool test, uint flag)
|
||||
{
|
||||
if (test) {
|
||||
value |= flag;
|
||||
}
|
||||
else {
|
||||
value &= ~flag;
|
||||
}
|
||||
}
|
||||
void set_flag_from_test(int &value, bool test, int flag)
|
||||
{
|
||||
if (test) {
|
||||
value |= flag;
|
||||
}
|
||||
else {
|
||||
value &= ~flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep define to match C++ implementation. */
|
||||
#define SET_FLAG_FROM_TEST(value, test, flag) set_flag_from_test(value, test, flag)
|
||||
|
||||
/**
|
||||
* Return true if the bit inside bitmask at bit_index is set high.
|
||||
* Assume the lower bits are inside first component of bitmask,
|
||||
*/
|
||||
bool bitmask64_test(uint2 bitmask, uint bit_index)
|
||||
{
|
||||
uint bitmask32 = (bit_index >= 32u) ? bitmask.y : bitmask.x;
|
||||
return flag_test(bitmask32, 1u << (bit_index & 0x1Fu));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack two 16-bit uint into one 32-bit uint.
|
||||
*/
|
||||
uint packUvec2x16(uint2 a)
|
||||
{
|
||||
a = (a & 0xFFFFu) << uint2(0u, 16u);
|
||||
return a.x | a.y;
|
||||
}
|
||||
uint2 unpackUvec2x16(uint a)
|
||||
{
|
||||
return (uint2(a) >> uint2(0u, 16u)) & uint2(0xFFFFu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack four 8-bit uint into one 32-bit uint.
|
||||
*/
|
||||
uint packUvec4x8(uint4 a)
|
||||
{
|
||||
a = (a & 0xFFu) << uint4(0u, 8u, 16u, 24u);
|
||||
return a.x | a.y | a.z | a.w;
|
||||
}
|
||||
uint4 unpackUvec4x8(uint a)
|
||||
{
|
||||
return (uint4(a) >> uint4(0u, 8u, 16u, 24u)) & uint4(0xFFu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from float representation to ordered int allowing min/max atomic operation.
|
||||
* Based on: https://stackoverflow.com/a/31010352
|
||||
*/
|
||||
int floatBitsToOrderedInt(float value)
|
||||
{
|
||||
/* Floats can be sorted using their bits interpreted as integers for positive values.
|
||||
* Negative values do not follow int's two's complement ordering which is reversed.
|
||||
* So we have to XOR all bits except the sign bits in order to reverse the ordering.
|
||||
* Note that this is highly hardware dependent, but there seems to be no case of GPU where the
|
||||
* ints ares not two's complement. */
|
||||
int int_value = floatBitsToInt(value);
|
||||
return (int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value;
|
||||
}
|
||||
float orderedIntBitsToFloat(int int_value)
|
||||
{
|
||||
return intBitsToFloat((int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_area_borders_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_area_borders)
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Should be 1.0f but minimize the AA on the edges. */
|
||||
float dist = (length(uv) - (0.98f - width)) * scale;
|
||||
|
||||
fragColor = color;
|
||||
fragColor.a *= smoothstep(-0.09f, 1.09f, dist);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_area_borders_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_area_borders)
|
||||
|
||||
void main()
|
||||
{
|
||||
int corner_id = (gl_VertexID / cornerLen) % 4;
|
||||
bool inner = all(lessThan(abs(pos), float2(1.0f)));
|
||||
|
||||
/* Scale the inner part of the border.
|
||||
* Add a sub pixel offset to the outer part to make sure we don't miss a pixel row/column. */
|
||||
float2 final_pos = pos * ((inner) ? (1.0f - width) : 1.05f);
|
||||
|
||||
uv = final_pos;
|
||||
/* Rescale to the corner size and position the corner. */
|
||||
if (corner_id == 0) {
|
||||
/* top right */
|
||||
final_pos = (final_pos - float2(1.0f, 1.0f)) * scale + rect.yw;
|
||||
}
|
||||
else if (corner_id == 1) {
|
||||
/* top left */
|
||||
final_pos = (final_pos - float2(-1.0f, 1.0f)) * scale + rect.xw;
|
||||
}
|
||||
else if (corner_id == 2) {
|
||||
/* bottom left */
|
||||
final_pos = (final_pos - float2(-1.0f, -1.0f)) * scale + rect.xz;
|
||||
}
|
||||
else {
|
||||
/* bottom right */
|
||||
final_pos = (final_pos - float2(1.0f, -1.0f)) * scale + rect.yz;
|
||||
}
|
||||
|
||||
gl_Position = (ModelViewProjectionMatrix * float4(final_pos, 0.0f, 1.0f));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Simple shader that just draw one icon at the specified location
|
||||
* does not need any vertex input (producing less call to immBegin/End)
|
||||
*/
|
||||
|
||||
#include "infos/gpu_shader_2D_image_rect_color_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_image_rect_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 uv;
|
||||
float2 co;
|
||||
|
||||
if (gl_VertexID == 0) {
|
||||
co = rect_geom.xw;
|
||||
uv = rect_icon.xw;
|
||||
}
|
||||
else if (gl_VertexID == 1) {
|
||||
co = rect_geom.xy;
|
||||
uv = rect_icon.xy;
|
||||
}
|
||||
else if (gl_VertexID == 2) {
|
||||
co = rect_geom.zw;
|
||||
uv = rect_icon.zw;
|
||||
}
|
||||
else {
|
||||
co = rect_geom.zy;
|
||||
uv = rect_icon.zy;
|
||||
}
|
||||
|
||||
gl_Position = ModelViewProjectionMatrix * float4(co, 0.0f, 1.0f);
|
||||
texCoord_interp = uv;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_image_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_image_common)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos.xy, 0.0f, 1.0f);
|
||||
texCoord_interp = texCoord;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/*
|
||||
* Fragment Shader for dashed lines, with uniform multi-color(s),
|
||||
* or any single-color, and any thickness.
|
||||
*
|
||||
* Dashed is performed in screen space.
|
||||
*/
|
||||
|
||||
#include "infos/gpu_shader_line_dashed_uniform_color_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
float distance_along_line = distance(stipple_pos, stipple_start);
|
||||
/* Solid line case, simple. */
|
||||
if (udash_factor >= 1.0f) {
|
||||
fragColor = color;
|
||||
}
|
||||
/* Actually dashed line... */
|
||||
else {
|
||||
float normalized_distance = fract(distance_along_line / dash_width);
|
||||
if (normalized_distance <= udash_factor) {
|
||||
fragColor = color;
|
||||
}
|
||||
else if (colors_len > 0) {
|
||||
fragColor = color2;
|
||||
}
|
||||
else {
|
||||
gpu_discard_fragment();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_node_socket_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
|
||||
|
||||
#include "gpu_shader_math_constants_lib.glsl"
|
||||
#include "gpu_shader_math_matrix_construct_lib.glsl"
|
||||
|
||||
/* Values in `eNodeSocketDisplayShape` in DNA_node_types.h. Keep in sync. */
|
||||
#define SOCK_DISPLAY_SHAPE_CIRCLE 0
|
||||
#define SOCK_DISPLAY_SHAPE_SQUARE 1
|
||||
#define SOCK_DISPLAY_SHAPE_DIAMOND 2
|
||||
#define SOCK_DISPLAY_SHAPE_CIRCLE_DOT 3
|
||||
#define SOCK_DISPLAY_SHAPE_SQUARE_DOT 4
|
||||
#define SOCK_DISPLAY_SHAPE_DIAMOND_DOT 5
|
||||
#define SOCK_DISPLAY_SHAPE_LINE 6
|
||||
#define SOCK_DISPLAY_SHAPE_VOLUME_GRID 7
|
||||
#define SOCK_DISPLAY_SHAPE_LIST 8
|
||||
|
||||
/* Calculates a squared distance field of a square. */
|
||||
float square_sdf(float2 absCo, float2 half_size)
|
||||
{
|
||||
float2 extruded_co = absCo - half_size;
|
||||
float2 clamped_extruded_co = float2(max(0.0f, extruded_co.x), max(0.0f, extruded_co.y));
|
||||
|
||||
float exterior_distance_squared = dot(clamped_extruded_co, clamped_extruded_co);
|
||||
|
||||
float interior_distance = min(max(extruded_co.x, extruded_co.y), 0.0f);
|
||||
float interior_distance_squared = interior_distance * interior_distance;
|
||||
|
||||
return exterior_distance_squared - interior_distance_squared;
|
||||
}
|
||||
|
||||
float2 rotate_45(float2 co)
|
||||
{
|
||||
return from_rotation(AngleRadian{M_PI * 0.25f}) * co;
|
||||
}
|
||||
|
||||
/* Calculates an upper and lower limit for an anti-aliased cutoff of the squared distance. */
|
||||
float2 calculate_thresholds(float threshold)
|
||||
{
|
||||
/* Use the absolute on one of the factors to preserve the sign. */
|
||||
float inner_threshold = (threshold - 0.5f * AAsize) * abs(threshold - 0.5f * AAsize);
|
||||
float outer_threshold = (threshold + 0.5f * AAsize) * abs(threshold + 0.5f * AAsize);
|
||||
return float2(inner_threshold, outer_threshold);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 absUV = abs(uv);
|
||||
float2 co = float2(max(absUV.x - extrusion.x, 0.0f), max(absUV.y - extrusion.y, 0.0f));
|
||||
|
||||
float distance_squared = 0.0f;
|
||||
float alpha_threshold = 0.0f;
|
||||
float dot_threshold = -1.0f;
|
||||
|
||||
constexpr float circle_radius = 0.5f;
|
||||
const float square_radius = 0.5f / sqrt(2.0f / M_PI) * M_SQRT1_2;
|
||||
const float diamond_radius = 0.5f / sqrt(2.0f / M_PI) * M_SQRT1_2;
|
||||
constexpr float corner_rounding = 0.0f;
|
||||
|
||||
switch (finalShape) {
|
||||
default:
|
||||
case SOCK_DISPLAY_SHAPE_CIRCLE: {
|
||||
distance_squared = dot(co, co);
|
||||
alpha_threshold = circle_radius;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_CIRCLE_DOT: {
|
||||
distance_squared = dot(co, co);
|
||||
alpha_threshold = circle_radius;
|
||||
dot_threshold = finalDotRadius;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_SQUARE: {
|
||||
distance_squared = square_sdf(co, float2(square_radius - corner_rounding));
|
||||
alpha_threshold = corner_rounding;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_SQUARE_DOT: {
|
||||
distance_squared = square_sdf(co, float2(square_radius - corner_rounding));
|
||||
alpha_threshold = corner_rounding;
|
||||
dot_threshold = finalDotRadius;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_DIAMOND: {
|
||||
distance_squared = square_sdf(abs(rotate_45(co)), float2(diamond_radius - corner_rounding));
|
||||
alpha_threshold = corner_rounding;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: {
|
||||
distance_squared = square_sdf(abs(rotate_45(co)), float2(diamond_radius - corner_rounding));
|
||||
alpha_threshold = corner_rounding;
|
||||
dot_threshold = finalDotRadius;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_LINE: {
|
||||
distance_squared = square_sdf(co, float2(square_radius * 0.75, square_radius * 1.4));
|
||||
alpha_threshold = corner_rounding;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_VOLUME_GRID: {
|
||||
constexpr float rect_side_length = 0.25f;
|
||||
const float2 oversize = float2(0.0f, square_radius * 1.4) / 2.5f;
|
||||
const float2 rect_corner = max(float2(rect_side_length), extrusion / 2.0f + oversize) +
|
||||
finalOutlineThickness / 4.0f;
|
||||
const float2 mirrored_uv = abs(abs(uv) - rect_corner);
|
||||
distance_squared = square_sdf(mirrored_uv, rect_corner + finalOutlineThickness / 2.0f);
|
||||
alpha_threshold = corner_rounding;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_LIST: {
|
||||
constexpr float2 rect_side_length = float2(0.5f, 0.25f);
|
||||
const float2 oversize = float2(0.0f, square_radius * 1.4) / 2.5f;
|
||||
const float2 rect_corner = max(rect_side_length, extrusion / 2.0f + oversize) +
|
||||
finalOutlineThickness / 4.0f;
|
||||
const float2 mirrored_uv = float2(
|
||||
abs(uv.x), abs(abs(abs(uv.y) - rect_corner.y / 1.5f) - rect_corner.y / 1.5f));
|
||||
distance_squared = square_sdf(
|
||||
mirrored_uv, (rect_corner + finalOutlineThickness / 2.0f) / float2(1.0f, 1.5f));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float2 alpha_thresholds = calculate_thresholds(alpha_threshold);
|
||||
float2 outline_thresholds = calculate_thresholds(alpha_threshold - finalOutlineThickness);
|
||||
float2 dot_thresholds = calculate_thresholds(dot_threshold);
|
||||
|
||||
float alpha_mask = smoothstep(alpha_thresholds[1], alpha_thresholds[0], distance_squared);
|
||||
float dot_mask = smoothstep(dot_thresholds[1], dot_thresholds[0], dot(co, co));
|
||||
float outline_mask = smoothstep(outline_thresholds[0], outline_thresholds[1], distance_squared) +
|
||||
dot_mask;
|
||||
|
||||
fragColor = mix(finalColor, finalOutlineColor, outline_mask);
|
||||
fragColor.a *= alpha_mask;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_node_socket_infos.hh"
|
||||
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
|
||||
|
||||
#define rect parameters[widgetID * MAX_SOCKET_PARAMETERS + 0]
|
||||
#define colorInner parameters[widgetID * MAX_SOCKET_PARAMETERS + 1]
|
||||
#define colorOutline parameters[widgetID * MAX_SOCKET_PARAMETERS + 2]
|
||||
#define outlineThickness parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].x
|
||||
#define outlineOffset parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].y
|
||||
#define shape parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].z
|
||||
#define aspect parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].w
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Scale the original rectangle to accommodate the diagonal of the diamond shape. */
|
||||
float2 originalRectSize = rect.yw - rect.xz;
|
||||
float offset = 0.125f * min(originalRectSize.x, originalRectSize.y) +
|
||||
outlineOffset * outlineThickness;
|
||||
float2 ofs = float2(offset, -offset);
|
||||
float2 pos;
|
||||
switch (gl_VertexID) {
|
||||
default:
|
||||
case 0: {
|
||||
pos = rect.xz + ofs.yy;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
pos = rect.xw + ofs.yx;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
pos = rect.yz + ofs.xy;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
pos = rect.yw + ofs.xx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
|
||||
|
||||
float2 rectSize = rect.yw - rect.xz + 2.0f * float2(outlineOffset, outlineOffset);
|
||||
float minSize = min(rectSize.x, rectSize.y);
|
||||
|
||||
float2 centeredCoordinates = pos - ((rect.xz + rect.yw) / 2.0f);
|
||||
uv = centeredCoordinates / minSize;
|
||||
|
||||
/* Calculate the necessary "extrusion" of the coordinates to draw the middle part of
|
||||
* multi sockets. */
|
||||
float ratio = rectSize.x / rectSize.y;
|
||||
extrusion = (ratio > 1.0f) ? float2((ratio - 1.0f) / 2.0f, 0.0f) :
|
||||
float2(0.0f, ((1.0f / ratio) - 1.0f) / 2.0f);
|
||||
|
||||
/* Shape parameters. */
|
||||
finalShape = int(shape);
|
||||
finalOutlineThickness = outlineThickness / minSize;
|
||||
finalDotRadius = outlineThickness / minSize;
|
||||
AAsize = 1.0f * aspect / minSize;
|
||||
|
||||
/* Pass through parameters. */
|
||||
finalColor = colorInner;
|
||||
finalOutlineColor = colorOutline;
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* 2D Cubic Bezier thick line drawing
|
||||
*/
|
||||
|
||||
/**
|
||||
* `uv.x` is position along the curve, defining the tangent space.
|
||||
* `uv.y` is "signed" distance (compressed to [0..1] range) from the pos in expand direction
|
||||
* `pos` is the verts position in the curve tangent space
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "GPU_shader_shared.hh"
|
||||
|
||||
#include "gpu_shader_attribute_load_lib.glsl"
|
||||
#include "gpu_shader_math_vector_compare_lib.glsl"
|
||||
|
||||
namespace builtin::nodelink {
|
||||
|
||||
struct NodeLinkVertIn {
|
||||
[[attribute(0)]] float2 uv;
|
||||
[[attribute(1)]] float2 pos;
|
||||
[[attribute(2)]] float2 expand;
|
||||
};
|
||||
|
||||
struct NodeLinkVertOut {
|
||||
[[smooth]] float4 final_color;
|
||||
[[smooth]] float2 line_uv;
|
||||
[[flat]] float line_length;
|
||||
[[flat]] float line_thickness;
|
||||
[[flat]] float dash_length;
|
||||
[[flat]] float dash_factor;
|
||||
[[flat]] float dash_alpha;
|
||||
[[flat]] float aspect;
|
||||
[[flat]] int has_back_link;
|
||||
[[flat]] int is_main_line;
|
||||
};
|
||||
|
||||
struct NodeLinkFragOut {
|
||||
[[frag_color(0)]] float4 color;
|
||||
};
|
||||
|
||||
struct NodeLinkSRT {
|
||||
[[push_constant]] float4x4 ModelViewProjectionMatrix;
|
||||
[[storage(0, read)]] NodeLinkData (&link_data_buf)[];
|
||||
[[uniform(0)]] NodeLinkUniformData &link_uniforms;
|
||||
};
|
||||
|
||||
[[vertex]] void vert([[vertex_id]] const int gl_VertexID,
|
||||
[[instance_id]] const int gl_InstanceID,
|
||||
[[resource_table]] const NodeLinkSRT &srt,
|
||||
[[in]] const NodeLinkVertIn &v_in,
|
||||
[[position]] float4 &gl_Position,
|
||||
[[out]] NodeLinkVertOut &interp)
|
||||
{
|
||||
constexpr float start_gradient_threshold = 0.35f;
|
||||
constexpr float end_gradient_threshold = 0.65f;
|
||||
|
||||
const NodeLinkData link = srt.link_data_buf[gl_InstanceID];
|
||||
|
||||
const float2 P0 = link.bezier_P0;
|
||||
const float2 P1 = link.bezier_P1;
|
||||
const float2 P2 = link.bezier_P2;
|
||||
const float2 P3 = link.bezier_P3;
|
||||
|
||||
const uint3 color_ids = gpu_attr_decode_uchar4_to_uint4(link.color_ids).xyz;
|
||||
|
||||
const float4 color_start = (color_ids[0] < 3u) ? link.start_color :
|
||||
srt.link_uniforms.colors[color_ids[0]];
|
||||
const float4 color_end = (color_ids[1] < 3u) ? link.end_color :
|
||||
srt.link_uniforms.colors[color_ids[1]];
|
||||
const float4 color_shadow = srt.link_uniforms.colors[color_ids[2]];
|
||||
float line_thickness = link.thickness;
|
||||
|
||||
/* Each instance contains both the outline and the "main" line on top. */
|
||||
constexpr int mid_vertex = 65;
|
||||
bool is_outline_pass = gl_VertexID < mid_vertex;
|
||||
|
||||
interp.line_thickness = line_thickness;
|
||||
interp.is_main_line = (v_in.expand.y == 1.0f && !is_outline_pass) ? 1 : 0;
|
||||
interp.has_back_link = int(link.has_back_link);
|
||||
interp.aspect = srt.link_uniforms.aspect;
|
||||
/* Parameters for the dashed line. */
|
||||
interp.dash_length = link.dash_length;
|
||||
interp.dash_factor = link.dash_factor;
|
||||
interp.dash_alpha = link.dash_alpha;
|
||||
/* Approximate line length, no need for real bezier length calculation. */
|
||||
interp.line_length = distance(P0, P3);
|
||||
/* TODO: Incorrect U, this leads to non-uniform dash distribution. */
|
||||
interp.line_uv = v_in.uv;
|
||||
|
||||
if ((v_in.expand.y == 1.0f) && link.has_back_link) {
|
||||
/* Increase width because two links are drawn. */
|
||||
line_thickness *= 1.7f;
|
||||
}
|
||||
|
||||
if (is_outline_pass) {
|
||||
/* Outline pass. */
|
||||
interp.final_color = color_shadow;
|
||||
}
|
||||
else {
|
||||
/* Second pass. */
|
||||
if (v_in.uv.x < start_gradient_threshold) {
|
||||
interp.final_color = color_start;
|
||||
}
|
||||
else if (v_in.uv.x > end_gradient_threshold) {
|
||||
interp.final_color = color_end;
|
||||
}
|
||||
else {
|
||||
float mixFactor = (v_in.uv.x - start_gradient_threshold) /
|
||||
(end_gradient_threshold - start_gradient_threshold);
|
||||
interp.final_color = mix(color_start, color_end, mixFactor);
|
||||
}
|
||||
line_thickness *= 0.65f;
|
||||
if (link.do_muted) {
|
||||
interp.final_color[3] = 0.65f;
|
||||
}
|
||||
}
|
||||
interp.final_color.a *= link.dim_factor;
|
||||
|
||||
float t = v_in.uv.x;
|
||||
float t2 = t * t;
|
||||
float t2_3 = 3.0f * t2;
|
||||
float one_minus_t = 1.0f - t;
|
||||
float one_minus_t2 = one_minus_t * one_minus_t;
|
||||
float one_minus_t2_3 = 3.0f * one_minus_t2;
|
||||
|
||||
float2 point = (P0 * one_minus_t2 * one_minus_t + P1 * one_minus_t2_3 * t +
|
||||
P2 * t2_3 * one_minus_t + P3 * t2 * t);
|
||||
|
||||
float2 tangent = ((P1 - P0) * one_minus_t2_3 + (P2 - P1) * 6.0f * (t - t2) + (P3 - P2) * t2_3);
|
||||
|
||||
/* Tangent space at t. If the inner and outer control points overlap, the tangent is invalid.
|
||||
* Use the vector between the sockets instead. */
|
||||
tangent = is_zero(tangent) ? normalize(P3 - P0) : normalize(tangent);
|
||||
float2 normal = tangent.yx * float2(-1.0f, 1.0f);
|
||||
|
||||
/* Position vertex on the curve tangent space */
|
||||
point += (v_in.pos.x * tangent + v_in.pos.y * normal) * srt.link_uniforms.arrow_size;
|
||||
|
||||
gl_Position = srt.ModelViewProjectionMatrix * float4(point, 0.0f, 1.0f);
|
||||
|
||||
float2 exp_axis = v_in.expand.x * tangent + v_in.expand.y * normal;
|
||||
|
||||
/* rotate & scale the expand axis */
|
||||
exp_axis = srt.ModelViewProjectionMatrix[0].xy * exp_axis.xx +
|
||||
srt.ModelViewProjectionMatrix[1].xy * exp_axis.yy;
|
||||
|
||||
float expand_dist = line_thickness * (v_in.uv.y * 2.0f - 1.0f);
|
||||
|
||||
/* Expand into a line */
|
||||
gl_Position.xy += exp_axis * srt.link_uniforms.aspect * expand_dist;
|
||||
|
||||
/* If the link is not muted or is not a reroute arrow the points are squashed to the center of
|
||||
* the line. Magic numbers are defined in `drawnode.cc`. */
|
||||
if ((v_in.expand.x == 1.0f && !link.do_muted) ||
|
||||
(v_in.expand.y != 1.0f && (v_in.pos.x < 0.70f || v_in.pos.x > 0.71f) && !link.do_arrow))
|
||||
{
|
||||
gl_Position.xy *= 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#define ANTIALIAS 0.75f
|
||||
|
||||
float get_line_alpha(float2 line_uv, float line_thickness, float center, float relative_radius)
|
||||
{
|
||||
float radius = relative_radius * line_thickness;
|
||||
float sdf = abs(line_thickness * (line_uv.y - center));
|
||||
return smoothstep(radius, radius - ANTIALIAS, sdf);
|
||||
}
|
||||
|
||||
[[fragment]] void frag([[in]] const NodeLinkVertOut &interp, [[out]] NodeLinkFragOut &frag_out)
|
||||
{
|
||||
float dash_frag_alpha = 1.0f;
|
||||
if (interp.dash_factor < 1.0f) {
|
||||
float distance_along_line = interp.line_length * interp.line_uv.x;
|
||||
|
||||
/* Checking if `normalized_distance <= interp.dash_factor` is already enough for a basic
|
||||
* dash, however we want to handle a nice anti-alias. */
|
||||
|
||||
float dash_center = interp.dash_length * interp.dash_factor * 0.5f;
|
||||
float normalized_distance_triangle =
|
||||
1.0f -
|
||||
abs((fract((distance_along_line - dash_center) / interp.dash_length)) * 2.0f - 1.0f);
|
||||
float t = interp.aspect * ANTIALIAS / interp.dash_length;
|
||||
float slope = 1.0f / (2.0f * t);
|
||||
|
||||
float unclamped_alpha = 1.0f - slope * (normalized_distance_triangle - interp.dash_factor + t);
|
||||
float alpha = max(interp.dash_alpha, min(unclamped_alpha, 1.0f));
|
||||
|
||||
dash_frag_alpha = alpha;
|
||||
}
|
||||
|
||||
if (interp.is_main_line == 0) {
|
||||
frag_out.color = interp.final_color;
|
||||
frag_out.color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.5f, 0.5f) *
|
||||
dash_frag_alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
if (interp.has_back_link == 0) {
|
||||
frag_out.color = interp.final_color;
|
||||
frag_out.color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.5f, 0.5f) *
|
||||
dash_frag_alpha;
|
||||
}
|
||||
else {
|
||||
/* Draw two links right next to each other, the main link and the back-link. */
|
||||
float4 main_link_color = interp.final_color;
|
||||
main_link_color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.75f, 0.3f);
|
||||
|
||||
float4 back_link_color = float4(float3(0.8f), 1.0f);
|
||||
back_link_color.a *= get_line_alpha(interp.line_uv, interp.line_thickness, 0.2f, 0.25f);
|
||||
|
||||
/* Combine both links. */
|
||||
frag_out.color.rgb = main_link_color.rgb * main_link_color.a +
|
||||
back_link_color.rgb * back_link_color.a;
|
||||
frag_out.color.a = main_link_color.a * dash_frag_alpha + back_link_color.a;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace builtin::nodelink
|
||||
|
||||
PipelineGraphic gpu_shader_2D_nodelink(builtin::nodelink::vert, builtin::nodelink::frag);
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_aa)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
|
||||
gl_PointSize = size;
|
||||
|
||||
/* calculate concentric radii in pixels */
|
||||
float radius = 0.5f * size;
|
||||
|
||||
/* start at the outside and progress toward the center */
|
||||
radii[0] = radius;
|
||||
radii[1] = radius - 1.0f;
|
||||
|
||||
/* convert to PointCoord units */
|
||||
radii /= size;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_outline_aa)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
|
||||
gl_PointSize = size;
|
||||
|
||||
/* calculate concentric radii in pixels */
|
||||
float radius = 0.5f * size;
|
||||
|
||||
/* start at the outside and progress toward the center */
|
||||
radii[0] = radius;
|
||||
radii[1] = radius - 1.0f;
|
||||
radii[2] = radius - outlineWidth;
|
||||
radii[3] = radius - outlineWidth - 1.0f;
|
||||
|
||||
/* convert to PointCoord units */
|
||||
radii /= size;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_point_varying_size_varying_color_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_point_varying_size_varying_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
|
||||
gl_PointSize = size;
|
||||
finalColor = color;
|
||||
}
|
||||
@@ -0,0 +1,761 @@
|
||||
/* SPDX-FileCopyrightText: 2021 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2026 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
namespace builtin::mipmaps {
|
||||
|
||||
/* Conversion functions. */
|
||||
template<typename DstType, typename SrcType>
|
||||
void convert(DstType & /*dst_value*/, const SrcType /*src_value*/)
|
||||
{
|
||||
}
|
||||
|
||||
template<> void convert<float4, float>(float4 &dst_value, const float src_value)
|
||||
{
|
||||
dst_value.x = src_value;
|
||||
dst_value.y = 0.0;
|
||||
dst_value.z = 0.0;
|
||||
dst_value.w = 0.0;
|
||||
}
|
||||
template<> void convert<float, float4>(float &dst_value, const float4 src_value)
|
||||
{
|
||||
dst_value = src_value.x;
|
||||
}
|
||||
template<> void convert<float4, float4>(float4 &dst_value, const float4 src_value)
|
||||
{
|
||||
dst_value = src_value;
|
||||
}
|
||||
|
||||
/* Color transfer functions */
|
||||
/* TODO: should be moved to a library */
|
||||
float srgb_to_linearrgb(float c)
|
||||
{
|
||||
if (c < 0.04045f) {
|
||||
return (c < 0.0f) ? 0.0f : c * (1.0f / 12.92f);
|
||||
}
|
||||
|
||||
return pow((c + 0.055f) * (1.0f / 1.055f), 2.4f);
|
||||
}
|
||||
|
||||
float linearrgb_to_srgb(float c)
|
||||
{
|
||||
if (c < 0.0031308f) {
|
||||
return (c < 0.0f) ? 0.0f : c * 12.92f;
|
||||
}
|
||||
|
||||
return 1.055f * pow(c, 1.0f / 2.4f) - 0.055f;
|
||||
}
|
||||
|
||||
/**
|
||||
* General-case shader for generating 1 or 2 levels of the mip pyramid.
|
||||
* When generating 1 level, each workgroup handles up to 128 samples of the
|
||||
* output mip level. When generating 2 levels, each workgroup handles
|
||||
* a 8x8 tile of the last (2nd) output mip level, generating up to
|
||||
* 17x17 samples of the intermediate (1st) output mip level along the way.
|
||||
*
|
||||
* Dispatch with y, z = 1
|
||||
*/
|
||||
#define LOCAL_SIZE_X 128
|
||||
#define TILE_SIZE 8
|
||||
#define MAX_SHARED_SAMPLES (TILE_SIZE + TILE_SIZE + 1)
|
||||
#define INPUT_LEVEL 0
|
||||
|
||||
/** Shared storage that can store intermediate results using without encoding. */
|
||||
template<typename T> struct Shared {
|
||||
/**
|
||||
* When generating 2 levels, the results the first level are cached here; this is the input tile
|
||||
* needed to generate the 8x8 tile of the second level.
|
||||
*/
|
||||
[[shared]] T intermediate_level[MAX_SHARED_SAMPLES][MAX_SHARED_SAMPLES];
|
||||
|
||||
void store_sample(int2 dst_coord, T color)
|
||||
{
|
||||
intermediate_level[dst_coord.y][dst_coord.x] = color;
|
||||
}
|
||||
|
||||
T load_sample(int2 src_coord)
|
||||
{
|
||||
return intermediate_level[src_coord.y][src_coord.x];
|
||||
}
|
||||
};
|
||||
|
||||
/** Shared storage that can store intermediate results encoded as uint. */
|
||||
struct SharedUnorm {
|
||||
/**
|
||||
* When generating 2 levels, the results the first level are cached here; this is the input tile
|
||||
* needed to generate the 8x8 tile of the second level.
|
||||
*/
|
||||
[[shared]] uint intermediate_level[MAX_SHARED_SAMPLES][MAX_SHARED_SAMPLES];
|
||||
|
||||
void store_sample(int2 dst_coord, float color)
|
||||
{
|
||||
uint encoded = uint(clamp(color, 0.0f, 1.0f) * UINT_MAX);
|
||||
intermediate_level[dst_coord.y][dst_coord.x] = encoded;
|
||||
}
|
||||
|
||||
float load_sample(int2 src_coord)
|
||||
{
|
||||
uint encoded = intermediate_level[src_coord.y][src_coord.x];
|
||||
return float(encoded) / UINT_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
/** Shared storage that can store intermediate results in an SRGB encoded uint. */
|
||||
struct SharedSRGB {
|
||||
/**
|
||||
* When generating 2 levels, the results the first level are cached here; this is the input tile
|
||||
* needed to generate the 8x8 tile of the second level.
|
||||
*/
|
||||
[[shared]] uint intermediate_level[MAX_SHARED_SAMPLES][MAX_SHARED_SAMPLES];
|
||||
|
||||
void store_sample(int2 dst_coord, float4 color)
|
||||
{
|
||||
float4 srgba;
|
||||
srgba.r = linearrgb_to_srgb(color.r);
|
||||
srgba.g = linearrgb_to_srgb(color.g);
|
||||
srgba.b = linearrgb_to_srgb(color.b);
|
||||
srgba.a = color.a;
|
||||
uint srgb_packed = packUnorm4x8(srgba);
|
||||
intermediate_level[dst_coord.y][dst_coord.x] = srgb_packed;
|
||||
}
|
||||
|
||||
float4 load_sample(int2 src_coord)
|
||||
{
|
||||
uint srgb_packed = intermediate_level[src_coord.y][src_coord.x];
|
||||
float4 srgba = unpackUnorm4x8(srgb_packed);
|
||||
float4 linear_color;
|
||||
linear_color.r = srgb_to_linearrgb(srgba.r);
|
||||
linear_color.g = srgb_to_linearrgb(srgba.g);
|
||||
linear_color.b = srgb_to_linearrgb(srgba.b);
|
||||
linear_color.a = srgba.a;
|
||||
return linear_color;
|
||||
}
|
||||
};
|
||||
|
||||
int2 kernel_size_from_input_size(int2 input_size)
|
||||
{
|
||||
return int2(input_size.x == 1 ? 1 : (2 | (input_size.x & 1)),
|
||||
input_size.y == 1 ? 1 : (2 | (input_size.y & 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Templated struct for bindings and performing the mipmap generation.
|
||||
*
|
||||
* The mipmap generation is based on the general algorithm of
|
||||
* https://github.com/nvpro-samples/vk_compute_mipmaps/tree/main/nvpro_pyramid
|
||||
* It can generate 2 mipmap levels per dispatch.
|
||||
*
|
||||
* \param format: is the texture format of the mipmap images.
|
||||
*
|
||||
* \param SharedStorage: is the storage class to store intermediate levels. Depending on the
|
||||
* texture format an optimal storage class can be selected.
|
||||
*
|
||||
* \param InnerType: the type to use for computation. Depending on the number of samples that a
|
||||
* texture format has a more memory efficient type can be used.
|
||||
*/
|
||||
template<enum TextureWriteFormat format, typename SharedStorage, typename InnerType>
|
||||
struct Resources {
|
||||
[[compilation_constant]] const bool is_srgb_texture;
|
||||
[[compilation_constant]] const bool is_layered;
|
||||
[[push_constant]] const int num_levels;
|
||||
[[image(0, read, format), condition(!is_layered)]] image2D mip_in;
|
||||
[[image(1, write, format), condition(!is_layered)]] image2D mip_out1;
|
||||
[[image(2, write, format), condition(!is_layered)]] image2D mip_out2;
|
||||
[[image(0, read, format), condition(is_layered)]] image2DArray mip_array_in;
|
||||
[[image(1, write, format), condition(is_layered)]] image2DArray mip_array_out1;
|
||||
[[image(2, write, format), condition(is_layered)]] image2DArray mip_array_out2;
|
||||
[[resource_table]] srt_t<SharedStorage> shared_storage;
|
||||
|
||||
/** Store sample result into an output mip image. */
|
||||
void store_sample(int2 dst_coord, int dst_level, InnerType color)
|
||||
{
|
||||
float4 color_out;
|
||||
convert<float4, InnerType>(color_out, color);
|
||||
if (is_srgb_texture) [[static_branch]] {
|
||||
color_out.r = linearrgb_to_srgb(color_out.r);
|
||||
color_out.g = linearrgb_to_srgb(color_out.g);
|
||||
color_out.b = linearrgb_to_srgb(color_out.b);
|
||||
}
|
||||
if (is_layered == false) [[static_branch]] {
|
||||
if (dst_level == 1) {
|
||||
imageStore(mip_out1, dst_coord, color_out);
|
||||
}
|
||||
else if (dst_level == 2) {
|
||||
imageStore(mip_out2, dst_coord, color_out);
|
||||
}
|
||||
}
|
||||
if (is_layered) [[static_branch]] {
|
||||
if (dst_level == 1) {
|
||||
imageStore(mip_array_out1, int3(dst_coord, 0), color_out);
|
||||
}
|
||||
else if (dst_level == 2) {
|
||||
imageStore(mip_array_out2, int3(dst_coord, 0), color_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void store_shared_sample(int2 dst_coord, InnerType color)
|
||||
{
|
||||
SharedStorage &storage = shared_storage;
|
||||
storage.store_sample(dst_coord, color);
|
||||
}
|
||||
|
||||
InnerType load_sample(int2 src_coord, bool load_from_shared)
|
||||
{
|
||||
InnerType color;
|
||||
if (load_from_shared) {
|
||||
SharedStorage &storage = shared_storage;
|
||||
color = storage.load_sample(src_coord);
|
||||
}
|
||||
else {
|
||||
float4 loaded_color;
|
||||
if (is_layered == false) [[static_branch]] {
|
||||
loaded_color = imageLoad(mip_in, src_coord);
|
||||
}
|
||||
if (is_layered) [[static_branch]] {
|
||||
loaded_color = imageLoad(mip_array_in, int3(src_coord, 0));
|
||||
}
|
||||
if (is_srgb_texture) [[static_branch]] {
|
||||
loaded_color.r = srgb_to_linearrgb(loaded_color.r);
|
||||
loaded_color.g = srgb_to_linearrgb(loaded_color.g);
|
||||
loaded_color.b = srgb_to_linearrgb(loaded_color.b);
|
||||
}
|
||||
convert<InnerType, float4>(color, loaded_color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
int2 level_size(int level)
|
||||
{
|
||||
int2 mip_in_size;
|
||||
if (is_layered == false) [[static_branch]] {
|
||||
mip_in_size = imageSize(mip_in);
|
||||
}
|
||||
if (is_layered) [[static_branch]] {
|
||||
mip_in_size = imageSize(mip_array_in).xy;
|
||||
}
|
||||
int2 mip_size = max((mip_in_size >> level), int2(1));
|
||||
return mip_size;
|
||||
}
|
||||
|
||||
InnerType pyramid_reduce_3(
|
||||
float a0, InnerType v0, float a1, InnerType v1, float a2, InnerType v2)
|
||||
{
|
||||
return a0 * v0 + a1 * v1 + a2 * v2;
|
||||
}
|
||||
|
||||
InnerType pyramid_reduce_2(InnerType v0, InnerType v1)
|
||||
{
|
||||
return 0.5 * (v0 + v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle loading and reducing a rectangle of size kernel_size
|
||||
* with the given upper-left coordinate src_coord. Samples read from
|
||||
* mip level src_level if !loadFromShared_, sharedLevel_ otherwise.
|
||||
*
|
||||
* kernel_size must range from 1x1 to 3x3.
|
||||
*
|
||||
* Once computed, the sample is written to the given coordinate of the
|
||||
* specified destination mip level, and returned. The destination
|
||||
* image size is needed to compute the kernel weights.
|
||||
*/
|
||||
template<bool load_from_shared>
|
||||
InnerType reduce_store_sample(int2 src_coord,
|
||||
int /*src_level*/,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level)
|
||||
{
|
||||
float num_dst_pixels = dst_image_size.y;
|
||||
float rcp = 1.0f / (2 * num_dst_pixels + 1);
|
||||
float w0 = rcp * (num_dst_pixels - dst_coord.y);
|
||||
float w1 = rcp * num_dst_pixels;
|
||||
float w2 = 1.0f - w0 - w1;
|
||||
|
||||
InnerType v0, v1, v2, h0, h1, h2, out_pixel;
|
||||
|
||||
/* Reduce vertically up to 3 times (depending on kernel horizontal size) */
|
||||
switch (kernel_size.x) {
|
||||
case 3:
|
||||
switch (kernel_size.y) {
|
||||
case 3:
|
||||
v2 = load_sample(src_coord + int2(2, 2), load_from_shared);
|
||||
ATTR_FALLTHROUGH;
|
||||
case 2:
|
||||
v1 = load_sample(src_coord + int2(2, 1), load_from_shared);
|
||||
ATTR_FALLTHROUGH;
|
||||
case 1:
|
||||
v0 = load_sample(src_coord + int2(2, 0), load_from_shared);
|
||||
break;
|
||||
}
|
||||
switch (kernel_size.y) {
|
||||
case 3:
|
||||
h2 = pyramid_reduce_3(w0, v0, w1, v1, w2, v2);
|
||||
break;
|
||||
case 2:
|
||||
h2 = pyramid_reduce_2(v0, v1);
|
||||
break;
|
||||
case 1:
|
||||
h2 = v0;
|
||||
break;
|
||||
}
|
||||
ATTR_FALLTHROUGH;
|
||||
case 2:
|
||||
switch (kernel_size.y) {
|
||||
case 3:
|
||||
v2 = load_sample(src_coord + int2(1, 2), load_from_shared);
|
||||
ATTR_FALLTHROUGH;
|
||||
case 2:
|
||||
v1 = load_sample(src_coord + int2(1, 1), load_from_shared);
|
||||
ATTR_FALLTHROUGH;
|
||||
case 1:
|
||||
v0 = load_sample(src_coord + int2(1, 0), load_from_shared);
|
||||
break;
|
||||
}
|
||||
switch (kernel_size.y) {
|
||||
case 3:
|
||||
h1 = pyramid_reduce_3(w0, v0, w1, v1, w2, v2);
|
||||
break;
|
||||
case 2:
|
||||
h1 = pyramid_reduce_2(v0, v1);
|
||||
break;
|
||||
case 1:
|
||||
h1 = v0;
|
||||
break;
|
||||
}
|
||||
ATTR_FALLTHROUGH;
|
||||
case 1:
|
||||
switch (kernel_size.y) {
|
||||
case 3:
|
||||
v2 = load_sample(src_coord + int2(0, 2), load_from_shared);
|
||||
ATTR_FALLTHROUGH;
|
||||
case 2:
|
||||
v1 = load_sample(src_coord + int2(0, 1), load_from_shared);
|
||||
ATTR_FALLTHROUGH;
|
||||
case 1:
|
||||
v0 = load_sample(src_coord + int2(0, 0), load_from_shared);
|
||||
break;
|
||||
}
|
||||
switch (kernel_size.y) {
|
||||
case 3:
|
||||
h0 = pyramid_reduce_3(w0, v0, w1, v1, w2, v2);
|
||||
break;
|
||||
case 2:
|
||||
h0 = pyramid_reduce_2(v0, v1);
|
||||
break;
|
||||
case 1:
|
||||
h0 = v0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduce up to 3 samples horizontally. */
|
||||
switch (kernel_size.x) {
|
||||
case 3:
|
||||
num_dst_pixels = dst_image_size.x;
|
||||
rcp = 1.0f / (2 * num_dst_pixels + 1);
|
||||
w0 = rcp * (num_dst_pixels - dst_coord.x);
|
||||
w1 = rcp * num_dst_pixels;
|
||||
w2 = 1.0f - w0 - w1;
|
||||
out_pixel = pyramid_reduce_3(w0, h0, w1, h1, w2, h2);
|
||||
break;
|
||||
case 2:
|
||||
out_pixel = pyramid_reduce_2(h0, h1);
|
||||
break;
|
||||
case 1:
|
||||
out_pixel = h0;
|
||||
}
|
||||
|
||||
/* Write out sample. */
|
||||
store_sample(dst_coord, dst_level, out_pixel);
|
||||
return out_pixel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and write out (to the 1st mip level generated) the samples
|
||||
* at coordinates
|
||||
* init_dst_coord,
|
||||
* init_dst_coord + step, ...
|
||||
* init_dst_coord + (iterations-1) * step
|
||||
* and cache them at in the sharedLevel_ tile at coordinates
|
||||
* init_shared_coord,
|
||||
* init_shared_coord + step, ...
|
||||
* init_shared_coord + (iterations-1) * step
|
||||
* If use_bounds_check is true, skip coordinates that are out of bounds.
|
||||
*/
|
||||
void intermediate_level_loop(int2 init_dst_coord,
|
||||
int2 init_shared_coord,
|
||||
int2 step,
|
||||
int iterations,
|
||||
bool use_bounds_check)
|
||||
{
|
||||
int2 dst_coord = init_dst_coord;
|
||||
int2 shared_coord = init_shared_coord;
|
||||
int src_level = INPUT_LEVEL;
|
||||
int dst_level = src_level + 1;
|
||||
int2 src_image_size = level_size(src_level);
|
||||
int2 dst_image_size = level_size(dst_level);
|
||||
int2 kernel_size = kernel_size_from_input_size(src_image_size);
|
||||
|
||||
for (int i_ = 0; i_ < iterations; ++i_) {
|
||||
int2 src_coord = dst_coord * 2;
|
||||
|
||||
if (use_bounds_check) {
|
||||
if (uint(dst_coord.x) >= uint(dst_image_size.x)) {
|
||||
continue;
|
||||
}
|
||||
if (uint(dst_coord.y) >= uint(dst_image_size.y)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
InnerType result = reduce_store_sample<false>(
|
||||
src_coord, src_level, kernel_size, dst_image_size, dst_coord, dst_level);
|
||||
|
||||
/* `reduce_store_sample` handles writing to the actual output; manually
|
||||
* cache into shared memory here. */
|
||||
store_shared_sample(shared_coord, result);
|
||||
dst_coord += step;
|
||||
shared_coord += step;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for the workgroup that handles filling the intermediate level
|
||||
* (caching it in shared memory as well).
|
||||
*
|
||||
* We need somewhere from 16x16 to 17x17 samples, depending
|
||||
* on what the kernel size for the 2nd mip level generation will be.
|
||||
*
|
||||
* dst_tile_coord : upper left coordinate of the tile to generate.
|
||||
* use_bounds_check : whether to skip samples that are out-of-bounds.
|
||||
*/
|
||||
void fill_intermediate_tile(uint local_index, int2 dst_tile_coord, bool use_bounds_check)
|
||||
{
|
||||
int2 init_thread_offset;
|
||||
int2 step;
|
||||
int iterations;
|
||||
|
||||
int2 dst_image_size = level_size(INPUT_LEVEL + 1);
|
||||
int2 future_kernel_size = kernel_size_from_input_size(dst_image_size);
|
||||
|
||||
if (future_kernel_size.x == 3) {
|
||||
if (future_kernel_size.y == 3) {
|
||||
/* Fill in 2 17x7 steps and 1 17x3 step (9 idle threads) */
|
||||
init_thread_offset = int2(local_index % 17u, local_index / 17u);
|
||||
step = int2(0, 7);
|
||||
iterations = local_index >= 7 * 17 ? 0 : local_index < 3 * 17 ? 3 : 2;
|
||||
}
|
||||
else {
|
||||
/* Future 3x[2,1] kernel
|
||||
* Fill in 2 8x16 steps and 1 1x16 step */
|
||||
init_thread_offset = int2(local_index / 16u, local_index % 16u);
|
||||
step = int2(8, 0);
|
||||
iterations = local_index < 1 * 16 ? 3 : 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (future_kernel_size.y == 3) {
|
||||
/* Fill in 2 16x8 steps and 1 16x1 step */
|
||||
init_thread_offset = int2(local_index % 16u, local_index / 16u);
|
||||
step = int2(0, 8);
|
||||
iterations = local_index < 1 * 16 ? 3 : 2;
|
||||
}
|
||||
else {
|
||||
/* Fill in 2 16x8 steps */
|
||||
init_thread_offset = int2(local_index % 16u, local_index / 16u);
|
||||
step = int2(0, 8);
|
||||
iterations = 2;
|
||||
}
|
||||
}
|
||||
|
||||
intermediate_level_loop(dst_tile_coord + init_thread_offset,
|
||||
init_thread_offset,
|
||||
step,
|
||||
iterations,
|
||||
use_bounds_check);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for the workgroup that handles filling the last level tile
|
||||
* (2nd level after the original input level), using as input the
|
||||
* tile in shared memory.
|
||||
*
|
||||
* dst_tile_coord : upper left coordinate of the tile to generate.
|
||||
* use_bounds_check : whether to skip samples that are out-of-bounds.
|
||||
*/
|
||||
void fill_last_tile(uint local_index, int2 dst_tile_coord, bool use_bounds_check)
|
||||
{
|
||||
|
||||
if (local_index < 8 * 8) {
|
||||
int2 thread_offset = int2(local_index % 8u, local_index / 8u);
|
||||
int src_level = INPUT_LEVEL + 1;
|
||||
int dst_level = INPUT_LEVEL + 2;
|
||||
int2 src_image_size = level_size(src_level);
|
||||
int2 dst_image_size = level_size(dst_level);
|
||||
|
||||
int2 src_shared_coord = thread_offset * 2;
|
||||
int2 kernel_size = kernel_size_from_input_size(src_image_size);
|
||||
int2 dst_coord = thread_offset + dst_tile_coord;
|
||||
|
||||
bool within_bounds = true;
|
||||
if (use_bounds_check) {
|
||||
within_bounds = (uint(dst_coord.x) < uint(dst_image_size.x)) &&
|
||||
(uint(dst_coord.y) < uint(dst_image_size.y));
|
||||
}
|
||||
if (within_bounds) {
|
||||
reduce_store_sample<true>(
|
||||
src_shared_coord, 0, kernel_size, dst_image_size, dst_coord, dst_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<enum TextureWriteFormat format, typename SharedStorage, typename InnerType>
|
||||
[[local_size(LOCAL_SIZE_X)]] [[compute]]
|
||||
void update_mipmaps([[global_invocation_id]] const uint3 global_id,
|
||||
[[work_group_id]] const uint3 group_id,
|
||||
[[local_invocation_id]] const uint3 local_index,
|
||||
[[resource_table]] Resources<format, SharedStorage, InnerType> &srt)
|
||||
{
|
||||
if (srt.num_levels == 1u) {
|
||||
int2 kernel_size = kernel_size_from_input_size(srt.level_size(INPUT_LEVEL));
|
||||
int2 dst_image_size = srt.level_size(INPUT_LEVEL + 1);
|
||||
int2 dst_coord = int2(int(global_id.x) % dst_image_size.x,
|
||||
int(global_id.x) / dst_image_size.x);
|
||||
int2 src_coord = dst_coord * 2;
|
||||
|
||||
if (dst_coord.y < dst_image_size.y) {
|
||||
srt.template reduce_store_sample<false>(
|
||||
src_coord, INPUT_LEVEL, kernel_size, dst_image_size, dst_coord, INPUT_LEVEL + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Handling two levels.
|
||||
* Assign a 8x8 tile of mip level inputLevel_ + 2 to this workgroup. */
|
||||
int level2 = INPUT_LEVEL + 2;
|
||||
int2 level2_size = srt.level_size(level2);
|
||||
int2 tile_count;
|
||||
tile_count.x = int(uint(level2_size.x + 7) / 8u);
|
||||
tile_count.y = int(uint(level2_size.y + 7) / 8u);
|
||||
int2 tile_index = int2(group_id.x % uint(tile_count.x), group_id.x / uint(tile_count.x));
|
||||
|
||||
/* Determine if bounds checking is needed; this is only the case
|
||||
* for tiles at the right or bottom fringe that might be cut off
|
||||
* by the image border. Note that later, I use if statements rather
|
||||
* than passing use_bounds_check directly to convince the compiler
|
||||
* to inline everything. */
|
||||
bool use_bounds_check = tile_index.x >= tile_count.x - 1 || tile_index.y >= tile_count.y - 1;
|
||||
|
||||
if (use_bounds_check) {
|
||||
/* Compute the tile in level inputLevel_ + 1 that's needed to
|
||||
* compute the above 8x8 tile. */
|
||||
srt.fill_intermediate_tile(local_index.x, tile_index * 2 * int2(8, 8), true);
|
||||
barrier();
|
||||
|
||||
/* Compute the inputLevel_ + 2 tile of size 8x8, loading
|
||||
* inputs from shared memory. */
|
||||
srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), true);
|
||||
}
|
||||
else {
|
||||
/* Same but without bounds checking. */
|
||||
srt.fill_intermediate_tile(local_index.x, tile_index * 2 * int2(8, 8), false);
|
||||
barrier();
|
||||
srt.fill_last_tile(local_index.x, tile_index * int2(8, 8), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template struct Shared<float>;
|
||||
template struct Shared<float4>;
|
||||
|
||||
template struct Resources<UNORM_8, SharedUnorm, float>;
|
||||
template struct Resources<UNORM_8_8_8_8, SharedSRGB, float4>;
|
||||
template struct Resources<SFLOAT_16, Shared<float>, float>;
|
||||
template struct Resources<SFLOAT_16_16_16_16, Shared<float4>, float4>;
|
||||
template struct Resources<SFLOAT_32, Shared<float>, float>;
|
||||
template struct Resources<SFLOAT_32_32_32_32, Shared<float4>, float4>;
|
||||
|
||||
template float Resources<UNORM_8, SharedUnorm, float>::reduce_store_sample<true>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float Resources<UNORM_8, SharedUnorm, float>::reduce_store_sample<false>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float4 Resources<UNORM_8_8_8_8, SharedSRGB, float4>::reduce_store_sample<true>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float4 Resources<UNORM_8_8_8_8, SharedSRGB, float4>::reduce_store_sample<false>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float Resources<SFLOAT_16, Shared<float>, float>::reduce_store_sample<true>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float Resources<SFLOAT_16, Shared<float>, float>::reduce_store_sample<false>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float4 Resources<SFLOAT_16_16_16_16, Shared<float4>, float4>::reduce_store_sample<true>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float4 Resources<SFLOAT_16_16_16_16, Shared<float4>, float4>::reduce_store_sample<false>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float Resources<SFLOAT_32, Shared<float>, float>::reduce_store_sample<true>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float Resources<SFLOAT_32, Shared<float>, float>::reduce_store_sample<false>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float4 Resources<SFLOAT_32_32_32_32, Shared<float4>, float4>::reduce_store_sample<true>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
template float4 Resources<SFLOAT_32_32_32_32, Shared<float4>, float4>::reduce_store_sample<false>(
|
||||
int2 src_coord,
|
||||
int src_level,
|
||||
int2 kernel_size,
|
||||
int2 dst_image_size,
|
||||
int2 dst_coord,
|
||||
int dst_level);
|
||||
|
||||
template void update_mipmaps<UNORM_8, SharedUnorm, float>(
|
||||
const uint3 global_id,
|
||||
const uint3 group_id,
|
||||
const uint3 local_index,
|
||||
Resources<UNORM_8, SharedUnorm, float> &srt);
|
||||
template void update_mipmaps<UNORM_8_8_8_8, SharedSRGB, float4>(
|
||||
const uint3 global_id,
|
||||
const uint3 group_id,
|
||||
const uint3 local_index,
|
||||
Resources<UNORM_8_8_8_8, SharedSRGB, float4> &srt);
|
||||
template void update_mipmaps<SFLOAT_16, Shared<float>, float>(
|
||||
const uint3 global_id,
|
||||
const uint3 group_id,
|
||||
const uint3 local_index,
|
||||
Resources<SFLOAT_16, Shared<float>, float> &srt);
|
||||
template void update_mipmaps<SFLOAT_16_16_16_16, Shared<float4>, float4>(
|
||||
const uint3 global_id,
|
||||
const uint3 group_id,
|
||||
const uint3 local_index,
|
||||
Resources<SFLOAT_16_16_16_16, Shared<float4>, float4> &srt);
|
||||
template void update_mipmaps<SFLOAT_32, Shared<float>, float>(
|
||||
const uint3 global_id,
|
||||
const uint3 group_id,
|
||||
const uint3 local_index,
|
||||
Resources<SFLOAT_32, Shared<float>, float> &srt);
|
||||
template void update_mipmaps<SFLOAT_32_32_32_32, Shared<float4>, float4>(
|
||||
const uint3 global_id,
|
||||
const uint3 group_id,
|
||||
const uint3 local_index,
|
||||
Resources<SFLOAT_32_32_32_32, Shared<float4>, float4> &srt);
|
||||
|
||||
} // namespace builtin::mipmaps
|
||||
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8(
|
||||
builtin::mipmaps::update_mipmaps<UNORM_8, builtin::mipmaps::SharedUnorm, float>,
|
||||
builtin::mipmaps::Resources<UNORM_8, builtin::mipmaps::SharedUnorm, float>{
|
||||
.is_srgb_texture = false, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8_layered(
|
||||
builtin::mipmaps::update_mipmaps<UNORM_8, builtin::mipmaps::SharedUnorm, float>,
|
||||
builtin::mipmaps::Resources<UNORM_8, builtin::mipmaps::SharedUnorm, float>{
|
||||
.is_srgb_texture = false, .is_layered = true});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8_8_8_8(
|
||||
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
|
||||
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
|
||||
.is_srgb_texture = false, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_unorm_8_8_8_8_layered(
|
||||
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
|
||||
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
|
||||
.is_srgb_texture = false, .is_layered = true});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_16, builtin::mipmaps::Shared<float>, float>,
|
||||
builtin::mipmaps::Resources<SFLOAT_16, builtin::mipmaps::Shared<float>, float>{
|
||||
.is_srgb_texture = false, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16_layered(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_16, builtin::mipmaps::Shared<float>, float>,
|
||||
builtin::mipmaps::Resources<SFLOAT_16, builtin::mipmaps::Shared<float>, float>{
|
||||
.is_srgb_texture = false, .is_layered = true});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16_16_16_16(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>,
|
||||
builtin::mipmaps::Resources<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>{
|
||||
.is_srgb_texture = false, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_16_16_16_16_layered(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>,
|
||||
builtin::mipmaps::Resources<SFLOAT_16_16_16_16, builtin::mipmaps::Shared<float4>, float4>{
|
||||
.is_srgb_texture = false, .is_layered = true});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_32, builtin::mipmaps::Shared<float>, float>,
|
||||
builtin::mipmaps::Resources<SFLOAT_32, builtin::mipmaps::Shared<float>, float>{
|
||||
.is_srgb_texture = false, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32_layered(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_32, builtin::mipmaps::Shared<float>, float>,
|
||||
builtin::mipmaps::Resources<SFLOAT_32, builtin::mipmaps::Shared<float>, float>{
|
||||
.is_srgb_texture = false, .is_layered = true});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32_32_32_32(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>,
|
||||
builtin::mipmaps::Resources<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>{
|
||||
.is_srgb_texture = false, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_sfloat_32_32_32_32_layered(
|
||||
builtin::mipmaps::update_mipmaps<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>,
|
||||
builtin::mipmaps::Resources<SFLOAT_32_32_32_32, builtin::mipmaps::Shared<float4>, float4>{
|
||||
.is_srgb_texture = false, .is_layered = true});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_srgba_8_8_8_8(
|
||||
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
|
||||
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
|
||||
.is_srgb_texture = true, .is_layered = false});
|
||||
PipelineCompute gpu_shader_2D_update_mipmaps_srgba_8_8_8_8_layered(
|
||||
builtin::mipmaps::update_mipmaps<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>,
|
||||
builtin::mipmaps::Resources<UNORM_8_8_8_8, builtin::mipmaps::SharedSRGB, float4>{
|
||||
.is_srgb_texture = true, .is_layered = true});
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_checker_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_checker)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
#include "GPU_shader_shared.hh"
|
||||
#include "gpu_interface_infos.hh"
|
||||
#include "gpu_shader_colorspace_lib.glsl"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
/* TODO(fclem): Share with C code. */
|
||||
#define MAX_PARAM 12
|
||||
#define MAX_INSTANCE 6
|
||||
|
||||
namespace builtin::widget {
|
||||
|
||||
struct VertOut {
|
||||
[[flat]] float discard_fac;
|
||||
[[flat]] float line_width;
|
||||
[[flat]] float2 out_rect_size;
|
||||
[[flat]] float4 border_color;
|
||||
[[flat]] float4 emboss_color;
|
||||
[[flat]] float4 out_round_corners;
|
||||
[[no_perspective]] float but_co;
|
||||
[[no_perspective]] float2 uv_interp;
|
||||
[[no_perspective]] float4 inner_color;
|
||||
};
|
||||
|
||||
struct [[host_shared]] WidgetRaw {
|
||||
float4 data[12];
|
||||
};
|
||||
|
||||
struct [[host_shared]] Widget {
|
||||
float4 recti;
|
||||
float4 rect;
|
||||
|
||||
float radsi;
|
||||
float rads;
|
||||
float2 faci;
|
||||
|
||||
float4 round_corners;
|
||||
float4 color_inner1;
|
||||
float4 color_inner2;
|
||||
float4 color_edge;
|
||||
float4 color_emboss;
|
||||
float4 color_tria;
|
||||
|
||||
float2 tria1_center;
|
||||
float2 tria2_center;
|
||||
|
||||
float tria1_size;
|
||||
float tria2_size;
|
||||
float shade_dir;
|
||||
float alpha_discard;
|
||||
|
||||
float tria_type;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
|
||||
/* We encode alpha check and discard factor together. */
|
||||
bool do_alpha_check() const
|
||||
{
|
||||
return alpha_discard < 0.0f;
|
||||
}
|
||||
float discard_factor() const
|
||||
{
|
||||
return abs(alpha_discard);
|
||||
}
|
||||
|
||||
VertOut do_widget(int vert_id, float2 &pos)
|
||||
{
|
||||
VertOut v_out;
|
||||
/* Offset to avoid losing pixels (mimics conservative rasterization). */
|
||||
constexpr float2 ofs = float2(0.5f, -0.5f);
|
||||
v_out.line_width = abs(rect.x - recti.x);
|
||||
float2 emboss_ofs = float2(0.0f, -v_out.line_width);
|
||||
|
||||
switch (vert_id) {
|
||||
default:
|
||||
case 0: {
|
||||
pos = rect.xz + emboss_ofs + ofs.yy;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
pos = rect.xw + ofs.yx;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
pos = rect.yz + emboss_ofs + ofs.xy;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
pos = rect.yw + ofs.xx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v_out.uv_interp = pos - rect.xz;
|
||||
v_out.out_rect_size = rect.yw - rect.xz;
|
||||
v_out.out_round_corners = rads * round_corners;
|
||||
|
||||
float2 uv = v_out.uv_interp / v_out.out_rect_size;
|
||||
float fac = clamp((shade_dir > 0.0f) ? uv.y : uv.x, 0.0f, 1.0f);
|
||||
/* Note innerColor is premultiplied inside the fragment shader. */
|
||||
if (do_alpha_check()) {
|
||||
v_out.inner_color = color_inner1;
|
||||
v_out.but_co = uv.x;
|
||||
}
|
||||
else {
|
||||
v_out.inner_color = mix(color_inner2, color_inner1, fac);
|
||||
v_out.but_co = -abs(uv.x);
|
||||
}
|
||||
|
||||
/* We need premultiplied color for transparency. */
|
||||
v_out.border_color = color_edge * float4(color_edge.aaa, 1.0f);
|
||||
v_out.emboss_color = color_emboss * float4(color_emboss.aaa, 1.0f);
|
||||
|
||||
return v_out;
|
||||
}
|
||||
|
||||
VertOut do_tria(int vert_id, float2 &pos)
|
||||
{
|
||||
VertOut v_out;
|
||||
int vidx = vert_id % 4;
|
||||
bool tria2 = vert_id > 7;
|
||||
|
||||
pos = float2(0.0f);
|
||||
float size = (tria2) ? -tria2_size : tria1_size;
|
||||
float2 center = (tria2) ? tria2_center : tria1_center;
|
||||
|
||||
float2 arrow_pos[] = {
|
||||
float2(0.0f, 0.6f), float2(0.6f, 0.0f), float2(-0.6f, 0.0f), float2(0.0f, -0.6f)};
|
||||
/* Rotated uv space by 45deg and mirrored. */
|
||||
float2 arrow_uvs[] = {
|
||||
float2(0.0f, 0.85f), float2(0.85f, 0.85f), float2(0.0f, 0.0f), float2(0.0f, 0.85f)};
|
||||
|
||||
float2 point_pos[] = {
|
||||
float2(-1.0f, -1.0f), float2(-1.0f, 1.0f), float2(1.0f, -1.0f), float2(1.0f, 1.0f)};
|
||||
float2 point_uvs[] = {
|
||||
float2(0.0f, 0.0f), float2(0.0f, 1.0f), float2(1.0f, 0.0f), float2(1.0f, 1.0f)};
|
||||
|
||||
/* We reuse the SDF round-box rendering of widget to render the tria shapes.
|
||||
* This means we do clever tricks to position the rectangle the way we want using
|
||||
* the 2 triangles uvs. */
|
||||
if (tria_type == 0.0f) {
|
||||
/* ROUNDBOX_TRIA_NONE */
|
||||
v_out.out_rect_size = v_out.uv_interp = pos = float2(0);
|
||||
v_out.out_round_corners = float4(0.01f);
|
||||
}
|
||||
else if (tria_type == 1.0f) {
|
||||
/* ROUNDBOX_TRIA_ARROWS */
|
||||
pos = arrow_pos[vidx];
|
||||
v_out.uv_interp = arrow_uvs[vidx];
|
||||
v_out.uv_interp -= float2(0.05f, 0.63f); /* Translate */
|
||||
v_out.out_rect_size = float2(0.74f, 0.17f);
|
||||
v_out.out_round_corners = float4(0.08f);
|
||||
}
|
||||
else if (tria_type == 2.0f) {
|
||||
/* ROUNDBOX_TRIA_SCROLL */
|
||||
pos = point_pos[vidx];
|
||||
v_out.uv_interp = point_uvs[vidx];
|
||||
v_out.out_rect_size = float2(1.0f);
|
||||
v_out.out_round_corners = float4(0.5f);
|
||||
}
|
||||
else if (tria_type == 3.0f) {
|
||||
/* ROUNDBOX_TRIA_MENU */
|
||||
pos = tria2 ? float2(0.0f) : arrow_pos[vidx]; /* Solo tria */
|
||||
pos = float2(pos.y, -pos.x); /* Rotate */
|
||||
pos += float2(-0.05f, 0.0f); /* Translate */
|
||||
size *= 0.8f; /* Scale */
|
||||
v_out.uv_interp = arrow_uvs[vidx];
|
||||
v_out.uv_interp -= float2(0.05f, 0.63f); /* Translate */
|
||||
v_out.out_rect_size = float2(0.74f, 0.17f);
|
||||
v_out.out_round_corners = float4(0.01f);
|
||||
}
|
||||
else if (tria_type == 4.0f) {
|
||||
/* ROUNDBOX_TRIA_CHECK */
|
||||
/* A bit more hacky: We use the two triangles joined together to render
|
||||
* both sides of the check-mark with different length. */
|
||||
pos = arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */
|
||||
pos.y = tria2 ? -pos.y : pos.y; /* Mirror along X */
|
||||
pos = pos.x * float2(0.0872f, -0.996f) +
|
||||
pos.y * float2(0.996f, 0.0872f); /* Rotate (85deg) */
|
||||
pos += float2(-0.1f, 0.2f); /* Translate */
|
||||
center = tria1_center;
|
||||
size = tria1_size * 1.7f; /* Scale */
|
||||
v_out.uv_interp = arrow_uvs[vidx];
|
||||
v_out.uv_interp -= tria2 ? float2(0.4f, 0.65f) : float2(0.08f, 0.65f); /* Translate */
|
||||
v_out.out_rect_size = float2(0.74f, 0.14f);
|
||||
v_out.out_round_corners = float4(0.01f);
|
||||
}
|
||||
else if (tria_type == 5.0f) {
|
||||
/* ROUNDBOX_TRIA_HOLD_ACTION_ARROW */
|
||||
/* We use a single triangle to cut the round rect in half.
|
||||
* The edge will not be Anti-aliased. */
|
||||
pos = tria2 ? float2(0.0f) : arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */
|
||||
pos = pos.x * float2(0.707f, 0.707f) + pos.y * float2(-0.707f, 0.707f); /* Rotate (45deg)
|
||||
*/
|
||||
pos += float2(-1.7f, 2.4f); /* Translate (hard-coded, might want to remove). */
|
||||
size *= 0.4f; /* Scale */
|
||||
v_out.uv_interp = arrow_uvs[vidx];
|
||||
v_out.uv_interp -= float2(0.05f, 0.05f); /* Translate */
|
||||
v_out.out_rect_size = float2(0.75f);
|
||||
v_out.out_round_corners = float4(0.01f);
|
||||
}
|
||||
else if (tria_type == 6.0f) {
|
||||
/* ROUNDBOX_TRIA_DASH */
|
||||
pos = point_pos[vidx];
|
||||
v_out.uv_interp = point_uvs[vidx];
|
||||
v_out.uv_interp -= float2(0.2f, 0.45f); /* Translate */
|
||||
v_out.out_rect_size = float2(0.6f, 0.1f);
|
||||
v_out.out_round_corners = float4(0.01f);
|
||||
}
|
||||
|
||||
v_out.uv_interp *= abs(size);
|
||||
v_out.out_rect_size *= abs(size);
|
||||
v_out.out_round_corners *= abs(size);
|
||||
|
||||
pos = pos * size + center;
|
||||
|
||||
v_out.inner_color = color_tria * float4(color_tria.aaa, 1.0f);
|
||||
|
||||
v_out.line_width = 0.0f;
|
||||
v_out.border_color = float4(0.0f);
|
||||
v_out.emboss_color = float4(0.0f);
|
||||
|
||||
v_out.but_co = -2.0f;
|
||||
|
||||
return v_out;
|
||||
}
|
||||
};
|
||||
|
||||
/* WORKAROUND: We cannot use structs with push constants, so we push a float4 array and reinterpret
|
||||
* using a union. */
|
||||
struct WidgetUnion {
|
||||
union {
|
||||
union_t<WidgetRaw> raw;
|
||||
union_t<Widget> data;
|
||||
};
|
||||
};
|
||||
|
||||
struct Resources {
|
||||
[[legacy_info]] ShaderCreateInfo gpu_srgb_to_framebuffer_space;
|
||||
|
||||
[[push_constant]] const float4x4 ModelViewProjectionMatrix;
|
||||
[[push_constant]] const float3 checkerColorAndSize;
|
||||
|
||||
[[compilation_constant]] const bool instanced;
|
||||
[[push_constant, condition(instanced)]] const float4 parameters_inst[MAX_PARAM * MAX_INSTANCE];
|
||||
[[push_constant, condition(!instanced)]] const float4 parameters[MAX_PARAM];
|
||||
|
||||
/** Unpack widget data passed as raw array of float4 through push constants. */
|
||||
Widget get_widget(int index)
|
||||
{
|
||||
/* Hopefully, all of these move instructions are optimized out. */
|
||||
WidgetRaw raw;
|
||||
if (this->instanced) [[static_branch]] {
|
||||
for (int i = 0; i < 12; i++) [[unroll]] {
|
||||
raw.data[i] = parameters_inst[index * MAX_PARAM + i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < 12; i++) [[unroll]] {
|
||||
raw.data[i] = parameters[i];
|
||||
}
|
||||
}
|
||||
/* Equivalent of reinterpret_cast. */
|
||||
WidgetUnion widget;
|
||||
widget.raw() = raw;
|
||||
return widget.data();
|
||||
}
|
||||
|
||||
float4 do_checkerboard(float2 frag_co)
|
||||
{
|
||||
float size = checkerColorAndSize.z;
|
||||
float2 phase = mod(frag_co.xy, size * 2.0f);
|
||||
|
||||
if ((phase.x > size && phase.y < size) || (phase.x < size && phase.y > size)) {
|
||||
return float4(checkerColorAndSize.xxx, 1.0f);
|
||||
}
|
||||
return float4(checkerColorAndSize.yyy, 1.0f);
|
||||
}
|
||||
};
|
||||
|
||||
[[vertex]] void vert([[vertex_id]] const int vert_id,
|
||||
[[instance_id]] const int inst_id,
|
||||
[[resource_table]] Resources &srt,
|
||||
[[out]] VertOut &v_out,
|
||||
[[position]] float4 &position)
|
||||
{
|
||||
Widget widget = srt.get_widget(inst_id);
|
||||
|
||||
bool is_tria = (vert_id > 3);
|
||||
float2 pos;
|
||||
VertOut vert_out = (is_tria) ? widget.do_tria(vert_id, pos) : widget.do_widget(vert_id, pos);
|
||||
|
||||
/* WORKAROUND: Quirk of current BSL implementation.
|
||||
* Current implementation doesn't allow to assign the output struct at once. */
|
||||
v_out.discard_fac = widget.discard_factor();
|
||||
v_out.line_width = vert_out.line_width;
|
||||
v_out.out_rect_size = vert_out.out_rect_size;
|
||||
v_out.border_color = vert_out.border_color;
|
||||
v_out.emboss_color = vert_out.emboss_color;
|
||||
v_out.out_round_corners = vert_out.out_round_corners;
|
||||
v_out.but_co = vert_out.but_co;
|
||||
v_out.uv_interp = vert_out.uv_interp;
|
||||
v_out.inner_color = vert_out.inner_color;
|
||||
|
||||
position = srt.ModelViewProjectionMatrix * float4(pos, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
struct FragOut {
|
||||
[[frag_color(0)]] float4 color;
|
||||
};
|
||||
|
||||
[[fragment]] void frag([[in]] const VertOut &v_out,
|
||||
[[out]] FragOut &frag_out,
|
||||
[[frag_coord]] const float4 frag_co,
|
||||
[[resource_table]] Resources &srt)
|
||||
{
|
||||
if (min(1.0f, -v_out.but_co) > v_out.discard_fac) {
|
||||
gpu_discard_fragment();
|
||||
}
|
||||
|
||||
float2 uv = v_out.uv_interp;
|
||||
|
||||
bool upper_half = uv.y > v_out.out_rect_size.y * 0.5f;
|
||||
bool right_half = uv.x > v_out.out_rect_size.x * 0.5f;
|
||||
float corner_rad;
|
||||
|
||||
/* Correct aspect ratio for 2D views not using uniform scaling.
|
||||
* uv is already in pixel space so a uniform scale should give us a ratio of 1. */
|
||||
float ratio = (v_out.but_co != -2.0f) ? abs(gpu_dfdy(uv.y) / gpu_dfdx(uv.x)) : 1.0f;
|
||||
float2 uv_sdf = uv;
|
||||
uv_sdf.x *= ratio;
|
||||
|
||||
if (right_half) {
|
||||
uv_sdf.x = v_out.out_rect_size.x * ratio - uv_sdf.x;
|
||||
}
|
||||
if (upper_half) {
|
||||
uv_sdf.y = v_out.out_rect_size.y - uv_sdf.y;
|
||||
corner_rad = right_half ? v_out.out_round_corners.z : v_out.out_round_corners.w;
|
||||
}
|
||||
else {
|
||||
corner_rad = right_half ? v_out.out_round_corners.y : v_out.out_round_corners.x;
|
||||
}
|
||||
|
||||
/* Fade emboss at the border. */
|
||||
float emboss_size = upper_half ? 0.0f : min(1.0f, uv_sdf.x / (corner_rad * ratio));
|
||||
|
||||
/* Signed distance field from the corner (in pixel).
|
||||
* inner_sdf is sharp and outer_sdf is rounded. */
|
||||
uv_sdf -= corner_rad;
|
||||
float inner_sdf = max(0.0f, min(uv_sdf.x, uv_sdf.y));
|
||||
float outer_sdf = -length(min(uv_sdf, 0.0f));
|
||||
float sdf = inner_sdf + outer_sdf + corner_rad;
|
||||
|
||||
/* Clamp line width to be at least 1px wide. This can happen if the projection matrix
|
||||
* has been scaled (i.e: Node editor)... */
|
||||
float line_width = (v_out.line_width > 0.0f) ? max(gpu_fwidth(uv.y), v_out.line_width) : 0.0f;
|
||||
|
||||
constexpr float aa_radius = 0.5f;
|
||||
float3 masks;
|
||||
masks.x = smoothstep(-aa_radius, aa_radius, sdf);
|
||||
masks.y = smoothstep(-aa_radius, aa_radius, sdf - line_width);
|
||||
masks.z = smoothstep(-aa_radius, aa_radius, sdf + line_width * emboss_size);
|
||||
|
||||
/* Compose masks together to avoid having too much alpha. */
|
||||
masks.zx = max(float2(0.0f), masks.zx - masks.xy);
|
||||
|
||||
if (v_out.but_co > 0.0f) {
|
||||
/* Alpha checker widget. */
|
||||
if (v_out.but_co > 0.5f) {
|
||||
float4 checker = srt.do_checkerboard(frag_co.xy);
|
||||
frag_out.color = mix(checker, v_out.inner_color, v_out.inner_color.a);
|
||||
}
|
||||
else {
|
||||
/* Set alpha to 1.0f. */
|
||||
frag_out.color = v_out.inner_color;
|
||||
}
|
||||
frag_out.color.a = 1.0f;
|
||||
}
|
||||
else {
|
||||
/* Pre-multiply here. */
|
||||
frag_out.color = v_out.inner_color * float4(v_out.inner_color.aaa, 1.0f);
|
||||
}
|
||||
frag_out.color *= masks.y;
|
||||
frag_out.color += masks.x * v_out.border_color;
|
||||
frag_out.color += masks.z * v_out.emboss_color;
|
||||
|
||||
/* Un-pre-multiply because the blend equation is already doing the multiplication. */
|
||||
if (frag_out.color.a > 0.0f) {
|
||||
frag_out.color.rgb /= frag_out.color.a;
|
||||
}
|
||||
|
||||
frag_out.color = blender_srgb_to_framebuffer_space(frag_out.color);
|
||||
}
|
||||
|
||||
} // namespace builtin::widget
|
||||
|
||||
PipelineGraphic gpu_shader_2D_widget_base(builtin::widget::vert,
|
||||
builtin::widget::frag,
|
||||
builtin::widget::Resources{.instanced = false});
|
||||
PipelineGraphic gpu_shader_2D_widget_base_inst(builtin::widget::vert,
|
||||
builtin::widget::frag,
|
||||
builtin::widget::Resources{.instanced = true});
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_widget_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_widget_shadow)
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = float4(0.0f);
|
||||
/* Manual curve fit of the falloff curve of previous drawing method. */
|
||||
float shadow_alpha = alpha * (shadowFalloff * shadowFalloff * 0.722f + shadowFalloff * 0.277f);
|
||||
float inner_alpha = smoothstep(0.0f, 0.05f, innerMask);
|
||||
|
||||
fragColor.a = inner_alpha * shadow_alpha;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_widget_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_widget_shadow)
|
||||
|
||||
#define BIT_RANGE(x) uint((1 << x) - 1)
|
||||
|
||||
/* 2 bits for corner */
|
||||
/* Attention! Not the same order as in UI_interface.hh!
|
||||
* Ordered by drawing order. */
|
||||
#define BOTTOM_LEFT 0u
|
||||
#define BOTTOM_RIGHT 1u
|
||||
#define TOP_RIGHT 2u
|
||||
#define TOP_LEFT 3u
|
||||
#define CNR_FLAG_RANGE BIT_RANGE(2)
|
||||
|
||||
/* 4bits for corner id */
|
||||
#define CORNER_VEC_OFS 2u
|
||||
#define CORNER_VEC_RANGE BIT_RANGE(4)
|
||||
|
||||
#define INNER_FLAG uint(1 << 10) /* is inner vert */
|
||||
|
||||
/* Radii and rad per corner. */
|
||||
#define recti parameters[0]
|
||||
#define rect parameters[1]
|
||||
#define radsi parameters[2].x
|
||||
#define rads parameters[2].y
|
||||
#define roundCorners parameters[3]
|
||||
|
||||
void main()
|
||||
{
|
||||
/* NOTE(Metal): Declaring constant array in function scope to avoid increasing local shader
|
||||
* memory pressure. */
|
||||
constexpr float2 cornervec[36] = float2_array(float2(0.0f, 1.0f),
|
||||
float2(0.02f, 0.805f),
|
||||
float2(0.067f, 0.617f),
|
||||
float2(0.169f, 0.45f),
|
||||
float2(0.293f, 0.293f),
|
||||
float2(0.45f, 0.169f),
|
||||
float2(0.617f, 0.076f),
|
||||
float2(0.805f, 0.02f),
|
||||
float2(1.0f, 0.0f),
|
||||
float2(-1.0f, 0.0f),
|
||||
float2(-0.805f, 0.02f),
|
||||
float2(-0.617f, 0.067f),
|
||||
float2(-0.45f, 0.169f),
|
||||
float2(-0.293f, 0.293f),
|
||||
float2(-0.169f, 0.45f),
|
||||
float2(-0.076f, 0.617f),
|
||||
float2(-0.02f, 0.805f),
|
||||
float2(0.0f, 1.0f),
|
||||
float2(0.0f, -1.0f),
|
||||
float2(-0.02f, -0.805f),
|
||||
float2(-0.067f, -0.617f),
|
||||
float2(-0.169f, -0.45f),
|
||||
float2(-0.293f, -0.293f),
|
||||
float2(-0.45f, -0.169f),
|
||||
float2(-0.617f, -0.076f),
|
||||
float2(-0.805f, -0.02f),
|
||||
float2(-1.0f, 0.0f),
|
||||
float2(1.0f, 0.0f),
|
||||
float2(0.805f, -0.02f),
|
||||
float2(0.617f, -0.067f),
|
||||
float2(0.45f, -0.169f),
|
||||
float2(0.293f, -0.293f),
|
||||
float2(0.169f, -0.45f),
|
||||
float2(0.076f, -0.617f),
|
||||
float2(0.02f, -0.805f),
|
||||
float2(0.0f, -1.0f));
|
||||
|
||||
constexpr float2 center_offset[4] = float2_array(
|
||||
float2(1.0f, 1.0f), float2(-1.0f, 1.0f), float2(-1.0f, -1.0f), float2(1.0f, -1.0f));
|
||||
|
||||
uint cflag = vflag & CNR_FLAG_RANGE;
|
||||
uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
|
||||
|
||||
bool is_inner = (vflag & INNER_FLAG) != 0u;
|
||||
|
||||
float shadow_width = rads - radsi;
|
||||
float shadow_width_top = rect.w - recti.w;
|
||||
|
||||
float rad_inner = radsi * roundCorners[cflag];
|
||||
float rad_outer = rad_inner + shadow_width;
|
||||
float radius = (is_inner) ? rad_inner : rad_outer;
|
||||
|
||||
float shadow_offset = (is_inner && (cflag > BOTTOM_RIGHT)) ? (shadow_width - shadow_width_top) :
|
||||
0.0f;
|
||||
|
||||
float2 c = center_offset[cflag];
|
||||
float2 center_outer = rad_outer * c;
|
||||
float2 center = radius * c;
|
||||
|
||||
/* First expand all vertices to the outer shadow border. */
|
||||
float2 v = rad_outer * cornervec[cflag * 9u + vofs];
|
||||
|
||||
/* Now shrink the inner vertices onto the inner rectangle.
|
||||
* At the top corners we keep the vertical offset to distribute a few of the vertices along the
|
||||
* straight part of the rectangle. This allows us to get a better falloff at the top. */
|
||||
if (is_inner && (cflag > BOTTOM_RIGHT) && (v.y < (shadow_offset - rad_outer))) {
|
||||
v.y += shadow_width_top;
|
||||
v.x = 0.0f;
|
||||
}
|
||||
else {
|
||||
v = radius * normalize(v - (center_outer + float2(0.0f, shadow_offset))) + center;
|
||||
}
|
||||
|
||||
/* Position to corner */
|
||||
float4 rct = (is_inner) ? recti : rect;
|
||||
if (cflag == BOTTOM_LEFT) {
|
||||
v += rct.xz;
|
||||
}
|
||||
else if (cflag == BOTTOM_RIGHT) {
|
||||
v += rct.yz;
|
||||
}
|
||||
else if (cflag == TOP_RIGHT) {
|
||||
v += rct.yw;
|
||||
}
|
||||
else /* (cflag == TOP_LEFT) */ {
|
||||
v += rct.xw;
|
||||
}
|
||||
|
||||
float inner_shadow_strength = min((rect.w - v.y) / rad_outer + 0.1f, 1.0f);
|
||||
shadowFalloff = (is_inner) ? inner_shadow_strength : 0.0f;
|
||||
innerMask = (is_inner) ? 0.0f : 1.0f;
|
||||
|
||||
gl_Position = ModelViewProjectionMatrix * float4(v, 0.0f, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_uniform_color_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_clipped_uniform_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
|
||||
gl_ClipDistance[0] = dot(ModelMatrix * float4(pos, 1.0f), ClipPlane);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_flat_color_infos.hh"
|
||||
|
||||
#include "gpu_shader_cfg_world_clip_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
float4 pos_4d = float4(pos, 1.0f);
|
||||
gl_Position = ModelViewProjectionMatrix * pos_4d;
|
||||
finalColor = color;
|
||||
|
||||
#ifdef USE_WORLD_CLIP_PLANES
|
||||
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * pos_4d).xyz);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_image_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_image_common)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos.xyz, 1.0f);
|
||||
texCoord_interp = texCoord;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/*
|
||||
* Vertex Shader for dashed lines with 3D coordinates,
|
||||
* with uniform multi-colors or uniform single-color, and unary thickness.
|
||||
*
|
||||
* Dashed is performed in screen space.
|
||||
*/
|
||||
|
||||
#include "infos/gpu_shader_line_dashed_uniform_color_infos.hh"
|
||||
|
||||
#include "gpu_shader_cfg_world_clip_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color_clipped)
|
||||
|
||||
void main()
|
||||
{
|
||||
float4 pos_4d = float4(pos, 1.0f);
|
||||
gl_Position = ModelViewProjectionMatrix * pos_4d;
|
||||
stipple_start = stipple_pos = viewport_size * 0.5f * (gl_Position.xy / gl_Position.w);
|
||||
#ifdef USE_WORLD_CLIP_PLANES
|
||||
world_clip_planes_calc_clip_distance((ModelMatrix * pos_4d).xyz);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_simple_lighting_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_simple_lighting)
|
||||
|
||||
void main()
|
||||
{
|
||||
normal = normalize(NormalMatrix * nor);
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_point_infos.hh"
|
||||
|
||||
#include "gpu_shader_cfg_world_clip_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_point_flat_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
float4 pos_4d = float4(pos, 1.0f);
|
||||
gl_Position = ModelViewProjectionMatrix * pos_4d;
|
||||
gl_PointSize = size;
|
||||
finalColor = color;
|
||||
|
||||
#ifdef USE_WORLD_CLIP_PLANES
|
||||
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * pos_4d).xyz);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_point_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa)
|
||||
|
||||
void main()
|
||||
{
|
||||
float4 pos_4d = float4(pos, 1.0f);
|
||||
gl_Position = ModelViewProjectionMatrix * pos_4d;
|
||||
gl_PointSize = size;
|
||||
|
||||
/* Calculate concentric radii in pixels. */
|
||||
float radius = 0.5f * size;
|
||||
|
||||
/* Start at the outside and progress toward the center. */
|
||||
radii[0] = radius;
|
||||
radii[1] = radius - 1.0f;
|
||||
|
||||
/* Convert to PointCoord units. */
|
||||
radii /= size;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_point_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_point_varying_size_varying_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
|
||||
gl_PointSize = size;
|
||||
finalColor = color;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_polyline_infos.hh"
|
||||
|
||||
#include "gpu_shader_colorspace_lib.glsl"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_polyline_uniform_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifdef CLIP
|
||||
if (clip < 0.0f) {
|
||||
gpu_discard_fragment();
|
||||
}
|
||||
#endif
|
||||
fragColor = final_color;
|
||||
if (lineSmooth) {
|
||||
fragColor.a *= clamp((lineWidth + SMOOTH_WIDTH) * 0.5f - abs(smoothline), 0.0f, 1.0f);
|
||||
}
|
||||
fragColor = blender_srgb_to_framebuffer_space(fragColor);
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_polyline_infos.hh"
|
||||
|
||||
#include "gpu_shader_attribute_load_lib.glsl"
|
||||
#include "gpu_shader_index_load_lib.glsl"
|
||||
#include "gpu_shader_math_base_lib.glsl"
|
||||
#include "gpu_shader_utildefines_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_polyline_flat_color)
|
||||
|
||||
struct VertIn {
|
||||
float3 ls_P;
|
||||
float4 final_color;
|
||||
};
|
||||
|
||||
VertIn input_assembly(uint in_vertex_id)
|
||||
{
|
||||
uint v_i = gpu_index_load(in_vertex_id);
|
||||
uint ofs = uint(gpu_vert_stride_count_offset.z);
|
||||
|
||||
VertIn vert_in;
|
||||
vert_in.ls_P = float3(0.0f, 0.0f, 0.0f);
|
||||
/* Need to support 1, 2 and 3 dimensional input (sigh). */
|
||||
vert_in.ls_P.x = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 0 + ofs];
|
||||
if (gpu_attr_0_len >= 2) {
|
||||
vert_in.ls_P.y = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 1 + ofs];
|
||||
}
|
||||
if (gpu_attr_0_len >= 3) {
|
||||
vert_in.ls_P.z = pos[gpu_attr_load_index(v_i, gpu_attr_0) + 2 + ofs];
|
||||
}
|
||||
|
||||
if (gpu_attr_0_fetch_int) {
|
||||
vert_in.ls_P = float3(floatBitsToInt(vert_in.ls_P));
|
||||
}
|
||||
#ifndef UNIFORM
|
||||
vert_in.final_color = float4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
/* Need to support 1, 2, 3 and 4 dimensional input (sigh). */
|
||||
vert_in.final_color.x = color[gpu_attr_load_index(v_i, gpu_attr_1) + 0 + ofs];
|
||||
if (gpu_attr_1_fetch_unorm8) {
|
||||
vert_in.final_color = unpackUnorm4x8(floatBitsToUint(vert_in.final_color.x));
|
||||
}
|
||||
else {
|
||||
if (gpu_attr_1_len >= 2) {
|
||||
vert_in.final_color.y = color[gpu_attr_load_index(v_i, gpu_attr_1) + 1 + ofs];
|
||||
}
|
||||
if (gpu_attr_1_len >= 3) {
|
||||
vert_in.final_color.z = color[gpu_attr_load_index(v_i, gpu_attr_1) + 2 + ofs];
|
||||
}
|
||||
if (gpu_attr_1_len >= 4) {
|
||||
vert_in.final_color.w = color[gpu_attr_load_index(v_i, gpu_attr_1) + 3 + ofs];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return vert_in;
|
||||
}
|
||||
|
||||
struct VertOut {
|
||||
float4 gpu_position;
|
||||
float4 final_color;
|
||||
float clip;
|
||||
};
|
||||
|
||||
VertOut vertex_main(VertIn vert_in)
|
||||
{
|
||||
VertOut vert_out;
|
||||
vert_out.gpu_position = ModelViewProjectionMatrix * float4(vert_in.ls_P, 1.0f);
|
||||
#ifndef UNIFORM
|
||||
vert_out.final_color = vert_in.final_color;
|
||||
#endif
|
||||
#ifdef CLIP
|
||||
vert_out.clip = dot(ModelMatrix * float4(vert_in.ls_P, 1.0f), ClipPlane);
|
||||
#endif
|
||||
return vert_out;
|
||||
}
|
||||
|
||||
/* Clips point to near clip plane before perspective divide. */
|
||||
float4 clip_line_point_homogeneous_space(float4 p, float4 q)
|
||||
{
|
||||
if (p.z < -p.w) {
|
||||
/* Just solves p + (q - p) * A; for A when p.z / p.w = -1.0f. */
|
||||
float denom = q.z - p.z + q.w - p.w;
|
||||
if (denom == 0.0f) {
|
||||
/* No solution. */
|
||||
return p;
|
||||
}
|
||||
float A = (-p.z - p.w) / denom;
|
||||
p = p + (q - p) * A;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
struct GeomOut {
|
||||
float4 gpu_position;
|
||||
float4 final_color;
|
||||
float clip;
|
||||
float smoothline;
|
||||
};
|
||||
|
||||
void export_vertex(GeomOut geom_out)
|
||||
{
|
||||
gl_Position = geom_out.gpu_position;
|
||||
final_color = geom_out.final_color;
|
||||
smoothline = geom_out.smoothline;
|
||||
clip = geom_out.clip;
|
||||
}
|
||||
|
||||
void strip_EmitVertex(const uint strip_index,
|
||||
uint out_vertex_id,
|
||||
uint out_primitive_id,
|
||||
GeomOut geom_out)
|
||||
{
|
||||
bool is_odd_primitive = (out_primitive_id & 1u) != 0u;
|
||||
/* Maps triangle list primitives to triangle strip indices. */
|
||||
uint out_strip_index = (is_odd_primitive ? (2u - out_vertex_id) : out_vertex_id) +
|
||||
out_primitive_id;
|
||||
|
||||
if (out_strip_index == strip_index) {
|
||||
export_vertex(geom_out);
|
||||
}
|
||||
}
|
||||
|
||||
void do_vertex(const uint i,
|
||||
uint out_vertex_id,
|
||||
uint out_primitive_id,
|
||||
VertOut geom_in[2],
|
||||
float4 position,
|
||||
float2 ofs)
|
||||
{
|
||||
GeomOut geom_out;
|
||||
#if defined(UNIFORM)
|
||||
geom_out.final_color = color;
|
||||
|
||||
#elif defined(FLAT)
|
||||
/* WATCH: Assuming last provoking vertex. */
|
||||
geom_out.final_color = geom_in[1].final_color;
|
||||
|
||||
#elif defined(SMOOTH)
|
||||
geom_out.final_color = geom_in[i].final_color;
|
||||
#endif
|
||||
|
||||
#ifdef CLIP
|
||||
geom_out.clip = geom_in[i].clip;
|
||||
#endif
|
||||
|
||||
geom_out.smoothline = (lineWidth + SMOOTH_WIDTH * float(lineSmooth)) * 0.5f;
|
||||
geom_out.gpu_position = position;
|
||||
geom_out.gpu_position.xy += ofs * position.w;
|
||||
strip_EmitVertex(i * 2u + 0u, out_vertex_id, out_primitive_id, geom_out);
|
||||
|
||||
geom_out.smoothline = -(lineWidth + SMOOTH_WIDTH * float(lineSmooth)) * 0.5f;
|
||||
geom_out.gpu_position = position;
|
||||
geom_out.gpu_position.xy -= ofs * position.w;
|
||||
strip_EmitVertex(i * 2u + 1u, out_vertex_id, out_primitive_id, geom_out);
|
||||
}
|
||||
|
||||
void geometry_main(VertOut geom_in[2],
|
||||
uint out_vertex_id,
|
||||
uint out_primitive_id,
|
||||
uint /*out_invocation_id*/)
|
||||
{
|
||||
float4 p0 = clip_line_point_homogeneous_space(geom_in[0].gpu_position, geom_in[1].gpu_position);
|
||||
float4 p1 = clip_line_point_homogeneous_space(geom_in[1].gpu_position, geom_in[0].gpu_position);
|
||||
float2 e = normalize(((p1.xy / p1.w) - (p0.xy / p0.w)) * viewportSize.xy);
|
||||
|
||||
#if 0 /* Hard turn when line direction changes quadrant. */
|
||||
e = abs(e);
|
||||
float2 ofs = (e.x > e.y) ? float2(0.0f, 1.0f / e.x) : float2(1.0f / e.y, 0.0f);
|
||||
#else /* Use perpendicular direction. */
|
||||
float2 ofs = float2(-e.y, e.x);
|
||||
#endif
|
||||
ofs /= viewportSize.xy;
|
||||
ofs *= lineWidth + SMOOTH_WIDTH * float(lineSmooth);
|
||||
|
||||
do_vertex(0u, out_vertex_id, out_primitive_id, geom_in, p0, ofs);
|
||||
do_vertex(1u, out_vertex_id, out_primitive_id, geom_in, p1, ofs);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Line list primitive. */
|
||||
uint input_primitive_vertex_count = uint(gpu_vert_stride_count_offset.x);
|
||||
/* Triangle list primitive (emulating triangle strip). */
|
||||
constexpr uint output_primitive_vertex_count = 3u;
|
||||
constexpr uint output_primitive_count = 2u;
|
||||
constexpr uint output_invocation_count = 1u;
|
||||
constexpr uint output_vertex_count_per_invocation = output_primitive_count *
|
||||
output_primitive_vertex_count;
|
||||
constexpr uint output_vertex_count_per_input_primitive = output_vertex_count_per_invocation *
|
||||
output_invocation_count;
|
||||
|
||||
uint in_primitive_id = uint(gl_VertexID) / output_vertex_count_per_input_primitive;
|
||||
uint in_primitive_first_vertex = in_primitive_id * input_primitive_vertex_count;
|
||||
|
||||
uint out_vertex_id = uint(gl_VertexID) % output_primitive_vertex_count;
|
||||
uint out_primitive_id = (uint(gl_VertexID) / output_primitive_vertex_count) %
|
||||
output_primitive_count;
|
||||
uint out_invocation_id = (uint(gl_VertexID) / output_vertex_count_per_invocation) %
|
||||
output_invocation_count;
|
||||
/* Used to wrap around for the line loop case. */
|
||||
uint input_total_vertex_count = uint(gpu_vert_stride_count_offset.y);
|
||||
|
||||
VertIn vert_in[2];
|
||||
vert_in[0] = input_assembly(in_primitive_first_vertex + 0u);
|
||||
vert_in[1] = input_assembly((in_primitive_first_vertex + 1u) % input_total_vertex_count);
|
||||
|
||||
VertOut vert_out[2];
|
||||
vert_out[0] = vertex_main(vert_in[0]);
|
||||
vert_out[1] = vertex_main(vert_in[1]);
|
||||
|
||||
/* Discard by default. */
|
||||
gl_Position = float4(NAN_FLT);
|
||||
geometry_main(vert_out, out_vertex_id, out_primitive_id, out_invocation_id);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_smooth_color_infos.hh"
|
||||
|
||||
#include "gpu_shader_colorspace_lib.glsl"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = finalColor;
|
||||
fragColor = blender_srgb_to_framebuffer_space(fragColor);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_smooth_color_infos.hh"
|
||||
|
||||
#include "gpu_shader_cfg_world_clip_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
|
||||
finalColor = color;
|
||||
|
||||
#ifdef USE_WORLD_CLIP_PLANES
|
||||
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * float4(pos, 1.0f)).xyz);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_depth_only_infos.hh"
|
||||
|
||||
#include "gpu_shader_cfg_world_clip_lib.glsl"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_3D_depth_only)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * float4(pos, 1.0f);
|
||||
|
||||
#ifdef USE_WORLD_CLIP_PLANES
|
||||
world_clip_planes_calc_clip_distance((clipPlanes.ClipModelMatrix * float4(pos, 1.0f)).xyz);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infos/gpu_clip_planes_infos.hh"
|
||||
|
||||
#ifdef GPU_FRAGMENT_SHADER
|
||||
# error File should not be included in fragment shader
|
||||
#endif
|
||||
|
||||
#ifdef USE_WORLD_CLIP_PLANES
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_clip_planes)
|
||||
|
||||
void world_clip_planes_calc_clip_distance(float3 wpos)
|
||||
{
|
||||
float4 pos = float4(wpos, 1.0f);
|
||||
|
||||
gl_ClipDistance[0] = dot(clipPlanes.world[0], pos);
|
||||
gl_ClipDistance[1] = dot(clipPlanes.world[1], pos);
|
||||
gl_ClipDistance[2] = dot(clipPlanes.world[2], pos);
|
||||
gl_ClipDistance[3] = dot(clipPlanes.world[3], pos);
|
||||
gl_ClipDistance[4] = dot(clipPlanes.world[4], pos);
|
||||
gl_ClipDistance[5] = dot(clipPlanes.world[5], pos);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_checker_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_checker)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 phase = mod(gl_FragCoord.xy, (size * 2));
|
||||
|
||||
if ((phase.x > size && phase.y < size) || (phase.x < size && phase.y > size)) {
|
||||
fragColor = color1;
|
||||
}
|
||||
else {
|
||||
fragColor = color2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
float3 calc_barycentric_distances(float3 pos0, float3 pos1, float3 pos2)
|
||||
{
|
||||
float3 edge21 = pos2 - pos1;
|
||||
float3 edge10 = pos1 - pos0;
|
||||
float3 edge02 = pos0 - pos2;
|
||||
float3 d21 = normalize(edge21);
|
||||
float3 d10 = normalize(edge10);
|
||||
float3 d02 = normalize(edge02);
|
||||
|
||||
float3 dists;
|
||||
float d = dot(d21, edge02);
|
||||
dists.x = sqrt(dot(edge02, edge02) - d * d);
|
||||
d = dot(d02, edge10);
|
||||
dists.y = sqrt(dot(edge10, edge10) - d * d);
|
||||
d = dot(d10, edge21);
|
||||
dists.z = sqrt(dot(edge21, edge21) - d * d);
|
||||
return dists;
|
||||
}
|
||||
|
||||
float2 calc_barycentric_co(int vertid)
|
||||
{
|
||||
float2 bary;
|
||||
bary.x = float((vertid % 3) == 0);
|
||||
bary.y = float((vertid % 3) == 1);
|
||||
return bary;
|
||||
}
|
||||
|
||||
/* Assumes GPU_VEC4 is color data, special case that needs luminance coefficients from OCIO. */
|
||||
#define float_from_float4(v, luminance_coefficients) dot(v.rgb, luminance_coefficients)
|
||||
#define float_from_float3(v) ((v.r + v.g + v.b) * (1.0f / 3.0f))
|
||||
#define float_from_float2(v) ((v.x + v.y) * (1.0f / 2.0f))
|
||||
|
||||
#define float2_from_float4(v) v.xy
|
||||
#define float2_from_float3(v) v.xy
|
||||
#define float2_from_float(v) float2(v)
|
||||
|
||||
#define float3_from_float4(v) v.rgb
|
||||
#define float3_from_float2(v) float3(v.xy, 0.0f)
|
||||
#define float3_from_float(v) float3(v)
|
||||
|
||||
#define float4_from_float3(v) float4(v, 1.0f)
|
||||
#define float4_from_float2(v) float4(v.xy, 0.0f, 1.0f)
|
||||
#define float4_from_float(v) float4(float3(v), 1.0f)
|
||||
|
||||
#ifdef GPU_FRAGMENT_SHADER
|
||||
# define FrontFacing gl_FrontFacing
|
||||
#else
|
||||
# define FrontFacing true
|
||||
#endif
|
||||
|
||||
enum ClosureType : uchar {
|
||||
CLOSURE_NONE_ID = 0u,
|
||||
/* Diffuse */
|
||||
CLOSURE_BSDF_DIFFUSE_ID = 1u,
|
||||
// CLOSURE_BSDF_OREN_NAYAR_ID = 2u, /* TODO */
|
||||
// CLOSURE_BSDF_SHEEN_ID = 4u, /* TODO */
|
||||
// CLOSURE_BSDF_DIFFUSE_TOON_ID = 5u, /* TODO */
|
||||
CLOSURE_BSDF_TRANSLUCENT_ID = 6u,
|
||||
|
||||
/* Glossy */
|
||||
CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID = 7u,
|
||||
// CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID = 8u, /* TODO */
|
||||
// CLOSURE_BSDF_ASHIKHMIN_VELVET_ID = 9u, /* TODO */
|
||||
// CLOSURE_BSDF_GLOSSY_TOON_ID = 10u, /* TODO */
|
||||
// CLOSURE_BSDF_HAIR_REFLECTION_ID = 11u, /* TODO */
|
||||
|
||||
/* Transmission */
|
||||
CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID = 12u,
|
||||
CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID = 13u,
|
||||
|
||||
/* Glass */
|
||||
// CLOSURE_BSDF_HAIR_HUANG_ID = 14u, /* TODO */
|
||||
|
||||
/* BSSRDF */
|
||||
CLOSURE_BSSRDF_BURLEY_ID = 15u,
|
||||
};
|
||||
|
||||
struct ClosureUndetermined {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
ClosureType type;
|
||||
/* Additional data different for each closure type. */
|
||||
packed_float4 data;
|
||||
};
|
||||
|
||||
bool closure_has_transmission(const ClosureType closure)
|
||||
{
|
||||
return closure == CLOSURE_BSDF_TRANSLUCENT_ID ||
|
||||
closure == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID ||
|
||||
closure == CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID;
|
||||
}
|
||||
|
||||
ClosureUndetermined closure_new(ClosureType type)
|
||||
{
|
||||
ClosureUndetermined cl;
|
||||
cl.type = type;
|
||||
return cl;
|
||||
}
|
||||
|
||||
struct ClosureOcclusion {
|
||||
packed_float3 N;
|
||||
};
|
||||
|
||||
struct ClosureDiffuse {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
};
|
||||
|
||||
struct ClosureSubsurface {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
packed_float3 sss_radius;
|
||||
};
|
||||
|
||||
struct ClosureTranslucent {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
};
|
||||
|
||||
struct ClosureReflection {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
float roughness;
|
||||
};
|
||||
|
||||
struct ClosureRefraction {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
float roughness;
|
||||
float ior;
|
||||
};
|
||||
|
||||
struct ClosureHair {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 T;
|
||||
float offset;
|
||||
packed_float2 roughness;
|
||||
};
|
||||
|
||||
struct ClosureVolumeScatter {
|
||||
packed_float3 scattering;
|
||||
float weight;
|
||||
float anisotropy;
|
||||
};
|
||||
|
||||
struct ClosureVolumeAbsorption {
|
||||
packed_float3 absorption;
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct ClosureEmission {
|
||||
packed_float3 emission;
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct ClosureTransparency {
|
||||
packed_float3 transmittance;
|
||||
float weight;
|
||||
float holdout;
|
||||
};
|
||||
|
||||
struct ClosureThinRefraction {
|
||||
packed_float3 color;
|
||||
float weight;
|
||||
packed_float3 N;
|
||||
float roughness;
|
||||
};
|
||||
|
||||
ClosureDiffuse to_closure_diffuse(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureDiffuse closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
return closure;
|
||||
}
|
||||
|
||||
ClosureSubsurface to_closure_subsurface(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureSubsurface closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
closure.sss_radius = cl.data.xyz;
|
||||
return closure;
|
||||
}
|
||||
|
||||
ClosureTranslucent to_closure_translucent(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureTranslucent closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
return closure;
|
||||
}
|
||||
|
||||
ClosureReflection to_closure_reflection(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureReflection closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
closure.roughness = cl.data.x;
|
||||
return closure;
|
||||
}
|
||||
|
||||
ClosureRefraction to_closure_refraction(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureRefraction closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
closure.roughness = cl.data.x;
|
||||
closure.ior = cl.data.y;
|
||||
return closure;
|
||||
}
|
||||
|
||||
ClosureThinRefraction to_closure_thin_refraction(ClosureUndetermined cl)
|
||||
{
|
||||
ClosureThinRefraction closure;
|
||||
closure.N = cl.N;
|
||||
closure.color = cl.color;
|
||||
closure.roughness = cl.data.x;
|
||||
return closure;
|
||||
}
|
||||
|
||||
struct GlobalData {
|
||||
/** World position. */
|
||||
packed_float3 P;
|
||||
/** Surface Normal. Normalized, overridden by bump displacement. */
|
||||
packed_float3 N;
|
||||
/** Raw interpolated normal (non-normalized) data. */
|
||||
packed_float3 Ni;
|
||||
/** Geometric Normal. */
|
||||
packed_float3 Ng;
|
||||
/** Curve Tangent Space. */
|
||||
packed_float3 curve_T, curve_B, curve_N;
|
||||
/** Barycentric coordinates. */
|
||||
packed_float2 barycentric_coords;
|
||||
packed_float3 barycentric_dists;
|
||||
/** Hair thickness in world space. */
|
||||
float hair_diameter;
|
||||
/** Index of the strand for per strand effects. */
|
||||
int hair_strand_id;
|
||||
/** Ray properties (approximation). */
|
||||
float ray_depth;
|
||||
float ray_length;
|
||||
uchar ray_type;
|
||||
/** Is hair. */
|
||||
bool is_strand;
|
||||
};
|
||||
|
||||
GlobalData g_data;
|
||||
|
||||
#ifndef GPU_FRAGMENT_SHADER
|
||||
/* Stubs. */
|
||||
|
||||
# define dF_impl(a) (float3(0.0f))
|
||||
# define dF_branch(a, b, c) (c = float2(0.0f))
|
||||
# define dF_branch_incomplete(a, b, c) (c = float2(0.0f))
|
||||
|
||||
#elif defined(GPU_FAST_DERIVATIVE) /* TODO(@fclem): User Option? */
|
||||
/* Fast derivatives */
|
||||
float3 dF_impl(float3 v)
|
||||
{
|
||||
return float3(0.0f);
|
||||
}
|
||||
|
||||
void dF_branch(float fn, float2 &result)
|
||||
{
|
||||
/* NOTE: this function is currently unused, once it is used we need to check if
|
||||
* `g_derivative_filter_width` needs to be applied. */
|
||||
result.x = gpu_dfdx(fn) * derivative_scale_get();
|
||||
result.y = gpu_dfdy(fn) * derivative_scale_get();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Offset of coordinates for evaluating bump node. Unit in pixel. */
|
||||
float g_derivative_filter_width = 0.0f;
|
||||
/* Precise derivatives */
|
||||
int g_derivative_flag = 0;
|
||||
|
||||
float3 dF_impl(float3 v)
|
||||
{
|
||||
if (g_derivative_flag > 0) {
|
||||
return gpu_dfdx(v) * g_derivative_filter_width;
|
||||
}
|
||||
else if (g_derivative_flag < 0) {
|
||||
return gpu_dfdy(v) * g_derivative_filter_width;
|
||||
}
|
||||
return float3(0.0f);
|
||||
}
|
||||
|
||||
# define dF_branch(fn, filter_width, result) \
|
||||
if (true) { \
|
||||
g_derivative_filter_width = filter_width * derivative_scale_get(); \
|
||||
g_derivative_flag = 1; \
|
||||
result.x = (fn); \
|
||||
g_derivative_flag = -1; \
|
||||
result.y = (fn); \
|
||||
g_derivative_flag = 0; \
|
||||
result -= float2((fn)); \
|
||||
}
|
||||
|
||||
/* Used when the non-offset value is already computed elsewhere */
|
||||
# define dF_branch_incomplete(fn, filter_width, result) \
|
||||
if (true) { \
|
||||
g_derivative_filter_width = filter_width * derivative_scale_get(); \
|
||||
g_derivative_flag = 1; \
|
||||
result.x = (fn); \
|
||||
g_derivative_flag = -1; \
|
||||
result.y = (fn); \
|
||||
g_derivative_flag = 0; \
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infos/gpu_srgb_to_framebuffer_space_infos.hh"
|
||||
|
||||
SHADER_LIBRARY_CREATE_INFO(gpu_srgb_to_framebuffer_space)
|
||||
|
||||
/* Undefine the macro that avoids compilation errors. */
|
||||
#undef blender_srgb_to_framebuffer_space
|
||||
|
||||
/**
|
||||
* Input is Rec.709 sRGB.
|
||||
* Output is Rec.709 linear if hardware will add a Linear to sRGB conversion, NOOP otherwise.
|
||||
* NOTE: Old naming convention, but avoids breaking compatibility for python shaders.
|
||||
*
|
||||
* As per GPU API design, all frame-buffers with SRGBA_8_8_8_8 attachments will always enable SRGB
|
||||
* rendering. In this mode the shader output is expected to be in a linear color space. This allows
|
||||
* to do the blending stage with linear values (more correct) and then store the result in 8bpc
|
||||
* keeping accurate colors.
|
||||
*
|
||||
* To ensure consistent result (blending excluded) between a frame-buffer using SRGBA_8_8_8_8 and
|
||||
* one using RGBA_8_8_8_8, we need to do the sRGB > linear conversion to counteract the hardware
|
||||
* encoding during frame-buffer output.
|
||||
*
|
||||
* For reference: https://wikis.khronos.org/opengl/framebuffer#Colorspace
|
||||
*/
|
||||
float4 blender_srgb_to_framebuffer_space(float4 srgb_color)
|
||||
{
|
||||
/**
|
||||
* IMPORTANT: srgbTarget denote that the output is expected to be in __linear__ space.
|
||||
* https://wikis.khronos.org/opengl/framebuffer#Colorspace
|
||||
*/
|
||||
if (!srgbTarget) {
|
||||
/* Input should already be in sRGB. */
|
||||
return srgb_color;
|
||||
}
|
||||
/* Note that this is simply counteracting the hardware Linear > sRGB conversion. */
|
||||
float3 c = max(srgb_color.rgb, float3(0.0f));
|
||||
float3 c1 = c * (1.0f / 12.92f);
|
||||
float3 c2 = pow((c + 0.055f) * (1.0f / 1.055f), float3(2.4f));
|
||||
float4 linear_color;
|
||||
linear_color.rgb = mix(c1, c2, step(float3(0.04045f), c));
|
||||
linear_color.a = srgb_color.a;
|
||||
return linear_color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input is Rec.709 sRGB.
|
||||
* Output is Rec.709 linear if hardware will add a Linear to sRGB conversion, NOOP otherwise.
|
||||
*/
|
||||
float4 blender_rec709_srgb_to_output_space(float4 srgb_color)
|
||||
{
|
||||
return blender_srgb_to_framebuffer_space(srgb_color);
|
||||
}
|
||||
|
||||
/* Input is Blender Scene Linear. Output is Rec.709 sRGB. */
|
||||
float4 blender_scene_linear_to_rec709_srgb(float3x3 scene_linear_to_rec709,
|
||||
float4 scene_linear_color)
|
||||
{
|
||||
float3 rec709_linear = scene_linear_to_rec709 * scene_linear_color.rgb;
|
||||
|
||||
/* TODO(fclem): For wide gamut (extended sRGB), we need to encode negative values in a certain
|
||||
* way here. */
|
||||
|
||||
/* Linear to sRGB transform. */
|
||||
float3 c = max(rec709_linear, float3(0.0f));
|
||||
float3 c1 = c * 12.92f;
|
||||
float3 c2 = 1.055f * pow(c, float3(1.0f / 2.4f)) - 0.055f;
|
||||
float4 srgb_color;
|
||||
srgb_color.rgb = mix(c1, c2, step(float3(0.0031308f), c));
|
||||
srgb_color.a = scene_linear_color.a;
|
||||
return srgb_color;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Implementation of basic shading language functions (types, builtin functions, etc...).
|
||||
*
|
||||
* Each backend will replace it with its own implementation at runtime.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
/* This file must replaced at runtime. The following content is only a possible implementation. */
|
||||
#pragma runtime_generated
|
||||
|
||||
#include "gpu_shader_compat_cxx.hh" // IWYU pragma: export
|
||||
/* Other possible implementation. */
|
||||
// #include "gpu_shader_compat_glsl.hh"
|
||||
// #include "gpu_shader_compat_msl.hh"
|
||||
@@ -0,0 +1,305 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Shading language to C++ stubs.
|
||||
*
|
||||
* The goal of this header is to make the Shading Language source file compile using a modern C++
|
||||
* compiler. This allows for linting and IDE functionalities to work.
|
||||
*
|
||||
* This file can be included inside any Shading Language file to make the Shading Language syntax
|
||||
* to work. Then your IDE must to be configured to associate `.glsl` files to C++ so that the C++
|
||||
* linter does the analysis.
|
||||
*
|
||||
* This is why the implementation of each function is not needed. However, we make sure that type
|
||||
* casting is always explicit. This is because implicit casts are not always supported on all
|
||||
* implementations.
|
||||
*
|
||||
* Some of the features of Shading Language are omitted by design. They are either:
|
||||
* - Not needed (e.g. per component matrix multiplication).
|
||||
* - Against our code-style (e.g. `stpq` swizzle).
|
||||
* - Unsupported by our Metal Shading Language layer (e.g. mixed vector-scalar matrix constructor).
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio> // IWYU pragma: export printf
|
||||
|
||||
#include "gpu_shader_cxx_builtin.hh" // IWYU pragma: export
|
||||
#include "gpu_shader_cxx_global.hh" // IWYU pragma: export
|
||||
#include "gpu_shader_cxx_image.hh" // IWYU pragma: export
|
||||
#include "gpu_shader_cxx_matrix.hh" // IWYU pragma: export
|
||||
#include "gpu_shader_cxx_sampler.hh" // IWYU pragma: export
|
||||
#include "gpu_shader_cxx_string.hh" // IWYU pragma: export
|
||||
#include "gpu_shader_cxx_vector.hh" // IWYU pragma: export
|
||||
|
||||
#define assert(assertion)
|
||||
|
||||
#include "gpu_shader_cxx_attribute.hh" // IWYU pragma: export
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Keywords
|
||||
* \{ */
|
||||
|
||||
/* Decorate a variable in global scope that is common to all threads in a thread-group. */
|
||||
#define shared
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compatibility
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Member hiding type.
|
||||
* Wrapper type for members of unions in host shared structure.
|
||||
* This is needed to force the accessor syntax in the shader code.
|
||||
*/
|
||||
template<typename T> struct union_t {
|
||||
char bytes[sizeof(T)];
|
||||
|
||||
const T &operator()() const
|
||||
{
|
||||
return *reinterpret_cast<const T *>(&bytes);
|
||||
}
|
||||
|
||||
T &operator()()
|
||||
{
|
||||
return *reinterpret_cast<T *>(&bytes);
|
||||
}
|
||||
};
|
||||
|
||||
/* Array syntax compatibility. */
|
||||
/* clang-format off */
|
||||
#define float_array(...) { __VA_ARGS__ }
|
||||
#define float2_array(...) { __VA_ARGS__ }
|
||||
#define float3_array(...) { __VA_ARGS__ }
|
||||
#define float4_array(...) { __VA_ARGS__ }
|
||||
#define int_array(...) { __VA_ARGS__ }
|
||||
#define int2_array(...) { __VA_ARGS__ }
|
||||
#define int3_array(...) { __VA_ARGS__ }
|
||||
#define int4_array(...) { __VA_ARGS__ }
|
||||
#define uint_array(...) { __VA_ARGS__ }
|
||||
#define uint2_array(...) { __VA_ARGS__ }
|
||||
#define uint3_array(...) { __VA_ARGS__ }
|
||||
#define uint4_array(...) { __VA_ARGS__ }
|
||||
#define bool_array(...) { __VA_ARGS__ }
|
||||
#define bool2_array(...) { __VA_ARGS__ }
|
||||
#define bool3_array(...) { __VA_ARGS__ }
|
||||
#define bool4_array(...) { __VA_ARGS__ }
|
||||
/* clang-format on */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* Use to suppress `-Wimplicit-fallthrough` (in place of `break`). */
|
||||
#ifndef ATTR_FALLTHROUGH
|
||||
# ifdef __GNUC__
|
||||
# define ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
# else
|
||||
# define ATTR_FALLTHROUGH ((void)0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* GLSL main function must return void. C++ need to return int.
|
||||
* Inject real main (C++) inside the GLSL main definition. */
|
||||
#define main() \
|
||||
/* Fake main prototype. */ \
|
||||
/* void */ _fake_main(); \
|
||||
/* Real main. */ \
|
||||
int main() \
|
||||
{ \
|
||||
_fake_main(); \
|
||||
return 0; \
|
||||
} \
|
||||
/* Fake main definition. */ \
|
||||
void _fake_main()
|
||||
|
||||
#define GLSL_CPP_STUBS
|
||||
#ifndef GPU_SHADER
|
||||
# define GPU_SHADER
|
||||
#endif
|
||||
|
||||
/* Reserved keywords in GLSL that are allowed in preprocessor directives for compiling in C++. */
|
||||
#define sizeof static_assert(false, "sizeof is a reserved keyword")
|
||||
|
||||
#ifdef GPU_SHADER_LIBRARY
|
||||
# define GPU_VERTEX_SHADER
|
||||
# define GPU_FRAGMENT_SHADER
|
||||
# define GPU_COMPUTE_SHADER
|
||||
#endif
|
||||
|
||||
/* Resource accessor. */
|
||||
#define specialization_constant_get(create_info, _res) create_info::_res
|
||||
#define shared_variable_get(create_info, _res) create_info::_res
|
||||
#define push_constant_get(create_info, _res) create_info::_res
|
||||
#define interface_get(create_info, _res) create_info::_res
|
||||
#define attribute_get(create_info, _res) create_info::_res
|
||||
#define buffer_get(create_info, _res) create_info::_res
|
||||
#define sampler_get(create_info, _res) create_info::_res
|
||||
#define image_get(create_info, _res) create_info::_res
|
||||
#define srt_access(create_info, _res) create_info::_res
|
||||
/**
|
||||
* WORKAROUND(fclem): Only used for cases when passing down the resource_table is impractical.
|
||||
* Note that this placeholder is just for the code to compile.
|
||||
*/
|
||||
#define resource_table_get(table_type) (*(table_type *)(new char[1024 * 16]))
|
||||
|
||||
/**
|
||||
* Member hiding type.
|
||||
* Allows to declare fake references to Shader Resource Tables.
|
||||
* This make sure we cannot directly reference them.
|
||||
* This is just a safety measure for our fragile SRT implementation which cannot safely directly
|
||||
* access SRT members that are more that 1 level deep.
|
||||
* This should only be used in SRT struct member declaration for wrapping other SRT types.
|
||||
*/
|
||||
template<typename T> struct srt_t {
|
||||
operator const T &() const
|
||||
{
|
||||
return *reinterpret_cast<const T *>(this);
|
||||
}
|
||||
|
||||
operator T &()
|
||||
{
|
||||
return *reinterpret_cast<T *>(this);
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderCreateInfo {};
|
||||
|
||||
struct NoConstants {};
|
||||
|
||||
template<typename VertFn,
|
||||
typename FragFn,
|
||||
typename ConstT1 = NoConstants,
|
||||
typename ConstT2 = NoConstants,
|
||||
typename ConstT3 = NoConstants>
|
||||
struct PipelineGraphic {
|
||||
VertFn vert;
|
||||
FragFn frag;
|
||||
/* Constant values. */
|
||||
ConstT1 c1;
|
||||
ConstT2 c2;
|
||||
ConstT3 c3;
|
||||
|
||||
PipelineGraphic(VertFn vert, FragFn frag) : vert(vert), frag(frag), c1({}), c2({}), c3({}) {}
|
||||
PipelineGraphic(VertFn vert, FragFn frag, ConstT1 c1)
|
||||
: vert(vert), frag(frag), c1(c1), c2({}), c3({})
|
||||
{
|
||||
}
|
||||
PipelineGraphic(VertFn vert, FragFn frag, ConstT1 c1, ConstT2 c2)
|
||||
: vert(vert), frag(frag), c1(c1), c2(c2), c3({})
|
||||
{
|
||||
}
|
||||
PipelineGraphic(VertFn vert, FragFn frag, ConstT1 c1, ConstT2 c2, ConstT3 c3)
|
||||
: vert(vert), frag(frag), c1(c1), c2(c2), c3(c3)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* For assert support. */
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
# define GPU_THREAD uint3(0)
|
||||
#elif defined(GPU_FRAGMENT_SHADER)
|
||||
# define GPU_THREAD uint3(0)
|
||||
#elif defined(GPU_COMPUTE_SHADER)
|
||||
# define GPU_THREAD uint3(0)
|
||||
#else
|
||||
# define GPU_THREAD error_not_in_a_shader_question_mark
|
||||
#endif
|
||||
|
||||
template<typename CompFn,
|
||||
typename ConstT1 = NoConstants,
|
||||
typename ConstT2 = NoConstants,
|
||||
typename ConstT3 = NoConstants>
|
||||
struct PipelineCompute {
|
||||
CompFn comp;
|
||||
/* Constant values. */
|
||||
ConstT1 c1;
|
||||
ConstT2 c2;
|
||||
ConstT3 c3;
|
||||
|
||||
PipelineCompute(CompFn comp) : comp(comp), c1({}), c2({}), c3({}) {}
|
||||
PipelineCompute(CompFn comp, ConstT1 c1) : comp(comp), c1(c1), c2({}), c3({}) {}
|
||||
PipelineCompute(CompFn comp, ConstT1 c1, ConstT2 c2) : comp(comp), c1(c1), c2(c2), c3({}) {}
|
||||
PipelineCompute(CompFn comp, ConstT1 c1, ConstT2 c2, ConstT3 c3)
|
||||
: comp(comp), c1(c1), c2(c2), c3(c3)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Enums
|
||||
*
|
||||
* Enums should be defined in the root namespace when used directly in the pipeline, as they will
|
||||
* not be fully qualified when generating the template name substitution. Defining in the root
|
||||
* works around this limitation
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* TextureWriteFormat.
|
||||
*
|
||||
* We can not use GPU_TEXTURE_WRITE_FORMAT_EXPAND as other parts are included that will intervene
|
||||
* with the compatibility defines.
|
||||
*/
|
||||
enum TextureWriteFormat : uint32_t {
|
||||
SNORM_8,
|
||||
SNORM_8_8,
|
||||
SNORM_8_8_8_8,
|
||||
|
||||
SNORM_16,
|
||||
SNORM_16_16,
|
||||
SNORM_16_16_16_16,
|
||||
|
||||
UNORM_8,
|
||||
UNORM_8_8,
|
||||
UNORM_8_8_8_8,
|
||||
|
||||
UNORM_16,
|
||||
UNORM_16_16,
|
||||
UNORM_16_16_16_16,
|
||||
|
||||
SINT_8,
|
||||
SINT_8_8,
|
||||
SINT_8_8_8_8,
|
||||
|
||||
SINT_16,
|
||||
SINT_16_16,
|
||||
SINT_16_16_16_16,
|
||||
|
||||
SINT_32,
|
||||
SINT_32_32,
|
||||
SINT_32_32_32_32,
|
||||
|
||||
UINT_8,
|
||||
UINT_8_8,
|
||||
UINT_8_8_8_8,
|
||||
|
||||
UINT_16,
|
||||
UINT_16_16,
|
||||
UINT_16_16_16_16,
|
||||
|
||||
UINT_32,
|
||||
UINT_32_32,
|
||||
UINT_32_32_32_32,
|
||||
|
||||
SFLOAT_16,
|
||||
SFLOAT_16_16,
|
||||
SFLOAT_16_16_16_16,
|
||||
|
||||
SFLOAT_32,
|
||||
SFLOAT_32_32,
|
||||
SFLOAT_32_32_32_32,
|
||||
|
||||
UNORM_10_10_10_2,
|
||||
UINT_10_10_10_2,
|
||||
|
||||
UFLOAT_11_11_10,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,252 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
/* Do not apply C++ processing to this file. */
|
||||
#pragma no_processing
|
||||
|
||||
#include "gpu_shader_glsl_extension.glsl"
|
||||
|
||||
/** Type aliases. */
|
||||
/** IMPORTANT: Be wary of size and alignment matching for types that are present
|
||||
* in C++ shared code. */
|
||||
|
||||
/* Matrix reshaping functions. Needs to be declared before matrix type aliases. */
|
||||
#define RESHAPE(name, mat_to, mat_from) \
|
||||
mat_to to_##name(mat_from m) \
|
||||
{ \
|
||||
return mat_to(m); \
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
RESHAPE(float2x2, mat2x2, mat3x3)
|
||||
RESHAPE(float2x2, mat2x2, mat4x4)
|
||||
RESHAPE(float3x3, mat3x3, mat4x4)
|
||||
RESHAPE(float3x3, mat3x3, mat2x2)
|
||||
RESHAPE(float4x4, mat4x4, mat2x2)
|
||||
RESHAPE(float4x4, mat4x4, mat3x3)
|
||||
/* clang-format on */
|
||||
/* TODO(fclem): Remove. Use Transform instead. */
|
||||
RESHAPE(float3x3, mat3x3, mat3x4)
|
||||
|
||||
#undef RESHAPE
|
||||
|
||||
/* constexpr is equivalent to const in GLSL + special chaining rules.
|
||||
* See "GLSL Specification section 4.3.3. Constant Expressions". */
|
||||
#define constexpr const
|
||||
|
||||
/* Boolean in GLSL are 32bit in interface structs. */
|
||||
#define bool32_t bool
|
||||
#define bool2 bvec2
|
||||
#define bool3 bvec3
|
||||
#define bool4 bvec4
|
||||
|
||||
#define float2 vec2
|
||||
#define float3 vec3
|
||||
#define float4 vec4
|
||||
#define int2 ivec2
|
||||
#define int3 ivec3
|
||||
#define int4 ivec4
|
||||
#define uint2 uvec2
|
||||
#define uint3 uvec3
|
||||
#define uint4 uvec4
|
||||
/* GLSL already follows the packed alignment / size rules for vec3. */
|
||||
#define packed_float2 float2
|
||||
#define packed_int2 int2
|
||||
#define packed_uint2 uint2
|
||||
#define packed_float3 float3
|
||||
#define packed_int3 int3
|
||||
#define packed_uint3 uint3
|
||||
#define packed_float4 float4
|
||||
#define packed_int4 int4
|
||||
#define packed_uint4 uint4
|
||||
|
||||
#define float2x2 mat2x2
|
||||
#define float3x2 mat3x2
|
||||
#define float4x2 mat4x2
|
||||
#define float2x3 mat2x3
|
||||
#define float3x3 mat3x3
|
||||
#define float4x3 mat4x3
|
||||
#define float2x4 mat2x4
|
||||
#define float3x4 mat3x4
|
||||
#define float4x4 mat4x4
|
||||
|
||||
/* Small types are unavailable in GLSL (or badly supported), promote them to bigger type. */
|
||||
#define char int
|
||||
#define char2 int2
|
||||
#define char3 int3
|
||||
#define char4 int4
|
||||
#define short int
|
||||
#define short2 int2
|
||||
#define short3 int3
|
||||
#define short4 int4
|
||||
#define uchar uint
|
||||
#define uchar2 uint2
|
||||
#define uchar3 uint3
|
||||
#define uchar4 uint4
|
||||
#define ushort uint
|
||||
#define ushort2 uint2
|
||||
#define ushort3 uint3
|
||||
#define ushort4 uint4
|
||||
#define half float
|
||||
#define half2 float2
|
||||
#define half3 float3
|
||||
#define half4 float4
|
||||
|
||||
/* Aliases for supported fixed width types. */
|
||||
#define int32_t int
|
||||
#define uint32_t uint
|
||||
|
||||
/* Fast load/store variant macro. In GLSL this is the same as imageLoad/imageStore, but assumes no
|
||||
* bounds checking. */
|
||||
#define imageStoreFast imageStore
|
||||
#define imageLoadFast imageLoad
|
||||
|
||||
/* Texture format tokens -- Type explicitness required by other Graphics APIs. */
|
||||
#define sampler2DDepth sampler2D
|
||||
#define sampler2DArrayDepth sampler2DArray
|
||||
#define samplerCubeDepth sampler2D
|
||||
#define samplerCubeArrayDepth sampler2DArray
|
||||
|
||||
#define usampler2DArrayAtomic usampler2DArray
|
||||
#define usampler2DAtomic usampler2D
|
||||
#define usampler3DAtomic usampler3D
|
||||
#define isampler2DArrayAtomic isampler2DArray
|
||||
#define isampler2DAtomic isampler2D
|
||||
#define isampler3DAtomic isampler3D
|
||||
|
||||
/* Pass through functions. */
|
||||
#define imageFence(image)
|
||||
|
||||
/* Backend Functions. */
|
||||
#define select(A, B, mask) mix(A, B, mask)
|
||||
|
||||
/* Array syntax compatibility. */
|
||||
#define float_array float[]
|
||||
#define float2_array vec2[]
|
||||
#define float3_array vec3[]
|
||||
#define float4_array vec4[]
|
||||
#define int_array int[]
|
||||
#define int2_array int2[]
|
||||
#define int3_array int3[]
|
||||
#define int4_array int4[]
|
||||
#define uint_array uint[]
|
||||
#define uint2_array uint2[]
|
||||
#define uint3_array uint3[]
|
||||
#define uint4_array uint4[]
|
||||
#define bool_array bool[]
|
||||
#define bool2_array bool2[]
|
||||
#define bool3_array bool3[]
|
||||
#define bool4_array bool4[]
|
||||
#define ARRAY_T(type) type[]
|
||||
#define ARRAY_V
|
||||
|
||||
#define SHADER_LIBRARY_CREATE_INFO(a)
|
||||
#define VERTEX_SHADER_CREATE_INFO(a)
|
||||
#define FRAGMENT_SHADER_CREATE_INFO(a)
|
||||
#define COMPUTE_SHADER_CREATE_INFO(a)
|
||||
|
||||
#define ATTR_FALLTHROUGH
|
||||
|
||||
#define _in_sta
|
||||
#define _in_end
|
||||
#define _out_sta
|
||||
#define _out_end
|
||||
#define _inout_sta
|
||||
#define _inout_end
|
||||
#define _shared_sta
|
||||
#define _shared_end
|
||||
/* References (inout and out).
|
||||
* Less verbose that the above for reading processed code. */
|
||||
#define _ref(_type, _var) inout _type _var
|
||||
/* Constructor / initializer. */
|
||||
#define _ctor(_type) _type(
|
||||
#define _rotc() )
|
||||
|
||||
/* Resource accessor. */
|
||||
#define specialization_constant_get(create_info, _res) _res
|
||||
#define shared_variable_get(create_info, _res) _res
|
||||
#define push_constant_get(create_info, _res) _res
|
||||
#define interface_get(create_info, _res) _res
|
||||
#define attribute_get(create_info, _res) _res
|
||||
#define buffer_get(create_info, _res) _res
|
||||
#define sampler_get(create_info, _res) _res
|
||||
#define image_get(create_info, _res) _res
|
||||
#define srt_access(create_info, _res) access_##create_info##_##_res()
|
||||
/**
|
||||
* WORKAROUND(fclem): Only used for cases when passing down the resource_table is impractical.
|
||||
* Note that this placeholder is just for the code to compile.
|
||||
*/
|
||||
#define resource_table_get(table_type) table_type##_ctor_()
|
||||
|
||||
/* Incompatible keywords. */
|
||||
#define static
|
||||
#define constant
|
||||
#define device
|
||||
#define thread
|
||||
#define threadgroup
|
||||
|
||||
/* MSL component compatibility. */
|
||||
#define textureGather0(_tex, _co) textureGather(_tex, _co, 0)
|
||||
#define textureGather1(_tex, _co) textureGather(_tex, _co, 1)
|
||||
#define textureGather2(_tex, _co) textureGather(_tex, _co, 2)
|
||||
#define textureGather3(_tex, _co) textureGather(_tex, _co, 3)
|
||||
|
||||
/**
|
||||
* This string type is much like the OSL string.
|
||||
* It is merely a hash of the actual string and it immutable.
|
||||
* Named `string_t` to avoid name collision with `std::string`.
|
||||
*/
|
||||
struct string_t {
|
||||
uint hash;
|
||||
};
|
||||
|
||||
#if 0 /* Causes NVidia compiler error on OpenGL. To be fixed. */
|
||||
bool equal(string_t a, string_t b)
|
||||
{
|
||||
return a.hash == b.hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint as_uint(string_t str)
|
||||
{
|
||||
return str.hash;
|
||||
}
|
||||
|
||||
float4 texelFetchExtend(sampler2D samp, int2 texel, int lvl)
|
||||
{
|
||||
texel = clamp(texel, int2(0), textureSize(samp, lvl).xy - 1);
|
||||
return texelFetch(samp, texel, lvl);
|
||||
}
|
||||
|
||||
/* For assert support. */
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
# define GPU_THREAD uint3(gl_VertexID, gl_InstanceID, 0)
|
||||
#elif defined(GPU_FRAGMENT_SHADER)
|
||||
# define GPU_THREAD uint3(gl_FragCoord.x, gl_FragCoord.y, 0)
|
||||
#elif defined(GPU_COMPUTE_SHADER)
|
||||
# define GPU_THREAD gl_GlobalInvocationID
|
||||
#else
|
||||
# define GPU_THREAD error_not_in_a_shader_question_mark
|
||||
#endif
|
||||
|
||||
/* Stage agnostic builtin function.
|
||||
* GLSL doesn't allow mixing shader stages inside the same source file.
|
||||
* Make sure builtin functions are stubbed when used in an invalid stage. */
|
||||
#ifdef GPU_FRAGMENT_SHADER
|
||||
# define gpu_discard_fragment() discard
|
||||
# ifdef GPU_ARB_derivative_control
|
||||
# define gpu_dfdx(x) dFdxFine(x)
|
||||
# define gpu_dfdy(x) dFdyFine(x)
|
||||
# else
|
||||
# define gpu_dfdx(x) dFdx(x)
|
||||
# define gpu_dfdy(x) dFdy(x)
|
||||
# endif
|
||||
# define gpu_fwidth(x) fwidth(x)
|
||||
#else
|
||||
# define gpu_discard_fragment()
|
||||
# define gpu_dfdx(x) x
|
||||
# define gpu_dfdy(x) x
|
||||
# define gpu_fwidth(x) x
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Special header for mapping commonly defined tokens to API-specific variations.
|
||||
* Where possible, this will adhere closely to base GLSL, where semantics are the same.
|
||||
* However, host code shader code may need modifying to support types where necessary variations
|
||||
* exist between APIs but are not expressed through the source. (e.g. distinction between depth2d
|
||||
* and texture2d types in metal).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_msl_atomic.msl"
|
||||
#include "gpu_shader_msl_attribute.msl"
|
||||
#include "gpu_shader_msl_builtin.msl"
|
||||
#include "gpu_shader_msl_image.msl"
|
||||
#include "gpu_shader_msl_matrix.msl"
|
||||
#include "gpu_shader_msl_matrix_legacy.msl"
|
||||
#include "gpu_shader_msl_sampler.msl"
|
||||
#include "gpu_shader_msl_string.msl"
|
||||
#include "gpu_shader_msl_types.msl"
|
||||
#include "gpu_shader_msl_types_legacy.msl"
|
||||
|
||||
/* Suppress unhelpful shader compiler warnings. */
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wcomment"
|
||||
|
||||
/* Wrapper needs to be last. */
|
||||
#include "gpu_shader_msl_wrapper.msl"
|
||||
|
||||
/* Array syntax compatibility. */
|
||||
/* clang-format off */
|
||||
#define float_array(...) { __VA_ARGS__ }
|
||||
#define float2_array(...) { __VA_ARGS__ }
|
||||
#define float3_array(...) { __VA_ARGS__ }
|
||||
#define float4_array(...) { __VA_ARGS__ }
|
||||
#define int_array(...) { __VA_ARGS__ }
|
||||
#define int2_array(...) { __VA_ARGS__ }
|
||||
#define int3_array(...) { __VA_ARGS__ }
|
||||
#define int4_array(...) { __VA_ARGS__ }
|
||||
#define uint_array(...) { __VA_ARGS__ }
|
||||
#define uint2_array(...) { __VA_ARGS__ }
|
||||
#define uint3_array(...) { __VA_ARGS__ }
|
||||
#define uint4_array(...) { __VA_ARGS__ }
|
||||
#define bool_array(...) { __VA_ARGS__ }
|
||||
#define bool2_array(...) { __VA_ARGS__ }
|
||||
#define bool3_array(...) { __VA_ARGS__ }
|
||||
#define bool4_array(...) { __VA_ARGS__ }
|
||||
#define ARRAY_T(type)
|
||||
#define ARRAY_V(...) {__VA_ARGS__}
|
||||
/* clang-format on */
|
||||
|
||||
#define SHADER_LIBRARY_CREATE_INFO(a)
|
||||
#define VERTEX_SHADER_CREATE_INFO(a)
|
||||
#define FRAGMENT_SHADER_CREATE_INFO(a)
|
||||
#define COMPUTE_SHADER_CREATE_INFO(a)
|
||||
|
||||
#define ATTR_FALLTHROUGH
|
||||
|
||||
#define in
|
||||
#define out thread
|
||||
#define inout thread
|
||||
#define _in_sta
|
||||
#define _in_end
|
||||
#define _out_sta (&
|
||||
#define _out_end )
|
||||
#define _inout_sta (&
|
||||
#define _inout_end )
|
||||
/* References (inout and out).
|
||||
* Less verbose that the above for reading processed code. */
|
||||
#define _ref(_type, _var) thread _type(&_var)
|
||||
/* Constructor / initializer. */
|
||||
#define _ctor(_type) \
|
||||
_type \
|
||||
{
|
||||
#define _rotc() }
|
||||
|
||||
#define shared threadgroup
|
||||
#define _shared_sta (&
|
||||
#define _shared_end )
|
||||
|
||||
float4 texelFetchExtend(sampler2D samp, int2 texel, int lvl)
|
||||
{
|
||||
texel = clamp(texel, int2(0), textureSize(samp, lvl).xy - 1);
|
||||
return texelFetch(samp, texel, lvl);
|
||||
}
|
||||
float4 texelFetchExtend(sampler2DDepth samp, int2 texel, int lvl)
|
||||
{
|
||||
texel = clamp(texel, int2(0), textureSize(samp, lvl).xy - 1);
|
||||
return texelFetch(samp, texel, lvl);
|
||||
}
|
||||
|
||||
/* Resource accessor. */
|
||||
#define specialization_constant_get(create_info, _res) _res
|
||||
#define shared_variable_get(create_info, _res) _res
|
||||
#define push_constant_get(create_info, _res) _res
|
||||
#define interface_get(create_info, _res) _res
|
||||
#define attribute_get(create_info, _res) _res
|
||||
#define buffer_get(create_info, _res) _res
|
||||
#define sampler_get(create_info, _res) _res
|
||||
#define image_get(create_info, _res) _res
|
||||
#define srt_access(create_info, _res) access_##create_info##_##_res()
|
||||
/**
|
||||
* WORKAROUND(fclem): Only used for cases when passing down the resource_table is impractical.
|
||||
* Note that this placeholder is just for the code to compile.
|
||||
*/
|
||||
#define resource_table_get(table_type) (table_type{})
|
||||
|
||||
/* For assert support. */
|
||||
#if defined(GPU_VERTEX_SHADER)
|
||||
# define GPU_THREAD uint3(gl_VertexID, gl_InstanceID, 0)
|
||||
#elif defined(GPU_FRAGMENT_SHADER)
|
||||
# define GPU_THREAD uint3(gl_FragCoord.x, gl_FragCoord.y, 0)
|
||||
#elif defined(GPU_COMPUTE_SHADER)
|
||||
# define GPU_THREAD gl_GlobalInvocationID
|
||||
#else
|
||||
# define GPU_THREAD error_not_in_a_shader_question_mark
|
||||
#endif
|
||||
|
||||
/* Stage agnostic builtin function.
|
||||
* MSL allow mixing shader stages inside the same source file.
|
||||
* Leaving the calls untouched makes sure we catch invalid usage during CI testing. */
|
||||
#define gpu_discard_fragment() discard
|
||||
#define gpu_dfdx(x) dFdx(x)
|
||||
#define gpu_dfdy(x) dFdy(x)
|
||||
#define gpu_fwidth(x) fwidth(x)
|
||||
@@ -0,0 +1,155 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* BSL attributes.
|
||||
*
|
||||
* Define them as standard attribute with similar placement to trigger compiler warning about typos
|
||||
* and error for misplacement.
|
||||
*
|
||||
* Actual implementation is done through the shader_tool and the attributes are not present in
|
||||
* final shader code.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(_MSC_VER) || defined(__clang__)
|
||||
/* MSVC doesn't support using the same attribute multiple times (fixed in later versions).
|
||||
* However, Clang in MSVC compatibility mode does. */
|
||||
|
||||
/* Specify a function is a compute shader entry point. */
|
||||
# define compute maybe_unused
|
||||
/* Specify a function is a vertex shader entry point. */
|
||||
# define vertex maybe_unused
|
||||
/* Specify a function is a fragment shader entry point. */
|
||||
# define fragment maybe_unused
|
||||
/* Set compute shader workgroup size. */
|
||||
# define local_size(...) maybe_unused
|
||||
/* Request performing fragment tests before the fragment function executes. */
|
||||
# define early_fragment_tests maybe_unused
|
||||
/* Metal specific hints. To be used on entry point functions. */
|
||||
# define metal_max_total_threads_per_threadgroup(count) maybe_unused
|
||||
/* Enable shader patching on GL to remap clip range to 0..1. */
|
||||
# define clip_control maybe_unused
|
||||
/* Enable texture atomics on older versions of metal. */
|
||||
# define texture_atomic maybe_unused
|
||||
|
||||
/* In a compute function, specify an input variable containing the 3-dimensional index of the local
|
||||
* work invocation within the work group that the current shader is executing in. */
|
||||
# define local_invocation_id maybe_unused
|
||||
/* In a compute function, specify a derived input variable containing the 3-dimensional index of
|
||||
* the work invocation within the global work group that the current shader is executing on. The
|
||||
* value is equal to work_group_id * work_group_size + local_invocation_id. */
|
||||
# define global_invocation_id maybe_unused
|
||||
/* In a compute function, specify an 1-dimensional linearized index of the work invocation within
|
||||
* the work group that the current shader is executing on. */
|
||||
# define local_invocation_index maybe_unused
|
||||
/* In a compute function, specify an input variable containing the 3-dimensional index of the
|
||||
* global work group that the current compute shader invocation is executing within. */
|
||||
# define work_group_id maybe_unused
|
||||
/* In a compute function, specify an input variable containing the total number of work groups that
|
||||
* will execute for the current compute shader dispatch. */
|
||||
# define num_work_groups maybe_unused
|
||||
|
||||
/* Specify a vertex attribute. */
|
||||
# define attribute(slot) maybe_unused
|
||||
/* Vertex attribute interpolation modes. */
|
||||
# define flat maybe_unused
|
||||
# define smooth maybe_unused
|
||||
# define no_perspective maybe_unused
|
||||
|
||||
/* Vertex shader output position. */
|
||||
# define position maybe_unused
|
||||
/* Vertex shader output point size. */
|
||||
# define point_size maybe_unused
|
||||
/* Vertex shader output, distance from vertex to clipping plane. */
|
||||
# define clip_distance maybe_unused
|
||||
/* The render target array index. */
|
||||
# define layer maybe_unused
|
||||
/* The viewport (and scissor rectangle) index value of the primitive. */
|
||||
# define viewport_index maybe_unused
|
||||
|
||||
/* Vertex shader input vertex index, which includes the base vertex if one is specified. */
|
||||
# define vertex_id maybe_unused
|
||||
/* Vertex shader input instance index, which doesn't include the base instance. */
|
||||
# define instance_id maybe_unused
|
||||
/* Vertex shader input instance index, which includes the base instance if one is specified. */
|
||||
# define instance_index maybe_unused
|
||||
/* Vertex shader input base instance value added to each instance identifier before reading
|
||||
* per-instance data. */
|
||||
# define base_instance maybe_unused
|
||||
|
||||
# define frag_coord maybe_unused
|
||||
# define point_coord maybe_unused
|
||||
# define front_facing maybe_unused
|
||||
|
||||
/* Fragment shader color input index for subpass input.
|
||||
* `sampler_type` is the type of image to bind to this (e.g. usampler2DArray).
|
||||
* It must be compatible with the frame-buffer attachment type. */
|
||||
# define subpass_input(index, sampler_type) maybe_unused
|
||||
|
||||
/* Fragment shader output. */
|
||||
# define frag_color(slot) maybe_unused
|
||||
/* Fragment shader output. */
|
||||
# define frag_depth(mode) maybe_unused
|
||||
/* Fragment shader output. Set stencil reference value per pixel.
|
||||
* Only supported on some platform. Check for compatibility first. */
|
||||
# define frag_stencil_ref maybe_unused
|
||||
/* Fragment shader color output index for dual source blending. */
|
||||
# define index(i) maybe_unused
|
||||
/* Fragment shader color output index for raster order group on Metal. */
|
||||
# define raster_order_group(i) maybe_unused
|
||||
|
||||
/* Graphic pipeline stage in/out. */
|
||||
# define in maybe_unused
|
||||
# define out maybe_unused
|
||||
# define subpass_in maybe_unused
|
||||
|
||||
/* Declare a dependency to a legacy create info whose name is the struct member name. */
|
||||
# define legacy_info maybe_unused
|
||||
/* Declare a sampler at the given slot. */
|
||||
# define sampler(slot) maybe_unused
|
||||
/* Declare a uniform buffer at the given slot. */
|
||||
# define uniform(slot) maybe_unused
|
||||
/* Declare a storage buffer at the given slot. */
|
||||
# define storage(slot, qualifiers) maybe_unused
|
||||
/* Declare a storage buffer at the given slot. */
|
||||
# define image(slot, qualifiers, format) maybe_unused
|
||||
# define compilation_constant maybe_unused
|
||||
# define specialization_constant(default_value) maybe_unused
|
||||
# define push_constant maybe_unused
|
||||
/* Declare a nested resource table member. */
|
||||
# define resource_table maybe_unused
|
||||
/* Only declare the member if cond evaluates to true. */
|
||||
# define condition(cond) maybe_unused
|
||||
/* Set binding frequency of a resource (storage, uniform, image, sampler). */
|
||||
# define frequency(freq) maybe_unused
|
||||
|
||||
/* Make a structure layout or enum shared between CPU and GPU code.
|
||||
* Required for structs defining storage and uniform buffer layout. */
|
||||
# define host_shared
|
||||
|
||||
/** Make function callable thought the node-tree code-generating system. */
|
||||
# define node maybe_unused
|
||||
|
||||
/* Make the branch condition evaluate at compile time. */
|
||||
# define static_branch likely
|
||||
/* Unroll the loop at compile time. */
|
||||
# define unroll likely
|
||||
/**
|
||||
* Unroll the loop N time at compile time.
|
||||
* IMPORTANT: Will discard any iteration above N.
|
||||
*/
|
||||
# define unroll_n(N) likely
|
||||
#else
|
||||
/* This path checks for unused variables. Disable warning about unknown attributes. */
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wattributes"
|
||||
# elif defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 5030)
|
||||
# pragma warning(disable : 5222)
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,233 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_cxx_vector.hh"
|
||||
|
||||
/* Some compilers complain about lack of return values. Keep it short. */
|
||||
#define RET \
|
||||
{ \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Builtin Functions
|
||||
* \{ */
|
||||
|
||||
template<typename T, int D> VecBase<bool, D> greaterThan(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<bool, D> lessThan(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<bool, D> lessThanEqual(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<bool, D> greaterThanEqual(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<bool, D> equal(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<bool, D> notEqual(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<int D> bool any(VecOp<bool, D>) RET;
|
||||
template<int D> bool all(VecOp<bool, D>) RET;
|
||||
/* `not` is a C++ keyword that aliases the `!` operator. Simply overload it. */
|
||||
template<int D> VecBase<bool, D> operator!(VecOp<bool, D>) RET;
|
||||
|
||||
template<int D> VecBase<int, D> bitCount(VecOp<int, D>) RET;
|
||||
template<int D> VecBase<int, D> bitCount(VecOp<uint, D>) RET;
|
||||
template<int D> VecBase<int, D> bitfieldExtract(VecOp<int, D>, int, int) RET;
|
||||
template<int D> VecBase<uint, D> bitfieldExtract(VecOp<uint, D>, int, int) RET;
|
||||
template<int D> VecBase<int, D> bitfieldInsert(VecOp<int, D>, VecOp<int, D>, int, int) RET;
|
||||
template<int D> VecBase<uint, D> bitfieldInsert(VecOp<uint, D>, VecOp<uint, D>, int, int) RET;
|
||||
template<int D> VecBase<int, D> bitfieldReverse(VecOp<int, D>) RET;
|
||||
template<int D> VecBase<uint, D> bitfieldReverse(VecOp<uint, D>) RET;
|
||||
int bitCount(int) RET;
|
||||
int bitCount(uint) RET;
|
||||
int bitfieldExtract(int) RET;
|
||||
uint bitfieldExtract(uint) RET;
|
||||
int bitfieldInsert(int) RET;
|
||||
uint bitfieldInsert(uint) RET;
|
||||
int bitfieldReverse(int) RET;
|
||||
uint bitfieldReverse(uint) RET;
|
||||
|
||||
template<int D> VecBase<int, D> findLSB(VecOp<int, D>) RET;
|
||||
template<int D> VecBase<int, D> findLSB(VecOp<uint, D>) RET;
|
||||
template<int D> VecBase<int, D> findMSB(VecOp<int, D>) RET;
|
||||
template<int D> VecBase<int, D> findMSB(VecOp<uint, D>) RET;
|
||||
int findLSB(int) RET;
|
||||
int findLSB(uint) RET;
|
||||
int findMSB(int) RET;
|
||||
int findMSB(uint) RET;
|
||||
|
||||
/* Math Functions. */
|
||||
|
||||
/* NOTE: Declared inside a namespace and exposed behind macros to prevent
|
||||
* errors on VS2019 due to `corecrt_math` conflicting functions. */
|
||||
namespace glsl {
|
||||
template<typename T> constexpr T abs(T) RET;
|
||||
/* TODO(fclem): These should be restricted to floats. */
|
||||
template<typename T> constexpr T ceil(T) RET;
|
||||
template<typename T> constexpr T exp(T) RET;
|
||||
template<typename T> constexpr T exp2(T) RET;
|
||||
template<typename T> constexpr T floor(T) RET;
|
||||
template<typename T> T fma(T, T, T) RET;
|
||||
float fma(float, float, float) RET;
|
||||
template<typename T> T frexp(T, T) RET;
|
||||
bool isinf(float) RET;
|
||||
template<int D> VecBase<bool, D> isinf(VecOp<float, D>) RET;
|
||||
bool isnan(float) RET;
|
||||
template<int D> VecBase<bool, D> isnan(VecOp<float, D>) RET;
|
||||
template<typename T> constexpr T log(T) RET;
|
||||
template<typename T> constexpr T log2(T) RET;
|
||||
template<typename T> T modf(T, T &) RET;
|
||||
template<typename T, typename U> constexpr T pow(T, U) RET;
|
||||
template<typename T> constexpr T round(T) RET;
|
||||
template<typename T> constexpr T sqrt(T) RET;
|
||||
template<typename T> constexpr T trunc(T) RET;
|
||||
template<typename T, typename U> T ldexp(T, U) RET;
|
||||
|
||||
template<typename T> constexpr T acos(T) RET;
|
||||
template<typename T> T acosh(T) RET;
|
||||
template<typename T> constexpr T asin(T) RET;
|
||||
template<typename T> T asinh(T) RET;
|
||||
template<typename T> T atan(T, T) RET;
|
||||
template<typename T> T atan(T) RET;
|
||||
template<typename T> T atanh(T) RET;
|
||||
template<typename T> constexpr T cos(T) RET;
|
||||
template<typename T> T cosh(T) RET;
|
||||
template<typename T> constexpr T sin(T) RET;
|
||||
template<typename T> T sinh(T) RET;
|
||||
template<typename T> T tan(T) RET;
|
||||
template<typename T> T tanh(T) RET;
|
||||
} // namespace glsl
|
||||
|
||||
#define abs glsl::abs
|
||||
#define ceil glsl::ceil
|
||||
#define exp glsl::exp
|
||||
#define exp2 glsl::exp2
|
||||
#define floor glsl::floor
|
||||
#define fma glsl::fma
|
||||
#define frexp glsl::frexp
|
||||
#define isinf glsl::isinf
|
||||
#define isnan glsl::isnan
|
||||
#define log glsl::log
|
||||
#define log2 glsl::log2
|
||||
#define modf glsl::modf
|
||||
#define pow glsl::pow
|
||||
#define round glsl::round
|
||||
#define sqrt glsl::sqrt
|
||||
#define trunc glsl::trunc
|
||||
#define ldexp glsl::ldexp
|
||||
#define acos glsl::acos
|
||||
#define acosh glsl::acosh
|
||||
#define asin glsl::asin
|
||||
#define asinh glsl::asinh
|
||||
#define atan glsl::atan
|
||||
#define atanh glsl::atanh
|
||||
#define cos glsl::cos
|
||||
#define cosh glsl::cosh
|
||||
#define sin glsl::sin
|
||||
#define sinh glsl::sinh
|
||||
#define tan glsl::tan
|
||||
#define tanh glsl::tanh
|
||||
|
||||
template<typename T> constexpr T max(T, T) RET;
|
||||
template<typename T> constexpr T min(T, T) RET;
|
||||
template<typename T> constexpr T sign(T) RET;
|
||||
template<typename T, typename U> constexpr T clamp(T, U, U) RET;
|
||||
template<typename T> constexpr T clamp(T, float, float) RET;
|
||||
template<typename T, typename U> constexpr T max(T, U) RET;
|
||||
template<typename T, typename U> constexpr T min(T, U) RET;
|
||||
/* TODO(fclem): These should be restricted to floats. */
|
||||
template<typename T> T fract(T) RET;
|
||||
template<typename T> constexpr T inversesqrt(T) RET;
|
||||
constexpr float mod(float, float) RET;
|
||||
template<int D> VecBase<float, D> constexpr mod(VecOp<float, D>, float) RET;
|
||||
template<int D> VecBase<float, D> constexpr mod(VecOp<float, D>, VecOp<float, D>) RET;
|
||||
template<typename T> T smoothstep(T, T, T) RET;
|
||||
float step(float, float) RET;
|
||||
template<int D> VecBase<float, D> step(VecOp<float, D>, VecOp<float, D>) RET;
|
||||
template<int D> VecBase<float, D> step(float, VecOp<float, D>) RET;
|
||||
float smoothstep(float, float, float) RET;
|
||||
template<int D> VecBase<float, D> smoothstep(float, float, VecOp<float, D>) RET;
|
||||
|
||||
template<typename T> constexpr T degrees(T) RET;
|
||||
template<typename T> constexpr T radians(T) RET;
|
||||
|
||||
/* Declared explicitly to avoid type errors. */
|
||||
float mix(float, float, float) RET;
|
||||
template<int D> VecBase<float, D> mix(VecOp<float, D>, VecOp<float, D>, float) RET;
|
||||
template<int D> VecBase<float, D> mix(VecOp<float, D>, VecOp<float, D>, VecOp<float, D>) RET;
|
||||
template<typename T, int D> VecBase<T, D> mix(VecOp<T, D>, VecOp<T, D>, VecOp<bool, D>) RET;
|
||||
|
||||
#define select(A, B, C) mix(A, B, C)
|
||||
|
||||
VecBase<float, 3> cross(VecOp<float, 3>, VecOp<float, 3>) RET;
|
||||
template<int D> float dot(VecOp<float, D>, VecOp<float, D>) RET;
|
||||
float distance(float, float) RET;
|
||||
template<int D> float distance(VecOp<float, D>, VecOp<float, D>) RET;
|
||||
template<int D> float length(VecOp<float, D>) RET;
|
||||
template<int D> VecBase<float, D> normalize(VecOp<float, D>) RET;
|
||||
|
||||
template<int D> VecBase<int, D> floatBitsToInt(VecOp<float, D>) RET;
|
||||
template<int D> VecBase<uint, D> floatBitsToUint(VecOp<float, D>) RET;
|
||||
template<int D> VecBase<float, D> intBitsToFloat(VecOp<int, D>) RET;
|
||||
template<int D> VecBase<float, D> uintBitsToFloat(VecOp<uint, D>) RET;
|
||||
int floatBitsToInt(float) RET;
|
||||
uint floatBitsToUint(float) RET;
|
||||
float intBitsToFloat(int) RET;
|
||||
float uintBitsToFloat(uint) RET;
|
||||
|
||||
/* Derivative functions. */
|
||||
template<typename T> T gpu_dfdx(T) RET;
|
||||
template<typename T> T gpu_dfdy(T) RET;
|
||||
template<typename T> T gpu_fwidth(T) RET;
|
||||
|
||||
/* Discards the output of the current fragment shader invocation and halts its execution. */
|
||||
void gpu_discard_fragment() {}
|
||||
|
||||
/* Geometric functions. */
|
||||
template<typename T, int D> VecBase<T, D> faceforward(VecOp<T, D>, VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<T, D> reflect(VecOp<T, D>, VecOp<T, D>) RET;
|
||||
template<typename T, int D> VecBase<T, D> refract(VecOp<T, D>, VecOp<T, D>, float) RET;
|
||||
|
||||
/* Atomic operations. */
|
||||
int atomicAdd(int &, int) RET;
|
||||
int atomicAnd(int &, int) RET;
|
||||
int atomicOr(int &, int) RET;
|
||||
int atomicXor(int &, int) RET;
|
||||
int atomicMin(int &, int) RET;
|
||||
int atomicMax(int &, int) RET;
|
||||
int atomicExchange(int &, int) RET;
|
||||
int atomicCompSwap(int &, int, int) RET;
|
||||
uint atomicAdd(uint &, uint) RET;
|
||||
uint atomicAnd(uint &, uint) RET;
|
||||
uint atomicOr(uint &, uint) RET;
|
||||
uint atomicXor(uint &, uint) RET;
|
||||
uint atomicMin(uint &, uint) RET;
|
||||
uint atomicMax(uint &, uint) RET;
|
||||
uint atomicExchange(uint &, uint) RET;
|
||||
uint atomicCompSwap(uint &, uint, uint) RET;
|
||||
|
||||
/* Packing functions. */
|
||||
uint packHalf2x16(float2) RET;
|
||||
uint packUnorm2x16(float2) RET;
|
||||
uint packSnorm2x16(float2) RET;
|
||||
uint packUnorm4x8(float4) RET;
|
||||
uint packSnorm4x8(float4) RET;
|
||||
float2 unpackHalf2x16(uint) RET;
|
||||
float2 unpackUnorm2x16(uint) RET;
|
||||
float2 unpackSnorm2x16(uint) RET;
|
||||
float4 unpackUnorm4x8(uint) RET;
|
||||
float4 unpackSnorm4x8(uint) RET;
|
||||
|
||||
void barrier() {}
|
||||
void memoryBarrier() {}
|
||||
void memoryBarrierShared() {}
|
||||
void memoryBarrierImage() {}
|
||||
void memoryBarrierBuffer() {}
|
||||
void groupMemoryBarrier() {}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef RET
|
||||
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_cxx_vector.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Special Variables
|
||||
* \{ */
|
||||
|
||||
namespace gl_VertexShader {
|
||||
|
||||
extern const int gl_VertexID;
|
||||
extern const int gl_InstanceID;
|
||||
extern const int gl_BaseVertex;
|
||||
extern const int gpu_BaseInstance;
|
||||
extern const int gpu_InstanceIndex;
|
||||
float4 gl_Position = float4(0);
|
||||
float gl_PointSize = 0;
|
||||
float gl_ClipDistance[6] = {0};
|
||||
int gpu_Layer = 0;
|
||||
int gpu_ViewportIndex = 0;
|
||||
|
||||
} // namespace gl_VertexShader
|
||||
|
||||
namespace gl_FragmentShader {
|
||||
|
||||
extern const float4 gl_FragCoord;
|
||||
const bool gl_FrontFacing = true;
|
||||
const float2 gl_PointCoord = float2(0);
|
||||
const int gl_PrimitiveID = 0;
|
||||
float gl_FragDepth = 0;
|
||||
const float gl_ClipDistance[6] = {0};
|
||||
const int gpu_Layer = 0;
|
||||
const int gpu_ViewportIndex = 0;
|
||||
|
||||
} // namespace gl_FragmentShader
|
||||
|
||||
/* Outside of namespace to be used in create infos. */
|
||||
constexpr uint3 gl_WorkGroupSize = uint3(16, 16, 16);
|
||||
|
||||
namespace gl_ComputeShader {
|
||||
|
||||
extern const uint3 gl_NumWorkGroups;
|
||||
extern const uint3 gl_WorkGroupID;
|
||||
extern const uint3 gl_LocalInvocationID;
|
||||
extern const uint3 gl_GlobalInvocationID;
|
||||
extern const uint gl_LocalInvocationIndex;
|
||||
|
||||
} // namespace gl_ComputeShader
|
||||
|
||||
/** \} */
|
||||
115
blender-5.2.0/source/blender/gpu/shaders/gpu_shader_cxx_image.hh
Normal file
115
blender-5.2.0/source/blender/gpu/shaders/gpu_shader_cxx_image.hh
Normal file
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_cxx_vector.hh"
|
||||
|
||||
/* Some compilers complain about lack of return values. Keep it short. */
|
||||
#define RET \
|
||||
{ \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Image Types
|
||||
* \{ */
|
||||
|
||||
template<typename T, int Dimensions, bool Array = false, bool Atomic = false> struct ImageBase {
|
||||
static constexpr int coord_dim = Dimensions + int(Array);
|
||||
|
||||
using int_coord_type = VecBase<int, coord_dim>;
|
||||
using data_vec_type = VecBase<T, 4>;
|
||||
using size_vec_type = VecBase<int, coord_dim>;
|
||||
};
|
||||
|
||||
#define IMG_TEMPLATE \
|
||||
template<typename T, \
|
||||
typename IntCoord = typename T::int_coord_type, \
|
||||
typename DataVec = typename T::data_vec_type, \
|
||||
typename SizeVec = typename T::size_vec_type>
|
||||
|
||||
IMG_TEMPLATE SizeVec imageSize(const T &) RET;
|
||||
IMG_TEMPLATE DataVec imageLoad(const T &, IntCoord) RET;
|
||||
IMG_TEMPLATE void imageStore(T &, IntCoord, DataVec) {}
|
||||
IMG_TEMPLATE void imageFence(T &) {}
|
||||
/* Cannot write to a read only image. */
|
||||
IMG_TEMPLATE void imageStore(const T &, IntCoord, DataVec) = delete;
|
||||
IMG_TEMPLATE void imageFence(const T &) = delete;
|
||||
|
||||
#define imageLoadFast imageLoad
|
||||
#define imageStoreFast imageStore
|
||||
|
||||
IMG_TEMPLATE uint imageAtomicAdd(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicMin(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicMax(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicAnd(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicXor(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicOr(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicExchange(T &, IntCoord, uint) RET;
|
||||
IMG_TEMPLATE uint imageAtomicCompSwap(T &, IntCoord, uint, uint) RET;
|
||||
IMG_TEMPLATE int imageAtomicAdd(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicMin(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicMax(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicAnd(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicXor(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicOr(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicExchange(T &, IntCoord, int) RET;
|
||||
IMG_TEMPLATE int imageAtomicCompSwap(T &, IntCoord, int, int) RET;
|
||||
/* Cannot write to a read only image. */
|
||||
IMG_TEMPLATE uint imageAtomicAdd(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicMin(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicMax(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicAnd(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicXor(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicOr(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicExchange(const T &, IntCoord, uint) = delete;
|
||||
IMG_TEMPLATE uint imageAtomicCompSwap(const T &, IntCoord, uint, uint) = delete;
|
||||
IMG_TEMPLATE int imageAtomicAdd(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicMin(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicMax(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicAnd(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicXor(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicOr(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicExchange(const T &, IntCoord, int) = delete;
|
||||
IMG_TEMPLATE int imageAtomicCompSwap(const T &, IntCoord, int, int) = delete;
|
||||
|
||||
#undef IMG_TEMPLATE
|
||||
|
||||
using image1D = ImageBase<float, 1>;
|
||||
using image2D = ImageBase<float, 2>;
|
||||
using image3D = ImageBase<float, 3>;
|
||||
using iimage1D = ImageBase<int, 1>;
|
||||
using iimage2D = ImageBase<int, 2>;
|
||||
using iimage3D = ImageBase<int, 3>;
|
||||
using uimage1D = ImageBase<uint, 1>;
|
||||
using uimage2D = ImageBase<uint, 2>;
|
||||
using uimage3D = ImageBase<uint, 3>;
|
||||
|
||||
using image1DArray = ImageBase<float, 1, true>;
|
||||
using image2DArray = ImageBase<float, 2, true>;
|
||||
using iimage1DArray = ImageBase<int, 1, true>;
|
||||
using iimage2DArray = ImageBase<int, 2, true>;
|
||||
using uimage1DArray = ImageBase<uint, 1, true>;
|
||||
using uimage2DArray = ImageBase<uint, 2, true>;
|
||||
|
||||
using iimage2DAtomic = ImageBase<int, 2, false, true>;
|
||||
using iimage3DAtomic = ImageBase<int, 3, false, true>;
|
||||
using uimage2DAtomic = ImageBase<uint, 2, false, true>;
|
||||
using uimage3DAtomic = ImageBase<uint, 3, false, true>;
|
||||
|
||||
using iimage2DArrayAtomic = ImageBase<int, 2, true, true>;
|
||||
using uimage2DArrayAtomic = ImageBase<uint, 2, true, true>;
|
||||
|
||||
/* Forbid Cube and cube arrays. Bind them as 3D textures instead. */
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef RET
|
||||
@@ -0,0 +1,154 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_cxx_builtin.hh"
|
||||
#include "gpu_shader_cxx_vector.hh"
|
||||
|
||||
/* Some compilers complain about lack of return values. Keep it short. */
|
||||
#define RET \
|
||||
{ \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Matrix Types
|
||||
* \{ */
|
||||
|
||||
template<int C, int R> struct MatBase {};
|
||||
|
||||
template<int C, int R> struct MatOp {
|
||||
using MatT = MatBase<C, R>;
|
||||
using ColT = VecBase<float, R>;
|
||||
using RowT = VecBase<float, C>;
|
||||
|
||||
const ColT &operator[](int) const
|
||||
{
|
||||
return *reinterpret_cast<const ColT *>(this);
|
||||
}
|
||||
ColT &operator[](int)
|
||||
{
|
||||
return *reinterpret_cast<ColT *>(this);
|
||||
}
|
||||
const ColT &operator[](uint) const
|
||||
{
|
||||
return *reinterpret_cast<const ColT *>(this);
|
||||
}
|
||||
ColT &operator[](uint)
|
||||
{
|
||||
return *reinterpret_cast<ColT *>(this);
|
||||
}
|
||||
|
||||
MatT operator+() RET;
|
||||
MatT operator-() RET;
|
||||
|
||||
MatT operator*(MatT) const RET;
|
||||
|
||||
friend RowT operator*(ColT, MatT) RET;
|
||||
friend ColT operator*(MatT, RowT) RET;
|
||||
};
|
||||
|
||||
template<int R> struct MatBase<2, R> : MatOp<2, R> {
|
||||
using T = float;
|
||||
using ColT = VecBase<float, R>;
|
||||
ColT x, y;
|
||||
|
||||
MatBase() = default;
|
||||
explicit MatBase(T) {}
|
||||
explicit MatBase(T, T, T, T) {}
|
||||
explicit MatBase(ColT, ColT) {}
|
||||
template<int OtherC, int OtherR> explicit MatBase(const MatBase<OtherC, OtherR> &) {}
|
||||
};
|
||||
|
||||
template<int R> struct MatBase<3, R> : MatOp<3, R> {
|
||||
using T = float;
|
||||
using ColT = VecBase<float, R>;
|
||||
ColT x, y, z;
|
||||
|
||||
MatBase() = default;
|
||||
explicit MatBase(T) {}
|
||||
explicit MatBase(T, T, T, T, T, T, T, T, T) {}
|
||||
explicit MatBase(ColT, ColT, ColT) {}
|
||||
template<int OtherC, int OtherR> explicit MatBase(const MatBase<OtherC, OtherR> &) {}
|
||||
};
|
||||
|
||||
template<int R> struct MatBase<4, R> : MatOp<4, R> {
|
||||
using T = float;
|
||||
using ColT = VecBase<float, R>;
|
||||
ColT x, y, z, w;
|
||||
|
||||
MatBase() = default;
|
||||
explicit MatBase(T) {}
|
||||
explicit MatBase(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) {}
|
||||
explicit MatBase(ColT, ColT, ColT, ColT) {}
|
||||
template<int OtherC, int OtherR> explicit MatBase(const MatBase<OtherC, OtherR> &) {}
|
||||
};
|
||||
|
||||
using float2x2 = MatBase<2, 2>;
|
||||
using float2x3 = MatBase<2, 3>;
|
||||
using float2x4 = MatBase<2, 4>;
|
||||
using float3x2 = MatBase<3, 2>;
|
||||
using float3x3 = MatBase<3, 3>;
|
||||
using float3x4 = MatBase<3, 4>;
|
||||
using float4x2 = MatBase<4, 2>;
|
||||
using float4x3 = MatBase<4, 3>;
|
||||
using float4x4 = MatBase<4, 4>;
|
||||
|
||||
/* Matrix reshaping functions. */
|
||||
#define RESHAPE(mat_to, mat_from, ...) \
|
||||
mat_to to_##mat_to(mat_from m) \
|
||||
{ \
|
||||
return mat_to(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
RESHAPE(float2x2, float3x3, m[0].xy, m[1].xy)
|
||||
RESHAPE(float2x2, float4x4, m[0].xy, m[1].xy)
|
||||
RESHAPE(float3x3, float4x4, m[0].xyz, m[1].xyz, m[2].xyz)
|
||||
RESHAPE(float3x3, float2x2, m[0].x, m[0].y, 0, m[1].x, m[1].y, 0, 0, 0, 1)
|
||||
RESHAPE(float4x4, float2x2, m[0].x, m[0].y, 0, 0, m[1].x, m[1].y, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||
RESHAPE(float4x4, float3x3, m[0].x, m[0].y, m[0].z, 0, m[1].x, m[1].y, m[1].z, 0, m[2].x, m[2].y, m[2].z, 0, 0, 0, 0, 1)
|
||||
/* clang-format on */
|
||||
/* TODO(fclem): Remove. Use Transform instead. */
|
||||
RESHAPE(float3x3, float3x4, m[0].xyz, m[1].xyz, m[2].xyz)
|
||||
#undef RESHAPE
|
||||
|
||||
/* Matrix compare operators. */
|
||||
#define EQ_OP(type, ...) \
|
||||
inline bool operator==(type a, type b) \
|
||||
{ \
|
||||
return __VA_ARGS__; \
|
||||
}
|
||||
EQ_OP(float2x2, all(equal(a[0], b[0])) && all(equal(a[1], b[1])))
|
||||
EQ_OP(float2x3, all(equal(a[0], b[0])) && all(equal(a[1], b[1])))
|
||||
EQ_OP(float2x4, all(equal(a[0], b[0])) && all(equal(a[1], b[1])))
|
||||
EQ_OP(float3x2, all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])))
|
||||
EQ_OP(float3x3, all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])))
|
||||
EQ_OP(float3x4, all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])))
|
||||
EQ_OP(float4x2,
|
||||
all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])) &&
|
||||
all(equal(a[3], b[3])))
|
||||
EQ_OP(float4x3,
|
||||
all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])) &&
|
||||
all(equal(a[3], b[3])))
|
||||
EQ_OP(float4x4,
|
||||
all(equal(a[0], b[0])) && all(equal(a[1], b[1])) && all(equal(a[2], b[2])) &&
|
||||
all(equal(a[3], b[3])))
|
||||
#undef EQ_OP
|
||||
|
||||
/* Matrices functions. */
|
||||
template<int C, int R> float determinant(MatBase<C, R>) RET;
|
||||
template<int C, int R> MatBase<C, R> inverse(MatBase<C, R>) RET;
|
||||
template<int C, int R> MatBase<R, C> transpose(MatBase<C, R>) RET;
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef RET
|
||||
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_cxx_vector.hh"
|
||||
|
||||
/* Some compilers complain about lack of return values. Keep it short. */
|
||||
#define RET \
|
||||
{ \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sampler Types
|
||||
* \{ */
|
||||
|
||||
template<typename T,
|
||||
int Dimensions,
|
||||
bool Cube = false,
|
||||
bool Array = false,
|
||||
bool Atomic = false,
|
||||
bool Depth = false>
|
||||
struct SamplerBase {
|
||||
static constexpr int coord_dim = Dimensions + int(Cube) + int(Array);
|
||||
static constexpr int deriv_dim = Dimensions + int(Cube);
|
||||
static constexpr int extent_dim = Dimensions + int(Array);
|
||||
|
||||
using int_coord_type = VecBase<int, coord_dim>;
|
||||
using flt_coord_type = VecBase<float, coord_dim>;
|
||||
using derivative_type = VecBase<float, deriv_dim>;
|
||||
using data_vec_type = VecBase<T, 4>;
|
||||
using size_vec_type = VecBase<int, extent_dim>;
|
||||
};
|
||||
|
||||
#define TEX_TEMPLATE \
|
||||
template<typename T, \
|
||||
typename IntCoord = typename T::int_coord_type, \
|
||||
typename FltCoord = typename T::flt_coord_type, \
|
||||
typename DerivVec = typename T::derivative_type, \
|
||||
typename DataVec = typename T::data_vec_type, \
|
||||
typename SizeVec = typename T::size_vec_type>
|
||||
|
||||
TEX_TEMPLATE SizeVec textureSize(T, int) RET;
|
||||
TEX_TEMPLATE DataVec texelFetch(T, IntCoord, int) RET;
|
||||
TEX_TEMPLATE DataVec texelFetchOffset(T, IntCoord, int, IntCoord) RET;
|
||||
TEX_TEMPLATE DataVec texture(T, FltCoord, float /*bias*/ = 0.0f) RET;
|
||||
TEX_TEMPLATE DataVec textureGather(T, FltCoord, int /*comp*/ = 0) RET;
|
||||
TEX_TEMPLATE DataVec textureGrad(T, FltCoord, DerivVec, DerivVec) RET;
|
||||
TEX_TEMPLATE DataVec textureLod(T, FltCoord, float) RET;
|
||||
TEX_TEMPLATE DataVec textureLodOffset(T, FltCoord, float, IntCoord) RET;
|
||||
|
||||
#undef TEX_TEMPLATE
|
||||
|
||||
using samplerBuffer = SamplerBase<float, 1>;
|
||||
using sampler1D = SamplerBase<float, 1>;
|
||||
using sampler2D = SamplerBase<float, 2>;
|
||||
using sampler3D = SamplerBase<float, 3>;
|
||||
using isamplerBuffer = SamplerBase<int, 1>;
|
||||
using isampler1D = SamplerBase<int, 1>;
|
||||
using isampler2D = SamplerBase<int, 2>;
|
||||
using isampler3D = SamplerBase<int, 3>;
|
||||
using usamplerBuffer = SamplerBase<uint, 1>;
|
||||
using usampler1D = SamplerBase<uint, 1>;
|
||||
using usampler2D = SamplerBase<uint, 2>;
|
||||
using usampler3D = SamplerBase<uint, 3>;
|
||||
|
||||
using sampler1DArray = SamplerBase<float, 1, false, true>;
|
||||
using sampler2DArray = SamplerBase<float, 2, false, true>;
|
||||
using isampler1DArray = SamplerBase<int, 1, false, true>;
|
||||
using isampler2DArray = SamplerBase<int, 2, false, true>;
|
||||
using usampler1DArray = SamplerBase<uint, 1, false, true>;
|
||||
using usampler2DArray = SamplerBase<uint, 2, false, true>;
|
||||
|
||||
using samplerCube = SamplerBase<float, 2, true>;
|
||||
using isamplerCube = SamplerBase<int, 2, true>;
|
||||
using usamplerCube = SamplerBase<uint, 2, true>;
|
||||
|
||||
using samplerCubeArray = SamplerBase<float, 2, true, true>;
|
||||
using isamplerCubeArray = SamplerBase<int, 2, true, true>;
|
||||
using usamplerCubeArray = SamplerBase<uint, 2, true, true>;
|
||||
|
||||
using usampler1DAtomic = SamplerBase<uint, 1, false, false, true>;
|
||||
using usampler2DAtomic = SamplerBase<uint, 2, false, false, true>;
|
||||
using usampler2DArrayAtomic = SamplerBase<uint, 2, false, true, true>;
|
||||
using usampler3DAtomic = SamplerBase<uint, 3, false, false, true>;
|
||||
|
||||
using isampler1DAtomic = SamplerBase<int, 1, false, false, true>;
|
||||
using isampler2DAtomic = SamplerBase<int, 2, false, false, true>;
|
||||
using isampler2DArrayAtomic = SamplerBase<int, 2, false, true, true>;
|
||||
using isampler3DAtomic = SamplerBase<int, 3, false, false, true>;
|
||||
|
||||
using sampler2DDepth = SamplerBase<float, 2, false, false, false, true>;
|
||||
using sampler2DArrayDepth = SamplerBase<float, 2, false, true, false, true>;
|
||||
using samplerCubeDepth = SamplerBase<float, 2, true, false, false, true>;
|
||||
using samplerCubeArrayDepth = SamplerBase<float, 2, true, true, false, true>;
|
||||
|
||||
/* Sampler Buffers do not have LOD. */
|
||||
float4 texelFetch(samplerBuffer, int) RET;
|
||||
int4 texelFetch(isamplerBuffer, int) RET;
|
||||
uint4 texelFetch(usamplerBuffer, int) RET;
|
||||
|
||||
float4 texelFetchExtend(sampler2D /*samp*/, int2 /*texel*/, int /*lvl*/) RET;
|
||||
float4 texelFetchExtend(sampler2DDepth /*samp*/, int2 /*texel*/, int /*lvl*/) RET;
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef RET
|
||||
@@ -0,0 +1,34 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* For uint declaration. */
|
||||
#include "gpu_shader_cxx_vector.hh" // IWYU pragma: export
|
||||
|
||||
/**
|
||||
* Placeholder type for the actual shading language type.
|
||||
* This string type is much like the OSL string.
|
||||
* It is merely a hash of the actual string and it immutable.
|
||||
* Named `string_t` to avoid name collision with `std::string`.
|
||||
*/
|
||||
struct string_t {
|
||||
string_t(const char * /*str*/) {}
|
||||
};
|
||||
|
||||
bool equal(string_t, string_t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint as_uint(string_t)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* C++ stubs for shading language.
|
||||
*
|
||||
* IMPORTANT: Please ask the module team if you need some feature that are not listed in this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Implement type_trait manually to avoid dragging compile time down. */
|
||||
namespace cxx {
|
||||
|
||||
template<typename T, typename U> struct is_same {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template<typename T> struct is_same<T, T> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template<typename T, typename U> inline constexpr bool is_same_v = is_same<T, U>::value;
|
||||
template<typename T> struct is_integral_base {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
// Helper macro to stamp out the true cases quickly
|
||||
#define REGISTER_INTEGRAL(TYPE) \
|
||||
template<> struct is_integral_base<TYPE> { \
|
||||
static constexpr bool value = true; \
|
||||
};
|
||||
|
||||
REGISTER_INTEGRAL(char)
|
||||
REGISTER_INTEGRAL(signed char)
|
||||
REGISTER_INTEGRAL(unsigned char)
|
||||
REGISTER_INTEGRAL(short)
|
||||
REGISTER_INTEGRAL(unsigned short)
|
||||
REGISTER_INTEGRAL(int)
|
||||
REGISTER_INTEGRAL(unsigned int)
|
||||
REGISTER_INTEGRAL(long)
|
||||
REGISTER_INTEGRAL(unsigned long)
|
||||
REGISTER_INTEGRAL(long long)
|
||||
REGISTER_INTEGRAL(unsigned long long)
|
||||
|
||||
template<typename T> inline constexpr bool is_integral_v = is_integral_base<T>::value;
|
||||
} // namespace cxx
|
||||
|
||||
#undef REGISTER_INTEGRAL
|
||||
|
||||
/* Some compilers complain about lack of return values. Keep it short. */
|
||||
#define RET \
|
||||
{ \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept NotBool = !cxx::is_same_v<T, bool>;
|
||||
|
||||
template<typename T>
|
||||
concept IsIntegral = cxx::is_integral_v<T> && !cxx::is_same_v<T, bool>;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vector Types
|
||||
* \{ */
|
||||
|
||||
template<typename T, int Sz> struct VecBase {};
|
||||
|
||||
template<typename T, int Sz> struct VecOp {
|
||||
using VecT = VecBase<T, Sz>;
|
||||
|
||||
const T &operator[](int) const
|
||||
{
|
||||
return *reinterpret_cast<const T *>(this);
|
||||
}
|
||||
const T &operator[](unsigned int) const
|
||||
{
|
||||
return *reinterpret_cast<const T *>(this);
|
||||
}
|
||||
T &operator[](int)
|
||||
{
|
||||
return *reinterpret_cast<T *>(this);
|
||||
}
|
||||
T &operator[](unsigned int)
|
||||
{
|
||||
return *reinterpret_cast<T *>(this);
|
||||
}
|
||||
|
||||
#define STD_OP requires NotBool<T>
|
||||
|
||||
VecT operator+() const STD_OP RET;
|
||||
VecT operator-() const STD_OP RET;
|
||||
|
||||
friend VecT operator+(VecT, VecT) STD_OP RET;
|
||||
friend VecT operator-(VecT, VecT) STD_OP RET;
|
||||
friend VecT operator/(VecT, VecT) STD_OP RET;
|
||||
friend VecT operator*(VecT, VecT) STD_OP RET;
|
||||
|
||||
friend VecT operator+(VecT, T) STD_OP RET;
|
||||
friend VecT operator-(VecT, T) STD_OP RET;
|
||||
friend VecT operator/(VecT, T) STD_OP RET;
|
||||
friend VecT operator*(VecT, T) STD_OP RET;
|
||||
|
||||
friend VecT operator+(T, VecT) STD_OP RET;
|
||||
friend VecT operator-(T, VecT) STD_OP RET;
|
||||
friend VecT operator/(T, VecT) STD_OP RET;
|
||||
friend VecT operator*(T, VecT) STD_OP RET;
|
||||
|
||||
friend VecT operator+=(VecT, VecT) STD_OP RET;
|
||||
friend VecT operator-=(VecT, VecT) STD_OP RET;
|
||||
friend VecT operator/=(VecT, VecT) STD_OP RET;
|
||||
friend VecT operator*=(VecT, VecT) STD_OP RET;
|
||||
|
||||
friend VecT operator+=(VecT, T) STD_OP RET;
|
||||
friend VecT operator-=(VecT, T) STD_OP RET;
|
||||
friend VecT operator/=(VecT, T) STD_OP RET;
|
||||
friend VecT operator*=(VecT, T) STD_OP RET;
|
||||
|
||||
#undef STD_OP
|
||||
|
||||
#define INT_OP requires IsIntegral<T>
|
||||
|
||||
friend VecT operator~(VecT) INT_OP RET;
|
||||
|
||||
friend VecT operator%(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator&(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator|(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator^(VecT, VecT) INT_OP RET;
|
||||
|
||||
friend VecT operator%(VecT, T) INT_OP RET;
|
||||
friend VecT operator&(VecT, T) INT_OP RET;
|
||||
friend VecT operator|(VecT, T) INT_OP RET;
|
||||
friend VecT operator^(VecT, T) INT_OP RET;
|
||||
|
||||
friend VecT operator%(T, VecT) INT_OP RET;
|
||||
friend VecT operator&(T, VecT) INT_OP RET;
|
||||
friend VecT operator|(T, VecT) INT_OP RET;
|
||||
friend VecT operator^(T, VecT) INT_OP RET;
|
||||
|
||||
friend VecT operator%=(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator&=(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator|=(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator^=(VecT, VecT) INT_OP RET;
|
||||
|
||||
friend VecT operator%=(VecT, T) INT_OP RET;
|
||||
friend VecT operator&=(VecT, T) INT_OP RET;
|
||||
friend VecT operator|=(VecT, T) INT_OP RET;
|
||||
friend VecT operator^=(VecT, T) INT_OP RET;
|
||||
|
||||
friend VecT operator<<(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator>>(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator<<=(VecT, VecT) INT_OP RET;
|
||||
friend VecT operator>>=(VecT, VecT) INT_OP RET;
|
||||
|
||||
friend VecT operator<<(T, VecT) INT_OP RET;
|
||||
friend VecT operator>>(T, VecT) INT_OP RET;
|
||||
friend VecT operator<<=(T, VecT) INT_OP RET;
|
||||
friend VecT operator>>=(T, VecT) INT_OP RET;
|
||||
|
||||
friend VecT operator<<(VecT, T) INT_OP RET;
|
||||
friend VecT operator>>(VecT, T) INT_OP RET;
|
||||
friend VecT operator<<=(VecT, T) INT_OP RET;
|
||||
friend VecT operator>>=(VecT, T) INT_OP RET;
|
||||
|
||||
#undef INT_OP
|
||||
};
|
||||
|
||||
template<typename T, int Sz> struct SwizzleBase : VecOp<T, Sz> {
|
||||
using VecT = VecBase<T, Sz>;
|
||||
|
||||
SwizzleBase() = default;
|
||||
SwizzleBase(T) {}
|
||||
|
||||
constexpr VecT operator=(const VecT &) RET;
|
||||
operator VecT() const RET;
|
||||
VecT operator()() const RET;
|
||||
};
|
||||
|
||||
#define SWIZZLE_XY(T) \
|
||||
SwizzleBase<T, 2> xx, xy, yx, yy; \
|
||||
SwizzleBase<T, 3> xxx, xxy, xyx, xyy, yxx, yxy, yyx, yyy; \
|
||||
SwizzleBase<T, 4> xxxx, xxxy, xxyx, xxyy, xyxx, xyxy, xyyx, xyyy, yxxx, yxxy, yxyx, yxyy, yyxx, \
|
||||
yyxy, yyyx, yyyy;
|
||||
|
||||
#define SWIZZLE_RG(T) \
|
||||
SwizzleBase<T, 2> rr, rg, gr, gg; \
|
||||
SwizzleBase<T, 3> rrr, rrg, rgr, rgg, grr, grg, ggr, ggg; \
|
||||
SwizzleBase<T, 4> rrrr, rrrg, rrgr, rrgg, rgrr, rgrg, rggr, rggg, grrr, grrg, grgr, grgg, ggrr, \
|
||||
ggrg, gggr, gggg;
|
||||
|
||||
#define SWIZZLE_XYZ(T) \
|
||||
SWIZZLE_XY(T) \
|
||||
SwizzleBase<T, 2> xz, yz, zx, zy, zz; \
|
||||
SwizzleBase<T, 3> xxz, xyz, xzx, xzy, xzz, yxz, yyz, yzx, yzy, yzz, zxx, zxy, zxz, zyx, zyy, \
|
||||
zyz, zzx, zzy, zzz; \
|
||||
SwizzleBase<T, 4> xxxz, xxyz, xxzx, xxzy, xxzz, xyxz, xyyz, xyzx, xyzy, xyzz, xzxx, xzxy, xzxz, \
|
||||
xzyx, xzyy, xzyz, xzzx, xzzy, xzzz, yxxz, yxyz, yxzx, yxzy, yxzz, yyxz, yyyz, yyzx, yyzy, \
|
||||
yyzz, yzxx, yzxy, yzxz, yzyx, yzyy, yzyz, yzzx, yzzy, yzzz, zxxx, zxxy, zxxz, zxyx, zxyy, \
|
||||
zxyz, zxzx, zxzy, zxzz, zyxx, zyxy, zyxz, zyyx, zyyy, zyyz, zyzx, zyzy, zyzz, zzxx, zzxy, \
|
||||
zzxz, zzyx, zzyy, zzyz, zzzx, zzzy, zzzz;
|
||||
|
||||
#define SWIZZLE_RGB(T) \
|
||||
SWIZZLE_RG(T) \
|
||||
SwizzleBase<T, 2> rb, gb, br, bg, bb; \
|
||||
SwizzleBase<T, 3> rrb, rgb, rbr, rbg, rbb, grb, ggb, gbr, gbg, gbb, brr, brg, brb, bgr, bgg, \
|
||||
bgb, bbr, bbg, bbb; \
|
||||
SwizzleBase<T, 4> rrrb, rrgb, rrbr, rrbg, rrbb, rgrb, rggb, rgbr, rgbg, rgbb, rbrr, rbrg, rbrb, \
|
||||
rbgr, rbgg, rbgb, rbbr, rbbg, rbbb, grrb, grgb, grbr, grbg, grbb, ggrb, gggb, ggbr, ggbg, \
|
||||
ggbb, gbrr, gbrg, gbrb, gbgr, gbgg, gbgb, gbbr, gbbg, gbbb, brrr, brrg, brrb, brgr, brgg, \
|
||||
brgb, brbr, brbg, brbb, bgrr, bgrg, bgrb, bggr, bggg, bggb, bgbr, bgbg, bgbb, bbrr, bbrg, \
|
||||
bbrb, bbgr, bbgg, bbgb, bbbr, bbbg, bbbb;
|
||||
|
||||
#define SWIZZLE_XYZW(T) \
|
||||
SWIZZLE_XYZ(T) \
|
||||
SwizzleBase<T, 2> xw, yw, zw, wx, wy, wz, ww; \
|
||||
SwizzleBase<T, 3> xxw, xyw, xzw, xwx, xwy, xwz, xww, yxw, yyw, yzw, ywx, ywy, ywz, yww, zxw, \
|
||||
zyw, zzw, zwx, zwy, zwz, zww, wxx, wxy, wxz, wxw, wyx, wyy, wyz, wyw, wzx, wzy, wzz, wzw, \
|
||||
wwx, wwy, wwz, www; \
|
||||
SwizzleBase<T, 4> xxxw, xxyw, xxzw, xxwx, xxwy, xxwz, xxww, xyxw, xyyw, xyzw, xywx, xywy, xywz, \
|
||||
xyww, xzxw, xzyw, xzzw, xzwx, xzwy, xzwz, xzww, xwxx, xwxy, xwxz, xwxw, xwyx, xwyy, xwyz, \
|
||||
xwyw, xwzx, xwzy, xwzz, xwzw, xwwx, xwwy, xwwz, xwww, yxxw, yxyw, yxzw, yxwx, yxwy, yxwz, \
|
||||
yxww, yyxw, yyyw, yyzw, yywx, yywy, yywz, yyww, yzxw, yzyw, yzzw, yzwx, yzwy, yzwz, yzww, \
|
||||
ywxx, ywxy, ywxz, ywxw, ywyx, ywyy, ywyz, ywyw, ywzx, ywzy, ywzz, ywzw, ywwx, ywwy, ywwz, \
|
||||
ywww, zxxw, zxyw, zxzw, zxwx, zxwy, zxwz, zxww, zyxw, zyyw, zyzw, zywx, zywy, zywz, zyww, \
|
||||
zzxw, zzyw, zzzw, zzwx, zzwy, zzwz, zzww, zwxx, zwxy, zwxz, zwxw, zwyx, zwyy, zwyz, zwyw, \
|
||||
zwzx, zwzy, zwzz, zwzw, zwwx, zwwy, zwwz, zwww, wxxx, wxxy, wxxz, wxxw, wxyx, wxyy, wxyz, \
|
||||
wxyw, wxzx, wxzy, wxzz, wxzw, wxwx, wxwy, wxwz, wxww, wyxx, wyxy, wyxz, wyxw, wyyx, wyyy, \
|
||||
wyyz, wyyw, wyzx, wyzy, wyzz, wyzw, wywx, wywy, wywz, wyww, wzxx, wzxy, wzxz, wzxw, wzyx, \
|
||||
wzyy, wzyz, wzyw, wzzx, wzzy, wzzz, wzzw, wzwx, wzwy, wzwz, wzww, wwxx, wwxy, wwxz, wwxw, \
|
||||
wwyx, wwyy, wwyz, wwyw, wwzx, wwzy, wwzz, wwzw, wwwx, wwwy, wwwz, wwww;
|
||||
|
||||
#define SWIZZLE_RGBA(T) \
|
||||
SWIZZLE_RGB(T) \
|
||||
SwizzleBase<T, 2> ra, ga, ba, ar, ag, ab, aa; \
|
||||
SwizzleBase<T, 3> rra, rga, rba, rar, rag, rab, raa, gra, gga, gba, gar, gag, gab, gaa, bra, \
|
||||
bga, bba, bar, bag, bab, baa, arr, arg, arb, ara, agr, agg, agb, aga, abr, abg, abb, aba, \
|
||||
aar, aag, aab, aaa; \
|
||||
SwizzleBase<T, 4> rrra, rrga, rrba, rrar, rrag, rrab, rraa, rgra, rgga, rgba, rgar, rgag, rgab, \
|
||||
rgaa, rbra, rbga, rbba, rbar, rbag, rbab, rbaa, rarr, rarg, rarb, rara, ragr, ragg, ragb, \
|
||||
raga, rabr, rabg, rabb, raba, raar, raag, raab, raaa, grra, grga, grba, grar, grag, grab, \
|
||||
graa, ggra, ggga, ggba, ggar, ggag, ggab, ggaa, gbra, gbga, gbba, gbar, gbag, gbab, gbaa, \
|
||||
garr, garg, garb, gara, gagr, gagg, gagb, gaga, gabr, gabg, gabb, gaba, gaar, gaag, gaab, \
|
||||
gaaa, brra, brga, brba, brar, brag, brab, braa, bgra, bgga, bgba, bgar, bgag, bgab, bgaa, \
|
||||
bbra, bbga, bbba, bbar, bbag, bbab, bbaa, barr, barg, barb, bara, bagr, bagg, bagb, baga, \
|
||||
babr, babg, babb, baba, baar, baag, baab, baaa, arrr, arrg, arrb, arra, argr, argg, argb, \
|
||||
arga, arbr, arbg, arbb, arba, arar, arag, arab, araa, agrr, agrg, agrb, agra, aggr, aggg, \
|
||||
aggb, agga, agbr, agbg, agbb, agba, agar, agag, agab, agaa, abrr, abrg, abrb, abra, abgr, \
|
||||
abgg, abgb, abga, abbr, abbg, abbb, abba, abar, abag, abab, abaa, aarr, aarg, aarb, aara, \
|
||||
aagr, aagg, aagb, aaga, aabr, aabg, aabb, aaba, aaar, aaag, aaab, aaaa;
|
||||
|
||||
template<typename T> struct VecBase<T, 1> {
|
||||
VecBase() = default;
|
||||
template<typename U> explicit VecBase(VecOp<U, 1>) {}
|
||||
VecBase(T) {}
|
||||
|
||||
operator T() RET;
|
||||
};
|
||||
|
||||
template<typename T> struct VecBase<T, 2> : VecOp<T, 2> {
|
||||
private:
|
||||
/* Weird non-zero value to avoid error about division by zero in constexpr. */
|
||||
static constexpr T V = T(0.123f);
|
||||
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
T x, y;
|
||||
};
|
||||
struct {
|
||||
T r, g;
|
||||
};
|
||||
SWIZZLE_XY(T);
|
||||
SWIZZLE_RG(T);
|
||||
};
|
||||
|
||||
VecBase() = default;
|
||||
template<typename U> explicit VecBase(VecOp<U, 2>) {}
|
||||
constexpr explicit VecBase(T) : x(V), y(V) {}
|
||||
/* Implemented correctly for GCC to compile the constexpr float2 arrays. */
|
||||
constexpr explicit VecBase(T x_, T y_) : x(x_), y(y_) {}
|
||||
};
|
||||
|
||||
template<typename T> struct VecBase<T, 3> : VecOp<T, 3> {
|
||||
private:
|
||||
/* Weird non-zero value to avoid error about division by zero in constexpr. */
|
||||
static constexpr T V = T(0.123f);
|
||||
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
T x, y, z;
|
||||
};
|
||||
struct {
|
||||
T r, g, b;
|
||||
};
|
||||
SWIZZLE_XYZ(T);
|
||||
SWIZZLE_RGB(T);
|
||||
};
|
||||
|
||||
VecBase() = default;
|
||||
template<typename U> explicit VecBase(VecOp<U, 3>) {}
|
||||
constexpr explicit VecBase(T) : x(V), y(V), z(V) {}
|
||||
/* Implemented correctly for GCC to compile the constexpr gl_WorkGroupSize. */
|
||||
constexpr explicit VecBase(T x_, T y_, T z_) : x(x_), y(y_), z(z_) {}
|
||||
constexpr explicit VecBase(VecOp<T, 2>, T) : x(V), y(V), z(V) {}
|
||||
constexpr explicit VecBase(T, VecOp<T, 2>) : x(V), y(V), z(V) {}
|
||||
};
|
||||
|
||||
template<typename T> struct VecBase<T, 4> : VecOp<T, 4> {
|
||||
private:
|
||||
/* Weird non-zero value to avoid error about division by zero in constexpr. */
|
||||
static constexpr T V = T(0.123f);
|
||||
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
T x, y, z, w;
|
||||
};
|
||||
struct {
|
||||
T r, g, b, a;
|
||||
};
|
||||
SWIZZLE_XYZW(T);
|
||||
SWIZZLE_RGBA(T);
|
||||
};
|
||||
|
||||
VecBase() = default;
|
||||
template<typename U> explicit VecBase(VecOp<U, 4>) {}
|
||||
constexpr explicit VecBase(T) : x(V), y(V), z(V), w(V) {}
|
||||
/* Implemented correctly for GCC to compile the constexpr. */
|
||||
constexpr explicit VecBase(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {}
|
||||
constexpr explicit VecBase(VecOp<T, 2>, T, T) : x(V), y(V), z(V), w(V) {}
|
||||
constexpr explicit VecBase(T, VecOp<T, 2>, T) : x(V), y(V), z(V), w(V) {}
|
||||
constexpr explicit VecBase(T, T, VecOp<T, 2>) : x(V), y(V), z(V), w(V) {}
|
||||
constexpr explicit VecBase(VecOp<T, 2>, VecOp<T, 2>) : x(V), y(V), z(V), w(V) {}
|
||||
constexpr explicit VecBase(VecOp<T, 3>, T) : x(V), y(V), z(V), w(V) {}
|
||||
constexpr explicit VecBase(T, VecOp<T, 3>) : x(V), y(V), z(V), w(V) {}
|
||||
};
|
||||
|
||||
/* Boolean vectors do not have operators and are not convertible from other types. */
|
||||
|
||||
template<> struct VecBase<bool, 2> : VecOp<bool, 2> {
|
||||
union {
|
||||
struct {
|
||||
bool x, y;
|
||||
};
|
||||
SWIZZLE_XY(bool);
|
||||
};
|
||||
|
||||
VecBase() = default;
|
||||
explicit VecBase(bool) {}
|
||||
explicit VecBase(bool, bool) {}
|
||||
/* Should be forbidden, but is used by SMAA. */
|
||||
explicit VecBase(VecOp<float, 2>) {}
|
||||
};
|
||||
|
||||
template<> struct VecBase<bool, 3> : VecOp<bool, 3> {
|
||||
union {
|
||||
struct {
|
||||
bool x, y, z;
|
||||
};
|
||||
SWIZZLE_XYZ(bool);
|
||||
};
|
||||
|
||||
VecBase() = default;
|
||||
explicit VecBase(bool) {}
|
||||
explicit VecBase(bool, bool, bool) {}
|
||||
explicit VecBase(VecOp<bool, 2>, bool) {}
|
||||
explicit VecBase(bool, VecOp<bool, 2>) {}
|
||||
};
|
||||
|
||||
template<> struct VecBase<bool, 4> : VecOp<bool, 4> {
|
||||
union {
|
||||
struct {
|
||||
bool x, y, z, w;
|
||||
};
|
||||
SWIZZLE_XYZW(bool);
|
||||
};
|
||||
|
||||
VecBase() = default;
|
||||
explicit VecBase(bool) {}
|
||||
explicit VecBase(bool, bool, bool, bool) {}
|
||||
explicit VecBase(VecOp<bool, 2>, bool, bool) {}
|
||||
explicit VecBase(bool, VecOp<bool, 2>, bool) {}
|
||||
explicit VecBase(bool, bool, VecOp<bool, 2>) {}
|
||||
explicit VecBase(VecOp<bool, 2>, VecOp<bool, 2>) {}
|
||||
explicit VecBase(VecOp<bool, 3>, bool) {}
|
||||
explicit VecBase(bool, VecOp<bool, 3>) {}
|
||||
};
|
||||
|
||||
using uint = unsigned int;
|
||||
using uint32_t = unsigned int; /* For typed enums. */
|
||||
|
||||
using float2 = VecBase<float, 2>;
|
||||
using float3 = VecBase<float, 3>;
|
||||
using float4 = VecBase<float, 4>;
|
||||
|
||||
using uint2 = VecBase<uint, 2>;
|
||||
using uint3 = VecBase<uint, 3>;
|
||||
using uint4 = VecBase<uint, 4>;
|
||||
|
||||
using int2 = VecBase<int, 2>;
|
||||
using int3 = VecBase<int, 3>;
|
||||
using int4 = VecBase<int, 4>;
|
||||
|
||||
using uchar = unsigned int;
|
||||
using uchar2 = VecBase<uchar, 2>;
|
||||
using uchar3 = VecBase<uchar, 3>;
|
||||
using uchar4 = VecBase<uchar, 4>;
|
||||
|
||||
using char2 = VecBase<char, 2>;
|
||||
using char3 = VecBase<char, 3>;
|
||||
using char4 = VecBase<char, 4>;
|
||||
|
||||
using ushort = unsigned short;
|
||||
using ushort2 = VecBase<ushort, 2>;
|
||||
using ushort3 = VecBase<ushort, 3>;
|
||||
using ushort4 = VecBase<ushort, 4>;
|
||||
|
||||
using short2 = VecBase<short, 2>;
|
||||
using short3 = VecBase<short, 3>;
|
||||
using short4 = VecBase<short, 4>;
|
||||
|
||||
using half = float;
|
||||
using half2 = VecBase<half, 2>;
|
||||
using half3 = VecBase<half, 3>;
|
||||
using half4 = VecBase<half, 4>;
|
||||
|
||||
using bool2 = VecBase<bool, 2>;
|
||||
using bool3 = VecBase<bool, 3>;
|
||||
using bool4 = VecBase<bool, 4>;
|
||||
|
||||
using bool32_t = uint;
|
||||
|
||||
/** Packed types are needed for MSL which have different alignment rules for float3. */
|
||||
using packed_float2 = float2;
|
||||
using packed_float3 = float3;
|
||||
using packed_float4 = float4;
|
||||
using packed_int2 = int2;
|
||||
using packed_int3 = int3;
|
||||
using packed_int4 = int4;
|
||||
using packed_uint2 = uint2;
|
||||
using packed_uint3 = uint3;
|
||||
using packed_uint4 = uint4;
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef RET
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_depth_only_infos.hh"
|
||||
|
||||
void main()
|
||||
{
|
||||
/* No color output, only depth (line below is implicit). */
|
||||
// gl_FragDepth = gl_FragCoord.z;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_diag_stripes_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_diag_stripes)
|
||||
|
||||
void main()
|
||||
{
|
||||
float phase = mod((gl_FragCoord.x + gl_FragCoord.y), float(size1 + size2));
|
||||
|
||||
if (phase < size1) {
|
||||
fragColor = color1;
|
||||
}
|
||||
else {
|
||||
fragColor = color2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_image_overlays_merge_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_cycles_display_fallback)
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = texture(image_texture, texCoord_interp);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_2D_image_overlays_merge_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpu_shader_cycles_display_fallback)
|
||||
|
||||
float2 normalize_coordinates()
|
||||
{
|
||||
return (float2(2.0f) * (pos / fullscreen)) - float2(1.0f);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = float4(normalize_coordinates(), 0.0f, 1.0f);
|
||||
texCoord_interp = texCoord;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-FileCopyrightText: 2016-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpu_shader_3D_flat_color_infos.hh"
|
||||
|
||||
#include "gpu_shader_colorspace_lib.glsl"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = finalColor;
|
||||
fragColor = blender_srgb_to_framebuffer_space(fragColor);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Always start a shader with #version directive and the required #extension.
|
||||
* The extensions are different depending on the backend and implementation support.
|
||||
* For this reason this file is generated at runtime.
|
||||
* The present implementation is just for reference and documentation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma runtime_generated
|
||||
|
||||
#if defined(GPU_OPENGL)
|
||||
/* We only require OpenGL 4.3. */
|
||||
# version 430
|
||||
/* Required: For draw call batching. */
|
||||
# extension GL_ARB_shader_draw_parameters : enable
|
||||
/* Optional: Avoid geometry shader for layered rendering. */
|
||||
# extension GL_ARB_shader_viewport_layer_array : enable
|
||||
/* Optional: Avoid geometry shader for barycentric coordinates. */
|
||||
# extension GL_AMD_shader_explicit_vertex_parameter : enable
|
||||
/* Optional: For sub-pass input emulation. */
|
||||
# extension GL_EXT_shader_framebuffer_fetch : enable
|
||||
/* Optional: For faster EEVEE GBuffer classification. */
|
||||
# extension GL_ARB_shader_stencil_export : enable
|
||||
|
||||
#elif defined(GPU_VULKAN)
|
||||
# version 450
|
||||
/* Required: For draw call batching. */
|
||||
# extension GL_ARB_shader_draw_parameters : enable
|
||||
/* Optional: Avoid geometry shader for layered rendering. */
|
||||
# extension GL_ARB_shader_viewport_layer_array : enable
|
||||
/* Optional: Avoid geometry shader for barycentric coordinates. */
|
||||
# extension GL_EXT_fragment_shader_barycentric : require
|
||||
/* Optional: For faster EEVEE GBuffer classification. */
|
||||
# extension GL_ARB_shader_stencil_export : enable
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user