Làm thế nào để tránh chảy máu kết cấu trong một bản đồ kết cấu?


46

Trong trò chơi của tôi có một địa hình giống như Minecraft được tạo thành từ các hình khối. Tôi tạo một bộ đệm đỉnh từ dữ liệu voxel và sử dụng tập bản đồ kết cấu cho các khối khác nhau:

kết cấu bản đồ cho địa hình dựa trên voxel

Vấn đề là kết cấu của các hình khối ở xa sẽ nội suy với các ô liền kề trong tập bản đồ kết cấu. Điều đó dẫn đến các dòng màu sai giữa các hình khối (bạn có thể cần phải xem ảnh chụp màn hình bên dưới ở kích thước đầy đủ của nó để xem các lỗi đồ họa):

Ảnh chụp màn hình địa hình với các sọc sai màu

Hiện tại tôi sử dụng các cài đặt nội suy này nhưng tôi đã thử mọi kết hợp và thậm chí GL_NEARESTkhông có mipmapping không cung cấp kết quả tốt hơn.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

Tôi cũng đã thử thêm một phần bù trong tọa độ kết cấu để chọn một vùng nhỏ hơn một chút nhưng vì hiệu ứng không mong muốn phụ thuộc vào khoảng cách đến máy ảnh nên điều này không thể giải quyết hoàn toàn vấn đề. Ở khoảng cách xa các sọc xảy ra.

Làm thế nào tôi có thể giải quyết chảy máu kết cấu này? Vì sử dụng một tập bản đồ kết cấu là kỹ thuật phổ biến, có thể có một cách tiếp cận phổ biến. Đáng buồn thay, vì một số lý do được giải thích trong các bình luận, tôi không thể thay đổi thành các kết cấu hoặc mảng kết cấu khác nhau.


4
Vấn đề nằm ở mipmapping - tọa độ kết cấu của bạn có thể hoạt động tốt ở cấp độ mip lớn nhất, nhưng khi mọi thứ đi xa hơn, các gạch viền giáp với nhau. Bạn có thể chống lại điều này bằng cách không sử dụng mipmap (có thể không phải là ý tưởng hay), phát triển vùng bảo vệ của bạn (khu vực giữa các ô), phát triển vùng bảo vệ một cách linh hoạt trong một shader dựa trên cấp độ mip (lừa), làm điều gì đó kỳ lạ trong khi tạo ra mipmap Mặc dù tôi không nghĩ gì cả) .. Tôi chưa bao giờ tự mình giải quyết vấn đề này, nhưng có những thứ để bắt đầu .. =)
Jari Komppa

Như tôi đã nói buồn khi tắt mipmapping không giải quyết được vấn đề. Nếu không có mipmap, nó sẽ nội suy tuyến tính GL_LINEAR, cho kết quả tương tự hoặc chọn pixel gần nhất GL_NEARESTmà dù sao cũng dẫn đến các sọc giữa các khối. Tùy chọn được đề cập cuối cùng giảm nhưng không loại bỏ lỗ hổng pixel.
danijar

7
Một giải pháp là sử dụng một định dạng đã tạo ra các mipmap, sau đó bạn có thể tạo ra các mip maps cho chính mình và sửa lỗi. Một giải pháp khác là buộc một mức mipmap cụ thể trong trình phân mảnh của bạn khi bạn lấy mẫu kết cấu. Một giải pháp khác là lấp đầy các mipmap bằng mipmap đầu tiên. Nói cách khác, khi bạn đang tạo kết cấu của mình, bạn chỉ có thể thêm các ảnh con vào hình ảnh cụ thể đó, chứa cùng một dữ liệu.
Tordin

1
Tôi đã có vấn đề này trước đây và nó đã được giải quyết bằng cách thêm 1-2 pixel vào tập bản đồ của bạn giữa mỗi kết cấu khác nhau.
Chuck D

1
@Kylotan. Vấn đề là về thay đổi kích thước kết cấu bất kể nó được thực hiện bởi mipmap, nội suy tuyến tính hay chọn pixel gần nhất.
danijar

Câu trả lời:


34

Được rồi, tôi nghĩ rằng bạn có hai vấn đề đang xảy ra ở đây.

Vấn đề đầu tiên là với mipmapping. Nói chung, bạn không muốn trộn lẫn một cách ngây thơ với mipmapping, bởi vì trừ khi tất cả các phụ đề của bạn có kích thước chính xác là 1x1 pixel, bạn sẽ bị chảy máu kết cấu. Thêm phần đệm sẽ đơn giản chuyển vấn đề xuống mức mip thấp hơn.

Theo nguyên tắc thông thường, đối với 2D, đó là nơi bạn thường thực hiện, bạn không mipmap; trong khi đối với 3D, đó là nơi bạn mipmap, bạn không tập bản đồ (thực ra, bạn làm da theo cách chảy máu sẽ không phải là vấn đề)

Tuy nhiên, điều tôi nghĩ đang diễn ra với chương trình của bạn ngay bây giờ là có lẽ bạn không sử dụng tọa độ kết cấu chính xác và do đó kết cấu của bạn bị chảy máu.

Hãy giả sử một kết cấu 8x8. Bạn có thể nhận được quý trên cùng bên trái bằng cách sử dụng tọa độ uv của (0,0) đến (0,5, 0,5):

Ánh xạ không chính xác

Và vấn đề là ở tọa độ u 0,5, bộ lấy mẫu sẽ nội suy đoạn đích bằng cách sử dụng một nửa texel bên trái và một nửa texel bên phải. Điều tương tự sẽ xảy ra ở tọa độ u 0 và tương tự đối với tọa độ v. Điều này là do bạn đang giải quyết các đường viền giữa texels chứ không phải các trung tâm.

Thay vào đó, những gì bạn nên làm là giải quyết trung tâm của mỗi texel. Trong ví dụ này, đây là các tọa độ bạn muốn sử dụng:

Ánh xạ chính xác

Trong trường hợp này, bạn sẽ luôn lấy mẫu trung tâm của mỗi texel và bạn sẽ không bị chảy máu ... Trừ khi bạn sử dụng mipmapping, trong trường hợp đó, bạn sẽ luôn bị chảy máu bắt đầu ở cấp độ mip trong đó phần phụ được chia tỷ lệ không gọn gàng phù hợp bên trong lưới pixel .

Để khái quát hóa, nếu bạn muốn truy cập một texel cụ thể, tọa độ được tính như sau:

function get_texel_coords(x, y, tex_width, tex_height)
    u = (x + 0.5) / tex_width
    v = (y + 0.5) / tex_height
    return u, v
end

Điều này được gọi là "hiệu chỉnh một nửa pixel". Có một lời giải thích chi tiết hơn ở đây nếu bạn quan tâm.


Lời giải thích của bạn nghe có vẻ rất hứa hẹn. Đáng buồn thay vì tôi phải quay video vào lúc này, tôi chưa thể thực hiện nó. Tôi sẽ làm như vậy nếu thẻ sửa chữa đến. Và tôi sẽ tự mình tính toán các mipmap của tập bản đồ mà không cần nội suy qua các ranh giới của gạch.
danijar

@sharethis Nếu bạn chắc chắn phải tập bản đồ, hãy đảm bảo rằng các phụ đề của bạn có kích cỡ bằng 2, vì vậy bạn có thể hạn chế chảy máu ở mức mip thấp nhất. Nếu bạn làm như vậy, bạn không thực sự cần phải tự chế tạo mipmap của riêng mình.
Panda Pajama

Ngay cả các kết cấu có kích thước PoT cũng có thể có các tạo tác chảy máu, bởi vì bộ lọc song tuyến có thể lấy mẫu qua các ranh giới đó. (Ít nhất là trong các triển khai tôi đã thấy.) Đối với hiệu chỉnh nửa pixel, điều đó có nên áp dụng trong trường hợp này không? Nếu tôi muốn lập bản đồ kết cấu đá của anh ấy chẳng hạn, chắc chắn đó sẽ luôn là 0,0 đến 0,25 dọc theo tọa độ U và V?
Kylotan

1
@Kylotan Nếu kích thước kết cấu của bạn là chẵn và tất cả các phụ đề đều có kích thước chẵn và nằm ở tọa độ chẵn, lọc song tuyến khi giảm một nửa kích thước kết cấu sẽ không trộn lẫn giữa các phụ đề. Tổng quát hóa cho quyền hạn của hai người (nếu bạn đang nhìn thấy các vật phẩm, có lẽ bạn đang sử dụng bộ lọc nhị phân, không phải bộ lọc song tuyến). Về hiệu chỉnh nửa pixel, điều này là cần thiết, bởi vì 0,25 không phải là texel ngoài cùng bên phải, mà là điểm giữa giữa cả hai phụ đề. Để đặt nó vào phối cảnh, hãy tưởng tượng rằng bạn là người rasterizer: màu của kết cấu tại (0,00, 0,25) là gì?
Panda Pajama

1
@sharethis kiểm tra câu trả lời khác này tôi đã viết có thể giúp bạn gamedev.stackexchange.com/questions/50023/ trên
Panda Pajama

19

Một tùy chọn sẽ dễ dàng hơn nhiều so với việc sử dụng mipmap và thêm các yếu tố fuzz phối hợp kết cấu là sử dụng một mảng kết cấu. Các mảng kết cấu tương tự như kết cấu 3d, nhưng không có mipmap trong chiều thứ 3, vì vậy chúng rất lý tưởng cho các kết cấu trong đó các "phụ đề" đều có cùng kích thước.

http://www.opengl.org/wiki/Array_Texture


Nếu chúng có sẵn, chúng là một giải pháp tốt hơn nhiều - bạn cũng có được các tính năng khác, như kết cấu bao bọc, mà bạn bỏ lỡ với các bản đồ.
Jari Komppa

@JariKomppa. Tôi không muốn sử dụng mảng kết cấu bởi vì theo cách đó tôi sẽ có thêm các bóng đổ cho địa hình. Vì công cụ tôi viết nên càng chung chung càng tốt, tôi không muốn tạo ra ngoại lệ này. Có cách nào để tạo một shader có thể xử lý cả hai mảng kết cấu và kết cấu đơn không?
danijar

8
@sharethis Loại bỏ các giải pháp kỹ thuật siêu hạng một cách rõ ràng bởi vì bạn muốn "chung chung" là một công thức cho sự thất bại. Nếu bạn viết một trò chơi, đừng viết một công cụ.
sam hocevar

@SamHocevar. Tôi đang viết một công cụ và dự án của tôi là về một kiến ​​trúc cụ thể là các thành phần được tách rời hoàn toàn. Vì vậy, thành phần biểu mẫu không nên quan tâm đến thành phần địa hình chỉ nên cung cấp bộ đệm tiêu chuẩn và kết cấu tiêu chuẩn.
danijar

1
@sharethis OK. Tôi đã bị nhầm lẫn bởi phần "Trong trò chơi của tôi" của câu hỏi.
sam hocevar

6

Sau khi vật lộn rất nhiều với vấn đề này, cuối cùng tôi đã đưa ra một giải pháp.

Để sử dụng cả hai, một bản đồ kết cấu và mipmapping, tôi cần phải tự thực hiện việc lấy mẫu xuống, bởi vì OpenGL sẽ nội suy qua các ranh giới của các ô trong tập bản đồ. Ngoài ra, tôi cần phải thiết lập các tham số kết cấu phù hợp, bởi vì nội suy giữa các mipmap cũng sẽ gây chảy máu kết cấu.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

Với các thông số này, không có chảy máu xảy ra.

Có một điều bạn phải chú ý khi cung cấp mipmap tùy chỉnh. Vì nó không có ý nghĩa để thu nhỏ tập bản đồ ngay cả khi đã có từng ô 1x1, nên mức mipmap tối đa phải được đặt theo những gì bạn cung cấp.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 7); // pick mipmap level 7 or lower

Cảm ơn tất cả các câu trả lời khác và ý kiến ​​rất hữu ích! Bằng cách này, tôi vẫn không biết làm thế nào để sử dụng tỷ lệ tăng tuyến tính, nhưng tôi đoán không có cách nào.


thật tốt khi biết bạn đã giải quyết nó. Bạn nên đánh dấu câu trả lời này là câu trả lời đúng thay vì của tôi.
Panda Pyjama

mipmapping là một kỹ thuật dành riêng cho downsampling, không có ý nghĩa gì đối với việc upampling. Ý tưởng là nếu bạn định vẽ một hình tam giác nhỏ, sẽ mất rất nhiều thời gian để lấy mẫu một họa tiết lớn chỉ để lấy một vài pixel ra khỏi nó, vì vậy bạn hãy lấy mẫu trước và sử dụng mức mip phù hợp khi vẽ hình tam giác nhỏ. Đối với các hình tam giác lớn hơn, sử dụng bất cứ thứ gì khác với cấp độ mip lớn hơn sẽ không có ý nghĩa, do đó, thật sai lầm khi làm điều gì đó nhưglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_NEAREST_MIPMAP_LINEAR);
Panda Pajama

@PandaPajama. Bạn nói đúng, tôi đã sửa câu trả lời của mình. Để thiết lập GL_TEXTURE_MAX_FILTERđể GL_NEAREST_MIPMAP_LINEARnguyên nhân chảy máu không vì lý do mipmapping, nhưng vì lý do nội suy. Vì vậy, trong trường hợp này, nó tương đương với GL_LINEARmức mipmap thấp nhất được sử dụng.
danijar

@danijar - Bạn có thể giải thích chi tiết hơn về cách bạn tự thực hiện việc lấy mẫu xuống và cách bạn nói với OpenGL ở đâu (và khi nào) để sử dụng tập bản đồ xuống?
Tim Kane

1
@TimKane Chia bản đồ thành các ô. Đối với mỗi cấp độ mipmap, hãy lấy mẫu gạch song song, kết hợp chúng và tải nó lên GPU chỉ định cấp độ mipmap làm tham số glTexImage2D(). GPU tự động chọn mức chính xác dựa trên kích thước đối tượng trên màn hình.
danijar

4

Có vẻ như vấn đề có thể do MSAA gây ra. Xem câu trả lời nàybài viết được liên kết :

"khi bạn bật MSAA, sau đó trình tạo bóng sẽ được thực thi đối với các mẫu nằm trong vùng pixel, nhưng bên ngoài vùng tam giác"

Giải pháp là sử dụng lấy mẫu centroid. Nếu bạn đang tính toán gói các tọa độ kết cấu trong một shader đỉnh, việc di chuyển logic đó sang shader mảnh cũng có thể khắc phục vấn đề.


Như tôi đã viết trong một bình luận khác, hiện tại tôi không có card màn hình nên tôi không thể kiểm tra xem đó có phải là vấn đề thực sự không. Tôi sẽ làm như vậy ngay khi tôi có thể.
danijar

Tôi đã vô hiệu hóa khử răng cưa phần cứng nhưng vẫn chảy máu. Tôi đã làm các kết cấu tìm kiếm trong shader mảnh.
danijar

1

Đây là một chủ đề cũ, và tôi đã có một vấn đề tương tự với chủ đề này.

Tôi đã có tọa độ kết cấu tốt, nhưng bằng cách nới lỏng vị trí máy ảnh (đã thay đổi vị trí của phần tử của tôi) như vậy:

public static void tween(Camera cam, Direction dir, Vec2 buffer, Vec2 start, Vec2 end) {
    if(step > steps) {
        tweening = false;
        return;
    }

    final float alpha = step/steps;

    switch(dir) {
    case UP:
    case DOWN:
        buffer = new Vec2(start.getX(), Util.lerp(start.getY(), (float)end.getY(), alpha));
        cam.getPosition().set(buffer);
        break;
    case LEFT:
    case RIGHT:
        buffer = new Vec2(Util.lerp(start.getX(), end.getX(), alpha), start.getY());
        cam.getPosition().set(buffer);
        break;
    }

    step += 1;
}

Toàn bộ vấn đề là Util.lerp () trả về số float hoặc double. Điều này thật tệ vì máy ảnh đã dịch ra khỏi lưới và gây ra một số vấn đề kỳ lạ với các đoạn kết cấu có> 0 ở X và> 0 ở Y.

TLDR: không tween hoặc bất cứ điều gì sử dụng phao

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.