07/09/2018, 16:02

Chụp màn hình OpenGL thành phim

Khi viết chương trình OpenGL xong, muốn chụp màn hình lại thành ảnh tĩnh ta đơn giản là chụp lại màn hình (ví dụ ấn PrintScreen trên Windows). Muốn chụp thành phim thường ta dùng chương trình đại loại như iShowU. Có cách khác là tự xuất ra ảnh ngay trong chương trình OpenGL của mình, rồi sau đấy ...

Khi viết chương trình OpenGL xong, muốn chụp màn hình lại thành ảnh tĩnh ta đơn giản là chụp lại màn hình (ví dụ ấn PrintScreen trên Windows). Muốn chụp thành phim thường ta dùng chương trình đại loại như iShowU. Có cách khác là tự xuất ra ảnh ngay trong chương trình OpenGL của mình, rồi sau đấy ghép ảnh thành phim. Bài viết này sẽ cho thấy cách này đơn giản chỉ cần thêm vài dòng mã.

Qui trình

  1. Sau khi vẽ ra màn hình, trước khi swap buffer ta dùng hàm glReadPixels để chụp lại màn hình OpenGL ra mảng byte theo thứ tự màu RGB ứng với mỗi điểm ảnh.
  2. Ghi mảng byte này thành tập tin .ppm trên đĩa. Tập tin .ppm có 2 kiểu: plain text và binary, ta dùng kiểu binary. Định dạng .ppm này đơn giản, viết chục dòng là xuất được. Nếu muốn xuất theo định dạng .png, .jpg v.v. thì phải dùng thêm thư viện có thể sẽ hơi loằng ngoằng.
  3. Dùng chương trình ffmpeg để gom các tập tin .ppm thành phim.

Có thể xử lí cao cấp hơn là dùng thẳng thư viện encode video như libffmpeg để chuyển thẳng mảng byte thành phim. Trên Mac OS còn có thêm lựa chọn khác là có thể dùng thư viện QuickTime để chuyển thành phim .mov, có rất nhiều ví dụ ở developer.apple.com.

Ví dụ

Ta sẽ chụp màn hình của bài NeHe số 36 thành phim. Để đơn giản, ta dùng ruby-opengl, nó có sẵn bài này. Ta thêm vài dòng sau trước glutSwapBuffers:

  frame = glReadPixels(0, 0, 640, 480, GL_RGB, GL_UNSIGNED_BYTE)
@iframe = 0 unless @iframe
filename = sprintf('%03d.ppm', @iframe)
File.open(filename, 'wb') { |f| f.write("P6 640 480 255 "); f.write(frame) }
print " "
@iframe += 1

Chạy chương trình để thu được các tập tin .ppm, rồi chạy lệnh:

ffmpeg -r 60 -qscale 1 -i %03d.ppm movie.avi

để thu được phim.

Có vài điểm cần cải tiến thêm, đây là bài tập nhỏ dành cho độc giả:

  • Nên tính toán chính xác frame rate thay vì chỉ định số 60 như trên. Hơn nữa mắt người chỉ cần khoảng 30 frame/giây là đủ, không cần ghi ra quá nhiều tập tin như trên.
  • Ảnh thu từ hàm glReadPixels bị lộn ngược theo hướng trên dưới, cần đảo lại.

Ghi chú cho OpenGL ES

OpenGL ES dùng cho thiết bị di động, như iPhone. OpenGL ES cũng có hàm glReadPixels, nhưng có thể không hoạt động với tham số GL_RGB như trong ví dụ trên. Khi chuyển thành GL_RGBA hoặc GL_BGRA thì có thể nó lại hoạt động tốt. Tất nhiên lúc này cần xử lí thêm để loại bỏ kênh A, đổi lại thành RGB trước khi ghi ra đĩa.

0