绘制圆角矩形

This commit is contained in:
Nanako 2024-11-05 23:55:57 +08:00
parent 64d7894981
commit 4ccc968818
16 changed files with 159 additions and 102 deletions

View File

@ -26,4 +26,3 @@ set(BUILD_EXAMPLE FALSE CACHE BOOL "Build example")
if (BUILD_EXAMPLE)
add_subdirectory(example)
endif ()

View File

@ -21,13 +21,18 @@ function(compile_slang_shaders input_file stage entry_point)
#
set(output_files)
set(extra_args ${ARGN})
# debug
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND extra_args "-g")
endif()
#
if(GL_BACKEND)
set(output_file ${output_dir}/${filename}_${entry_point}.glsl)
add_custom_command(
OUTPUT ${output_file}
COMMAND slangc -target glsl -entry ${entry_point} -stage ${stage} ${ARGN} -o ${output_file} ${input_file}
COMMAND slangc -target glsl -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file}
DEPENDS ${input_file}
COMMENT "Compiling ${input_file} to GLSL (${stage}, ${entry_point})"
)
@ -38,7 +43,7 @@ function(compile_slang_shaders input_file stage entry_point)
set(output_file ${output_dir}/${filename}_${entry_point}.dxbc)
add_custom_command(
OUTPUT ${output_file}
COMMAND slangc -target dxbc -profile ${profile} -entry ${entry_point} -stage ${stage} ${ARGN} -o ${output_file} ${input_file}
COMMAND slangc -target dxbc -profile ${profile} -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file}
DEPENDS ${input_file}
COMMENT "Compiling ${input_file} to DXBC (${stage}, ${entry_point}) with profile ${profile}"
)
@ -49,7 +54,7 @@ function(compile_slang_shaders input_file stage entry_point)
set(output_file ${output_dir}/${filename}_${entry_point}.spirv)
add_custom_command(
OUTPUT ${output_file}
COMMAND slangc -target spirv -entry ${entry_point} -stage ${stage} ${ARGN} -o ${output_file} ${input_file}
COMMAND slangc -target spirv -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file}
DEPENDS ${input_file}
COMMENT "Compiling ${input_file} to SPIR-V (${stage}, ${entry_point})"
)
@ -60,7 +65,7 @@ function(compile_slang_shaders input_file stage entry_point)
set(output_file ${output_dir}/${filename}_${entry_point}.metal)
add_custom_command(
OUTPUT ${output_file}
COMMAND slangc -target msl -entry ${entry_point} -stage ${stage} ${ARGN} -o ${output_file} ${input_file}
COMMAND slangc -target msl -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file}
DEPENDS ${input_file}
COMMENT "Compiling ${input_file} to Metal Shading Language (${stage}, ${entry_point})"
)

View File

@ -5,9 +5,3 @@ set(SRC_FILES "")
retrieve_files(src SRC_FILES)
add_executable(${PROJECT_NAME} ${SRC_FILES})
target_link_libraries(${PROJECT_NAME} PRIVATE aorii_renderer)
# ${SHADER_OUTPUT_DIR}${CMAKE_CURRENT_BINARY_DIR}/resource
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${SHADER_OUTPUT_DIR} $<TARGET_FILE_DIR:${PROJECT_NAME}>/resource/shaders
COMMENT "Copying shader files to resource directory after build")

View File

@ -62,3 +62,18 @@ compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/default_shader.slang"
compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/default_shader.slang" "pixel" "pixel_main")
compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/default_shader.slang" "pixel" "rounded_rect_pixel_main" -D IS_ROUNDED_RECT)
add_compile_shaders_target("compile_aorii_shaders" ${PROJECT_NAME})
if (BUILD_EXAMPLE)
# ${SHADER_OUTPUT_DIR}${CMAKE_CURRENT_BINARY_DIR}/example/resource/shaders
set(SOURCE_DIR ${SHADER_OUTPUT_DIR})
set(DEST_DIR ${CMAKE_BINARY_DIR}/example/resource/shaders)
# , compile_aorii_shaders, ${SOURCE_DIR}${DEST_DIR}
add_custom_command(
TARGET compile_aorii_shaders
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_directory ${SOURCE_DIR} ${DEST_DIR}
COMMENT "Copying shaders to ${DEST_DIR}"
)
endif ()

View File

@ -6,11 +6,12 @@
#include "dx_renderer.h"
#include "core/renderer/renderer.h"
dx_pipeline::dx_pipeline(): vertex_buffer(buffer_type::vertex, 4), triangle_buffer(buffer_type::index, 2), constant_buffer(buffer_type::constant, 1) {
}
dx_pipeline::~dx_pipeline() {
if (vertex_shader) { vertex_shader->Release(); }
if (pixel_shader) { pixel_shader->Release(); }
delete vertex_buffer;
delete constant_buffer;
}
void dx_pipeline::use() {
@ -26,9 +27,9 @@ void dx_pipeline::draw() {
// 设置顶点缓冲区
constexpr UINT stride = sizeof(aorii_vertex_type);
constexpr UINT offset = 0;
auto* v_buffer = static_cast<ID3D11Buffer*>(vertex_buffer->get_native_handle());
auto* i_buffer = static_cast<ID3D11Buffer*>(triangle_buffer->get_native_handle());
auto* c_buffer = static_cast<ID3D11Buffer*>(constant_buffer->get_native_handle());
auto* v_buffer = static_cast<ID3D11Buffer*>(vertex_buffer.get_native_handle());
auto* i_buffer = static_cast<ID3D11Buffer*>(triangle_buffer.get_native_handle());
auto* c_buffer = static_cast<ID3D11Buffer*>(constant_buffer.get_native_handle());
d3d_context->IASetVertexBuffers(0, 1, &v_buffer, &stride, &offset);
d3d_context->IASetIndexBuffer(i_buffer, DXGI_FORMAT_R32_UINT, 0);
d3d_context->VSSetConstantBuffers(0, 1, &c_buffer);
@ -38,7 +39,7 @@ void dx_pipeline::draw() {
d3d_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
// 绘制矩形
d3d_context->Draw(vertex_buffer->get_size(), 0);
d3d_context->Draw(vertex_buffer.get_size(), 0);
}
void dx_pipeline::load_pixel_shader(const char* shader_name) {
@ -66,30 +67,24 @@ void dx_pipeline::load_vertex_shader() {
spdlog::critical("无法创建输入布局: {0}", hr);
return;
}
vertex_buffer = new dx_buffer<aorii_vertex_type>(buffer_type::vertex, 4);
triangle_buffer = new dx_buffer<aorii_triangle_type>(buffer_type::index, 2);
}
void dx_pipeline::build_constant_buffer() {
constant_buffer = new dx_buffer<aorii_constant_buffer_type>(buffer_type::constant, 1);
}
void dx_pipeline::set_triangle(const std::span<const aorii_vertex_type>& in_vertexes,
const std::span<const aorii_triangle_type>& in_triangles) {
if (vertex_buffer->get_size() < in_vertexes.size()) { vertex_buffer->resize(in_vertexes.size()); }
aorii_vertex_type* v_buffer = vertex_buffer->lock();
if (vertex_buffer.get_size() < in_vertexes.size()) { vertex_buffer.resize(static_cast<int>(in_vertexes.size())); }
aorii_vertex_type* v_buffer = vertex_buffer.lock();
std::ranges::copy(in_vertexes, v_buffer);
vertex_buffer->unlock();
vertex_buffer.unlock();
if (triangle_buffer->get_size() < in_triangles.size()) { triangle_buffer->resize(in_triangles.size()); }
aorii_triangle_type* i_buffer = triangle_buffer->lock();
if (triangle_buffer.get_size() < in_triangles.size()) { triangle_buffer.resize(static_cast<int>(in_triangles.size())); }
aorii_triangle_type* i_buffer = triangle_buffer.lock();
std::ranges::copy(in_triangles, i_buffer);
triangle_buffer->unlock();
triangle_buffer.unlock();
}
aorii_constant_buffer_type* dx_pipeline::lock_constant_buffer() { return constant_buffer->lock(); }
aorii_constant_buffer_type* dx_pipeline::lock_constant_buffer() { return constant_buffer.lock(); }
void dx_pipeline::unlock_constant_buffer() { constant_buffer->unlock(); }
void dx_pipeline::unlock_constant_buffer() { constant_buffer.unlock(); }
std::vector<char> dx_pipeline::load_shader(const std::string& shader_name) {
auto file_pathname = aorii::get_shader_path(shader_name).generic_string() + ".dxbc";

View File

@ -10,6 +10,7 @@
class dx_pipeline : public pipeline {
public:
dx_pipeline();
~dx_pipeline() override;
void use() override;
void draw() override;
@ -17,8 +18,6 @@ public:
void load_pixel_shader(const char* shader_name);
void load_vertex_shader();
void build_constant_buffer();
void set_triangle(const std::span<const aorii_vertex_type>& in_vertexes, const std::span<const aorii_triangle_type>& in_triangles) override;
aorii_constant_buffer_type* lock_constant_buffer() override;
@ -31,8 +30,8 @@ private:
ID3D11InputLayout* input_layout = nullptr;
dx_buffer<aorii_vertex_type>* vertex_buffer = nullptr;
dx_buffer<aorii_triangle_type>* triangle_buffer = nullptr;
dx_buffer<aorii_constant_buffer_type>* constant_buffer = nullptr;
dx_buffer<aorii_vertex_type> vertex_buffer;
dx_buffer<aorii_triangle_type> triangle_buffer;
dx_buffer<aorii_constant_buffer_type> constant_buffer;
int vertex_count = 0;
};

View File

@ -12,10 +12,43 @@ bool dx_renderer::init() {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
if (!create_device()) return false;
D3D11_BLEND_DESC blend_desc = {};
blend_desc.RenderTarget[0].BlendEnable = TRUE;
blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
auto hr = device->CreateBlendState(&blend_desc, &blend_state);
if (FAILED(hr)) {
spdlog::critical("无法创建混合状态, 错误: {0}", hr);
return false;
}
default_pipeline = new dx_pipeline();
rounded_rect_pipeline = new dx_pipeline();
default_pipeline->load_pixel_shader(AORII_DEFAULT_PIXEL_SHADER_NAME);
default_pipeline->load_vertex_shader();
rounded_rect_pipeline->load_pixel_shader(AORII_DEFAULT_ROUNDED_RECT_PIXEL_SHADER_NAME);
rounded_rect_pipeline->load_vertex_shader();
return true;
}
void dx_renderer::destroy() { glfwTerminate(); }
void dx_renderer::destroy() {
if (blend_state) blend_state->Release();
delete default_pipeline;
delete rounded_rect_pipeline;
default_pipeline = nullptr;
rounded_rect_pipeline = nullptr;
glfwTerminate();
}
bool dx_renderer::render() {
const auto& all_window = aorii::get_all_window();

View File

@ -3,6 +3,7 @@
#include "core/renderer/renderer.h"
class dx_pipeline;
class dx_window;
class dx_renderer : public renderer {
@ -14,8 +15,12 @@ public:
renderer_texture* create_texture(const Eigen::Vector2i& size) override;
void destroy_texture(renderer_texture* texture) override;
ID3D11Device* get_d3d_device() const { return device; }
ID3D11DeviceContext* get_d3d_context() const { return context; }
[[nodiscard]] ID3D11Device* get_d3d_device() const { return device; }
[[nodiscard]] ID3D11DeviceContext* get_d3d_context() const { return context; }
[[nodiscard]] dx_pipeline* get_default_pipeline() const { return default_pipeline; }
[[nodiscard]] dx_pipeline* get_rounded_rect_pipeline() const { return rounded_rect_pipeline; }
[[nodiscard]] ID3D11BlendState* get_blend_state() const { return blend_state; }
private:
renderer_window* create_window() override;
@ -23,5 +28,9 @@ private:
ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr;
ID3D11BlendState* blend_state = nullptr;
dx_pipeline* default_pipeline = nullptr;
dx_pipeline* rounded_rect_pipeline = nullptr;
};

View File

@ -5,14 +5,11 @@
#include "dx_renderer.h"
#include "misc/scope_exit.h"
#include <math.h>
using namespace aorii;
dx_pipeline default_pipeline;
dx_pipeline rounded_rect_pipeline;
bool dx_window::create_surface(GLFWwindow* in_window) {
const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
auto dx = aorii::get_renderer<dx_renderer>();
const auto d3d_device = dx->get_d3d_device();
IDXGIDevice* dxgi_device = nullptr;
auto hr = d3d_device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgi_device));
@ -62,23 +59,14 @@ bool dx_window::create_surface(GLFWwindow* in_window) {
return false;
}
default_pipeline.build_constant_buffer();
rounded_rect_pipeline.build_constant_buffer();
hr = build_render_target_view();
if (FAILED(hr)) {
spdlog::critical("无法创建渲染模板视图, 错误: {0}", hr);
return false;
}
default_pipeline.load_pixel_shader(AORII_DEFAULT_PIXEL_SHADER_NAME);
default_pipeline.load_vertex_shader();
rounded_rect_pipeline.load_pixel_shader(AORII_DEFAULT_ROUNDED_RECT_PIXEL_SHADER_NAME);
rounded_rect_pipeline.load_vertex_shader();
context.set_default_pipeline(&default_pipeline);
context.set_rounded_rectangular_pipeline(&rounded_rect_pipeline);
context.set_default_pipeline(dx->get_default_pipeline());
context.set_rounded_rectangular_pipeline(dx->get_rounded_rect_pipeline());
return true;
}
@ -90,6 +78,7 @@ void dx_window::begin_frame() {
const auto d3d_context = render->get_d3d_context();
d3d_context->OMSetRenderTargets(1, &render_target_view, nullptr);
d3d_context->OMSetBlendState(render->get_blend_state(), nullptr, 0xffffffff);
d3d_context->ClearRenderTargetView(render_target_view, clear_color);
if constexpr (false) {
@ -136,7 +125,7 @@ void dx_window::begin_frame() {
}
}
context.draw_rounded_rectangle({ 100, 100 }, { 100, 100 }, { 1, 0, 0, 1 }, 10);
context.draw_rounded_rectangle({ 100, 100 }, { 200, 200 }, { 1, 0, 0, 1 }, 10);
context.flush();
swap_chain->Present(1, 0);
@ -195,14 +184,7 @@ HRESULT dx_window::build_render_target_view() {
const auto& projection_matrix = get_projection_matrix().transpose();
// 更新常量缓冲区
const auto constant_buffer = default_pipeline.lock_constant_buffer();
constant_buffer->projection_matrix = projection_matrix;
default_pipeline.unlock_constant_buffer();
const auto rounded_rect_constant_buffer = rounded_rect_pipeline.lock_constant_buffer();
rounded_rect_constant_buffer->projection_matrix = projection_matrix;
rounded_rect_pipeline.unlock_constant_buffer();
context.set_projection_matrix(projection_matrix);
return hr;
}

View File

@ -18,6 +18,7 @@ struct aorii_triangle {
};
struct aorii_constant_buffer {
Eigen::Matrix4f projection_matrix;
Eigen::Vector2f pos;
Eigen::Vector2f size;
float radius;
};

View File

@ -10,6 +10,7 @@
std::chrono::duration<double> delta_time = {};
decltype(std::chrono::high_resolution_clock::now()) last_time = {};
renderer* s_renderer = nullptr;
void renderer::tick() {
}
@ -18,6 +19,10 @@ void renderer::destroy_window(renderer_window* window) {
delete window;
}
renderer* aorii::get_renderer_raw() {
return s_renderer;
}
bool aorii::create_renderer(renderer_api api) {
if (s_renderer) return true;
switch (api) {

View File

@ -32,11 +32,11 @@ private:
};
namespace aorii {
inline renderer* s_renderer;
renderer* get_renderer_raw();
template<typename T>
static T* get_renderer() {
return static_cast<T*>(s_renderer);
return static_cast<T*>(get_renderer_raw());
}
bool create_renderer(renderer_api api);

View File

@ -23,13 +23,17 @@ void renderer_context::draw_rectangle(const Eigen::Vector2i& in_pos, const Eigen
}
void renderer_context::draw_rounded_rectangle(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, const linear_color& in_color, float in_radius) {
auto constant_buffer = rounded_rectangular_pipeline->lock_constant_buffer();
constant_buffer->size = in_size.cast<float>();
constant_buffer->radius = in_radius;
rounded_rectangular_pipeline->unlock_constant_buffer();
pipeline_context.pos = in_pos.cast<float>();
pipeline_context.size = in_size.cast<float>();
pipeline_context.radius = in_radius;
switch_pipeline(rounded_rectangular_pipeline);
const auto constant_buffer = rounded_rectangular_pipeline->lock_constant_buffer();
memcpy(constant_buffer, &pipeline_context, sizeof(aorii_constant_buffer_type));
rounded_rectangular_pipeline->unlock_constant_buffer();
const aorii_vertex_type v1{ { in_pos.x(), in_pos.y() }, in_color }; // 左上角
const aorii_vertex_type v2{ { in_pos.x() + in_size.x(), in_pos.y() }, in_color }; // 右上角
const aorii_vertex_type v3{ { in_pos.x(), in_pos.y() + in_size.y() }, in_color }; // 左下角

View File

@ -51,12 +51,27 @@ public:
}
void flush() {
if (vertices.empty() || triangles.empty()) return;
if (default_pipeline) {
default_pipeline->set_triangle(vertices, triangles);
default_pipeline->draw();
if (current_pipeline) {
current_pipeline->set_triangle(vertices, triangles);
current_pipeline->draw();
}
clear();
}
void set_projection_matrix(const Eigen::Matrix4f& in_matrix) {
pipeline_context.projection_matrix = in_matrix;
// 更新默认管线的投影矩阵
if (default_pipeline) {
auto constant_buffer = default_pipeline->lock_constant_buffer();
memcpy(constant_buffer, &pipeline_context, sizeof(aorii_constant_buffer_type));
default_pipeline->unlock_constant_buffer();
}
// 圆角矩形管线的投影矩阵不需要更新, 因为它在每次绘制时都会更新
}
[[nodiscard]] const auto& get_pipeline_context() const {
return pipeline_context;
}
protected:
void switch_pipeline(pipeline* in_pipeline) {
#if DEBUG
@ -72,6 +87,7 @@ protected:
private:
std::vector<aorii_vertex_type> vertices;
std::vector<aorii_triangle_type> triangles;
aorii_constant_buffer_type pipeline_context = {};
pipeline* default_pipeline = nullptr;
pipeline* rounded_rectangular_pipeline = nullptr;

View File

@ -11,13 +11,13 @@ void on_window_size_change(GLFWwindow* changed_window, int width, int height) {
window_manager::~window_manager() {
for (const auto window : windows) {
aorii::s_renderer->destroy_window(window);
aorii::get_renderer_raw()->destroy_window(window);
}
windows.clear();
}
renderer_window* window_manager::create_window(const Eigen::Vector2i& in_size, const std::string& in_title) {
const auto window = aorii::s_renderer->create_window();
const auto window = aorii::get_renderer_raw()->create_window();
if (!window->init(in_size, in_title)) {
delete window;
spdlog::error("Failed to create window");

View File

@ -1,36 +1,28 @@
struct ParamBuffer
{
matrix transform;
float2 size; // 像素单位
float radius; // 像素单位
float2 pos; // 屏幕坐标
float2 size; // 圆角矩形像素单位大小
float radius; // 圆角像素单位
};
ParameterBlock<ParamBuffer> param_buffer : register(b0);
SamplerState texture_sampler : register(s0);
struct VSInput {
float2 position : POSITION;
float4 color : COLOR;
float2 position : POSITION; // 窗口坐标
float4 color : COLOR; // 颜色
};
struct PSInput {
float4 position : SV_POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
float4 position : SV_POSITION; // 裁剪空间坐标
float4 color : COLOR; // 颜色
};
// 计算点到矩形边的最小距离
float rounded_box_SDF(float2 center_position, float2 size, float radius)
{
float2 q = abs(center_position) - size + radius;
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
}
PSInput vertex_main(VSInput input)
{
PSInput output;
output.position = mul(float4(input.position, 0.0, 1.0), param_buffer.transform);
output.color = input.color;
output.uv = (input.position + float2(1.0, 1.0)) * 0.5; // 将 [-1, 1] 映射到 [0, 1]
return output;
}
@ -38,20 +30,28 @@ PSInput vertex_main(VSInput input)
// 圆角矩形像素着色器
float4 rounded_rect_pixel_main(PSInput input) : SV_TARGET
{
float2 size = param_buffer.size;
float2 pixel_pos = input.position.xy;
// 圆角半径
float radius = param_buffer.radius;
// 将UV坐标转换为中心坐标系统
float2 center_position = (input.uv - 0.5) * size;
// 计算到圆角矩形边缘的距离
float distance = rounded_box_SDF(center_position, size * 0.5, radius);
// 使用平滑步进函数创建抗锯齿边缘
float alpha = 1.0 - smoothstep(-1.0, 1.0, distance);
// 返回最终颜色
return float4(float3(1, 1, 1), input.color.a * alpha);
// 计算矩形的中心位置
float2 rect_center = param_buffer.pos + param_buffer.size * 0.5;
// 计算半尺寸,减去圆角半径
float2 half_size = param_buffer.size * 0.5 - radius;
// 将坐标系平移到矩形中心
float2 pos = pixel_pos - rect_center;
// 计算距离
float2 d = abs(pos) - half_size;
// 取最大值与0比较确保不小于0
float inside = length(max(d, 0.0)) - radius;
// inside <= 0.0 说明在圆角矩形内
return input.color * step(inside, 0.0);
}
#else
float4 pixel_main(PSInput input) : SV_TARGET