Số ngẫu nhiên hlsl


13

Làm thế nào để bạn tạo một số ngẫu nhiên trong HLSL?

Tôi đang hỏi bởi vì tôi muốn thử theo dõi tia gpu . Bạn cần tạo các hướng ngẫu nhiên trong một pixel shader. Vì vậy, tôi muốn randFloat(), trong đó kết quả là một số ngẫu nhiên giữa -1 và +1.

Ngoài ra, thỏa thuận với hướng dẫn tiếng ồn hlsl là gì? Các tài liệu nói rằng nó đã bị xóa khỏi HLSL 2.0 trở lên. Tại sao ?

Tôi đọc về một cách tiếp cận trong đó bạn điền vào một kết cấu với các giá trị ngẫu nhiên, sau đó có tọa độ kết cấu trong mỗi đỉnh có một chỉ mục vào kết cấu đó. Nhưng đó là trên mỗi đỉnh , tôi cần một hướng dẫn mà tôi có thể gọi trong trình đổ bóng pixel. Ngoài ra, cách tiếp cận này yêu cầu "sắp xếp lại" các texcoords trên mỗi đỉnh nếu bạn muốn các giá trị khác nhau cho mỗi khung và yêu cầu bộ đệm đỉnh cập nhật từng khung (có thể tốn kém!)

Với chi tiết, làm thế nào bạn có thể tạo ra một số ngẫu nhiên trên GPU với giá rẻ?


1
Bạn có ý nghĩa gì với mỗi đỉnh, tọa độ tex đó được nội suy trong trình đổ bóng pixel và tìm kiếm trên mỗi pixel theo kết cấu ngẫu nhiên đó.
Kikaimaru

Ý tôi là, nếu 2 giá trị kết cấu xảy ra quá gần nhau, sẽ có sự phụ thuộc này (bạn sẽ nhận được giá trị "pha trộn"). Giả sử một đỉnh có (0,5,0,5) cho u, v và đỉnh liền kề có (0,51,0,52) và có khoảng 500 đoạn trên đoạn đó .. thì đầu ra sẽ là 2 hoặc 3 giá trị ngẫu nhiên thực tế (nhìn lên từ kết cấu), nhưng phần còn lại sẽ nội suy tuyến tính dọc theo khuôn mặt và không thực sự ngẫu nhiên. Tôi muốn loại bỏ khả năng đó và có một hàm rand () mà tôi có thể gọi trong trình đổ bóng pixel
bobobobo

Tại sao bạn lại nói rằng một đỉnh có 0,5 và 0,51 khác, điều đó không có vẻ ngẫu nhiên, bạn cần phải "ngẫu nhiên hóa" các hợp âm tex này, bạn có thể thực hiện nó trong pixel shader nếu muốn, nhưng đó là nhiều thao tác hơn . Các hợp âm tex đó không cần phải được tạo ra từ vị trí của chúng vì vậy không quan trọng chúng gần nhau như thế nào. Bạn có thể lấy mẫu kết cấu ngẫu nhiên một lần trong trình tạo bóng đỉnh (sử dụng vị trí, bình thường, texcoords * một số tham số dưới dạng texcoords) và điều này sẽ cung cấp cho bạn texcoords bạn chuyển qua trình đổ bóng pixel
Kikaimaru

@Kikaimaru Ý tôi là do tình huống xấu, điều đó là có thể ..
bobobobo

Có trong chuỗi ngẫu nhiên có thể có hai giá trị giống nhau
Kikaimaru

Câu trả lời:


14

Các số ngẫu nhiên giả trong trình đổ bóng pixel không dễ dàng có được. Một trình tạo số ngẫu nhiên giả trên CPU sẽ có một số trạng thái mà nó vừa đọc và ghi vào, trên mỗi cuộc gọi đến hàm. Bạn không thể làm điều đó trong một shader pixel.

Dưới đây là một số tùy chọn:

  1. Sử dụng một shader tính toán thay vì shader pixel - chúng hỗ trợ truy cập đọc-ghi vào bộ đệm, do đó bạn có thể thực hiện bất kỳ PRNG tiêu chuẩn nào.

  2. Mẫu từ một hoặc nhiều kết cấu có chứa dữ liệu ngẫu nhiên, dựa trên một tham số như vị trí không gian màn hình. Bạn cũng có thể thực hiện một số phép toán trên vị trí trước khi sử dụng nó để tìm kiếm kết cấu, điều này sẽ cho phép bạn sử dụng lại kết cấu nếu toán học liên quan đến hằng số đổ bóng ngẫu nhiên trên mỗi cuộc gọi.

  3. Tìm một số chức năng toán học của vị trí không gian màn hình, cho kết quả 'đủ ngẫu nhiên'.

Một tìm kiếm nhanh trên google đã tìm thấy trang này với các chức năng sau:

float rand_1_05(in float2 uv)
{
    float2 noise = (frac(sin(dot(uv ,float2(12.9898,78.233)*2.0)) * 43758.5453));
    return abs(noise.x + noise.y) * 0.5;
}

float2 rand_2_10(in float2 uv) {
    float noiseX = (frac(sin(dot(uv, float2(12.9898,78.233) * 2.0)) * 43758.5453));
    float noiseY = sqrt(1 - noiseX * noiseX);
    return float2(noiseX, noiseY);
}

float2 rand_2_0004(in float2 uv)
{
    float noiseX = (frac(sin(dot(uv, float2(12.9898,78.233)      )) * 43758.5453));
    float noiseY = (frac(sin(dot(uv, float2(12.9898,78.233) * 2.0)) * 43758.5453));
    return float2(noiseX, noiseY) * 0.004;
}

Re: tính toán shader, bạn cũng có thể lưu trữ trạng thái RNG trong bộ nhớ chia sẻ nhóm. Để đạt hiệu quả, bạn không chỉ muốn một trạng thái RNG (sự tranh chấp sẽ là một nút cổ chai lớn khi mọi luồng cố gắng cập nhật nó), nhưng nhiều - có thể nhiều như một trên mỗi luồng, hoặc có thể là một trên 4 hoặc 8 luồng hoặc một số như vậy - miễn là các tiểu bang đủ nhỏ để làm cho điều đó khả thi (có lẽ không phải là Mersenne Twister). Có các thiết kế RNG trong đó một số luồng SIMD có thể hợp tác để tạo ra một số số ngẫu nhiên cùng một lúc không? Điều đó sẽ khá hữu ích ở đây.
Nathan Reed

1
liên kết bên ngoài bị hỏng
George Birbilis

2

Đây là cách tôi tạo các số ngẫu nhiên giả cho các hiệu ứng hạt của mình với một shader tính toán. Không chắc chắn mã này thực sự ngẫu nhiên như thế nào nhưng nó hoạt động đủ tốt cho mục đích của tôi.

Chìa khóa để cung cấp cho mỗi nhân GPU là chuỗi ngẫu nhiên của riêng nó là cung cấp cho trình đổ bóng tính toán một hạt giống ngẫu nhiên ban đầu từ CPU. Mỗi hạt nhân sau đó sử dụng hạt giống này để quay vòng trình tạo số sử dụng id luồng của nó làm số đếm. Từ đó, mỗi luồng nên có hạt giống riêng để tạo các giá trị duy nhất.

// Source
// http://www.gamedev.net/topic/592001-random-number-generation-based-on-time-in-hlsl/
// Supposebly from the NVidia Direct3D10 SDK
// Slightly modified for my purposes
#define RANDOM_IA 16807
#define RANDOM_IM 2147483647
#define RANDOM_AM (1.0f/float(RANDOM_IM))
#define RANDOM_IQ 127773u
#define RANDOM_IR 2836
#define RANDOM_MASK 123459876

struct NumberGenerator {
    int seed; // Used to generate values.

    // Returns the current random float.
    float GetCurrentFloat() {
        Cycle();
        return RANDOM_AM * seed;
    }

    // Returns the current random int.
    int GetCurrentInt() {
        Cycle();
        return seed;
    }

    // Generates the next number in the sequence.
    void Cycle() {  
        seed ^= RANDOM_MASK;
        int k = seed / RANDOM_IQ;
        seed = RANDOM_IA * (seed - k * RANDOM_IQ ) - RANDOM_IR * k;

        if (seed < 0 ) 
            seed += RANDOM_IM;

        seed ^= RANDOM_MASK;
    }

    // Cycles the generator based on the input count. Useful for generating a thread unique seed.
    // PERFORMANCE - O(N)
    void Cycle(const uint _count) {
        for (uint i = 0; i < _count; ++i)
            Cycle();
    }

    // Returns a random float within the input range.
    float GetRandomFloat(const float low, const float high) {
        float v = GetCurrentFloat();
        return low * ( 1.0f - v ) + high * v;
    }

    // Sets the seed
    void SetSeed(const uint value) {
        seed = int(value);
        Cycle();
    }
};
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.