{"id":3219,"date":"2026-08-29T20:27:56","date_gmt":"2026-08-29T20:27:56","guid":{"rendered":"https:\/\/yazilimperver.com\/?p=3219"},"modified":"2026-08-29T20:28:44","modified_gmt":"2026-08-29T20:28:44","slug":"sdl-painter-temel-sekiller-ve-kabiliyetler-faz-1","status":"publish","type":"post","link":"https:\/\/yazilimperver.com\/index.php\/2026\/08\/29\/sdl-painter-temel-sekiller-ve-kabiliyetler-faz-1\/","title":{"rendered":"sdl-painter \u2013 Temel \u015eekiller ve Kabiliyetler (faz-1)"},"content":{"rendered":"<p dir=\"auto\" data-line=\"17\">Sevgili yaz\u0131l\u0131mperver dostlar\u0131m, tekrar merhaba. Bu zaman zarf\u0131nda, gerek k\u00fct\u00fcphane gerekse siteme dair bir \u00e7ok de\u011fi\u015fiklik oldu \ud83d\ude42 Bunlar\u0131, ilgili yerlerde k\u0131sa notlarla belirtece\u011fim; hangi tasar\u0131m\u0131n ilk h\u00e2liyle kald\u0131\u011f\u0131 ve hangisinin sonradan de\u011fi\u015fti\u011fi.<\/p>\n<p dir=\"auto\" data-line=\"17\">\u00d6ncelikle sitemin hostingini de\u011fi\u015ftirdim ve art\u0131k daha h\u0131zl\u0131. K\u00fct\u00fcphaneye ise bir \u00e7ok yeni kabiliyet ekledim, bunlara zaten yak\u0131nda de\u011finece\u011fim. Neyse \u015fimdi konumuza geri d\u00f6nelim. Serinin \u00f6nceki yaz\u0131lar\u0131nda, CMake, Conan ve <code>IRenderer<\/code> aray\u00fcz\u00fcyle projenin temelleri \u00fczerinden ge\u00e7tik. Shader derleniyor, VAO\/VBO haz\u0131rlan\u0131yordu ama ekrana hen\u00fcz hi\u00e7bir \u015fey \u00e7izilmiyordu. Sonras\u0131nda da CI\/CD ve kod i\u00e7i dok\u00fcmantasyon kabiliyetlerine bakt\u0131k.<\/p>\n<p dir=\"auto\" data-line=\"17\">Bu yaz\u0131mda \u015fu konulara g\u00f6z ataca\u011f\u0131z: OpenGL backend&#8217;i, backend ba\u011f\u0131ms\u0131z<code>Tessellator<\/code>\u00a0mimarisi ve\u00a0<code>glLineWidth<\/code> yerine neden quad geometrisi kulland\u0131\u011f\u0131m\u0131z. Sonunda konkav poligonlar i\u00e7in ear clipping ve \u00e7izimleri toparlayan <code>RenderBatcher<\/code> ile tamamlayaca\u011f\u0131z.<\/p>\n<h2 dir=\"auto\" data-line=\"17\"><span style=\"color: #0000ff;\"><strong>OpenGL 3.3 Core Backend<\/strong><\/span><\/h2>\n<p><code>OpenGLRenderer<\/code>,\u00a0<code>IRenderer<\/code> aray\u00fcz\u00fcn\u00fc implement eden s\u0131n\u0131f\u0131m\u0131z. <code>Initialize<\/code> \u00fc\u00e7 i\u015f yap\u0131yor: context olu\u015ftur, GLAD&#8217;i y\u00fckler ve shader&#8217;lar\u0131 derler.<\/p>\n<pre class=\"lang:c++ decode:true \">bool OpenGLRenderer::Initialize(SDL_Window* window) {\r\n  mWindow = window;\r\n\r\n  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);\r\n  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);\r\n  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);\r\n\r\n  mGLContext = SDL_GL_CreateContext(window);\r\n  if (mGLContext == nullptr) {\r\n    spdlog::error(\"[OpenGLRenderer] Failed to create GL context: {}\", SDL_GetError());\r\n    return false;\r\n  }\r\n\r\n  if (gladLoadGLLoader(reinterpret_cast&lt;GLADloadproc&gt;(SDL_GL_GetProcAddress)) == 0) {\r\n    spdlog::error(\"[OpenGLRenderer] Failed to load GLAD\");\r\n    return false;\r\n  }\r\n\r\n  \/\/ Baslangic karistirma durumu tek kaynaktan: SetBlendMode.\r\n  SetBlendMode(BlendMode::kAlpha);\r\n\r\n  \/\/ Shader kaynaklari binary'ye gomulu; calisma zamaninda dosya aranmaz.\r\n  if (!mBasicShader.Build(detail::kBasicVert, detail::kBasicFrag)) {\r\n    return false;\r\n  }\r\n  \/\/ ...\r\n  SetupBuffers();\r\n  return true;\r\n}<\/pre>\n<p>Burada ilk g\u00fcncellemeye bak\u0131yor olaca\u011f\u0131z. <strong>Shader&#8217;lar art\u0131k diskten okunmuyor.<\/strong> Faz 1&#8217;de shader dosyalar\u0131 exe&#8217;nin yan\u0131ndaki <code>shaders\/<\/code>\u00a0klas\u00f6r\u00fcnden (<code>SDL_GetBasePath()<\/code>) ya da derleme s\u0131ras\u0131nda g\u00f6m\u00fclen bir kaynak dizini makrosundan y\u00fckl\u00fcyorduk. Bu asl\u0131nda, repo i\u00e7inde \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131z m\u00fcddet\u00e7e bir s\u0131k\u0131nt\u0131 yaratmaz, ama bunun d\u0131\u015f\u0131ndaki durumlarda probleme yol a\u00e7ar: <code>SDL_GetBasePath()<\/code>\u00a0<strong>t\u00fcketicinin<\/strong> exe dizinini d\u00f6nd\u00fcr\u00fcr, yani k\u00fct\u00fcphaneyi paket olarak alan herkesin shader dosyalar\u0131n\u0131 elle kopyalamas\u0131 gerekirdi. \u015eimdi burada paket olay\u0131 nereden \u00e7\u0131kt\u0131 diyebilirsiniz. O da k\u00fct\u00fcphaneyi, nihai olarak bir Conan paketi olarak kullanma vizyonundan geliyor.<\/p>\n<p>Derleme makinesinin mutlak yolunun pakete de girmesi, Conan Center&#8217;\u0131n relocatable paket kural\u0131na uymuyor. Bu konuya, ADR-009&#8217;da de\u011findik. Bu sayede, GLSL kaynaklar\u0131 art\u0131k ham string literal, SPIR-V ise <code>uint32_t<\/code> dizisi olarak binary&#8217;ye\u00a0 g\u00f6m\u00fcl\u00fcyor. <code>ShaderProgram::Build(vert_src, frag_src)<\/code> API&#8217;sini ise de\u011fi\u015ftirmeye gerek kalmad\u0131 (zaten kaynaktan derliyor).<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"65\"><strong>Blend durumu tek yerden kuruluyor.<\/strong>\u00a0Burada eskiden ayr\u0131 bir\u00a0<code>glBlendFunc<\/code> \u00e7a\u011fr\u0131s\u0131 vard\u0131.\u00a0 Bu da OpenGL ile Vulkan aras\u0131nda bir ayr\u0131\u015fmaya sebebiyet veriyordu, bu sebeple art\u0131k buras\u0131, <code>SetBlendMode<\/code> \u00fczerinden ayarlan\u0131yor. Detaylara ilgili dosyadan bakabilirsiniz.<\/p>\n<h2 id=\"vao-ve-vbo\" class=\"code-line\" dir=\"auto\" data-line=\"67\"><strong><span style=\"color: #0000ff;\">VAO ve VBO<\/span><\/strong><\/h2>\n<p dir=\"auto\" data-line=\"65\"><code>SetupBuffers<\/code> \u00e7izimlerde kullanmak i\u00e7in iki VAO\/VBO \u00e7ifti olu\u015fturuyor: biri d\u00fcz renkli vertex&#8217;ler, biri texture koordinatl\u0131 olanlar i\u00e7in.<\/p>\n<pre class=\"lang:c++ decode:true \">glBindVertexArray(mVao);\r\nglBindBuffer(GL_ARRAY_BUFFER, mVbo);\r\n\r\n\/\/ Location 0: pozisyon (2x float)\r\nglEnableVertexAttribArray(0);\r\nglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),\r\n                      reinterpret_cast&lt;void*&gt;(static_cast&lt;uintptr_t&gt;(0)));\r\n\r\n\/\/ Location 1: renk (4x uint8; GL_TRUE ile [0,255] -&gt; [0,1] normalize edilir)\r\nglEnableVertexAttribArray(1);\r\nglVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex),\r\n                      reinterpret_cast&lt;void*&gt;(static_cast&lt;uintptr_t&gt;(2 * sizeof(float))));<\/pre>\n<p dir=\"auto\" data-line=\"65\"><code>Vertex<\/code> basit bir yap\u0131: iki float pozisyon, d\u00f6rt byte renk. Rengin uniform yerine vertex&#8217;te ta\u015f\u0131nmas\u0131 da burada bilin\u00e7li olarak yap\u0131ld\u0131, neden mi?Batch&#8217;leme b\u00f6l\u00fcm\u00fcnde g\u00f6rece\u011fiz.<\/p>\n<h2 id=\"vao-ve-vbo\" class=\"code-line\" dir=\"auto\" data-line=\"67\"><strong><span style=\"color: #0000ff;\">Shader Tasar\u0131m\u0131<\/span><\/strong><\/h2>\n<p>\u015eu an i\u00e7in iki shader \u00e7iftimiz var: <code>basic<\/code>\u00a0(d\u00fcz renkli \u015fekiller) ve\u00a0<code>textured<\/code> (imaj\/dokular, faz 3&#8217;de bak\u0131yor olaca\u011f\u0131z).<\/p>\n<pre class=\"lang:c++ decode:true\">\/\/ basic.vert\r\n#version 330 core\r\nlayout(location = 0) in vec2 a_position;\r\nlayout(location = 1) in vec4 a_color;\r\n\r\nuniform mat3 u_model;\r\nuniform mat4 u_projection;\r\n\r\nout vec4 v_color;\r\n\r\nvoid main() {\r\n    vec3 transformed = u_model * vec3(a_position, 1.0);\r\n    gl_Position = u_projection * vec4(transformed.xy, 0.0, 1.0);\r\n    v_color = a_color;\r\n}<\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"114\">Fragment shader&#8217;\u0131n tek bir i\u015fi var: vertex renginin alfas\u0131n\u0131 global <code>u_opacity<\/code>\u00a0ile \u00e7arp\u0131p yazmak.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"116\">Faz 1&#8217;deki ak\u0131\u015f ise \u015fuydu: <code>Painter<\/code>\u00a0her \u00e7izimden \u00f6nce 3&#215;3 affine matrisini\u00a0<code>SetModelMatrix<\/code> ile renderer&#8217;a yazar, shader ise onu k\u00f6\u015fe koordinatlar\u0131na uygular ve ortografik projeksiyon ekran koordinat\u0131na \u00e7evirirdi. <code>u_model<\/code> shader&#8217;da h\u00e2l\u00e2 duruyor ama art\u0131k her frame ba\u015f\u0131na bir kez, daima birim matris olarak yaz\u0131l\u0131yor. D\u00f6n\u00fc\u015f\u00fcm vertex&#8217;lere CPU taraf\u0131nda uygulan\u0131yor. Ekstra i\u015f gibi gelebilir ama faydas\u0131na yine batch&#8217;leme b\u00f6l\u00fcm\u00fcnde bakaca\u011f\u0131z. K\u0131saca: model matrisi bir uniform oldu\u011fu s\u00fcrece her <code>Rotate<\/code> \u00e7a\u011fr\u0131s\u0131 daha \u00f6nceden batch&#8217;i k\u0131r\u0131yor, performans kayb\u0131na sebebiyet veriyordu. Belki buna y\u00f6nelik ayr\u0131 bir yaz\u0131 da ekliyor olabiliriz.<\/p>\n<h2 id=\"tessellator-backendi-tan\u0131mayan-katman\" class=\"code-line\" dir=\"auto\" data-line=\"122\"><span style=\"color: #0000ff;\"><strong>Tessellator<\/strong><\/span><\/h2>\n<p>sdl-painter&#8217;\u0131n as\u0131l i\u015fi yapt\u0131\u011f\u0131 ve \u015fekilleri vertex listelerine \u00e7eviren kodun tamam\u0131\u00a0<code>Tessellator<\/code> s\u0131n\u0131f\u0131nda bulunuyor. ADR-004&#8217;teki karar\u0131n \u00f6z\u00fc tek c\u00fcmle: Tessellator renderer&#8217;\u0131 tan\u0131maz\/tan\u0131mamal\u0131 baba<strong>, <\/strong>sadece <strong><code>std::vector&lt;Vertex&gt;<\/code><\/strong>\u00fcretir.<\/p>\n<pre class=\"lang:c++ decode:true \">\/\/ Basit bir \u00e7ember \u00e7izimi i\u00e7in izlenen ak\u0131\u015f\r\nPainter::DrawCircle(cx, cy, radius)\r\n    |\r\n    v\r\nTessellator::TessellateFilledCircle(...)   -&gt; std::vector&lt;Vertex&gt;\r\n    |\r\n    v\r\nRenderBatcher (biriktirir, d\u00f6n\u00fc\u015f\u00fcm\u00fc uygular)\r\n    |\r\n    v\r\nIRenderer::DrawTriangles(vertices)  -&gt;  OpenGL veya Vulkan<\/pre>\n<p>Elbette bu soyutlama\/ayr\u0131m\u0131n bir bedeli var. Bu ayr\u0131m\u0131n bedeli tek bir vekt\u00f6r kopyas\u0131, kar\u015f\u0131l\u0131\u011f\u0131 ise Faz 5&#8217;te de g\u00f6rece\u011fimiz \u00fczere Vulkan backend&#8217;i eklerken tessellator dosyalar\u0131n\u0131 aynen kullanmak. Ayn\u0131 vertex verisi her iki\u00a0 (belki ileride daha fazla) backend&#8217;e de gidiyor.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"141\">S\u0131n\u0131f\u0131n t\u00fcm metotlar\u0131 statik ve hi\u00e7 state tutmuyoruz:<\/p>\n<pre class=\"lang:c++ decode:true \">class Tessellator {\r\n public:\r\n  static std::vector&lt;Vertex&gt; TessellateFilledCircle(float cx, float cy, float radius);\r\n  static std::vector&lt;Vertex&gt; TessellateThickLine(float x1, float y1,\r\n                                                 float x2, float y2,\r\n                                                 float line_width,\r\n                                                 LineCap cap = LineCap::kButt);\r\n  static std::vector&lt;Vertex&gt; TessellateFilledPolygon(const std::vector&lt;Point&gt;&amp; points);\r\n  \/\/ ...\r\n private:\r\n  static int32_t AdaptiveSegments(float radius);\r\n  static std::vector&lt;Vertex&gt; EarClipping(const std::vector&lt;Point&gt;&amp; points);\r\n};<\/pre>\n<p>Tahmin edebilece\u011finiz \u00fczere, \u015fekillere y\u00f6nelik vertexlerin tan\u0131mlanarak \u00fcretildi\u011fi yer bu s\u0131n\u0131f.<\/p>\n<p>Bu arada \/doc dizini alt\u0131nda da g\u00f6rebilece\u011finiz \u00fczere \u00f6rnek bir dikd\u00f6rtgen \u00e7izimi i\u00e7in \u015f\u00f6yle bir ak\u0131\u015f\u0131m\u0131z mevcut:<\/p>\n<p id=\"mSJaTrf\"><img loading=\"lazy\" decoding=\"async\" width=\"892\" height=\"891\" class=\"alignnone size-full wp-image-3225 \" src=\"https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e.png\" alt=\"\" srcset=\"https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e.png 892w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-300x300.png 300w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-150x150.png 150w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-768x767.png 768w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-500x499.png 500w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-400x400.png 400w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-800x799.png 800w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-200x200.png 200w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-57x57.png 57w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-72x72.png 72w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-114x114.png 114w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933f95ef12e-144x144.png 144w\" sizes=\"auto, (max-width: 892px) 100vw, 892px\" \/><\/p>\n<h2 id=\"daire-ve-elips-adaptif-segment-say\u0131s\u0131\" class=\"code-line\" dir=\"auto\" data-line=\"167\"><strong><span style=\"color: #0000ff;\">Daire ve Elips: Adaptif Segment Say\u0131s\u0131<\/span><\/strong><\/h2>\n<p>OpenGL&#8217;de ve SDL&#8217;de daire \u00e7izim API&#8217;leri bulunmaz. Genelde bu \u015fekiller segmentler kullan\u0131larak \u00e7izilir. Biz de k\u00fc\u00e7\u00fck bir daire i\u00e7in 16 segment kullanarak bunu olu\u015fturuyoruz, tabi bu sabit de\u011fil, <code>AdaptiveSegments<\/code> bunu yar\u0131\u00e7apa g\u00f6re ayarl\u0131yor:<\/p>\n<pre class=\"lang:c++ decode:true \">static int32_t AdaptiveSegments(float radius) {\r\n  constexpr int32_t kMinSegments = 16;\r\n  constexpr int32_t kMaxSegments = 512;\r\n  if (!(radius &gt; 0.0F)) {  \/\/ NaN ve negatif yaricap korumasi\r\n    return kMinSegments;\r\n  }\r\n  const float kScaled = radius * 0.5F;\r\n  if (kScaled &gt;= static_cast&lt;float&gt;(kMaxSegments)) {\r\n    return kMaxSegments;\r\n  }\r\n  return std::max(kMinSegments, static_cast&lt;int32_t&gt;(kScaled));\r\n}<\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"186\">Alt s\u0131n\u0131r g\u00f6rsel kalite i\u00e7in: 10 piksellik bir daire de 16 segment al\u0131yor. Ayr\u0131ca hata durumlar\u0131na kar\u015f\u0131 kontroller de mevcut.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"188\">Dolu daire ise bir triangle fan arac\u0131l\u0131\u011f\u0131yla: merkez + her segmentte iki \u00e7evre noktas\u0131 ile \u00e7izilir.<\/p>\n<pre class=\"lang:c++ decode:true \">int32_t segments = AdaptiveSegments(radius);\r\nresult.reserve(static_cast&lt;std::size_t&gt;(segments) * 3);\r\n\r\nfor (int32_t i = 0; i &lt; segments; ++i) {\r\n  float a0 = kTwoPi * static_cast&lt;float&gt;(i)     \/ static_cast&lt;float&gt;(segments);\r\n  float a1 = kTwoPi * static_cast&lt;float&gt;(i + 1) \/ static_cast&lt;float&gt;(segments);\r\n  result.emplace_back(cx, cy);  \/\/ merkez\r\n  result.emplace_back(cx + std::cos(a0) * radius, cy + std::sin(a0) * radius);\r\n  result.emplace_back(cx + std::cos(a1) * radius, cy + std::sin(a1) * radius);\r\n}<\/pre>\n<p>Elipste tek fark\u00a0<code>rx<\/code>\u00a0ve\u00a0<code>ry<\/code>&#8216;nin ayr\u0131 \u00f6l\u00e7eklenmesi; segment say\u0131s\u0131 ise b\u00fcy\u00fck yar\u0131 eksene g\u00f6re hesaplan\u0131yor.<\/p>\n<p>Buradaki yakla\u015f\u0131m yay, dilim ve benzeri \u00e7izimler de bu mant\u0131\u011f\u0131 takip eder. Bunlara da primitif \u00f6rne\u011finden bakabilirsiniz.<\/p>\n<h2 id=\"kal\u0131n-\u00e7izgiler-quad-yakla\u015f\u0131m\u0131\" class=\"code-line\" dir=\"auto\" data-line=\"211\"><span style=\"color: #0000ff;\">Kal\u0131n \u00c7izgiler: Quad Yakla\u015f\u0131m\u0131<\/span><\/h2>\n<p>sdl-painter bir di\u011fer \u00f6nemli farkl\u0131 \u00e7izgi \u00e7izimleri, daha \u00f6nceki uygulamalar\u0131mda hep glLineWidth kulland\u0131m, fakat bu genel hep ayn\u0131 sonuc vermiyor. Ayr\u0131ca <code>glLineWidth(x &gt; 1.0f)<\/code> Core Profile&#8217;da da deprecated olmu\u015f durumda yani pek \u00e7ok s\u00fcr\u00fcc\u00fcde maksimum de\u011fer 1 piksel \ud83d\ude42<\/p>\n<p>U\u00e7 ve birle\u015fim stili \u00fczerinde de hi\u00e7bir kontrol\u00fcm\u00fcz olmuyor. ADR-003&#8217;te bu hususu masaya yat\u0131rd\u0131k ve \u015f\u00f6yle karar ald\u0131k: <code>glLineWidth<\/code> hi\u00e7bir ko\u015fulda kullan\u0131lmayacak, her \u00e7izgi segmenti bir quad olarak tessellate edilecek.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"215\">Y\u00f6ntem basit: \u00e7izginin y\u00f6n\u00fcne dik normal vekt\u00f6r\u00fc bul, kal\u0131nl\u0131\u011f\u0131n yar\u0131s\u0131 kadar iki y\u00f6ne uygula, d\u00f6rt k\u00f6\u015feden iki \u00fc\u00e7gen \u00fcret.<\/p>\n<pre class=\"lang:c++ decode:true \">Orijinal \u00e7izgi: A -------------------- B\r\n\r\n    p0 --------------------- p2   (+half * n)\r\n    |                          |\r\n    |      quad (2 \u00fc\u00e7gen)      |\r\n    |                          |\r\n    p1 --------------------- p3   (-half * n)\r\n\r\nfloat dx = x2 - x1;\r\nfloat dy = y2 - y1;\r\nfloat len = std::sqrt(dx * dx + dy * dy);\r\nif (len &lt; 1e-6F) {\r\n  return {};  \/\/ dejenere cizgi korumasi\r\n}\r\n\r\nfloat nx = -dy \/ len;  \/\/ cizgi yonune dik, normalize\r\nfloat ny =  dx \/ len;\r\nfloat hw = line_width * 0.5F;\r\n\r\nstd::vector&lt;Vertex&gt; result = {\r\n    {x1 + hw * nx, y1 + hw * ny}, {x1 - hw * nx, y1 - hw * ny},\r\n    {x2 + hw * nx, y2 + hw * ny}, {x1 - hw * nx, y1 - hw * ny},\r\n    {x2 - hw * nx, y2 - hw * ny}, {x2 + hw * nx, y2 + hw * ny},\r\n};\r\n\r\nif (cap != LineCap::kButt) {  \/\/ uc stili: sonradan eklendi\r\n  AppendCap(result, Point{x1, y1}, -dx \/ len, -dy \/ len, hw, cap);\r\n  AppendCap(result, Point{x2, y2},  dx \/ len,  dy \/ len, hw, cap);\r\n}<\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"251\">Segmentler ba\u011f\u0131ms\u0131z quad&#8217;lar olarak \u00fcretildi\u011fi i\u00e7in her k\u00f6\u015fede kama bi\u00e7imli bir bo\u015fluk kal\u0131r. Buradaki k\u00f6\u015feye yuvarlak bir disk ekleyerek kapatt\u0131k, ve ard\u0131ndan <code>LineJoin<\/code> ile miter ve bevel se\u00e7eneklerini sunduk. Miter, <code>kMiterLimit<\/code> (4.0, SVG&#8217;nin varsay\u0131lan\u0131) a\u015f\u0131ld\u0131\u011f\u0131nda keskin a\u00e7\u0131larda sivri \u00e7\u0131k\u0131nt\u0131 \u00fcretmemek i\u00e7in kendili\u011finden bevel&#8217;a d\u00fc\u015f\u00fcyor. Bu kavramlar ne anlama geldi\u011fini a\u00e7\u0131klamak yerine san\u0131r\u0131m \u015fekil eklesem daha iyi olacak \ud83d\ude42<\/p>\n<p dir=\"auto\" data-line=\"251\"><img decoding=\"async\" src=\"https:\/\/encrypted-tbn0.gstatic.com\/images?q=tbn:ANd9GcRgtxNVZNcz2HYjM8RujZcUj20gjBYNekgJSa_I1-Vp_A&amp;s=10\" alt=\"Variable-Width Line Ends and Line Joins\" \/><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"253\">U\u00e7 stili yaln\u0131zca\u00a0<strong>a\u00e7\u0131k<\/strong> geometriye uygulan\u0131yor bu arada. Yani: <code>DrawLine<\/code>\u00a0ve\u00a0<code>DrawPolyline<\/code>, orada da sadece iki u\u00e7 noktaya. Kapal\u0131 \u015fekillerin (dikd\u00f6rtgen, daire, poligon \u00e7er\u00e7evesi) ucu olmuyor, yaln\u0131zca birle\u015fimi var.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"253\">Primitif uygulamas\u0131ndaki \u00f6rneklerimiz ise \u015f\u00f6yle:<\/p>\n<p id=\"DjJjKAA\"><img loading=\"lazy\" decoding=\"async\" width=\"561\" height=\"187\" class=\"alignnone size-full wp-image-3223 \" src=\"https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7.png\" alt=\"\" srcset=\"https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7.png 561w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7-300x100.png 300w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7-500x167.png 500w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7-150x50.png 150w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7-400x133.png 400w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a93394d87ae7-200x67.png 200w\" sizes=\"auto, (max-width: 561px) 100vw, 561px\" \/><\/p>\n<h2 id=\"konkav-poligon-ear-clipping\" class=\"code-line\" dir=\"auto\" data-line=\"257\"><span style=\"color: #0000ff;\"><strong>Konkav (i\u00e7 b\u00fckey) Poligon: Ear Clipping<\/strong><\/span><\/h2>\n<p>uEngine de oldu\u011fu gibi hedefimiz, <code>FillPolygon<\/code> ile konkav \u015fekilleri de desteklemek. \u00d6rne\u011fin: L, T, y\u0131ld\u0131z \u015fekilleri. Basit triangle fan burada yetmiyor, \u00e7\u00fcnk\u00fc merkezden \u00e7\u0131kan baz\u0131 \u00fc\u00e7genler \u015feklin d\u0131\u015f\u0131na ta\u015fabiliyor.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"261\">ADR-006&#8217;da ear clipping&#8217;i se\u00e7memize y\u00f6nelik motivasyonu anlatt\u0131m: uygulamas\u0131 orta d\u00fczey, d\u0131\u015f ba\u011f\u0131ml\u0131l\u0131k yok, 2B aray\u00fcz \u015fekillerinin tipik nokta say\u0131s\u0131nda (&lt; 200) O(n\u00b2) karma\u015f\u0131kl\u0131k da bizim i\u00e7in sorun de\u011fil.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"263\">Bir &#8220;kulak&#8221;, poligonun d\u0131\u015fb\u00fckey bir k\u00f6\u015fesi ve iki kom\u015fusundan olu\u015fan, i\u00e7inde ba\u015fka hi\u00e7bir k\u00f6\u015fe bar\u0131nd\u0131rmayan \u00fc\u00e7gen. Algoritma:<\/p>\n<ol class=\"code-line\" dir=\"auto\" data-line=\"265\">\n<li class=\"code-line\" dir=\"auto\" data-line=\"265\">Sarma y\u00f6n\u00fcn\u00fc shoelace form\u00fcl\u00fcyle bul, gerekiyorsa indeksleri ters \u00e7evirerek her zaman CCW \u00fczerinden \u00e7al\u0131\u015f,<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"266\">Bir kulak bul: k\u00f6\u015fenin d\u0131\u015fb\u00fckeyli\u011fini cross product ile s\u0131na, sonra di\u011fer noktalar\u0131n \u00fc\u00e7gen i\u00e7inde olup olmad\u0131\u011f\u0131na bak,<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"267\">Kula\u011f\u0131 \u00fc\u00e7gen olarak kaydet, indeks listesinden \u00e7\u0131kar,<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"268\">\u00dc\u00e7 nokta kalana kadar tekrarla, son \u00fc\u00e7geni ekle.<\/li>\n<\/ol>\n<pre class=\"lang:c++ decode:true \">while (indices.size() &gt; 3) {\r\n  bool found_ear = false;\r\n  std::size_t n = indices.size();\r\n\r\n  for (std::size_t i = 0; i &lt; n; ++i) {\r\n    std::size_t iprev = (i + n - 1) % n;\r\n    std::size_t inext = (i + 1) % n;\r\n    const Point&amp; a = points[indices[iprev]];\r\n    const Point&amp; b = points[indices[i]];\r\n    const Point&amp; c = points[indices[inext]];\r\n\r\n    \/\/ CCW poligonda disbukey kose: cross(a-&gt;b, b-&gt;c) &gt; 0\r\n    float cross = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\r\n    if (cross &lt;= 0.0F) {\r\n      continue;  \/\/ icbukey kose, kulak degil\r\n    }\r\n\r\n    bool is_ear = true;\r\n    for (std::size_t j = 0; j &lt; n; ++j) {\r\n      if (j == iprev || j == i || j == inext) continue;\r\n      if (PointInTriangle(points[indices[j]], a, b, c)) { is_ear = false; break; }\r\n    }\r\n\r\n    if (is_ear) {\r\n      result.emplace_back(a.x, a.y);\r\n      result.emplace_back(b.x, b.y);\r\n      result.emplace_back(c.x, c.y);\r\n      indices.erase(indices.begin() + static_cast&lt;std::ptrdiff_t&gt;(i));\r\n      found_ear = true;\r\n      break;\r\n    }\r\n  }\r\n\r\n  if (!found_ear) {  \/\/ dejenere girdi \u2014 sessizce yarida kesme, uyar\r\n    spdlog::warn(\"Tessellator: poligon tam \u00fc\u00e7genlenemedi \u2014 {} k\u00f6\u015fe atland\u0131.\",\r\n                 indices.size() - 3);\r\n    break;\r\n  }\r\n}<\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"312\"><code>PointInTriangle<\/code>\u00a0cross product i\u015faretlerine bak\u0131yor: \u00fc\u00e7 kenar i\u00e7in de ayn\u0131 y\u00f6ndeyse nokta i\u00e7eridedir.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"320\">\u015eimdi geldik bir di\u011fer \u00f6nemli kabiliyete: RenderBatcher.<\/p>\n<h2 id=\"renderbatcher-draw-calllar\u0131-azaltmak\" class=\"code-line\" dir=\"auto\" data-line=\"326\"><strong><span style=\"color: #0000ff;\">RenderBatcher: Draw Call&#8217;lar\u0131 Azaltmak<\/span><\/strong><\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"328\">Her\u00a0<code>FillRect<\/code>\u00a0veya\u00a0<code>DrawCircle<\/code> \u00e7a\u011fr\u0131s\u0131n\u0131n vertex&#8217;lerini do\u011frudan GPU&#8217;ya g\u00f6ndermek, \u00e7ok \u015fekilli sahnelerde y\u00fczlerce draw call anlam\u0131na geliyor ki, \u00f6nceki k\u00fct\u00fcphanelerimde de hep b\u00f6yle yapt\u0131m, bu sefer bu konuya e\u011filmeye karar verdim.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"328\">\u0130\u015fte tam bu noktada\u00a0 <code>Painter<\/code>\u00a0ile\u00a0<code>IRenderer<\/code>\u00a0aras\u0131ndaki\u00a0<code>RenderBatcher<\/code> bunlar\u0131 bir arabellekte biriktirip topluca GPU&#8217;ya g\u00f6nderiyor.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"330\"><code>PushTriangles<\/code>, batch&#8217;i bozacak herhangi bir durumda ise \u00f6nce <code>Flush()<\/code> \u00e7a\u011fr\u0131l\u0131yor:<\/p>\n<pre class=\"lang:c++ decode:true \">void RenderBatcher::PushTriangles(const std::vector&lt;Vertex&gt;&amp; vertices,\r\n                                  const glm::mat3&amp; transform,\r\n                                  const Color&amp; color, float opacity) {\r\n  if (mCurrentMode != DrawMode::kBasic ||        \/\/ mod degisti\r\n      !SameOpacity(mCurrentOpacity, opacity) ||  \/\/ opaklik degisti\r\n      (mVertexBuffer.size() + vertices.size()) &gt; kMaxVertices) {  \/\/ 8192 limit\r\n    Flush();\r\n  }\r\n\r\n  mCurrentMode = DrawMode::kBasic;\r\n  mCurrentOpacity = opacity;\r\n\r\n  const Affine2D t(transform);\r\n  for (auto v : vertices) {\r\n    const float x = v.x;\r\n    const float y = v.y;\r\n    v.x = t.X(x, y);\r\n    v.y = t.Y(x, y);\r\n    v.r = color.r; v.g = color.g; v.b = color.b; v.a = color.a;\r\n    mVertexBuffer.push_back(v);\r\n  }\r\n}<\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"357\">Texture&#8217;l\u0131 \u00e7izimde bir tetikleyici daha var: aktif texture de\u011fi\u015fti\u011finde de flush gerekiyor, \u00e7\u00fcnk\u00fc tek draw call tek texture ba\u011flayabilir. Kar\u0131\u015ft\u0131rma modu da \u00f6yle \u2014 o bir GPU durumu, vertex&#8217;te ta\u015f\u0131nam\u0131yor. Bu ne demek, e\u011fer farkl\u0131 farkl\u0131 dokulara ait \u015fekilleri \u00e7izmeye kalkarsan\u0131z, bu mekanizman\u0131n \u00e7ok bir faydas\u0131n\u0131 g\u00f6remeyebilirsiniz.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"359\">Peki neden rengi her vertex&#8217;e tek tek yaz\u0131yoruz? \u00c7\u00fcnk\u00fc batch i\u00e7inde farkl\u0131 renkli \u015fekiller olabiliyor. Renk uniform olsayd\u0131 her renk de\u011fi\u015fimi batch&#8217;i k\u0131rard\u0131; vertex&#8217;te ta\u015f\u0131n\u0131nca art\u0131k k\u0131rm\u0131yor. Ayn\u0131 esneklik bize \u015funlar\u0131 da sa\u011flad\u0131: doku tonlamas\u0131 (tint) ve gradient dolgular. Gradient shader gerektirmiyor \u2014 k\u00f6\u015fe renkleri tessellation s\u0131ras\u0131nda hesaplan\u0131yor, aradaki ge\u00e7i\u015fi donan\u0131m enterpolasyonu yap\u0131yor ve voila.<\/p>\n<p dir=\"auto\" data-line=\"359\">Evet gelelim, ilk etapta yapmad\u0131\u011f\u0131m\u0131z fakat art\u0131k RenderBatcher&#8217;\u0131n sahip oldu\u011fu bir \u00f6zelli\u011fe daha de\u011finelim: Transformlar.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"363\">Faz 1&#8217;de, transformlar bir shader uniform&#8217;uydu ve <code>Painter<\/code>, g\u00f6rsel s\u0131ralamay\u0131 bozabilecek her noktada flush ediyordu: <code>Translate<\/code>,\u00a0<code>Rotate<\/code>,\u00a0<code>Scale<\/code>,\u00a0<code>Save<\/code>,\u00a0<code>Restore<\/code>.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"365\">\u015eekil ba\u015f\u0131na transform kullan\u0131lan durumlarda, ne yaz\u0131k ki batch&#8217;lemeden hi\u00e7 fayda g\u00f6remiyorduk. Ayn\u0131 2000 \u015fekil, transform&#8217;suz <strong>2<\/strong>\u00a0draw call \u00fcretirken \u015fekil ba\u015f\u0131na\u00a0<code>Save<\/code>+<code>Translate<\/code>+<code>Restore<\/code>\u00a0ile\u00a0<strong>2000<\/strong> draw calla kadar \u00e7\u0131kabiliyordu.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"367\">\u00c7\u00f6z\u00fcm temelde bu transformasyon i\u015flerini CPU&#8217;ya almak oldu. <code>RenderBatcher<\/code> transformasyon matrisini vertex&#8217;lere rengin yaz\u0131ld\u0131\u011f\u0131 <strong>ayn\u0131 kopyalama d\u00f6ng\u00fcs\u00fcnde<\/strong>\u00a0uyguluyor: ek ge\u00e7i\u015f yok, ek tahsis yok.\u00a0<code>u_model<\/code>\u00a0art\u0131k daima birim g\u00f6nderiliyor ve transform de\u011fi\u015fimi batch&#8217;i k\u0131rm\u0131yor.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"367\">Windows, MSVC Release, 2000 \u015fekil ile \u00f6l\u00e7t\u00fc\u011f\u00fcm\u00fczde: \u015fekil ba\u015f\u0131na <code>Save<\/code>+<code>Translate<\/code>+<code>Restore<\/code>\u00a0ile\u00a0<strong>9.44 ms \u2192 0.30 ms<\/strong>, yakla\u015f\u0131k 31 kat.\u00a0<code>Rotate<\/code> da eklendi\u011finde 9.37 ms \u2192 0.37 ms&#8217;lere kadar d\u00fc\u015ft\u00fc\u011f\u00fcn\u00fc g\u00f6rd\u00fck.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"371\">Bug\u00fcn flush&#8217;u tetikleyen \u015feyler sadece ger\u00e7ek GPU durumlar\u0131: k\u0131rpma, viewport, \u00e7izim hedefi, kar\u0131\u015ft\u0131rma modu, doku de\u011fi\u015fimi, opakl\u0131k ve 8192 vertex&#8217;lik arabellek s\u0131n\u0131r\u0131.<\/p>\n<h2 id=\"renderbatcher-draw-calllar\u0131-azaltmak\" class=\"code-line\" dir=\"auto\" data-line=\"326\"><strong><span style=\"color: #0000ff;\">Demo<\/span><\/strong><\/h2>\n<p>Faz 1 demosu,\u00a0 <a href=\"https:\/\/github.com\/yazilimperver\/sdl-painter\/blob\/main\/examples\/basics\/primitives.cpp\"><code>examples\/basics\/primitives.cpp<\/code><\/a> ad\u0131yla repoda (eski ad\u0131 <code>phase1_demo<\/code>). T\u00fcm primitifleri tek ekranda topluyor:<\/p>\n<pre class=\"lang:c++ decode:true \">painter.Begin();\r\npainter.Clear(sdl_painter::Color{25, 25, 35, 255});\r\n\r\npainter.SetPen(sdl_painter::Pen(sdl_painter::Color{255, 220, 50, 255}, 3.0f));\r\npainter.DrawRect(430.0f, 70.0f, 180.0f, 100.0f);\r\n\r\n\/\/ Konkav poligon \u2014 ear clipping'in isi\r\npainter.SetBrush(sdl_painter::Brush(sdl_painter::Color{200, 180, 50, 210}));\r\npainter.FillPolygon({{600, 350}, {750, 350}, {750, 420},\r\n                     {680, 420}, {680, 480}, {600, 480}});\r\n\r\n\/\/ Transform: dondurulmus dikdortgen\r\npainter.Save();\r\npainter.Translate(150.0f, 580.0f);\r\npainter.Rotate(30.0f);\r\npainter.SetBrush(sdl_painter::Brush(sdl_painter::Color{80, 180, 255, 180}));\r\npainter.FillRect(-60.0f, -35.0f, 120.0f, 70.0f);\r\npainter.Restore();\r\n\r\npainter.End();<\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"404\"><code>Save()<\/code>\u00a0\/\u00a0<code>Restore()<\/code>\u00a0\u00e7ifti burada i\u015fini yap\u0131yor: dikd\u00f6rtgen d\u00f6nd\u00fcr\u00fclm\u00fc\u015f koordinat sisteminde \u00e7iziliyor,\u00a0<code>Restore<\/code> sonras\u0131 bir sonraki \u015fekil yine ekran koordinatlar\u0131nda. Buna faz 2&#8217;de ayr\u0131nt\u0131l\u0131 de\u011finece\u011fiz.<\/p>\n<p id=\"AMwWnZQ\"><img loading=\"lazy\" decoding=\"async\" width=\"897\" height=\"730\" class=\"alignnone size-full wp-image-3224 \" src=\"https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e.png\" alt=\"\" srcset=\"https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e.png 897w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-300x244.png 300w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-768x625.png 768w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-500x407.png 500w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-150x122.png 150w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-400x326.png 400w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-800x651.png 800w, https:\/\/yazilimperver.com\/wp-content\/uploads\/2026\/08\/img_6a933ced3668e-200x163.png 200w\" sizes=\"auto, (max-width: 897px) 100vw, 897px\" \/><\/p>\n<h2><strong><span style=\"color: #0000ff;\">Sonu\u00e7<\/span><\/strong><\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"414\">Bu yaz\u0131m\u0131zla birlikte art\u0131k elle tutulur \u015fekillerimizi g\u00f6rebildik. Burada \u00f6nemli \u00fc\u00e7 husus var:<\/p>\n<ul class=\"code-line\" dir=\"auto\" data-line=\"416\">\n<li class=\"code-line\" dir=\"auto\" data-line=\"416\"><strong>Tessellator ayr\u0131m\u0131<\/strong> (ADR-004): renderer&#8217;dan ba\u011f\u0131ms\u0131z vertex \u00fcretimi. Vulkan backend&#8217;i i\u00e7in de bu kullan\u0131l\u0131yor olacak,<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"417\"><strong>Quad tabanl\u0131\/kal\u0131nl\u0131\u011fa sahip \u00e7izgiler<\/strong>\u00a0(ADR-003):\u00a0<code>glLineWidth<\/code> kullan\u0131m\u0131 yok,<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"418\"><strong>RenderBatcher<\/strong>: GPU \u00e7a\u011fr\u0131lar\u0131n\u0131 azaltt\u0131k,<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"418\">\n<p id=\"konkav-poligon-ear-clipping\" class=\"code-line\" dir=\"auto\" data-line=\"257\"><strong>Konkav\/\u0130\u00e7 B\u00fckey Poligon:<\/strong> Standard poligonlar d\u0131\u015f\u0131ndaki \u015fekilleri de \u00e7izebiliyoruz.<\/p>\n<\/li>\n<\/ul>\n<p class=\"code-line\" dir=\"auto\" data-line=\"422\">Faz 2&#8217;de transformasyon hususlar\u0131na bak\u0131yor olaca\u011f\u0131z: <code>glm::mat3<\/code>\u00a0ile affine matris birikimi,\u00a0<code>Save<\/code>\/<code>Restore<\/code> y\u0131\u011f\u0131n\u0131 ve scissor tabanl\u0131 k\u0131rpma kabiliyetlerine bakaca\u011f\u0131z.<\/p>\n<p dir=\"auto\" data-line=\"422\">Bu arada k\u00fct\u00fcphane art\u0131k duyurulacak d\u00fczeye geldi, bunu da ayr\u0131 bir yaz\u0131 ile yapar\u0131m muhtemelen. Kaynak kodlar i\u00e7in <a href=\"https:\/\/github.com\/yazilimperver\/sdl-painter\" data-href=\"https:\/\/github.com\/yazilimperver\/sdl-painter\">github.com\/yazilimperver\/sdl-painter<\/a> a bakabilirsiniz.<\/p>\n<p dir=\"auto\" data-line=\"424\">Bir sonraki yaz\u0131mda g\u00f6r\u00fc\u015fmek dile\u011fiyle kendinize \u00e7ok iyi bak\u0131n.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sevgili yaz\u0131l\u0131mperver dostlar\u0131m, tekrar merhaba. Bu zaman zarf\u0131nda, gerek k\u00fct\u00fcphane gerekse siteme dair bir \u00e7ok de\u011fi\u015fiklik oldu \ud83d\ude42 Bunlar\u0131, ilgili yerlerde k\u0131sa notlarla belirtece\u011fim; hangi tasar\u0131m\u0131n ilk h\u00e2liyle kald\u0131\u011f\u0131 ve hangisinin sonradan de\u011fi\u015fti\u011fi. \u00d6ncelikle sitemin hostingini de\u011fi\u015ftirdim ve art\u0131k daha h\u0131zl\u0131. K\u00fct\u00fcphaneye ise bir \u00e7ok yeni kabiliyet ekledim, bunlara zaten yak\u0131nda de\u011finece\u011fim. Neyse \u015fimdi konumuza&#8230; <a class=\"more-link\" href=\"https:\/\/yazilimperver.com\/index.php\/2026\/08\/29\/sdl-painter-temel-sekiller-ve-kabiliyetler-faz-1\/\">Continue reading <span class=\"meta-nav\">&#8594;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_seopress_titles_title":"","_seopress_titles_desc":"","_seopress_robots_index":"","_seopress_robots_follow":"","_seopress_robots_imageindex":"","_seopress_robots_snippet":"","_seopress_robots_primary_cat":"41","_seopress_robots_breadcrumbs":"","_seopress_robots_freeze_modified_date":"","_seopress_robots_custom_modified_date":"","_seopress_robots_canonical":"","_seopress_social_fb_title":"","_seopress_social_fb_desc":"","_seopress_social_fb_img":"","_seopress_social_fb_img_attachment_id":0,"_seopress_social_fb_img_width":0,"_seopress_social_fb_img_height":0,"_seopress_social_twitter_title":"","_seopress_social_twitter_desc":"","_seopress_social_twitter_img":"","_seopress_social_twitter_img_attachment_id":0,"_seopress_social_twitter_img_width":0,"_seopress_social_twitter_img_height":0,"_seopress_redirections_value":"","_seopress_redirections_enabled":"","_seopress_redirections_enabled_regex":"","_seopress_redirections_logged_status":"","_seopress_redirections_param":"","_seopress_redirections_type":0,"_seopress_analysis_target_kw":"","dsgo_overlay_header":false,"dsgo_overlay_header_text_color":"","dsgo_overlay_skip_top_bar":false,"_designsetgo_exclude_llms":false,"footnotes":""},"categories":[10,41,25],"tags":[1223,13,1224,334,1096,1225,1155,1222],"class_list":["post-3219","post","type-post","status-publish","format-standard","hentry","category-c","category-modern-c","category-oyun-gelistirme","tag-2b-grafik","tag-c","tag-ear-clipping","tag-opengl","tag-painter","tag-primitive-display","tag-sdl3","tag-tessellation"],"_links":{"self":[{"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/posts\/3219","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/comments?post=3219"}],"version-history":[{"count":5,"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/posts\/3219\/revisions"}],"predecessor-version":[{"id":3228,"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/posts\/3219\/revisions\/3228"}],"wp:attachment":[{"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/media?parent=3219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/categories?post=3219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yazilimperver.com\/index.php\/wp-json\/wp\/v2\/tags?post=3219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}