Sử dụng LUT để tăng tốc độ cho một shader nặng trig cho thiết bị di động


10

Tôi đang cố gắng để trình shader này chạy trên một iDevice thực sự cũ, cũng như cuối cùng là Android.

Ngay cả khi tôi giảm mã xuống còn 2 hàm sin cho mỗi đoạn, shader vẫn chạy với tốc độ khoảng 20fps.

Tôi đã cân nhắc việc lấy một chiếc lá ra khỏi cuốn sách về các kỹ thuật tạo bóng cũ và tạo ra một mảng chứa một loạt các giá trị trig được xác định trước và bằng cách nào đó sử dụng chúng để ước tính shader.

Trong shader tôi đã liên kết ở trên, tôi đã mô phỏng điều đó bằng cách làm tròn các giá trị được gửi đến hàm trig, chuột càng rời (trong khi xuống) sẽ giảm chất lượng của shader. Nó thực sự khá tuyệt vì thực sự ở gần bên trái, nó trông giống như một shader hoàn toàn khác và khá tuyệt.

Dù sao tôi cũng có hai tình huống khó xử:

  1. Tôi không biết cách hiệu quả nhất để có một mảng có giá trị 360 như thế nào trong trình tạo bóng GLSL, không đổi hoặc thống nhất?
  2. Tôi không thể tìm ra cách đặt một số trong một phạm vi như thường lệ nếu tôi muốn một góc trong khoảng từ 0 đến 360 (vâng tôi biết GPU sử dụng radian) tôi sẽ làm như vậy.

    func range(float angle)
    {
       float temp = angle
       while (temp > 360) {temp -= 360;}
       while (temp < 0)   {temp += 360;}
       return temp;
    }
    

    Tuy nhiên GLSL không cho phép trong khi các vòng lặp hoặc các hàm đệ quy.


Tôi không biết liệu có thực tế không khi thực hiện điều này, nhưng nó sẽ giúp các giá trị sin được tính toán trước cách đều nhau, với các giá trị được phân cụm chặt chẽ hơn trong đó độ dốc của đường cong hình sin là dốc nhất và ít giá trị hơn khi nó vượt qua và không thay đổi nhiều? Điều này sẽ cho phép độ chính xác cao hơn ở nơi cần thiết, mà không cần lưu trữ một số lượng lớn các giá trị?
trichoplax

5
Về # 2, modchức năng tích hợp là những gì bạn muốn. Bạn sẽ viết mod(angle, 360.0).
Nathan Reed

1
@trichoplax ý tưởng tuyệt vời nhưng tôi không biết làm thế nào bạn có thể tìm kiếm các giá trị trên bàn sau đó. Giả sử chúng ta đặt chúng trong một mảng với một số trong số chúng tập trung hơn. Làm thế nào chúng ta có thể tìm thấy chỉ số đúng?
J.Doe

6
Làm thế nào về việc đưa các giá trị của bạn vào kết cấu 1D 3 kênh? Bằng cách đó bạn có thể nhận được tội lỗi, cos và tan ra với giá của một tra cứu kết cấu duy nhất. Nếu bạn ánh xạ góc 0 - 2pi thành 0 - 1 UV và sử dụng chế độ kết cấu lặp lại, bạn thậm chí không cần cuộc gọi mod, nó sẽ tự động 'bọc' và bạn cũng có thể lấy các xấp xỉ được lọc tuyến tính ở giữa các giá trị được lưu trữ của mình thay vì chụp đến cái gần nhất
russ

3
Rất nhiều thời gian bạn có thể loại bỏ các hàm trig khi được sử dụng cho hình học bằng cách không sử dụng góc mà bắt đầu và kết thúc với cặp sin / cos và sử dụng nhận dạng trig cho nửa góc và như vậy.
ratchet freak

Câu trả lời:


8

Nó đã được đề xuất trong các bình luận nhiều lần, nhưng không ai cảm thấy cần phải đưa ra một câu trả lời thích hợp, vì vậy để hoàn thiện, một giải pháp đơn giản và phổ biến cho vấn đề này có thể là sử dụng kết cấu như bảng tra cứu, cụ thể là kết cấu 1D có chứa tất cả các giá trị của hàm của bạn cho phạm vi đầu vào có thể (ví dụ / ). Điều này có những lợi thế khác nhau:[0,360)[0,2π)

  • Nó sử dụng tọa độ chuẩn hóa, tức là bạn truy cập vào kết cấu bằng cách ánh xạ các góc của bạn từ đến . Điều này có nghĩa là shader của bạn không thực sự phải quan tâm đến số lượng giá trị cụ thể . Bạn có thể điều chỉnh kích thước của nó theo bất kỳ bộ nhớ / tốc độ nào so với sự đánh đổi chất lượng mà bạn muốn (và đặc biệt là trên phần cứng cũ / nhúng mà bạn có thể muốn có sức mạnh bằng hai kích thước kết cấu).[0,360][0,1]
  • Bạn nhận được lợi ích bổ sung khi không phải thực hiện điều chỉnh khoảng thời gian giống như vòng lặp của mình (mặc dù vậy, dù sao bạn cũng không cần vòng lặp và chỉ có thể sử dụng thao tác mô đun). Chỉ cần sử dụng GL_REPEATlàm chế độ gói cho kết cấu và nó sẽ tự động bắt đầu lại từ đầu khi truy cập với các đối số> 1 (và tương tự cho các đối số phủ định).
  • Và bạn cũng nhận được lợi ích của việc nội suy tuyến tính giữa hai giá trị trong mảng về cơ bản miễn phí (hoặc giả sử gần như miễn phí) bằng cách sử dụng GL_LINEARlàm bộ lọc kết cấu, theo cách này nhận được các giá trị mà bạn thậm chí không lưu trữ. Tất nhiên phép nội suy tuyến tính không chính xác 100% cho các hàm lượng giác, nhưng chắc chắn tốt hơn là không nội suy.
  • Bạn có thể lưu trữ nhiều hơn một giá trị trong kết cấu bằng cách sử dụng kết cấu RGBA (hoặc tuy nhiên nhiều thành phần bạn cần). Bằng cách này bạn có thể nhận được ví dụ sin và cos với một tra cứu kết cấu duy nhất.
  • Đối với sin và cos, bạn chỉ cần lưu trữ các giá trị trong , dù sao bạn cũng có thể nâng cấp một cách tự nhiên từ phạm vi chuẩn hóa của định dạng điểm cố định 8 bit phổ biến. Tuy nhiên, điều đó có thể không đủ chính xác cho nhu cầu của bạn. Một số người đề xuất sử dụng các giá trị điểm nổi 16 bit, vì chúng chính xác hơn các giá trị điểm cố định 8 bit thông thường nhưng ít bộ nhớ hơn so với số float 32 bit thực. Nhưng một lần nữa, tôi cũng không biết liệu việc triển khai của bạn có hỗ trợ kết cấu dấu phẩy động hay không. Nếu không, thì có lẽ bạn có thể sử dụng 2 thành phần điểm cố định 8 bit và kết hợp chúng thành một giá trị duy nhất với một cái gì đó như (hoặc thậm chí nhiều thành phần hơn cho hạt mịn hơn). Điều này cho phép bạn kiếm lợi nhuận từ kết cấu nhiều thành phần một lần nữa.[1,1][0,1]float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;

Tất nhiên nó vẫn phải được đánh giá nếu đây là một giải pháp tốt hơn, vì truy cập kết cấu cũng không hoàn toàn miễn phí, cũng như sự kết hợp tốt nhất của kích thước và định dạng kết cấu sẽ là gì.

Để điền vào kết cấu với dữ liệu và nhấn vào một trong những bình luận của bạn, bạn phải xem xét rằng lọc kết cấu trả về giá trị chính xác tại trung tâm texel , tức là kết cấu giảm đi một nửa kích thước texel. Vì vậy, có, bạn nên tạo các giá trị tại .5texels , tức là một cái gì đó như thế này trong mã ứng dụng:

float texels[256];
for(unsigned int i = 0; i < 256; ++i)
    texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);

Tuy nhiên, bạn vẫn có thể muốn so sánh hiệu suất của phương pháp này với cách tiếp cận bằng cách sử dụng một mảng thống nhất nhỏ (nghĩa là uniform float sinTable[361], hoặc có thể ít hơn trong thực tế, để mắt đến giới hạn thực hiện của bạn về kích thước mảng đồng nhất) mà bạn chỉ cần tải với các giá trị tương ứng bằng cách sử dụng glUniform1fvvà truy cập bằng cách điều chỉnh góc của bạn thành bằng cách sử dụng chức năng và làm tròn nó đến giá trị gần nhất:[0,360)mod

angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];

1
Đây là một phần mở rộng thú vị để lưu trữ tra cứu bảng trong kết cấu. Nó (ab) sử dụng phép nội suy kết cấu tuyến tính N để có được phép nội suy bậc cao (hay tốt hơn tuyến tính) của các điểm dữ liệu, bề mặt, khối lượng và hypervolume. blog.demofox.org/2016/02/22/với
Alan Wolfe
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.