텍스처 아틀라스에서 텍스처 번짐을 피하는 방법은 무엇입니까?


46

내 게임에는 큐브로 만들어진 Minecraft와 같은 지형이 있습니다. 복셀 데이터에서 정점 버퍼를 생성하고 다른 블록의 모양에 텍스처 아틀라스를 사용합니다.

복셀 기반 지형의 텍스처 아틀라스

문제는 먼 큐브의 텍스처가 텍스처 아틀라스의 인접한 타일과 보간된다는 것입니다. 큐브 사이에 잘못된 색상이 표시됩니다 (그래픽 결함을 보려면 아래의 스크린 샷을 전체 크기로 봐야 할 수 있음).

잘못된 색상의 줄무늬가있는 지형의 스크린 샷

지금은 이러한 보간 설정을 사용하지만 모든 조합을 시도했지만 GL_NEAREST밉 매핑 없이도 더 나은 결과를 제공하지는 않습니다.

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);

타일의 약간 작은 영역을 선택하기 위해 텍스처 좌표에 오프셋을 추가하려고했지만 원치 않는 효과는 카메라까지의 거리에 따라 다르므로 문제를 완전히 해결할 수는 없습니다. 먼 거리에서 줄무늬가 발생합니다.

이 텍스처 출혈을 어떻게 해결할 수 있습니까? 텍스처 아틀라스를 사용하는 것이 널리 사용되는 기술이므로 일반적인 접근 방식이있을 수 있습니다. 안타깝게도 주석에 설명 된 몇 가지 이유로 다른 텍스처 또는 텍스처 배열로 변경할 수 없습니다.


4
문제는 밉 매핑에 있습니다. 텍스처 좌표가 가장 큰 밉 레벨에서 잘 작동 할 수 있지만 더 멀어 질수록 경계 타일이 혼합됩니다. 밉맵 (아마도 좋은 생각은 아님)을 사용하지 않고, 가드 영역 (타일 사이의 영역)을 늘리고, 밉 레벨 (까다로운)을 기반으로 셰이더에서 가드 영역을 동적으로 늘리고, 밉맵을 생성하는 동안 이상한 작업을 수행하여 ( 그래도 아무것도 생각하지 않습니다 .. 나는이 문제를 직접 해결 한 적이 없지만 시작할 것입니다 .. =)
Jari Komppa

슬프게도 밉 매핑을 해제해도 문제가 해결되지 않습니다. 밉맵이 없으면 선형 GL_LINEAR을 보간 하여 비슷한 결과 GL_NEAREST를 얻 거나 가장 가까운 픽셀 을 선택하여 블록 사이에 줄무늬가 생깁니다. 마지막으로 언급 된 옵션은 픽셀 결함을 줄이지 만 제거하지는 않습니다.
danijar

7
한 가지 해결책은 밉맵을 생성 한 형식을 사용하는 것입니다. 그런 다음 밉맵을 직접 만들고 오류를 수정할 수 있습니다. 다른 솔루션은 텍스처를 샘플링 할 때 프래그먼트 셰이더에서 특정 밉맵 레벨을 강제하는 것입니다. 또 다른 해결책은 첫 번째 밉맵으로 밉맵을 채우는 것입니다. 즉, 텍스처를 만들 때 동일한 데이터를 포함하는 특정 이미지에 하위 이미지를 추가 할 수 있습니다.
Tordin

1
전에이 문제가 있었고 서로 다른 질감 사이에 아틀라스에 1-2 픽셀 패딩을 추가하여 해결되었습니다.
Chuck D

1
@ 킬로 탄. 문제는 밉맵, 선형 보간 또는 가장 가까운 픽셀 선택으로 수행되는지에 관계없이 텍스처 크기 조정에 관한 것입니다.
danijar

답변:


34

네, 여기 두 가지 문제가 있다고 생각합니다.

첫 번째 문제는 밉 매핑입니다. 일반적으로 모든 하위 텍스처가 정확히 1x1 픽셀 크기가 아닌 한 텍스처 블리딩이 발생하기 때문에 아틀라스와 밉 매핑을 순진하게 혼합하고 싶지 않습니다. 패딩을 추가하면 문제가 더 낮은 밉 레벨로 이동합니다.

경험적으로 일반적으로 아틀라스를 수행하는 2D의 경우 밉맵은 없습니다. 밉맵 인 3D의 경우 아틀라스가 없습니다 (실제로 출혈이 문제가되지 않는 방식으로 피부를 닦습니다)

그러나 지금 귀하의 프로그램으로 진행하고 있다고 생각하는 것은 올바른 질감 좌표를 사용하지 않았으며 질감으로 인해 출혈이 발생한다는 것입니다.

8x8 텍스처를 가정 해 봅시다. 아마도 (0,0)에서 (0.5, 0.5)의 uv 좌표를 사용하여 왼쪽 상단을 얻는 것 같습니다.

잘못된 매핑

그리고 문제는 u 좌표 0.5에서 샘플러가 왼쪽 텍셀의 절반과 오른쪽 텍셀의 절반을 사용하여 대상 조각을 보간한다는 것입니다. U 좌표 0에서도 마찬가지이며 V 좌표에서도 마찬가지입니다. 이것은 중심이 아닌 텍셀 사이의 경계를 다루기 때문입니다.

대신해야 할 일은 각 텍셀의 중심을 다루는 것입니다. 이 예에서 사용하려는 좌표는 다음과 같습니다.

올바른 매핑

이 경우, 항상 각 텍셀의 중심을 샘플링 할 것이며 출혈이 없어야합니다 ... 밉 매핑을 사용하지 않으면 스케일링 된 서브 텍스처가 깔끔하지 않은 밉 레벨에서 항상 출혈이 생깁니다. 픽셀 격자 안에 맞습니다 .

일반화하기 위해 특정 텍셀에 액세스하려는 경우 좌표는 다음과 같이 계산됩니다.

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

이것을 "반 화소 보정"이라고합니다. 관심이 있다면 여기에 더 자세한 설명이 있습니다 .


당신의 설명은 매우 유망한 것 같습니다. 슬프게도 현재 비디오 카드를 사용해야하므로 아직 구현할 수 없습니다. 수리 된 카드가 도착하면 그렇게하겠습니다. 그리고 타일의 경계를 보간하지 않고 아틀라스의 밉맵을 스스로 계산할 것입니다.
danijar

@sharethis 확실히 아틀라스가 필요한 경우 하위 텍스처의 크기가 2의 거듭 제곱인지 확인하여 출혈을 최저 밉 수준으로 제한 할 수 있습니다. 그렇게하면 실제로 밉맵을 직접 만들 필요가 없습니다.
Panda Pajama

이중 선형 필터링은 이러한 경계를 가로 질러 샘플링 할 수 있기 때문에 PoT 크기의 텍스처조차도 블리드 아티팩트를 가질 수 있습니다. (적어도 내가 본 구현에서) 반 픽셀 수정에 관해서는이 경우에 적용되어야합니까? 예를 들어 그의 바위 텍스처를 매핑하려면 U와 V 좌표를 따라 항상 0.0에서 0.25가됩니까?
Kylotan

1
@Kylotan 텍스처 크기가 고르고 모든 서브 텍스처의 크기가 고르고 좌표가 같은 경우 텍스처 크기를 반으로 줄 였을 때 이중 선형 필터링은 서브 텍스처간에 혼합되지 않습니다. 2의 거듭 제곱을 일반화합니다 (아티팩트가 표시되는 경우 이중 선형 필터링이 아닌 이중 입방 형을 사용하는 것입니다). 반 픽셀 보정과 관련하여 0.25가 가장 오른쪽에있는 텍셀이 아니라 두 하위 텍스처 사이의 중간 지점이기 때문에 필요합니다. 원근감있게 표현하려면 래스터 라이저라고 생각하십시오. 텍스처의 색상은 (0.00, 0.25)입니까?
Panda Pajama 2012

1
@sharethis 내가 당신에게 gamedev.stackexchange.com/questions/50023/
Panda Pajama

19

밉맵으로 조정하고 텍스처 좌표 퍼지 요소를 추가하는 것보다 훨씬 쉬운 옵션은 텍스처 배열을 사용하는 것입니다. 텍스처 배열은 3d 텍스처와 유사하지만 3 차원에서 밉 매핑이 없으므로 "하위 텍스처"가 모두 같은 크기 인 텍스처 아틀라스에 이상적입니다.

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


사용 가능한 경우 훨씬 더 나은 솔루션입니다. 텍스처를 래핑하는 등 아틀라스로 놓친 다른 기능도 있습니다.
Jari Komppa

@JariKomppa. 텍스처 배열을 사용하고 싶지 않습니다. 그러면 지형에 추가 쉐이더가 있기 때문입니다. 내가 작성하는 엔진은 가능한 한 일반적이어야하므로이 예외를 원하지 않습니다. 텍스처 배열과 단일 텍스처를 모두 처리 할 수있는 하나의 셰이더를 만드는 방법이 있습니까?
danijar

8
@sharethis 당신이 "일반적인"되기를 원하기 때문에 명백히 뛰어난 기술적 솔루션을 해산하는 것은 실패의 레시피입니다. 게임을 작성하는 경우 엔진을 작성하지 마십시오.
sam hocevar

@SamHocevar. 엔진을 작성 중이며 내 프로젝트는 구성 요소가 완전히 분리 된 특정 아키텍처에 관한 것입니다. 따라서 폼 구성 요소는 표준 버퍼와 표준 텍스처 만 제공해야하는 터 레인 구성 요소를 처리하지 않아야합니다.
danijar

1
@sharethis OK. 나는 질문의 "내 게임에서"부분에 어떻게 든 오해되었다.
sam hocevar

6

이 문제로 많은 어려움을 겪은 후 마침내 해결책을 찾았습니다.

텍스처 아틀라스와 밉 매핑을 모두 사용하려면 OpenGL이 아틀라스의 타일 경계를 보간하기 때문에 다운 샘플링을 직접 수행해야합니다. 또한 밉맵 사이의 보간으로 인해 텍스처 번짐이 발생하기 때문에 올바른 텍스처 매개 변수를 설정해야했습니다.

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

이 매개 변수를 사용하면 출혈이 발생하지 않습니다.

커스텀 밉맵을 제공 할 때주의해야 할 것이 있습니다. 각 타일이 이미 있더라도 아틀라스를 축소하는 것은 의미가 없으므로 1x1최대 밉맵 수준은 제공 한 내용에 따라 설정해야합니다.

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

다른 모든 답변과 매우 유용한 의견에 감사드립니다! 그런데 여전히 선형 스케일링을 사용하는 방법을 모르지만 방법이 없다고 생각합니다.


당신이 그것을 해결했다는 것을 알고 반갑습니다. 이 답변을 내 대신 올바른 답변으로 표시해야합니다.
Panda Pajama

밉 매핑은 다운 샘플링 전용 기술이며 업 샘플링에는 의미가 없습니다. 작은 삼각형을 그리려면 큰 픽셀을 추출하기 위해 큰 텍스처를 샘플링하는 데 많은 시간이 걸리므로 미리 다운 샘플링하고 적절한 밉 레벨을 사용하는 것이 좋습니다 작은 삼각형 그리기. 더 큰 삼각형의 경우 더 큰 밉 레벨과 다른 것을 사용하는 것은 의미가 없으므로 다음과 같은 작업을 수행하는 것은 오류입니다.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_NEAREST_MIPMAP_LINEAR);
Panda Pajama

@PandaPajama. 네 말이 맞아, 나는 내 대답을 수정했다. 설정하는 방법 GL_TEXTURE_MAX_FILTERGL_NEAREST_MIPMAP_LINEAR밉 매핑의 이유없는 출혈 원인이 있지만, 보간의 이유. 따라서이 경우 GL_LINEAR가장 낮은 밉맵 수준이 사용되므로 동등 합니다.
danijar

@danijar-다운 샘플링을 직접 수행하는 방법과 OpenGL에 다운 샘플링 된 아틀라스를 언제 어디서 사용할지 어떻게 설명 할 수 있습니까?
Tim Kane

1
@TimKane 아틀라스를 타일로 나눕니다. 각 밉맵 레벨에 대해 타일을 이중선으로 다운 샘플링하고 결합한 다음 밉맵 레벨을 매개 변수로 지정하여 GPU에 업로드합니다 glTexImage2D(). GPU는 화면의 객체 크기에 따라 올바른 레벨을 자동으로 선택합니다.
danijar

4

MSAA로 인해 문제가 발생한 것 같습니다. 참조 이 답변링크 된 기사를 :

"MSAA를 켜면 픽셀 영역 내부에 있지만 삼각형 영역 외부에있는 샘플에 대해 셰이더를 실행할 수있게됩니다."

해결책은 중심 샘플링을 사용하는 것입니다. 정점 셰이더에서 텍스처 좌표의 줄 바꿈을 계산하는 경우 해당 로직을 프래그먼트 셰이더로 이동하면 문제가 해결 될 수도 있습니다.


이미 다른 의견을 작성 했으므로 현재 작동중인 비디오 카드가 없으므로 실제 문제인지 확인할 수 없습니다. 가능한 빨리 그렇게하겠습니다.
danijar

하드웨어 앤티 앨리어싱을 비활성화했지만 출혈은 여전히 ​​남아 있습니다. 프래그먼트 셰이더에서 텍스처를보고 있습니다.
danijar

1

이것은 오래된 주제이며 주제와 비슷한 문제가 있습니다.

텍스처 좌표는 괜찮 았지만 카메라 위치 (요소의 위치를 ​​변경 한)를 다음과 같이 유지했습니다.

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;
}

전체 문제는 Util.lerp ()가 float 또는 double을 반환한다는 것입니다. 카메라가 그리드 외부를 번역하고 X에서> 0, Y에서> 0 인 텍스처 스 니펫에 이상한 문제가 발생했기 때문에 이것은 나빴습니다.

TLDR : 트윈을 사용하지 않거나 수레를 사용하는 것

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.