Làm đồ họa như năm 1993
(staniks.github.io)- Catlantean 3D là một dự án phụ nhằm tạo ra một game bắn súng góc nhìn thứ nhất hoàn chỉnh với các ràng buộc kiểu game PC đầu thập niên 1990, nhắm tới kết xuất dựa trên độ phân giải 320x240 và bảng màu 256 màu
- Vì renderer chỉ xử lý chỉ số palette, dự án tiền tính trước colormap 32 mức để làm tối theo khoảng cách, rồi khi chạy chỉ cần tra cứu O(1) để chọn màu tối hơn
- Quy trình tạo asset được chia thành sprite dựng sẵn từ Blender, sprite/texture vẽ tay, và texture thủ tục được tạo bằng script Python
- HUD vẽ tay và quy tắc tỉ lệ theo pixel là những ràng buộc cốt lõi để giữ độ sắc nét và khả năng đọc ở độ phân giải thấp, với 1 đơn vị thế giới được căn theo chuẩn 64 pixel
- Thay vì Tiled, tác giả làm trình biên tập bản đồ riêng và dự định phát hành cùng chính công cụ đó cho người chơi, đồng thời sẽ công bố mã nguồn game dưới dạng mã nguồn mở trên GitHub
Mục tiêu và ràng buộc của dự án
- Catlantean 3D là một dự án phụ đang được phát triển chậm rãi trong thời gian rảnh suốt hơn một năm, với mục tiêu phát hành trên Steam vào năm sau
- Mục tiêu là tạo ra một game bắn súng góc nhìn thứ nhất hoàn chỉnh, có thể phát hành được, bằng những kỹ thuật phổ biến vào đầu thập niên 1990
- Cho phép dùng trình biên dịch hiện đại và lớp trừu tượng nền tảng, nhưng lớp trừu tượng chỉ giới hạn ở framebuffer ghi pixel, input bàn phím/chuột, audio buffer ghi sample và filesystem I/O
- Toàn bộ game, kể cả asset, đều phải được làm từ đầu, và cả rendering lẫn sound mixing cũng phải tự triển khai
- Độ phân giải mục tiêu là 320x240, và mọi pixel trên màn hình chỉ được dùng một trong 256 màu
- Logic game dùng fixed-point để bảo đảm hành vi xác định, còn rendering dùng floating-point vì tính xác định ở đó ít quan trọng hơn
- Kết quả phải là một game hoàn chỉnh, chơi vui, chứ không phải bản demo công nghệ, và không dùng sản phẩm do AI tạo ra
- Mọi thứ được hiển thị đều đang trong quá trình thực hiện và có thể thay đổi nhiều
Kết xuất bằng palette
-
Đồ họa VGA
- Mode 13h của phần cứng VGA là chế độ đồ họa 320x200 256 màu, một chế độ nổi tiếng đã định hình cả một thế hệ game PC
- Từ góc nhìn lập trình viên, Mode 13h cung cấp một framebuffer tuyến tính trong đó mỗi pixel được biểu diễn bằng 1 byte là chỉ số vào bảng màu 256 màu
- Muốn vẽ pixel chỉ cần ghi 1 byte vào địa chỉ tương ứng, không cần xử lý các khái niệm như shader hay VRAM
- Asset game hiện đại có thể dùng hàng triệu màu trong ảnh, nhưng dưới giới hạn 256 màu thì mọi lựa chọn màu đều phải rất cân nhắc và có chủ đích
- Các game như Doom và Duke Nukem được nêu làm ví dụ cho việc giới hạn kỹ thuật lại tạo ra độ sắc nét và rõ ràng trong đồ họa
- Catlantean 3D muốn tái hiện cảm giác đó, nhưng chọn 320x240 gần với VGA Mode-X hơn thay vì 320x200
- Hiển thị 320x200 trên màn hình 4:3 sẽ tạo ra pixel không vuông; cách này chính thống hơn nhưng được tránh theo sở thích cá nhân
-
Bảng màu
- Bảng màu bắt đầu từ 768 byte và được chọn sau nhiều lần thử sai và lặp lại
- Trong bảng màu có dành riêng một màu hồng tươi cho màu trong suốt, một màu trắng tinh và một màu đen tinh
- Cần nhiều sắc đỏ để thể hiện máu, và cũng cần nhiều sắc xanh lá cùng xanh dương cho chìa khóa đỏ/xanh lá/xanh dương và các cửa phân biệt màu
- Bối cảnh là Catlantis, một vùng đất nhại theo Ai Cập cổ đại vì tín ngưỡng thờ mèo, nên cần nhiều màu sa mạc thiên về vàng và nâu
- Catlantis theo thiết lập đã bị loài người-chó điều khiển học chiếm đóng, nên cũng cần nhiều sắc xám để thể hiện các cơ sở công nghệ
- Để phá sự đơn điệu của màu xám và dùng làm màu thay thế ấm hơn khi làm tối, còn bổ sung thêm các sắc be
- Các màu còn lại được lấp vào khi cần trong quá trình làm texture, dựa trên đánh giá chủ quan là “trông đúng”
- Bảng màu không được hoàn thiện trong một lần mà tiếp tục được điều chỉnh qua quá trình làm asset, thử nghiệm và lặp lại
colormap và xử lý ánh sáng
-
Cấu trúc raycaster
- Catlantean 3D là một raycaster truyền thống, với bản đồ được tạo từ các ô tile cùng kích thước
- Một số tile là tường, còn các tile khác là không gian trống chỉ có sàn và trần
- Với mỗi cột trên màn hình, renderer dùng thuật toán DDA để đi xuyên tilemap và tìm vị trí va chạm với hình học của bản đồ
- Tùy theo vị trí va chạm, nó vẽ cột tường được lấy mẫu từ tọa độ texture phù hợp lên màn hình
- Sàn và trần sau đó được render bằng các scanline ngang để lấp đầy phần còn lại của màn hình
- Nếu chỉ dùng palette đơn thuần để render thế giới game, hình ảnh sẽ phẳng và không ấn tượng
- Khi càng xa người chơi thì ánh sáng càng giảm, và nếu một mặt của tile bản đồ tối hơn mặt kia một chút thì sẽ tạo cảm giác chiều sâu
-
Làm tối dựa trên palette
- Trong renderer tăng tốc phần cứng hiện đại, có thể dễ dàng làm tối trong shader bằng cách nhân vector màu với hệ số floating-point dựa trên khoảng cách đến đỉnh
- Renderer dùng palette không làm việc với khái niệm màu mà chỉ với chỉ số palette, nên để tìm màu tối hơn của một màu cụ thể thì phải duyệt cả bảng màu
- Vì duyệt bảng 256 màu cho mọi pixel khi render là quá chậm, nên hệ thống chuẩn bị sẵn tra cứu màu nhanh bằng tiền tính toán trước khi chạy
- Nếu xếp palette thành một hàng và chọn 32 mức sáng tối, thì với mỗi màu sẽ cần 31 biến thể tối hơn ngoài màu gốc
- Từ giá trị RGB của từng màu và chỉ số mức sáng tối có thể tính ra màu mục tiêu đã làm tối, nhưng màu đó có thể không thực sự tồn tại trong palette
- Để tìm màu palette gần nhất với màu mục tiêu, hệ thống duyệt palette để xây dựng colormap
- Ban đầu dùng khoảng cách Euclid nhưng nhiều màu có xu hướng bị kéo về phía xám, khiến màu tối trông lạnh và thiếu sức sống
- Sau đó màu được chuyển sang không gian màu Oklab và dùng công thức khoảng cách tri giác gần hơn với cách con người cảm nhận khác biệt màu sắc
- Cũng áp dụng khái niệm hue shifting trong pixel art, tức dịch nhẹ về tông ấm hơn khi màu trở nên tối hơn
- colormap là một ma trận hai chiều các chỉ số palette thể hiện mức sáng tối của từng màu; vì vẫn chỉ được dùng màu palette nên chuyển sắc không thể hoàn hảo
-
Giảm chi phí khi chạy
- Khi đã xác định được chỉ số hàng colormap theo khoảng cách, chỉ cần lấy phần tử thứ N trong hàng mức sáng tối đó để nhận được chỉ số palette của màu N đã làm tối
- Cách này biến việc chọn màu tối hơn trong lúc chạy thành tra cứu O(1)
- Khi render tường, cột tường hoàn toàn thẳng đứng và mọi pixel trong cột đều ở cùng khoảng cách tới camera, nên chỉ cần tính chỉ số hàng colormap một lần cho mỗi cột màn hình
- Khi render sàn, mọi pixel trong một hàng ngang đều có cùng khoảng cách, nên chỉ cần tính một lần cho mỗi hàng màn hình
- Sprite là billboard phẳng nên mọi pixel của nó đều có cùng khoảng cách tới camera, vì vậy chỉ cần tính một lần cho mỗi sprite nhìn thấy được
- Tường cần tính 320 lần, sàn tối đa 240 lần, còn mỗi sprite thấy được chỉ tính một lần; raycasting cũng miễn phí luôn việc loại bỏ vật thể bị che khuất
- Doom và nhiều game khác cũng dùng cách tiếp cận tương tự
Cách tạo asset
-
Ba nhóm asset
- Texture và sprite của Catlantean 3D được chia thành ba nhóm
- Nhóm đầu tiên là sprite dựng sẵn từ các mô hình 3D trong Blender được render thành texture
- Nhóm thứ hai là sprite và texture vẽ tay
- Nhóm thứ ba là texture thủ tục được tạo bằng các script Python đặc biệt để kết hợp art vẽ tay
-
Sprite dựng sẵn
- Sprite hoạt họa phức tạp phải chỉnh nhiều frame nên việc lặp đi lặp lại rất khó và tốn thời gian
- Cách hiệu quả hơn là tạo mô hình 3D trong Blender, rigging và animate, rồi dùng script với Blender Python API để render ra nhiều texture
- Việc chỉnh sửa diễn ra trên mô hình, còn script render lo phần còn lại, nhờ đó thời gian lặp lại giảm đi đáng kể
- Khó khăn chính là sprite render ra thường rất mờ và trông như bị bạc màu
- Cách render ở độ phân giải cao rồi thu nhỏ bằng filtering cho kết quả lẫn lộn, vì chi tiết dễ bị lọc mất và độ sắc ở cạnh bị biến mất
- Cách hiệu quả và tái sử dụng tốt nhất là dùng compositing của Blender để đạt được độ tương phản và sắc nét phù hợp
- Khi ảnh đã sẵn sàng, một script Python đặc biệt sẽ thực hiện lượng tử hóa bảng màu để tạo ảnh pixel 1 byte mà engine dùng
- Với mỗi pixel của ảnh gốc, script tìm màu palette gần nhất về mặt tri giác theo Oklab và dùng chỉ số của màu đó làm giá trị pixel
- Mảng chỉ số và thông tin kích thước được đóng gói thành định dạng TEX đơn giản mà game sử dụng
- Sprite kẻ địch có thể có nhiều animation, và mỗi animation phải có frame cho 8 hướng mà sprite có thể quay mặt tới
- Script Python sẽ xoay sprite theo từng animation, render mọi frame rồi tiếp tục xoay lại lặp đi lặp lại
- Tên file sprite được lưu theo quy ước thể hiện tên sprite, tên hành động, hướng và chỉ số frame
- Sprite đã render không được lưu trong repository mà bị
.gitignore; trên máy khác, script biên dịch sẽ render mọi mô hình để tạo sprite - Trên RTX 3070, xử lý khoảng 15 mô hình mất khoảng 10 giây
-
Sprite và texture vẽ tay
- Ở giai đoạn đầu phát triển, tác giả từng làm một cái đầu hình mèo có texture của mèo Vilko trong Blender để dùng làm khuôn mặt ở thanh trạng thái
- Kết quả này trông lười biếng, ít công sức, không truyền tải cảm xúc tốt và là điều mọi người chỉ ra đầu tiên khi phản hồi về không khí tổng thể
- Một số yếu tố bắt buộc phải được vẽ tay, và phiên bản vẽ tay có animation được đánh giá là tốt hơn nhiều
- Do kích thước sprite nhỏ, mọi pixel đều phải có chủ đích, không thể phó mặc cho renderer của Blender
- Logic tương tự cũng áp dụng cho hầu hết các vật phẩm nhặt được, vì kết quả dựng sẵn trước đó không cho chất lượng ổn định ở tỉ lệ nhỏ với compositor của Blender
- Sau khi có sự can thiệp thủ công, độ sắc nét và khả năng đọc của các vật phẩm nhặt được cải thiện rõ rệt
- Chỉ tăng độ phân giải sprite thì rasterizer của game vẫn có thể scale, nhưng vì tỉ lệ pixel không nhất quán nên kết quả không tốt
- Người chơi vô thức kỳ vọng rằng kích thước pixel sẽ giữ nguyên khi vật thể di chuyển tới lui trên cùng một hàng hoặc cột màn hình, nên nếu mỗi sprite có tỉ lệ pixel khác nhau sẽ rất kỳ cục
- Trong Catlantean 3D, 1 đơn vị thế giới tương ứng 64 pixel, và mọi sprite đều được tạo theo đúng tỉ lệ này
- Một sprite cao bằng một phần tư đơn vị thế giới thì phải có chiều cao 64/4=16 pixel
HUD và pipeline tạo sinh thủ tục
-
HUD
- HUD và các thành phần của nó gần như đều được bố trí và vẽ bằng tay
- Thanh trạng thái dưới đáy màn hình, nhiều panel và màn hình chuyển cảnh, cùng font chữ đều thuộc nhóm HUD vẽ tay
- Thay vì tô trực tiếp mọi yếu tố, tác giả dùng nhiều hiệu ứng layer và compositing của Affinity Photo
- Các hiệu ứng được dùng gồm emboss để tạo cảm giác 3D trên bề mặt phẳng, tạo noise và overlay để làm bề mặt thô hơn, color overlay, blending mode và hiệu ứng glow
- Vì các yếu tố HUD được chỉnh sửa lặp đi lặp lại thường xuyên, sự thuận tiện của việc sắp xếp lại dựa trên layer cũng rất quan trọng
- Thông thường tác giả làm trước ở truecolor trong Affinity Photo, và nhiều yếu tố chỉ là hình chữ nhật đơn sắc có áp hiệu ứng đặc biệt cùng blending
- Ảnh xuất từ Affinity Photo chứa các artifact lạ có vẻ liên quan đến anti-aliasing, và không thể tắt chúng một cách ổn định
- Vì không phù hợp cho công việc cần độ chính xác từng pixel, nên tác giả làm thêm trong Aseprite như chữ pixel-perfect, cắt tách artwork và tô đè lại các đường biên cho sắc hơn
-
Texture tạo sinh thủ tục
- Một số texture đủ đơn giản hoặc cụ thể để tự vẽ trực tiếp, nhưng nhiều texture lại chia sẻ cùng kiểu hao mòn, bụi bẩn và biến thể chi tiết bề mặt trên một vật liệu nền
- Nếu vẽ tay từng biến thể thì vừa chán vừa thiếu nhất quán, nên chúng được tạo bằng script Python
- Pipeline tạo sinh nhận vào một heightmap xác định relief bề mặt, noise map để tạo biến thể, grime map cho bụi bẩn và hao mòn, hai màu cơ bản và một brightmap
- Heightmap thực tế được dùng để tạo normal map, rồi normal map được dùng để bake ánh sáng và bóng đổ đơn giản
- Brightmap chỉ định những vùng cần giữ nguyên màu bất kể các tham số khác
- Script tạo texture cuối cùng rồi lượng tử hóa theo palette để có thể dùng ngay trong engine
- Việc chỉnh texture khi đó trở thành điều chỉnh tham số thay vì vẽ lại pixel, giúp tiết kiệm rất nhiều thời gian khi làm một mình
gibs và hiệu ứng dựng sẵn
-
Gibs
- Khi kẻ địch chịu lượng sát thương quá mức như phát shotgun bắn cự ly sát mặt hay vụ nổ, hiện tượng gibbing thường xảy ra
- Để truyền tải cảm giác của cú sát thương lớn, game dùng animation trong đó kẻ địch nổ tung thành các mảnh bê bết máu
- Pipeline này do script Python điều phối, nhận vào sprite, palette và tập tham số để tạo các frame animation đưa vào dữ liệu game
- Ở bước đầu, Voronoi decomposition chọn ngẫu nhiên K pixel seed trong phần thân không trong suốt của sprite rồi gán mọi pixel cho seed gần nhất
- Mỗi cell tạo ra theo cách đó sẽ trở thành một mảnh bay đi
- Ở bước hai, wound bleeding đánh dấu các pixel biên tiếp giáp mảnh khác là vết thương có độ sâu 0, rồi BFS lan vào trong và gán giá trị độ sâu
- Khi render, các pixel gần biên sẽ được trộn về phía màu máu của một ramp suy ra từ palette game, còn càng vào sâu bên trong mảnh thì màu gốc của sprite càng được giữ lại nhiều hơn
- Việc chọn palette ramp được tham số hóa nên có thể dùng cả “máu” màu xanh lá hay xanh dương cho một số kẻ địch nhất định
- Ở bước ba là physics, mỗi mảnh được gán tâm, vận tốc tỏa ra ngẫu nhiên theo hướng ra ngoài từ tâm sprite, tốc độ quay, trọng lực và drag
- Không có phát hiện va chạm, nhưng mảnh sẽ dừng lại khi chạm sàn; tuy thô nhưng đủ tốt
- Số lượng mảnh, lực nổ, trọng lực, drag, độ tản và độ sâu vết thương đều có thể chỉnh bằng tham số
- Cần thử sai một chút để tìm được seed trông đẹp mắt, nhưng vẫn nhanh hơn vẽ tay animation
- Cùng kỹ thuật này cũng được dùng cho các vật thể môi trường có thể phá hủy như chậu cây, thùng phuy và thùng hàng
- Giống animation dựng sẵn, kết quả gibs cũng không được lưu trong repository mà sẽ tạo lại sau khi checkout, và thời gian chạy là không đáng kể
-
Hệ thống particle dựng sẵn
- Phần lớn hiệu ứng particle được vẽ tay trong Aseprite, nhưng một số được tạo và bake theo cách tương tự gibs
- Script Python chạy mô phỏng để tạo chuỗi frame PNG, rồi sau đó lượng tử hóa sang TEX
- Không có hệ thống particle runtime; mọi hiệu ứng đều được bake sẵn để software rasterizer có thể render nhanh nhất có thể
- Từ “particle” ở đây phần nào gây hiểu nhầm, vì thực tế không mô phỏng các hạt riêng lẻ
- Mỗi frame được tổng hợp bằng cách tính radial energy field theo từng pixel và cộng nhiều lớp độc lập lại
- core là một đĩa mềm mở rộng ra ngoài trong suốt animation
- rays là các tia nhọn quanh core, có thể thiết lập độ sắc và độ dài, và mỗi tia có dao động độ dài dựa trên RNG để trông bất quy tắc
- ring là một shockwave mở rộng tùy chọn, còn noise nhân value noise vào toàn bộ năng lượng để biến hình dạng sạch sẽ thành thô ráp và bất quy tắc hơn
- Năng lượng tích lũy theo từng pixel được lượng tử hóa theo palette ramp chỉ định bởi tham số script
- Do cách thiết kế palette, mỗi hàng được xem như một gradient đi từ sáng sang tối, nên có thể làm tối pixel chỉ bằng phép toán chỉ số palette mà không cần blending hay tính alpha
- Trên một ngưỡng nhất định, pixel được đẩy về phía trắng để tạo ấn tượng lõi trắng nóng rực
- Có thể tùy chọn rải thêm các sparkle nhỏ phía trên; các hình chữ thập này di chuyển ra ngoài và mờ dần theo vòng đời riêng
- Animation hỗ trợ chế độ one-shot, nơi hiệu ứng như explosion hay teleport flash phình ra rồi biến mất, và cả chế độ loop khớp frame đầu với frame cuối để lặp mượt
- Chế độ loop hữu ích cho các hiệu ứng lặp liên tục như plasma bolts và energy projectiles
Trình biên tập bản đồ và hệ sinh thái công cụ
- Việc chỉnh sửa bản đồ ban đầu bắt đầu với Tiled, vốn nhìn chung là một công cụ hợp lý nhưng thiếu các tính năng cụ thể mà game cần
- Tiled không có khái niệm tô light level theo từng ô, cell flags hay thuộc tính đặc thù của game, nên lúc đầu phải lạm dụng object properties để lách qua
- Cũng cần một script Python để chuyển đầu ra JSON của Tiled sang định dạng nhị phân mà engine dùng, đây là một thành phần bổ sung để bù cho sự lệch nhau giữa công cụ và yêu cầu game
- Nếu người chơi phải cài Tiled để làm map, học giao diện của nó và thiết lập script chuyển đổi, thì gánh nặng đó đủ lớn để gần như triệt tiêu khả năng editor được dùng thực sự
- Editor tự làm hỗ trợ native việc tô light level, cell flags, cùng mọi entity và kiểu property mà game hiểu
- Khi game phát hành, người chơi cũng sẽ nhận được chính editor mà tác giả dùng để phát triển
- Editor hoạt động theo kiểu plug and play và có thể chạy level trực tiếp từ trong editor
- Tác giả biết icon toolbar rất tệ, và chính vì vậy nên sẽ giữ nguyên như thế
- Editor được làm bằng wxPython, phù hợp hơn tkinter ở widget, xử lý sự kiện và layout
- Kết quả từ wxPython trông native hơn và giúp quá trình lặp diễn ra nhanh chóng
- Kiến trúc xoay quanh mẫu MVP giúp tách gọn logic UI và dữ liệu map, điều này quan trọng vì định dạng map còn chưa ổn định và cả hai phía đều thay đổi thường xuyên
- Không phải mọi phần của editor đều được viết bằng Python; phần lớn model dựa vào thư viện
pybast pybastlà Python binding nội bộ của engine thông qua pybind, cung cấp khả năng đọc game data archive, đọc game textures, fixed point class cho tọa độ entity và serialization- Đây là lựa chọn để không phải triển khai lại trong Python những gì đã có bằng C++, và engine cùng công cụ tạo thành một hệ sinh thái nhỏ, gắn chặt với nhau
Kế hoạch phát hành và cách công bố
- Catlantean 3D được kỳ vọng ra mắt trong quý 1 năm 2027
- Trọng tâm hiện tại là thiết kế màn chơi, thêm kẻ địch và vũ khí, cùng các công việc polish đang diễn ra
- Mục tiêu giá bán là trong khoảng 5–8 USD
- Mã nguồn game được dự định công bố mã nguồn mở trên GitHub
- Kho dữ liệu thực tế chứa đồ họa, màn chơi, âm thanh, âm nhạc v.v. sẽ chỉ có khi mua game
- Tính minh bạch trong quá trình được xem là một trong số ít yếu tố tạo dựng niềm tin lâu dài
- Khác với AAA, game indie phụ thuộc vào một nhóm khán giả nhỏ hơn, nhưng nhóm này lại sẵn sàng theo dõi dự án, ủng hộ và giới thiệu cho người khác hơn
- Việc cho thấy quá trình làm game được xem là cách trung thực nhất để thể hiện rằng tác giả thực sự quan tâm đến thứ mình đang tạo ra
1 bình luận
Ý kiến trên Hacker News
Nếu muốn vọc software rendering, có một ví dụ gần như là đoạn mã ngắn nhất để đẩy hiệu quả một mảng 2D ARGB8888 trong bộ nhớ chính lên màn hình trên mọi nền tảng bằng SDL2 và C: https://gist.github.com/CoryBloyd/6725bb78323bb1157ff8d4175d...
Bạn sẽ phải tự chuyển framebuffer palette 320x200x8-bit sang ARGB ;)
Nếu muốn tìm cảm hứng về những gì có thể làm với framebuffer palette, hãy bấm Show Options ở http://www.effectgames.com/demos/canvascycle/ hoặc xem bài nói chuyện GDC của nghệ sĩ đó tại https://youtu.be/aMcJ1Jvtef0
Sau đó, nếu muốn cảm giác Deluxe Paint IIe cổ điển thì mở https://github.com/mriale/PyDPainter, còn nếu muốn công cụ hiện đại hơn thì dùng https://www.aseprite.org/
Ít nhất trong SDL3, không còn cần renderer hay texture nữa. Chỉ cần lấy surface bằng SDL_GetWindowSurface và hiển thị bằng SDL_UpdateWindowSurface
Theo cách tôi hiểu thư viện này, đây là phương thức gần với đồ họa phần mềm nhất, và SDL vẫn xử lý double buffering thay cho bạn
Chắc chắn đây là cách cơ bản nhất. Nếu muốn tối ưu nhỏ trong vòng lặp bên trong thì có thể tính trước offset của scanline trước khi vào vòng lặp pixel:
int s = y*screenRect.w;for (int x = 0; x < screenRect.w; x++) {pixels[s + x] = argb(255, frame>>3, y+frame, x+frame);}Cảm ơn vì đã chia sẻ. Đã có vài fork Quake phổ biến, nhưng Planimeter đang phát hành fork Quake-VS2026 không thêm thay đổi nào
Nhóm đang làm bản build x64, và để làm được vậy họ phải thay SciTech Multi-platform Graphics Library cũ (chỉ dành cho x86) bằng SDL3. Hoặc phải port scitech-mgl sang x64, nhưng khả năng đó có vẻ thấp, và theo lần cuối tôi biết thì software renderer thậm chí có thể bị loại bỏ
Tuy vậy, có lẽ vẫn có thể giữ lại software renderer cùng với SDL_Texture
Bài này lấy rất nhiều cảm hứng từ Doom, nhưng raycasting engine thực tế lại gần với các trò ra trước Doom hơn, nổi tiếng nhất là Wolfenstein 3D
Nó dùng tường thẳng đứng, cùng với chiều cao sàn và trần cố định. Wolf3D không có sàn và trần có texture vì lý do hiệu năng, nhưng một số game tương tự khác thì có
Nếu tôi nhớ đúng thì Doom và cả Duke Nukem dùng BSP engine linh hoạt hơn nhiều, nơi tường có thể giao nhau ở góc bất kỳ hoặc độ cao sàn/trần có thể thay đổi. Tuy vậy, màn chơi vẫn mang tính “phẳng”, nên không thể tạo nhiều tầng trong cùng một level; ví dụ bạn không thể thiết kế một cây cầu mà người chơi đi được cả phía trên lẫn phía dưới
Build engine không dùng BSP. Nó xem kết nối giữa các sector như các portal, rồi clip theo các portal đó và rasterize tường như những hình thang quay 90 độ
Nhờ vậy mới có hình học tường động như tàu di chuyển hay đèn xoay, đồng thời vẫn có thể tạo kiểu “room over room” miễn là không thể nhìn thấy hai phòng cùng lúc
Trong Blood và Shadow Warrior, họ tạo ra không gian gần “3D” hơn bằng cách làm các sector cùng hình dạng và dùng sàn của một sector như portal nối tới trần của sector kia. Đây không phải tính năng được engine hỗ trợ gốc, nhưng nó đủ linh hoạt để các studio tự làm được ngay cả khi không có quyền truy cập mã nguồn
Level đầu tiên của Duke Nukem 3D cũng dùng vài mẹo của Build. Chẳng hạn, sprite có thể được căn theo trục thay vì xoay theo camera và còn có va chạm, nên nếu coi mỗi sprite là một hình chữ nhật căn trục thì có thể tạo ra hình học 3D cơ bản. Trong level đầu tiên, cách này được dùng để làm cây cầu giữa hai tòa nhà ngay trước nút thoát
Blake Stone và Rise of the Triad dùng các phiên bản về sau của Wolf3D engine, và có sàn và trần có texture
Build engine của Duke Nukem không dùng BSP
https://www.jonof.id.au/forum/topic-137.html#msg1548
Về sau trong Shadow Warrior hình như cũng làm được kiểu đó. Tôi nhớ là nó được triển khai bằng portal, và thiết lập trong editor khá là cực hình
Về phần sàn, theo tôi biết thì ngay cả DOOM cũng không xử lý hoàn toàn chính xác. Với tường thẳng đứng, với mỗi cột pixel của một đoạn tường cụ thể chỉ cần thực hiện phép chia phối cảnh một lần
Còn với sàn thì tiếc là không có sự xa xỉ đó; nếu tôi nhớ đúng thì DOOM chia sàn thành các patch, chỉ tính phối cảnh chính xác ở các góc rồi nội suy phần ở giữa
Lúc đầu tôi tưởng đây chỉ là Wolfenstein 3D thay skin thôi. Đó là một đánh giá cực kỳ bất công, vì rõ ràng đã có rất nhiều công sức được bỏ vào đây
Bài viết rất hay. Đặc biệt mình thấy cách làm hoạt ảnh gib rất thú vị.
Dù đây là một bản demo kỹ thuật, vào khoảng giữa thập niên 90 mình cũng từng làm thứ gì đó tương tự. Một điểm không thấy nhắc trong bài là, bọn mình dùng lightmap 8x8 hoặc 16x16 cho texture để dễ tạo các hiệu ứng như đuốc chập chờn hoặc tên lửa bay dọc hành lang và hắt sáng. Nếu muốn thì cũng có thể dùng lightmap để “bake” ánh sáng vào luôn
Vì lightmap chỉ “vỏn vẹn” 8x8 nên vẫn kham nổi lượng toán học như tính khoảng cách đến nguồn sáng và đường ngắm cho từng luxel, tức từng đơn vị của lightmap, để ra giá trị độ sáng. Khi render texture, bọn mình dùng luxel cùng với bảng tra cứu để quyết định màu pixel thực sự sẽ được vẽ
Để tối ưu hiệu năng, nếu mình nhớ không lầm thì lightmap được cập nhật 15 lần mỗi giây. Nhờ DJGPP, mình dùng inline assembly cho phần render, và vì phép toán dấu phẩy động khi đó chậm nên dùng fixed-point vốn được tối ưu tốt. Hiệu năng render theo chuẩn máy tính thời đó là đáng kinh ngạc
Lập trình đồ họa đầu đến giữa thập niên 1990 khá là vui. Bạn ghi dữ liệu pixel vào video RAM được ánh xạ bộ nhớ là nó hiện thẳng lên màn hình
Chỉ cần một con trỏ trỏ tới 0xA0000 là đủ, chẳng cần API gì cả. Lý do của chế độ VGA 320×200 với pixel không vuông được nhắc đến là vì video buffer có kích thước 64000 byte, vừa khít trong một segment 16-bit nên dễ đánh địa chỉ hơn với code 16-bit và CPU
Nhưng cũng nhờ điểm yếu đó mà bạn có thể làm được nhiều việc hơn trên từng pixel của màn hình, và vì thế mới có các hệ thống raycasting hay cây BSP như thế này
Không có bộ xử lý chuyên dụng cho sprite và lớp nền, nhưng đổi lại PC không bị giam trong một kiến trúc chức năng cố định cứng nhắc
Đến giữa cuối thập niên 90, khi bộ xử lý 3D chuyên dụng xuất hiện thì chuyện đó không còn là vấn đề nữa, nhưng trong một quãng ngắn ở đầu thập niên 90 đã tồn tại một sân chơi cho kiểu kết xuất hình ảnh rất độc đáo
DJGPP và Free Pascal dùng cùng một go32 extender của DJ Delorie, nhưng nó không ánh xạ tuyến tính hoàn toàn, nên để hiện được gì đó lên màn hình thì phải chỉnh thêm một chút
Điều thú vị nhất là các công cụ nội bộ. Những thứ như script Python để tạo hoạt ảnh gib, hay script Python khác để sinh 2D sprite sheet từ Blender
Tác giả bài gốc rõ ràng trông như một kỹ sư 10x còn có thể làm hình minh họa đẹp, mà kiểu người như vậy theo mình thật sự rất hiếm. Việc có được một art direction nhất quán cũng khá đáng kinh ngạc
Còn 15 năm trở lại đây trong ngành game thì ngoài CEO hay lead director ra mình gần như chẳng biết tên ai cả
Mình vừa nhận ra đây có thể là một trong số ít game bắn súng có nhân vật chính là nữ. Con mèo có bộ lông tam thể, mà loại mèo đó hầu như luôn là mèo cái (https://en.wikipedia.org/wiki/Calico_cat)
Nhân vật chính nữ trong game bắn súng hiếm đến vậy sao? Nghĩ ngay ra vài ví dụ khá mainstream như Perfect Dark, Mirror's Edge, Dishonored phần 1 hay 2 gì đó, Metroid, đều là một kiểu shooter nào đó và đều có nữ chính
Tất nhiên, nói thật chính xác thì Mirror's Edge giống “góc nhìn thứ nhất” hơn là “shooter”
Ngoài ra trong nhóm “RPG + FPS” cũng có nhiều game cho phép chơi nam hoặc nữ
Có vẻ tác giả cũng biết về hoa văn bộ lông và khả năng giới tính của con mèo:
After all, I do need to give the protagonist his fair share. [image] (Yes, I know it's a female, but call it convention rooted in dialect.)Đây đâu phải game Perfect Dark
Dạo này boomer shooter có khá nhiều nữ chính. Ví dụ như Selaco[0], Supplice[1], The Citadel[2] và phần tiếp theo[3], Zortch[4] và phần tiếp theo sắp ra mắt[5], Nightmare Reaper[6], COVEN[7], Viscerafest[8], Hedon[9], v.v.
Thậm chí giờ có cảm giác boomer shooter với nữ chính còn nhiều hơn loại không có nữ chính nữa :-P Nếu gộp tag “boomer shooter” và “female protagonist” trên Steam thì ra 143 kết quả, dù trong đó cũng có game cho chọn giới tính nhân vật, hoặc phần lớn chơi nam nhưng có một số đoạn chơi nữ
[0] https://store.steampowered.com/app/1592280/Selaco/
[1] https://store.steampowered.com/app/1693280/Supplice/
[2] https://store.steampowered.com/app/1378290/The_Citadel/
[3] https://store.steampowered.com/app/3371240/Beyond_Citadel/
[4] https://store.steampowered.com/app/2443360/Zortch/
[5] https://store.steampowered.com/app/3807500/Zortch_2/
[6] https://store.steampowered.com/app/1051690/Nightmare_Reaper/
Có lẽ không phải là có chủ đích, nhưng nhìn chung tôi không mấy ấn tượng hay thấy giá trị ở những điểm như vậy. Mô tả trong Hollywood về việc phụ nữ hạ gục một người đàn ông to gấp đôi mình cũng vậy
Tôi thấy nó phi thực tế, lố bịch và có hại
Thật sự rất ngầu. Một mẹo thú vị khác từng được dùng trong thập niên 90 là palette animation. Chỉ cần thay đổi palette thôi cũng có thể tạo ra hiệu ứng cực kỳ ấn tượng với chi phí chạy rất thấp
Đúng vậy. Nếu muốn xem một ví dụ tuyệt vời về kỹ thuật này, tôi cực kỳ khuyên bạn xem trang web này
http://www.effectgames.com/demos/canvascycle/
Việc thay đổi palette ngay giữa khung hình cũng rất thú vị. PC không có thứ như copper của Amiga nên phải chú ý đến timing hơn nhiều, nhưng vẫn làm được
Tôi nhớ là nhiều kẻ địch trong Diablo 1 và 2 thực chất là cùng một sprite nhưng được áp các palette khác nhau. Có phải cũng là cùng một mẹo không?
Màu xanh dương là nước, màu tím là plasma, đỏ/cam là máu hoặc dung nham
Tôi thật sự ngạc nhiên vì sprite đã lượng tử hóa sau khi render trông khá đẹp. Nhờ quá trình chuyển đổi nhanh đó mà nó trông rất sắc nét
Với tư cách là một nhà phát triển đồng nghiệp đang làm một 3D engine với những ràng buộc khắc nghiệt đến mức phi lý, tôi rất thích được thấy chi tiết của phần giải thích này và con đường đã đi qua
Tôi đang mày mò một bản tech demo rendering không gian voxel cho PlayStation homebrew. Sau một hai ngày cuối tuần làm việc, nó đã cho ra kết quả khá ổn ở mức khoảng 10~15 FPS, mà tôi còn chưa dùng DMA hay GTE, thậm chí cả primitive polyline cũng chưa dùng
Thật mới mẻ khi lôi lại lượng giác và những mẹo tối ưu hóa cấp thấp kiểu xưa ra dùng. Khi bộ đệm scratch chỉ có 1KiB và stack chỉ dùng được một phần trong đó, bạn mới nhận ra những vi điều khiển mình dùng ở chỗ làm xa xỉ đến mức nào. Ở đó mỗi thread được cấp stack 8KiB, lại còn có cả backtrace với hơn 50 hàm template C++ xếp chồng lên nhau nữa mà.