Files
test2/source/blender/draw/intern/draw_shader.cc
Clément Foucault ba4589e894 DRW: New Curve Drawing
Implementation of the design task #142969.

This adds the following:
- Exact GPU interpolation of curves of all types.
- Radius attribute support.
- Cyclic curve support.
- Resolution attribute support.
- New Cylinder hair shape type.
![image.png](/attachments/a8e7aea0-b0e5-4694-b660-89fb3df1ddcd)

What changed:
- EEVEE doesn't compute random normals for strand hairs anymore. These are considered legacy now.
- EEVEE now have an internal shadow bias to avoid self shadowing on hair.
- Workbench Curves Strip display option is no longer flat and has better shading.
- Legacy Hair particle system evaluates radius at control points before applying additional subdivision. This now matches Cycles.
- Color Attribute Node without a name do not fetch the active color attribute anymore. This now matches Cycles.

Notes:
- This is not 100% matching the CPU implementation for interpolation (see the epsilons in the tests).
- Legacy Hair Particle points is now stored in local space after interpolation.

The new cylinder shape allows for more correct hair shading in workbench and better intersection in EEVEE.

|      | Strand | Strip | Cylinder |
| ---- | --- | --- | --- |
| Main | ![main_strand.png](/attachments/67d3b792-962c-4272-a92c-1c0c7c6cf8de) | ![main_strip.png](/attachments/f2aa3575-368e-4fbb-b888-74df845918f1) | N/A |
| PR   | ![pr_strand.png](/attachments/cc012483-25f0-491f-a06e-ad3029981d47) | ![pr_strip.png](/attachments/73fa2f5c-5252-4b30-a334-e935ed0fb938) | ![pr_cylinder.png](/attachments/3133b2d4-a6f2-41ee-8e2d-f6fd00db0c8d) |

|      | Strand | Strip | Cylinder |
| ---- | --- | --- | --- |
| Main | ![main_strand_closeup.png](/attachments/730bd79c-6762-446d-819b-3ea47961ff9f) |![main_strip_closeup.png](/attachments/d9ace578-cfeb-4895-9896-3625b6ad7a02) | N/A |
| PR   | ![pr_strand_closeup.png](/attachments/ac8f3b0c-6ef6-4d54-b714-6322f9865036)|![pr_strip_closeup.png](/attachments/8504711a-955b-4ab2-aa3d-c2d114baf9d4)| ![pr_cylinder_closeup.png](/attachments/1e2899a8-0a5c-431f-ac6c-5184d87e9598) |

Cyclic Curve, Mixed curve type, and proper radius support:
![image.png](/attachments/7f0bf05e-62ee-4ae9-aef9-a5599249b8d7)

Test file for attribute lookup: [test_attribute_lookup.blend](/attachments/1d54dd06-379b-4480-a1c5-96adc1953f77)

Follow Up Tasks:
- Correct full tube segments orientation based on tangent and normal attributes
- Correct V resolution property per object
- More attribute type support (currently only color)

TODO:
- [x] Attribute Loading Changes
  - [x] Generic Attributes
  - [x] Length Attribute
  - [x] Intercept Attribute
  - [x] Original Coordinate Attribute
- [x] Cyclic Curves
- [x] Legacy Hair Particle conversion
  - [x] Attribute Loading
  - [x] Additional Subdivision
- [x] Move some function to generic headers (VertBuf, OffsetIndices)
- [x] Fix default UV/Color attribute assignment

Pull Request: https://projects.blender.org/blender/blender/pulls/143180
2025-08-27 09:49:43 +02:00

235 lines
7.1 KiB
C++

/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#include "draw_shader.hh"
#include "GPU_batch.hh"
#include "GPU_capabilities.hh"
#include "opensubdiv_capi_type.hh"
#include "opensubdiv_evaluator_capi.hh"
#include "DRW_render.hh"
#define SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS 4
static blender::StringRefNull get_subdiv_shader_info_name(SubdivShaderType shader_type)
{
switch (shader_type) {
case SubdivShaderType::BUFFER_LINES:
return "subdiv_lines";
case SubdivShaderType::BUFFER_LINES_LOOSE:
return "subdiv_lines_loose";
case SubdivShaderType::BUFFER_TRIS:
return "subdiv_tris_single_material";
case SubdivShaderType::BUFFER_TRIS_MULTIPLE_MATERIALS:
return "subdiv_tris_multiple_materials";
case SubdivShaderType::BUFFER_EDGE_FAC:
return "subdiv_edge_fac";
case SubdivShaderType::BUFFER_SCULPT_DATA:
return "subdiv_sculpt_data";
case SubdivShaderType::PATCH_EVALUATION:
return "subdiv_patch_evaluation_verts";
case SubdivShaderType::PATCH_EVALUATION_FVAR:
return "subdiv_patch_evaluation_fvar";
case SubdivShaderType::PATCH_EVALUATION_FACE_DOTS:
return "subdiv_patch_evaluation_fdots";
case SubdivShaderType::PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS:
return "subdiv_patch_evaluation_fdots_normals";
case SubdivShaderType::PATCH_EVALUATION_ORCO:
return "subdiv_patch_evaluation_verts_orcos";
case SubdivShaderType::BUFFER_UV_STRETCH_ANGLE:
return "subdiv_edituv_stretch_angle";
case SubdivShaderType::BUFFER_UV_STRETCH_AREA:
return "subdiv_edituv_stretch_area";
case SubdivShaderType::BUFFER_NORMALS_ACCUMULATE:
return "subdiv_normals_accumulate";
case SubdivShaderType::BUFFER_PAINT_OVERLAY_FLAG:
return "subdiv_paint_overlay_flag";
case SubdivShaderType::BUFFER_LNOR:
return "subdiv_loop_normals";
case SubdivShaderType::COMP_CUSTOM_DATA_INTERP:
break;
default:
break;
}
BLI_assert_unreachable();
return "";
}
namespace blender::draw::Shader {
class ShaderCache {
static gpu::StaticShaderCache<ShaderCache> &get_static_cache()
{
static gpu::StaticShaderCache<ShaderCache> static_cache;
return static_cache;
}
public:
static ShaderCache &get()
{
return get_static_cache().get();
}
static void release()
{
get_static_cache().release();
}
gpu::StaticShader curves_topology = {"draw_curves_topology"};
gpu::StaticShader curves_evaluate_position = {"draw_curves_interpolate_position"};
gpu::StaticShader curves_evaluate_float4 = {"draw_curves_interpolate_float4_attribute"};
gpu::StaticShader curves_evaluate_float3 = {"draw_curves_interpolate_float3_attribute"};
gpu::StaticShader curves_evaluate_float2 = {"draw_curves_interpolate_float2_attribute"};
gpu::StaticShader curves_evaluate_float = {"draw_curves_interpolate_float_attribute"};
gpu::StaticShader curves_evaluate_length_intercept = {"draw_curves_evaluate_length_intercept"};
gpu::StaticShader debug_draw_display = {"draw_debug_draw_display"};
gpu::StaticShader draw_visibility_compute = {"draw_visibility_compute"};
gpu::StaticShader draw_view_finalize = {"draw_view_finalize"};
gpu::StaticShader draw_resource_finalize = {"draw_resource_finalize"};
gpu::StaticShader draw_command_generate = {"draw_command_generate"};
gpu::StaticShader subdiv_sh[SUBDIVISION_MAX_SHADERS];
gpu::StaticShader subdiv_custom_data_sh[SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS][GPU_COMP_MAX];
gpu::StaticShader subdiv_interp_corner_normals_sh = {
"subdiv_custom_data_interp_3d_f32_normalize"};
ShaderCache()
{
for (int i : IndexRange(SUBDIVISION_MAX_SHADERS)) {
if (SubdivShaderType(i) == SubdivShaderType::COMP_CUSTOM_DATA_INTERP) {
continue;
}
subdiv_sh[i] = {get_subdiv_shader_info_name(SubdivShaderType(i)).c_str()};
}
for (int dimension : IndexRange(SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS)) {
for (GPUVertCompType comp_type : {GPU_COMP_U16, GPU_COMP_I32, GPU_COMP_F32}) {
std::string info_name = "subdiv_custom_data_interp";
const char *dimension_names[] = {"_1d", "_2d", "_3d", "_4d"};
info_name += dimension_names[dimension];
switch (comp_type) {
case GPU_COMP_U16:
info_name += "_u16";
break;
case GPU_COMP_I32:
info_name += "_i32";
break;
case GPU_COMP_F32:
info_name += "_f32";
break;
default:
BLI_assert_unreachable();
}
subdiv_custom_data_sh[dimension][comp_type] = {info_name};
}
}
}
};
} // namespace blender::draw::Shader
using namespace blender::draw::Shader;
blender::gpu::Shader *DRW_shader_curves_topology_get()
{
return ShaderCache::get().curves_topology.get();
}
blender::gpu::Shader *DRW_shader_curves_refine_get(blender::draw::CurvesEvalShader type)
{
switch (type) {
case blender::draw::CURVES_EVAL_POSITION:
return ShaderCache::get().curves_evaluate_position.get();
case blender::draw::CURVES_EVAL_FLOAT4:
return ShaderCache::get().curves_evaluate_float4.get();
case blender::draw::CURVES_EVAL_FLOAT3:
return ShaderCache::get().curves_evaluate_float3.get();
case blender::draw::CURVES_EVAL_FLOAT2:
return ShaderCache::get().curves_evaluate_float2.get();
case blender::draw::CURVES_EVAL_FLOAT:
return ShaderCache::get().curves_evaluate_float.get();
case blender::draw::CURVES_EVAL_LENGTH_INTERCEPT:
return ShaderCache::get().curves_evaluate_length_intercept.get();
}
BLI_assert_unreachable();
return nullptr;
}
blender::gpu::Shader *DRW_shader_debug_draw_display_get()
{
return ShaderCache::get().debug_draw_display.get();
}
blender::gpu::Shader *DRW_shader_draw_visibility_compute_get()
{
return ShaderCache::get().draw_visibility_compute.get();
}
blender::gpu::Shader *DRW_shader_draw_view_finalize_get()
{
return ShaderCache::get().draw_view_finalize.get();
}
blender::gpu::Shader *DRW_shader_draw_resource_finalize_get()
{
return ShaderCache::get().draw_resource_finalize.get();
}
blender::gpu::Shader *DRW_shader_draw_command_generate_get()
{
return ShaderCache::get().draw_command_generate.get();
}
blender::gpu::Shader *DRW_shader_subdiv_get(SubdivShaderType shader_type)
{
BLI_assert(!ELEM(shader_type, SubdivShaderType::COMP_CUSTOM_DATA_INTERP));
return ShaderCache::get().subdiv_sh[uint(shader_type)].get();
}
blender::gpu::Shader *DRW_shader_subdiv_custom_data_get(GPUVertCompType comp_type, int dimensions)
{
BLI_assert(dimensions >= 1 && dimensions <= SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS);
if (comp_type == GPU_COMP_U16) {
BLI_assert(dimensions == 4);
}
BLI_assert(ELEM(comp_type, GPU_COMP_U16, GPU_COMP_I32, GPU_COMP_F32));
return ShaderCache::get().subdiv_custom_data_sh[dimensions - 1][comp_type].get();
}
blender::gpu::Shader *DRW_shader_subdiv_interp_corner_normals_get()
{
return ShaderCache::get().subdiv_interp_corner_normals_sh.get();
}
void DRW_shaders_free()
{
GPU_shader_unbind();
ShaderCache::release();
}