눈팅하는 게임개발자 블로그

OpenGL Advanced Lighting 6-5 Parallax Mapping 본문

공부한거/OpenGL

OpenGL Advanced Lighting 6-5 Parallax Mapping

Palamore 2020. 12. 14. 14:00

원문 사이트

learnopengl.com/Advanced-Lighting/Parallax-Mapping

 

LearnOpenGL - Parallax Mapping

Parallax Mapping Advanced-Lighting/Parallax-Mapping Parallax mapping is a technique similar to normal mapping, but based on different principles. Just like normal mapping it is a technique that significantly boosts a textured surface's detail and gives it

learnopengl.com

번역 사이트

gyutts.tistory.com/175?category=755809

 

LearnOpenGL - Advanced Lighting : Parallax Mapping

link : https://learnopengl.com/Advanced-Lighting/Parallax-Mapping Parallax Mapping  Parallax Mapping은 일반적인 맵핑과 비슷한 기술이지만 다른 원칙에 기반한다. 일반 맵핑과 마찬가지로 텍스처 표면의..

gyutts.tistory.com

Parallax Mapping

Parallax Mapping은 일반적인 매핑과 비슷한 기술이지만 다른 원칙을 기반으로 한다.

normal mapping과 마찬가지로 텍스처 표면의 디테일을 대폭 향상시키고 깊이감을 주는 기법이다.

또한, parallax 매핑은 깊이 감각을 전달하는 것에 훨씬 뛰어나면서도 normal mapping과 함께 매우 현실적인 결과를 보여준다.

parallax 매핑은 반드시 조명과 직접적으로 관련이 있는 기술은 아니지만, 이 기술은 normal 매핑의 논리적인 후속작업이므로

여기서도 조명에 대해 논의할 것이다.

parallax 매핑에 대해 배우기 전에 normal mapping, 특히 탄젠트 공간에 대한 이해를 강력히 권장한다.

 

parallax 매핑은 텍스처 내부에 저장된 기하학적 정보를 기반으로 정점을 대체하거나 오프셋하는 변위 매핑 기술에 속한다.

이를 수행하는 한 가지 방법은 약 1000개의 정점을 가진 평면을 가져와서 특정 영역에서 평면의 높이를 알려주는

텍스처의 값에 따라 각 정점을 대체하는 것이다.

텍셀당 높이 값을 포함하는 텍스처를 높이 맵이라고 한다.

간단한 벽돌 표면의 기하학적 특성에서 파생된 높이 맵의 예는 다음과 같다.

평면 위에 걸쳐지면 각 꼭지점은 높이 맵의 샘플링된 높이 값을 기준으로 대체되어 재질의 기하학적 특성을 기반으로

평면이 거친 울퉁불퉁한 표면으로 변형된다.

예를 들어 위의 높이맵으로 대체된 평면을 가져오면 다음 이미지가 생성된다.

꼭지점을 옮기는 문제는 현실적인 변위를 얻기 위해 plane이 많은 양의 삼각형으로 구성되어야 한다는 것이다.

그렇지 않으면 변위가 너무 거칠어 보인다, 각각의 평평한 표면이 1000개 이상의 정점을 요구할 수 있기 때문에

이는 빠르게 계산이 불가능하다.

여분의 꼭지점이 필요없이 비슷한 사실주의를 어떻게든 얻을 수 있다면 어떨까?

사실, 위의 변위된 표면이 실제로 6개의 꼭지점으로 렌더링되었다면?

이 벽돌 표면은 parallax mapping으로 렌더링되었다.

변위 매핑 기술은 깊이를 전달하기 위해 추가 정점 데이터를 필요로 하지 않지만

normal 매핑과 비슷하게 유저를 속이기 위한 영리한 기술을 사용한다.

 

parallax 매핑의 기본 개념은 fragment의 표면이 실제보다 높거나 낮게 보이는 방식으로 텍스처 방향을 변경하는 것이다.

모든 것은 뷰 방향과 높이 맵을 기반으로 한다.

작동 원리를 이해하기 위해 다음과 같은 벽돌 표면의 이미지를 살펴보자.

여기서 붉은 색 선은 높이 맵의 값을 벽돌 표면의 기하학적 표면 표현으로 나타내고,

벡터 V는 방향을 보는 표면을 나타낸다. (viewDir)

평면에 실제 변위가 있을 경우 관측자는 점 B에서 표면을 볼 수 있다.

그러나 평면에 실제 변위가 없으므로 뷰 방향은 평면에서 A점으로 평평한 평면에 도달한다.

parallax 매핑은 B 지점에서 텍스처 좌표를 얻는 방식으로 fragment 위치 A에서 텍스처 좌표를 오프셋하는 것을 목표로 한다.

그 다음 모든 후속 텍스처 샘플에 대해 B점에서 텍스처 좌표를 사용해 뷰어가 실제로 보고 있는 것처럼 보이게 한다.

 

점 A에서 점 B의 텍스처 좌표를 얻는 방법을 찾는 것이 트릭이다.

parallax 매핑은 fragmnet A의 높이로 fragment-to-view 방향 벡터 V를 스케일링해 이를 해결하려고 시도한다.

V의 길이는 fragment 위치 A에서의 높이 맵 H(A)로부터 샘플링된 값과 같아야 한다.

아래 이미지는 이 스케일된 벡터 P를 보여준다.

이 벡터 P를 취해 평면과 정렬된 벡터 좌표를 텍스처 좌표 오프셋으로 취한다.

이는 벡터 P가 높이 맵의 높이 값을 사용해 계산되기 때문에 fragment의 높이가 높을 수록 효과적으로 대체된다.

 

이 작은 트릭은 대부분 좋은 결과를 주지만 근사값에 불과하다는 한계가 존재한다.

표면의 높이가 급격하게 변하면 벡터 P가 B에 가까워지지 않으며 이는 비현실적인 결과가 되어버린다.

다음과 같은 경우가 그러하다.

parallax 매핑의 또 다른 문제점은 표면이 어떤 방식으로든 임의로 회전할 때 P에서 검색할 좌표를 파악하기 어렵다는 것이다.

벡터 P의 x와 y 성분이 항상 텍스처의 표면과 정렬되는 다른 좌표 공간에서의 parallax 매핑이다.

접선 공간에서의 parallax 매핑을 수행한다.

 

fragment-to-view 벡터 V를 접선 공간으로 변환함으로써 변환된 P 벡터는 그 표면의 탄젠트 및 접선 벡터에 정렬된 x 및 y 성분을 가질 것이다.

접선 벡터와 접하는 벡터가 표면의 텍스처 좌표와 같은 방향을 가리키고 있기 때문에 표면의 방향에 관계없이

P의 x 및 y 구성 요소를 텍스처 좌표 오프셋으로 사용할 수 있다.

 

이론에 대해서는 충분하다. 실제 parallax 매핑을 수행해본다.

 

Parallax mapping

parallax 매핑의 경우 접선과 접선 벡터를 게산한 간단한 2D 평면을 GPU로 보내기 전에 이를 사용한다.

normal mapping 강좌에서 했던 것과 비슷하다.

이 예제에서는 normal 매핑과 함께 parallax 매핑을 사용한다.

parallax 매핑은 표면을 대체한다는 착각을 주기 때문에 조명이 일치하지 않으면 해당 트릭이 완성되지 않는다.

normal 맵은 종종 높이 맵에서 생성되기 때문에 높이 맵과 함께 normal 맵을 사용하면 조명이 displacement와 함께

적절한 자신의 위치에 있는지 확인할 수 있다.

 

위에 연결된 변위 맵이 이 강좌의 시작 부분에 표시된 높이 맵의 inverse라고 이미 언급되었을 것이다.

parallax 매핑을 사용하면 평평한 표면의 높이보다 가짜 깊이를 쉽게 만드는 것처럼 높이 맵의 역수를 사용하는 것이 더 적합하다.

이는 아래와 같이 parallax 매핑을 인식하는 방식을 약간 변경한다.

다시 점 A와 B를 가지지만 이번에는 점 A의 텍스처 좌표에서 벡터 V를 뺀 벡터 P를 얻는다.

쉐이더에서 1.0이 샘플된 높이 맵 값을 뺀 높이 값 대신에 깊이 값을 얻을 수 있다.

또는 위에 링크된 깊이 맵에서 했던 것처럼 이미지 편집 소프트웨어에서 단순히 텍스처 값을 반대로 변경하는 것이다.

 

변위 효과는 삼각형 표면 전체에서 다르기 때문에 parallax 매핑은 fragment 쉐이더에서 구현된다.

fragment 쉐이더에서는 fragment-to-view 벡터 V를 계산해야 하므로

접선 공간에서 뷰 위치와 fragment 위치가 필요하다.

normal 매핑 강좌에서 접선 공간에 이러한 벡터를 보내는 fragment 쉐이더가 이미 있으므로

해당 강좌의 정점 쉐이더의 정확한 복사본을 얻을 수 있다.

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent;

out VS_OUT {
    vec3 FragPos;
    vec2 TexCoords;
    vec3 TangentLightPos;
    vec3 TangentViewPos;
    vec3 TangentFragPos;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

uniform vec3 lightPos;
uniform vec3 viewPos;

void main()
{
    gl_Position      = projection * view * model * vec4(aPos, 1.0);
    vs_out.FragPos   = vec3(model * vec4(aPos, 1.0));   
    vs_out.TexCoords = aTexCoords;    
    
    vec3 T   = normalize(mat3(model) * aTangent);
    vec3 B   = normalize(mat3(model) * aBitangent);
    vec3 N   = normalize(mat3(model) * aNormal);
    mat3 TBN = transpose(mat3(T, B, N));

    vs_out.TangentLightPos = TBN * lightPos;
    vs_out.TangentViewPos  = TBN * viewPos;
    vs_out.TangentFragPos  = TBN * vs_out.FragPos;
}   

주목해야할 점은 parallax 매핑의 경우 aPos와 뷰어의 위치 viewPos를 접선 공간에 fragment shader로 보내야 한다는 것이다.

 

fragment 쉐이더 내에서 parallax 매핑 로직을 구현한다.

fragment 쉐이더는 다음과 같다.

#version 330 core
out vec4 FragColor;

in VS_OUT {
    vec3 FragPos;
    vec2 TexCoords;
    vec3 TangentLightPos;
    vec3 TangentViewPos;
    vec3 TangentFragPos;
} fs_in;

uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform sampler2D depthMap;
  
uniform float height_scale;
  
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir);
  
void main()
{           
    // offset texture coordinates with Parallax Mapping
    vec3 viewDir   = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
    vec2 texCoords = ParallaxMapping(fs_in.TexCoords,  viewDir);

    // then sample textures with new texture coords
    vec3 diffuse = texture(diffuseMap, texCoords);
    vec3 normal  = texture(normalMap, texCoords);
    normal = normalize(normal * 2.0 - 1.0);
    // proceed with lighting code
    [...]    
}

ParallaxMapping이라는 함수를 정의했는데,

이 함수는 접선 공간에서 fragment의 텍스처 좌표와 fragment-to-view V를 입력으로 취한다.

이 함수는 이동된 텍스처 좌표를 반환한다.

그 다음 이러한 이동된 텍스처 좌표를 확산 및 법선 맵을 샘플링하기 위한 좌표로 사용한다.

결과적으로 fragment의 확산 색상 및 법선 벡터는 표면의 배치된 형상과 동일하게 일치한다.

 

ParallaxMapping 함수를 살펴보자면,

vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{ 
    float height =  texture(depthMap, texCoords).r;    
    vec2 p = viewDir.xy / viewDir.z * (height * height_scale);
    return texCoords - p;    
} 

이 비교적 간단한 함수는 지금까지 논의한 것을 직접 반영한 것이다.

현재 fragment H(A)의 depthMap에서 높이를 샘플링한다. 그 다음 p를 접선 공간의 viewDir 벡터의 x, y 구성요소를

z 구성 요소로 나눈 값으로 계산하고 fragment 높이로 스케일한다.

parallax 효과는 일반적으로 여분의 눈금 매개 변수 없이 너무 강하기 때문에 일부 추가 컨트롤을 위해

height_scale 유니폼을 사용한다.

그 다음 텍스처 좌표에서 이 벡터 P를 빼서 최종 치환된 텍스처 좌표를 얻는다.

 

흥미로운 점은 viewDir.z에 의해 viewDir.xy가 분할된다는 것이다.

viewDir 벡터가 정규화되면 viewDir.z는 0.0과 1.0사이의 어딘가에 위치한다.

viewDir이 표면과 대체로 평행할 때 z 컴포넌트는 0.0에 가깝고 division은 viewDir이 표면에 대체로 수직일 때보다

훨씬 더 큰 벡터 P를 반환한다.

그래서 기본적으로 꼭대기에서 보았을 때와 비교할 때 각도에서 표면을 볼 때 더 큰 스케일에서 텍스처 좌표를

오프셋하는 방식으로 P의 크기를 늘린다. 이는 보다 사실적인 결과를 제공한다.

어떤 사람들은 norm parallax 매핑이 각도에서 바람직하지 않은 결과를 생성할 수 있으므로 방정식에서 viewDir.z로

나누기를 선호한다.

이 기술은 parallax mapping wih Offset limiting이라고 불린다.

선택해야 할 기법을 선택하는 것은 대개 개인적인 취향에 달려 있지만 보통 Parallax Mapping을 사용하는 경향이 있다.

 

결과 텍스처 좌표는 다른 텍스처를 샘플링하는 데 사용되며 아래에서 height_scale이 대략 0.1인 것으로 볼 수 있는 것처럼

아주 깔끔한 대체 효과를 제공한다.

여기서 normal 매핑과 parallax 매핑의 차이점을 볼 수 있다.

parallax 매핑은 깊이를 시뮬레이트 하기 때문에 실제로 보는 방향에 따라 다른 벽돌과 벽돌을 겹치게하는 것이 가능하다.

 

parallax 매핑된 평면의 가장자리에서 여전히 이상한 테두리 인공물을 볼 수있는데,

이는 평면의 가장자리에서 변위가 적용된 텍스처 좌표가 [0, 1]범위를 초과해 오버 샘플링할 수 있기 때문에 텍스처의 배치 모드를

기반으로 비현실적인 결과를 나타내는 것이다.

이 문제를 해결하기 위한 멋진 트릭은 기본 텍스처 좌표 범위를 벗어나 샘플링 할 때마다 fragment를 폐기하는 것이다.

texCoords = ParallaxMapping(fs_in.TexCoords,  viewDir);
if(texCoords.x > 1.0 || texCoords.y > 1.0 || texCoords.x < 0.0 || texCoords.y < 0.0)
    discard;

기본 범위 밖의 텍스처 좌표를 가진 모든 fragment는 폐기되고, parallax mapping은 표면의 가장자리 주위에서

적절한 결과를 제공한다.

이 트릭은 모든 유형의 표면에서 제대로 작동하지 않지만 plane에 적용하면 평면이 실제로 사라지는 것처럼 보이게 하는

훌륭한 결과를 제공한다.

parallax mapping을 작동시키기 위해 여분의 텍스처 샘플 하나만 있으면 된다.

하지만 이는 몇가지 문제가 있다.

특정 각도에서 보았을 때 일그러지고 아래에서 보듯이 가파른 높이 변화가 있을 경우 잘못된 결과가 나온다.

이렇게 제대로 작동하지 않는 이유는 이것이 변위 매핑의 근본적인 근사일 뿐이기 때문이다.

그러나 가파른 높이 변화에도 거의 완벽한 결과를 얻을 수 있는 몇 가지 추가 트릭이 있다.

예를 들어, 하나의 표본 대신에 B에 가장 가까운 점을 찾기 위해 여러 표본을 취하는 방법이 있다.

 

Steep Parallax Mapping

Steep Parallax Mapping은 동일한 원칙을 사용한다는 점에서 Parallax Mapping의 확장이다.

그러나 1샘플 대신 벡터 P를 B에 더 정확하게 나타내기 위해 여러 샘플을 필요로 한다.

이는 가파른 높이 변화로도 훨씬 좋은 결과를 제공한다.

기술의 정확성은 샘플 수만큼 향상된다.

 

Steep Parallax Mapping의 일반적인 개념은 전체 깊이 범위를 동일한 높이/깊이의 여러 레이어로 나누는 것이다.

이 레이어들 각각에 대해 현재 레이어의 깊이 값보다 낮은 샘플링된 깊이 값을 찾을 때까지

텍스처 좌표를 P 방향으로 시프트하는 깊이 맵을 샘플링한다.

다음 이미지를 살펴보자.

깊이 레이어를 위에서 아래로 가로지르며 각 레이어에 대해 깊이 값을 깊이 맵에 저장된 깊이 값과 비교한다.

레이어의 깊이 값이 깊이 맵의 값보다 작으면 이 레이어의 벡터 P의 일부가 표면 아래에 있지 않다는 것을 의미한다.

레이어의 깊이가 깊이 맵에 저장된 값보다 높을 때까지 이 과정을 계속한다.

이 점은 기하학적인 표면 아래에 있다.

 

이 예제에서 두 번째 레이어의 깊이 맵 값(D (2) = 0.73)이 두 번째 레이어의 깊이 값 0.4보다 여전히 낮아서 계속 진행할

수 있음을 알 수 있다. 다음 반복에서 레이어의 깊이 값 0.6은 깊이 맵의 샘플 값(D(3) = 0.37)보다 높아진다.

따라서 제 3층이 벡터 P가 변위된 기하학 트리의 가장 실용적인 위치라고 가정할 수 있다.

벡터 P3에서 텍스처 좌표 오프셋 T3을 취해 fragment의 좌표를 대체할 수 있다.

깊이가 더 깊어지면 정확도가 어떻게 증가하는지 확인할 수 있다.

 

이 기술을 구현하기 위해 필요한 모든 변수가 이미 있으므로 ParallaxMapping 함수만 변경하면 된다.

vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{ 
    // number of depth layers
    const float numLayers = 10;
    // calculate the size of each layer
    float layerDepth = 1.0 / numLayers;
    // depth of current layer
    float currentLayerDepth = 0.0;
    // the amount to shift the texture coordinates per layer (from vector P)
    vec2 P = viewDir.xy * height_scale; 
    vec2 deltaTexCoords = P / numLayers;
  
    [...]     
}   

여기에서는 먼저 레이어 수를 지정하고 각 레이어의 깊이를 계산한 다음

레이어당 P 방향을 따라 이동해야 하는 텍스처 좌표 오프셋을 계산한다.

 

레이어의 깊이 값보다 작은 depthmap 값을 찾을 때까지 위에서부터 모든 레이어를 반복한다.

// get initial values
vec2  currentTexCoords     = texCoords;
float currentDepthMapValue = texture(depthMap, currentTexCoords).r;
  
while(currentLayerDepth < currentDepthMapValue)
{
    // shift texture coordinates along direction of P
    currentTexCoords -= deltaTexCoords;
    // get depthmap value at current texture coordinates
    currentDepthMapValue = texture(depthMap, currentTexCoords).r;  
    // get depth of next layer
    currentLayerDepth += layerDepth;  
}

return currentTexCoords;

여기에서 각 깊이 레이어를 반복하고, 벡터 P를 따라 텍스처 좌표 오프셋을 찾을 때까지 반복한다.

처음에는 표면 아래의 깊이를 반복한다.

최종 오프셋된 텍스처 좌표 벡터를 얻기 위해 fragment의 텍스처 좌표에서 결과 오프셋을 뺀다.

이번에는 전통적인 parallax 매핑과 비교해서 훨씬 더 정확하다.

 

약 10개의 샘플을 사용하면 벽돌 표면이 이전 각도에서 보았을 때보다 훨씬 더 실용적으로 보인다.

그러나 steep parallax 매핑은 이전에 표시된 나무 장난감 표면과 같이 가파른 높이 변화가 있는

복잡한 표면을 가질 때 더 가치가 있다.

parallax mapping의 속성 중 하나를 이용해 알고리즘을 다소 개선할 수 있다.

표면에 직선으로 보았을 때 텍스처 변위는 별로 일어나지 않지만 각도를 틀어서 표면을 볼 때 많은 변위가 발생한다.

각을 직선으로 볼 때 샘플을 적게 사용하고 각을 틀어서 볼 때 샘플을 더 많이 취함으로써 필요한 양만 샘플링한다.

const float minLayers = 8.0;
const float maxLayers = 32.0;
float numLayers = mix(maxLayers, minLayers, max(dot(vec3(0.0, 0.0, 1.0), viewDir), 0.0));  

여기에서 viewDir과 양의 z 방향의 내적을 취해 그 결과를 사용해 표면을 바라보는 각도를 기준으로

샘플 수를 minLayers 또는 maxLayers에 정렬한다.

표면에 평행한 방향을 보려고 한다면 총 32개의 레이어를 사용할 것이다.

 

Steep Parallax mapping 역시 문제를 가지고 있다.

이 기술은 한정된 수의 샘플을 기반으로 하기 때문에 앨리어싱 효과를 얻고

레이어 간의 명확한 구분을 쉽게 발견할 수 있다.

더 많은 수의 샘플을 사용해 문제를 완화할 수 있지만 성능에 너무 큰 부담이 된다.

표면 아래에 있는 첫 번째 위치를 취하지 않고 B에 훨씬 더 가까운 위치를 찾기 위해

위치의 가장 가까운 두 깊이 레이어를 보간해 이 문제를 해결하는 것을 목표로 하는 여러 접근법이 있다.

 

이러한 접근 방식 중 가장 인기있는 두 가지를 Relief Parallax Mapping 및

Parallax Occlusion Mapping이라고 하며

Relief Parallax Mapping은 가장 정확한 결과를 제공하지만 Parallax Occlusion Mapping과 비교할 때

훨씬 더 무거운 퍼포먼스가 필요하다.

Parallax Occlusion Mapping은 Relief Parallax mapping과 거의 동일한 결과를 제공하기 때문에 

더 효율적이다.

종종 선호되는 접근 방법이며 마지막 Parallax Mapping 유형이다.

 

Parallax Occlusion Mapping

Parallax Occlusion Mapping은 Steep Parallax Mapping과 같은 원칙을 기반으로 하지만,

충돌 후 첫 번째 깊이 레이어의 텍스처 좌표를 가져오는 대신 충돌 후와 깊이 레이어 사이를 선형으로 보간하려고 한다.

선형 보간의 가중치는 표면의 높이가 깊이 레이어의 두 레이어 값과 얼마나 떨어져 있는지를 기준으로 한다.

어떻게 작동하는지 파악하기 위해 다음 이미지를 살펴보자.

보면  알 수 있듯이 Steep Parallax Mapping과 거의 비슷하다.

이 단계는 교차점을 둘러싼 두 개의 깊이 레이어의 텍스처 좌표 간의 선형 보간을 추가로 수행한다.

이는 근사값이지만 Steep Prallax Mapping보다 훨씬 더 정확하다.

 

Parallax Occlusion mapping의 코드는 Steep Parallax Mapping의 맨 위에 있는 확장이며 그리 어렵지 않다.

[...] // steep parallax mapping code here
  
// get texture coordinates before collision (reverse operations)
vec2 prevTexCoords = currentTexCoords + deltaTexCoords;

// get depth after and before collision for linear interpolation
float afterDepth  = currentDepthMapValue - currentLayerDepth;
float beforeDepth = texture(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth;
 
// interpolation of texture coordinates
float weight = afterDepth / (afterDepth - beforeDepth);
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);

return finalTexCoords;  

표면 기하를 교차시킨 후 깊이 레이어를 찾은 후에는 교차하기 전에 깊이 레이어의 텍스처 좌표를 검색한다.

다음으로 대응하는 깊이 레이어들로부터 기하의 깊이 거리를 계산하고 이 두 값들 사이를 보간한다.

선형 보간은 두 레이어의 텍스처 좌표 간의 기본 보간이다.

함수는 마지막으로 최종 보간된 텍스처 좌표를 반환한다.

 

Parallax Occlusion Mapping은 놀라울정도로 좋은 결과를 제공한다.

약간의 인공물과 앨리어싱 문제는 여전히 눈에 띄지만 전반적으로 좋은 트레이드 오프이며

매우 심하게 확대하거나 매우 가파른 각도를 볼 때만 해당 문제를 볼 수 있다.

parallax 매핑은 장면의 세부 묘사를 향상시키는 훌륭한 기술이지만 이를 사용할 때 고려해야 할 몇가지 인공물이 있다.

대부분의 경우 parallax 매핑은 바닥이나 벽과 같은 표면에 사용된다.

표면의 윤곽선을 쉽게 결정할 수 없으며 시야각은 일반적으로 표면에 대략 직각이다.

이 방법으로 Parallax mapping의 유물은 눈에 띄지 않게 되어 객체의 세부 묘사를 향상시키는 매우 흥미로운 기술이 된다.