Kết xuất tilemap liền mạch (hình ảnh liền kề không biên giới)


18

Tôi có một công cụ trò chơi 2D vẽ các hình chữ nhật bằng cách vẽ các hình xếp từ hình ảnh xếp hình. Bởi vì theo mặc định, OpenGL chỉ có thể bao bọc toàn bộ kết cấu ( GL_REPEAT) và không chỉ là một phần của nó, mỗi ô được chia thành một kết cấu riêng biệt. Sau đó, các vùng của cùng một ô được hiển thị liền kề nhau. Đây là những gì nó trông giống như khi nó hoạt động như dự định:

Tilemap không có đường nối

Tuy nhiên, ngay khi bạn giới thiệu tỷ lệ phân đoạn, các đường nối xuất hiện:

Tilemap với các đường nối

Lý do tại sao điều này xảy ra? Tôi nghĩ rằng đó là do lọc tuyến tính pha trộn các đường viền của các hình tứ giác, nhưng nó vẫn xảy ra với lọc điểm. Giải pháp duy nhất tôi tìm thấy cho đến nay là đảm bảo tất cả định vị và chia tỷ lệ chỉ xảy ra ở các giá trị nguyên sử dụng lọc điểm. Điều này có thể làm giảm chất lượng hình ảnh của trò chơi (đặc biệt là định vị pixel phụ không còn hoạt động nên chuyển động không được mượt mà).

Những điều tôi đã thử / xem xét:

  • khử răng cưa giảm, nhưng không loại bỏ hoàn toàn, các đường nối
  • tắt mipmapping, không có tác dụng
  • hiển thị từng ô riêng lẻ và đùn các cạnh bằng 1px - nhưng đây là tối ưu hóa, vì nó không còn có thể hiển thị các vùng của gạch trong một lần và tạo các vật phẩm khác dọc theo các cạnh của các khu vực trong suốt
  • thêm viền 1px xung quanh ảnh nguồn và lặp lại các pixel cuối cùng - nhưng sau đó chúng không còn là sức mạnh của hai, gây ra sự cố tương thích với các hệ thống không có hỗ trợ NPOT
  • viết một shader tùy chỉnh để xử lý các hình ảnh lát gạch - nhưng sau đó bạn sẽ làm gì khác? GL_REPEATnên lấy pixel từ phía đối diện của hình ảnh ở viền và không chọn độ trong suốt.
  • hình học là chính xác liền kề, không có lỗi làm tròn điểm nổi.
  • nếu shader mảnh được mã hóa cứng để trả về cùng màu, các đường nối sẽ biến mất .
  • nếu kết cấu được đặt thành GL_CLAMPthay vì GL_REPEAT, các đường nối sẽ biến mất (mặc dù kết xuất bị sai).
  • nếu kết cấu được đặt thành GL_MIRRORED_REPEAT, các đường nối sẽ biến mất (mặc dù kết xuất lại bị sai).
  • Nếu tôi làm nền đỏ, các đường may vẫn trắng. Điều này cho thấy nó lấy mẫu màu trắng đục từ một nơi nào đó chứ không phải trong suốt.

Vì vậy, các đường nối chỉ xuất hiện khi GL_REPEATđược thiết lập. Đối với một số lý do chỉ trong chế độ này, tại các cạnh của hình học có một số chảy máu / rò rỉ / trong suốt. Làm thế nào mà có thể được? Toàn bộ kết cấu mờ đục.


Bạn có thể thử đặt bộ lấy mẫu kết cấu của mình thành kẹp hoặc điều chỉnh màu đường viền, vì tôi nghi ngờ nó có thể liên quan đến điều đó. Ngoài ra, GL_Repeat chỉ đơn giản là bao bọc các tọa độ UV, vì vậy cá nhân tôi hơi bối rối khi bạn nói rằng nó chỉ có thể lặp lại toàn bộ kết cấu, chứ không phải là một phần của nó.
Evan

1
Có thể bạn đang giới thiệu các vết nứt trong lưới của bạn bằng cách nào đó? Bạn có thể kiểm tra nó bằng cách đặt shader chỉ trả về một màu đơn hơn là lấy mẫu một kết cấu. Nếu bạn vẫn thấy các vết nứt tại thời điểm đó, chúng sẽ ở dạng hình học. Việc khử răng cưa làm giảm các đường nối cho thấy đây có thể là vấn đề, vì khử răng cưa không ảnh hưởng đến việc lấy mẫu kết cấu.
Nathan Reed

Bạn có thể thực hiện lặp lại các ô riêng lẻ trong tập bản đồ kết cấu. Bạn cần phải thực hiện một số phép toán phối hợp kết cấu bổ sung và chèn các đường viền viền xung quanh mỗi ô. Bài viết này giải thích rất nhiều về điều này (mặc dù chủ yếu tập trung vào quy ước phối hợp kết cấu khó chịu của D3D9). Ngoài ra, nếu việc triển khai của bạn đủ mới, bạn có thể sử dụng kết cấu mảng cho bản đồ ô của mình; giả sử mỗi ô có cùng kích thước, điều này sẽ hoạt động tốt và sẽ không yêu cầu bất kỳ phép toán phối hợp bổ sung nào.
Andon M. Coleman

Hoạ tiết 3D với GL_NEARESTlấy mẫu Rtheo hướng tọa độ cũng hoạt động giống như kết cấu mảng cho hầu hết mọi thứ trong kịch bản này. Mipmapping sẽ không hoạt động, nhưng đánh giá bằng ứng dụng của bạn, có lẽ bạn không cần mipmap.
Andon M. Coleman

1
Theo cách nào thì kết xuất "sai", khi được đặt thành CLAMP?
Martijn Courteaux

Câu trả lời:


1

Các đường nối là hành vi chính xác để lấy mẫu với GL_REPEAT. Hãy xem xét các lát sau:

gạch biên giới

Lấy mẫu trên các cạnh của gạch bằng các giá trị phân đoạn trộn màu từ cạnh và cạnh đối diện, dẫn đến màu sai: Cạnh trái phải là màu xanh lá cây, nhưng là sự pha trộn từ màu xanh lá cây và màu be. Cạnh phải là màu be, nhưng cũng là một màu hỗn hợp. Đặc biệt là các đường màu be trên nền màu xanh lá cây rất dễ nhìn thấy, nhưng nếu nhìn kỹ bạn có thể thấy màu xanh lá cây chảy vào mép:

ngói tỷ lệ

khử răng cưa giảm, nhưng không loại bỏ hoàn toàn, các đường nối

MSAA hoạt động bằng cách lấy nhiều mẫu hơn xung quanh các cạnh đa giác. Các mẫu được lấy bên trái hoặc bên phải của cạnh sẽ là "màu xanh lá cây" (một lần nữa, xem xét cạnh trái của hình ảnh đầu tiên) và chỉ có một mẫu sẽ được "trên" cạnh, lấy mẫu một phần từ khu vực "màu be". Khi các mẫu được trộn, hiệu quả sẽ giảm.

Phương pháp khả thi:

Trong mọi trường hợp, bạn nên chuyển sang GL_CLAMPđể ngăn chảy máu từ pixel ở phía đối diện của kết cấu. Sau đó, bạn có ba lựa chọn:

  1. Cho phép khử răng cưa để làm trơn tru quá trình chuyển đổi giữa các ô một chút.

  2. Đặt bộ lọc kết cấu thành GL_NEAREST. Điều này cung cấp cho tất cả các pixel các cạnh cứng, do đó các cạnh đa giác / sprite trở nên không thể phân biệt được, nhưng rõ ràng nó thay đổi phong cách của trò chơi khá nhiều.

  3. Thêm đường viền 1px đã thảo luận, chỉ cần đảm bảo đường viền có màu của ô liền kề (chứ không phải màu của cạnh đối diện).

    • Đây cũng có thể là thời điểm tốt để chuyển sang tập bản đồ kết cấu (chỉ cần làm cho nó lớn hơn nếu bạn quan tâm đến hỗ trợ NPOT).
    • Đây là giải pháp "hoàn hảo" duy nhất, cho phép GL_LINEARlọc giống như các cạnh của đa giác / họa tiết.

OOh, cảm ơn - sự bao bọc xung quanh kết cấu không giải thích điều đó. Tôi sẽ xem xét các giải pháp đó!
AshleyBrain

6

Bởi vì theo mặc định, OpenGL chỉ có thể bao bọc toàn bộ kết cấu (GL_REPEAT) và không chỉ là một phần của nó, mỗi ô được chia thành một kết cấu riêng biệt. Sau đó, các vùng của cùng một ô được hiển thị liền kề nhau.

Hãy xem xét việc hiển thị một hình tứ có kết cấu thông thường trong OpenGL. Có bất kỳ đường nối, ở quy mô nào? Không bao giờ. Mục tiêu của bạn là để tất cả các ô cảnh của bạn được đóng gói chặt chẽ vào một kết cấu duy nhất, sau đó gửi nó đến màn hình. EDIT Để làm rõ điều này hơn nữa: Nếu bạn có các đỉnh giới hạn riêng biệt trên mỗi ô vuông, bạn sẽ có các đường nối dường như không chính đáng trong hầu hết các trường hợp. Đó là cách phần cứng GPU hoạt động ... lỗi dấu phẩy động tạo ra những khoảng trống này dựa trên phối cảnh hiện tại ... các lỗi được tránh trên một đa tạp thống nhất, vì nếu hai mặt liền kề trong cùng một đa tạp(Subesh), phần cứng sẽ khiến chúng không có đường nối, đảm bảo. Bạn đã thấy điều này trong vô số trò chơi và ứng dụng. Bạn phải có một kết cấu được đóng gói chặt chẽ trên một lớp con duy nhất mà không cần gấp đôi đỉnh để tránh điều này một lần và mãi mãi. Đây là một vấn đề xuất hiện nhiều lần trên trang web này và các nơi khác: nếu bạn không hợp nhất các đỉnh góc của gạch của mình (cứ 4 gạch chia sẻ một đỉnh góc), hãy mong đợi các đường nối.

(Hãy xem xét rằng bạn thậm chí có thể không cần các đỉnh trừ ở bốn góc của toàn bộ bản đồ ... phụ thuộc vào cách tiếp cận của bạn để tạo bóng.)

Để giải quyết: kết xuất (với tỷ lệ 1: 1) tất cả các kết cấu ô của bạn vào một FBO / RBO không có khoảng trống, sau đó gửi FBO đó đến bộ đệm khung mặc định (màn hình). Bởi vì bản thân FBO về cơ bản là một kết cấu duy nhất, bạn không thể kết thúc với những khoảng trống về tỷ lệ. Tất cả các ranh giới texel không rơi vào ranh giới pixel màn hình sẽ được trộn nếu bạn đang sử dụng GL_LINEAR .... đó chính xác là những gì bạn muốn. Đây là cách tiếp cận tiêu chuẩn.

Điều này cũng mở ra một số tuyến đường khác nhau để nhân rộng:

  • nhân rộng kích thước của hình tứ giác mà bạn sẽ kết xuất FBO
  • thay đổi tia cực tím trên bộ tứ đó
  • loay hoay với cài đặt camera

Tôi không nghĩ rằng điều này sẽ làm việc tốt cho chúng tôi. Có vẻ như bạn đang đề xuất rằng ở mức thu phóng 10%, chúng tôi sẽ tạo ra diện tích lớn hơn gấp 10 lần. Điều này không tốt cho các thiết bị có tỷ lệ lấp đầy hạn chế. Ngoài ra, tôi vẫn còn thắc mắc tại sao cụ thể GL_REPEAT chỉ gây ra các đường nối và không có chế độ nào khác. Làm thế nào mà có thể được?
AshleyBrain

@AshleyBrain Không sequitur. Tôi đang bối rối bởi tuyên bố của bạn. Vui lòng giải thích cách tăng zoom 10% làm tăng tỷ lệ lấp đầy gấp 10 lần? Hoặc làm thế nào để tăng thu phóng lên gấp 10 lần - vì việc cắt chế độ xem sẽ ngăn tỷ lệ lấp đầy hơn 100% trên một lượt? Tôi gợi ý bạn hiển thị chính xác những gì bạn đã có ý định - không hơn, không kém - trong những gì có thể là một nữa thời trang hiệu quả và liền mạch, bởi thống nhất kết quả cuối cùng của bạn sử dụng RTT, một kỹ thuật phổ biến. Phần cứng sẽ xử lý cắt khung nhìn, chia tỷ lệ, trộn và nội suy mà hầu như không mất phí, vì đây chỉ là một công cụ 2D.
Kỹ sư

@AshleyBrain Tôi đã chỉnh sửa câu hỏi của mình để làm cho các vấn đề với cách tiếp cận hiện tại của bạn trở nên rõ ràng.
Kỹ sư

@ArcaneEngineer Xin chào, tôi đã thử cách tiếp cận của bạn để giải quyết hoàn hảo vấn đề đường may. Tuy nhiên, bây giờ, thay vì có các đường nối, tôi có lỗi pixel. Bạn có thể có một ý tưởng về vấn đề tôi đang đề cập ở đây . Bạn có biết những gì có thể là nguyên nhân của điều này? Lưu ý rằng tôi đang làm mọi thứ trong WebGL.
Nemikolh

1
@ArcaneEngineer Tôi sẽ hỏi một câu hỏi mới, giải thích ở đây quá cồng kềnh. Tôi xin lỗi vì sự khó chịu.
Nemikolh

1

Nếu hình ảnh xuất hiện dưới dạng một bề mặt, hãy hiển thị những hình này dưới dạng một kết cấu bề mặt (tỷ lệ 1x) trên một lưới / mặt phẳng 3D. Nếu bạn kết xuất từng đối tượng 3D riêng biệt, nó sẽ luôn có các đường nối do lỗi làm tròn.

Câu trả lời của @ Nick-Wiggill là chính xác, tôi nghĩ bạn đã hiểu nhầm nó.


0

2 khả năng có thể đáng để thử:

  1. Sử dụng GL_NEARESTthay vì GL_LINEARlọc kết cấu của bạn. (Như Andon M. Coleman đã chỉ ra)

    GL_LINEARsẽ (trong hiệu ứng) làm mờ hình ảnh một chút (nội suy giữa các pixel gần nhất), cho phép chuyển màu mượt mà từ một pixel sang pixel tiếp theo. Điều này có thể giúp làm cho kết cấu trông đẹp hơn trong nhiều trường hợp, vì nó ngăn kết cấu của bạn có các pixel khối đó trên tất cả. Nó cũng làm cho nó sao cho một chút màu nâu từ một pixel của một ô trên có thể bị mờ sang pixel xanh lục liền kề của ô liền kề.

    GL_NEARESTchỉ cần tìm pixel gần nhất và sử dụng màu đó. Không nội suy. Kết quả là, nó thực sự nhanh hơn một chút.

  2. Thu nhỏ tọa độ kết cấu cho mỗi gạch một chút.

    Sắp xếp giống như thêm pixel đó xung quanh mỗi ô dưới dạng bộ đệm, ngoại trừ thay vì mở rộng ô trên ảnh, bạn thu nhỏ ô trong phần mềm. Gạch 14x14px trong khi kết xuất thay vì gạch 16x16px.

    Bạn thậm chí có thể thoát khỏi gạch 14,5x14,5 mà không có quá nhiều màu nâu được trộn vào.

--chỉnh sửa--

Hình dung của lời giải thích trong tùy chọn # 2

Như thể hiện trong hình trên, bạn vẫn có thể dễ dàng sử dụng sức mạnh của hai kết cấu. Vì vậy, bạn có thể hỗ trợ phần cứng không hỗ trợ kết cấu NPOT. Những thay đổi là tọa độ kết cấu của bạn, thay vì đi từ (0.0f, 0.0f)đến (1.0f, 1.0f)Bạn sẽ đi từ (0.03125f, 0.03125f)đến (0.96875f, 0.96875f).

Điều đó đặt co-tex của bạn một chút bên trong ô làm giảm độ phân giải hiệu quả trong trò chơi (mặc dù không phải trên phần cứng để bạn vẫn có kết cấu mạnh mẽ của hai), tuy nhiên sẽ có hiệu ứng kết xuất giống như mở rộng kết cấu.


Tôi đã đề cập rằng tôi đã thử GL_NEAREST (còn gọi là lọc điểm) và nó không khắc phục được. Tôi không thể làm số 2 vì tôi cần hỗ trợ các thiết bị NPOT.
AshleyBrain

Thực hiện một chỉnh sửa có thể làm sáng tỏ một số điều. (Tôi giả sử rằng "cần hỗ trợ các thiết bị NPOT [(không phải là hai năng lượng)]" có nghĩa là một cái gì đó dọc theo dòng của bạn cần để giữ kết cấu ở mức 2?)
Wolfgang Skyler

0

Đây là những gì tôi nghĩ rằng có thể xảy ra: Khi hai gạch va chạm, thành phần x của các cạnh của chúng phải bằng nhau. Nếu điều đó không đúng, họ có thể bị làm tròn sang hướng ngược lại. Vì vậy, hãy chắc chắn rằng chúng có cùng giá trị x. Làm thế nào để bạn đảm bảo rằng điều này xảy ra? Bạn có thể cố gắng, giả sử, nhân với 10, làm tròn và sau đó chia cho 10 (chỉ làm điều này trên CPU, trước khi các đỉnh của bạn đi vào trình tạo bóng đỉnh). Điều đó sẽ cho kết quả chính xác. Ngoài ra, không vẽ ô bằng cách sử dụng ma trận biến đổi, mà đặt chúng vào một VBO theo lô để đảm bảo rằng kết quả sai không đến từ hệ thống điểm trôi nổi mất kết hợp với phép nhân ma trận.

Tại sao tôi đề nghị điều này? Bởi vì theo kinh nghiệm của tôi, nếu các đỉnh có cùng tọa độ chính xác khi chúng ra khỏi bộ đổ bóng đỉnh, việc điền vào các hình tam giác tương ứng sẽ tạo ra kết quả liền mạch. Hãy nhớ rằng một cái gì đó là chính xác về mặt toán học, có thể là một chút do IEEE. Bạn càng thực hiện nhiều phép tính trên các số của mình, kết quả sẽ càng kém chính xác. Và đúng vậy, phép nhân ma trận đòi hỏi khá nhiều thao tác, có thể được thực hiện chỉ bằng phép nhân và phép cộng khi tạo VBO của bạn, điều này sẽ cho kết quả chính xác hơn.

Điều cũng có thể là vấn đề là bạn đang sử dụng một spritesheet (còn được gọi là atlas) và khi lấy mẫu kết cấu, các pixel của kết cấu gạch liền kề sẽ được chọn. Để đảm bảo rằng điều này không xảy ra là tạo ra một đường viền nhỏ trong ánh xạ UV. Vì vậy, nếu bạn có một lát 64x64, ánh xạ UV của bạn sẽ che đi một chút. Bao nhiêu? Tôi nghĩ rằng tôi đã sử dụng trong các trò chơi của mình 1 phần tư pixel ở mỗi bên của hình chữ nhật. Vì vậy, hãy bù UV của bạn bằng 1 / (4 * widthOfTheAtlas) cho các thành phần x và 1 / (4 * heightOfTheAtlas) cho các thành phần y.


Cảm ơn, nhưng như tôi đã lưu ý rằng hình học chắc chắn là liền kề - thực tế tất cả các tọa độ đều dẫn đến các số nguyên chính xác, vì vậy thật dễ dàng để nói. Không có atlase được sử dụng ở đây.
AshleyBrain

0

Để giải quyết vấn đề này trong không gian 3D, tôi đã trộn các mảng kết cấu với đề xuất của Wolfgang Skyler. Tôi đặt đường viền 8 pixel xung quanh kết cấu thực của mình cho mỗi ô (tổng 120x120, 128x128), ở mỗi bên, tôi có thể điền vào hình ảnh "được bọc" hoặc bên được mở rộng. Người lấy mẫu đọc khu vực này khi nó đang nội suy hình ảnh.

Bây giờ với tính năng lọc và mipmapping, bộ lấy mẫu vẫn có thể dễ dàng đọc qua toàn bộ viền 8 pixel. Để giải quyết vấn đề nhỏ đó (tôi nói nhỏ vì nó chỉ xảy ra trên một vài pixel khi hình học thực sự bị lệch hoặc ở xa), tôi chia các ô thành một mảng kết cấu, vì vậy mỗi ô có không gian kết cấu riêng và có thể sử dụng kẹp trên các cạnh.

Trong trường hợp của bạn (2D / căn hộ), tôi chắc chắn sẽ thực hiện việc hiển thị một cảnh hoàn hảo pixel, và sau đó chia tỷ lệ phần kết quả mong muốn vào chế độ xem, như đề xuất của Nick Wiggil.

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.