Topik ini membahas penggambaran dua dimensi pada jendela raylib, mulai dari primitif dasar seperti rectangle, lingkaran, dan garis, teks dengan font bawaan, warna, hingga tekstur dari berkas gambar. Bagian terakhir membahas render target untuk menggambar ke tekstur offscreen, yaitu ke area gambar yang tidak tampil langsung di jendela. Seluruh pembahasan berpijak pada sistem koordinat jendela yang menjadi acuan setiap proc menggambar. Setelah topik ini dibaca, scene, yaitu susunan bentuk yang digambar bersama pada satu jendela, dapat disusun dari beberapa panggilan Draw dengan koordinat dan urutan gambar yang terkendali.
Sistem koordinat jendela#
Setiap proc menggambar pada raylib menerima koordinat dalam satuan piksel jendela, dengan titik nol (0, 0) di sudut kiri atas. Sumbu x bertambah ke kanan, sedangkan sumbu y bertambah ke bawah, berlawanan dengan koordinat Kartesius yang lazim dipelajari di sekolah. Akibatnya, objek yang tampak lebih rendah di layar memiliki nilai y yang lebih besar. Pada jendela berukuran 640×360 piksel sebagaimana program contoh di topik ini, angka x yang sah berkisar dari 0 hingga mendekati 640 dan angka y dari 0 hingga mendekati 360, sedangkan nilai di luar rentang itu jatuh di luar area gambar dan tidak terlihat.
Diagram berikut memperlihatkan sistem koordinat tersebut beserta bentuk-bentuk yang dipakai program contoh, dengan garis putus-putus sebagai penanda bahwa jarak setiap titik acuan diukur dari tepi kiri dan tepi atas jendela.
Perbedaan titik acuan antarbentuk perlu dicermati ketika membaca dokumentasi karena tidak semua bentuk mengikuti konvensi yang sama. DrawRectangle menempatkan rectangle dari sudut kiri atasnya dengan lebar dan tinggi yang menjalar ke kanan dan ke bawah, sedangkan DrawCircle menempatkan lingkaran dari titik pusatnya dengan radius yang menjalar ke segala arah. Karena perbedaan acuan itu, dua bentuk dengan angka koordinat yang sama tidak menumpuk pada posisi yang sama.
Warna dan primitif dasar#
Warna pada raylib bertipe rl.Color, yaitu gabungan empat komponen berurutan untuk merah, hijau, biru, dan alpha, masing-masing bernilai 0 sampai 255, dengan alpha 255 yang berarti sepenuhnya pekat dan 0 yang berarti tembus pandang. Pada binding vendor:raylib, yaitu deklarasi proc Odin yang menjembatani pustaka C-nya, keempat komponen itu disimpan sebagai array empat elemen, sehingga warna dapat dibangun dengan literal seperti rl.Color{110, 180, 240, 255} maupun dipilih dari konstanta siap pakai seperti rl.WHITE, rl.RED, dan rl.GOLD.
Tiga primitif berikut menutupi sebagian besar kebutuhan menggambar bentuk sederhana, dan ketiganya menerima warna sebagai argumen terakhir. Varian lain seperti DrawRectangleLines untuk kerangka atau DrawLineEx untuk garis dengan ketebalan tertentu dapat ditelusuri pada cheatsheet resmi yang dirujuk di bagian Referensi.
| Proc | Acuan posisi | Parameter posisi | Hasil |
|---|---|---|---|
rl.DrawRectangle | sudut kiri atas | posX, posY, width, height bertipe c.int | rectangle berisi warna penuh |
rl.DrawCircle | titik pusat | centerX, centerY bertipe c.int dan radius bertipe f32 | lingkaran berisi warna penuh |
rl.DrawLine | kedua ujung garis | startPosX, startPosY, endPosX, endPosY bertipe c.int | garis lurus setebal satu piksel |
Pilihan warna pada program contoh memperlihatkan kedua cara itu sekaligus. Langit memakai warna kustom agar nuansanya tidak sama dengan konstanta mana pun, sedangkan tanah, rumah, dan matahari memakai konstanta karena namanya langsung mengungkapkan maksud tanpa perlu membaca empat angkanya.
Scene primitif dalam satu program#
Program berikut menyusun scene primitif di dalam jendela 640×360 piksel dengan semua bentuk yang sudah dibahas. Langit diisi oleh rl.ClearBackground menggunakan warna kustom, lalu tanah, rumah, pintu, dan matahari digambar berurutan di dalam pasangan rl.BeginDrawing dan rl.EndDrawing. Urutan panggilan menentukan lapisannya karena bentuk yang digambar kemudian menutupi bentuk yang digambar lebih dahulu, konvensi yang disebut painter’s algorithm.
package main
import "core:fmt"
import rl "vendor:raylib"
// ukuran jendela scene primitif dalam piksel
LEBAR :: 640
TINGGI :: 360
main :: proc() {
rl.InitWindow(LEBAR, TINGGI, "Scene primitif")
if !rl.IsWindowReady() {
fmt.println("Jendela tidak dapat dibuka pada lingkungan tanpa tampilan.")
fmt.println("Program berhenti rapi tanpa memasuki game loop.")
return
}
defer rl.CloseWindow()
rl.SetTargetFPS(60)
// warna kustom dibangun dari empat komponen RGBA
langit := rl.Color{110, 180, 240, 255}
// judul bertipe cstring karena DrawText dan MeasureText menerima cstring
judul: cstring = "Scene primitif"
lebar_judul := rl.MeasureText(judul, 24)
for !rl.WindowShouldClose() {
rl.BeginDrawing()
// langit sebagai latar, lalu tanah, rumah, pintu, dan matahari
rl.ClearBackground(langit)
rl.DrawRectangle(0, 280, LEBAR, 80, rl.GREEN)
rl.DrawLine(0, 280, LEBAR, 280, rl.DARKGREEN)
rl.DrawRectangle(120, 160, 160, 120, rl.BROWN)
rl.DrawRectangle(180, 220, 40, 60, rl.DARKBROWN)
rl.DrawCircle(520, 80, 40.0, rl.GOLD)
// teks memakai font bawaan, judul dipusatkan dengan MeasureText
rl.DrawText(judul, (LEBAR - lebar_judul) / 2, 20, 24, rl.WHITE)
rl.DrawText("Tekan ESC untuk keluar", 16, 328, 16, rl.WHITE)
rl.EndDrawing()
}
}Beberapa detail program layak diperhatikan. Konstanta LEBAR dan TINGGI dipakai baik untuk InitWindow maupun untuk argumen gambar, sehingga mengubah ukuran jendela cukup dilakukan pada satu tempat. Variabel judul dideklarasikan bertipe cstring karena DrawText dan MeasureText menerima cstring, yaitu string berakhiran karakter nol untuk antarmuka C. Pemeriksaan rl.IsWindowReady() membuat program berhenti rapi ketika jendela tidak dapat dibuka, sedangkan rl.SetTargetFPS(60) menahan laju putaran game loop pada 60 frame per detik agar penggambaran stabil.
Program diperiksa dan dikompilasi dari dalam folder topik.
# sumberkan lingkungan sandbox jalur odin bila belum tersedia
[ -f /home/kal/my-project/tools/env.sh ] && source /home/kal/my-project/tools/env.sh
# periksa tipe dan pemanggilan dari dalam folder topik
odin check .
# kompilasi program menjadi binari scene_primitif
odin build . -out:scene_primitifKedua perintah di atas selesai dengan kode keluar 0 tanpa mencetak satu baris pun, karena pemeriksaan yang bersih dan kompilasi yang berhasil tidak menghasilkan pesan apa pun. Keluaran program grafika berupa jendela scene, bukan baris teks yang dapat ditempel sebagai blok Keluaran. Sandbox tempat topik ini ditulis tidak memiliki tampilan, sehingga binarinya hanya diperiksa dan dikompilasi di sana. Pada mesin dengan layar, binari scene_primitif dijalankan dengan ./scene_primitif, sedangkan pada lingkungan tanpa tampilan program tetap keluar dengan rapi melalui pemeriksaan rl.IsWindowReady() tanpa memasuki game loop.
Teks dengan font bawaan#
rl.DrawText menggambar teks memakai font bawaan yang dimuat raylib sejak InitWindow, sehingga penyajian teks tidak memerlukan berkas font tambahan. Parameternya berurutan, yaitu text bertipe cstring, posX dan posY bertipe c.int, fontSize bertipe c.int, dan color, dengan posX serta posY menunjuk sudut kiri atas area teks dan fontSize menyatakan tinggi huruf dalam piksel. Warna teks memakai palet yang sama dengan primitif bentuk, sehingga judul program contoh digambar dengan rl.WHITE di atas langit biru yang ditetapkan program.
Pengukuran lebar teks menjadi kunci penempatan yang rapi. rl.MeasureText(text, fontSize) mengembalikan lebar teks dalam piksel yang dihitung dengan font yang sama, sehingga judul dapat dipusatkan melalui (LEBAR - lebar_judul) / 2 sebagaimana program contoh. Untuk kebutuhan lanjutan seperti font kustom, raylib menyediakan rl.LoadFont yang mengunggah berkas font ke GPU dengan cara kerja yang sejajar dengan pemuatan tekstur pada bagian berikut.
Tekstur: dari berkas gambar ke memori GPU#
Tekstur memindahkan sumber gambar dari kode ke berkas, sekaligus memindahkan datanya ke memori GPU. raylib membedakan dua tempat penyimpanan gambar: rl.LoadImage membaca berkas ke memori utama sebagai Image berisi piksel mentah yang mudah diubah oleh CPU, sedangkan rl.LoadTexture membaca berkas lalu langsung mengunggahnya ke memori GPU sebagai Texture2D. Karena piksel pada setiap frame ditandai oleh GPU, tekstur yang sudah berada di memori GPU dapat digambar berulang kali dengan biaya yang stabil.
| Image | Texture2D | |
|---|---|---|
| Lokasi data | memori utama (RAM) | memori GPU (VRAM) |
| Dibuat dengan | rl.LoadImage | rl.LoadTexture |
| Cocok untuk | membaca dan mengubah piksel | digambar pada setiap frame |
Tekstur juga menjadi dasar sprite, yaitu gambar dari berkas yang digambar berulang pada posisi mana pun. Tiga langkah berikut cukup untuk pola dasarnya: tekstur dimuat sekali sebelum game loop, digambar pada fase menggambar, lalu dibebaskan setelah loop selesai.
// tidak-dijalankan
// muat sekali sebelum game loop: berkas dibaca lalu diunggah ke GPU
sprite := rl.LoadTexture("petak-rumput.png")
defer rl.UnloadTexture(sprite)
// di dalam fase menggambar, tint rl.WHITE menampilkan warna asli
rl.DrawTexture(sprite, 40, 200, rl.WHITE)rl.DrawTexture menempatkan tekstur dari sudut kiri atasnya pada ukuran asli berkas. Argumen tint menintai hasilnya: rl.WHITE mempertahankan warna asli, sedangkan warna lain mencampurkan ronanya pada gambar. rl.UnloadTexture membebaskan memori GPU, dan pemanggilannya dijadwalkan dengan defer agar tidak tertinggal. Apabila berkas tidak ditemukan, tekstur yang dikembalikan tidak valid, dan rl.IsTextureValid dapat dipakai untuk mengujinya sebelum digambar.
Render target: menggambar ke tekstur offscreen#
Render target membalik arah penggambaran: alih-alih menggambar ke jendela, program menggambar ke sebuah tekstur yang kemudian dipakai seperti tekstur lain. rl.LoadRenderTexture(width, height) menyiapkan pasangan framebuffer, yaitu area gambar di GPU, beserta tekstur kosong yang dikembalikan sebagai RenderTexture2D. Field texture pada struktur tersebut menjadi tekstur warna tempat hasil gambar disimpan. Selama rl.BeginTextureMode(target) aktif, semua panggilan rl.Draw diarahkan ke tekstur tersebut, dan rl.EndTextureMode() mengembalikan penggambaran ke jendela. Susunan ini berguna untuk gambar yang mahal disusun tetapi jarang berubah.
// tidak-dijalankan
target := rl.LoadRenderTexture(320, 240)
defer rl.UnloadRenderTexture(target)
// fase menggambar dialihkan ke tekstur, bukan ke jendela
rl.BeginTextureMode(target)
rl.ClearBackground(rl.RAYWHITE)
rl.DrawRectangle(40, 40, 100, 60, rl.BLUE)
rl.EndTextureMode()
// di dalam fase menggambar jendela, isi tekstur ditampilkan dengan
// tinggi sumber negatif karena isi render texture tersimpan terbalik
sumber := rl.Rectangle{0, 0, f32(target.texture.width), -f32(target.texture.height)}
rl.DrawTextureRec(target.texture, sumber, rl.Vector2{0, 0}, rl.WHITE)DrawTextureRec menggambar bagian tekstur sesuai rectangle sumber pada posisi rl.Vector2 yang diberikan, dan fragment di atas sengaja memakai tinggi sumber negatif agar isi yang tersimpan terbalik tampil kembali tegak. Konversi f32 diperlukan karena field width dan height bertipe c.int, sedangkan field pada Rectangle bertipe f32.
Kesalahan umum#
Kesalahan pada topik ini umumnya tidak tertangkap compiler karena tipe datanya benar, melainkan baru terlihat ketika program berjalan atau ketika hasilnya diperiksa.
Memuat tekstur di dalam game loop#
Pemanggilan rl.LoadTexture di dalam game loop mengunggah tekstur baru ke GPU pada setiap frame sementara tekstur lama tidak pernah dibebaskan, sehingga memori GPU cepat terkuras dan laju frame jatuh. Perbaikannya memindahkan pemuatan ke sebelum loop, menyimpan Texture2D yang dikembalikan, menggambar tekstur itu setiap frame, dan membebaskannya dengan rl.UnloadTexture setelah loop berakhir sebagaimana fragment pada bagian tekstur.
Menampilkan render target tanpa pembalikan#
Isi render texture tersimpan terbalik secara vertikal, sehingga menggambar target.texture dengan DrawTexture biasa menghasilkan gambar yang terbalik atas-bawahnya, padahal proses render target itu sendiri benar. Perbaikannya mengikuti pola fragment di atas, yaitu menggambar dengan rectangle sumber bertinggi negatif melalui DrawTextureRec sehingga isi yang tersimpan terbalik dibalik sekali lagi saat ditampilkan.
Menganggap sumbu y naik ke atas#
Kebiasaan dari koordinat Kartesius membuat arah y tertukar, misalnya matahari yang diharapkan di langit justru digambar di bawah tanah. Gejalanya mudah dikenali, yaitu seluruh scene tampak dicerminkan secara vertikal dari yang diharapkan. Perbaikannya adalah menghitung ulang setiap y dari tepi atas jendela dengan mengingat bahwa y bertambah ke bawah, lalu memeriksa ulang angkanya terhadap diagram sistem koordinat di awal topik.
Ringkasan#
- Sistem koordinat jendela menaruh titik nol di sudut kiri atas dengan sumbu x ke kanan dan sumbu y ke bawah, sehingga semua posisi dibaca dalam piksel dari tepi atas dan tepi kiri.
rl.DrawRectangle,rl.DrawCircle, danrl.DrawLinemenggambar primitif dengan warna bertiperl.Color, baik dari konstanta sepertirl.WHITEmaupun literal sepertirl.Color{110, 180, 240, 255}, dan urutan panggilan menentukan lapisan gambar.rl.DrawTextdanrl.MeasureTextmenyajikan serta mengukur teks dengan font bawaan, sedangkanrl.LoadTexturedanrl.DrawTexturemenampilkan berkas gambar dari memori GPU.rl.LoadRenderTexturebersama pasanganrl.BeginTextureModedanrl.EndTextureModemengalihkan penggambaran ke tekstur offscreen yang ditampilkan kembali melalui rectangle sumber bertinggi negatif.
Referensi#
- Cheatsheet — raylib — Daftar tanda tangan seluruh proc raylib, mencakup keluarga Draw, LoadTexture, LoadRenderTexture, dan DrawTextureRec.
- Examples — raylib.com — Kumpulan contoh resmi, termasuk core_render_texture yang memperagakan penggambaran ke render texture beserta penampilannya dengan tinggi sumber negatif.
- raylib — GitHub — Repositori resmi pustaka C yang dibundel sebagai vendor:raylib pada penginstalan Odin.