t
This commit is contained in:
parent
7e90e0f302
commit
ba993e294c
@ -7,6 +7,12 @@ if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
endif ()
|
||||
|
||||
# 设置全局着色器输出目录
|
||||
set(SHADER_OUTPUT_DIR "${CMAKE_BINARY_DIR}/shaders" CACHE PATH "着色器编译输出路径")
|
||||
# 设置全局着色器路径列表
|
||||
set(SHADER_SOURCE_DIRS "" CACHE STRING "着色器源文件路径列表")
|
||||
mark_as_advanced(SHADER_OUTPUT_DIR)
|
||||
|
||||
set(MSDFGEN_USE_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE)
|
||||
set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE)
|
||||
set(MSDFGEN_USE_OPENMP ON CACHE BOOL "Use OpenMP for MSDFGen" FORCE)
|
||||
|
@ -1,88 +1,16 @@
|
||||
function(compile_slang_shaders input_file stage entry_point)
|
||||
# 保证输出目录存在
|
||||
if (NOT DEFINED SHADER_OUTPUT_DIR)
|
||||
message(FATAL_ERROR "SHADER_OUTPUT_DIR not defined")
|
||||
endif ()
|
||||
set(output_dir ${SHADER_OUTPUT_DIR})
|
||||
# 查找编译器和Python
|
||||
find_program(PYTHON_EXECUTABLE python)
|
||||
find_program(GLSLANG_VALIDATOR glslangValidator)
|
||||
find_program(SLANG_COMPILER slangc)
|
||||
|
||||
# 获取输出文件的基本名称
|
||||
get_filename_component(filename ${input_file} NAME_WE)
|
||||
|
||||
# 定义根据阶段确定的配置
|
||||
if(stage STREQUAL "vertex")
|
||||
set(profile "vs_5_0")
|
||||
elseif(stage STREQUAL "pixel")
|
||||
set(profile "ps_5_0")
|
||||
elseif (stage STREQUAL "comp")
|
||||
set(profile "cs_5_0")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported shader stage: ${stage}")
|
||||
endif()
|
||||
|
||||
# 初始化一个列表来存储输出文件
|
||||
set(output_files)
|
||||
set(extra_args ${ARGN})
|
||||
# 如果是debug模式,添加调试标志
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
list(APPEND extra_args "-g")
|
||||
endif()
|
||||
|
||||
# 为每个后台创建自定义命令
|
||||
if(LLGL_BUILD_RENDERER_OPENGL OR LLGL_BUILD_RENDERER_OPENGL_ES3 OR LLGL_BUILD_RENDERER_WEBGL)
|
||||
set(output_file ${output_dir}/${filename}_${entry_point}.glsl)
|
||||
add_custom_command(
|
||||
OUTPUT ${output_file}
|
||||
COMMAND slangc -target glsl -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_OPENGL=1 -DUV_FLIP=1
|
||||
DEPENDS ${input_file}
|
||||
COMMENT "Compiling ${input_file} to GLSL (${stage}, ${entry_point})"
|
||||
)
|
||||
list(APPEND output_files ${output_file})
|
||||
endif()
|
||||
|
||||
if(LLGL_BUILD_RENDERER_DIRECT3D11 OR LLGL_BUILD_RENDERER_DIRECT3D12)
|
||||
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} ${extra_args} -o ${output_file} ${input_file} -DUSE_DX=1
|
||||
DEPENDS ${input_file}
|
||||
COMMENT "Compiling ${input_file} to DXBC (${stage}, ${entry_point}) with profile ${profile}"
|
||||
)
|
||||
list(APPEND output_files ${output_file})
|
||||
endif()
|
||||
|
||||
if(LLGL_BUILD_RENDERER_VULKAN)
|
||||
set(output_file ${output_dir}/${filename}_${entry_point}.spirv)
|
||||
add_custom_command(
|
||||
OUTPUT ${output_file}
|
||||
COMMAND slangc -target spirv -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_VULKAN=1 -DUV_FLIP=1
|
||||
DEPENDS ${input_file}
|
||||
COMMENT "Compiling ${input_file} to SPIR-V (${stage}, ${entry_point})"
|
||||
)
|
||||
list(APPEND output_files ${output_file})
|
||||
endif()
|
||||
|
||||
if(LLGL_BUILD_RENDERER_METAL)
|
||||
set(output_file ${output_dir}/${filename}_${entry_point}.metal)
|
||||
add_custom_command(
|
||||
OUTPUT ${output_file}
|
||||
COMMAND slangc -target metal -entry ${entry_point} -stage ${stage} ${extra_args} -o ${output_file} ${input_file} -DUSE_METAL=1
|
||||
DEPENDS ${input_file}
|
||||
COMMENT "Compiling ${input_file} to Metal Shading Language (${stage}, ${entry_point})"
|
||||
)
|
||||
list(APPEND output_files ${output_file})
|
||||
endif()
|
||||
|
||||
# 将输出文件添加到全局列表中
|
||||
set_property(GLOBAL APPEND PROPERTY ALL_SHADER_OUTPUTS ${output_files})
|
||||
function(shader_compile_target INPUT_DIR)
|
||||
# 将路径拼接到全局着色器路径列表中
|
||||
list(APPEND SHADER_SOURCE_DIRS ${INPUT_DIR})
|
||||
endfunction()
|
||||
|
||||
function(add_compile_shaders_target target_name before_target)
|
||||
file(MAKE_DIRECTORY ${SHADER_OUTPUT_DIR})
|
||||
|
||||
get_property(ALL_SHADER_OUTPUTS GLOBAL PROPERTY ALL_SHADER_OUTPUTS)
|
||||
add_custom_target(${target_name} ALL DEPENDS ${ALL_SHADER_OUTPUTS})
|
||||
# 将编译着色器的目标添加到指定目标之前
|
||||
add_dependencies(${before_target} ${target_name})
|
||||
# 将ALL_SHADER_OUTPUTS清空
|
||||
set_property(GLOBAL PROPERTY ALL_SHADER_OUTPUTS "")
|
||||
endfunction()
|
||||
# 添加自定义命令, 用于编译着色器, 调用scripts/compile_shaders.py
|
||||
add_custom_target(compile_aorii_shaders
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/compile_shaders.py --input-dir ${SHADER_SOURCE_DIRS} --output-dir ${SHADER_OUTPUT_DIR}
|
||||
COMMENT "Compiling shaders"
|
||||
VERBATIM
|
||||
)
|
||||
|
100
scripts/compile_shaders.py
Normal file
100
scripts/compile_shaders.py
Normal file
@ -0,0 +1,100 @@
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
|
||||
def find_shader_files(input_dir, extensions):
|
||||
for root, _, files in os.walk(input_dir):
|
||||
for file in files:
|
||||
if any(file.endswith(ext) for ext in extensions):
|
||||
yield os.path.join(root, file)
|
||||
|
||||
def compile_glsl(input_file, output_dir, glslang_validator):
|
||||
try:
|
||||
base = os.path.splitext(os.path.basename(input_file))[0]
|
||||
ext = os.path.splitext(input_file)[1][1:]
|
||||
relative_path = os.path.relpath(os.path.dirname(input_file), args.input_dir)
|
||||
output_subdir = os.path.join(output_dir, relative_path)
|
||||
output_file = os.path.join(output_subdir, f"{base}.{ext}.spv")
|
||||
|
||||
os.makedirs(output_subdir, exist_ok=True)
|
||||
|
||||
cmd = [glslang_validator, "-V", input_file, "-o", output_file]
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
print(f"Compiled GLSL: {input_file} -> {output_file}")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error compiling {input_file}:")
|
||||
print(e.stderr)
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Unexpected error with {input_file}: {str(e)}")
|
||||
return False
|
||||
|
||||
def find_slang_entries(input_file):
|
||||
try:
|
||||
with open(input_file, 'r') as f:
|
||||
content = f.read()
|
||||
pattern = r'\[shader\(\s*"(?:\w+)"\s*\)\]\s*\n\s*\w+\s+(\w+)\s*\('
|
||||
return list(set(re.findall(pattern, content)))
|
||||
except Exception as e:
|
||||
print(f"Error parsing {input_file}: {str(e)}")
|
||||
return []
|
||||
|
||||
def compile_slang(input_file, output_dir, slangc):
|
||||
try:
|
||||
entries = find_slang_entries(input_file)
|
||||
if not entries:
|
||||
print(f"No entries found in {input_file}")
|
||||
return False
|
||||
|
||||
base = os.path.splitext(os.path.basename(input_file))[0]
|
||||
relative_path = os.path.relpath(os.path.dirname(input_file), args.input_dir)
|
||||
output_subdir = os.path.join(output_dir, relative_path)
|
||||
os.makedirs(output_subdir, exist_ok=True)
|
||||
|
||||
success = True
|
||||
for entry in entries:
|
||||
output_file = os.path.join(output_subdir, f"{base}_{entry}.spv")
|
||||
cmd = [slangc, input_file, "-entry", entry, "-o", output_file, "-target", "spirv"]
|
||||
try:
|
||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
print(f"Compiled Slang: {input_file}:{entry} -> {output_file}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error compiling {input_file}:{entry}")
|
||||
print(e.stderr)
|
||||
success = False
|
||||
return success
|
||||
except Exception as e:
|
||||
print(f"Unexpected error with {input_file}: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Compile shaders to SPIR-V")
|
||||
parser.add_argument("--input-dir", required=True, help="Input directory containing shaders")
|
||||
parser.add_argument("--output-dir", required=True, help="Output directory for SPIR-V files")
|
||||
parser.add_argument("--glslang", default="glslangValidator", help="Path to glslangValidator")
|
||||
parser.add_argument("--slangc", default="slangc", help="Path to slangc")
|
||||
args = parser.parse_args()
|
||||
|
||||
glsl_ext = [".vert", ".frag", ".comp", ".geom", ".tesc", ".tese", ".glsl"]
|
||||
slang_ext = [".slang"]
|
||||
|
||||
all_success = True
|
||||
|
||||
# Compile GLSL
|
||||
for file in find_shader_files(args.input_dir, glsl_ext):
|
||||
if not compile_glsl(file, args.output_dir, args.glang):
|
||||
all_success = False
|
||||
|
||||
# Compile Slang
|
||||
for file in find_shader_files(args.input_dir, slang_ext):
|
||||
if not compile_slang(file, args.output_dir, args.slangc):
|
||||
all_success = False
|
||||
|
||||
if not all_success:
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -27,20 +27,7 @@ if (APPLE)
|
||||
endif ()
|
||||
|
||||
# 添加编译shader的自定义命令
|
||||
if (NOT DEFINED SHADER_OUTPUT_DIR)
|
||||
set(SHADER_OUTPUT_DIR ${CMAKE_BINARY_DIR}/shaders CACHE PATH "Output directory for compiled shaders")
|
||||
message(STATUS "SHADER_OUTPUT_DIR not defined, using default: ${SHADER_OUTPUT_DIR}")
|
||||
endif ()
|
||||
function(compile_aorii_shader SHADER_FILE)
|
||||
compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/${SHADER_FILE}.slang" "vertex" "vertex_main")
|
||||
compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/${SHADER_FILE}.slang" "pixel" "pixel_main")
|
||||
endfunction()
|
||||
compile_aorii_shader(aorii_rect)
|
||||
compile_aorii_shader(aorii_rounded_rect)
|
||||
compile_aorii_shader(aorii_texture)
|
||||
compile_aorii_shader(aorii_segment)
|
||||
compile_aorii_shader(aorii_sdf_text)
|
||||
add_compile_shaders_target("compile_aorii_shaders" ${PROJECT_NAME})
|
||||
shader_compile_target(${CMAKE_CURRENT_SOURCE_DIR}/shaders)
|
||||
|
||||
# 如果需要编译example, 添加自定义命令用于拷贝shader文件
|
||||
if (BUILD_EXAMPLE)
|
||||
@ -50,10 +37,10 @@ if (BUILD_EXAMPLE)
|
||||
|
||||
# 添加自定义命令, 当外部目标compile_aorii_shaders被编译时, 将${SOURCE_DIR}文件复制到${DEST_DIR}
|
||||
add_custom_command(
|
||||
TARGET compile_aorii_shaders
|
||||
TARGET ${PROJECT_NAME}
|
||||
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 ()
|
||||
endif ()
|
||||
|
@ -46,12 +46,20 @@ namespace aorii {
|
||||
}
|
||||
|
||||
int run(const init_info& in_init_info) {
|
||||
if (!init(in_init_info.main_window_desc))
|
||||
try {
|
||||
if (!init(in_init_info.main_window_desc))
|
||||
return -1;
|
||||
while (!should_exit()) {
|
||||
update();
|
||||
}
|
||||
destroy();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("运行时异常: {}", e.what());
|
||||
return -1;
|
||||
} catch (...) {
|
||||
spdlog::error("未知异常");
|
||||
return -1;
|
||||
while (!should_exit()) {
|
||||
update();
|
||||
}
|
||||
destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -162,6 +162,7 @@ bool aorii_renderer::init() {
|
||||
queue_create_info.setQueueCount(1);
|
||||
queue_create_info.setPQueuePriorities(&queue_priority);
|
||||
queue_create_infos.push_back(queue_create_info);
|
||||
break;
|
||||
}
|
||||
|
||||
vk::DeviceCreateInfo device_create_info{};
|
||||
@ -182,6 +183,7 @@ bool aorii_renderer::init() {
|
||||
}
|
||||
|
||||
void aorii_renderer::destroy() {
|
||||
spdlog::info("销毁渲染器");
|
||||
main_device->destroy();
|
||||
instance.destroy();
|
||||
}
|
||||
@ -203,26 +205,21 @@ const std::vector<device_handle>& aorii_renderer::get_compute_device() const {
|
||||
if (!compute_device.empty()) return compute_device;
|
||||
|
||||
for (const auto& device: selector.compute_group) {
|
||||
const float queue_priority = 1.0f;
|
||||
constexpr float queue_priority = 1.0f;
|
||||
std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
|
||||
const auto queue_families = device.getQueueFamilyProperties();
|
||||
for (uint32_t i = 0; i < queue_families.size(); ++i) {
|
||||
const bool queue_compute_support = static_cast<bool>(
|
||||
queue_families[i].queueFlags & vk::QueueFlagBits::eCompute);
|
||||
if (queue_compute_support) {
|
||||
vk::DeviceQueueCreateInfo queue_create_info{};
|
||||
queue_create_info.setQueueFamilyIndex(i);
|
||||
queue_create_info.setQueueCount(1);
|
||||
queue_create_info.setPQueuePriorities(&queue_priority);
|
||||
queue_create_infos.push_back(queue_create_info);
|
||||
}
|
||||
const auto& families = device_selector::get_queue_families(device, vk::QueueFlagBits::eCompute);
|
||||
for (const auto i: families) {
|
||||
vk::DeviceQueueCreateInfo queue_create_info{};
|
||||
queue_create_info.setQueueFamilyIndex(i);
|
||||
queue_create_info.setQueueCount(1);
|
||||
queue_create_info.setPQueuePriorities(&queue_priority);
|
||||
queue_create_infos.push_back(queue_create_info);
|
||||
}
|
||||
|
||||
device_handle handle;
|
||||
vk::DeviceCreateInfo device_create_info{};
|
||||
device_create_info.setQueueCreateInfos(queue_create_infos);
|
||||
handle.create_device(device, device_create_info);
|
||||
spdlog::info("创建 Vulkan 计算设备: {}", handle.get_physical_device_name());
|
||||
compute_device.push_back(handle);
|
||||
}
|
||||
return compute_device;
|
||||
|
@ -19,6 +19,8 @@ public:
|
||||
void destroy_surface(const vk::SurfaceKHR& in_surface) const;
|
||||
device_handle get_main_device() const { return main_device; }
|
||||
const std::vector<device_handle>& get_compute_device() const;
|
||||
|
||||
const auto& get_instance() const { return instance; }
|
||||
private:
|
||||
vk::Instance instance;
|
||||
device_selector selector;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
struct device_handle {
|
||||
vk::PhysicalDevice physical_device;
|
||||
@ -17,6 +18,7 @@ struct device_handle {
|
||||
void create_device(const vk::PhysicalDevice in_physical_device, const vk::DeviceCreateInfo& in_create_info) {
|
||||
physical_device = in_physical_device;
|
||||
device = physical_device.createDevice(in_create_info);
|
||||
spdlog::info("创建 Vulkan 设备: {}", get_physical_device_name());
|
||||
}
|
||||
void destroy_device() {
|
||||
if (device) {
|
||||
|
@ -14,6 +14,7 @@ struct PSInput {
|
||||
float4 color : COLOR; // 颜色
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
PSInput vertex_main(VSInput input)
|
||||
{
|
||||
PSInput output;
|
||||
@ -32,6 +33,7 @@ float distance_from_rect_uv(float2 p, float corner_radius) {
|
||||
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - corner_radius;
|
||||
}
|
||||
|
||||
[shader("pixel")]
|
||||
float4 pixel_main(PSInput input) : SV_Target
|
||||
{
|
||||
float2 p = uv_to_ndc(input.uv);
|
||||
|
@ -6,6 +6,7 @@ struct PSInput {
|
||||
float4 color : COLOR; // 颜色
|
||||
};
|
||||
|
||||
[[vk::binding(0), vk::set(0)]]
|
||||
cbuffer segment_buffer : register(b0) {
|
||||
matrix transform;
|
||||
float2 pos_a; // 线段的起点
|
||||
|
@ -4,7 +4,7 @@ struct ParamBuffer
|
||||
{
|
||||
matrix transform;
|
||||
};
|
||||
ParameterBlock<ParamBuffer> param_buffer : register(b0);
|
||||
ParameterBlock<ParamBuffer> param_buffer;
|
||||
|
||||
struct PSInput {
|
||||
float4 position : SV_POSITION; // 裁剪空间坐标
|
||||
@ -12,8 +12,8 @@ struct PSInput {
|
||||
float4 color : COLOR; // 颜色
|
||||
};
|
||||
|
||||
Texture2D<float4> texture : register(t0);
|
||||
SamplerState sampler : register(s0);
|
||||
Texture2D<float4> texture;
|
||||
SamplerState sampler;
|
||||
|
||||
PSInput vertex_main(VSInput input)
|
||||
{
|
||||
|
9
src/core/shader/test.frag
Normal file
9
src/core/shader/test.frag
Normal file
@ -0,0 +1,9 @@
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
23
src/core/shader/test.vert
Normal file
23
src/core/shader/test.vert
Normal file
@ -0,0 +1,23 @@
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(0.0, -0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(-0.5, 0.5)
|
||||
);
|
||||
vec3 colors[3] = vec3[](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
fragColor = colors[gl_VertexIndex];
|
||||
}
|
112
src/core/shader/utils_vulkan.glsl
Normal file
112
src/core/shader/utils_vulkan.glsl
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright Take Vos 2021.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
/** logical and.
|
||||
*/
|
||||
bvec4 and(bvec4 lhs, bvec4 rhs)
|
||||
{
|
||||
return bvec4(
|
||||
lhs.x && rhs.x,
|
||||
lhs.y && rhs.y,
|
||||
lhs.z && rhs.z,
|
||||
lhs.w && rhs.w
|
||||
);
|
||||
}
|
||||
|
||||
/** Check if the rectangle contains the point.
|
||||
*
|
||||
* @param rectangle A axis-aligned rectangle encoded as left-bottom=(x,y), right-top=(z,w)
|
||||
* @param point A 2D point
|
||||
* @return True is the point is inside the rectangle.
|
||||
*/
|
||||
bool contains(vec4 rectangle, vec2 point)
|
||||
{
|
||||
return greaterThanEqual(point.xyxy, rectangle) == bvec4(true, true, false, false);
|
||||
}
|
||||
|
||||
/** Convert coverage to a perceptional uniform alpha.
|
||||
*
|
||||
* This function takes into account the lightness of the full pixel, then
|
||||
* determines based on this if the background is either black or white, or
|
||||
* if linear conversion of coverage to alpha is needed.
|
||||
*
|
||||
* On black and white background we measure the target lightness of each sub-pixel
|
||||
* then convert to target luminosity and eventually the alpha value.
|
||||
*
|
||||
* The alpha-component of the return value is calculated based on the full pixel
|
||||
* lightness and from the green sub-pixel coverage.
|
||||
*
|
||||
* The full formula to convert coverage to alpha taking into account perceptional
|
||||
* uniform lightness between foreground and background colors:
|
||||
* ```
|
||||
* F = foreground color
|
||||
* B = background color
|
||||
* T = target color
|
||||
* c = coverage
|
||||
* a = alpha
|
||||
* T = mix(sqrt(F), sqrt(B), c) ^ 2
|
||||
*
|
||||
* a = (T - B) / (F - B) if F != B
|
||||
* a = c otherwise
|
||||
* ```
|
||||
*
|
||||
* To simplify this formula and remove the division we fill in the foreground and background
|
||||
* with black and white and the other way around:
|
||||
* ```
|
||||
* a = c^2 if F == 1 and B == 0
|
||||
* a = 2c - c^2 if F == 0 and B == 1
|
||||
* ```
|
||||
*
|
||||
* Now we mix based on the foreground color, expecting the background color to mirror.
|
||||
* ```
|
||||
* a = mix(2c - c^2, c^2, F^2) if B^2 == 1 - F^2
|
||||
* ```
|
||||
*
|
||||
* @param coverage The amount of coverage. Elements must be between 0.0 and 1.0
|
||||
* @param foreground_sq The sqrt of the foreground. Elements must be between 0.0 and 1.0
|
||||
* @return The alpha value for the red, blue, green, alpha color components.
|
||||
*/
|
||||
float coverage_to_alpha(float coverage, float sqrt_foreground)
|
||||
{
|
||||
float coverage_sq = coverage * coverage;
|
||||
float coverage_2 = coverage + coverage;
|
||||
return mix(coverage_2 - coverage_sq, coverage_sq, sqrt_foreground);
|
||||
}
|
||||
|
||||
/** Convert coverage to a perceptional uniform alpha.
|
||||
*
|
||||
* @see coverage_to_alpha(float, float)
|
||||
*/
|
||||
vec4 coverage_to_alpha(vec4 coverage, vec4 sqrt_foreground)
|
||||
{
|
||||
vec4 coverage_sq = coverage * coverage;
|
||||
vec4 coverage_2 = coverage + coverage;
|
||||
return mix(coverage_2 - coverage_sq, coverage_sq, sqrt_foreground);
|
||||
}
|
||||
|
||||
/** Multiply the alpha with the color.
|
||||
*
|
||||
* @param color The color+alpha without pre-multiplication.
|
||||
* @return The color+alpha where the color is multiplied with the alpha.
|
||||
*/
|
||||
vec4 multiply_alpha(vec4 color)
|
||||
{
|
||||
return vec4(color.rgb * color.a, color.a);
|
||||
}
|
||||
|
||||
/** Convert RGB to Y.
|
||||
*/
|
||||
float rgb_to_y(vec3 color)
|
||||
{
|
||||
vec3 tmp = color * vec3(0.2126, 0.7152, 0.0722);
|
||||
return tmp.r + tmp.g + tmp.b;
|
||||
}
|
||||
|
||||
/** Convert RGB to RGBY.
|
||||
*/
|
||||
vec4 rgb_to_rgby(vec3 color)
|
||||
{
|
||||
return vec4(color, rgb_to_y(color));
|
||||
}
|
||||
|
@ -47,11 +47,21 @@ renderer_window::~renderer_window() {
|
||||
}
|
||||
}
|
||||
|
||||
void renderer_window::set_title(const std::wstring& in_title) const {
|
||||
void renderer_window::set_title(const std::string& in_title) const {
|
||||
glfwSetWindowTitle(window, in_title.c_str());
|
||||
}
|
||||
|
||||
void renderer_window::set_size(int in_width, int in_height) {
|
||||
glfwSetWindowSize(window, in_width, in_height);
|
||||
on_resize(in_width, in_height);
|
||||
}
|
||||
|
||||
void renderer_window::close() const {
|
||||
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
||||
}
|
||||
|
||||
void renderer_window::update() {
|
||||
// swapchain->;
|
||||
}
|
||||
|
||||
bool renderer_window::create_surface() {
|
||||
|
@ -30,7 +30,8 @@ public:
|
||||
explicit renderer_window(const window_desc& in_desc);
|
||||
virtual ~renderer_window();
|
||||
|
||||
void set_title(const std::wstring& in_title) const;
|
||||
void set_title(const std::string& in_title) const;
|
||||
void set_size(int in_width, int in_height);
|
||||
|
||||
void close() const;
|
||||
bool should_close() const { return glfwWindowShouldClose(window); }
|
||||
@ -46,6 +47,8 @@ public:
|
||||
return vk::Extent2D(width, height);
|
||||
}
|
||||
|
||||
void update();
|
||||
|
||||
bool create_surface();
|
||||
[[nodiscard]] auto get_surface() const { return surface; }
|
||||
bool create_swap_chain(const renderer_swapchain::create_info& in_create_info);
|
||||
|
Loading…
x
Reference in New Issue
Block a user