Kaydol

Merhaba Sevgili Floodlar.com Kullanıcısı, Web sitemizde geçirdiğiniz zaman ve bu büyüleyici flood evrenine katılımınız için teşekkür ederiz. Floodların geniş dünyasıyla dolu deneyiminizi daha fazla keşfetmek için, web sitemizi sınırsız olarak kullanabilmeniz adına giriş yapmanız gerekmektedir.

Oturum aç

Merhaba Floodlar.com Kullanıcısı, İlk üç sayfayı tamamladınız, tebrikler! Ancak, floodların devamını görmek ve daha fazla interaktif deneyim yaşamak için giriş yapmanız gerekiyor. Hesabınız yoksa, hızlıca oluşturabilirsiniz. Sınırsız floodlar ve etkileşimler sizleri bekliyor. Giriş yapmayı unutmayın!

Şifremi hatırlamıyorum

Şifreniz mi unuttunuz? Endişelenmeyin! Lütfen kayıtlı e-posta adresinizi giriniz. Size bir bağlantı göndereceğiz ve bu link üzerinden yeni bir şifre oluşturabileceksiniz.

Fil Necati Masonlar Locası Subreddit Adı Nedir? Cevap: ( N31 )

Üzgünüz, flood girme izniniz yok, Flood girmek için giriş yapmalısınız.

Lütfen bu Floodun neden bildirilmesi gerektiğini düşündüğünüzü kısaca açıklayın.

Lütfen bu cevabın neden bildirilmesi gerektiğini kısaca açıklayın.

Lütfen bu kullanıcının neden rapor edilmesi gerektiğini düşündüğünüzü kısaca açıklayın.

Mobil Uygulamada Açın

Güncel Floodlar En sonuncu Nesne

Emscripten’de tuvale çizim

Emscripten’de tuvale çizim

Farklı işletim sistemlerinin grafik çizmek için farklı API’leri vardır. Platformlar arası bir kod yazarken veya grafikleri bir sistemden diğerine taşırken, yerel kodu WebAssembly’a taşırken bile farklılıklar daha da kafa karıştırıcı hale gelir.

Bu gönderide, Emscripten ile derlenmiş C veya C++ kodundan web üzerindeki canvas öğesine 2B grafikler çizmek için birkaç yöntem öğreneceksiniz.

Embind aracılığıyla tuval #

Mevcut bir projeyi taşımaya çalışmak yerine yeni bir projeye başlıyorsanız, HTML’yi kullanmak en kolayı olabilir. Tuval API’sı Emscripten’in ciltleme sistemi aracılığıyla Ortada. Gömme, doğrudan isteğe bağlı JavaScript değerleri üzerinde işlem yapmanızı sağlar.

Embind’in nasıl kullanılacağını anlamak için önce aşağıdakilere bir göz atın. MDN’den örnek bir &LTcanvas> elemanı bulur ve üzerine bazı şekiller çizer.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 150, 100);

Embind ile C++’a şu şekilde dönüştürülebilir:

#include &LTemscripten/val.h>

using emscripten::val;

// Use thread_local when you want to retrieve & cache a global JS variable once per thread.
thread_local const val document = val::global("document");

// …

int main() {
val canvas = document.callval>("getElementById", "canvas");
val ctx = canvas.callval>("getContext", "2d");
ctx.set("fillStyle", "green");
ctx.callvoid>("fillRect", 10, 10, 150, 100);
}

Bu kodu bağlarken, geçtiğinizden emin olun. --bind Gömmeyi etkinleştirmek için:

emcc --bind example.cpp -o example.html

Ardından, derlenmiş varlıkları statik bir sunucuyla sunabilir ve örneği bir tarayıcıya yükleyebilirsiniz:

Tuval öğesini seçme #

Önceki kabuk komutuyla Emscripten tarafından oluşturulan HTML kabuğunu kullanırken, tuval dahil edilir ve sizin için ayarlanır. Basit demolar ve örnekler oluşturmayı kolaylaştırır, ancak daha büyük uygulamalarda Emscripten tarafından oluşturulan JavaScript’i ve WebAssembly’yi kendi tasarımınız olan bir HTML sayfasına dahil etmek isteyebilirsiniz.

Oluşturulan JavaScript kodu, içinde depolanan canvas öğesini bulmayı bekler. Module.canvas mülk. Beğenmek diğer Modül özellikleribaşlatma sırasında ayarlanabilir.

ES6 modunu kullanıyorsanız (çıktıyı uzantılı bir yola ayarlama .mjs veya kullanarak -s EXPORT_ES6 ayarı), tuvali şu şekilde iletebilirsiniz:

import initModule from './emscripten-generated.mjs';

const Module = await initModule({
canvas: document.getElementById('my-canvas')
});

Normal betik çıktısı kullanıyorsanız, Module Emscripten tarafından oluşturulan JavaScript dosyasını yüklemeden önce nesne:

script>
var Module = {
canvas: document.getElementById('my-canvas')
};
&LT/script>
script src="emscripten-generated.js">&LT/script>

OpenGL ve SDL2 #

OpenGL bilgisayar grafikleri için popüler bir çapraz platform API’sidir. Emscripten’de kullanıldığında, OpenGL işlemlerinin desteklenen alt kümesini dönüştürmekle ilgilenecektir. WebGL. Uygulamanız OpenGL ES 2.0 veya 3.0’da desteklenen ancak WebGL’de desteklenmeyen özelliklere dayanıyorsa, Emscripten bunları da taklit edebilir, ancak karşılık gelen ayarlar.

OpenGL’yi doğrudan veya daha üst düzey 2B ve 3B grafik kitaplıkları aracılığıyla kullanabilirsiniz. Bunlardan birkaçı Emscripten ile web’e taşındı. Bu yazıda, 2D grafiklere odaklanıyorum ve bunun için SDL2 şu anda tercih edilen kitaplıktır çünkü iyi bir şekilde test edilmiştir ve resmi olarak yukarı yönde Emscripten arka ucunu destekler.

dikdörtgen çizmek #

“SDL Hakkında” bölümü resmi internet sitesi diyor:

Simple DirectMedia Layer, OpenGL ve Direct3D aracılığıyla ses, klavye, fare, oyun çubuğu ve grafik donanımına düşük düzeyde erişim sağlamak için tasarlanmış bir çapraz platform geliştirme kitaplığıdır.

Sesi, klavyeyi, fareyi ve grafikleri kontrol eden tüm bu özellikler taşınmıştır ve web üzerinde Emscripten ile birlikte çalışır, böylece SDL2 ile oluşturulan tüm oyunları çok fazla güçlük çekmeden taşıyabilirsiniz. Mevcut bir projeyi taşıyorsanız şuna bakın: “Bir derleme sistemiyle bütünleştirme” Emscripten belgeleri bölümü.

Basit olması için, bu gönderide tek dosyalık bir vakaya odaklanacağım ve önceki dikdörtgen örneğini SDL2’ye çevireceğim:

#include &LTSDL2/SDL.h>

int main() {
// Initialize SDL graphics subsystem.
SDL_Init(SDL_INIT_VIDEO);

// Initialize a 300x300 window and a renderer.
SDL_Window *window;
SDL_Renderer *renderer;
SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);

// Set a color for drawing matching the earlier `ctx.fillStyle = "green"`.
SDL_SetRenderDrawColor(renderer, /* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
// Create and draw a rectangle like in the earlier `ctx.fillRect()`.
SDL_Rect rect = {.x = 10, .y = 10, .w = 150, .h = 100};
SDL_RenderFillRect(renderer, &rect);

// Render everything from a buffer to the actual screen.
SDL_RenderPresent(renderer);

// TODO: cleanup
}

Emscripten ile bağlantı kurarken kullanmanız gerekir -s USE_SDL=2. Bu, Emscripten’e WebAssembly için önceden derlenmiş olan SDL2 kitaplığını getirmesini ve onu ana uygulamanızla bağlamasını söyleyecektir.

emcc example.cpp -o example.html -s USE_SDL=2

Örnek tarayıcıya yüklendiğinde, tanıdık yeşil dikdörtgeni göreceksiniz:

Bu kodun birkaç sorunu var. İlk olarak, tahsis edilen kaynakların uygun şekilde temizlenmesinden yoksundur. İkincisi, web’de, bir uygulama yürütmeyi bitirdiğinde sayfalar otomatik olarak kapanmaz, bu nedenle tuval üzerindeki görüntü korunur. Ancak, aynı kod yerel olarak yeniden derlendiğinde

clang example.cpp -o example -lSDL2

ve yürütüldüğünde, oluşturulan pencere yalnızca kısa bir süre yanıp söner ve çıkışta hemen kapanır, böylece kullanıcının görüntüyü görme şansı olmaz.

Bir olay döngüsünü entegre etme #

Daha eksiksiz ve deyimsel bir örnek, kullanıcı uygulamadan çıkmayı seçene kadar bir olay döngüsünde bekleme ihtiyacı olarak görünecektir:

#include &LTSDL2/SDL.h>

int main() {
SDL_Init(SDL_INIT_VIDEO);

SDL_Window *window;
SDL_Renderer *renderer;
SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);

SDL_SetRenderDrawColor(renderer, /* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
SDL_Rect rect = {.x = 10, .y = 10, .w = 150, .h = 100};
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);

while (1) {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
break;
}
}

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);

SDL_Quit();
}

Görüntü bir pencereye çizildikten sonra, uygulama artık klavye, fare ve diğer kullanıcı olaylarını işleyebileceği bir döngüde bekler. Kullanıcı pencereyi kapattığında, bir SDL_QUIT döngüden çıkmak için yakalanacak olay. Döngüden çıkıldıktan sonra uygulama temizlemeyi yapacak ve ardından kendi kendine çıkacaktır.

Şimdi bu örneği Linux’ta derlemek beklendiği gibi çalışıyor ve yeşil bir dikdörtgenle 300’e 300’lük bir pencere gösteriyor:

Ancak, örnek artık web üzerinde çalışmıyor. Emscripten tarafından oluşturulan sayfa, yükleme sırasında hemen donuyor ve işlenmiş görüntüyü asla göstermiyor:

Ne oldu? “WebAssembly’den eşzamansız web API’lerini kullanma” makalesindeki yanıtı alıntılayacağım:

Kısa versiyon, tarayıcının tüm kod parçalarını sıradan birer birer alarak sonsuz bir döngü içinde çalıştırmasıdır. Bir olay tetiklendiğinde, tarayıcı ilgili işleyiciyi kuyruğa alır ve bir sonraki döngü yinelemesinde sıradan çıkarılır ve yürütülür. Bu mekanizma, yalnızca tek bir iş parçacığı kullanırken eşzamanlılığın simüle edilmesine ve çok sayıda paralel işlemin çalıştırılmasına izin verir.

Bu mekanizma hakkında hatırlanması gereken önemli nokta, özel JavaScript (veya WebAssembly) kodunuz yürütülürken olay döngüsünün engellenmiş olmasıdır. […]

Önceki örnek, sonsuz bir olay döngüsü yürütürken, kodun kendisi tarayıcı tarafından dolaylı olarak sağlanan başka bir sonsuz olay döngüsü içinde çalışır. İç döngü kontrolü asla dış döngüye bırakmaz, bu nedenle tarayıcının dış olayları işleme veya sayfaya bir şeyler çizme şansı olmaz.

Bu sorunu çözmenin iki yolu vardır.

Asyncify ile olay döngüsünün engelini kaldırma #

İlk olarak, bağlantılı makalede açıklandığı gibi kullanabilirsiniz Eşzamansızlaştır. Bu, C veya C++ programını “duraklatmaya”, kontrolü olay döngüsüne geri vermeye ve bazı eşzamansız işlemler bittiğinde programı uyandırmaya izin veren bir Emscripten özelliğidir.

Bu tür eşzamansız işlem, “mümkün olan minimum süre için uyku” bile olabilir, şu şekilde ifade edilir: emscripten_sleep(0) API. Bunu döngünün ortasına yerleştirerek, kontrolün her yinelemede tarayıcının olay döngüsüne döndürülmesini ve sayfanın yanıt vermeye devam etmesini ve tüm olayları işleyebilmesini sağlayabilirim:

#include &LTSDL2/SDL.h>
#ifdef __EMSCRIPTEN__
#include &LTemscripten.h>
#endif

int main() {
SDL_Init(SDL_INIT_VIDEO);

SDL_Window *window;
SDL_Renderer *renderer;
SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);

SDL_SetRenderDrawColor(renderer, /* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
SDL_Rect rect = {.x = 10, .y = 10, .w = 150, .h = 100};
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);

while (1) {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
break;
}
#ifdef __EMSCRIPTEN__
emscripten_sleep(0);
#endif
}

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);

SDL_Quit();
}

Bu kodun artık Asyncify etkinken derlenmesi gerekiyor:

emcc example.cpp -o example.html -s USE_SDL=2 -s ASYNCIFY

Ve uygulama web’de tekrar beklendiği gibi çalışıyor:

Ancak Asyncify, önemsiz olmayan kod boyutu ek yüküne sahip olabilir. Uygulamada yalnızca üst düzey bir olay döngüsü için kullanılıyorsa, daha iyi bir seçenek, emscripten_set_main_loop işlev.

“Ana döngü” API’leri ile olay döngüsünün engellemesini kaldırma #

emscripten_set_main_loop çağrı yığınını çözmek ve geri sarmak için herhangi bir derleyici dönüşümü gerektirmez ve bu şekilde kod boyutu yükünden kaçınılır. Bununla birlikte, karşılığında, kodda çok daha fazla manuel değişiklik yapılmasını gerektirir.

İlk olarak, olay döngüsünün gövdesinin ayrı bir işleve çıkarılması gerekir. Daha sonra, emscripten_set_main_loop bu işlevle ilk bağımsız değişkende bir geri arama, ikinci bağımsız değişkende bir FPS (0 yerel yenileme aralığı için) ve sonsuz döngünün simüle edilip edilmeyeceğini gösteren bir boole (true) üçüncüde:

emscripten_set_main_loop(callback, 0, true);

Yeni oluşturulan geri arama, yığın değişkenlerine herhangi bir erişime sahip olmayacaktır. main işlev, yani değişkenler gibi window Ve renderer yığın tahsisli bir yapıya çıkarılması ve işaretçisinin üzerinden geçirilmesi gerekir. emscripten_set_main_loop_arg API’nin varyantı veya global olarak çıkarılan static değişkenler (basitlik için ikincisiyle gittim). Sonucu takip etmek biraz daha zor ama son örnektekiyle aynı dikdörtgeni çiziyor:

#include &LTSDL2/SDL.h>
#include &LTstdio.h>
#ifdef __EMSCRIPTEN__
#include &LTemscripten.h>
#endif

SDL_Window *window;
SDL_Renderer *renderer;

bool handle_events() {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
return false;
}
return true;
}

void run_main_loop() {
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop([]() { handle_events(); }, 0, true);
#else
while (handle_events())
;
#endif
}

int main() {
SDL_Init(SDL_INIT_VIDEO);

SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);

SDL_SetRenderDrawColor(renderer, /* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
SDL_Rect rect = {.x = 10, .y = 10, .w = 150, .h = 100};
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);

run_main_loop();

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);

SDL_Quit();
}

Tüm kontrol akışı değişiklikleri manuel olduğundan ve kaynak koduna yansıtıldığından, Asyncify özelliği olmadan tekrar derlenebilir:

emcc example.cpp -o example.html -s USE_SDL=2

Bu örnek işe yaramaz görünebilir, çünkü kodu çok daha basit olmasına rağmen dikdörtgenin tuval üzerine başarılı bir şekilde çizildiği ilk sürümden hiçbir farkı yoktur. SDL_QUIT olay—bu alanda işlenen tek olay handle_events function—web’de zaten yoksayılır.

Ancak, uygun olay döngüsü entegrasyonu – ya Asyncify aracılığıyla ya da aracılığıyla emscripten_set_main_loop – herhangi bir animasyon veya etkileşim eklemeye karar verirseniz karşılığını verir.

Kullanıcı etkileşimlerini yönetme #

Örneğin, son örnekte birkaç değişiklik yaparak klavye olaylarına yanıt olarak dikdörtgenin hareket etmesini sağlayabilirsiniz:

#include &LTSDL2/SDL.h>
#ifdef __EMSCRIPTEN__
#include &LTemscripten.h>
#endif

SDL_Window *window;
SDL_Renderer *renderer;

SDL_Rect rect = {.x = 10, .y = 10, .w = 150, .h = 100};

void redraw() {
SDL_SetRenderDrawColor(renderer, /* RGBA: black */ 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, /* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);
}

uint32_t ticksForNextKeyDown = 0;

bool handle_events() {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
return false;
}
if (event.type == SDL_KEYDOWN) {
uint32_t ticksNow = SDL_GetTicks();
if (SDL_TICKS_PASSED(ticksNow, ticksForNextKeyDown)) {
// Throttle keydown events for 10ms.
ticksForNextKeyDown = ticksNow + 10;
switch (event.key.keysym.sym) {
case SDLK_UP:
rect.y -= 1;
break;
case SDLK_DOWN:
rect.y += 1;
break;
case SDLK_RIGHT:
rect.x += 1;
break;
case SDLK_LEFT:
rect.x -= 1;
break;
}
redraw();
}
}
return true;
}

void run_main_loop() {
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop([]() { handle_events(); }, 0, true);
#else
while (handle_events())
;
#endif
}

int main() {
SDL_Init(SDL_INIT_VIDEO);

SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);

redraw();
run_main_loop();

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);

SDL_Quit();
}

SDL2_gfx ile başka şekiller çizme #

SDL2, platformlar arası farklılıkları ve çeşitli medya aygıtı türlerini tek bir API’de soyutlar, ancak yine de oldukça düşük seviyeli bir kitaplıktır. Özellikle grafikler için noktalar, çizgiler ve dikdörtgenler çizmek için API’ler sağlarken, daha karmaşık şekillerin ve dönüşümlerin uygulanması kullanıcıya bırakılmıştır.

SDL2_gfx bu boşluğu dolduran ayrı bir kitaplıktır. Örneğin, yukarıdaki örnekte bir dikdörtgeni daire ile değiştirmek için kullanılabilir:

#include &LTSDL2/SDL.h>
#include &LTSDL2/SDL2_gfxPrimitives.h>
#ifdef __EMSCRIPTEN__
#include &LTemscripten.h>
#endif

SDL_Window *window;
SDL_Renderer *renderer;

SDL_Point center = {.x = 100, .y = 100};
const int radius = 100;

void redraw() {
SDL_SetRenderDrawColor(renderer, /* RGBA: black */ 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer);
filledCircleRGBA(renderer, center.x, center.y, radius,
/* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
SDL_RenderPresent(renderer);
}

uint32_t ticksForNextKeyDown = 0;

bool handle_events() {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
return false;
}
if (event.type == SDL_KEYDOWN) {
uint32_t ticksNow = SDL_GetTicks();
if (SDL_TICKS_PASSED(ticksNow, ticksForNextKeyDown)) {
// Throttle keydown events for 10ms.
ticksForNextKeyDown = ticksNow + 10;
switch (event.key.keysym.sym) {
case SDLK_UP:
center.y -= 1;
break;
case SDLK_DOWN:
center.y += 1;
break;
case SDLK_RIGHT:
center.x += 1;
break;
case SDLK_LEFT:
center.x -= 1;
break;
}
redraw();
}
}
return true;
}

void run_main_loop() {
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop([]() { handle_events(); }, 0, true);
#else
while (handle_events())
;
#endif
}

int main() {
SDL_Init(SDL_INIT_VIDEO);

SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);

redraw();
run_main_loop();

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);

SDL_Quit();
}

Artık SDL2_gfx kitaplığının da uygulamaya bağlanması gerekiyor. SDL2’ye benzer şekilde yapılır:

# Native version
$ clang example.cpp -o example -lSDL2 -lSDL2_gfx
# Web version
$ emcc --bind foo.cpp -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2

Ve işte Linux’ta çalışan sonuçlar:

Ve web’de:

Daha fazla grafik ilkel için şuraya göz atın: otomatik oluşturulan dokümanlar.

İlgili Mesajlar

Yorum eklemek için giriş yapmalısınız.