우수한 Sokol GFX 라이브러리를 통해 최신 그래픽 API를 사용하여 Pure C의 최소 효율적인 크로스 플랫폼 2D 그래픽 화가.
Sokol GP 또는 짧은 SGP는 Sokol Graphics Painter를 나타냅니다.
Sokol GFX는 최신 그래픽 카드의 고정되지 않은 파이프 라인을 사용하여 렌더링하기위한 훌륭한 라이브러리이지만 간단한 2D 도면에 사용하기에는 너무 복잡하며 API는 너무 일반적이고 3D 렌더링에 특화되어 있습니다. 2D 물건을 그리려면 프로그래머는 일반적으로 Sokol GFX를 사용할 때 사용자 정의 셰이더를 설정하거나 Sokol GL Extra Library를 사용해야하지만 Sokol GL에는 3D 디자인이있는 API가있어 일부 비용과 제한 사항이 발생합니다.
이 라이브러리는 Sokol GFX를 통해 2D 프리미티브를 쉽게 그릴 수 있도록 만들어졌으며 3D 사용법을 고려하지 않으면 2D 렌더링에만 최적화되며 자동 배치 최적화기가 특징입니다. 자세한 내용은 아래에 설명되어 있습니다.
라이브러리를 그리면 아직 그려지지 않은 모든 프리미티브의 드로우 명령 큐를 생성 할 때, 새로운 드로우 명령이 추가 될 때마다 배치 옵티마이저는 마지막 8 개의 최근 드로우 명령 (조정 가능)을 다시 볼 수 있으며 다음 기준을 충족하는 이전 드로우 명령을 찾으면 드로잉 명령을 재 배열하고 병합하려고 시도합니다.
이 작업을 수행함으로써 배치 옵티마이저는 예를 들어 다른 중개 텍스처가 그 사이에 그려져있는 경우에도 텍스처링 된 드로우 호출을 병합 할 수 있습니다. 덜 드로우 호출이 GPU로 발송되기 때문에 그 효과가 더 효율성이 높습니다.
이 라이브러리는 런타임에 무대 뒤에서 드로우 호출을 자동으로 병합함으로써 효율적인 2D 드로잉 배치 시스템을 만드는 많은 작업을 피할 수 있으므로 프로그래머는 배치 된 드로우 호출을 수동으로 관리 할 필요가 없으며 배치 된 텍스처 드로우 호출을 정렬 할 필요가 없으며, 라이브러리는 무대 뒤에서이 작업을 원활하게 수행 할 필요가 있습니다.
배치 알고리즘은 빠르지 만 모든 새로운 DRAW 명령에 대해 O(n) CPU 복잡성이 있으며, 여기서 n SGP_BATCH_OPTIMIZER_DEPTH 구성입니다. 기본값으로 8 사용하는 실험에서는 기본값이 좋지만 사례에 따라 다른 값을 시험해 볼 수 있습니다. 알고리즘이 이전 드로우 명령을 너무 길게 스캔하고 더 많은 CPU 리소스를 소비 할 수 있기 때문에 너무 높은 값을 사용하는 것은 권장되지 않습니다.
SGP_BATCH_OPTIMIZER_DEPTH 0으로 설정하여 배치 최적화기를 비활성화 할 수 있습니다.이를 사용하여 영향을 측정 할 수 있습니다.
이 저장소의 샘플 디렉토리에는 Bath Optimizer 활성화/비활성화로 테스트하는 벤치 마크 예제가 있습니다. 내 컴퓨터에서 벤치 마크는 활성화 될 때 2.2 배의 성능을 향상시킬 수있었습니다. 일부 개인 게임 프로젝트에서는 배치 옵티마이저이저의 이익은 게임 자체에 대한 내부 변경없이 그래픽 백엔드를이 라이브러리로 대체함으로써 FPS 성능을 1.5 배 이상 증가시키는 것으로 입증되었습니다.
도서관에는 성능을 염두에두고 디자인 선택이 있습니다.
Sokol GFX와 마찬가지로 Sokol GP는 드로우 루프에서 할당을 수행하지 않으므로 초기화 할 때는 드로우 명령 큐 버퍼 및 정점 버퍼의 최대 크기를 미리 구성해야합니다.
모든 2D 공간 변환 ( sgp_rotate 와 같은 함수)은 CPU에 의해 수행되며 GPU에 의해 수행됩니다. GPU에 추가 오버 헤드를 추가하는 것을 피하는 것입니다. 일반적으로 2D 애플리케이션의 정점 수는 크기가 크지 않기 때문에 CPU로 모든 변환을 수행하는 것이 더 효율적이기 때문입니다. CPU <-> GPU 버스. 대조적으로 3D 애플리케이션은 일반적으로 정점 셰이더를 사용하여 GPU로 정점 변환을 발산합니다. 3D 객체의 정점의 양은 매우 클 수 있으며 일반적으로 최상의 선택이지만 2D 렌더링에는 해당되지 않습니다.
원시를 그리기 전에 2D 공간을 변환하기위한 많은 API가 번역, 회전 및 스케일과 같이 사용할 수 있습니다. 3D 그래픽 API에서 사용 가능한 것과 유사하게 사용할 수 있지만 2D를 위해 제작됩니다. 예를 들어 2D를 사용할 때는 4x4 또는 3x3 매트릭스를 사용하여 정점 변환을 수행 할 필요가 없으며 대신 코드가 2D에 특수화되어 추가 CPU 플로트 컴퓨팅을 저장할 수 있습니다.
모든 파이프 라인은 텍스처가 아닌 프리미티브를 그리는 경우에도 항상 텍스처와 관련된 텍스처를 사용합니다. 이렇게하면 텍스처 호출과 텍스처가 아닌 통화를 혼합 할 때 그래픽 파이프 라인이 변경되어 효율성이 향상되기 때문입니다.
라이브러리는 Sokol GFX 헤더 스타일로 코딩되어 많은 매크로를 재사용하여 사용자 정의 할당 자, 사용자 정의 로그 함수 및 기타 세부 사항과 같은 의미를 변경할 sokol_gfx.h 있습니다.
다른 sokol 헤더와 함께 sokol_gp.h 동일한 폴더로 복사하십시오. Sokol GFX를 일반적으로 설정 한 다음 sgp_setup(desc) 직후에 sg_setup(desc) 에 호출을 추가 한 다음 sgp_shutdown() 직전에 sg_shutdown() 로 호출하십시오. sgp_is_valid() 로 생성 후 SGP가 유효한 지 확인하고 오류가 있으면 우아하게 종료해야합니다.
프레임 드로우 함수에서 SGP Draw 함수를 호출하기 전에 sgp_begin(width, height) 추가 한 다음 프리미티브를 그립니다. 프레임의 끝 (또는 프레임 버퍼)에서는 항상 sokol gfx 시작/종료 렌더 패스 사이에서 sgp_flush() 호출해야합니다. sgp_flush() 는 모든 드로우 명령을 Sokol GFX로 발송합니다. 그런 다음 sgp_end() 즉시 호출하여 Draw 명령 큐를 폐기하십시오.
이 설정의 실제 예는 다음과 같습니다.
다음은 Sokol GFX 및 Sokol 앱을 사용 하여이 라이브러리에 대한 방법에 대한 간단한 예입니다.
// This is an example on how to set up and use Sokol GP to draw a filled rectangle.
// Includes Sokol GFX, Sokol GP and Sokol APP, doing all implementations.
#define SOKOL_IMPL
#include "sokol_gfx.h"
#include "sokol_gp.h"
#include "sokol_app.h"
#include "sokol_glue.h"
#include "sokol_log.h"
#include <stdio.h> // for fprintf()
#include <stdlib.h> // for exit()
#include <math.h> // for sinf() and cosf()
// Called on every frame of the application.
static void frame ( void ) {
// Get current window size.
int width = sapp_width (), height = sapp_height ();
float ratio = width /( float ) height ;
// Begin recording draw commands for a frame buffer of size (width, height).
sgp_begin ( width , height );
// Set frame buffer drawing region to (0,0,width,height).
sgp_viewport ( 0 , 0 , width , height );
// Set drawing coordinate space to (left=-ratio, right=ratio, top=1, bottom=-1).
sgp_project ( - ratio , ratio , 1.0f , -1.0f );
// Clear the frame buffer.
sgp_set_color ( 0.1f , 0.1f , 0.1f , 1.0f );
sgp_clear ();
// Draw an animated rectangle that rotates and changes its colors.
float time = sapp_frame_count () * sapp_frame_duration ();
float r = sinf ( time ) * 0.5 + 0.5 , g = cosf ( time ) * 0.5 + 0.5 ;
sgp_set_color ( r , g , 0.3f , 1.0f );
sgp_rotate_at ( time , 0.0f , 0.0f );
sgp_draw_filled_rect ( -0.5f , -0.5f , 1.0f , 1.0f );
// Begin a render pass.
sg_pass pass = {. swapchain = sglue_swapchain ()};
sg_begin_pass ( & pass );
// Dispatch all draw commands to Sokol GFX.
sgp_flush ();
// Finish a draw command queue, clearing it.
sgp_end ();
// End render pass.
sg_end_pass ();
// Commit Sokol render.
sg_commit ();
}
// Called when the application is initializing.
static void init ( void ) {
// Initialize Sokol GFX.
sg_desc sgdesc = {
. environment = sglue_environment (),
. logger . func = slog_func
};
sg_setup ( & sgdesc );
if (! sg_isvalid ()) {
fprintf ( stderr , "Failed to create Sokol GFX context!n" );
exit ( -1 );
}
// Initialize Sokol GP, adjust the size of command buffers for your own use.
sgp_desc sgpdesc = { 0 };
sgp_setup ( & sgpdesc );
if (! sgp_is_valid ()) {
fprintf ( stderr , "Failed to create Sokol GP context: %sn" , sgp_get_error_message ( sgp_get_last_error ()));
exit ( -1 );
}
}
// Called when the application is shutting down.
static void cleanup ( void ) {
// Cleanup Sokol GP and Sokol GFX resources.
sgp_shutdown ();
sg_shutdown ();
}
// Implement application main through Sokol APP.
sapp_desc sokol_main ( int argc , char * argv []) {
( void ) argc ;
( void ) argv ;
return ( sapp_desc ){
. init_cb = init ,
. frame_cb = frame ,
. cleanup_cb = cleanup ,
. window_title = "Rectangle (Sokol GP)" ,
. logger . func = slog_func ,
};
} 이 예제를 실행하려면 먼저 다른 Sokol 헤더와 함께 sokol_gp.h 헤더를 동일한 폴더로 복사 한 다음 적절한 링크 플래그 ( sokol_gfx.h 참조)를 사용하여 모든 C 컴파일러로 컴파일하십시오.
폴더 samples 에서 라이브러리의 모든 API를 다루는 다음 완전한 예를 찾을 수 있습니다.
sgp_begin() 사용하는 방법을 보여주는 예입니다. 이 예제는 라이브러리의 테스트 스위트로 사용되며 make 입력하여 빌드 할 수 있습니다.
많은 드로우를 호출 한 후 명령 또는 정점 버퍼가 오버 플로우가 오버 플로우를 옮길 수 있습니다.이 경우 라이브러리는 오류 오류 상태를 설정하고 정상적으로 작동하지만 sgp_flush() 로 드로잉 명령 큐를 플러시 할 때는 드로우 명령이 발송되지 않습니다. 라이브러리는 사전 할당 된 버퍼를 사용하기 때문에 발생할 수 있습니다. 이러한 경우 sgp_setup() 호출 할 때 접두사가있는 명령 큐 버퍼와 정점 버퍼를 증가시켜 문제를 해결할 수 있습니다.
sgp_push_transform() 및 sgp_pop_transform() 의 푸시/팝을 잘못 만들거나 너무 많은 sgp_begin() 및 sgp_end() 를 중첩하면 오류가 발생할 수 있습니다. 즉, 사용 실수입니다.
sgp_end() 호출 한 후 sgp_get_last_error() 읽어서 디버그하거나 프로그래밍 방식으로 오류를 처리 할 수 있도록 SOKOL_DEBUG 매크로를 활성화 할 수 있습니다. 또한 Sokol을 개발할 때 SOKOL_DEBUG 가능하게하여 실수를 일찍 잡을 수 있습니다.
라이브러리는 2D에 사용되는 가장 일반적인 블렌드 모드를 지원합니다.
SGP_BLENDMODE_NONE 블렌딩 없음 ( dstRGBA = srcRGBA ).SGP_BLENDMODE_BLEND 알파 블렌딩 ( dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA)) 및 dstA = srcA + (dstA * (1-srcA)) )SGP_BLENDMODE_BLEND_PREMULTIPLIED 사전 다기성 알파 블렌딩 ( dstRGBA = srcRGBA + (dstRGBA * (1-srcA)) )SGP_BLENDMODE_ADD 첨가제 블렌딩 ( dstRGB = (srcRGB * srcA) + dstRGB 및 dstA = dstA )SGP_BLENDMODE_ADD_PREMULTIPLIED 사전 연습 첨가제 블렌딩 ( dstRGB = srcRGB + dstRGB 및 dstA = dstA )SGP_BLENDMODE_MOD -color modulate ( dstRGB = srcRGB * dstRGB 및 dstA = dstA )SGP_BLENDMODE_MUL 색상 곱하기 ( dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA)) 및 dstA = (srcA * dstA) + (dstA * (1-srcA)) ) sgp_viewport(x, y, width, height) 호출하여 스크린 영역을 변경할 수 있습니다. sgp_project(left, right, top, bottom) 호출하여 2D 공간의 좌표계를 변경할 수 있습니다.
sgp_translate(x, y) , sgp_rotate(theta) 등과 같이 라이브러리가 제공하는 변환 함수를 사용하여 드로우 호출 전에 2D 공간을 변환, 회전 또는 스케일링 할 수 있습니다. 치트 시트 또는 헤더에 더 많은 정보를 확인하십시오.
변환 상태를 저장하고 복원하려면 sgp_push_transform() 과 나중에 sgp_pop_transform() 호출해야합니다.
라이브러리는 모든 기본 프리미티브, 즉 sgp_draw_line() 및 sgp_draw_filled_rect() 와 같은 포인트, 선, 삼각형 및 사각형에 대한 드로잉 기능을 제공합니다. 치트 시트 나 헤더에 자세한 내용을 확인하십시오. 그들 모두는 배치 된 변형을 가지고 있습니다.
텍스처 직사각형을 그리려면 sgp_set_image(0, img) 사용할 수 있고 sgp_draw_filled_rect() 사용할 수 있으면 전체 텍스처를 사각형으로 그립니다. 나중에 sgp_reset_image(0) 로 이미지를 재설정하여 바인딩 이미지를 기본 흰색 이미지로 복원해야합니다. 그렇지 않으면 단색을 그리면 결함이 생깁니다.
텍스처에서 특정 소스를 그려 보려면 대신 sgp_draw_textured_rect() 사용해야합니다.
기본적으로 간단한 가장 가까운 필터 샘플러를 사용하여 그려지면 sgp_reset_sampler(0) sgp_set_sampler(0, smp) 로 샘플러를 변경할 수 있습니다.
모든 일반적인 파이프 라인에는 색 변조가 있으며 sgp_set_color(r,g,b,a) 로 현재 상태 색상을 설정하여 드로우하기 전에 색상을 변조 할 수 있으며 나중에 sgp_reset_color() 를 사용하여 색상을 기본값 (흰색)으로 재설정해야합니다.
사용자 정의 셰이더를 사용하는 경우 셰이더, 블렌드 모드 및 그와 관련된 드로우 프리미티브를 사용하여 sgp_make_pipeline(desc) 으로 파이프 라인을 만들어야합니다. 그런 다음 셰이더 드로우 호출 전에 sgp_set_pipeline() 호출해야합니다. 동일한 블렌드 모드를 사용하고 생성 된 파이프 라인과 프리미티브를 그리는 책임이 있습니다.
맞춤형 유니폼은 sgp_set_uniform(vs_data, vs_size, fs_data, fs_size) 사용하여 셰이더로 전달할 수 있으며, 여기서 정점 및 조각 셰이더에 정의 된 것과 정확히 동일한 스키마와 크기를 가진 구조물에 대한 포인터를 항상 전달해야합니다.
각 그래픽 백엔드에 대한 사용자 정의 셰이더를 수동으로 만들 수 있지만 Sokol Shader Compiler SHDC를 사용해야합니다. 단일 .glsl 파일에서 여러 백엔드에 대한 셰이더를 생성 할 수 있으므로 일반적으로 잘 작동합니다.
기본적으로 도서관 균일 한 버퍼는 콜에 8 개의 플로트 유니폼 ( SGP_UNIFORM_CONTENT_SLOTS 구성)이 있으며 사용자 정의 셰이더와 함께 사용하기에는 너무 낮을 수 있습니다. 일반적으로 신규 이민자가 사용자 정의 2D 셰이더를 사용하고 싶지 않을 수도 있고 더 큰 값을 높이면 더 많은 오버 헤드를 의미하기 때문에 기본값입니다. 사용자 정의 셰이더를 사용하는 경우이 값이 가장 큰 셰이더의 유니폼 수를 유지할 수있을 정도로이 값을 크게 증가시킵니다.
라이브러리 동작을 변경하기 전에 다음의 매크로를 정의 할 수 있습니다.
SGP_BATCH_OPTIMIZER_DEPTH 배치 최적화기가 되돌아 보는 드로우 명령 수. 기본값은 8입니다.SGP_UNIFORM_CONTENT_SLOTS 각 드로우 콜라스 균일 버퍼에 저장할 수있는 최대 플로트 수. 기본값은 8입니다.SGP_TEXTURE_SLOTS 추첨 당 결속 될 수있는 최대 텍스처 수. 기본값은 4입니다. MIT, 라이센스 파일 또는 sokol_gp.h 파일의 끝을 참조하십시오.
다음은 빠른 참조를위한 모든 라이브러리 기능의 빠른 목록입니다.
/* Initialization and de-initialization. */
void sgp_setup ( const sgp_desc * desc ); /* Initializes the SGP context, and should be called after `sg_setup`. */
void sgp_shutdown ( void ); /* Destroys the SGP context. */
bool sgp_is_valid ( void ); /* Checks if SGP context is valid, should be checked after `sgp_setup`. */
/* Error handling. */
sgp_error sgp_get_last_error ( void ); /* Returns last SGP error. */
const char * sgp_get_error_message ( sgp_error error ); /* Returns a message with SGP error description. */
/* Custom pipeline creation. */
sg_pipeline sgp_make_pipeline ( const sgp_pipeline_desc * desc ); /* Creates a custom shader pipeline to be used with SGP. */
/* Draw command queue management. */
void sgp_begin ( int width , int height ); /* Begins a new SGP draw command queue. */
void sgp_flush ( void ); /* Dispatch current Sokol GFX draw commands. */
void sgp_end ( void ); /* End current draw command queue, discarding it. */
/* 2D coordinate space projection */
void sgp_project ( float left , float right , float top , float bottom ); /* Set the coordinate space boundary in the current viewport. */
void sgp_reset_project ( void ); /* Resets the coordinate space to default (coordinate of the viewport). */
/* 2D coordinate space transformation. */
void sgp_push_transform ( void ); /* Saves current transform matrix, to be restored later with a pop. */
void sgp_pop_transform ( void ); /* Restore transform matrix to the same value of the last push. */
void sgp_reset_transform ( void ); /* Resets the transform matrix to identity (no transform). */
void sgp_translate ( float x , float y ); /* Translates the 2D coordinate space. */
void sgp_rotate ( float theta ); /* Rotates the 2D coordinate space around the origin. */
void sgp_rotate_at ( float theta , float x , float y ); /* Rotates the 2D coordinate space around a point. */
void sgp_scale ( float sx , float sy ); /* Scales the 2D coordinate space around the origin. */
void sgp_scale_at ( float sx , float sy , float x , float y ); /* Scales the 2D coordinate space around a point. */
/* State change for custom pipelines. */
void sgp_set_pipeline ( sg_pipeline pipeline ); /* Sets current draw pipeline. */
void sgp_reset_pipeline ( void ); /* Resets to the current draw pipeline to default (builtin pipelines). */
void sgp_set_uniform ( const void * vs_data , uint32_t vs_size , const void * fs_data , uint32_t fs_size ); /* Sets uniform buffer for a custom pipeline. */
void sgp_reset_uniform ( void ); /* Resets uniform buffer to default (current state color). */
/* State change functions for the common pipelines. */
void sgp_set_blend_mode ( sgp_blend_mode blend_mode ); /* Sets current blend mode. */
void sgp_reset_blend_mode ( void ); /* Resets current blend mode to default (no blending). */
void sgp_set_color ( float r , float g , float b , float a ); /* Sets current color modulation. */
void sgp_reset_color ( void ); /* Resets current color modulation to default (white). */
void sgp_set_image ( int channel , sg_image image ); /* Sets current bound image in a texture channel. */
void sgp_unset_image ( int channel ); /* Remove current bound image in a texture channel (no texture). */
void sgp_reset_image ( int channel ); /* Resets current bound image in a texture channel to default (white texture). */
void sgp_set_sampler ( int channel , sg_sampler sampler ); /* Sets current bound sampler in a texture channel. */
void sgp_unset_sampler ( int channel ); /* Remove current bound sampler in a texture channel (no sampler). */
void sgp_reset_sampler ( int channel ); /* Resets current bound sampler in a texture channel to default (nearest sampler). */
/* State change functions for all pipelines. */
void sgp_viewport ( int x , int y , int w , int h ); /* Sets the screen area to draw into. */
void sgp_reset_viewport ( void ); /* Reset viewport to default values (0, 0, width, height). */
void sgp_scissor ( int x , int y , int w , int h ); /* Set clip rectangle in the viewport. */
void sgp_reset_scissor ( void ); /* Resets clip rectangle to default (viewport bounds). */
void sgp_reset_state ( void ); /* Reset all state to default values. */
/* Drawing functions. */
void sgp_clear ( void ); /* Clears the current viewport using the current state color. */
void sgp_draw ( sg_primitive_type primitive_type , const sgp_vertex * vertices , uint32_t count ); /* Low level drawing function, capable of drawing any primitive. */
void sgp_draw_points ( const sgp_point * points , uint32_t count ); /* Draws points in a batch. */
void sgp_draw_point ( float x , float y ); /* Draws a single point. */
void sgp_draw_lines ( const sgp_line * lines , uint32_t count ); /* Draws lines in a batch. */
void sgp_draw_line ( float ax , float ay , float bx , float by ); /* Draws a single line. */
void sgp_draw_lines_strip ( const sgp_point * points , uint32_t count ); /* Draws a strip of lines. */
void sgp_draw_filled_triangles ( const sgp_triangle * triangles , uint32_t count ); /* Draws triangles in a batch. */
void sgp_draw_filled_triangle ( float ax , float ay , float bx , float by , float cx , float cy ); /* Draws a single triangle. */
void sgp_draw_filled_triangles_strip ( const sgp_point * points , uint32_t count ); /* Draws strip of triangles. */
void sgp_draw_filled_rects ( const sgp_rect * rects , uint32_t count ); /* Draws a batch of rectangles. */
void sgp_draw_filled_rect ( float x , float y , float w , float h ); /* Draws a single rectangle. */
void sgp_draw_textured_rects ( int channel , const sgp_textured_rect * rects , uint32_t count ); /* Draws a batch textured rectangle, each from a source region. */
void sgp_draw_textured_rect ( int channel , sgp_rect dest_rect , sgp_rect src_rect ); /* Draws a single textured rectangle from a source region. */
/* Querying functions. */
sgp_state * sgp_query_state ( void ); /* Returns the current draw state. */
sgp_desc sgp_query_desc ( void ); /* Returns description of the current SGP context. */ 이 라이브러리는 2020 년부터 개인 프로젝트에서 테스트되었으며 안정적으로 입증되었습니다.
이 라이브러리는 원래 MMORPG 게임 Medivia Online에서 후원했습니다. 저의 작품을 지원해 주셔서 감사합니다.
다른 색상 기능을 갖춘 배치 드로우 호출을 후원 해 주신 @kkukshtel에게 감사드립니다.
@floooh의 우수한 Sokol 프로젝트를 확인하십시오. 게임 개발에 사용할 수있는 품질로 만든 유용한 단일 헤더 C 라이브러리가 많이 있습니다.
다른 싱글 헤더 C 라이브러리 미니 코로를 확인할 수도 있습니다. 게임 데블 로프에서 유한 상태 머신을 단순화하는 데 매우 유용한 C에 대한 스택 형 코 루틴을 가져옵니다.
sgp_set_uniform API의 차단이 발생하여 새로운 사전 모형 블렌드 모드가 추가되었습니다. 다음은 samples 디렉토리의 모든 샘플의 스크린 샷입니다. 브라우저에서 실제로 실시간으로 렌더링되는 이미지를 보려면 이미지를 클릭하십시오.
프리미티브 샘플 :
혼합 모드 샘플 :
프레임 버퍼 샘플 :
사각형 샘플 :
효과 샘플 :
SDF 샘플 :
저는 풀 타임 오픈 소스 개발자입니다. GitHub를 통한 기부금은 모두 감사하게 될 것이며이 오픈 소스 프로젝트 및 기타 오픈 소스 프로젝트를 계속 지원하도록 격려 할 수 있습니다. 프로젝트 목표와 일치하는 작은 기능 또는 사소한 개선 사항에 대한 일회성 후원을 수락 할 수 있습니다.이 경우 저에게 연락하십시오.