距离场绘制圆角矩形

This commit is contained in:
Nanako 2024-11-16 16:18:40 +08:00
parent 242cdacf2b
commit ffcdc3b9f5
11 changed files with 98 additions and 127 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.15)
project(aorii)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD 26)
include(cmake/retrieve_files.cmake)
include(cmake/detect_os.cmake)

View File

@ -7,7 +7,7 @@
class dx_buffer : public renderer_buffer {
public:
explicit dx_buffer(buffer_type in_type, int in_element_byte, int in_size): renderer_buffer(in_type, in_element_byte, in_size) {
explicit dx_buffer(buffer_type in_type, int32_t in_element_byte, int32_t in_size): renderer_buffer(in_type, in_element_byte, in_size) {
const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
D3D11_BUFFER_DESC buffer_desc = {};
@ -50,7 +50,7 @@ public:
return buffer;
}
protected:
void on_resize(int new_count) override {
void on_resize(int32_t new_count) override {
const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
// 获取原始缓冲区的描述

View File

@ -83,13 +83,13 @@ Eigen::Matrix4f dx_renderer::make_projection_matrix(const Eigen::Vector2i& size)
return matrix;
}
std::vector<char> dx_renderer::load_shader(const std::string& shader_name) {
std::vector<int8_t> dx_renderer::load_shader(const std::string& shader_name) {
auto file_pathname = aorii::get_shader_path(shader_name).generic_string() + ".dxbc";
std::ifstream file(file_pathname, std::ios::binary);
return { (std::istreambuf_iterator(file)), std::istreambuf_iterator<char>() };
}
renderer_buffer* dx_renderer::create_buffer(buffer_type in_buffer_type, int in_element_byte, int in_size) {
renderer_buffer* dx_renderer::create_buffer(buffer_type in_buffer_type, int32_t in_element_byte, int32_t in_size) {
return new dx_buffer(in_buffer_type, in_element_byte, in_size);
}

View File

@ -29,9 +29,9 @@ public:
[[nodiscard]] ID3D11BlendState* get_blend_state() const { return blend_state; }
std::vector<char> load_shader(const std::string& shader_name) override;
std::vector<int8_t> load_shader(const std::string& shader_name) override;
renderer_buffer* create_buffer(buffer_type in_buffer_type, int in_element_byte, int in_size) override;
renderer_buffer* create_buffer(buffer_type in_buffer_type, int32_t in_element_byte, int32_t in_size) override;
private:
renderer_window* create_window() override;

View File

@ -51,7 +51,7 @@ bool dx_window::create_surface(GLFWwindow* in_window) {
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 2;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
sd.Flags = 0;
const auto hwnd = static_cast<HWND>(get_window_handle());
@ -88,8 +88,8 @@ void dx_window::begin_frame() {
context.draw_rectangle({ 400, 0 }, { 100, 100 }, { 0, 1, 0, 1 });
auto radius = abs(sin(get_total_time().count())) * 50;
auto angle = sin(get_total_time().count() * 0.001) * 45;
context.draw_rounded_rectangle({ 100, 100 }, { 200, 400 }, { 1, 0, 0, 1 }, angle, 10);
auto angle = sin(get_total_time().count()) * 45;
context.draw_rounded_rectangle({ 500, 500 }, { 100, 400 }, { 1, 0, 0, 1 }, 10, {10, 20, 30, 40});
double mouse_x, mouse_y;
glfwGetCursorPos(get_glfw_window(), &mouse_x, &mouse_y);

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <span>
#include <string>
#include <Eigen/Eigen>

View File

@ -5,10 +5,9 @@ class rounded_rect_pipeline : public pipeline {
public:
struct param {
Eigen::Matrix4f projection_matrix;
Eigen::Vector2f p1;
Eigen::Vector2f p2;
float radius;
float thickness;
Eigen::Vector2f size;
Eigen::Vector2f pos;
Eigen::Vector4f radius; // 左上, 右上, 左下, 右下
};
bool init() override;

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <chrono>
#include <Eigen/Eigen>
#include <filesystem>
@ -228,15 +228,15 @@ public:
* @param shader_name , , 使aorii::get_shader_path获取路径, API下会自动添加后缀
* @return
*/
virtual std::vector<char> load_shader(const std::string& shader_name) = 0;
virtual std::vector<int8_t> load_shader(const std::string& shader_name) = 0;
renderer_buffer* create_vertex_buffer(const int in_size = 4) {
renderer_buffer* create_vertex_buffer(const int32_t in_size = 4) {
return create_buffer(buffer_type::vertex, sizeof(aorii_vertex), in_size);
}
renderer_buffer* create_index_buffer(const int in_size = 2) {
renderer_buffer* create_index_buffer(const int32_t in_size = 2) {
return create_buffer(buffer_type::index, sizeof(aorii_triangle), in_size);
}
virtual renderer_buffer* create_buffer(buffer_type in_buffer_type, int in_element_byte, int in_element_count) = 0;
virtual renderer_buffer* create_buffer(buffer_type in_buffer_type, int32_t in_element_byte, int32_t in_element_count) = 0;
virtual void destroy_buffer(renderer_buffer* buffer) { delete buffer; }
virtual rect_pipeline* get_rect_pipeline() = 0;

View File

@ -1,4 +1,4 @@
#include "renderer_context.h"
#include "renderer_context.h"
#include "renderer.h"
#include "backend/dx/dx_renderer.h"
@ -11,10 +11,16 @@ void renderer_context::draw_rectangle(const Eigen::Vector2f& in_pos, const Eigen
make_rect(in_pos, in_size, in_color);
}
void renderer_context::draw_rounded_rectangle(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const linear_color& in_color, float in_angle, float in_radius) {
to_rounded_rect_pipeline(in_pos, in_size, in_angle, in_radius);
void renderer_context::draw_rounded_rectangle(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const linear_color& in_color, float in_angle, const rect_radius& in_radius) {
to_rounded_rect_pipeline(in_pos, in_size, in_radius);
// in_angle传入的是角度, 需要转换为弧度
in_angle = in_angle * M_PI / 180;
make_rect(in_pos, in_size, in_color, in_angle);
if (in_angle != 0) {
auto center = in_pos + in_size / 2;
rotate_vertex(in_angle, center);
}
// 立刻绘制并重置管线, 因为下一个绘制命令的参数可能不同, 所以无法合批
flush();
}
@ -51,32 +57,16 @@ void renderer_context::to_rect_pipeline() {
switch_pipeline(rect_p);
}
void renderer_context::to_rounded_rect_pipeline(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, float in_angle, float in_radius) {
void renderer_context::to_rounded_rect_pipeline(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const rect_radius& in_radius) {
const auto rounded_rect_p = aorii::get_renderer<dx_renderer>()->get_rounded_rect_pipeline();
switch_pipeline(rounded_rect_p);
Eigen::Vector2f top_middle = in_pos + Eigen::Vector2f(in_size.x() / 2, 0);
Eigen::Vector2f bottom_middle = in_pos + Eigen::Vector2f(in_size.x() / 2, in_size.y());
// 如果角度不为0, 则需要计算旋转后的坐标
if (in_angle != 0) {
// 将top_middle和bottom_middle绕盒子中心旋转
const Eigen::Vector2f& center = in_pos + in_size / 2;
Eigen::Affine2f transform =
Eigen::Translation<float, 2>(center) * // 平移到旋转中心
Eigen::Rotation2D<float>(in_angle) * // 旋转
Eigen::Translation<float, 2>(-center); // 平移回原点
top_middle = transform * top_middle;
bottom_middle = transform * bottom_middle;
}
rounded_rect_pipeline::param param;
param.projection_matrix = projection_matrix;
param.p1 = top_middle;
param.p2 = bottom_middle;
param.size = in_size;
param.pos = in_pos;
param.radius = in_radius;
param.thickness = in_size.x();
rounded_rect_p->set_param(param);
}
@ -100,58 +90,33 @@ void renderer_context::to_segment_pipeline(const Eigen::Vector2f& in_pos_p1, con
void renderer_context::make_rect(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size,
const linear_color& in_color, float in_angle) {
// 如果角度不为0, 则需要计算旋转后的坐标
if (in_angle != 0) {
// 绕盒子中心旋转
// 创建仿射变换矩阵(先平移,后旋转,再平移回去)
const Eigen::Vector2f& center = in_pos + in_size / 2;
Eigen::Affine2f transform =
Eigen::Translation<float, 2>(center) * // 平移到旋转中心
Eigen::Rotation2D<float>(in_angle) * // 旋转
Eigen::Translation<float, 2>(-center); // 平移回原点
const aorii_vertex v1{ { in_pos.x(), in_pos.y() }, { -1, 1 }, in_color }; // 左上角
const aorii_vertex v2{ { in_pos.x() + in_size.x(), in_pos.y() }, { 1, 1 }, in_color }; // 右上角
const aorii_vertex v3{ { in_pos.x(), in_pos.y() + in_size.y() }, { -1, -1 }, in_color }; // 左下角
const aorii_vertex v4{ { in_pos.x() + in_size.x(), in_pos.y() + in_size.y() }, { 1, -1 }, in_color }; // 右下角
const Eigen::Vector2f top_left = transform * in_pos;
const Eigen::Vector2f top_right = transform * Eigen::Vector2f(in_pos.x() + in_size.x(), in_pos.y());
const Eigen::Vector2f bottom_left = transform * Eigen::Vector2f(in_pos.x(), in_pos.y() + in_size.y());
const Eigen::Vector2f bottom_right = transform * (in_pos + in_size);
const uint32_t index1 = vertices.size();
vertices.push_back(v1);
const uint32_t index2 = vertices.size();
vertices.push_back(v2);
const uint32_t index3 = vertices.size();
vertices.push_back(v3);
const uint32_t index4 = vertices.size();
vertices.push_back(v4);
const aorii_vertex v1{ { top_left.x(), top_left.y() }, { 0, 0 }, in_color }; // 左上角
const aorii_vertex v2{ { top_right.x(), top_right.y() }, { 1, 0 }, in_color }; // 右上角
const aorii_vertex v3{ { bottom_left.x(), bottom_left.y() }, { 0, 1 }, in_color }; // 左下角
const aorii_vertex v4{ { bottom_right.x(), bottom_right.y() }, { 1, 1 }, in_color }; // 右下角
const aorii_triangle t1 = { index1, index2, index3 };
const aorii_triangle t2 = { index2, index3, index4 };
triangles.push_back(t1);
triangles.push_back(t2);
}
const uint32_t index1 = vertices.size();
vertices.push_back(v1);
const uint32_t index2 = vertices.size();
vertices.push_back(v2);
const uint32_t index3 = vertices.size();
vertices.push_back(v3);
const uint32_t index4 = vertices.size();
vertices.push_back(v4);
void renderer_context::rotate_vertex(const float in_angle, const Eigen::Vector2f& in_center) {
const Eigen::Affine2f transform =
Eigen::Translation<float, 2>(in_center) * // 平移到旋转中心
Eigen::Rotation2D<float>(in_angle) * // 旋转
Eigen::Translation<float, 2>(-in_center); // 平移回原点
const aorii_triangle t1 = { index1, index2, index3 };
const aorii_triangle t2 = { index2, index3, index4 };
triangles.push_back(t1);
triangles.push_back(t2);
}
else {
const aorii_vertex v1{ { in_pos.x(), in_pos.y() }, { 0, 0 }, in_color }; // 左上角
const aorii_vertex v2{ { in_pos.x() + in_size.x(), in_pos.y() }, { 1, 0 }, in_color }; // 右上角
const aorii_vertex v3{ { in_pos.x(), in_pos.y() + in_size.y() }, { 0, 1 }, in_color }; // 左下角
const aorii_vertex v4{ { in_pos.x() + in_size.x(), in_pos.y() + in_size.y() }, { 1, 1 }, in_color }; // 右下角
const uint32_t index1 = vertices.size();
vertices.push_back(v1);
const uint32_t index2 = vertices.size();
vertices.push_back(v2);
const uint32_t index3 = vertices.size();
vertices.push_back(v3);
const uint32_t index4 = vertices.size();
vertices.push_back(v4);
const aorii_triangle t1 = { index1, index2, index3 };
const aorii_triangle t2 = { index2, index3, index4 };
triangles.push_back(t1);
triangles.push_back(t2);
for (auto& vertex : vertices) {
vertex.position = transform * vertex.position;
}
}

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <spdlog/spdlog.h>
#include "core/pipeline/pipeline.h"
@ -7,6 +7,21 @@
class renderer_texture;
struct rect_radius {
rect_radius(float radius) : top_left(radius), top_right(radius), bottom_left(radius), bottom_right(radius) {}
rect_radius(float top, float bottom) : top_left(top), top_right(top), bottom_left(bottom), bottom_right(bottom) {}
rect_radius(float top_left, float top_right, float bottom_left, float bottom_right) : top_left(top_left), top_right(top_right), bottom_left(bottom_left), bottom_right(bottom_right) {}
float top_left;
float top_right;
float bottom_left;
float bottom_right;
operator Eigen::Vector4f() const {
return { top_left, top_right, bottom_left, bottom_right };
}
};
class renderer_context {
public:
void init();
@ -26,7 +41,7 @@ public:
* @param in_angle
* @param in_radius ()
*/
void draw_rounded_rectangle(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const linear_color& in_color, float in_angle, float in_radius);
void draw_rounded_rectangle(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const linear_color& in_color, float in_angle, const rect_radius& in_radius);
/**
* 线
@ -80,7 +95,7 @@ protected:
}
void to_rect_pipeline();
void to_rounded_rect_pipeline(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, float in_angle, float in_radius);
void to_rounded_rect_pipeline(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const rect_radius& in_radius);
void to_texture_pipeline(renderer_texture* in_texture);
void to_segment_pipeline(const Eigen::Vector2f& in_pos_p1, const Eigen::Vector2f& in_pos_p2, float in_thickness);
private:
@ -92,4 +107,5 @@ private:
Eigen::Vector2i framebuffer_size;
private:
void make_rect(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const linear_color& in_color, float in_angle = 0);
void rotate_vertex(float in_angle, const Eigen::Vector2f& in_center);
};

View File

@ -1,10 +1,9 @@
cbuffer ParamBuffer : register(b0)
{
matrix transform;
float2 p1; // 点1位置
float2 p2; // 点2位置
float radius; // 圆角像素单位
float thickness; // 线段的宽度
float2 size; // 矩形大小 像素单位
float2 pos; // 矩形位置 像素单位
float4 radius; // 四角圆角像素单位 左上 右上 左下 右下
};
struct VSInput {
@ -28,33 +27,25 @@ PSInput vertex_main(VSInput input)
return output;
}
// 圆角矩形像素着色器
float4 pixel_main(PSInput input) : SV_TARGET
{
// 计算input.position到线段p1p2的最短距离
float2 p = input.uv;
return float4(p, 0, 1);
// 计算当前片段到线段的距离
float2 ba = p2 - p1;
float2 pa = p - p1;
float2 ba_norm = normalize(ba);
float h = clamp(dot(pa, ba_norm), 0.0, length(ba));
float2 pb = p1 + ba_norm * h;
float dist = length(p - pb); // 点到线段的距离
float half_thickness = thickness / 2;
// 抗锯齿
float alpha = smoothstep(half_thickness - 1, half_thickness, dist);
// 圆角
float2 corner = float2(radius, radius);
float2 d = abs(p - pb) - corner;
float2 max_d = max(d, 0);
// float corner_dist = length(max_d);
// float corner_alpha = smoothstep(0, 1, corner_dist);
float4 color = input.color;
color.a *= (1 - alpha);
return color;
float distance_from_rect_uv(float2 p, float corner_radius) {
corner_radius *= 2;
float2 corner_radius_uv = corner_radius / size;
float2 inner_rect = float2(1) - corner_radius_uv; // 圆心
float2 q = abs(p) - inner_rect;
q *= size;
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - corner_radius;
}
float4 pixel_main(PSInput input) : SV_Target
{
float2 p = input.uv;
// 象限
int2 quadrant = sign(p);
int idx = (quadrant.x > 0 ? 1 : 0) + (quadrant.y > 0 ? 0 : 2);
float r = radius[idx];
float d = distance_from_rect_uv(p, r);
// TODO 抗锯齿
return input.color * -d;
}