feat(widget): add rounded rectangle mask widget and shader widget base
- Implemented `rounded_rect_mask_widget` for creating rounded rectangle masks with customizable corner radii, feathering, and border width. - Introduced `shader_widget` and `shader_widget_base` classes for managing shader resources, including pipeline and descriptor set creation. - Added concepts for shader bindings to ensure type safety and proper usage of shader resources. - Defined enums for shader widget modes and dirty flags to manage widget state and updates effectively. - Created utility functions for bitwise operations on dirty flags to simplify state management.
This commit is contained in:
@@ -121,6 +121,7 @@ add_subdirectory(src/window)
|
||||
add_subdirectory(src/resource)
|
||||
add_subdirectory(src/shader)
|
||||
add_subdirectory(src/render)
|
||||
add_subdirectory(src/widget)
|
||||
add_subdirectory(src/text)
|
||||
add_subdirectory(src/input)
|
||||
add_subdirectory(src/reactive)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
# ============================================================================
|
||||
# MIRAI 着色器编译 CMake 模块
|
||||
# ============================================================================
|
||||
# 提供自动化着色器编译和代码生成功能
|
||||
# 自动检测 [shader("xxx")] 属性并编译所有入口点
|
||||
# 提供自动化 GLSL 着色器编译和代码生成功能
|
||||
# 使用 glslangValidator 或 glslc (shaderc) 编译 GLSL 到 SPIR-V
|
||||
#
|
||||
# 主要函数:
|
||||
# add_shader_library() - 创建着色器库目标
|
||||
# add_glsl_shader_library() - 创建 GLSL 着色器库目标
|
||||
# compile_glsl_shader() - 编译单个 GLSL 着色器
|
||||
#
|
||||
# 辅助函数:
|
||||
# find_python_with_jinja2() - 查找带 Jinja2 的 Python
|
||||
# find_glsl_compiler() - 查找 GLSL 编译器
|
||||
# ============================================================================
|
||||
|
||||
include_guard(GLOBAL)
|
||||
@@ -44,6 +46,46 @@ function(find_python_with_jinja2 OUT_PYTHON_EXECUTABLE)
|
||||
set(${OUT_PYTHON_EXECUTABLE} "${Python3_EXECUTABLE}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# ============================================================================
|
||||
# find_glsl_compiler - 查找 GLSL 编译器 (glslangValidator 或 glslc)
|
||||
# ============================================================================
|
||||
function(find_glsl_compiler OUT_COMPILER OUT_COMPILER_TYPE)
|
||||
# 首先尝试查找 glslc (shaderc)
|
||||
find_program(GLSLC_EXECUTABLE glslc
|
||||
HINTS
|
||||
$ENV{VULKAN_SDK}/Bin
|
||||
$ENV{VULKAN_SDK}/bin
|
||||
${Vulkan_GLSLC_EXECUTABLE}
|
||||
)
|
||||
|
||||
if(GLSLC_EXECUTABLE)
|
||||
message(STATUS "Found GLSL compiler: ${GLSLC_EXECUTABLE} (glslc)")
|
||||
set(${OUT_COMPILER} "${GLSLC_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(${OUT_COMPILER_TYPE} "glslc" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# 尝试查找 glslangValidator
|
||||
find_program(GLSLANG_VALIDATOR_EXECUTABLE glslangValidator
|
||||
HINTS
|
||||
$ENV{VULKAN_SDK}/Bin
|
||||
$ENV{VULKAN_SDK}/bin
|
||||
${Vulkan_GLSLANG_VALIDATOR_EXECUTABLE}
|
||||
)
|
||||
|
||||
if(GLSLANG_VALIDATOR_EXECUTABLE)
|
||||
message(STATUS "Found GLSL compiler: ${GLSLANG_VALIDATOR_EXECUTABLE} (glslangValidator)")
|
||||
set(${OUT_COMPILER} "${GLSLANG_VALIDATOR_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(${OUT_COMPILER_TYPE} "glslangValidator" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(FATAL_ERROR
|
||||
"No GLSL compiler found. Please install Vulkan SDK or shaderc.\n"
|
||||
"Expected: glslc or glslangValidator in PATH or VULKAN_SDK"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# ============================================================================
|
||||
# 内部变量设置
|
||||
# ============================================================================
|
||||
@@ -65,73 +107,188 @@ function(_mirai_ensure_shader_compiler)
|
||||
if(NOT TARGET ${MIRAI_SHADER_COMPILER_TARGET})
|
||||
message(FATAL_ERROR
|
||||
"Shader compiler target '${MIRAI_SHADER_COMPILER_TARGET}' not found.\n"
|
||||
"Make sure add_subdirectory(tools/shader_compile) is called before using add_shader_library()."
|
||||
"Make sure add_subdirectory(tools/shader_compile) is called before using add_glsl_shader_library()."
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# ============================================================================
|
||||
# add_shader_library - 添加着色器库到现有目标
|
||||
# _get_shader_stage_from_extension - 从文件扩展名获取着色器阶段
|
||||
# ============================================================================
|
||||
function(_get_shader_stage_from_extension FILE_PATH OUT_STAGE)
|
||||
get_filename_component(EXT "${FILE_PATH}" EXT)
|
||||
string(TOLOWER "${EXT}" EXT_LOWER)
|
||||
|
||||
if(EXT_LOWER STREQUAL ".vert")
|
||||
set(${OUT_STAGE} "vert" PARENT_SCOPE)
|
||||
elseif(EXT_LOWER STREQUAL ".frag")
|
||||
set(${OUT_STAGE} "frag" PARENT_SCOPE)
|
||||
elseif(EXT_LOWER STREQUAL ".comp")
|
||||
set(${OUT_STAGE} "comp" PARENT_SCOPE)
|
||||
elseif(EXT_LOWER STREQUAL ".geom")
|
||||
set(${OUT_STAGE} "geom" PARENT_SCOPE)
|
||||
elseif(EXT_LOWER STREQUAL ".tesc")
|
||||
set(${OUT_STAGE} "tesc" PARENT_SCOPE)
|
||||
elseif(EXT_LOWER STREQUAL ".tese")
|
||||
set(${OUT_STAGE} "tese" PARENT_SCOPE)
|
||||
else()
|
||||
set(${OUT_STAGE} "" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# ============================================================================
|
||||
# compile_glsl_shader - 编译单个 GLSL 着色器
|
||||
# ============================================================================
|
||||
#
|
||||
# 将着色器编译和代码生成附加到现有的编译目标上
|
||||
# 自动检测所有 [shader("xxx")] 入口点并编译
|
||||
# 用法:
|
||||
# compile_glsl_shader(
|
||||
# INPUT shader.vert
|
||||
# OUTPUT shader.vert.spv
|
||||
# STAGE vert
|
||||
# DEFINES DEBUG FEATURE_X
|
||||
# INCLUDE_DIRS include/
|
||||
# )
|
||||
#
|
||||
# 参数:
|
||||
# INPUT - 输入 GLSL 文件路径(必需)
|
||||
# OUTPUT - 输出 SPIR-V 文件路径(必需)
|
||||
# STAGE - 着色器阶段 (vert/frag/comp/geom/tesc/tese)(可选,自动检测)
|
||||
# DEFINES - 预处理器定义(可选)
|
||||
# INCLUDE_DIRS - include 目录(可选)
|
||||
#
|
||||
function(compile_glsl_shader)
|
||||
cmake_parse_arguments(SHADER "" "INPUT;OUTPUT;STAGE" "DEFINES;INCLUDE_DIRS" ${ARGN})
|
||||
|
||||
# 验证必需参数
|
||||
if(NOT SHADER_INPUT)
|
||||
message(FATAL_ERROR "compile_glsl_shader: INPUT is required")
|
||||
endif()
|
||||
if(NOT SHADER_OUTPUT)
|
||||
message(FATAL_ERROR "compile_glsl_shader: OUTPUT is required")
|
||||
endif()
|
||||
|
||||
# 自动检测着色器阶段
|
||||
if(NOT SHADER_STAGE)
|
||||
_get_shader_stage_from_extension("${SHADER_INPUT}" SHADER_STAGE)
|
||||
if(NOT SHADER_STAGE)
|
||||
message(FATAL_ERROR "compile_glsl_shader: Cannot determine shader stage from extension. Please specify STAGE.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# 查找 GLSL 编译器
|
||||
find_glsl_compiler(GLSL_COMPILER GLSL_COMPILER_TYPE)
|
||||
|
||||
# 构建编译命令参数
|
||||
set(COMPILE_ARGS "")
|
||||
|
||||
if(GLSL_COMPILER_TYPE STREQUAL "glslc")
|
||||
# glslc 参数
|
||||
list(APPEND COMPILE_ARGS "-fshader-stage=${SHADER_STAGE}")
|
||||
list(APPEND COMPILE_ARGS "--target-env=vulkan1.3")
|
||||
list(APPEND COMPILE_ARGS "-o" "${SHADER_OUTPUT}")
|
||||
|
||||
# 添加 include 目录
|
||||
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
|
||||
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||
endforeach()
|
||||
|
||||
# 添加宏定义
|
||||
foreach(DEF ${SHADER_DEFINES})
|
||||
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||
endforeach()
|
||||
|
||||
list(APPEND COMPILE_ARGS "${SHADER_INPUT}")
|
||||
else()
|
||||
# glslangValidator 参数
|
||||
list(APPEND COMPILE_ARGS "-V") # 生成 SPIR-V
|
||||
list(APPEND COMPILE_ARGS "--target-env" "vulkan1.3")
|
||||
list(APPEND COMPILE_ARGS "-S" "${SHADER_STAGE}")
|
||||
list(APPEND COMPILE_ARGS "-o" "${SHADER_OUTPUT}")
|
||||
|
||||
# 添加 include 目录
|
||||
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
|
||||
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||
endforeach()
|
||||
|
||||
# 添加宏定义
|
||||
foreach(DEF ${SHADER_DEFINES})
|
||||
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||
endforeach()
|
||||
|
||||
list(APPEND COMPILE_ARGS "${SHADER_INPUT}")
|
||||
endif()
|
||||
|
||||
# 创建编译命令
|
||||
add_custom_command(
|
||||
OUTPUT "${SHADER_OUTPUT}"
|
||||
COMMAND ${GLSL_COMPILER} ${COMPILE_ARGS}
|
||||
DEPENDS "${SHADER_INPUT}"
|
||||
COMMENT "Compiling GLSL shader: ${SHADER_INPUT}"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# ============================================================================
|
||||
# add_glsl_shader_library - 添加 GLSL 着色器库到现有目标
|
||||
# ============================================================================
|
||||
#
|
||||
# 将 GLSL 着色器编译和代码生成附加到现有的编译目标上
|
||||
#
|
||||
# 用法:
|
||||
# # 方案1: 自动搜索着色器,指定引用路径
|
||||
# add_shader_library(TARGET mirai_shader
|
||||
# SHADER_PATH shaders
|
||||
# REF_PATHS shaders/common shaders/math
|
||||
# # 方案1: 自动搜索着色器
|
||||
# add_glsl_shader_library(TARGET mirai_shader
|
||||
# SHADER_PATH shaders/glsl
|
||||
# INCLUDE_DIRS shaders/glsl/common
|
||||
# )
|
||||
#
|
||||
# # 方案2: 指定具体着色器文件
|
||||
# add_shader_library(TARGET mirai_shader
|
||||
# SHADERS shaders/ui/rect.slang shaders/ui/text.slang
|
||||
# REF_PATHS shaders/common
|
||||
# )
|
||||
#
|
||||
# # 方案3: 混合模式,添加额外着色器
|
||||
# add_shader_library(TARGET mirai_shader
|
||||
# SHADER_PATH shaders
|
||||
# SHADERS shaders/ui/extra.slang
|
||||
# REF_PATHS shaders/common
|
||||
# add_glsl_shader_library(TARGET mirai_shader
|
||||
# SHADERS shaders/ui/rect.vert shaders/ui/rect.frag
|
||||
# INCLUDE_DIRS shaders/common
|
||||
# )
|
||||
#
|
||||
# 参数:
|
||||
# TARGET - 现有的编译目标(必需)
|
||||
# SHADERS - 具体的着色器文件列表(可选)
|
||||
# SHADER_PATH - 着色器搜索目录,相对于 CMAKE_CURRENT_SOURCE_DIR(可选)
|
||||
# REF_PATHS - 着色器引用路径,用于 include 通用模块(可选)
|
||||
# INCLUDE_DIRS - 着色器 include 路径(可选)
|
||||
# DEFINES - 预处理器定义(可选)
|
||||
# RECURSIVE - 是否递归搜索子目录(可选,默认 OFF)
|
||||
#
|
||||
function(add_shader_library)
|
||||
function(add_glsl_shader_library)
|
||||
# 解析参数
|
||||
set(options RECURSIVE)
|
||||
set(oneValueArgs TARGET SHADER_PATH)
|
||||
set(multiValueArgs SHADERS REF_PATHS DEFINES)
|
||||
set(multiValueArgs SHADERS INCLUDE_DIRS DEFINES)
|
||||
cmake_parse_arguments(SHADER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# 验证必需参数
|
||||
if(NOT SHADER_TARGET)
|
||||
message(FATAL_ERROR "add_shader_library: TARGET is required")
|
||||
message(FATAL_ERROR "add_glsl_shader_library: TARGET is required")
|
||||
endif()
|
||||
|
||||
# 验证目标存在
|
||||
if(NOT TARGET ${SHADER_TARGET})
|
||||
message(FATAL_ERROR "add_shader_library: Target '${SHADER_TARGET}' does not exist")
|
||||
message(FATAL_ERROR "add_glsl_shader_library: Target '${SHADER_TARGET}' does not exist")
|
||||
endif()
|
||||
|
||||
# 收集着色器文件
|
||||
set(ALL_SHADERS "${SHADER_SHADERS}")
|
||||
|
||||
if(SHADER_SHADER_PATH)
|
||||
set(SHADER_EXTENSIONS "vert;frag;comp;geom;tesc;tese")
|
||||
|
||||
if(SHADER_RECURSIVE)
|
||||
retrieve_files_custom(${CMAKE_CURRENT_SOURCE_DIR} "slang" FOUND_SHADERS)
|
||||
foreach(EXT ${SHADER_EXTENSIONS})
|
||||
file(GLOB_RECURSE FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.${EXT}")
|
||||
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
|
||||
endforeach()
|
||||
else()
|
||||
file(GLOB FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.slang")
|
||||
foreach(EXT ${SHADER_EXTENSIONS})
|
||||
file(GLOB FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.${EXT}")
|
||||
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
|
||||
endforeach()
|
||||
endif()
|
||||
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
|
||||
endif()
|
||||
|
||||
# 去重
|
||||
@@ -152,71 +309,145 @@ function(add_shader_library)
|
||||
# 查找 Python with Jinja2
|
||||
find_python_with_jinja2(PYTHON_EXECUTABLE)
|
||||
|
||||
# 查找 GLSL 编译器
|
||||
find_glsl_compiler(GLSL_COMPILER GLSL_COMPILER_TYPE)
|
||||
|
||||
# 构建 include 路径参数
|
||||
set(INCLUDE_ARGS "")
|
||||
foreach(INC_DIR ${SHADER_REF_PATHS})
|
||||
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
|
||||
if(IS_ABSOLUTE "${INC_DIR}")
|
||||
list(APPEND INCLUDE_ARGS "-I${INC_DIR}")
|
||||
list(APPEND INCLUDE_ARGS "${INC_DIR}")
|
||||
else()
|
||||
list(APPEND INCLUDE_ARGS "-I${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}")
|
||||
list(APPEND INCLUDE_ARGS "${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# 构建宏定义参数
|
||||
set(DEFINE_ARGS "")
|
||||
foreach(DEF ${SHADER_DEFINES})
|
||||
list(APPEND DEFINE_ARGS "-D${DEF}")
|
||||
endforeach()
|
||||
|
||||
# 收集生成的文件
|
||||
set(ALL_GENERATED_HEADERS "")
|
||||
set(ALL_SPV_FILES "")
|
||||
|
||||
# 处理每个着色器文件
|
||||
# 按着色器名称分组(去掉扩展名后相同的文件归为一组)
|
||||
set(SHADER_GROUPS "")
|
||||
foreach(SHADER_SOURCE ${ALL_SHADERS})
|
||||
get_filename_component(SHADER_NAME_WE "${SHADER_SOURCE}" NAME_WE)
|
||||
get_filename_component(SHADER_DIR "${SHADER_SOURCE}" DIRECTORY)
|
||||
|
||||
if(IS_ABSOLUTE "${SHADER_SOURCE}")
|
||||
set(SHADER_ABS "${SHADER_SOURCE}")
|
||||
else()
|
||||
set(SHADER_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SOURCE}")
|
||||
if(NOT EXISTS "${SHADER_ABS}")
|
||||
message(FATAL_ERROR "Shader not found: ${SHADER_SOURCE}")
|
||||
# 创建唯一的组标识符
|
||||
string(REPLACE "/" "_" DIR_SAFE "${SHADER_DIR}")
|
||||
set(GROUP_ID "${DIR_SAFE}_${SHADER_NAME_WE}")
|
||||
|
||||
# 添加到组列表
|
||||
list(APPEND SHADER_GROUPS "${GROUP_ID}")
|
||||
list(APPEND ${GROUP_ID}_SOURCES "${SHADER_SOURCE}")
|
||||
set(${GROUP_ID}_NAME "${SHADER_NAME_WE}")
|
||||
set(${GROUP_ID}_DIR "${SHADER_DIR}")
|
||||
endforeach()
|
||||
|
||||
# 去重组列表
|
||||
if(SHADER_GROUPS)
|
||||
list(REMOVE_DUPLICATES SHADER_GROUPS)
|
||||
endif()
|
||||
|
||||
# 处理每个着色器组
|
||||
foreach(GROUP_ID ${SHADER_GROUPS})
|
||||
set(GROUP_SOURCES "${${GROUP_ID}_SOURCES}")
|
||||
set(GROUP_NAME "${${GROUP_ID}_NAME}")
|
||||
set(GROUP_DIR "${${GROUP_ID}_DIR}")
|
||||
|
||||
# 每个着色器组的输出目录
|
||||
set(SPIRV_OUTPUT_DIR "${SHADER_INTERMEDIATE_DIR}/${GROUP_NAME}")
|
||||
set(GENERATED_HPP "${SHADER_OUTPUT_DIR}/${GROUP_NAME}_bindings.hpp")
|
||||
|
||||
file(MAKE_DIRECTORY "${SPIRV_OUTPUT_DIR}")
|
||||
|
||||
set(GROUP_SPV_FILES "")
|
||||
|
||||
# 编译组内的每个着色器
|
||||
foreach(SHADER_SOURCE ${GROUP_SOURCES})
|
||||
get_filename_component(SHADER_FILENAME "${SHADER_SOURCE}" NAME)
|
||||
_get_shader_stage_from_extension("${SHADER_SOURCE}" SHADER_STAGE)
|
||||
|
||||
if(NOT SHADER_STAGE)
|
||||
message(WARNING "Skipping unknown shader type: ${SHADER_SOURCE}")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(SPV_OUTPUT "${SPIRV_OUTPUT_DIR}/${SHADER_FILENAME}.spv")
|
||||
|
||||
# 构建编译命令参数
|
||||
set(COMPILE_ARGS "")
|
||||
|
||||
if(GLSL_COMPILER_TYPE STREQUAL "glslc")
|
||||
list(APPEND COMPILE_ARGS "-fshader-stage=${SHADER_STAGE}")
|
||||
list(APPEND COMPILE_ARGS "--target-env=vulkan1.3")
|
||||
list(APPEND COMPILE_ARGS "-o" "${SPV_OUTPUT}")
|
||||
|
||||
foreach(INC_DIR ${INCLUDE_ARGS})
|
||||
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||
endforeach()
|
||||
|
||||
foreach(DEF ${SHADER_DEFINES})
|
||||
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||
endforeach()
|
||||
|
||||
list(APPEND COMPILE_ARGS "${SHADER_SOURCE}")
|
||||
else()
|
||||
list(APPEND COMPILE_ARGS "-V")
|
||||
list(APPEND COMPILE_ARGS "--target-env" "vulkan1.3")
|
||||
list(APPEND COMPILE_ARGS "-S" "${SHADER_STAGE}")
|
||||
list(APPEND COMPILE_ARGS "-o" "${SPV_OUTPUT}")
|
||||
|
||||
foreach(INC_DIR ${INCLUDE_ARGS})
|
||||
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||
endforeach()
|
||||
|
||||
foreach(DEF ${SHADER_DEFINES})
|
||||
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||
endforeach()
|
||||
|
||||
list(APPEND COMPILE_ARGS "${SHADER_SOURCE}")
|
||||
endif()
|
||||
|
||||
# 创建编译命令
|
||||
add_custom_command(
|
||||
OUTPUT "${SPV_OUTPUT}"
|
||||
COMMAND ${GLSL_COMPILER} ${COMPILE_ARGS}
|
||||
DEPENDS "${SHADER_SOURCE}"
|
||||
COMMENT "Compiling GLSL: ${SHADER_FILENAME}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
list(APPEND GROUP_SPV_FILES "${SPV_OUTPUT}")
|
||||
list(APPEND ALL_SPV_FILES "${SPV_OUTPUT}")
|
||||
endforeach()
|
||||
|
||||
# 使用 mirai_shader_compile 工具生成反射和绑定
|
||||
if(GROUP_SPV_FILES)
|
||||
# 创建标记文件表示编译完成
|
||||
set(COMPILED_MARKER "${SPIRV_OUTPUT_DIR}/.compiled")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${COMPILED_MARKER}"
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${COMPILED_MARKER}"
|
||||
DEPENDS ${GROUP_SPV_FILES}
|
||||
COMMENT "GLSL shaders compiled: ${GROUP_NAME}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# 生成绑定头文件
|
||||
add_custom_command(
|
||||
OUTPUT "${GENERATED_HPP}"
|
||||
COMMAND $<TARGET_FILE:${MIRAI_SHADER_COMPILER_TARGET}>
|
||||
--dir "${SPIRV_OUTPUT_DIR}"
|
||||
--output "${GENERATED_HPP}"
|
||||
--name "${GROUP_NAME}"
|
||||
DEPENDS "${COMPILED_MARKER}" ${MIRAI_SHADER_COMPILER_TARGET}
|
||||
COMMENT "Generating bindings: ${GROUP_NAME}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
list(APPEND ALL_GENERATED_HEADERS "${GENERATED_HPP}")
|
||||
endif()
|
||||
|
||||
# 每个着色器文件的输出目录
|
||||
set(SPIRV_OUTPUT_DIR "${SHADER_INTERMEDIATE_DIR}/${SHADER_NAME_WE}")
|
||||
set(GENERATED_HPP "${SHADER_OUTPUT_DIR}/${SHADER_NAME_WE}_bindings.hpp")
|
||||
|
||||
# 步骤1: 编译着色器(自动检测所有入口点)
|
||||
add_custom_command(
|
||||
OUTPUT "${SPIRV_OUTPUT_DIR}/.compiled"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${SPIRV_OUTPUT_DIR}"
|
||||
COMMAND $<TARGET_FILE:${MIRAI_SHADER_COMPILER_TARGET}>
|
||||
"${SHADER_ABS}"
|
||||
-o "${SPIRV_OUTPUT_DIR}"
|
||||
${INCLUDE_ARGS}
|
||||
${DEFINE_ARGS}
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${SPIRV_OUTPUT_DIR}/.compiled"
|
||||
DEPENDS "${SHADER_ABS}" ${MIRAI_SHADER_COMPILER_TARGET}
|
||||
COMMENT "Compiling shader: ${SHADER_NAME_WE}"
|
||||
VERBATIM COMMAND_EXPAND_LISTS
|
||||
)
|
||||
|
||||
# 步骤2: 生成绑定头文件(Python 脚本自己查找 spv 文件)
|
||||
add_custom_command(
|
||||
OUTPUT "${GENERATED_HPP}"
|
||||
COMMAND ${PYTHON_EXECUTABLE} "${MIRAI_SHADER_GENERATOR_SCRIPT}"
|
||||
--dir "${SPIRV_OUTPUT_DIR}"
|
||||
--output "${GENERATED_HPP}"
|
||||
--name "${SHADER_NAME_WE}"
|
||||
--template-dir "${MIRAI_SHADER_TEMPLATE_DIR}"
|
||||
DEPENDS "${SPIRV_OUTPUT_DIR}/.compiled" "${MIRAI_SHADER_GENERATOR_SCRIPT}"
|
||||
COMMENT "Generating bindings: ${SHADER_NAME_WE}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
list(APPEND ALL_GENERATED_HEADERS "${GENERATED_HPP}")
|
||||
endforeach()
|
||||
|
||||
# 附加到目标:添加生成目录到 include 路径
|
||||
@@ -233,10 +464,52 @@ function(add_shader_library)
|
||||
# 导出变量
|
||||
set(${SHADER_TARGET}_SHADER_HEADERS ${ALL_GENERATED_HEADERS} PARENT_SCOPE)
|
||||
set(${SHADER_TARGET}_SHADER_OUTPUT_DIR ${SHADER_OUTPUT_DIR} PARENT_SCOPE)
|
||||
set(${SHADER_TARGET}_SPV_FILES ${ALL_SPV_FILES} PARENT_SCOPE)
|
||||
|
||||
message(STATUS "Added shaders to target: ${SHADER_TARGET}")
|
||||
message(STATUS "Added GLSL shaders to target: ${SHADER_TARGET}")
|
||||
message(STATUS " Output: ${SHADER_OUTPUT_DIR}")
|
||||
if(ALL_SHADERS)
|
||||
message(STATUS " Shaders: ${ALL_SHADERS}")
|
||||
list(LENGTH ALL_SHADERS SHADER_COUNT)
|
||||
message(STATUS " Shaders: ${SHADER_COUNT} files")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# ============================================================================
|
||||
# add_shader_library - 兼容性别名(调用 add_glsl_shader_library)
|
||||
# ============================================================================
|
||||
#
|
||||
# 为了向后兼容,保留 add_shader_library 函数名
|
||||
# 现在默认使用 GLSL 编译
|
||||
#
|
||||
function(add_shader_library)
|
||||
# 解析参数
|
||||
set(options RECURSIVE)
|
||||
set(oneValueArgs TARGET SHADER_PATH)
|
||||
set(multiValueArgs SHADERS REF_PATHS DEFINES)
|
||||
cmake_parse_arguments(SHADER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# 转换参数并调用新函数
|
||||
set(FORWARD_ARGS "TARGET" "${SHADER_TARGET}")
|
||||
|
||||
if(SHADER_SHADER_PATH)
|
||||
list(APPEND FORWARD_ARGS "SHADER_PATH" "${SHADER_SHADER_PATH}")
|
||||
endif()
|
||||
|
||||
if(SHADER_SHADERS)
|
||||
list(APPEND FORWARD_ARGS "SHADERS" ${SHADER_SHADERS})
|
||||
endif()
|
||||
|
||||
if(SHADER_REF_PATHS)
|
||||
list(APPEND FORWARD_ARGS "INCLUDE_DIRS" ${SHADER_REF_PATHS})
|
||||
endif()
|
||||
|
||||
if(SHADER_DEFINES)
|
||||
list(APPEND FORWARD_ARGS "DEFINES" ${SHADER_DEFINES})
|
||||
endif()
|
||||
|
||||
if(SHADER_RECURSIVE)
|
||||
list(APPEND FORWARD_ARGS "RECURSIVE")
|
||||
endif()
|
||||
|
||||
add_glsl_shader_library(${FORWARD_ARGS})
|
||||
endfunction()
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
---
|
||||
version: 0.1.0
|
||||
status: accepted
|
||||
status: superseded
|
||||
created: 2024-01-15
|
||||
superseded_by: adr-003-glsl-shader-system.md
|
||||
author: MIRAI Team
|
||||
---
|
||||
|
||||
# ADR-002: 选择 Slang 作为着色器语言
|
||||
|
||||
> **⚠️ 已废弃**:本 ADR 已被 [ADR-003: 迁移到 GLSL 着色器系统](adr-003-glsl-shader-system.md) 取代。
|
||||
>
|
||||
> 项目已从 Slang 迁移到标准 GLSL,以简化工具链并提供更好的用户自定义支持。
|
||||
|
||||
## 状态
|
||||
|
||||
**已接受** - 2024-01-15
|
||||
**已废弃** - 2024-01-20(被 [ADR-003](adr-003-glsl-shader-system.md) 取代)
|
||||
|
||||
原状态:已接受 - 2024-01-15
|
||||
|
||||
## 背景
|
||||
|
||||
|
||||
192
docs/adr/adr-003-glsl-shader-system.md
Normal file
192
docs/adr/adr-003-glsl-shader-system.md
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
version: 0.2.0
|
||||
status: accepted
|
||||
created: 2024-01-20
|
||||
author: MIRAI Team
|
||||
supersedes: adr-002-slang-shader-system.md
|
||||
---
|
||||
|
||||
# ADR-003: 迁移到 GLSL 着色器系统
|
||||
|
||||
## 状态
|
||||
|
||||
**已接受** - 2024-01-20
|
||||
|
||||
## 背景
|
||||
|
||||
原系统使用 Slang 着色器语言(参见 [ADR-002](adr-002-slang-shader-system.md)),虽然 Slang 提供了强大的反射和泛型功能,但在实际使用中遇到了以下问题:
|
||||
|
||||
1. **工具链复杂性**:Slang 编译器需要额外的依赖和配置
|
||||
2. **用户学习成本**:开发者需要学习新的着色器语言
|
||||
3. **调试困难**:Slang 生成的 SPIR-V 难以调试
|
||||
4. **生态系统兼容性**:大多数着色器工具和示例都基于 GLSL
|
||||
|
||||
## 决策
|
||||
|
||||
迁移到标准 **GLSL** 着色器语言,采用以下架构设计:
|
||||
|
||||
### 1. 资源分配约定
|
||||
|
||||
| 资源类型 | 分配 | 说明 |
|
||||
|---------|------|------|
|
||||
| **Set 0** | 框架专用 | 用户不要使用 |
|
||||
| **Set 1** | 用户 UBO | 用户自定义 Uniform Buffer |
|
||||
| **Set 2** | 用户纹理 | 用户自定义纹理 |
|
||||
| **Push Constant** | 用户专用 | 最大 128 bytes |
|
||||
|
||||
### 2. 框架 UBO 结构 (Set 0, Binding 0)
|
||||
|
||||
```glsl
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
```
|
||||
|
||||
### 3. 四种渲染模式
|
||||
|
||||
| 模式 | 顶点着色器 | 片段着色器 | 说明 |
|
||||
|------|-----------|-----------|------|
|
||||
| **Backdrop** | 框架提供 | 用户提供 | 后处理效果,框架提供背景纹理 |
|
||||
| **Procedural** | 框架提供 | 用户提供 | 程序化生成 |
|
||||
| **Mask** | 框架提供 | 用户提供 | 遮罩效果 |
|
||||
| **Custom Mesh** | 用户提供 | 用户提供 | 自定义网格渲染 |
|
||||
|
||||
### 4. Push Constant 完全留给用户
|
||||
|
||||
框架不使用 Push Constant,用户可以完全控制这 128 bytes 的快速更新数据。
|
||||
|
||||
## 理由
|
||||
|
||||
### 1. 简化工具链
|
||||
|
||||
```
|
||||
原流程: .slang → slangc → SPIR-V + 反射 JSON → C++ 绑定
|
||||
新流程: .glsl → glslangValidator → SPIR-V
|
||||
```
|
||||
|
||||
### 2. 用户友好
|
||||
|
||||
- GLSL 是业界标准,大多数图形开发者已经熟悉
|
||||
- 丰富的学习资源和示例代码
|
||||
- 更好的 IDE 支持和语法高亮
|
||||
|
||||
### 3. 调试便利
|
||||
|
||||
- SPIR-V 工具链成熟
|
||||
- RenderDoc 等工具原生支持
|
||||
- 错误信息更易理解
|
||||
|
||||
### 4. 灵活的用户控制
|
||||
|
||||
- Push Constant 完全留给用户,支持高频参数更新
|
||||
- 用户可以自由定义 Set 1/2 的资源布局
|
||||
- 模板文件提供清晰的起点
|
||||
|
||||
## 替代方案
|
||||
|
||||
| 方案 | 优点 | 缺点 |
|
||||
|------|------|------|
|
||||
| **GLSL** (选择) | 标准、简单、广泛支持 | 无反射、手动绑定 |
|
||||
| Slang | 反射、泛型、多后端 | 工具链复杂、学习成本 |
|
||||
| HLSL | DX12 原生 | 仅限 Windows |
|
||||
| WGSL | WebGPU 原生 | 生态不成熟 |
|
||||
|
||||
## 后果
|
||||
|
||||
### 积极
|
||||
|
||||
- 简化了编译工具链
|
||||
- 降低了用户学习成本
|
||||
- 用户可以完全控制 Push Constant
|
||||
- 更好的调试体验
|
||||
- 更广泛的社区支持
|
||||
|
||||
### 消极
|
||||
|
||||
- 失去了 Slang 的自动反射功能
|
||||
- 需要用户手动复制着色器模板
|
||||
- 需要手动维护 C++ 和 GLSL 的数据结构同步
|
||||
|
||||
## 实现
|
||||
|
||||
### 着色器编译流程
|
||||
|
||||
```
|
||||
用户 GLSL 片段着色器
|
||||
↓
|
||||
glslangValidator -V
|
||||
↓
|
||||
SPIR-V
|
||||
↓
|
||||
spirv-cross (可选反射)
|
||||
```
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
src/shader/shaders/glsl/
|
||||
├── framework/ # 框架内置着色器
|
||||
│ ├── quad.vert # 四边形顶点着色器
|
||||
│ └── fullscreen.vert # 全屏顶点着色器
|
||||
├── templates/ # 用户模板
|
||||
│ ├── procedural.frag.template
|
||||
│ ├── backdrop.frag.template
|
||||
│ ├── mask.frag.template
|
||||
│ └── mesh.vert.template
|
||||
└── examples/ # 示例着色器
|
||||
├── gradient.frag
|
||||
├── blur.frag
|
||||
└── rounded_rect.frag
|
||||
```
|
||||
|
||||
### CMake 集成
|
||||
|
||||
```cmake
|
||||
function(compile_glsl_shader INPUT OUTPUT)
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT}
|
||||
COMMAND glslangValidator -V ${INPUT} -o ${OUTPUT}
|
||||
DEPENDS ${INPUT}
|
||||
COMMENT "Compiling ${INPUT}"
|
||||
)
|
||||
endfunction()
|
||||
```
|
||||
|
||||
### C++ 端 UBO 结构
|
||||
|
||||
```cpp
|
||||
namespace mirai {
|
||||
|
||||
struct alignas(16) widget_bounds_ubo {
|
||||
float bounds[4]; // x, y, width, height
|
||||
float opacity;
|
||||
float _pad[3];
|
||||
};
|
||||
static_assert(sizeof(widget_bounds_ubo) == 32);
|
||||
|
||||
} // namespace mirai
|
||||
```
|
||||
|
||||
## 迁移指南
|
||||
|
||||
### 从 Slang 迁移到 GLSL
|
||||
|
||||
1. **复制模板文件**:根据渲染模式选择对应的模板
|
||||
2. **转换语法**:
|
||||
- `float4` → `vec4`
|
||||
- `float2` → `vec2`
|
||||
- `ConstantBuffer<T>` → `uniform T { ... }`
|
||||
- `[[vk::binding(n, m)]]` → `layout(set = m, binding = n)`
|
||||
3. **更新绑定**:确保使用正确的 Set 和 Binding
|
||||
4. **测试验证**:使用 glslangValidator 验证语法
|
||||
|
||||
## 参考
|
||||
|
||||
- [GLSL 规范](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL))
|
||||
- [Vulkan GLSL](https://www.khronos.org/opengl/wiki/Vulkan_GLSL)
|
||||
- [glslangValidator](https://github.com/KhronosGroup/glslang)
|
||||
- [MIRAI GLSL 着色器系统设计](../../plans/glsl_shader_system_design.md)
|
||||
646
docs/shader_widget_architecture.md
Normal file
646
docs/shader_widget_architecture.md
Normal file
@@ -0,0 +1,646 @@
|
||||
# MIRAI Shader Widget 统一接口架构设计
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文档描述 MIRAI 框架的着色器控件(Shader Widget)统一接口架构设计。该架构基于标准 GLSL 着色器语言,支持四种渲染模式,每种模式提供特定的核心功能,而具体效果参数由派生类和着色器定义。
|
||||
|
||||
> **注意**:本文档反映了从 Slang 迁移到 GLSL 后的新架构。详见 [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md)。
|
||||
|
||||
### 1.1 设计目标
|
||||
|
||||
1. **使用标准 GLSL**:采用业界标准着色器语言,降低学习成本
|
||||
2. **Push Constant 完全留给用户**:框架不使用 Push Constant,用户可完全控制
|
||||
3. **框架数据最小化**:只提供 bounds 和 opacity
|
||||
4. **简单约定**:提前约定框架和用户各自使用的资源
|
||||
5. **模板驱动**:提供模板让用户复制和修改
|
||||
|
||||
### 1.2 四种渲染模式
|
||||
|
||||
| 模式 | 顶点着色器 | 片段着色器 | 说明 |
|
||||
|------|-----------|-----------|------|
|
||||
| **Backdrop** | 框架提供 | 用户提供 | 后处理效果,框架提供背景纹理 |
|
||||
| **Procedural** | 框架提供 | 用户提供 | 程序化生成 |
|
||||
| **Mask** | 框架提供 | 用户提供 | 遮罩效果 |
|
||||
| **Custom Mesh** | 用户提供 | 用户提供 | 自定义网格渲染 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 资源分配约定
|
||||
|
||||
### 2.1 Descriptor Set 布局
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 资源分配约定表 │
|
||||
├─────────────────┬───────────────────────────────────────────┤
|
||||
│ 资源类型 │ 分配 │
|
||||
├─────────────────┼───────────────────────────────────────────┤
|
||||
│ Set 0 │ 框架专用(用户不要使用) │
|
||||
│ Set 1 │ 用户 UBO │
|
||||
│ Set 2 │ 用户纹理 │
|
||||
│ Push Constant │ 用户专用(最大 128 bytes) │
|
||||
├─────────────────┼───────────────────────────────────────────┤
|
||||
│ 顶点着色器 │ 框架提供(Custom Mesh 除外) │
|
||||
│ 片段着色器 │ 用户提供 │
|
||||
└─────────────────┴───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 Set 0:框架专用
|
||||
|
||||
#### 通用模式(Procedural, Mask)
|
||||
|
||||
```glsl
|
||||
// Set 0: 框架专用 - 用户不要在 Set 0 定义任何资源
|
||||
|
||||
// Binding 0: Widget 边界
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
// 大小: 32 bytes
|
||||
```
|
||||
|
||||
#### Backdrop 模式
|
||||
|
||||
```glsl
|
||||
// Binding 0: Widget 边界(同上)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// Binding 1: 背景纹理(框架自动绑定)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
```
|
||||
|
||||
### 2.3 Set 1:用户 UBO
|
||||
|
||||
用户可以在 Set 1 定义自己的 Uniform Buffer:
|
||||
|
||||
```glsl
|
||||
// 示例:模糊参数
|
||||
layout(set = 1, binding = 0, std140) uniform BlurParams {
|
||||
float sigma; // 高斯分布标准差
|
||||
int samples; // 采样半径
|
||||
vec2 texel_size; // 纹理像素大小
|
||||
} params;
|
||||
```
|
||||
|
||||
### 2.4 Set 2:用户纹理
|
||||
|
||||
用户可以在 Set 2 定义自己的纹理:
|
||||
|
||||
```glsl
|
||||
// 示例:自定义纹理
|
||||
layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
|
||||
layout(set = 2, binding = 1) uniform sampler2D u_noise_texture;
|
||||
```
|
||||
|
||||
### 2.5 Push Constant:用户专用
|
||||
|
||||
Push Constant 完全留给用户,最大 128 bytes:
|
||||
|
||||
```glsl
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_start; // 起始颜色
|
||||
vec4 color_end; // 结束颜色
|
||||
float angle; // 渐变角度
|
||||
float _pad[3];
|
||||
} pc;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 框架内置顶点着色器
|
||||
|
||||
框架为 Backdrop/Procedural/Mask 模式提供内置顶点着色器,用户无需编写。
|
||||
|
||||
### 3.1 顶点着色器输出
|
||||
|
||||
框架的顶点着色器会输出以下数据给片段着色器:
|
||||
|
||||
```glsl
|
||||
// 框架顶点着色器输出(用户片段着色器的输入)
|
||||
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
|
||||
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
|
||||
```
|
||||
|
||||
### 3.2 框架顶点着色器实现
|
||||
|
||||
```glsl
|
||||
// 框架内部实现 - quad.vert
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 a_position;
|
||||
layout(location = 1) in vec2 a_uv;
|
||||
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec2 v_local_pos;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
void main() {
|
||||
// 将顶点位置转换到 NDC
|
||||
vec2 pos = a_position * widget.u_bounds.zw + widget.u_bounds.xy;
|
||||
vec2 ndc = pos * 2.0 - 1.0;
|
||||
gl_Position = vec4(ndc, 0.0, 1.0);
|
||||
|
||||
v_uv = a_uv;
|
||||
v_local_pos = a_position;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户片段着色器模板
|
||||
|
||||
### 4.1 Procedural 模式模板
|
||||
|
||||
用于程序化生成效果(渐变、噪声、图案等):
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
|
||||
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1)
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
// layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// float time;
|
||||
// // ... 自定义参数
|
||||
// } params;
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
|
||||
|
||||
// Push Constant(可选,最大 128 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
// ... 自定义参数
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// TODO: 用户实现程序化效果
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Backdrop 模式模板
|
||||
|
||||
用于后处理效果(模糊、颜色调整等):
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// 背景纹理(框架提供)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
// layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// float radius;
|
||||
// // ... 自定义参数
|
||||
// } params;
|
||||
|
||||
// Push Constant(可选)
|
||||
// layout(push_constant) uniform PushConstants {
|
||||
// // ... 自定义参数
|
||||
// } pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 采样背景纹理
|
||||
vec4 color = texture(u_backdrop, v_uv);
|
||||
|
||||
// TODO: 用户实现后处理效果
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Mask 模式模板
|
||||
|
||||
用于遮罩效果(圆角矩形、圆形、自定义形状等):
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Push Constant(可选)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color; // 遮罩颜色
|
||||
float corner_radius; // 圆角半径
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 示例:圆角矩形遮罩
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 half_size = size * 0.5;
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
|
||||
vec2 q = abs(p) - half_size + pc.corner_radius;
|
||||
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.corner_radius;
|
||||
float mask = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= mask * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 Custom Mesh 模式模板
|
||||
|
||||
用于自定义网格渲染(粒子系统、自定义形状等):
|
||||
|
||||
**顶点着色器模板:**
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 用户定义顶点输入
|
||||
// ============================================================
|
||||
layout(location = 0) in vec3 a_position;
|
||||
layout(location = 1) in vec2 a_uv;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec4 v_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Push Constant
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 mvp;
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
gl_Position = pc.mvp * vec4(a_position, 1.0);
|
||||
v_uv = a_uv;
|
||||
v_color = a_color;
|
||||
}
|
||||
```
|
||||
|
||||
**片段着色器模板:**
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec4 v_color;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
layout(set = 2, binding = 0) uniform sampler2D u_texture;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(u_texture, v_uv) * v_color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 编译系统
|
||||
|
||||
### 5.1 编译流程
|
||||
|
||||
```
|
||||
用户 GLSL 着色器
|
||||
↓
|
||||
glslangValidator -V
|
||||
↓
|
||||
SPIR-V
|
||||
↓
|
||||
spirv-cross (可选反射)
|
||||
```
|
||||
|
||||
### 5.2 CMake 集成
|
||||
|
||||
```cmake
|
||||
# cmake/shader_compile.cmake
|
||||
|
||||
function(compile_glsl_shader INPUT OUTPUT)
|
||||
get_filename_component(INPUT_NAME ${INPUT} NAME)
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT}
|
||||
COMMAND glslangValidator -V ${INPUT} -o ${OUTPUT}
|
||||
DEPENDS ${INPUT}
|
||||
COMMENT "Compiling GLSL shader: ${INPUT_NAME}"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# 批量编译着色器
|
||||
function(compile_glsl_shaders TARGET_NAME SHADER_DIR OUTPUT_DIR)
|
||||
file(GLOB_RECURSE SHADER_FILES
|
||||
"${SHADER_DIR}/*.vert"
|
||||
"${SHADER_DIR}/*.frag"
|
||||
"${SHADER_DIR}/*.comp"
|
||||
)
|
||||
|
||||
set(SPIRV_FILES)
|
||||
foreach(SHADER ${SHADER_FILES})
|
||||
get_filename_component(SHADER_NAME ${SHADER} NAME)
|
||||
set(SPIRV_FILE "${OUTPUT_DIR}/${SHADER_NAME}.spv")
|
||||
compile_glsl_shader(${SHADER} ${SPIRV_FILE})
|
||||
list(APPEND SPIRV_FILES ${SPIRV_FILE})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(${TARGET_NAME} DEPENDS ${SPIRV_FILES})
|
||||
endfunction()
|
||||
```
|
||||
|
||||
### 5.3 目录结构
|
||||
|
||||
```
|
||||
src/shader/shaders/glsl/
|
||||
├── framework/ # 框架内置着色器
|
||||
│ ├── quad.vert # 四边形顶点着色器
|
||||
│ └── fullscreen.vert # 全屏顶点着色器
|
||||
├── templates/ # 用户模板(复制使用)
|
||||
│ ├── procedural.frag.template
|
||||
│ ├── backdrop.frag.template
|
||||
│ ├── mask.frag.template
|
||||
│ └── mesh.vert.template
|
||||
└── examples/ # 示例着色器
|
||||
├── gradient.frag # 渐变效果
|
||||
├── blur.frag # 模糊效果
|
||||
└── rounded_rect.frag # 圆角矩形
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. C++ 端实现
|
||||
|
||||
### 6.1 框架 UBO 结构
|
||||
|
||||
```cpp
|
||||
namespace mirai {
|
||||
|
||||
struct alignas(16) widget_bounds_ubo {
|
||||
float bounds[4]; // x, y, width, height
|
||||
float opacity;
|
||||
float _pad[3];
|
||||
};
|
||||
static_assert(sizeof(widget_bounds_ubo) == 32);
|
||||
|
||||
} // namespace mirai
|
||||
```
|
||||
|
||||
### 6.2 描述符集布局创建
|
||||
|
||||
```cpp
|
||||
namespace mirai {
|
||||
|
||||
// 通用模式 Set 0 布局
|
||||
inline vk::UniqueDescriptorSetLayout create_set0_layout_common(vk::Device device) {
|
||||
vk::DescriptorSetLayoutBinding binding{
|
||||
0, vk::DescriptorType::eUniformBuffer, 1,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
return device.createDescriptorSetLayoutUnique({{}, 1, &binding});
|
||||
}
|
||||
|
||||
// Backdrop 模式 Set 0 布局
|
||||
inline vk::UniqueDescriptorSetLayout create_set0_layout_backdrop(vk::Device device) {
|
||||
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{
|
||||
{0, vk::DescriptorType::eUniformBuffer, 1,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler, 1,
|
||||
vk::ShaderStageFlagBits::eFragment}
|
||||
}};
|
||||
return device.createDescriptorSetLayoutUnique({{}, bindings});
|
||||
}
|
||||
|
||||
} // namespace mirai
|
||||
```
|
||||
|
||||
### 6.3 Push Constant 范围
|
||||
|
||||
```cpp
|
||||
// 用户 Push Constant 范围(128 bytes)
|
||||
vk::PushConstantRange user_push_constant_range{
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
0, // offset
|
||||
128 // size (最大)
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 整体架构图
|
||||
|
||||
### 7.1 系统架构总览
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph BuildTime[构建时]
|
||||
A[GLSL 源文件] --> B[glslangValidator]
|
||||
B --> C[SPIR-V 字节码]
|
||||
end
|
||||
|
||||
subgraph Runtime[运行时]
|
||||
C --> D[shader_library]
|
||||
D --> E[shader_module]
|
||||
|
||||
subgraph ShaderWidgets[Shader Widget 层]
|
||||
F[shader_widget 基类]
|
||||
G[backdrop_widget]
|
||||
H[procedural_widget]
|
||||
I[mask_widget]
|
||||
J[custom_mesh_widget]
|
||||
|
||||
F --> G
|
||||
F --> H
|
||||
F --> I
|
||||
F --> J
|
||||
end
|
||||
|
||||
E --> F
|
||||
|
||||
subgraph Resources[资源管理]
|
||||
K[gpu_allocator]
|
||||
L[descriptor_pool]
|
||||
end
|
||||
|
||||
F --> K
|
||||
F --> L
|
||||
|
||||
subgraph Rendering[渲染管线]
|
||||
M[pipeline]
|
||||
N[command_buffer]
|
||||
end
|
||||
|
||||
F --> M
|
||||
M --> N
|
||||
end
|
||||
```
|
||||
|
||||
### 7.2 数据流
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户代码
|
||||
participant Widget as shader_widget
|
||||
participant UBO as Uniform Buffer
|
||||
participant PC as Push Constant
|
||||
participant GPU as GPU
|
||||
|
||||
User->>Widget: 设置参数
|
||||
Widget->>UBO: 更新 Set 1 UBO
|
||||
Widget->>PC: 更新 Push Constant
|
||||
|
||||
loop 每帧渲染
|
||||
Widget->>GPU: vkCmdBindPipeline
|
||||
Widget->>GPU: vkCmdBindDescriptorSets (Set 0, 1, 2)
|
||||
Widget->>GPU: vkCmdPushConstants
|
||||
Widget->>GPU: vkCmdDraw
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 资源分配总结
|
||||
|
||||
### 8.1 资源归属表
|
||||
|
||||
| 资源 | 归属 | 说明 |
|
||||
|------|------|------|
|
||||
| Set 0 | 框架 | WidgetBounds + Backdrop纹理 |
|
||||
| Set 1 | 用户 | 用户 UBO |
|
||||
| Set 2 | 用户 | 用户纹理 |
|
||||
| Push Constant | 用户 | 最大 128 bytes |
|
||||
| 顶点着色器 | 框架 | Custom Mesh 除外 |
|
||||
| 片段着色器 | 用户 | 所有模式 |
|
||||
|
||||
### 8.2 框架提供的数据
|
||||
|
||||
| 数据 | 位置 | 类型 | 说明 |
|
||||
|------|------|------|------|
|
||||
| u_bounds | Set 0, Binding 0 | vec4 | Widget 边界 (x, y, w, h) |
|
||||
| u_opacity | Set 0, Binding 0 | float | 透明度 (0-1) |
|
||||
| u_backdrop | Set 0, Binding 1 | sampler2D | 背景纹理(仅 Backdrop 模式) |
|
||||
|
||||
### 8.3 框架顶点着色器输出
|
||||
|
||||
| 输出 | location | 类型 | 说明 |
|
||||
|------|----------|------|------|
|
||||
| v_uv | 0 | vec2 | UV 坐标 (0-1) |
|
||||
| v_local_pos | 1 | vec2 | 局部坐标 (0-1) |
|
||||
|
||||
---
|
||||
|
||||
## 9. 相关文档
|
||||
|
||||
- [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md) - 架构决策记录
|
||||
- [GLSL 着色器系统设计](../plans/glsl_shader_system_design.md) - 详细设计文档
|
||||
- [Shader Widget 使用示例](shader_widget_examples.md) - 使用示例
|
||||
842
docs/shader_widget_examples.md
Normal file
842
docs/shader_widget_examples.md
Normal file
@@ -0,0 +1,842 @@
|
||||
# MIRAI Shader Widget 使用示例
|
||||
|
||||
本文档提供 MIRAI 框架着色器控件系统的详细使用示例,涵盖四种渲染模式和自定义控件开发。
|
||||
|
||||
> **注意**:本文档使用标准 GLSL 着色器语言。详见 [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md)。
|
||||
|
||||
## 目录
|
||||
|
||||
1. [Procedural 模式示例](#1-procedural-模式示例)
|
||||
2. [Backdrop 模式示例](#2-backdrop-模式示例)
|
||||
3. [Mask 模式示例](#3-mask-模式示例)
|
||||
4. [Custom Mesh 模式示例](#4-custom-mesh-模式示例)
|
||||
5. [Push Constant 使用示例](#5-push-constant-使用示例)
|
||||
6. [用户 UBO 使用示例](#6-用户-ubo-使用示例)
|
||||
|
||||
---
|
||||
|
||||
## 1. Procedural 模式示例
|
||||
|
||||
Procedural 模式用于程序化渲染,基于坐标生成图形。框架提供顶点着色器,用户只需编写片段着色器。
|
||||
|
||||
### 1.1 线性渐变效果
|
||||
|
||||
使用 Push Constant 传递颜色和角度参数:
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 线性渐变效果 (Procedural 模式)
|
||||
// ============================================================
|
||||
|
||||
// 框架输入
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Push Constant: 渐变参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_start; // 起始颜色
|
||||
vec4 color_end; // 结束颜色
|
||||
float angle; // 渐变角度(弧度)
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 计算渐变方向向量
|
||||
vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
|
||||
|
||||
// 计算当前位置在渐变方向上的投影
|
||||
float t = dot(v_local_pos - 0.5, dir) + 0.5;
|
||||
|
||||
// 线性插值颜色
|
||||
frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
|
||||
|
||||
// 应用框架透明度
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 径向渐变效果
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// 框架输入
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Push Constant: 径向渐变参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_center; // 中心颜色
|
||||
vec4 color_edge; // 边缘颜色
|
||||
vec2 center; // 渐变中心 (0-1)
|
||||
float radius; // 渐变半径
|
||||
float _pad;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 计算到中心的距离
|
||||
float dist = length(v_local_pos - pc.center) / pc.radius;
|
||||
|
||||
// 径向插值
|
||||
frag_color = mix(pc.color_center, pc.color_edge, clamp(dist, 0.0, 1.0));
|
||||
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 棋盘格图案
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_a; // 颜色 A
|
||||
vec4 color_b; // 颜色 B
|
||||
float grid_size; // 格子大小
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 计算格子坐标
|
||||
vec2 grid = floor(v_local_pos * widget.u_bounds.zw / pc.grid_size);
|
||||
|
||||
// 棋盘格模式
|
||||
float checker = mod(grid.x + grid.y, 2.0);
|
||||
|
||||
frag_color = mix(pc.color_a, pc.color_b, checker);
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Backdrop 模式示例
|
||||
|
||||
Backdrop 模式用于后处理效果,框架提供背景纹理 `u_backdrop`。
|
||||
|
||||
### 2.1 高斯模糊效果
|
||||
|
||||
使用用户 UBO (Set 1) 传递模糊参数:
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 高斯模糊效果 (Backdrop 模式)
|
||||
// ============================================================
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// 背景纹理(框架提供)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// Set 1: 用户 UBO - 模糊参数
|
||||
layout(set = 1, binding = 0, std140) uniform BlurParams {
|
||||
float sigma; // 高斯分布标准差
|
||||
int samples; // 采样半径
|
||||
vec2 texel_size; // 纹理像素大小 (1.0/width, 1.0/height)
|
||||
} params;
|
||||
|
||||
void main() {
|
||||
vec4 color = vec4(0.0);
|
||||
float total_weight = 0.0;
|
||||
|
||||
// 高斯模糊核
|
||||
float sigma2 = params.sigma * params.sigma;
|
||||
float inv_2sigma2 = 1.0 / (2.0 * sigma2);
|
||||
|
||||
// 双重循环采样
|
||||
for (int i = -params.samples; i <= params.samples; i++) {
|
||||
for (int j = -params.samples; j <= params.samples; j++) {
|
||||
vec2 offset = vec2(float(i), float(j)) * params.texel_size;
|
||||
|
||||
// 计算高斯权重
|
||||
float dist2 = float(i * i + j * j);
|
||||
float weight = exp(-dist2 * inv_2sigma2);
|
||||
|
||||
color += texture(u_backdrop, v_uv + offset) * weight;
|
||||
total_weight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
frag_color = color / total_weight;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 色彩调整效果
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// Push Constant: 色彩调整参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
float brightness; // 亮度 (-1 to 1)
|
||||
float contrast; // 对比度 (0 to 2)
|
||||
float saturation; // 饱和度 (0 to 2)
|
||||
float _pad;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(u_backdrop, v_uv);
|
||||
|
||||
// 亮度调整
|
||||
color.rgb += pc.brightness;
|
||||
|
||||
// 对比度调整
|
||||
color.rgb = (color.rgb - 0.5) * pc.contrast + 0.5;
|
||||
|
||||
// 饱和度调整
|
||||
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
|
||||
color.rgb = mix(vec3(gray), color.rgb, pc.saturation);
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 毛玻璃效果
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// Set 1: 模糊参数
|
||||
layout(set = 1, binding = 0, std140) uniform BlurParams {
|
||||
float sigma;
|
||||
int samples;
|
||||
vec2 texel_size;
|
||||
} blur;
|
||||
|
||||
// Push Constant: 毛玻璃参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 tint_color; // 着色颜色
|
||||
float tint_amount; // 着色强度 (0-1)
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 模糊采样
|
||||
vec4 color = vec4(0.0);
|
||||
float total = 0.0;
|
||||
float inv_2sigma2 = 1.0 / (2.0 * blur.sigma * blur.sigma);
|
||||
|
||||
for (int i = -blur.samples; i <= blur.samples; i++) {
|
||||
for (int j = -blur.samples; j <= blur.samples; j++) {
|
||||
vec2 offset = vec2(float(i), float(j)) * blur.texel_size;
|
||||
float w = exp(-float(i*i + j*j) * inv_2sigma2);
|
||||
color += texture(u_backdrop, v_uv + offset) * w;
|
||||
total += w;
|
||||
}
|
||||
}
|
||||
color /= total;
|
||||
|
||||
// 应用着色
|
||||
color.rgb = mix(color.rgb, pc.tint_color.rgb, pc.tint_amount);
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Mask 模式示例
|
||||
|
||||
Mask 模式用于创建遮罩效果,通常输出带有 alpha 通道的颜色。
|
||||
|
||||
### 3.1 圆角矩形遮罩
|
||||
|
||||
使用 SDF(有符号距离场)生成高质量圆角矩形:
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 圆角矩形遮罩 (Mask 模式)
|
||||
// ============================================================
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Push Constant: 遮罩参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color; // 遮罩颜色
|
||||
float radius; // 圆角半径(像素)
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
// 圆角矩形 SDF
|
||||
float rounded_rect_sdf(vec2 p, vec2 half_size, float radius) {
|
||||
vec2 q = abs(p) - half_size + radius;
|
||||
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 half_size = size * 0.5;
|
||||
|
||||
// 转换到以中心为原点的坐标系
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
|
||||
// 计算 SDF
|
||||
float d = rounded_rect_sdf(p, half_size, pc.radius);
|
||||
|
||||
// 抗锯齿
|
||||
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= alpha * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 圆形遮罩
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
vec2 center; // 圆心 (0-1)
|
||||
float radius; // 半径 (相对于短边)
|
||||
float softness; // 边缘柔和度
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 计算宽高比
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
float aspect = size.x / size.y;
|
||||
|
||||
// 调整坐标以保持圆形
|
||||
vec2 uv = v_local_pos;
|
||||
uv.x *= aspect;
|
||||
vec2 c = pc.center;
|
||||
c.x *= aspect;
|
||||
|
||||
// 计算到圆心的距离
|
||||
float dist = length(uv - c);
|
||||
|
||||
// 计算遮罩
|
||||
float min_size = min(size.x, size.y);
|
||||
float r = pc.radius * min_size / size.x; // 调整半径
|
||||
float alpha = 1.0 - smoothstep(r - pc.softness, r + pc.softness, dist);
|
||||
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= alpha * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 四角独立圆角
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
vec4 radii; // (左上, 右上, 右下, 左下)
|
||||
} pc;
|
||||
|
||||
// 四角独立圆角 SDF
|
||||
float rounded_rect_sdf_4(vec2 p, vec2 half_size, vec4 radii) {
|
||||
// 根据象限选择圆角半径
|
||||
float r = (p.x > 0.0)
|
||||
? ((p.y > 0.0) ? radii.y : radii.z) // 右上 / 右下
|
||||
: ((p.y > 0.0) ? radii.x : radii.w); // 左上 / 左下
|
||||
|
||||
vec2 q = abs(p) - half_size + r;
|
||||
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 half_size = size * 0.5;
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
|
||||
float d = rounded_rect_sdf_4(p, half_size, pc.radii);
|
||||
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= alpha * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Custom Mesh 模式示例
|
||||
|
||||
Custom Mesh 模式需要用户同时提供顶点和片段着色器。
|
||||
|
||||
### 4.1 粒子系统
|
||||
|
||||
**顶点着色器:**
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// 粒子实例数据
|
||||
layout(location = 0) in vec3 a_position; // 粒子位置
|
||||
layout(location = 1) in float a_size; // 粒子大小
|
||||
layout(location = 2) in vec4 a_color; // 粒子颜色
|
||||
layout(location = 3) in float a_rotation; // 粒子旋转
|
||||
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec4 v_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Push Constant: 变换矩阵
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 view_proj;
|
||||
} pc;
|
||||
|
||||
// 四边形顶点(Billboard)
|
||||
const vec2 QUAD_VERTICES[6] = vec2[](
|
||||
vec2(-0.5, -0.5), vec2(0.5, -0.5), vec2(0.5, 0.5),
|
||||
vec2(-0.5, -0.5), vec2(0.5, 0.5), vec2(-0.5, 0.5)
|
||||
);
|
||||
|
||||
const vec2 QUAD_UVS[6] = vec2[](
|
||||
vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0),
|
||||
vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
// 获取四边形顶点
|
||||
vec2 quad_pos = QUAD_VERTICES[gl_VertexIndex % 6];
|
||||
v_uv = QUAD_UVS[gl_VertexIndex % 6];
|
||||
|
||||
// 应用旋转
|
||||
float c = cos(a_rotation);
|
||||
float s = sin(a_rotation);
|
||||
mat2 rot = mat2(c, -s, s, c);
|
||||
quad_pos = rot * quad_pos;
|
||||
|
||||
// 应用大小
|
||||
quad_pos *= a_size;
|
||||
|
||||
// 计算世界位置
|
||||
vec3 world_pos = a_position + vec3(quad_pos, 0.0);
|
||||
|
||||
gl_Position = pc.view_proj * vec4(world_pos, 1.0);
|
||||
v_color = a_color;
|
||||
}
|
||||
```
|
||||
|
||||
**片段着色器:**
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec4 v_color;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Set 2: 粒子纹理(可选)
|
||||
layout(set = 2, binding = 0) uniform sampler2D u_particle_texture;
|
||||
|
||||
void main() {
|
||||
// 采样粒子纹理
|
||||
vec4 tex_color = texture(u_particle_texture, v_uv);
|
||||
|
||||
frag_color = tex_color * v_color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 简单圆形粒子(无纹理)
|
||||
|
||||
**片段着色器:**
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec4 v_color;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
void main() {
|
||||
// 计算到中心的距离
|
||||
vec2 center = v_uv - 0.5;
|
||||
float dist = length(center) * 2.0;
|
||||
|
||||
// 圆形遮罩,带软边缘
|
||||
float alpha = 1.0 - smoothstep(0.8, 1.0, dist);
|
||||
|
||||
frag_color = v_color;
|
||||
frag_color.a *= alpha * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Push Constant 使用示例
|
||||
|
||||
Push Constant 完全留给用户,最大 128 bytes,适合高频更新的参数。
|
||||
|
||||
### 5.1 Push Constant 结构设计
|
||||
|
||||
```glsl
|
||||
// 示例 1:简单颜色参数 (32 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color; // 16 bytes
|
||||
vec4 secondary; // 16 bytes
|
||||
} pc;
|
||||
|
||||
// 示例 2:渐变参数 (48 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_start; // 16 bytes
|
||||
vec4 color_end; // 16 bytes
|
||||
float angle; // 4 bytes
|
||||
float _pad[3]; // 12 bytes (对齐)
|
||||
} pc;
|
||||
|
||||
// 示例 3:变换矩阵 (64 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 transform; // 64 bytes
|
||||
} pc;
|
||||
|
||||
// 示例 4:复杂参数 (128 bytes - 最大)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 transform; // 64 bytes
|
||||
vec4 color; // 16 bytes
|
||||
vec4 params; // 16 bytes
|
||||
vec4 extra[2]; // 32 bytes
|
||||
} pc;
|
||||
```
|
||||
|
||||
### 5.2 C++ 端 Push Constant 更新
|
||||
|
||||
```cpp
|
||||
// 定义与着色器匹配的结构体
|
||||
struct GradientPushConstants {
|
||||
float color_start[4];
|
||||
float color_end[4];
|
||||
float angle;
|
||||
float _pad[3];
|
||||
};
|
||||
static_assert(sizeof(GradientPushConstants) == 48);
|
||||
|
||||
// 更新 Push Constant
|
||||
void update_gradient(vk::CommandBuffer cmd, vk::PipelineLayout layout) {
|
||||
GradientPushConstants pc{};
|
||||
pc.color_start[0] = 1.0f; pc.color_start[1] = 0.0f;
|
||||
pc.color_start[2] = 0.0f; pc.color_start[3] = 1.0f;
|
||||
pc.color_end[0] = 0.0f; pc.color_end[1] = 0.0f;
|
||||
pc.color_end[2] = 1.0f; pc.color_end[3] = 1.0f;
|
||||
pc.angle = 0.785f; // 45 度
|
||||
|
||||
cmd.pushConstants(
|
||||
layout,
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0,
|
||||
sizeof(pc),
|
||||
&pc
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 用户 UBO 使用示例
|
||||
|
||||
用户 UBO 使用 Set 1,适合不频繁更新的参数。
|
||||
|
||||
### 6.1 UBO 结构设计
|
||||
|
||||
```glsl
|
||||
// Set 1: 用户 UBO
|
||||
layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// 注意 std140 布局规则:
|
||||
// - vec4 对齐到 16 bytes
|
||||
// - vec3 对齐到 16 bytes
|
||||
// - vec2 对齐到 8 bytes
|
||||
// - float/int 对齐到 4 bytes
|
||||
// - 数组元素对齐到 16 bytes
|
||||
|
||||
vec4 color; // offset: 0, size: 16
|
||||
vec2 scale; // offset: 16, size: 8
|
||||
float intensity; // offset: 24, size: 4
|
||||
float _pad0; // offset: 28, size: 4 (对齐)
|
||||
vec4 extra_params; // offset: 32, size: 16
|
||||
} params;
|
||||
// 总大小: 48 bytes
|
||||
```
|
||||
|
||||
### 6.2 C++ 端 UBO 结构
|
||||
|
||||
```cpp
|
||||
// 必须与着色器中的布局完全匹配
|
||||
struct alignas(16) MyParamsUBO {
|
||||
float color[4]; // vec4
|
||||
float scale[2]; // vec2
|
||||
float intensity; // float
|
||||
float _pad0; // padding
|
||||
float extra_params[4]; // vec4
|
||||
};
|
||||
static_assert(sizeof(MyParamsUBO) == 48);
|
||||
|
||||
// 更新 UBO
|
||||
void update_params(buffer& ubo) {
|
||||
MyParamsUBO data{};
|
||||
data.color[0] = 1.0f;
|
||||
data.color[1] = 0.5f;
|
||||
data.color[2] = 0.0f;
|
||||
data.color[3] = 1.0f;
|
||||
data.scale[0] = 2.0f;
|
||||
data.scale[1] = 2.0f;
|
||||
data.intensity = 0.8f;
|
||||
|
||||
ubo.write(&data, sizeof(data));
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 多个 UBO 绑定
|
||||
|
||||
```glsl
|
||||
// 可以在 Set 1 定义多个 UBO
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialParams {
|
||||
vec4 albedo;
|
||||
float roughness;
|
||||
float metallic;
|
||||
vec2 _pad;
|
||||
} material;
|
||||
|
||||
layout(set = 1, binding = 1, std140) uniform LightParams {
|
||||
vec4 light_pos;
|
||||
vec4 light_color;
|
||||
float intensity;
|
||||
float _pad[3];
|
||||
} light;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 完整示例:自定义效果控件
|
||||
|
||||
### 7.1 创建波纹效果
|
||||
|
||||
**着色器 (ripple.frag):**
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Set 1: 波纹参数
|
||||
layout(set = 1, binding = 0, std140) uniform RippleParams {
|
||||
float time; // 当前时间
|
||||
float frequency; // 波纹频率
|
||||
float amplitude; // 波纹振幅
|
||||
float decay; // 衰减系数
|
||||
} params;
|
||||
|
||||
// Push Constant: 波纹中心和颜色
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
vec2 center;
|
||||
float _pad[2];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 计算到中心的距离
|
||||
float dist = length(v_local_pos - pc.center);
|
||||
|
||||
// 计算波纹
|
||||
float wave = sin(dist * params.frequency - params.time * 3.0);
|
||||
wave *= params.amplitude * exp(-dist * params.decay);
|
||||
|
||||
// 应用波纹到颜色亮度
|
||||
vec4 color = pc.color;
|
||||
color.rgb *= 1.0 + wave;
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
**C++ 使用:**
|
||||
|
||||
```cpp
|
||||
// UBO 结构
|
||||
struct alignas(16) RippleParamsUBO {
|
||||
float time;
|
||||
float frequency;
|
||||
float amplitude;
|
||||
float decay;
|
||||
};
|
||||
|
||||
// Push Constant 结构
|
||||
struct RipplePushConstants {
|
||||
float color[4];
|
||||
float center[2];
|
||||
float _pad[2];
|
||||
};
|
||||
|
||||
class ripple_widget {
|
||||
public:
|
||||
void set_center(float x, float y) {
|
||||
push_constants_.center[0] = x;
|
||||
push_constants_.center[1] = y;
|
||||
}
|
||||
|
||||
void set_color(float r, float g, float b, float a) {
|
||||
push_constants_.color[0] = r;
|
||||
push_constants_.color[1] = g;
|
||||
push_constants_.color[2] = b;
|
||||
push_constants_.color[3] = a;
|
||||
}
|
||||
|
||||
void update(float delta_time) {
|
||||
params_.time += delta_time;
|
||||
ubo_->write(¶ms_, sizeof(params_));
|
||||
}
|
||||
|
||||
void render(vk::CommandBuffer cmd) {
|
||||
cmd.pushConstants(
|
||||
pipeline_layout_,
|
||||
vk::ShaderStageFlagBits::eFragment,
|
||||
0,
|
||||
sizeof(push_constants_),
|
||||
&push_constants_
|
||||
);
|
||||
// ... 绑定管线和描述符集,绘制
|
||||
}
|
||||
|
||||
private:
|
||||
RippleParamsUBO params_{0.0f, 10.0f, 0.1f, 2.0f};
|
||||
RipplePushConstants push_constants_{};
|
||||
std::unique_ptr<buffer> ubo_;
|
||||
vk::PipelineLayout pipeline_layout_;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 相关文档
|
||||
|
||||
- [Shader Widget 架构设计](shader_widget_architecture.md) - 详细的架构设计文档
|
||||
- [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md) - 架构决策记录
|
||||
- [GLSL 着色器系统设计](../plans/glsl_shader_system_design.md) - 详细设计文档
|
||||
598
plans/glsl_shader_system_design.md
Normal file
598
plans/glsl_shader_system_design.md
Normal file
@@ -0,0 +1,598 @@
|
||||
# MIRAI GLSL 着色器系统架构设计
|
||||
|
||||
## 1. 系统架构概述
|
||||
|
||||
### 1.1 设计目标
|
||||
|
||||
1. **从 Slang 迁移到 GLSL**:使用标准 GLSL
|
||||
2. **Push Constant 完全留给用户**:框架不使用
|
||||
3. **框架数据最小化**:只提供 bounds 和 opacity
|
||||
4. **简单约定**:提前约定框架和用户各自使用的资源
|
||||
5. **无 include 机制**:提供模板让用户复制
|
||||
|
||||
### 1.2 四种渲染模式
|
||||
|
||||
| 模式 | 顶点着色器 | 片段着色器 | 说明 |
|
||||
|------|-----------|-----------|------|
|
||||
| Backdrop | 框架提供 | 用户提供 | 后处理效果,框架提供背景纹理 |
|
||||
| Procedural | 框架提供 | 用户提供 | 程序化生成 |
|
||||
| Mask | 框架提供 | 用户提供 | 遮罩效果 |
|
||||
| Custom Mesh | 用户提供 | 用户提供 | 自定义网格渲染 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 资源分配约定
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 资源分配约定表 │
|
||||
├─────────────────┬───────────────────────────────────────────┤
|
||||
│ 资源类型 │ 分配 │
|
||||
├─────────────────┼───────────────────────────────────────────┤
|
||||
│ Set 0 │ 框架专用(用户不要使用) │
|
||||
│ Set 1 │ 用户 UBO │
|
||||
│ Set 2 │ 用户纹理 │
|
||||
│ Push Constant │ 用户专用(最大 128 bytes) │
|
||||
├─────────────────┼───────────────────────────────────────────┤
|
||||
│ 顶点着色器 │ 框架提供(Custom Mesh 除外) │
|
||||
│ 片段着色器 │ 用户提供 │
|
||||
└─────────────────┴───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Set 0:框架专用(用户不要使用)
|
||||
|
||||
### 3.1 通用模式(Procedural, Mask)
|
||||
|
||||
```glsl
|
||||
// ============================================================
|
||||
// Set 0: 框架专用 - 用户不要在 Set 0 定义任何资源
|
||||
// ============================================================
|
||||
|
||||
// Binding 0: Widget 边界
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
// 大小: 32 bytes
|
||||
```
|
||||
|
||||
### 3.2 Backdrop 模式
|
||||
|
||||
```glsl
|
||||
// Binding 0: Widget 边界(同上)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// Binding 1: 背景纹理(框架自动绑定)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 框架内置顶点着色器
|
||||
|
||||
框架为 Backdrop/Procedural/Mask 模式提供内置顶点着色器,用户无需编写。
|
||||
|
||||
### 4.1 内置顶点着色器输出
|
||||
|
||||
框架的顶点着色器会输出以下数据给片段着色器:
|
||||
|
||||
```glsl
|
||||
// 框架顶点着色器输出(用户片段着色器的输入)
|
||||
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
|
||||
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
|
||||
```
|
||||
|
||||
### 4.2 框架内置顶点着色器实现(参考)
|
||||
|
||||
```glsl
|
||||
// 框架内部实现,用户无需关心
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 a_position;
|
||||
layout(location = 1) in vec2 a_uv;
|
||||
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec2 v_local_pos;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
void main() {
|
||||
// 将顶点位置转换到 NDC
|
||||
vec2 pos = a_position * widget.u_bounds.zw + widget.u_bounds.xy;
|
||||
vec2 ndc = pos * 2.0 - 1.0;
|
||||
gl_Position = vec4(ndc, 0.0, 1.0);
|
||||
|
||||
v_uv = a_uv;
|
||||
v_local_pos = a_position;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 用户片段着色器模板
|
||||
|
||||
### 5.1 Procedural 模式模板
|
||||
|
||||
用户只需编写片段着色器,复制以下模板:
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
float time;
|
||||
// ... 自定义参数
|
||||
} params;
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
|
||||
|
||||
// Push Constant(可选,最大 128 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
// ... 自定义参数
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 用户实现...
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Backdrop 模式模板
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// 背景纹理(框架提供)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
layout(set = 1, binding = 0, std140) uniform BlurParams {
|
||||
float radius;
|
||||
int samples;
|
||||
vec2 texel_size;
|
||||
} params;
|
||||
|
||||
// Push Constant(可选)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
// ... 自定义参数
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 采样背景纹理
|
||||
vec4 color = texture(u_backdrop, v_uv);
|
||||
|
||||
// 用户处理...
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Mask 模式模板
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Push Constant(可选)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
float corner_radius;
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 计算遮罩(示例:圆角矩形)
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
vec2 q = abs(p) - size * 0.5 + pc.corner_radius;
|
||||
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.corner_radius;
|
||||
float mask = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= mask * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 Custom Mesh 模式模板
|
||||
|
||||
Custom Mesh 模式需要用户同时提供顶点和片段着色器:
|
||||
|
||||
**顶点着色器:**
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 用户定义顶点输入
|
||||
// ============================================================
|
||||
layout(location = 0) in vec3 a_position;
|
||||
layout(location = 1) in vec2 a_uv;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec4 v_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Push Constant
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 mvp;
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
gl_Position = pc.mvp * vec4(a_position, 1.0);
|
||||
v_uv = a_uv;
|
||||
v_color = a_color;
|
||||
}
|
||||
```
|
||||
|
||||
**片段着色器:**
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec4 v_color;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
layout(set = 2, binding = 0) uniform sampler2D u_texture;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(u_texture, v_uv) * v_color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 示例着色器
|
||||
|
||||
### 6.1 纯色填充(Procedural)
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(push_constant) uniform PC {
|
||||
vec4 color;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 线性渐变(Procedural)
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(push_constant) uniform PC {
|
||||
vec4 color_start;
|
||||
vec4 color_end;
|
||||
float angle;
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
|
||||
float t = dot(v_local_pos - 0.5, dir) + 0.5;
|
||||
frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 高斯模糊(Backdrop)
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
layout(set = 1, binding = 0, std140) uniform BlurParams {
|
||||
float sigma;
|
||||
int samples;
|
||||
vec2 texel_size;
|
||||
} params;
|
||||
|
||||
void main() {
|
||||
vec4 color = vec4(0.0);
|
||||
float total = 0.0;
|
||||
|
||||
for (int i = -params.samples; i <= params.samples; i++) {
|
||||
for (int j = -params.samples; j <= params.samples; j++) {
|
||||
vec2 offset = vec2(i, j) * params.texel_size;
|
||||
float w = exp(-(float(i*i + j*j)) / (2.0 * params.sigma * params.sigma));
|
||||
color += texture(u_backdrop, v_uv + offset) * w;
|
||||
total += w;
|
||||
}
|
||||
}
|
||||
|
||||
frag_color = color / total;
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 圆角矩形(Mask)
|
||||
|
||||
```glsl
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
layout(push_constant) uniform PC {
|
||||
vec4 color;
|
||||
float radius;
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 half_size = size * 0.5;
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
|
||||
vec2 q = abs(p) - half_size + pc.radius;
|
||||
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.radius;
|
||||
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= alpha * widget.u_opacity;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. C++ 端实现(Vulkan HPP)
|
||||
|
||||
### 7.1 框架 UBO 结构
|
||||
|
||||
```cpp
|
||||
namespace mirai {
|
||||
|
||||
struct alignas(16) widget_bounds_ubo {
|
||||
float bounds[4]; // x, y, width, height
|
||||
float opacity;
|
||||
float _pad[3];
|
||||
};
|
||||
static_assert(sizeof(widget_bounds_ubo) == 32);
|
||||
|
||||
} // namespace mirai
|
||||
```
|
||||
|
||||
### 7.2 描述符集布局创建
|
||||
|
||||
```cpp
|
||||
namespace mirai {
|
||||
|
||||
// 通用模式 Set 0 布局
|
||||
inline vk::UniqueDescriptorSetLayout create_set0_layout_common(vk::Device device) {
|
||||
vk::DescriptorSetLayoutBinding binding{
|
||||
0, vk::DescriptorType::eUniformBuffer, 1,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment
|
||||
};
|
||||
return device.createDescriptorSetLayoutUnique({{}, 1, &binding});
|
||||
}
|
||||
|
||||
// Backdrop 模式 Set 0 布局
|
||||
inline vk::UniqueDescriptorSetLayout create_set0_layout_backdrop(vk::Device device) {
|
||||
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{
|
||||
{0, vk::DescriptorType::eUniformBuffer, 1,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler, 1,
|
||||
vk::ShaderStageFlagBits::eFragment}
|
||||
}};
|
||||
return device.createDescriptorSetLayoutUnique({{}, bindings});
|
||||
}
|
||||
|
||||
} // namespace mirai
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 编译系统
|
||||
|
||||
### 8.1 编译流程
|
||||
|
||||
```
|
||||
用户 GLSL 片段着色器 → glslangValidator → SPIR-V → spirv-cross(反射)
|
||||
```
|
||||
|
||||
### 8.2 CMake 集成
|
||||
|
||||
```cmake
|
||||
function(compile_glsl_shader INPUT OUTPUT)
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT}
|
||||
COMMAND glslangValidator -V ${INPUT} -o ${OUTPUT}
|
||||
DEPENDS ${INPUT}
|
||||
COMMENT "Compiling ${INPUT}"
|
||||
)
|
||||
endfunction()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 总结
|
||||
|
||||
### 资源分配
|
||||
|
||||
| 资源 | 归属 | 说明 |
|
||||
|------|------|------|
|
||||
| Set 0 | 框架 | WidgetBounds + Backdrop纹理 |
|
||||
| Set 1 | 用户 | 用户 UBO |
|
||||
| Set 2 | 用户 | 用户纹理 |
|
||||
| Push Constant | 用户 | 最大 128 bytes |
|
||||
| 顶点着色器 | 框架 | Custom Mesh 除外 |
|
||||
| 片段着色器 | 用户 | 所有模式 |
|
||||
|
||||
### 框架提供的数据
|
||||
|
||||
| 数据 | 位置 | 类型 |
|
||||
|------|------|------|
|
||||
| u_bounds | Set 0, Binding 0 | vec4 |
|
||||
| u_opacity | Set 0, Binding 0 | float |
|
||||
| u_backdrop | Set 0, Binding 1 | sampler2D (仅 Backdrop) |
|
||||
|
||||
### 框架顶点着色器输出
|
||||
|
||||
| 输出 | location | 类型 | 说明 |
|
||||
|------|----------|------|------|
|
||||
| v_uv | 0 | vec2 | UV 坐标 (0-1) |
|
||||
| v_local_pos | 1 | vec2 | 局部坐标 (0-1) |
|
||||
@@ -7,12 +7,12 @@ project(mirai_shader)
|
||||
simple_library(STATIC)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC mirai_core mirai_render mirai_resource Vulkan::Vulkan)
|
||||
|
||||
# 添加着色器编译到现有目标
|
||||
# 自动搜索 shaders 目录下的所有 .slang 文件
|
||||
# shaders/common 和 shaders/math 用于着色器模块引用
|
||||
add_shader_library(TARGET ${PROJECT_NAME}
|
||||
SHADER_PATH shaders
|
||||
REF_PATHS shaders/common shaders/math
|
||||
# 添加 GLSL 着色器编译到现有目标
|
||||
# 自动搜索 shaders/glsl 目录下的所有 .vert/.frag 文件
|
||||
add_glsl_shader_library(TARGET ${PROJECT_NAME}
|
||||
SHADER_PATH shaders/glsl
|
||||
INCLUDE_DIRS shaders/glsl
|
||||
RECURSIVE
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
@@ -27,4 +27,4 @@ install(TARGETS ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
message(STATUS "Configured mirai_shader library")
|
||||
message(STATUS "Configured mirai_shader library with GLSL shaders")
|
||||
@@ -1,159 +0,0 @@
|
||||
/**
|
||||
* @file basic.slang
|
||||
* @brief 基础 UI 着色器示例
|
||||
* @description 展示 GUI 渲染中常用的 Uniform Buffer、纹理采样、顶点属性、Push Constants
|
||||
* 以及多入口点支持(通过 [shader("xxx")] 属性自动检测)
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Uniform Buffer 定义
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 全局变换 Uniform Buffer
|
||||
* @set 0
|
||||
* @binding 0
|
||||
*/
|
||||
struct GlobalUniforms {
|
||||
float4x4 projection; // 正交投影矩阵
|
||||
float2 screenSize; // 屏幕尺寸
|
||||
float2 _padding; // 对齐填充
|
||||
};
|
||||
|
||||
[[vk::binding(0, 0)]]
|
||||
ConstantBuffer<GlobalUniforms> globals;
|
||||
|
||||
// ============================================================================
|
||||
// 纹理和采样器
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief UI 纹理(图标、图片等)
|
||||
*/
|
||||
[[vk::binding(0, 1)]]
|
||||
Texture2D<float4> uiTexture;
|
||||
|
||||
/**
|
||||
* @brief 线性采样器
|
||||
*/
|
||||
[[vk::binding(1, 1)]]
|
||||
SamplerState linearSampler;
|
||||
|
||||
/**
|
||||
* @brief 计算着色器用的输入纹理
|
||||
*/
|
||||
[[vk::binding(0, 2)]]
|
||||
RWTexture2D<float4> computeInput;
|
||||
|
||||
/**
|
||||
* @brief 计算着色器用的输出纹理
|
||||
*/
|
||||
[[vk::binding(1, 2)]]
|
||||
RWTexture2D<float4> computeOutput;
|
||||
|
||||
// ============================================================================
|
||||
// Push Constants
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 每次绘制调用的参数
|
||||
*/
|
||||
struct PushConstants {
|
||||
float4 color; // 顶点颜色乘数
|
||||
float2 offset; // 位置偏移
|
||||
float opacity; // 不透明度
|
||||
uint useTexture; // 是否使用纹理 (0 或 1)
|
||||
};
|
||||
|
||||
[[vk::push_constant]]
|
||||
PushConstants pc;
|
||||
|
||||
/**
|
||||
* @brief 计算着色器的 Push Constants
|
||||
*/
|
||||
struct ComputePushConstants {
|
||||
uint2 workgroupSize; // 工作组大小
|
||||
uint2 workgroupCount; // 工作组数量
|
||||
float intensity; // 强度参数
|
||||
};
|
||||
|
||||
[[vk::push_constant]]
|
||||
ComputePushConstants computePc;
|
||||
|
||||
// ============================================================================
|
||||
// 顶点输入输出结构
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 顶点着色器输入
|
||||
*/
|
||||
struct VertexInput {
|
||||
[[vk::location(0)]] float2 position : POSITION; // 2D 位置
|
||||
[[vk::location(1)]] float2 texCoord : TEXCOORD0; // 纹理坐标
|
||||
[[vk::location(2)]] float4 color : COLOR0; // 顶点颜色
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点着色器输出
|
||||
*/
|
||||
struct VertexOutput {
|
||||
float4 position : SV_Position;
|
||||
float2 texCoord : TEXCOORD0;
|
||||
float4 color : COLOR0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 顶点着色器
|
||||
// ============================================================================
|
||||
|
||||
[shader("vertex")]
|
||||
VertexOutput vertexMain(VertexInput input) {
|
||||
VertexOutput output;
|
||||
|
||||
// 应用偏移并变换到裁剪空间
|
||||
float2 pos = input.position + pc.offset;
|
||||
output.position = mul(globals.projection, float4(pos, 0.0f, 1.0f));
|
||||
|
||||
// 传递纹理坐标和颜色
|
||||
output.texCoord = input.texCoord;
|
||||
output.color = input.color * pc.color;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 片段着色器
|
||||
// ============================================================================
|
||||
|
||||
[shader("fragment")]
|
||||
float4 fragmentMain(VertexOutput input) : SV_Target {
|
||||
float4 color = input.color;
|
||||
|
||||
// 如果使用纹理,采样并相乘
|
||||
if (pc.useTexture != 0) {
|
||||
float4 texColor = uiTexture.Sample(linearSampler, input.texCoord);
|
||||
color *= texColor;
|
||||
}
|
||||
|
||||
// 应用不透明度
|
||||
color.a *= pc.opacity;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 计算着色器
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 简单的图像处理计算着色器
|
||||
* @description 演示多入口点支持 - 自动检测 [shader("compute")]
|
||||
*/
|
||||
[shader("compute")]
|
||||
void computeMain(uint3 globalID : SV_DispatchThreadID) {
|
||||
// 简单的图像处理:应用强度
|
||||
float4 color = computeInput[globalID.xy];
|
||||
color.rgb *= computePc.intensity;
|
||||
color.a = 1.0f;
|
||||
computeOutput[globalID.xy] = color;
|
||||
}
|
||||
60
src/shader/shaders/glsl/examples/blur.frag
Normal file
60
src/shader/shaders/glsl/examples/blur.frag
Normal file
@@ -0,0 +1,60 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 高斯模糊效果示例 (Backdrop 模式)
|
||||
// 使用用户 UBO (Set 1) 传递模糊参数
|
||||
// ============================================================
|
||||
|
||||
// 框架输入
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// 背景纹理(框架提供)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// Set 1: 用户 UBO - 模糊参数
|
||||
layout(set = 1, binding = 0, std140) uniform BlurParams {
|
||||
float sigma; // 高斯分布标准差
|
||||
int samples; // 采样半径(实际采样数为 (2*samples+1)^2)
|
||||
vec2 texel_size; // 纹理像素大小 (1.0/width, 1.0/height)
|
||||
} params;
|
||||
|
||||
void main() {
|
||||
vec4 color = vec4(0.0);
|
||||
float total_weight = 0.0;
|
||||
|
||||
// 高斯模糊核
|
||||
float sigma2 = params.sigma * params.sigma;
|
||||
float inv_2sigma2 = 1.0 / (2.0 * sigma2);
|
||||
|
||||
// 双重循环采样
|
||||
for (int i = -params.samples; i <= params.samples; i++) {
|
||||
for (int j = -params.samples; j <= params.samples; j++) {
|
||||
// 计算偏移
|
||||
vec2 offset = vec2(float(i), float(j)) * params.texel_size;
|
||||
|
||||
// 计算高斯权重
|
||||
float dist2 = float(i * i + j * j);
|
||||
float weight = exp(-dist2 * inv_2sigma2);
|
||||
|
||||
// 累加采样
|
||||
color += texture(u_backdrop, v_uv + offset) * weight;
|
||||
total_weight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
// 归一化
|
||||
frag_color = color / total_weight;
|
||||
|
||||
// 应用框架透明度
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
42
src/shader/shaders/glsl/examples/gradient.frag
Normal file
42
src/shader/shaders/glsl/examples/gradient.frag
Normal file
@@ -0,0 +1,42 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 线性渐变效果示例 (Procedural 模式)
|
||||
// 使用 Push Constant 传递颜色和角度
|
||||
// ============================================================
|
||||
|
||||
// 框架输入
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Push Constant: 渐变参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_start; // 起始颜色
|
||||
vec4 color_end; // 结束颜色
|
||||
float angle; // 渐变角度(弧度)
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
// 计算渐变方向向量
|
||||
vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
|
||||
|
||||
// 计算当前位置在渐变方向上的投影
|
||||
// v_local_pos 范围是 0-1,将其转换到 -0.5 到 0.5 范围
|
||||
float t = dot(v_local_pos - 0.5, dir) + 0.5;
|
||||
|
||||
// 线性插值颜色
|
||||
frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
|
||||
|
||||
// 应用框架透明度
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
53
src/shader/shaders/glsl/examples/rounded_rect.frag
Normal file
53
src/shader/shaders/glsl/examples/rounded_rect.frag
Normal file
@@ -0,0 +1,53 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 圆角矩形遮罩示例 (Mask 模式)
|
||||
// 使用 Push Constant 传递圆角半径
|
||||
// ============================================================
|
||||
|
||||
// 框架输入
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 1) in vec2 v_local_pos;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds;
|
||||
float u_opacity;
|
||||
float _pad0, _pad1, _pad2;
|
||||
} widget;
|
||||
|
||||
// Push Constant: 遮罩参数
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color; // 遮罩颜色
|
||||
float radius; // 圆角半径(像素)
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
// 计算圆角矩形的有符号距离场 (SDF)
|
||||
float rounded_rect_sdf(vec2 p, vec2 half_size, float radius) {
|
||||
vec2 q = abs(p) - half_size + radius;
|
||||
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// 获取 widget 尺寸
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 half_size = size * 0.5;
|
||||
|
||||
// 将局部坐标转换到以中心为原点的坐标系
|
||||
// v_local_pos 范围是 0-1,转换到 -half_size 到 half_size
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
|
||||
// 计算 SDF
|
||||
float d = rounded_rect_sdf(p, half_size, pc.radius);
|
||||
|
||||
// 使用 smoothstep 进行抗锯齿
|
||||
// d < 0 表示在形状内部,d > 0 表示在形状外部
|
||||
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
// 输出遮罩颜色
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= alpha * widget.u_opacity;
|
||||
}
|
||||
41
src/shader/shaders/glsl/framework/fullscreen.vert
Normal file
41
src/shader/shaders/glsl/framework/fullscreen.vert
Normal file
@@ -0,0 +1,41 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架内置全屏顶点着色器
|
||||
// 用于全屏后处理效果
|
||||
// ============================================================
|
||||
|
||||
// 无顶点输入 - 使用 gl_VertexIndex 生成全屏三角形
|
||||
|
||||
// 输出到片段着色器
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec2 v_local_pos;
|
||||
|
||||
// 框架 UBO - Set 0 专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height) - 归一化屏幕坐标
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
void main() {
|
||||
// 生成覆盖整个屏幕的三角形
|
||||
// 使用一个大三角形覆盖整个视口,比两个三角形更高效
|
||||
vec2 positions[3] = vec2[3](
|
||||
vec2(-1.0, -1.0),
|
||||
vec2( 3.0, -1.0),
|
||||
vec2(-1.0, 3.0)
|
||||
);
|
||||
|
||||
vec2 uvs[3] = vec2[3](
|
||||
vec2(0.0, 0.0),
|
||||
vec2(2.0, 0.0),
|
||||
vec2(0.0, 2.0)
|
||||
);
|
||||
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
v_uv = uvs[gl_VertexIndex];
|
||||
v_local_pos = uvs[gl_VertexIndex];
|
||||
}
|
||||
38
src/shader/shaders/glsl/framework/quad.vert
Normal file
38
src/shader/shaders/glsl/framework/quad.vert
Normal file
@@ -0,0 +1,38 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// 框架内置四边形顶点着色器
|
||||
// 用于 Backdrop/Procedural/Mask 模式
|
||||
// ============================================================
|
||||
|
||||
// 顶点输入
|
||||
layout(location = 0) in vec2 a_position; // 顶点位置 (0-1 范围)
|
||||
layout(location = 1) in vec2 a_uv; // UV 坐标
|
||||
|
||||
// 输出到片段着色器
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec2 v_local_pos;
|
||||
|
||||
// 框架 UBO - Set 0 专用
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height) - 归一化屏幕坐标
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
void main() {
|
||||
// 将顶点位置转换到 NDC
|
||||
// a_position 是 0-1 范围,widget.u_bounds.xy 是左上角位置,zw 是宽高
|
||||
vec2 pos = a_position * widget.u_bounds.zw + widget.u_bounds.xy;
|
||||
|
||||
// 转换到 NDC (-1 到 1)
|
||||
vec2 ndc = pos * 2.0 - 1.0;
|
||||
|
||||
// Vulkan Y轴翻转
|
||||
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0);
|
||||
|
||||
v_uv = a_uv;
|
||||
v_local_pos = a_position;
|
||||
}
|
||||
62
src/shader/shaders/glsl/templates/backdrop.frag.template
Normal file
62
src/shader/shaders/glsl/templates/backdrop.frag.template
Normal file
@@ -0,0 +1,62 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// Backdrop 模式片段着色器模板
|
||||
// 用于后处理效果,框架提供背景纹理
|
||||
// ============================================================
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
|
||||
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// 背景纹理(框架提供)
|
||||
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
// layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// float radius;
|
||||
// int samples;
|
||||
// vec2 texel_size;
|
||||
// // ... 自定义参数
|
||||
// } params;
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
|
||||
|
||||
// Push Constant(可选,最大 128 bytes)
|
||||
// layout(push_constant) uniform PushConstants {
|
||||
// // ... 自定义参数
|
||||
// } pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 采样背景纹理
|
||||
vec4 color = texture(u_backdrop, v_uv);
|
||||
|
||||
// TODO: 用户实现后处理效果
|
||||
// 例如:模糊、色彩调整、扭曲等
|
||||
|
||||
frag_color = color;
|
||||
|
||||
// 应用框架提供的透明度
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
62
src/shader/shaders/glsl/templates/mask.frag.template
Normal file
62
src/shader/shaders/glsl/templates/mask.frag.template
Normal file
@@ -0,0 +1,62 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// Mask 模式片段着色器模板
|
||||
// 用于遮罩效果,输出单通道 alpha 值
|
||||
// ============================================================
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
|
||||
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
// layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// // ... 自定义参数
|
||||
// } params;
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
// layout(set = 2, binding = 0) uniform sampler2D u_mask_texture;
|
||||
|
||||
// Push Constant(可选,最大 128 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color; // 遮罩颜色
|
||||
float corner_radius; // 圆角半径
|
||||
float _pad[3];
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// TODO: 用户实现遮罩计算
|
||||
|
||||
// 示例:圆角矩形遮罩
|
||||
vec2 size = widget.u_bounds.zw;
|
||||
vec2 half_size = size * 0.5;
|
||||
vec2 p = (v_local_pos - 0.5) * size;
|
||||
|
||||
vec2 q = abs(p) - half_size + pc.corner_radius;
|
||||
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.corner_radius;
|
||||
float mask = 1.0 - smoothstep(-1.0, 1.0, d);
|
||||
|
||||
// 输出遮罩颜色,alpha 通道包含遮罩值
|
||||
frag_color = pc.color;
|
||||
frag_color.a *= mask * widget.u_opacity;
|
||||
}
|
||||
54
src/shader/shaders/glsl/templates/mesh.vert.template
Normal file
54
src/shader/shaders/glsl/templates/mesh.vert.template
Normal file
@@ -0,0 +1,54 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// Custom Mesh 模式顶点着色器模板
|
||||
// 用于自定义网格渲染,用户需要同时提供顶点和片段着色器
|
||||
// ============================================================
|
||||
|
||||
// ============================================================
|
||||
// 用户定义顶点输入
|
||||
// ============================================================
|
||||
layout(location = 0) in vec3 a_position; // 顶点位置
|
||||
layout(location = 1) in vec2 a_uv; // UV 坐标
|
||||
layout(location = 2) in vec4 a_color; // 顶点颜色
|
||||
|
||||
// 输出到片段着色器
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
layout(location = 1) out vec4 v_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
// layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// mat4 view;
|
||||
// mat4 projection;
|
||||
// // ... 自定义参数
|
||||
// } params;
|
||||
|
||||
// Push Constant(可选,最大 128 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
mat4 mvp; // Model-View-Projection 矩阵
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// 使用 MVP 矩阵变换顶点位置
|
||||
gl_Position = pc.mvp * vec4(a_position, 1.0);
|
||||
|
||||
// 传递数据到片段着色器
|
||||
v_uv = a_uv;
|
||||
v_color = a_color;
|
||||
}
|
||||
55
src/shader/shaders/glsl/templates/procedural.frag.template
Normal file
55
src/shader/shaders/glsl/templates/procedural.frag.template
Normal file
@@ -0,0 +1,55 @@
|
||||
#version 450
|
||||
|
||||
// ============================================================
|
||||
// Procedural 模式片段着色器模板
|
||||
// 用于程序化生成效果
|
||||
// ============================================================
|
||||
|
||||
// ============================================================
|
||||
// 框架输入(不要修改)
|
||||
// ============================================================
|
||||
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
|
||||
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
// Set 0: 框架专用(不要修改)
|
||||
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
|
||||
vec4 u_bounds; // (x, y, width, height)
|
||||
float u_opacity; // 0.0 - 1.0
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
} widget;
|
||||
|
||||
// ============================================================
|
||||
// 用户区域(自由定义)
|
||||
// ============================================================
|
||||
|
||||
// Set 1: 用户 UBO(可选)
|
||||
// layout(set = 1, binding = 0, std140) uniform MyParams {
|
||||
// float time;
|
||||
// // ... 自定义参数
|
||||
// } params;
|
||||
|
||||
// Set 2: 用户纹理(可选)
|
||||
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
|
||||
|
||||
// Push Constant(可选,最大 128 bytes)
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color;
|
||||
// ... 自定义参数
|
||||
} pc;
|
||||
|
||||
// ============================================================
|
||||
// 主函数
|
||||
// ============================================================
|
||||
void main() {
|
||||
// TODO: 用户实现程序化效果
|
||||
|
||||
// 示例:使用 Push Constant 中的颜色
|
||||
frag_color = pc.color;
|
||||
|
||||
// 应用框架提供的透明度
|
||||
frag_color.a *= widget.u_opacity;
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/**
|
||||
* @file math.slang
|
||||
* @brief 公共数学函数模块
|
||||
* @description 提供常用的数学计算函数,供其他着色器模块导入使用
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// 常量定义
|
||||
// ============================================================================
|
||||
|
||||
static const float PI = 3.14159265358979323846f;
|
||||
static const float TWO_PI = 6.28318530717958647692f;
|
||||
static const float HALF_PI = 1.57079632679489661923f;
|
||||
static const float INV_PI = 0.31830988618379067154f;
|
||||
static const float EPSILON = 1e-6f;
|
||||
|
||||
// ============================================================================
|
||||
// 基础数学函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 将值限制在 [0, 1] 范围内
|
||||
*/
|
||||
float saturate(float x) {
|
||||
return clamp(x, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
float2 saturate(float2 v) {
|
||||
return clamp(v, float2(0.0f), float2(1.0f));
|
||||
}
|
||||
|
||||
float3 saturate(float3 v) {
|
||||
return clamp(v, float3(0.0f), float3(1.0f));
|
||||
}
|
||||
|
||||
float4 saturate(float4 v) {
|
||||
return clamp(v, float4(0.0f), float4(1.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 平滑插值(Hermite 插值)
|
||||
*/
|
||||
float smoothstep01(float x) {
|
||||
float t = saturate(x);
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更平滑的插值(5次多项式)
|
||||
*/
|
||||
float smootherstep(float x) {
|
||||
float t = saturate(x);
|
||||
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 向量操作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 计算向量的平方长度(避免 sqrt)
|
||||
*/
|
||||
float lengthSq(float2 v) {
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
float lengthSq(float3 v) {
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
float lengthSq(float4 v) {
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 安全的向量归一化(避免除零)
|
||||
*/
|
||||
float3 safeNormalize(float3 v) {
|
||||
float len = length(v);
|
||||
return len > EPSILON ? v / len : float3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算反射向量
|
||||
*/
|
||||
float3 reflectVector(float3 incident, float3 normal) {
|
||||
return incident - 2.0f * dot(incident, normal) * normal;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 颜色空间转换
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 线性空间到 sRGB
|
||||
*/
|
||||
float linearToSrgb(float linear) {
|
||||
if (linear <= 0.0031308f) {
|
||||
return linear * 12.92f;
|
||||
}
|
||||
return 1.055f * pow(linear, 1.0f / 2.4f) - 0.055f;
|
||||
}
|
||||
|
||||
float3 linearToSrgb(float3 linear) {
|
||||
return float3(
|
||||
linearToSrgb(linear.x),
|
||||
linearToSrgb(linear.y),
|
||||
linearToSrgb(linear.z)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sRGB 到线性空间
|
||||
*/
|
||||
float srgbToLinear(float srgb) {
|
||||
if (srgb <= 0.04045f) {
|
||||
return srgb / 12.92f;
|
||||
}
|
||||
return pow((srgb + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
|
||||
float3 srgbToLinear(float3 srgb) {
|
||||
return float3(
|
||||
srgbToLinear(srgb.x),
|
||||
srgbToLinear(srgb.y),
|
||||
srgbToLinear(srgb.z)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算亮度(Luminance)
|
||||
*/
|
||||
float luminance(float3 color) {
|
||||
return dot(color, float3(0.2126f, 0.7152f, 0.0722f));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 矩阵工具函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建绕 Z 轴旋转的 2D 旋转矩阵
|
||||
*/
|
||||
float2x2 rotate2D(float angle) {
|
||||
float c = cos(angle);
|
||||
float s = sin(angle);
|
||||
return float2x2(c, -s, s, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建 2D 缩放矩阵
|
||||
*/
|
||||
float2x2 scale2D(float2 scale) {
|
||||
return float2x2(scale.x, 0.0f, 0.0f, scale.y);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 噪声函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 简单的伪随机哈希函数
|
||||
*/
|
||||
float hash11(float p) {
|
||||
p = frac(p * 0.1031f);
|
||||
p *= p + 33.33f;
|
||||
p *= p + p;
|
||||
return frac(p);
|
||||
}
|
||||
|
||||
float hash21(float2 p) {
|
||||
float3 p3 = frac(float3(p.x, p.y, p.x) * 0.1031f);
|
||||
p3 += dot(p3, p3.yzx + 33.33f);
|
||||
return frac((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
float2 hash22(float2 p) {
|
||||
float3 p3 = frac(float3(p.x, p.y, p.x) * float3(0.1031f, 0.1030f, 0.0973f));
|
||||
p3 += dot(p3, p3.yzx + 33.33f);
|
||||
return frac((p3.xx + p3.yz) * p3.zy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 值噪声
|
||||
*/
|
||||
float valueNoise(float2 p) {
|
||||
float2 i = floor(p);
|
||||
float2 f = frac(p);
|
||||
|
||||
// 四个角的随机值
|
||||
float a = hash21(i);
|
||||
float b = hash21(i + float2(1.0f, 0.0f));
|
||||
float c = hash21(i + float2(0.0f, 1.0f));
|
||||
float d = hash21(i + float2(1.0f, 1.0f));
|
||||
|
||||
// 平滑插值
|
||||
float2 u = f * f * (3.0f - 2.0f * f);
|
||||
|
||||
return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y);
|
||||
}
|
||||
25
src/widget/CMakeLists.txt
Normal file
25
src/widget/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
# ================================================================================================
|
||||
# MIRAI Framework - Widget 模块
|
||||
# 描述: shader_widget 基础类和控件系统
|
||||
# ================================================================================================
|
||||
|
||||
project(mirai_widget)
|
||||
simple_library(STATIC)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
mirai_ui
|
||||
mirai_shader
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
EXPORT_NAME widget
|
||||
OUTPUT_NAME mirai_widget
|
||||
)
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT mirai-targets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
message(STATUS "Configured mirai_widget library")
|
||||
230
src/widget/effects.hpp
Normal file
230
src/widget/effects.hpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* @file effects.hpp
|
||||
* @brief MIRAI 框架效果控件汇总头文件
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件提供所有效果控件的统一包含入口,方便用户一次性包含所有效果。
|
||||
*
|
||||
* 包含的效果控件:
|
||||
* - blur_widget: 高斯模糊效果(Backdrop 模式)
|
||||
* - gradient_widget: 渐变效果(Procedural 模式)
|
||||
* - rounded_rect_mask_widget: 圆角矩形遮罩(Mask 模式)
|
||||
* - particle_widget: 粒子系统(Mesh 模式)
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* #include "widget/effects.hpp"
|
||||
*
|
||||
* using namespace mirai;
|
||||
*
|
||||
* // 创建各种效果控件
|
||||
* auto blur = std::make_shared<blur_widget>();
|
||||
* auto gradient = std::make_shared<gradient_widget>();
|
||||
* auto mask = std::make_shared<rounded_rect_mask_widget>();
|
||||
* auto particles = std::make_shared<particle_widget>(1000);
|
||||
* @endcode
|
||||
*
|
||||
* @see shader_widget_base
|
||||
* @see shader_widget
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// 基础类型和基类
|
||||
// ============================================================================
|
||||
|
||||
#include "widget_types.hpp"
|
||||
#include "shader_widget_base.hpp"
|
||||
#include "shader_widget.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// Backdrop 模式效果
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @defgroup backdrop_effects Backdrop 模式效果
|
||||
* @brief 后效处理效果,可采样背景纹理进行处理
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "effects/blur_widget.hpp"
|
||||
|
||||
/** @} */
|
||||
|
||||
// ============================================================================
|
||||
// Procedural 模式效果
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @defgroup procedural_effects Procedural 模式效果
|
||||
* @brief 程序化渲染效果,基于时间和坐标生成图形
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "effects/gradient_widget.hpp"
|
||||
|
||||
/** @} */
|
||||
|
||||
// ============================================================================
|
||||
// Mask 模式效果
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @defgroup mask_effects Mask 模式效果
|
||||
* @brief 遮罩效果,用于裁剪和形状遮罩
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "effects/rounded_rect_mask_widget.hpp"
|
||||
|
||||
/** @} */
|
||||
|
||||
// ============================================================================
|
||||
// Mesh 模式效果
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @defgroup mesh_effects Mesh 模式效果
|
||||
* @brief 自定义网格渲染效果,支持实例化渲染
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "effects/particle_widget.hpp"
|
||||
|
||||
/** @} */
|
||||
|
||||
// ============================================================================
|
||||
// 便捷类型别名
|
||||
// ============================================================================
|
||||
|
||||
namespace mirai {
|
||||
|
||||
/**
|
||||
* @brief 效果控件类型别名
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// 模糊效果控件指针类型
|
||||
using blur_widget_ptr = std::shared_ptr<blur_widget>;
|
||||
|
||||
/// 渐变效果控件指针类型
|
||||
using gradient_widget_ptr = std::shared_ptr<gradient_widget>;
|
||||
|
||||
/// 圆角遮罩控件指针类型
|
||||
using rounded_rect_mask_widget_ptr = std::shared_ptr<rounded_rect_mask_widget>;
|
||||
|
||||
/// 粒子系统控件指针类型
|
||||
using particle_widget_ptr = std::shared_ptr<particle_widget>;
|
||||
|
||||
/** @} */
|
||||
|
||||
// ============================================================================
|
||||
// 工厂函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 效果控件工厂函数
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 创建模糊效果控件
|
||||
* @param radius 模糊半径(像素),默认 8.0f
|
||||
* @param quality 模糊质量,默认 medium
|
||||
* @return 模糊效果控件指针
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto blur = make_blur_widget(10.0f, blur_quality::high);
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] inline blur_widget_ptr make_blur_widget(
|
||||
f32 radius = 8.0f,
|
||||
blur_quality quality = blur_quality::medium
|
||||
) {
|
||||
return std::make_shared<blur_widget>(radius, quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建渐变效果控件
|
||||
* @param start 起始颜色
|
||||
* @param end 结束颜色
|
||||
* @param type 渐变类型,默认 linear
|
||||
* @return 渐变效果控件指针
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto gradient = make_gradient_widget(
|
||||
* color{1.0f, 0.0f, 0.0f, 1.0f},
|
||||
* color{0.0f, 0.0f, 1.0f, 1.0f},
|
||||
* gradient_type::radial
|
||||
* );
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] inline gradient_widget_ptr make_gradient_widget(
|
||||
const color& start = color{1.0f, 1.0f, 1.0f, 1.0f},
|
||||
const color& end = color{0.0f, 0.0f, 0.0f, 1.0f},
|
||||
gradient_type type = gradient_type::linear
|
||||
) {
|
||||
return std::make_shared<gradient_widget>(start, end, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建圆角遮罩控件
|
||||
* @param radius 统一圆角半径,默认 0.0f
|
||||
* @return 圆角遮罩控件指针
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto mask = make_rounded_rect_mask_widget(0.1f);
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] inline rounded_rect_mask_widget_ptr make_rounded_rect_mask_widget(
|
||||
f32 radius = 0.0f
|
||||
) {
|
||||
return std::make_shared<rounded_rect_mask_widget>(radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建圆角遮罩控件(四角独立)
|
||||
* @param top_left 左上角圆角半径
|
||||
* @param top_right 右上角圆角半径
|
||||
* @param bottom_right 右下角圆角半径
|
||||
* @param bottom_left 左下角圆角半径
|
||||
* @return 圆角遮罩控件指针
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto mask = make_rounded_rect_mask_widget(0.1f, 0.1f, 0.2f, 0.2f);
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] inline rounded_rect_mask_widget_ptr make_rounded_rect_mask_widget(
|
||||
f32 top_left,
|
||||
f32 top_right,
|
||||
f32 bottom_right,
|
||||
f32 bottom_left
|
||||
) {
|
||||
return std::make_shared<rounded_rect_mask_widget>(
|
||||
top_left, top_right, bottom_right, bottom_left
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建粒子系统控件
|
||||
* @param count 粒子数量,默认 100
|
||||
* @return 粒子系统控件指针
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto particles = make_particle_widget(1000);
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] inline particle_widget_ptr make_particle_widget(u32 count = 100) {
|
||||
return std::make_shared<particle_widget>(count);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
} // namespace mirai
|
||||
164
src/widget/effects/blur_widget.hpp
Normal file
164
src/widget/effects/blur_widget.hpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @file blur_widget.hpp
|
||||
* @brief 高斯模糊效果控件
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 blur_widget 类,实现 Backdrop 模式的高斯模糊效果。
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto blur = std::make_shared<blur_widget>();
|
||||
* blur->set_blur_radius(10.0f);
|
||||
* blur->set_blur_quality(blur_quality::medium);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_widget.hpp"
|
||||
#include ""
|
||||
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// 模糊质量枚举
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 模糊质量等级
|
||||
*/
|
||||
enum class blur_quality : u8 {
|
||||
low = 1, ///< 低质量(4 次采样)
|
||||
medium = 2, ///< 中等质量(8 次采样)
|
||||
high = 3 ///< 高质量(12 次采样)
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// blur_widget 类
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 高斯模糊效果控件
|
||||
*
|
||||
* 继承自 backdrop_widget,实现对背景的高斯模糊效果。
|
||||
*
|
||||
* 特点:
|
||||
* - 可调节的模糊半径
|
||||
* - 三档质量选择(低/中/高)
|
||||
* - 使用分离式高斯模糊算法
|
||||
*
|
||||
* @see backdrop_widget
|
||||
* @see blur_bindings
|
||||
*/
|
||||
class blur_widget : public backdrop_widget<blur_bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(blur_widget, backdrop_widget<blur_bindings>)
|
||||
|
||||
// ============================================================================================
|
||||
// 构造与析构
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化默认参数:
|
||||
* - blur_radius = 8.0f
|
||||
* - quality = medium
|
||||
*/
|
||||
blur_widget()
|
||||
: blur_radius_(8.0f)
|
||||
, quality_(blur_quality::medium) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带参数的构造函数
|
||||
* @param radius 模糊半径(像素)
|
||||
* @param quality 模糊质量
|
||||
*/
|
||||
explicit blur_widget(f32 radius, blur_quality quality = blur_quality::medium)
|
||||
: blur_radius_(radius)
|
||||
, quality_(quality) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
~blur_widget() override = default;
|
||||
|
||||
// ============================================================================================
|
||||
// 参数设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置模糊半径
|
||||
* @param radius 模糊半径(像素),范围 [0, 100]
|
||||
*/
|
||||
void set_blur_radius(f32 radius) {
|
||||
blur_radius_ = std::clamp(radius, 0.0f, 100.0f);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模糊半径
|
||||
* @return 当前模糊半径
|
||||
*/
|
||||
[[nodiscard]] f32 blur_radius() const noexcept { return blur_radius_; }
|
||||
|
||||
/**
|
||||
* @brief 设置模糊质量
|
||||
* @param quality 模糊质量等级
|
||||
*/
|
||||
void set_blur_quality(blur_quality quality) {
|
||||
quality_ = quality;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模糊质量
|
||||
* @return 当前模糊质量
|
||||
*/
|
||||
[[nodiscard]] blur_quality quality() const noexcept { return quality_; }
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 重写基类方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 更新 Uniform Buffer
|
||||
*
|
||||
* 将模糊参数写入 GPU 缓冲区。
|
||||
*/
|
||||
void update_uniform_buffer() override {
|
||||
// BlurParams 结构体布局(与 blur.slang 中定义一致)
|
||||
struct BlurParams {
|
||||
f32 blur_radius;
|
||||
f32 blur_quality;
|
||||
f32 _padding[2];
|
||||
};
|
||||
|
||||
BlurParams params{};
|
||||
params.blur_radius = blur_radius_;
|
||||
params.blur_quality = static_cast<f32>(quality_);
|
||||
|
||||
// TODO: 将 params 写入 uniform buffer
|
||||
// uniform_buffer_->write(¶ms, sizeof(params));
|
||||
}
|
||||
|
||||
private:
|
||||
// ============================================================================================
|
||||
// 成员变量
|
||||
// ============================================================================================
|
||||
|
||||
/// 模糊半径(像素)
|
||||
f32 blur_radius_;
|
||||
|
||||
/// 模糊质量
|
||||
blur_quality quality_;
|
||||
};
|
||||
|
||||
} // namespace mirai
|
||||
372
src/widget/effects/gradient_widget.hpp
Normal file
372
src/widget/effects/gradient_widget.hpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @file gradient_widget.hpp
|
||||
* @brief 渐变效果控件
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 gradient_widget 类,实现 Procedural 模式的渐变效果。
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto gradient = std::make_shared<gradient_widget>();
|
||||
* gradient->set_start_color(color::red());
|
||||
* gradient->set_end_color(color::blue());
|
||||
* gradient->set_gradient_type(gradient_type::radial);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_widget.hpp"
|
||||
#include "types/color.h"
|
||||
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// 渐变类型枚举
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 渐变类型
|
||||
*/
|
||||
enum class gradient_type : u32 {
|
||||
linear = 0, ///< 线性渐变
|
||||
radial = 1, ///< 径向渐变
|
||||
angular = 2, ///< 角度渐变(圆锥渐变)
|
||||
diamond = 3 ///< 菱形渐变
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// Gradient 绑定类型(模拟代码生成器输出)
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief gradient_widget 的着色器绑定类型
|
||||
*
|
||||
* 此类型模拟代码生成器的输出,提供:
|
||||
* - SPIR-V 字节码访问
|
||||
* - 绑定点常量
|
||||
* - 静态反射信息
|
||||
*
|
||||
* @note 在实际项目中,此类型由 shader_codegen 工具自动生成
|
||||
*/
|
||||
struct gradient_bindings {
|
||||
// ============================================================================================
|
||||
// 着色器存在性标记
|
||||
// ============================================================================================
|
||||
|
||||
static constexpr bool HAS_VERTEX_SHADER = true;
|
||||
static constexpr bool HAS_FRAGMENT_SHADER = true;
|
||||
|
||||
// ============================================================================================
|
||||
// Procedural 模式特有标记
|
||||
// ============================================================================================
|
||||
|
||||
static constexpr bool HAS_FRAME_DATA = true;
|
||||
static constexpr u32 FRAME_DATA_BINDING = 0;
|
||||
static constexpr u32 FRAME_DATA_SET = 0;
|
||||
|
||||
// ============================================================================================
|
||||
// SPIR-V 数据访问(占位实现)
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
* @note 实际实现中,此数据由编译后的着色器提供
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> get_vertex_spirv() {
|
||||
// 占位:实际数据由着色器编译生成
|
||||
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
|
||||
return std::span<const u32>(placeholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取片段着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
* @note 实际实现中,此数据由编译后的着色器提供
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> get_fragment_spirv() {
|
||||
// 占位:实际数据由着色器编译生成
|
||||
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
|
||||
return std::span<const u32>(placeholder);
|
||||
}
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// gradient_widget 类
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 渐变效果控件
|
||||
*
|
||||
* 继承自 procedural_widget,实现程序化渐变效果。
|
||||
*
|
||||
* 特点:
|
||||
* - 支持四种渐变类型(线性、径向、角度、菱形)
|
||||
* - 可自定义起始和结束颜色
|
||||
* - 支持渐变角度和中心点设置
|
||||
* - 支持重复模式
|
||||
*
|
||||
* @see procedural_widget
|
||||
* @see gradient_bindings
|
||||
*/
|
||||
class gradient_widget : public procedural_widget<gradient_bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(gradient_widget, procedural_widget<gradient_bindings>)
|
||||
|
||||
// ============================================================================================
|
||||
// 构造与析构
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化默认参数:
|
||||
* - start_color = 白色
|
||||
* - end_color = 黑色
|
||||
* - type = linear
|
||||
* - angle = 0 (水平)
|
||||
*/
|
||||
gradient_widget()
|
||||
: start_color_{1.0f, 1.0f, 1.0f, 1.0f}
|
||||
, end_color_{0.0f, 0.0f, 0.0f, 1.0f}
|
||||
, type_(gradient_type::linear)
|
||||
, angle_(0.0f)
|
||||
, center_{0.5f, 0.5f}
|
||||
, radius_(0.5f)
|
||||
, repeat_count_(0.0f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带颜色参数的构造函数
|
||||
* @param start 起始颜色
|
||||
* @param end 结束颜色
|
||||
* @param type 渐变类型
|
||||
*/
|
||||
gradient_widget(const color& start, const color& end, gradient_type type = gradient_type::linear)
|
||||
: start_color_(start)
|
||||
, end_color_(end)
|
||||
, type_(type)
|
||||
, angle_(0.0f)
|
||||
, center_{0.5f, 0.5f}
|
||||
, radius_(0.5f)
|
||||
, repeat_count_(0.0f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
~gradient_widget() override = default;
|
||||
|
||||
// ============================================================================================
|
||||
// 颜色设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置起始颜色
|
||||
* @param c 起始颜色
|
||||
*/
|
||||
void set_start_color(const color& c) {
|
||||
start_color_ = c;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取起始颜色
|
||||
* @return 当前起始颜色
|
||||
*/
|
||||
[[nodiscard]] const color& start_color() const noexcept { return start_color_; }
|
||||
|
||||
/**
|
||||
* @brief 设置结束颜色
|
||||
* @param c 结束颜色
|
||||
*/
|
||||
void set_end_color(const color& c) {
|
||||
end_color_ = c;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取结束颜色
|
||||
* @return 当前结束颜色
|
||||
*/
|
||||
[[nodiscard]] const color& end_color() const noexcept { return end_color_; }
|
||||
|
||||
/**
|
||||
* @brief 同时设置起始和结束颜色
|
||||
* @param start 起始颜色
|
||||
* @param end 结束颜色
|
||||
*/
|
||||
void set_colors(const color& start, const color& end) {
|
||||
start_color_ = start;
|
||||
end_color_ = end;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
// ============================================================================================
|
||||
// 渐变类型设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置渐变类型
|
||||
* @param type 渐变类型
|
||||
*/
|
||||
void set_gradient_type(gradient_type type) {
|
||||
type_ = type;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取渐变类型
|
||||
* @return 当前渐变类型
|
||||
*/
|
||||
[[nodiscard]] gradient_type type() const noexcept { return type_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 渐变参数设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置渐变角度(用于线性渐变)
|
||||
* @param angle_degrees 角度(度数),0 = 水平向右
|
||||
*/
|
||||
void set_angle(f32 angle_degrees) {
|
||||
angle_ = angle_degrees * (3.14159265358979323846f / 180.0f);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取渐变角度
|
||||
* @return 当前角度(弧度)
|
||||
*/
|
||||
[[nodiscard]] f32 angle() const noexcept { return angle_; }
|
||||
|
||||
/**
|
||||
* @brief 设置渐变中心(用于径向/角度渐变)
|
||||
* @param x 中心 X 坐标 (0-1)
|
||||
* @param y 中心 Y 坐标 (0-1)
|
||||
*/
|
||||
void set_center(f32 x, f32 y) {
|
||||
center_[0] = x;
|
||||
center_[1] = y;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取渐变中心 X 坐标
|
||||
* @return 中心 X 坐标
|
||||
*/
|
||||
[[nodiscard]] f32 center_x() const noexcept { return center_[0]; }
|
||||
|
||||
/**
|
||||
* @brief 获取渐变中心 Y 坐标
|
||||
* @return 中心 Y 坐标
|
||||
*/
|
||||
[[nodiscard]] f32 center_y() const noexcept { return center_[1]; }
|
||||
|
||||
/**
|
||||
* @brief 设置渐变半径(用于径向渐变)
|
||||
* @param r 半径 (0-1)
|
||||
*/
|
||||
void set_radius(f32 r) {
|
||||
radius_ = std::max(0.001f, r);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取渐变半径
|
||||
* @return 当前半径
|
||||
*/
|
||||
[[nodiscard]] f32 radius() const noexcept { return radius_; }
|
||||
|
||||
/**
|
||||
* @brief 设置重复次数
|
||||
* @param count 重复次数(0 = 不重复)
|
||||
*/
|
||||
void set_repeat_count(f32 count) {
|
||||
repeat_count_ = std::max(0.0f, count);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取重复次数
|
||||
* @return 当前重复次数
|
||||
*/
|
||||
[[nodiscard]] f32 repeat_count() const noexcept { return repeat_count_; }
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 重写基类方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 更新 Uniform Buffer
|
||||
*
|
||||
* 将渐变参数写入 GPU 缓冲区。
|
||||
*/
|
||||
void update_uniform_buffer() override {
|
||||
// GradientParams 结构体布局(与 gradient.slang 中定义一致)
|
||||
struct GradientParams {
|
||||
f32 start_color[4];
|
||||
f32 end_color[4];
|
||||
f32 angle;
|
||||
u32 gradient_type;
|
||||
f32 center[2];
|
||||
f32 radius;
|
||||
f32 repeat_count;
|
||||
f32 _padding[2];
|
||||
};
|
||||
|
||||
GradientParams params{};
|
||||
params.start_color[0] = start_color_.r;
|
||||
params.start_color[1] = start_color_.g;
|
||||
params.start_color[2] = start_color_.b;
|
||||
params.start_color[3] = start_color_.a;
|
||||
params.end_color[0] = end_color_.r;
|
||||
params.end_color[1] = end_color_.g;
|
||||
params.end_color[2] = end_color_.b;
|
||||
params.end_color[3] = end_color_.a;
|
||||
params.angle = angle_;
|
||||
params.gradient_type = static_cast<u32>(type_);
|
||||
params.center[0] = center_[0];
|
||||
params.center[1] = center_[1];
|
||||
params.radius = radius_;
|
||||
params.repeat_count = repeat_count_;
|
||||
|
||||
// TODO: 将 params 写入 uniform buffer
|
||||
// uniform_buffer_->write(¶ms, sizeof(params));
|
||||
}
|
||||
|
||||
private:
|
||||
// ============================================================================================
|
||||
// 成员变量
|
||||
// ============================================================================================
|
||||
|
||||
/// 起始颜色
|
||||
color start_color_;
|
||||
|
||||
/// 结束颜色
|
||||
color end_color_;
|
||||
|
||||
/// 渐变类型
|
||||
gradient_type type_;
|
||||
|
||||
/// 渐变角度(弧度)
|
||||
f32 angle_;
|
||||
|
||||
/// 渐变中心
|
||||
f32 center_[2];
|
||||
|
||||
/// 渐变半径
|
||||
f32 radius_;
|
||||
|
||||
/// 重复次数
|
||||
f32 repeat_count_;
|
||||
};
|
||||
|
||||
} // namespace mirai
|
||||
485
src/widget/effects/particle_widget.hpp
Normal file
485
src/widget/effects/particle_widget.hpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/**
|
||||
* @file particle_widget.hpp
|
||||
* @brief 粒子系统控件
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 particle_widget 类,实现 Mesh 模式的粒子渲染效果。
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto particles = std::make_shared<particle_widget>();
|
||||
* particles->set_particle_count(1000);
|
||||
* particles->set_particle_size(5.0f, 1.0f); // 起始大小 -> 结束大小
|
||||
* particles->set_colors(color::yellow(), color::red());
|
||||
* particles->set_particle_mode(particle_mode::billboard);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_widget.hpp"
|
||||
#include "types/color.h"
|
||||
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// 粒子渲染模式枚举
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 粒子渲染模式
|
||||
*/
|
||||
enum class particle_mode : u32 {
|
||||
billboard = 0, ///< Billboard 模式(始终面向相机)
|
||||
velocity_aligned = 1, ///< 速度对齐模式
|
||||
stretched = 2 ///< 拉伸模式(沿速度方向拉伸)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 粒子形状
|
||||
*/
|
||||
enum class particle_shape : u8 {
|
||||
textured, ///< 使用纹理
|
||||
circle, ///< 圆形(无纹理)
|
||||
soft, ///< 软边缘圆形
|
||||
glow ///< 发光效果
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// 粒子实例数据
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 单个粒子的实例数据
|
||||
*
|
||||
* 与 mesh.slang 中的 ParticleInstance 结构体对应
|
||||
*/
|
||||
struct particle_instance {
|
||||
f32 position[3]; ///< 粒子位置
|
||||
f32 size; ///< 粒子大小
|
||||
f32 color[4]; ///< 粒子颜色
|
||||
f32 rotation; ///< 粒子旋转
|
||||
f32 life; ///< 生命周期 (0-1)
|
||||
f32 velocity[2]; ///< 速度
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// Particle 绑定类型(模拟代码生成器输出)
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief particle_widget 的着色器绑定类型
|
||||
*
|
||||
* 此类型模拟代码生成器的输出,提供:
|
||||
* - SPIR-V 字节码访问
|
||||
* - 绑定点常量
|
||||
* - 顶点类型定义
|
||||
*
|
||||
* @note 在实际项目中,此类型由 shader_codegen 工具自动生成
|
||||
*/
|
||||
struct particle_bindings {
|
||||
// ============================================================================================
|
||||
// 着色器存在性标记
|
||||
// ============================================================================================
|
||||
|
||||
static constexpr bool HAS_VERTEX_SHADER = true;
|
||||
static constexpr bool HAS_FRAGMENT_SHADER = true;
|
||||
|
||||
// ============================================================================================
|
||||
// Mesh 模式特有标记
|
||||
// ============================================================================================
|
||||
|
||||
static constexpr bool HAS_CUSTOM_VERTEX_INPUT = true;
|
||||
|
||||
/// 顶点类型定义
|
||||
using VertexType = particle_instance;
|
||||
|
||||
// ============================================================================================
|
||||
// SPIR-V 数据访问(占位实现)
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
* @note 实际实现中,此数据由编译后的着色器提供
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> get_vertex_spirv() {
|
||||
// 占位:实际数据由着色器编译生成
|
||||
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
|
||||
return std::span<const u32>(placeholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取片段着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
* @note 实际实现中,此数据由编译后的着色器提供
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> get_fragment_spirv() {
|
||||
// 占位:实际数据由着色器编译生成
|
||||
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
|
||||
return std::span<const u32>(placeholder);
|
||||
}
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// particle_widget 类
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 粒子系统控件
|
||||
*
|
||||
* 继承自 mesh_widget,使用实例化渲染绘制大量粒子。
|
||||
*
|
||||
* 特点:
|
||||
* - 支持大量粒子的高效渲染
|
||||
* - 多种渲染模式(Billboard、速度对齐、拉伸)
|
||||
* - 生命周期颜色和大小动画
|
||||
* - 淡入淡出效果
|
||||
*
|
||||
* @see mesh_widget
|
||||
* @see particle_bindings
|
||||
*/
|
||||
class particle_widget : public mesh_widget<particle_bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(particle_widget, mesh_widget<particle_bindings>)
|
||||
|
||||
// ============================================================================================
|
||||
// 构造与析构
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化默认参数:
|
||||
* - particle_count = 100
|
||||
* - size_start = 10.0f
|
||||
* - size_end = 2.0f
|
||||
* - color_start = 白色
|
||||
* - color_end = 透明白色
|
||||
*/
|
||||
particle_widget()
|
||||
: particle_count_(100)
|
||||
, size_start_(10.0f)
|
||||
, size_end_(2.0f)
|
||||
, color_start_{1.0f, 1.0f, 1.0f, 1.0f}
|
||||
, color_end_{1.0f, 1.0f, 1.0f, 0.0f}
|
||||
, fade_in_(0.1f)
|
||||
, fade_out_(0.3f)
|
||||
, mode_(particle_mode::billboard)
|
||||
, shape_(particle_shape::circle)
|
||||
, stretch_factor_(0.5f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带粒子数量的构造函数
|
||||
* @param count 粒子数量
|
||||
*/
|
||||
explicit particle_widget(u32 count)
|
||||
: particle_count_(count)
|
||||
, size_start_(10.0f)
|
||||
, size_end_(2.0f)
|
||||
, color_start_{1.0f, 1.0f, 1.0f, 1.0f}
|
||||
, color_end_{1.0f, 1.0f, 1.0f, 0.0f}
|
||||
, fade_in_(0.1f)
|
||||
, fade_out_(0.3f)
|
||||
, mode_(particle_mode::billboard)
|
||||
, shape_(particle_shape::circle)
|
||||
, stretch_factor_(0.5f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
~particle_widget() override = default;
|
||||
|
||||
// ============================================================================================
|
||||
// 粒子数量设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置粒子数量
|
||||
* @param count 粒子数量
|
||||
*/
|
||||
void set_particle_count(u32 count) {
|
||||
particle_count_ = count;
|
||||
particles_.resize(count);
|
||||
mark_dirty(widget_dirty_flags::vertex_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取粒子数量
|
||||
* @return 当前粒子数量
|
||||
*/
|
||||
[[nodiscard]] u32 particle_count() const noexcept { return particle_count_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 大小设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置粒子大小范围
|
||||
* @param start 起始大小(刚出生时)
|
||||
* @param end 结束大小(死亡时)
|
||||
*/
|
||||
void set_particle_size(f32 start, f32 end) {
|
||||
size_start_ = std::max(0.0f, start);
|
||||
size_end_ = std::max(0.0f, end);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置统一粒子大小
|
||||
* @param size 粒子大小
|
||||
*/
|
||||
void set_particle_size(f32 size) {
|
||||
set_particle_size(size, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取起始大小
|
||||
* @return 起始大小
|
||||
*/
|
||||
[[nodiscard]] f32 size_start() const noexcept { return size_start_; }
|
||||
|
||||
/**
|
||||
* @brief 获取结束大小
|
||||
* @return 结束大小
|
||||
*/
|
||||
[[nodiscard]] f32 size_end() const noexcept { return size_end_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 颜色设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置粒子颜色范围
|
||||
* @param start 起始颜色(刚出生时)
|
||||
* @param end 结束颜色(死亡时)
|
||||
*/
|
||||
void set_colors(const color& start, const color& end) {
|
||||
color_start_ = start;
|
||||
color_end_ = end;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置起始颜色
|
||||
* @param c 起始颜色
|
||||
*/
|
||||
void set_color_start(const color& c) {
|
||||
color_start_ = c;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置结束颜色
|
||||
* @param c 结束颜色
|
||||
*/
|
||||
void set_color_end(const color& c) {
|
||||
color_end_ = c;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取起始颜色
|
||||
* @return 起始颜色
|
||||
*/
|
||||
[[nodiscard]] const color& color_start() const noexcept { return color_start_; }
|
||||
|
||||
/**
|
||||
* @brief 获取结束颜色
|
||||
* @return 结束颜色
|
||||
*/
|
||||
[[nodiscard]] const color& color_end() const noexcept { return color_end_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 淡入淡出设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置淡入淡出时间
|
||||
* @param fade_in 淡入时间比例 (0-1)
|
||||
* @param fade_out 淡出时间比例 (0-1)
|
||||
*/
|
||||
void set_fade(f32 fade_in, f32 fade_out) {
|
||||
fade_in_ = std::clamp(fade_in, 0.0f, 1.0f);
|
||||
fade_out_ = std::clamp(fade_out, 0.0f, 1.0f);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取淡入时间
|
||||
* @return 淡入时间比例
|
||||
*/
|
||||
[[nodiscard]] f32 fade_in() const noexcept { return fade_in_; }
|
||||
|
||||
/**
|
||||
* @brief 获取淡出时间
|
||||
* @return 淡出时间比例
|
||||
*/
|
||||
[[nodiscard]] f32 fade_out() const noexcept { return fade_out_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 渲染模式设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置粒子渲染模式
|
||||
* @param mode 渲染模式
|
||||
*/
|
||||
void set_particle_mode(particle_mode mode) {
|
||||
mode_ = mode;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取粒子渲染模式
|
||||
* @return 当前渲染模式
|
||||
*/
|
||||
[[nodiscard]] particle_mode mode() const noexcept { return mode_; }
|
||||
|
||||
/**
|
||||
* @brief 设置粒子形状
|
||||
* @param shape 粒子形状
|
||||
*/
|
||||
void set_particle_shape(particle_shape shape) {
|
||||
shape_ = shape;
|
||||
mark_dirty(widget_dirty_flags::pipeline);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取粒子形状
|
||||
* @return 当前粒子形状
|
||||
*/
|
||||
[[nodiscard]] particle_shape shape() const noexcept { return shape_; }
|
||||
|
||||
/**
|
||||
* @brief 设置拉伸因子
|
||||
* @param factor 拉伸因子(用于速度拉伸模式)
|
||||
*/
|
||||
void set_stretch_factor(f32 factor) {
|
||||
stretch_factor_ = std::max(0.0f, factor);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取拉伸因子
|
||||
* @return 当前拉伸因子
|
||||
*/
|
||||
[[nodiscard]] f32 stretch_factor() const noexcept { return stretch_factor_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 粒子数据访问
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取粒子数据(可修改)
|
||||
* @return 粒子数据向量的引用
|
||||
*/
|
||||
[[nodiscard]] std::vector<particle_instance>& particles() { return particles_; }
|
||||
|
||||
/**
|
||||
* @brief 获取粒子数据(只读)
|
||||
* @return 粒子数据向量的常量引用
|
||||
*/
|
||||
[[nodiscard]] const std::vector<particle_instance>& particles() const { return particles_; }
|
||||
|
||||
/**
|
||||
* @brief 标记粒子数据已更新
|
||||
*
|
||||
* 在修改粒子数据后调用此方法以触发 GPU 缓冲区更新
|
||||
*/
|
||||
void mark_particles_dirty() {
|
||||
mark_dirty(widget_dirty_flags::vertex_buffer);
|
||||
}
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 重写基类方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 更新 Uniform Buffer
|
||||
*
|
||||
* 将粒子系统参数写入 GPU 缓冲区。
|
||||
*/
|
||||
void update_uniform_buffer() override {
|
||||
// ParticleSystemParams 结构体布局(与 particle.slang 中定义一致)
|
||||
struct ParticleSystemParams {
|
||||
f32 color_start[4];
|
||||
f32 color_end[4];
|
||||
f32 size_start;
|
||||
f32 size_end;
|
||||
f32 fade_in;
|
||||
f32 fade_out;
|
||||
u32 particle_mode;
|
||||
f32 stretch_factor;
|
||||
f32 _padding[2];
|
||||
};
|
||||
|
||||
ParticleSystemParams params{};
|
||||
params.color_start[0] = color_start_.r;
|
||||
params.color_start[1] = color_start_.g;
|
||||
params.color_start[2] = color_start_.b;
|
||||
params.color_start[3] = color_start_.a;
|
||||
params.color_end[0] = color_end_.r;
|
||||
params.color_end[1] = color_end_.g;
|
||||
params.color_end[2] = color_end_.b;
|
||||
params.color_end[3] = color_end_.a;
|
||||
params.size_start = size_start_;
|
||||
params.size_end = size_end_;
|
||||
params.fade_in = fade_in_;
|
||||
params.fade_out = fade_out_;
|
||||
params.particle_mode = static_cast<u32>(mode_);
|
||||
params.stretch_factor = stretch_factor_;
|
||||
|
||||
// TODO: 将 params 写入 uniform buffer
|
||||
// uniform_buffer_->write(¶ms, sizeof(params));
|
||||
}
|
||||
|
||||
private:
|
||||
// ============================================================================================
|
||||
// 成员变量
|
||||
// ============================================================================================
|
||||
|
||||
/// 粒子数量
|
||||
u32 particle_count_;
|
||||
|
||||
/// 起始大小
|
||||
f32 size_start_;
|
||||
|
||||
/// 结束大小
|
||||
f32 size_end_;
|
||||
|
||||
/// 起始颜色
|
||||
color color_start_;
|
||||
|
||||
/// 结束颜色
|
||||
color color_end_;
|
||||
|
||||
/// 淡入时间比例
|
||||
f32 fade_in_;
|
||||
|
||||
/// 淡出时间比例
|
||||
f32 fade_out_;
|
||||
|
||||
/// 渲染模式
|
||||
particle_mode mode_;
|
||||
|
||||
/// 粒子形状
|
||||
particle_shape shape_;
|
||||
|
||||
/// 拉伸因子
|
||||
f32 stretch_factor_;
|
||||
|
||||
/// 粒子实例数据
|
||||
std::vector<particle_instance> particles_;
|
||||
};
|
||||
|
||||
} // namespace mirai
|
||||
313
src/widget/effects/rounded_rect_mask_widget.hpp
Normal file
313
src/widget/effects/rounded_rect_mask_widget.hpp
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* @file rounded_rect_mask_widget.hpp
|
||||
* @brief 圆角矩形遮罩控件
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 rounded_rect_mask_widget 类,实现 Mask 模式的圆角矩形遮罩效果。
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* auto mask = std::make_shared<rounded_rect_mask_widget>();
|
||||
* mask->set_corner_radius(10.0f); // 统一圆角
|
||||
* // 或者设置四角独立圆角
|
||||
* mask->set_corner_radius(10.0f, 20.0f, 10.0f, 20.0f);
|
||||
* mask->set_feather(2.0f); // 边缘羽化
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_widget.hpp"
|
||||
#include "types/corner_radius.h"
|
||||
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// RoundedRectMask 绑定类型(模拟代码生成器输出)
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief rounded_rect_mask_widget 的着色器绑定类型
|
||||
*
|
||||
* 此类型模拟代码生成器的输出,提供:
|
||||
* - SPIR-V 字节码访问
|
||||
* - 绑定点常量
|
||||
* - 静态反射信息
|
||||
*
|
||||
* @note 在实际项目中,此类型由 shader_codegen 工具自动生成
|
||||
*/
|
||||
struct rounded_rect_mask_bindings {
|
||||
// ============================================================================================
|
||||
// 着色器存在性标记
|
||||
// ============================================================================================
|
||||
|
||||
static constexpr bool HAS_VERTEX_SHADER = true;
|
||||
static constexpr bool HAS_FRAGMENT_SHADER = true;
|
||||
|
||||
// ============================================================================================
|
||||
// Mask 模式特有标记
|
||||
// ============================================================================================
|
||||
|
||||
static constexpr bool HAS_MASK_SAMPLER = true;
|
||||
static constexpr u32 MASK_BINDING = 0;
|
||||
static constexpr u32 MASK_SET = 0;
|
||||
|
||||
// ============================================================================================
|
||||
// SPIR-V 数据访问(占位实现)
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
* @note 实际实现中,此数据由编译后的着色器提供
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> get_vertex_spirv() {
|
||||
// 占位:实际数据由着色器编译生成
|
||||
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
|
||||
return std::span<const u32>(placeholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取片段着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
* @note 实际实现中,此数据由编译后的着色器提供
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> get_fragment_spirv() {
|
||||
// 占位:实际数据由着色器编译生成
|
||||
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
|
||||
return std::span<const u32>(placeholder);
|
||||
}
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// rounded_rect_mask_widget 类
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 圆角矩形遮罩控件
|
||||
*
|
||||
* 继承自 mask_widget,使用 SDF 生成圆角矩形遮罩。
|
||||
*
|
||||
* 特点:
|
||||
* - 支持四角独立圆角半径
|
||||
* - 可调节的边缘羽化
|
||||
* - 支持边框模式
|
||||
* - 高质量抗锯齿边缘
|
||||
*
|
||||
* @see mask_widget
|
||||
* @see rounded_rect_mask_bindings
|
||||
*/
|
||||
class rounded_rect_mask_widget : public mask_widget<rounded_rect_mask_bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(rounded_rect_mask_widget, mask_widget<rounded_rect_mask_bindings>)
|
||||
|
||||
// ============================================================================================
|
||||
// 构造与析构
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化默认参数:
|
||||
* - corner_radius = 0 (无圆角)
|
||||
* - feather = 0 (无羽化)
|
||||
* - border_width = 0 (填充模式)
|
||||
*/
|
||||
rounded_rect_mask_widget()
|
||||
: corner_radius_{0.0f, 0.0f, 0.0f, 0.0f}
|
||||
, feather_(0.0f)
|
||||
, border_width_(0.0f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带统一圆角的构造函数
|
||||
* @param radius 统一圆角半径
|
||||
*/
|
||||
explicit rounded_rect_mask_widget(f32 radius)
|
||||
: corner_radius_{radius, radius, radius, radius}
|
||||
, feather_(0.0f)
|
||||
, border_width_(0.0f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带四角独立圆角的构造函数
|
||||
* @param top_left 左上角圆角半径
|
||||
* @param top_right 右上角圆角半径
|
||||
* @param bottom_right 右下角圆角半径
|
||||
* @param bottom_left 左下角圆角半径
|
||||
*/
|
||||
rounded_rect_mask_widget(f32 top_left, f32 top_right, f32 bottom_right, f32 bottom_left)
|
||||
: corner_radius_{top_left, top_right, bottom_right, bottom_left}
|
||||
, feather_(0.0f)
|
||||
, border_width_(0.0f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
~rounded_rect_mask_widget() override = default;
|
||||
|
||||
// ============================================================================================
|
||||
// 圆角设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置统一圆角半径
|
||||
* @param radius 圆角半径(相对于控件尺寸,范围 0-0.5)
|
||||
*/
|
||||
void set_corner_radius(f32 radius) {
|
||||
radius = std::clamp(radius, 0.0f, 0.5f);
|
||||
corner_radius_[0] = radius;
|
||||
corner_radius_[1] = radius;
|
||||
corner_radius_[2] = radius;
|
||||
corner_radius_[3] = radius;
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置四角独立圆角半径
|
||||
* @param top_left 左上角圆角半径
|
||||
* @param top_right 右上角圆角半径
|
||||
* @param bottom_right 右下角圆角半径
|
||||
* @param bottom_left 左下角圆角半径
|
||||
*/
|
||||
void set_corner_radius(f32 top_left, f32 top_right, f32 bottom_right, f32 bottom_left) {
|
||||
corner_radius_[0] = std::clamp(top_left, 0.0f, 0.5f);
|
||||
corner_radius_[1] = std::clamp(top_right, 0.0f, 0.5f);
|
||||
corner_radius_[2] = std::clamp(bottom_right, 0.0f, 0.5f);
|
||||
corner_radius_[3] = std::clamp(bottom_left, 0.0f, 0.5f);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使用 corner_radius 类型设置圆角
|
||||
* @param radius corner_radius 对象
|
||||
*/
|
||||
void set_corner_radius(const corner_radius& radius) {
|
||||
corner_radius_[0] = std::clamp(radius.top_left, 0.0f, 0.5f);
|
||||
corner_radius_[1] = std::clamp(radius.top_right, 0.0f, 0.5f);
|
||||
corner_radius_[2] = std::clamp(radius.bottom_right, 0.0f, 0.5f);
|
||||
corner_radius_[3] = std::clamp(radius.bottom_left, 0.0f, 0.5f);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取左上角圆角半径
|
||||
* @return 左上角圆角半径
|
||||
*/
|
||||
[[nodiscard]] f32 top_left_radius() const noexcept { return corner_radius_[0]; }
|
||||
|
||||
/**
|
||||
* @brief 获取右上角圆角半径
|
||||
* @return 右上角圆角半径
|
||||
*/
|
||||
[[nodiscard]] f32 top_right_radius() const noexcept { return corner_radius_[1]; }
|
||||
|
||||
/**
|
||||
* @brief 获取右下角圆角半径
|
||||
* @return 右下角圆角半径
|
||||
*/
|
||||
[[nodiscard]] f32 bottom_right_radius() const noexcept { return corner_radius_[2]; }
|
||||
|
||||
/**
|
||||
* @brief 获取左下角圆角半径
|
||||
* @return 左下角圆角半径
|
||||
*/
|
||||
[[nodiscard]] f32 bottom_left_radius() const noexcept { return corner_radius_[3]; }
|
||||
|
||||
// ============================================================================================
|
||||
// 羽化设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置边缘羽化量
|
||||
* @param feather 羽化量(相对于控件尺寸,范围 0-0.5)
|
||||
*/
|
||||
void set_feather(f32 feather) {
|
||||
feather_ = std::clamp(feather, 0.0f, 0.5f);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边缘羽化量
|
||||
* @return 当前羽化量
|
||||
*/
|
||||
[[nodiscard]] f32 feather() const noexcept { return feather_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 边框设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置边框宽度
|
||||
* @param width 边框宽度(0 = 填充模式)
|
||||
*/
|
||||
void set_border_width(f32 width) {
|
||||
border_width_ = std::max(0.0f, width);
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边框宽度
|
||||
* @return 当前边框宽度
|
||||
*/
|
||||
[[nodiscard]] f32 border_width() const noexcept { return border_width_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否为边框模式
|
||||
* @return 如果边框宽度大于 0 返回 true
|
||||
*/
|
||||
[[nodiscard]] bool is_border_mode() const noexcept { return border_width_ > 0.0f; }
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 重写基类方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 更新 Uniform Buffer
|
||||
*
|
||||
* 将遮罩参数写入 GPU 缓冲区。
|
||||
*/
|
||||
void update_uniform_buffer() override {
|
||||
// RoundedRectMaskParams 结构体布局(与 rounded_rect_mask.slang 中定义一致)
|
||||
struct RoundedRectMaskParams {
|
||||
f32 corner_radius[4];
|
||||
f32 feather;
|
||||
f32 border_width;
|
||||
f32 _padding[2];
|
||||
};
|
||||
|
||||
RoundedRectMaskParams params{};
|
||||
params.corner_radius[0] = corner_radius_[0];
|
||||
params.corner_radius[1] = corner_radius_[1];
|
||||
params.corner_radius[2] = corner_radius_[2];
|
||||
params.corner_radius[3] = corner_radius_[3];
|
||||
params.feather = feather_;
|
||||
params.border_width = border_width_;
|
||||
|
||||
// TODO: 将 params 写入 uniform buffer
|
||||
// uniform_buffer_->write(¶ms, sizeof(params));
|
||||
}
|
||||
|
||||
private:
|
||||
// ============================================================================================
|
||||
// 成员变量
|
||||
// ============================================================================================
|
||||
|
||||
/// 四角圆角半径 (top_left, top_right, bottom_right, bottom_left)
|
||||
f32 corner_radius_[4];
|
||||
|
||||
/// 边缘羽化量
|
||||
f32 feather_;
|
||||
|
||||
/// 边框宽度(0 = 填充模式)
|
||||
f32 border_width_;
|
||||
};
|
||||
|
||||
} // namespace mirai
|
||||
421
src/widget/shader_widget.hpp
Normal file
421
src/widget/shader_widget.hpp
Normal file
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* @file shader_widget.hpp
|
||||
* @brief MIRAI 框架 shader_widget 模板化基类
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 shader_widget 模板化基类,包括:
|
||||
* - 类型安全的着色器资源管理
|
||||
* - 编译时绑定类型检查
|
||||
* - 静态反射信息访问
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_widget_base.hpp"
|
||||
#include "widget_types.hpp"
|
||||
#include "shader_types.hpp"
|
||||
#include "pipeline.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "descriptor_pool.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// shader_widget 模板类
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 模板化的 shader_widget 基类
|
||||
*
|
||||
* @tparam Bindings 着色器绑定类型,必须满足 shader_bindings concept
|
||||
*
|
||||
* 这是所有着色器控件的基类模板。子类通过继承此类并指定绑定类型,
|
||||
* 获得类型安全的着色器资源管理能力。
|
||||
*
|
||||
* Bindings 类型必须提供:
|
||||
* - `HAS_VERTEX_SHADER` 静态常量
|
||||
* - `HAS_FRAGMENT_SHADER` 静态常量
|
||||
* - `get_vertex_spirv()` 静态方法
|
||||
* - `get_fragment_spirv()` 静态方法
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* // 假设 blur_shader_bindings 是由代码生成器生成的绑定类
|
||||
* class blur_widget : public shader_widget<blur_shader_bindings> {
|
||||
* public:
|
||||
* void set_radius(float radius) {
|
||||
* params_.radius = radius;
|
||||
* mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
* }
|
||||
* protected:
|
||||
* void update_uniform_buffer() override {
|
||||
* // 更新 uniform buffer 数据
|
||||
* }
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
template<typename Bindings>
|
||||
requires shader_bindings<Bindings>
|
||||
class shader_widget : public shader_widget_base {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(shader_widget, shader_widget_base)
|
||||
|
||||
/// 绑定类型别名
|
||||
using bindings_type = Bindings;
|
||||
|
||||
// 编译时验证
|
||||
static_assert(Bindings::HAS_VERTEX_SHADER, "Bindings must have vertex shader");
|
||||
static_assert(Bindings::HAS_FRAGMENT_SHADER, "Bindings must have fragment shader");
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
shader_widget() = default;
|
||||
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
~shader_widget() override = default;
|
||||
|
||||
// ============================================================================================
|
||||
// 静态访问器
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> vertex_spirv() {
|
||||
return Bindings::get_vertex_spirv();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取片段着色器 SPIR-V 数据
|
||||
* @return SPIR-V 字节码的 span
|
||||
*/
|
||||
[[nodiscard]] static std::span<const u32> fragment_spirv() {
|
||||
return Bindings::get_fragment_spirv();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取静态反射信息
|
||||
*
|
||||
* 如果 Bindings 类型提供了 get_reflection() 方法,则返回反射信息;
|
||||
* 否则返回空的反射信息。
|
||||
*
|
||||
* @return 静态着色器反射信息
|
||||
*/
|
||||
[[nodiscard]] static const static_shader_reflection& reflection() {
|
||||
if constexpr (requires { Bindings::get_reflection(); }) {
|
||||
return Bindings::get_reflection();
|
||||
} else {
|
||||
static const static_shader_reflection empty_reflection{};
|
||||
return empty_reflection;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 资源访问
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取管线句柄
|
||||
* @return Vulkan 管线句柄
|
||||
*/
|
||||
[[nodiscard]] VkPipeline pipeline() const noexcept { return pipeline_; }
|
||||
|
||||
/**
|
||||
* @brief 获取管线布局句柄
|
||||
* @return Vulkan 管线布局句柄
|
||||
*/
|
||||
[[nodiscard]] VkPipelineLayout pipeline_layout() const noexcept { return pipeline_layout_; }
|
||||
|
||||
/**
|
||||
* @brief 获取描述符集
|
||||
* @param index 描述符集索引
|
||||
* @return Vulkan 描述符集句柄,如果索引超出范围返回 VK_NULL_HANDLE
|
||||
*/
|
||||
[[nodiscard]] VkDescriptorSet descriptor_set(u32 index = 0) const noexcept {
|
||||
return index < descriptor_sets_.size() ? descriptor_sets_[index] : VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取描述符集数量
|
||||
* @return 描述符集数量
|
||||
*/
|
||||
[[nodiscard]] size_type descriptor_set_count() const noexcept {
|
||||
return descriptor_sets_.size();
|
||||
}
|
||||
|
||||
// ============================================================================================
|
||||
// 默认实现
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建管线(默认实现)
|
||||
*
|
||||
* 子类可以重写此方法以自定义管线创建。
|
||||
* 默认实现使用 vertex_spirv() 和 fragment_spirv() 创建标准的图形管线。
|
||||
*/
|
||||
void create_pipeline() override {
|
||||
// 获取 SPIR-V 数据
|
||||
[[maybe_unused]] auto vert_spirv = vertex_spirv();
|
||||
[[maybe_unused]] auto frag_spirv = fragment_spirv();
|
||||
|
||||
// TODO: 使用 pipeline 类创建管线
|
||||
// 这里需要根据实际的 pipeline 类接口实现
|
||||
//
|
||||
// 示例伪代码:
|
||||
// pipeline_create_info info;
|
||||
// info.vertex_spirv = vert_spirv;
|
||||
// info.fragment_spirv = frag_spirv;
|
||||
// info.descriptor_set_layouts = ...;
|
||||
// pipeline_ = create_graphics_pipeline(device(), info);
|
||||
// pipeline_layout_ = ...;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建描述符集(默认实现)
|
||||
*
|
||||
* 子类可以重写此方法以自定义描述符集创建。
|
||||
* 默认实现根据反射信息创建描述符集。
|
||||
*/
|
||||
void create_descriptor_sets() override {
|
||||
// TODO: 使用 descriptor_pool 创建描述符集
|
||||
// 这里需要根据实际的 descriptor_pool 类接口实现
|
||||
//
|
||||
// 示例伪代码:
|
||||
// auto& refl = reflection();
|
||||
// for (const auto& block : refl.uniform_blocks) {
|
||||
// // 创建描述符集布局
|
||||
// // 分配描述符集
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新 Uniform Buffer(默认实现)
|
||||
*
|
||||
* 子类必须实现此方法来更新自己的 uniform 数据。
|
||||
* 默认为空实现。
|
||||
*/
|
||||
void update_uniform_buffer() override {
|
||||
// 子类必须实现此方法
|
||||
// 默认为空实现
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 记录绘制命令(默认实现)
|
||||
*
|
||||
* 子类可以重写此方法以自定义绘制逻辑。
|
||||
* 默认实现绑定管线和描述符集,绘制全屏四边形。
|
||||
*
|
||||
* @param cmd 命令缓冲区
|
||||
*/
|
||||
void draw(command_buffer& cmd) override {
|
||||
// TODO: 实现默认绘制逻辑
|
||||
//
|
||||
// 示例伪代码:
|
||||
// cmd.bind_pipeline(vk::PipelineBindPoint::eGraphics, pipeline_);
|
||||
// cmd.bind_descriptor_sets(pipeline_layout_, 0, descriptor_sets_);
|
||||
// cmd.draw(6, 1, 0, 0); // 全屏四边形(两个三角形)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁资源(重写)
|
||||
*
|
||||
* 清理模板类特有的资源。
|
||||
*/
|
||||
void destroy() override {
|
||||
// 清理 Vulkan 资源
|
||||
// 注意:实际的资源销毁需要 VkDevice
|
||||
// 这里只是重置句柄
|
||||
|
||||
if (pipeline_ != VK_NULL_HANDLE) {
|
||||
// TODO: vkDestroyPipeline(device().get_handle(), pipeline_, nullptr);
|
||||
pipeline_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (pipeline_layout_ != VK_NULL_HANDLE) {
|
||||
// TODO: vkDestroyPipelineLayout(device().get_handle(), pipeline_layout_, nullptr);
|
||||
pipeline_layout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// 描述符集由描述符池管理,通常不需要手动释放
|
||||
descriptor_sets_.clear();
|
||||
|
||||
// 调用基类销毁
|
||||
shader_widget_base::destroy();
|
||||
}
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 成员变量
|
||||
// ============================================================================================
|
||||
|
||||
/// Vulkan 管线句柄
|
||||
VkPipeline pipeline_ = VK_NULL_HANDLE;
|
||||
|
||||
/// Vulkan 管线布局句柄
|
||||
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
|
||||
|
||||
/// 描述符集列表
|
||||
std::vector<VkDescriptorSet> descriptor_sets_;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// 特化模式模板类(可选扩展)
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief Backdrop 模式控件基类模板
|
||||
*
|
||||
* 用于后效处理模式的控件。提供背景纹理采样能力。
|
||||
*
|
||||
* @tparam Bindings 满足 backdrop_bindings concept 的绑定类型
|
||||
*/
|
||||
template<typename Bindings>
|
||||
requires backdrop_bindings<Bindings>
|
||||
class backdrop_widget : public shader_widget<Bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(backdrop_widget, shader_widget<Bindings>)
|
||||
|
||||
/**
|
||||
* @brief 获取渲染模式
|
||||
* @return 始终返回 backdrop
|
||||
*/
|
||||
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
|
||||
return shader_widget_mode::backdrop;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取背景采样器绑定点
|
||||
* @return 绑定点索引
|
||||
*/
|
||||
[[nodiscard]] static constexpr u32 backdrop_binding() noexcept {
|
||||
return Bindings::BACKDROP_BINDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取背景采样器所在描述符集
|
||||
* @return 描述符集索引
|
||||
*/
|
||||
[[nodiscard]] static constexpr u32 backdrop_set() noexcept {
|
||||
return Bindings::BACKDROP_SET;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Procedural 模式控件基类模板
|
||||
*
|
||||
* 用于程序化渲染模式的控件。提供时间和坐标输入。
|
||||
*
|
||||
* @tparam Bindings 满足 procedural_bindings concept 的绑定类型
|
||||
*/
|
||||
template<typename Bindings>
|
||||
requires procedural_bindings<Bindings>
|
||||
class procedural_widget : public shader_widget<Bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(procedural_widget, shader_widget<Bindings>)
|
||||
|
||||
/**
|
||||
* @brief 获取渲染模式
|
||||
* @return 始终返回 procedural
|
||||
*/
|
||||
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
|
||||
return shader_widget_mode::procedural;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取帧数据绑定点
|
||||
* @return 绑定点索引
|
||||
*/
|
||||
[[nodiscard]] static constexpr u32 frame_data_binding() noexcept {
|
||||
return Bindings::FRAME_DATA_BINDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取帧数据所在描述符集
|
||||
* @return 描述符集索引
|
||||
*/
|
||||
[[nodiscard]] static constexpr u32 frame_data_set() noexcept {
|
||||
return Bindings::FRAME_DATA_SET;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mask 模式控件基类模板
|
||||
*
|
||||
* 用于遮罩效果模式的控件。提供遮罩纹理采样能力。
|
||||
*
|
||||
* @tparam Bindings 满足 mask_bindings concept 的绑定类型
|
||||
*/
|
||||
template<typename Bindings>
|
||||
requires mask_bindings<Bindings>
|
||||
class mask_widget : public shader_widget<Bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(mask_widget, shader_widget<Bindings>)
|
||||
|
||||
/**
|
||||
* @brief 获取渲染模式
|
||||
* @return 始终返回 mask
|
||||
*/
|
||||
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
|
||||
return shader_widget_mode::mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取遮罩纹理绑定点
|
||||
* @return 绑定点索引
|
||||
*/
|
||||
[[nodiscard]] static constexpr u32 mask_binding() noexcept {
|
||||
return Bindings::MASK_BINDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取遮罩纹理所在描述符集
|
||||
* @return 描述符集索引
|
||||
*/
|
||||
[[nodiscard]] static constexpr u32 mask_set() noexcept {
|
||||
return Bindings::MASK_SET;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mesh 模式控件基类模板
|
||||
*
|
||||
* 用于自定义网格渲染模式的控件。提供自定义顶点数据支持。
|
||||
*
|
||||
* @tparam Bindings 满足 mesh_bindings concept 的绑定类型
|
||||
*/
|
||||
template<typename Bindings>
|
||||
requires mesh_bindings<Bindings>
|
||||
class mesh_widget : public shader_widget<Bindings> {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(mesh_widget, shader_widget<Bindings>)
|
||||
|
||||
/// 顶点类型别名
|
||||
using vertex_type = typename Bindings::VertexType;
|
||||
|
||||
/**
|
||||
* @brief 获取渲染模式
|
||||
* @return 始终返回 mesh
|
||||
*/
|
||||
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
|
||||
return shader_widget_mode::mesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否有自定义顶点输入
|
||||
* @return 始终返回 true(由 concept 保证)
|
||||
*/
|
||||
[[nodiscard]] static constexpr bool has_custom_vertex_input() noexcept {
|
||||
return Bindings::HAS_CUSTOM_VERTEX_INPUT;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mirai
|
||||
139
src/widget/shader_widget_base.cpp
Normal file
139
src/widget/shader_widget_base.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @file shader_widget_base.cpp
|
||||
* @brief MIRAI 框架 shader_widget_base 类实现
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*/
|
||||
|
||||
#include "shader_widget_base.hpp"
|
||||
#include "vulkan_device.hpp"
|
||||
#include "allocator.hpp"
|
||||
#include "buffer.hpp"
|
||||
#include "descriptor_pool.hpp"
|
||||
#include "assert.hpp"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// 构造函数和析构函数
|
||||
// ================================================================================================
|
||||
|
||||
shader_widget_base::shader_widget_base() = default;
|
||||
|
||||
shader_widget_base::~shader_widget_base() {
|
||||
if (initialized_) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// 生命周期方法
|
||||
// ================================================================================================
|
||||
|
||||
void shader_widget_base::initialize(vulkan_device& device, gpu_allocator& allocator, descriptor_pool& pool) {
|
||||
MIRAI_ASSERT(!initialized_, "shader_widget_base already initialized");
|
||||
|
||||
device_ = &device;
|
||||
allocator_ = &allocator;
|
||||
pool_ = &pool;
|
||||
|
||||
// 子类负责创建管线和描述符集
|
||||
create_pipeline();
|
||||
create_descriptor_sets();
|
||||
|
||||
initialized_ = true;
|
||||
dirty_flags_ = widget_dirty_flags::all;
|
||||
}
|
||||
|
||||
void shader_widget_base::destroy() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 子类应该重写此方法以清理自己的资源
|
||||
// 基类只负责清理引用
|
||||
device_ = nullptr;
|
||||
allocator_ = nullptr;
|
||||
pool_ = nullptr;
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// 渲染方法
|
||||
// ================================================================================================
|
||||
|
||||
void shader_widget_base::update(command_buffer& cmd) {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果 uniform_buffer 标记为脏,则更新
|
||||
if (has_flag(dirty_flags_, widget_dirty_flags::uniform_buffer)) {
|
||||
update_uniform_buffer();
|
||||
clear_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// 状态管理
|
||||
// ================================================================================================
|
||||
|
||||
void shader_widget_base::mark_dirty(widget_dirty_flags flags) {
|
||||
dirty_flags_ |= flags;
|
||||
}
|
||||
|
||||
void shader_widget_base::clear_dirty(widget_dirty_flags flags) {
|
||||
dirty_flags_ = dirty_flags_ & (~flags);
|
||||
}
|
||||
|
||||
bool shader_widget_base::is_dirty(widget_dirty_flags flag) const noexcept {
|
||||
return has_flag(dirty_flags_, flag);
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// 区域设置
|
||||
// ================================================================================================
|
||||
|
||||
void shader_widget_base::set_bounds(f32 x, f32 y, f32 width, f32 height) {
|
||||
// 检查是否有变化
|
||||
if (x_ != x || y_ != y || width_ != width || height_ != height) {
|
||||
x_ = x;
|
||||
y_ = y;
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
// 标记 uniform_buffer 为脏,因为边界变化通常需要更新 uniform 数据
|
||||
mark_dirty(widget_dirty_flags::uniform_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// 辅助方法
|
||||
// ================================================================================================
|
||||
|
||||
std::unique_ptr<buffer> shader_widget_base::create_uniform_buffer(size_type size) {
|
||||
MIRAI_ASSERT(allocator_ != nullptr, "allocator not set");
|
||||
|
||||
// 创建 buffer_create_info
|
||||
buffer_create_info info;
|
||||
info.size = static_cast<u64>(size);
|
||||
info.usage = buffer_usage::uniform;
|
||||
info.mem_usage = memory_usage::cpu_to_gpu;
|
||||
info.persistent_mapped = true;
|
||||
info.debug_name = "shader_widget_uniform";
|
||||
|
||||
// 注意:buffer 类需要 shared_ptr<gpu_allocator>,
|
||||
// 但我们这里只有原始指针。在实际使用中,
|
||||
// 可能需要通过其他方式获取 shared_ptr,
|
||||
// 或者修改 buffer 类接口。
|
||||
//
|
||||
// 这里返回 nullptr 作为占位实现,
|
||||
// 实际的 buffer 创建需要在子类中处理,
|
||||
// 因为子类可能有访问 shared_ptr<gpu_allocator> 的方式。
|
||||
|
||||
// TODO: 实现实际的 buffer 创建
|
||||
// 当前返回空指针,子类需要重写此方法或使用其他方式创建 buffer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mirai
|
||||
309
src/widget/shader_widget_base.hpp
Normal file
309
src/widget/shader_widget_base.hpp
Normal file
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* @file shader_widget_base.hpp
|
||||
* @brief MIRAI 框架 shader_widget 非模板基类
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 shader_widget 的非模板基类,包括:
|
||||
* - 基本的渲染资源管理能力
|
||||
* - 生命周期管理
|
||||
* - 脏标记系统
|
||||
* - 区域设置
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "object.hpp"
|
||||
#include "widget_types.hpp"
|
||||
#include "vulkan_types.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// 前向声明
|
||||
// ================================================================================================
|
||||
|
||||
class vulkan_device;
|
||||
class gpu_allocator;
|
||||
class descriptor_pool;
|
||||
class command_buffer;
|
||||
class buffer;
|
||||
class texture_view;
|
||||
class sampler;
|
||||
|
||||
// ================================================================================================
|
||||
// shader_widget_base 类
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief shader_widget 非模板基类
|
||||
*
|
||||
* 提供所有 shader_widget 的公共实现,避免模板代码膨胀。
|
||||
* 子类通过继承此类获取基本的渲染资源管理能力。
|
||||
*
|
||||
* @note 此类不能直接实例化,必须通过派生类使用
|
||||
*
|
||||
* @example
|
||||
* @code
|
||||
* // shader_widget_base 作为所有着色器控件的基类
|
||||
* // 通常不直接使用,而是通过模板化的 shader_widget<Bindings> 使用
|
||||
* class my_widget : public shader_widget<my_bindings> {
|
||||
* // 自定义实现
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
class shader_widget_base : public object {
|
||||
public:
|
||||
MIRAI_OBJECT_TYPE_INFO(shader_widget_base, object)
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
shader_widget_base();
|
||||
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
~shader_widget_base() override;
|
||||
|
||||
// 禁止拷贝
|
||||
shader_widget_base(const shader_widget_base&) = delete;
|
||||
shader_widget_base& operator=(const shader_widget_base&) = delete;
|
||||
|
||||
// 禁止移动(因为只能通过智能指针管理)
|
||||
shader_widget_base(shader_widget_base&&) = delete;
|
||||
shader_widget_base& operator=(shader_widget_base&&) = delete;
|
||||
|
||||
// ============================================================================================
|
||||
// 生命周期方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 初始化 GPU 资源
|
||||
*
|
||||
* 初始化控件所需的 GPU 资源,包括管线、描述符集等。
|
||||
* 必须在渲染前调用此方法。
|
||||
*
|
||||
* @param device Vulkan 设备
|
||||
* @param allocator GPU 内存分配器
|
||||
* @param pool 描述符池
|
||||
*/
|
||||
virtual void initialize(vulkan_device& device, gpu_allocator& allocator, descriptor_pool& pool);
|
||||
|
||||
/**
|
||||
* @brief 销毁 GPU 资源
|
||||
*
|
||||
* 销毁控件持有的所有 GPU 资源。
|
||||
* 在销毁控件或需要重新初始化时调用。
|
||||
*/
|
||||
virtual void destroy();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 如果已初始化返回 true
|
||||
*/
|
||||
[[nodiscard]] bool is_initialized() const noexcept { return initialized_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 渲染方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 更新脏资源
|
||||
*
|
||||
* 检查并更新所有标记为脏的资源。
|
||||
* 应在 draw() 之前调用。
|
||||
*
|
||||
* @param cmd 命令缓冲区(用于可能的数据传输)
|
||||
*/
|
||||
virtual void update(command_buffer& cmd);
|
||||
|
||||
/**
|
||||
* @brief 记录绘制命令
|
||||
*
|
||||
* 将绘制命令记录到命令缓冲区。
|
||||
* 子类必须实现此方法。
|
||||
*
|
||||
* @param cmd 命令缓冲区
|
||||
*/
|
||||
virtual void draw(command_buffer& cmd) = 0;
|
||||
|
||||
// ============================================================================================
|
||||
// 状态管理
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 标记为脏
|
||||
*
|
||||
* 标记指定的资源需要更新。
|
||||
*
|
||||
* @param flags 脏标记,默认为所有标记
|
||||
*/
|
||||
void mark_dirty(widget_dirty_flags flags = widget_dirty_flags::all);
|
||||
|
||||
/**
|
||||
* @brief 清除脏标记
|
||||
*
|
||||
* 清除指定的脏标记。
|
||||
*
|
||||
* @param flags 要清除的标记
|
||||
*/
|
||||
void clear_dirty(widget_dirty_flags flags);
|
||||
|
||||
/**
|
||||
* @brief 检查是否脏
|
||||
*
|
||||
* 检查是否有指定的脏标记。
|
||||
*
|
||||
* @param flag 要检查的标记
|
||||
* @return 如果有指定标记返回 true
|
||||
*/
|
||||
[[nodiscard]] bool is_dirty(widget_dirty_flags flag) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief 获取当前脏标记
|
||||
* @return 当前的脏标记集合
|
||||
*/
|
||||
[[nodiscard]] widget_dirty_flags dirty_flags() const noexcept { return dirty_flags_; }
|
||||
|
||||
// ============================================================================================
|
||||
// 区域设置
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置控件区域(相对于父控件)
|
||||
*
|
||||
* @param x X 坐标
|
||||
* @param y Y 坐标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
void set_bounds(f32 x, f32 y, f32 width, f32 height);
|
||||
|
||||
/**
|
||||
* @brief 获取 X 坐标
|
||||
* @return X 坐标
|
||||
*/
|
||||
[[nodiscard]] f32 x() const noexcept { return x_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 Y 坐标
|
||||
* @return Y 坐标
|
||||
*/
|
||||
[[nodiscard]] f32 y() const noexcept { return y_; }
|
||||
|
||||
/**
|
||||
* @brief 获取宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
[[nodiscard]] f32 width() const noexcept { return width_; }
|
||||
|
||||
/**
|
||||
* @brief 获取高度
|
||||
* @return 高度
|
||||
*/
|
||||
[[nodiscard]] f32 height() const noexcept { return height_; }
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 子类实现的钩子方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建管线
|
||||
*
|
||||
* 子类必须实现此方法来创建渲染管线。
|
||||
* 在 initialize() 中调用。
|
||||
*/
|
||||
virtual void create_pipeline() = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建描述符集
|
||||
*
|
||||
* 子类必须实现此方法来创建描述符集。
|
||||
* 在 initialize() 中调用。
|
||||
*/
|
||||
virtual void create_descriptor_sets() = 0;
|
||||
|
||||
/**
|
||||
* @brief 更新 Uniform Buffer
|
||||
*
|
||||
* 子类必须实现此方法来更新 Uniform Buffer 数据。
|
||||
* 在 update() 中当 uniform_buffer 标记为脏时调用。
|
||||
*/
|
||||
virtual void update_uniform_buffer() = 0;
|
||||
|
||||
// ============================================================================================
|
||||
// 辅助方法
|
||||
// ============================================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建 Uniform Buffer
|
||||
*
|
||||
* 使用 GPU 分配器创建指定大小的 Uniform Buffer。
|
||||
*
|
||||
* @param size 缓冲区大小(字节)
|
||||
* @return 创建的缓冲区
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<buffer> create_uniform_buffer(size_type size);
|
||||
|
||||
/**
|
||||
* @brief 获取设备引用
|
||||
* @return Vulkan 设备引用
|
||||
*/
|
||||
[[nodiscard]] vulkan_device& device() { return *device_; }
|
||||
|
||||
/**
|
||||
* @brief 获取设备引用(const 版本)
|
||||
* @return Vulkan 设备常量引用
|
||||
*/
|
||||
[[nodiscard]] const vulkan_device& device() const { return *device_; }
|
||||
|
||||
/**
|
||||
* @brief 获取分配器引用
|
||||
* @return GPU 内存分配器引用
|
||||
*/
|
||||
[[nodiscard]] gpu_allocator& allocator() { return *allocator_; }
|
||||
|
||||
/**
|
||||
* @brief 获取描述符池引用
|
||||
* @return 描述符池引用
|
||||
*/
|
||||
[[nodiscard]] descriptor_pool& pool() { return *pool_; }
|
||||
|
||||
protected:
|
||||
// ============================================================================================
|
||||
// 成员变量
|
||||
// ============================================================================================
|
||||
|
||||
/// 设备引用(不持有所有权)
|
||||
vulkan_device* device_ = nullptr;
|
||||
|
||||
/// 分配器引用(不持有所有权)
|
||||
gpu_allocator* allocator_ = nullptr;
|
||||
|
||||
/// 描述符池引用(不持有所有权)
|
||||
descriptor_pool* pool_ = nullptr;
|
||||
|
||||
/// 是否已初始化
|
||||
bool initialized_ = false;
|
||||
|
||||
/// 脏标记
|
||||
widget_dirty_flags dirty_flags_ = widget_dirty_flags::all;
|
||||
|
||||
/// X 坐标
|
||||
f32 x_ = 0.0f;
|
||||
|
||||
/// Y 坐标
|
||||
f32 y_ = 0.0f;
|
||||
|
||||
/// 宽度
|
||||
f32 width_ = 0.0f;
|
||||
|
||||
/// 高度
|
||||
f32 height_ = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace mirai
|
||||
220
src/widget/widget_types.hpp
Normal file
220
src/widget/widget_types.hpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @file widget_types.hpp
|
||||
* @brief MIRAI 框架 Widget 模块基础类型定义
|
||||
* @author MIRAI Team
|
||||
* @version 0.1.0
|
||||
*
|
||||
* 本文件定义了 Widget 模块使用的基础类型,包括:
|
||||
* - 着色器绑定 Concepts
|
||||
* - 渲染模式枚举
|
||||
* - 脏标记枚举
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types/types.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
// ================================================================================================
|
||||
// 前向声明
|
||||
// ================================================================================================
|
||||
|
||||
class vulkan_device;
|
||||
class gpu_allocator;
|
||||
class descriptor_pool;
|
||||
class command_buffer;
|
||||
class texture_view;
|
||||
class sampler;
|
||||
|
||||
// ================================================================================================
|
||||
// 着色器绑定 Concepts
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 基础绑定检查
|
||||
*
|
||||
* 所有绑定类型必须满足此 concept,提供基本的着色器信息
|
||||
*/
|
||||
template<typename T>
|
||||
concept shader_bindings = requires {
|
||||
/// 是否有顶点着色器
|
||||
{ T::HAS_VERTEX_SHADER } -> std::convertible_to<bool>;
|
||||
/// 是否有片段着色器
|
||||
{ T::HAS_FRAGMENT_SHADER } -> std::convertible_to<bool>;
|
||||
/// 获取顶点着色器 SPIR-V 数据
|
||||
{ T::get_vertex_spirv() } -> std::same_as<std::span<const u32>>;
|
||||
/// 获取片段着色器 SPIR-V 数据
|
||||
{ T::get_fragment_spirv() } -> std::same_as<std::span<const u32>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Backdrop 模式绑定检查
|
||||
*
|
||||
* 用于后效处理模式的绑定类型必须满足此 concept
|
||||
*/
|
||||
template<typename T>
|
||||
concept backdrop_bindings = shader_bindings<T> && requires {
|
||||
/// 是否有背景采样器
|
||||
{ T::HAS_BACKDROP_SAMPLER } -> std::convertible_to<bool>;
|
||||
/// 背景纹理绑定点
|
||||
{ T::BACKDROP_BINDING } -> std::convertible_to<u32>;
|
||||
/// 背景纹理所在描述符集
|
||||
{ T::BACKDROP_SET } -> std::convertible_to<u32>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Procedural 模式绑定检查
|
||||
*
|
||||
* 用于程序化渲染模式的绑定类型必须满足此 concept
|
||||
*/
|
||||
template<typename T>
|
||||
concept procedural_bindings = shader_bindings<T> && requires {
|
||||
/// 是否有帧数据
|
||||
{ T::HAS_FRAME_DATA } -> std::convertible_to<bool>;
|
||||
/// 帧数据绑定点
|
||||
{ T::FRAME_DATA_BINDING } -> std::convertible_to<u32>;
|
||||
/// 帧数据所在描述符集
|
||||
{ T::FRAME_DATA_SET } -> std::convertible_to<u32>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mask 模式绑定检查
|
||||
*
|
||||
* 用于遮罩效果模式的绑定类型必须满足此 concept
|
||||
*/
|
||||
template<typename T>
|
||||
concept mask_bindings = shader_bindings<T> && requires {
|
||||
/// 是否有遮罩采样器
|
||||
{ T::HAS_MASK_SAMPLER } -> std::convertible_to<bool>;
|
||||
/// 遮罩纹理绑定点
|
||||
{ T::MASK_BINDING } -> std::convertible_to<u32>;
|
||||
/// 遮罩纹理所在描述符集
|
||||
{ T::MASK_SET } -> std::convertible_to<u32>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mesh 模式绑定检查
|
||||
*
|
||||
* 用于自定义网格渲染模式的绑定类型必须满足此 concept
|
||||
*/
|
||||
template<typename T>
|
||||
concept mesh_bindings = shader_bindings<T> && requires {
|
||||
/// 是否有自定义顶点输入
|
||||
{ T::HAS_CUSTOM_VERTEX_INPUT } -> std::convertible_to<bool>;
|
||||
/// 顶点类型
|
||||
typename T::VertexType;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// 渲染模式枚举
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 着色器控件渲染模式
|
||||
*
|
||||
* 定义了着色器控件支持的四种渲染模式
|
||||
*/
|
||||
enum class shader_widget_mode : u8 {
|
||||
/// 后效处理模式:采样背景纹理进行处理
|
||||
backdrop,
|
||||
/// 程序化渲染模式:基于时间和坐标的程序化生成
|
||||
procedural,
|
||||
/// 遮罩模式:使用遮罩纹理进行裁剪或混合
|
||||
mask,
|
||||
/// 自定义网格模式:使用自定义顶点数据渲染
|
||||
mesh
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// 脏标记枚举
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 控件脏标记
|
||||
*
|
||||
* 用于标记哪些资源需要更新
|
||||
*/
|
||||
enum class widget_dirty_flags : u32 {
|
||||
/// 无脏标记
|
||||
none = 0,
|
||||
/// Uniform Buffer 需要更新
|
||||
uniform_buffer = 1 << 0,
|
||||
/// 描述符集需要更新
|
||||
descriptor_set = 1 << 1,
|
||||
/// 管线需要重建
|
||||
pipeline = 1 << 2,
|
||||
/// 顶点缓冲区需要更新
|
||||
vertex_buffer = 1 << 3,
|
||||
/// 所有标记
|
||||
all = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// 位运算支持
|
||||
// ================================================================================================
|
||||
|
||||
/**
|
||||
* @brief 脏标记按位或运算
|
||||
* @param a 第一个操作数
|
||||
* @param b 第二个操作数
|
||||
* @return 运算结果
|
||||
*/
|
||||
constexpr widget_dirty_flags operator|(widget_dirty_flags a, widget_dirty_flags b) {
|
||||
return static_cast<widget_dirty_flags>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 脏标记按位与运算
|
||||
* @param a 第一个操作数
|
||||
* @param b 第二个操作数
|
||||
* @return 运算结果
|
||||
*/
|
||||
constexpr widget_dirty_flags operator&(widget_dirty_flags a, widget_dirty_flags b) {
|
||||
return static_cast<widget_dirty_flags>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 脏标记按位或赋值运算
|
||||
* @param a 被赋值的操作数
|
||||
* @param b 第二个操作数
|
||||
* @return 赋值后的引用
|
||||
*/
|
||||
constexpr widget_dirty_flags& operator|=(widget_dirty_flags& a, widget_dirty_flags b) {
|
||||
return a = a | b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 脏标记按位与赋值运算
|
||||
* @param a 被赋值的操作数
|
||||
* @param b 第二个操作数
|
||||
* @return 赋值后的引用
|
||||
*/
|
||||
constexpr widget_dirty_flags& operator&=(widget_dirty_flags& a, widget_dirty_flags b) {
|
||||
return a = a & b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 脏标记按位取反运算
|
||||
* @param a 操作数
|
||||
* @return 取反结果
|
||||
*/
|
||||
constexpr widget_dirty_flags operator~(widget_dirty_flags a) {
|
||||
return static_cast<widget_dirty_flags>(~static_cast<u32>(a));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否包含指定标记
|
||||
* @param flags 标记集合
|
||||
* @param flag 要检查的标记
|
||||
* @return 如果包含返回 true
|
||||
*/
|
||||
[[nodiscard]] constexpr bool has_flag(widget_dirty_flags flags, widget_dirty_flags flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
} // namespace mirai
|
||||
@@ -1,10 +1,13 @@
|
||||
# tools/shader_compile/CMakeLists.txt
|
||||
# MIRAI 着色器编译器工具
|
||||
# 用于从 SPIR-V 提取反射信息并生成绑定代码
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
# 查找 Slang SDK (通过 vcpkg)
|
||||
find_package(slang CONFIG REQUIRED)
|
||||
# 查找 spirv-cross 用于反射
|
||||
find_package(spirv_cross_core CONFIG REQUIRED)
|
||||
find_package(spirv_cross_glsl CONFIG REQUIRED)
|
||||
find_package(spirv_cross_reflect CONFIG REQUIRED)
|
||||
|
||||
# 查找 nlohmann_json 用于反射 JSON 输出
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
@@ -17,7 +20,9 @@ add_executable(mirai_shader_compile
|
||||
target_compile_features(mirai_shader_compile PRIVATE cxx_std_20)
|
||||
|
||||
target_link_libraries(mirai_shader_compile PRIVATE
|
||||
slang::slang
|
||||
spirv-cross-core
|
||||
spirv-cross-glsl
|
||||
spirv-cross-reflect
|
||||
nlohmann_json::nlohmann_json
|
||||
)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,12 @@
|
||||
// tools/shader_compile/compiler.hpp
|
||||
// MIRAI 着色器编译器封装
|
||||
// MIRAI 着色器反射工具
|
||||
// 使用 spirv-cross 从 SPIR-V 提取反射信息
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <slang.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -23,6 +22,7 @@ enum class shader_stage {
|
||||
geometry,
|
||||
tessellation_control,
|
||||
tessellation_evaluation,
|
||||
unknown
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -36,157 +36,138 @@ enum class shader_stage {
|
||||
[[nodiscard]] const char* shader_stage_to_string(shader_stage stage);
|
||||
|
||||
/**
|
||||
* @brief 将 Slang 阶段转换为 shader_stage
|
||||
* @brief 从文件扩展名推断着色器阶段
|
||||
*/
|
||||
[[nodiscard]] std::optional<shader_stage> slang_stage_to_shader_stage(SlangStage stage);
|
||||
[[nodiscard]] shader_stage stage_from_extension(const std::string& ext);
|
||||
|
||||
/**
|
||||
* @brief 编译结果
|
||||
* @brief Uniform Buffer 成员信息
|
||||
*/
|
||||
struct compile_result {
|
||||
struct uniform_member {
|
||||
std::string name;
|
||||
std::string type;
|
||||
uint32_t offset{0};
|
||||
uint32_t size{0};
|
||||
uint32_t array_size{0}; // 0 表示非数组
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Uniform Buffer 信息
|
||||
*/
|
||||
struct uniform_buffer_info {
|
||||
std::string name;
|
||||
uint32_t set{0};
|
||||
uint32_t binding{0};
|
||||
uint32_t size{0};
|
||||
std::vector<uniform_member> members;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Push Constant 信息
|
||||
*/
|
||||
struct push_constant_info {
|
||||
std::string name;
|
||||
uint32_t size{0};
|
||||
std::vector<uniform_member> members;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 采样器/纹理信息
|
||||
*/
|
||||
struct sampler_info {
|
||||
std::string name;
|
||||
uint32_t set{0};
|
||||
uint32_t binding{0};
|
||||
std::string dimension; // "1D", "2D", "3D", "Cube"
|
||||
bool is_array{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点输入属性信息
|
||||
*/
|
||||
struct vertex_attribute_info {
|
||||
std::string name;
|
||||
uint32_t location{0};
|
||||
std::string type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器反射数据
|
||||
*/
|
||||
struct shader_reflection {
|
||||
std::string entry_point{"main"};
|
||||
shader_stage stage{shader_stage::unknown};
|
||||
std::vector<uniform_buffer_info> uniform_buffers;
|
||||
std::vector<push_constant_info> push_constants;
|
||||
std::vector<sampler_info> samplers;
|
||||
std::vector<vertex_attribute_info> vertex_inputs;
|
||||
std::vector<vertex_attribute_info> fragment_outputs;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SPIR-V 反射结果
|
||||
*/
|
||||
struct reflection_result {
|
||||
bool success{false};
|
||||
std::string entry_point; // 入口点名称
|
||||
shader_stage stage; // 着色器阶段
|
||||
std::vector<uint32_t> spirv;
|
||||
shader_reflection reflection;
|
||||
std::string reflection_json;
|
||||
std::string error_message;
|
||||
std::vector<std::filesystem::path> dependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 编译选项
|
||||
*/
|
||||
struct compile_options {
|
||||
std::string entry_point{"main"};
|
||||
std::optional<shader_stage> stage;
|
||||
std::vector<std::filesystem::path> include_paths;
|
||||
std::vector<std::pair<std::string, std::string>> defines;
|
||||
bool generate_debug_info{false};
|
||||
bool optimize{true};
|
||||
bool emit_spirv{true};
|
||||
bool emit_reflection{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 入口点信息(包含名称和阶段)
|
||||
*/
|
||||
struct entry_point_info {
|
||||
std::string name;
|
||||
shader_stage stage;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器编译器
|
||||
* @brief SPIR-V 反射器
|
||||
*
|
||||
* 封装 Slang SDK,用于将 .slang 文件编译为 SPIR-V
|
||||
* 使用 spirv-cross 从 SPIR-V 字节码提取反射信息
|
||||
*/
|
||||
class shader_compiler {
|
||||
class spirv_reflector {
|
||||
public:
|
||||
shader_compiler();
|
||||
~shader_compiler();
|
||||
spirv_reflector() = default;
|
||||
~spirv_reflector() = default;
|
||||
|
||||
// 禁止拷贝
|
||||
shader_compiler(const shader_compiler&) = delete;
|
||||
shader_compiler& operator=(const shader_compiler&) = delete;
|
||||
spirv_reflector(const spirv_reflector&) = delete;
|
||||
spirv_reflector& operator=(const spirv_reflector&) = delete;
|
||||
|
||||
// 允许移动
|
||||
shader_compiler(shader_compiler&&) noexcept;
|
||||
shader_compiler& operator=(shader_compiler&&) noexcept;
|
||||
spirv_reflector(spirv_reflector&&) noexcept = default;
|
||||
spirv_reflector& operator=(spirv_reflector&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief 编译着色器文件
|
||||
* @param path 输入文件路径
|
||||
* @param options 编译选项
|
||||
* @return 编译结果
|
||||
* @brief 从 SPIR-V 文件提取反射信息
|
||||
* @param path SPIR-V 文件路径
|
||||
* @return 反射结果
|
||||
*/
|
||||
[[nodiscard]] compile_result compile_file(
|
||||
const std::filesystem::path& path,
|
||||
const compile_options& options
|
||||
[[nodiscard]] reflection_result reflect_file(const std::filesystem::path& path);
|
||||
|
||||
/**
|
||||
* @brief 从 SPIR-V 字节码提取反射信息
|
||||
* @param spirv SPIR-V 字节码
|
||||
* @param filename 文件名(用于错误报告和阶段推断)
|
||||
* @return 反射结果
|
||||
*/
|
||||
[[nodiscard]] reflection_result reflect_spirv(
|
||||
const std::vector<uint32_t>& spirv,
|
||||
const std::string& filename = ""
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 编译着色器源码
|
||||
* @param source 着色器源码
|
||||
* @param filename 虚拟文件名(用于错误报告)
|
||||
* @param options 编译选项
|
||||
* @return 编译结果
|
||||
* @brief 从目录中的所有 SPIR-V 文件提取反射信息
|
||||
* @param dir 目录路径
|
||||
* @return 反射结果列表
|
||||
*/
|
||||
[[nodiscard]] compile_result compile_source(
|
||||
const std::string& source,
|
||||
const std::string& filename,
|
||||
const compile_options& options
|
||||
[[nodiscard]] std::vector<reflection_result> reflect_directory(
|
||||
const std::filesystem::path& dir
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 仅生成反射数据
|
||||
* @param path 输入文件路径
|
||||
* @param options 编译选项
|
||||
* @return 反射 JSON 字符串,失败返回 nullopt
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::string> generate_reflection(
|
||||
const std::filesystem::path& path,
|
||||
const compile_options& options
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 检查着色器文件是否存在入口函数
|
||||
* @param path 输入文件路径
|
||||
* @param options 编译选项
|
||||
* @return std::nullopt 表示无法检查,
|
||||
* true 表示存在入口函数,
|
||||
* false 表示不存在入口函数
|
||||
*/
|
||||
[[nodiscard]] std::optional<bool> has_entry_point(
|
||||
const std::filesystem::path& path,
|
||||
const compile_options& options
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 获取模块中的所有入口函数
|
||||
* @param path 输入文件路径
|
||||
* @param options 编译选项
|
||||
* @return 入口函数名称列表
|
||||
*/
|
||||
[[nodiscard]] std::vector<std::string> get_entry_points(
|
||||
const std::filesystem::path& path,
|
||||
const compile_options& options
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 获取模块中的所有入口函数及其阶段
|
||||
* @param path 输入文件路径
|
||||
* @param options 编译选项
|
||||
* @return 入口点信息列表
|
||||
*/
|
||||
[[nodiscard]] std::vector<entry_point_info> get_entry_points_with_stages(
|
||||
const std::filesystem::path& path,
|
||||
const compile_options& options
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 编译着色器文件(自动检测所有入口点)
|
||||
* @param path 输入文件路径
|
||||
* @param options 编译选项
|
||||
* @return 编译结果列表(每个入口点一个结果)
|
||||
*/
|
||||
[[nodiscard]] std::vector<compile_result> compile_file_all_entries(
|
||||
const std::filesystem::path& path,
|
||||
const compile_options& options
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 检查编译器是否可用
|
||||
*/
|
||||
[[nodiscard]] bool is_available() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief 获取 Slang 版本信息
|
||||
*/
|
||||
[[nodiscard]] std::string get_version() const;
|
||||
|
||||
private:
|
||||
struct impl;
|
||||
std::unique_ptr<impl> impl_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 将反射数据转换为 JSON 字符串
|
||||
*/
|
||||
[[nodiscard]] std::string reflection_to_json(const shader_reflection& reflection);
|
||||
|
||||
/**
|
||||
* @brief 将多个反射数据合并为一个 JSON 字符串
|
||||
*/
|
||||
[[nodiscard]] std::string reflections_to_json(const std::vector<shader_reflection>& reflections);
|
||||
|
||||
} // namespace mirai::tools
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// tools/shader_compile/main.cpp
|
||||
// MIRAI 着色器编译器命令行入口
|
||||
// 自动检测所有 [shader("xxx")] 入口点并编译
|
||||
// MIRAI 着色器反射工具命令行入口
|
||||
// 从 SPIR-V 文件提取反射信息并生成绑定代码
|
||||
|
||||
#include "compiler.hpp"
|
||||
|
||||
@@ -13,47 +13,46 @@ namespace {
|
||||
|
||||
void print_usage(const char* program_name) {
|
||||
std::cout << R"(
|
||||
MIRAI Shader Compiler - Auto Mode
|
||||
MIRAI Shader Reflection Tool
|
||||
|
||||
Usage: )" << program_name << R"( [options] <input.slang>
|
||||
Usage: )" << program_name << R"( [options]
|
||||
|
||||
Options:
|
||||
-o, --output <dir> Output directory for compiled shaders
|
||||
-I, --include <path> Add include search path
|
||||
-D, --define <MACRO> Define preprocessor macro (e.g., -DDEBUG or -DVALUE=1)
|
||||
--prefix <name> Prefix for output file names (default: shader name)
|
||||
-g Generate debug info
|
||||
-O0 Disable optimization
|
||||
--list Only list entry points, don't compile
|
||||
--dir <path> Directory containing SPIR-V files
|
||||
--file <path> Single SPIR-V file to process
|
||||
--output <path> Output file path (header or JSON)
|
||||
--name <name> Shader group name (for header generation)
|
||||
--json Output JSON reflection data
|
||||
--header Output C++ header with bindings (default)
|
||||
-h, --help Show this help
|
||||
-v, --version Show version info
|
||||
|
||||
Examples:
|
||||
)" << program_name << R"( shader.slang -o ./compiled_shaders
|
||||
)" << program_name << R"( shader.slang -o ./shaders --prefix myshader
|
||||
)" << program_name << R"( shader.slang --list
|
||||
)" << program_name << R"( --dir ./shaders --output bindings.hpp --name my_shader
|
||||
)" << program_name << R"( --file shader.vert.spv --output shader.json --json
|
||||
)" << program_name << R"( --dir ./compiled --output reflection.json --json
|
||||
|
||||
Output Format:
|
||||
Automatically compiles all entry points found via [shader("xxx")] attributes.
|
||||
Output files: <prefix>.<entry_point>.<stage>.spv
|
||||
<prefix>.<entry_point>.<stage>.reflect.json
|
||||
Output Formats:
|
||||
Header (.hpp): C++ header with embedded SPIR-V and reflection data
|
||||
JSON (.json): Structured reflection data for external tools
|
||||
|
||||
)";
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
std::cout << "MIRAI Shader Compiler v2.0.0 (Auto Mode)\n";
|
||||
std::cout << "Based on Slang Shader Language\n";
|
||||
std::cout << "MIRAI Shader Reflection Tool v1.0.0\n";
|
||||
std::cout << "Based on SPIRV-Cross\n";
|
||||
}
|
||||
|
||||
struct command_line_args {
|
||||
std::filesystem::path input_path;
|
||||
std::filesystem::path output_dir;
|
||||
std::string prefix;
|
||||
mirai::tools::compile_options options;
|
||||
std::filesystem::path input_dir;
|
||||
std::filesystem::path input_file;
|
||||
std::filesystem::path output_path;
|
||||
std::string name;
|
||||
bool output_json = false;
|
||||
bool output_header = true;
|
||||
bool show_help = false;
|
||||
bool show_version = false;
|
||||
bool list_only = false;
|
||||
};
|
||||
|
||||
bool parse_args(int argc, char* argv[], command_line_args& args) {
|
||||
@@ -68,67 +67,41 @@ bool parse_args(int argc, char* argv[], command_line_args& args) {
|
||||
args.show_version = true;
|
||||
return true;
|
||||
}
|
||||
if (arg == "-o" || arg == "--output") {
|
||||
if (arg == "--dir") {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: -o requires an argument\n";
|
||||
std::cerr << "Error: --dir requires an argument\n";
|
||||
return false;
|
||||
}
|
||||
args.output_dir = argv[i];
|
||||
args.input_dir = argv[i];
|
||||
}
|
||||
else if (arg == "-I" || arg == "--include") {
|
||||
else if (arg == "--file") {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: -I requires an argument\n";
|
||||
std::cerr << "Error: --file requires an argument\n";
|
||||
return false;
|
||||
}
|
||||
args.options.include_paths.emplace_back(argv[i]);
|
||||
args.input_file = argv[i];
|
||||
}
|
||||
else if (arg.starts_with("-I")) {
|
||||
args.options.include_paths.emplace_back(arg.substr(2));
|
||||
}
|
||||
else if (arg == "-D" || arg == "--define") {
|
||||
else if (arg == "--output" || arg == "-o") {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: -D requires an argument\n";
|
||||
std::cerr << "Error: --output requires an argument\n";
|
||||
return false;
|
||||
}
|
||||
std::string def = argv[i];
|
||||
auto eq_pos = def.find('=');
|
||||
if (eq_pos != std::string::npos) {
|
||||
args.options.defines.emplace_back(def.substr(0, eq_pos), def.substr(eq_pos + 1));
|
||||
} else {
|
||||
args.options.defines.emplace_back(def, "1");
|
||||
}
|
||||
args.output_path = argv[i];
|
||||
}
|
||||
else if (arg.starts_with("-D")) {
|
||||
std::string def = arg.substr(2);
|
||||
auto eq_pos = def.find('=');
|
||||
if (eq_pos != std::string::npos) {
|
||||
args.options.defines.emplace_back(def.substr(0, eq_pos), def.substr(eq_pos + 1));
|
||||
} else {
|
||||
args.options.defines.emplace_back(def, "1");
|
||||
}
|
||||
}
|
||||
else if (arg == "--prefix") {
|
||||
else if (arg == "--name") {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: --prefix requires an argument\n";
|
||||
std::cerr << "Error: --name requires an argument\n";
|
||||
return false;
|
||||
}
|
||||
args.prefix = argv[i];
|
||||
args.name = argv[i];
|
||||
}
|
||||
else if (arg == "--list") {
|
||||
args.list_only = true;
|
||||
else if (arg == "--json") {
|
||||
args.output_json = true;
|
||||
args.output_header = false;
|
||||
}
|
||||
else if (arg == "-g") {
|
||||
args.options.generate_debug_info = true;
|
||||
}
|
||||
else if (arg == "-O0") {
|
||||
args.options.optimize = false;
|
||||
}
|
||||
else if (!arg.starts_with("-")) {
|
||||
if (!args.input_path.empty()) {
|
||||
std::cerr << "Error: Multiple input files specified\n";
|
||||
return false;
|
||||
}
|
||||
args.input_path = arg;
|
||||
else if (arg == "--header") {
|
||||
args.output_header = true;
|
||||
args.output_json = false;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error: Unknown option: " << arg << "\n";
|
||||
@@ -139,29 +112,6 @@ bool parse_args(int argc, char* argv[], command_line_args& args) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string stage_to_suffix(mirai::tools::shader_stage stage) {
|
||||
switch (stage) {
|
||||
case mirai::tools::shader_stage::vertex: return "vert";
|
||||
case mirai::tools::shader_stage::fragment: return "frag";
|
||||
case mirai::tools::shader_stage::compute: return "comp";
|
||||
case mirai::tools::shader_stage::geometry: return "geom";
|
||||
case mirai::tools::shader_stage::tessellation_control: return "tesc";
|
||||
case mirai::tools::shader_stage::tessellation_evaluation: return "tese";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
bool write_spirv(const std::filesystem::path& path, const std::vector<uint32_t>& spirv) {
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Failed to open output file: " << path << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(reinterpret_cast<const char*>(spirv.data()), spirv.size() * sizeof(uint32_t));
|
||||
return file.good();
|
||||
}
|
||||
|
||||
bool write_text(const std::filesystem::path& path, const std::string& text) {
|
||||
std::ofstream file(path);
|
||||
if (!file) {
|
||||
@@ -173,6 +123,112 @@ bool write_text(const std::filesystem::path& path, const std::string& text) {
|
||||
return file.good();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_binary_file(const std::filesystem::path& path) {
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<uint8_t> data(size);
|
||||
file.read(reinterpret_cast<char*>(data.data()), size);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string generate_header(
|
||||
const std::string& name,
|
||||
const std::vector<mirai::tools::reflection_result>& results
|
||||
) {
|
||||
std::ostringstream ss;
|
||||
|
||||
// Header guard
|
||||
std::string guard_name = name;
|
||||
for (auto& c : guard_name) {
|
||||
c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
ss << "// Auto-generated shader bindings for: " << name << "\n";
|
||||
ss << "// DO NOT EDIT - Generated by mirai_shader_compile\n\n";
|
||||
ss << "#pragma once\n\n";
|
||||
ss << "#include <array>\n";
|
||||
ss << "#include <cstdint>\n";
|
||||
ss << "#include <string_view>\n\n";
|
||||
ss << "namespace mirai::shaders {\n\n";
|
||||
ss << "namespace " << name << " {\n\n";
|
||||
|
||||
// Generate SPIR-V data for each shader
|
||||
for (const auto& result : results) {
|
||||
if (!result.success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& reflection = result.reflection;
|
||||
std::string stage_name = mirai::tools::shader_stage_to_string(reflection.stage);
|
||||
|
||||
ss << "// " << stage_name << " shader\n";
|
||||
ss << "namespace " << stage_name << " {\n\n";
|
||||
|
||||
ss << "constexpr std::string_view entry_point = \"" << reflection.entry_point << "\";\n\n";
|
||||
|
||||
// Uniform buffer info
|
||||
if (!reflection.uniform_buffers.empty()) {
|
||||
ss << "// Uniform Buffers\n";
|
||||
for (const auto& ub : reflection.uniform_buffers) {
|
||||
ss << "struct " << ub.name << " {\n";
|
||||
ss << " static constexpr uint32_t set = " << ub.set << ";\n";
|
||||
ss << " static constexpr uint32_t binding = " << ub.binding << ";\n";
|
||||
ss << " static constexpr uint32_t size = " << ub.size << ";\n";
|
||||
ss << "};\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Push constant info
|
||||
if (!reflection.push_constants.empty()) {
|
||||
ss << "// Push Constants\n";
|
||||
for (const auto& pc : reflection.push_constants) {
|
||||
ss << "struct " << pc.name << " {\n";
|
||||
ss << " static constexpr uint32_t size = " << pc.size << ";\n";
|
||||
ss << "};\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Sampler info
|
||||
if (!reflection.samplers.empty()) {
|
||||
ss << "// Samplers\n";
|
||||
for (const auto& sampler : reflection.samplers) {
|
||||
ss << "struct " << sampler.name << "_info {\n";
|
||||
ss << " static constexpr uint32_t set = " << sampler.set << ";\n";
|
||||
ss << " static constexpr uint32_t binding = " << sampler.binding << ";\n";
|
||||
ss << " static constexpr std::string_view dimension = \"" << sampler.dimension << "\";\n";
|
||||
ss << "};\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
ss << "} // namespace " << stage_name << "\n\n";
|
||||
}
|
||||
|
||||
// Reflection JSON
|
||||
ss << "// Combined reflection data\n";
|
||||
ss << "constexpr std::string_view reflection_json = R\"JSON(\n";
|
||||
|
||||
std::vector<mirai::tools::shader_reflection> reflections;
|
||||
for (const auto& result : results) {
|
||||
if (result.success) {
|
||||
reflections.push_back(result.reflection);
|
||||
}
|
||||
}
|
||||
ss << mirai::tools::reflections_to_json(reflections);
|
||||
ss << "\n)JSON\";\n\n";
|
||||
|
||||
ss << "} // namespace " << name << "\n\n";
|
||||
ss << "} // namespace mirai::shaders\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
@@ -192,87 +248,80 @@ int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (args.input_path.empty()) {
|
||||
std::cerr << "Error: No input file specified\n";
|
||||
if (args.input_dir.empty() && args.input_file.empty()) {
|
||||
std::cerr << "Error: No input specified. Use --dir or --file.\n";
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 创建编译器
|
||||
mirai::tools::shader_compiler compiler;
|
||||
if (!compiler.is_available()) {
|
||||
std::cerr << "Error: Shader compiler is not available\n";
|
||||
if (args.output_path.empty()) {
|
||||
std::cerr << "Error: No output path specified. Use --output.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 获取所有入口点及其阶段
|
||||
auto entry_points = compiler.get_entry_points_with_stages(args.input_path, args.options);
|
||||
// Create reflector
|
||||
mirai::tools::spirv_reflector reflector;
|
||||
std::vector<mirai::tools::reflection_result> results;
|
||||
|
||||
if (entry_points.empty()) {
|
||||
std::cout << "Warning: No entry points found in " << args.input_path << "\n";
|
||||
std::cout << " (This may be a module file with no [shader(...)] attributes)\n";
|
||||
// Process input
|
||||
if (!args.input_dir.empty()) {
|
||||
results = reflector.reflect_directory(args.input_dir);
|
||||
} else if (!args.input_file.empty()) {
|
||||
auto result = reflector.reflect_file(args.input_file);
|
||||
results.push_back(result);
|
||||
}
|
||||
|
||||
if (results.empty()) {
|
||||
std::cout << "Warning: No SPIR-V files found to process.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 列出入口点
|
||||
std::cout << "Found " << entry_points.size() << " entry point(s) in " << args.input_path.filename() << ":\n";
|
||||
for (const auto& ep : entry_points) {
|
||||
std::cout << " - " << ep.name << " [" << mirai::tools::shader_stage_to_string(ep.stage) << "]\n";
|
||||
}
|
||||
|
||||
// 如果只是列出,不编译
|
||||
if (args.list_only) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 确保输出目录存在
|
||||
if (args.output_dir.empty()) {
|
||||
args.output_dir = ".";
|
||||
}
|
||||
if (!std::filesystem::exists(args.output_dir)) {
|
||||
std::filesystem::create_directories(args.output_dir);
|
||||
}
|
||||
|
||||
// 确定前缀
|
||||
if (args.prefix.empty()) {
|
||||
args.prefix = args.input_path.stem().string();
|
||||
}
|
||||
|
||||
// 编译所有入口点
|
||||
args.options.emit_spirv = true;
|
||||
args.options.emit_reflection = true;
|
||||
|
||||
auto results = compiler.compile_file_all_entries(args.input_path, args.options);
|
||||
|
||||
int success_count = 0;
|
||||
int fail_count = 0;
|
||||
|
||||
// Check for errors
|
||||
int error_count = 0;
|
||||
for (const auto& result : results) {
|
||||
if (!result.success) {
|
||||
std::cerr << "Error compiling " << result.entry_point << ": " << result.error_message << "\n";
|
||||
fail_count++;
|
||||
continue;
|
||||
std::cerr << "Error: " << result.error_message << "\n";
|
||||
error_count++;
|
||||
}
|
||||
|
||||
std::string suffix = stage_to_suffix(result.stage);
|
||||
std::string base_name = args.prefix + "." + result.entry_point + "." + suffix;
|
||||
|
||||
// 写入 SPIR-V
|
||||
std::filesystem::path spv_path = args.output_dir / (base_name + ".spv");
|
||||
if (write_spirv(spv_path, result.spirv)) {
|
||||
std::cout << "Generated: " << spv_path << " (" << result.spirv.size() * 4 << " bytes)\n";
|
||||
}
|
||||
|
||||
// 写入反射 JSON
|
||||
std::filesystem::path reflect_path = args.output_dir / (base_name + ".reflect.json");
|
||||
if (write_text(reflect_path, result.reflection_json)) {
|
||||
std::cout << "Generated: " << reflect_path << "\n";
|
||||
}
|
||||
|
||||
success_count++;
|
||||
}
|
||||
|
||||
std::cout << "\nSummary: " << success_count << " succeeded, " << fail_count << " failed\n";
|
||||
// Generate output
|
||||
std::string output;
|
||||
|
||||
if (args.output_json) {
|
||||
std::vector<mirai::tools::shader_reflection> reflections;
|
||||
for (const auto& result : results) {
|
||||
if (result.success) {
|
||||
reflections.push_back(result.reflection);
|
||||
}
|
||||
}
|
||||
output = mirai::tools::reflections_to_json(reflections);
|
||||
} else {
|
||||
// Header output
|
||||
if (args.name.empty()) {
|
||||
// Try to derive name from output path
|
||||
args.name = args.output_path.stem().string();
|
||||
// Remove _bindings suffix if present
|
||||
if (args.name.ends_with("_bindings")) {
|
||||
args.name = args.name.substr(0, args.name.length() - 9);
|
||||
}
|
||||
}
|
||||
output = generate_header(args.name, results);
|
||||
}
|
||||
|
||||
return fail_count > 0 ? 1 : 0;
|
||||
// Write output
|
||||
if (!write_text(args.output_path, output)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Generated: " << args.output_path << "\n";
|
||||
|
||||
int success_count = static_cast<int>(results.size()) - error_count;
|
||||
std::cout << "Processed " << success_count << " shader(s)";
|
||||
if (error_count > 0) {
|
||||
std::cout << " (" << error_count << " error(s))";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
return error_count > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"version>=": "3.2.28"
|
||||
},
|
||||
{
|
||||
"name": "shader-slang",
|
||||
"version>=": "2025.22.1"
|
||||
"name": "spirv-cross",
|
||||
"version>=": "1.3.296.0"
|
||||
},
|
||||
{
|
||||
"name": "nlohmann-json",
|
||||
|
||||
Reference in New Issue
Block a user