2012-10-25 24 views
5

Vì vậy, tôi đang làm việc với hệ thống hoạt ảnh 2D của bộ xương.Tối ưu hóa đỉnh cho hoạt hình xương trong OpenGL ES

Có số lượng xương X, mỗi xương có ít nhất 1 phần (một tứ giác, hai hình tam giác). Trung bình, tôi có thể có 20 xương, và 30 phần. Hầu hết xương phụ thuộc vào cha mẹ, xương sẽ di chuyển mọi khung hình. Có tổng cộng 1000 khung hình cho mỗi hoạt ảnh và tôi đang sử dụng khoảng 50 hoạt ảnh. Tổng cộng có khoảng 50.000 khung được tải trong bộ nhớ cùng một lúc. Các bộ phận khác nhau giữa các trường hợp của bộ xương.

Cách tiếp cận đầu tiên tôi mất là để tính toán vị trí/quay của mỗi xương, và xây dựng một mảng đỉnh, trong đó bao gồm những điều này, cho từng phần:

[x1,y1,u1,v1],[x2,y2,u2,v2],[x3,y3,u3,v3],[x4,y4,u4,v4] 

Và chuyển thông tin này thông qua để glDrawElements mỗi khung.

Điều này có vẻ tốt, bao gồm tất cả các tình huống mà tôi cần, không sử dụng nhiều bộ nhớ, nhưng hoạt động như một con chó. Trên iPod 4, có thể nhận được 15 khung hình/giây với 10 trong số các bộ xương này được hiển thị.

Tôi đã phát hiện ra rằng hầu hết hiệu suất đã bị ăn mòn bằng cách sao chép rất nhiều dữ liệu đỉnh mỗi khung hình. Tôi quyết định đi đến một cực khác, và "tính toán trước" các hình động, xây dựng một bộ đệm đỉnh lúc bắt đầu cho mỗi nhân vật, có chứa các tọa độ xyuv cho mỗi khung hình, cho mỗi phần, trong một nhân vật duy nhất. Sau đó, tôi tính toán chỉ số của khung nên được sử dụng trong một thời gian cụ thể và tính toán giá trị delta, được chuyển qua cho trình đổ bóng được sử dụng để nội suy giữa vị trí khung XY hiện tại và khung tiếp theo.

Các đỉnh trông như thế này, mỗi khung

[--------------------- Frame 1 ---------------------],[------- Frame 2 ------] 
[x1,y1,u1,v1,boneIndex],[x2, ...],[x3, ...],[x4, ...],[x1, ...][x2, ...][....] 

Các vertex shader trông như thế này:

attribute vec4 a_position; 
attribute vec4 a_nextPosition; 
attribute vec2 a_texCoords; 
attribute float a_boneIndex; 

uniform mat4 u_projectionViewMatrix; 
uniform float u_boneAlpha[255]; 

varying vec2 v_texCoords; 

void main() { 
    float alpha = u_boneAlpha[int(a_boneIndex)]; 
    vec4 position = mix(a_position, a_nextPosition, alpha); 
    gl_Position = u_projectionViewMatrix * position; 
    v_texCoords = a_texCoords; 
} 

Bây giờ, hiệu suất là rất tốt, với 10 trong số này trên màn hình, nó ngồi thoải mái tại 50 khung hình/giây Nhưng bây giờ, nó sử dụng một tấn dung lượng bộ nhớ. Tôi đã tối ưu hóa điều đó bằng cách mất một số độ chính xác trên xyuv, mà bây giờ là các nhóm.

Ngoài ra còn có vấn đề là phụ thuộc xương bị mất. Nếu có hai xương, cha mẹ và con, và đứa trẻ có một khung hình chính ở 0 và 2, cha mẹ có một khung hình chính ở 0s, 0.5s, 1.5s, 2s, sau đó đứa trẻ sẽ không bị thay đổi giữa 0.5s và 1,5s như nó phải.

Tôi đã đưa ra một giải pháp để khắc phục vấn đề về xương này - bằng cách buộc trẻ có các khung hình chính tại cùng một điểm với bố mẹ. Nhưng điều này sử dụng bộ nhớ nhiều hơn, và về cơ bản giết chết các điểm của hệ thống phân cấp xương.

Đây là nơi tôi đang ở. Tôi đang cố gắng tìm sự cân bằng giữa hiệu suất và mức sử dụng bộ nhớ. Tôi biết có rất nhiều thông tin dư thừa ở đây (các tọa độ UV giống hệt nhau cho tất cả các khung của một phần cụ thể, do đó lặp đi lặp lại ~ 30 lần). Và bộ đệm mới phải được tạo cho mỗi bộ phận (có tọa độ XYUV duy nhất - vị trí thay đổi vì các phần khác nhau có kích thước khác nhau)

Ngay bây giờ tôi sẽ thử thiết lập một mảng đỉnh cho mỗi ký tự, trong đó có xyuv cho tất cả các phần, và tính toán ma trận cho từng phần, và định vị lại chúng trong bóng đổ. Tôi biết điều này sẽ làm việc, nhưng tôi lo lắng rằng hiệu suất sẽ không tốt hơn là chỉ tải lên XYUV cho mỗi khung hình mà tôi đã làm lúc đầu.

Có cách nào tốt hơn để thực hiện việc này mà không làm mất hiệu suất tôi đã đạt được không?

Có bất kỳ ý tưởng hoang dã nào mà tôi có thể thử không?

+0

Câu hỏi hay, thưa bạn – Weacked

+0

Làm tất cả các xương "tự di chuyển" mỗi khung hình hay nhiều xương chỉ di chuyển vì cha mẹ di chuyển? – Dirk

+0

Tất cả nhưng một trong những xương di chuyển với cha mẹ. Có một số xương mà hầu như không có gì độc lập với cha mẹ, và sau đó có một số di chuyển nhiều lần hơn so với cha mẹ được di chuyển. –

Trả lời

1

Cách tốt hơn để làm điều này là chuyển đổi 30 phần của bạn khi đang di chuyển, không làm cho hàng ngàn bản sao của các bộ phận của bạn ở các vị trí khác nhau. Bộ đệm đỉnh của bạn sẽ chứa một bản sao dữ liệu đỉnh của bạn, tiết kiệm rất nhiều bộ nhớ. Sau đó, mỗi khung có thể được biểu diễn bằng một tập hợp các phép biến đổi được chuyển thành bộ đồng phục cho vertex shader của bạn cho mỗi xương bạn vẽ bằng một cuộc gọi tới glDrawElements(). Mỗi biến đổi của xương phụ thuộc được xây dựng tương đối so với xương bố mẹ. Sau đó, tùy thuộc vào vị trí liên tục giữa thủ công được tạo bằng tay và được tạo theo cách thủ công mà bạn muốn hoạt ảnh của mình, các bộ biến đổi của bạn có thể chiếm nhiều không gian và thời gian tính toán CPU.

cuốn sách miễn phí Jason L. McKesson của, Learning Modern 3D Graphics Programming, đưa ra một lời giải thích tốt về cách thực hiện điều này trong chương 6. Ví dụ chương trình ở phần cuối của chương này cho thấy làm thế nào để sử dụng một ma trận ngăn xếp để thực hiện một mô hình phân cấp. I have an OpenGL ES 2.0 on iOS port of this program available.

+0

Thật không may, đó là chính xác như thế nào phiên bản gốc của tôi làm việc. Và nó thực hiện hư không gần như nhanh chóng như việc thực hiện hiện tại. Tôi biết làm theo cách này sẽ sử dụng một lượng bộ nhớ khó chịu lớn, và tăng thời gian tải của chúng tôi, nhưng nó nhanh hơn 50% với nhiều ký tự trên màn hình. Nhưng, cảm ơn cho các đề xuất, lên bình chọn. –