GLSL 버전 330으로 스카이 박스 구현


14

OpenGL 3.3 및 GLSL 버전 330에서 작동하는 스카이 박스를 얻으려고합니다.

웹의 어느 곳에서나 완전히 현대적인 OGL 스카이 박스 자습서를 찾을 수 없었기 때문에 더 오래된 것을 현대화했습니다 ( 정점 glVertexAttribPointer()대신 사용 gl_Vertex). 주로 작동하지만 두 가지 주요 세부 사항이 있습니다.

스카이 박스는 하늘 삼각형과 비슷하며 질감이 심하게 뒤틀리고 늘어납니다 (스타 필드이어야하며 검정색 배경에 선이 표시됩니다). 99 % 확신합니다. 이전 튜토리얼을 완전히 포팅하지 않았기 때문입니다.

여기 내 스카이 박스 수업이 있습니다.

static ShaderProgram* cubeMapShader = nullptr;

static const GLfloat vertices[] = 
{
    1.0f, -1.0f,  1.0f,
    1.0f,  1.0f,  1.0f,
    1.0f,  1.0f, -1.0f,
    -1.0f, -1.0f,  1.0f,
    -1.0f, -1.0f, -1.0f,
    -1.0f,  1.0f, -1.0f,
    -1.0f,  1.0f,  1.0f,
    -1.0f,  1.0f, -1.0f,
    1.0f,  1.0f, -1.0f,
    1.0f,  1.0f,  1.0f,
    -1.0f,  1.0f,  1.0f,
    -1.0f, -1.0f,  1.0f,
    1.0f, -1.0f,  1.0f,
    1.0f, -1.0f, -1.0f,
    -1.0f, -1.0f, -1.0f,
    1.0f, -1.0f,  1.0f,
    -1.0f, -1.0f,  1.0f,
    -1.0f,  1.0f,  1.0f,
    1.0f,  1.0f,  1.0f,
    -1.0f, -1.0f, -1.0f,
    1.0f, -1.0f, -1.0f,
    1.0f,  1.0f, -1.0f,
    -1.0f,  1.0f, -1.0f
};

Skybox::Skybox(const char* xp, const char* xn, const char* yp, const char* yn, const        char* zp, const char* zn)
{
if (cubeMapShader == nullptr)
    cubeMapShader = new ShaderProgram("cubemap.vert", "cubemap.frag");

    texture = SOIL_load_OGL_cubemap(xp, xn, yp, yn, zp, zn, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);

    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

    glGenVertexArrays(1, &vaoID);
    glBindVertexArray(vaoID);
    glGenBuffers(1, &vboID);
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glBindVertexArray(0);

    scale = 1.0f;
}

Skybox::~Skybox()
{

}

void Skybox::Render()
{
    ShaderProgram::SetActive(cubeMapShader);
    glDisable(GL_DEPTH_TEST);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
    cubeMapShader->Uniform1i("SkyTexture", 0);
    cubeMapShader->UniformVec3("CameraPosition", Camera::ActiveCameraPosition());
    cubeMapShader->UniformMat4("MVP", 1, GL_FALSE, Camera::GetActiveCamera()->GetProjectionMatrix() * Camera::GetActiveCamera()->GetViewMatrix() * glm::mat4(1.0));
    glBindVertexArray(vaoID);
    glDrawArrays(GL_QUADS, 0, 24);
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

정점 셰이더 :

#version 330 
layout(location = 0) in vec3 Vertex;

uniform vec3 CameraPosition;
uniform mat4 MVP;

out vec3 Position;

void main()
{
    Position = Vertex.xyz;
    gl_Position = MVP * vec4(Vertex.xyz + CameraPosition, 1.0);
}

조각 쉐이더 :

#version 330 compatibility

uniform samplerCube SkyTexture;

in vec3 Position;

void main()
{
    gl_FragColor = textureCube(SkyTexture, Position);
}

글리치의 예는 다음과 같습니다 . GLSL을 잘 아는 사람 (아직도 배우고 있음)이나 스카이 박스를 볼 수 있다면 도움을 주시면 감사하겠습니다. 또한 조각 셰이더에서 더 이상 사용되지 않는 함수를 사용하는 방법을 가르쳐 줄 수 있다면 glsl 330의 호환성 프로파일을 사용할 필요가 없습니다.


편집 : 즉시 스트레칭 텍스처에 문제가 있음을 발견했습니다 . 버텍스 쉐이더 Position = Vertex.xyx대신에 사용하고있었습니다 Position = Vertex.xyz. 죄송합니다. 그러나 삼각형 오류는 여전히 존재합니다.


1
큐브 맵 텍스처로 스카이 박스를 렌더링하려면 4 개의 정점 (전체 화면 쿼드) 만 있으면됩니다. 카메라와 투영을 기반으로 올바른 텍스처 좌표를 계산하는 꼭짓점 셰이더 만 있으면됩니다.
msell

컬링 문제 일 수 있습니다. 전체 상자가 있는지 확인하기 위해 뒷면 컬링을 비활성화하려고 했습니까?
pwny

@ pwny, 나는 그것을 생각하지 않았다. 나는 그것을 시도했지만 작동하지 않지만 어떻게 그것을 버릴 수 있었는지 알 수 있습니다. 제안 해 주셔서 감사합니다.
sm81095

@msell, 나는이 접근법에 대해 들었지만 온라인에 대한 자습서를 찾지 못했지만 glsl을 배우는 과정에 있습니다. 이 작업을 수행하는 방법에 대한 예제 또는 예제 링크를 제공 할 수 있다면 대단히 감사하겠습니다.
sm81095

답변:


29

이 답변은 접근 방식의 문제점을 알려주지는 않지만 스카이 박스를 렌더링하는 더 간단한 방법을 제시합니다.

전통적인 방법 (질감 큐브)

스카이 박스를 생성하는 간단한 방법은 카메라 위치를 중심으로 질감 큐브를 렌더링하는 것입니다. 큐브의 각면은 두 개의 삼각형과 2D 텍스처 (또는 아틀라스의 일부)로 구성됩니다. 텍스처 좌표로 인해 각면마다 고유 한 꼭짓점이 필요합니다. 이 방법은 텍스처 값이 제대로 보간되지 않는 인접한면의 이음새에 문제가 있습니다.

큐브 맵 텍스처와 큐브

전통적인 방식과 마찬가지로 질감 큐브가 카메라 주위에 렌더링됩니다. 6 개의 2D 텍스처를 사용하는 대신 단일 큐브 맵 텍스처가 사용됩니다. 카메라가 큐브 내부에 중심이 있기 때문에 정점 좌표는 큐브 맵 샘플링 벡터와 일대일로 매핑됩니다. 따라서 메쉬 데이터에 텍스처 좌표가 필요하지 않으며 인덱스 버퍼를 사용하여면간에 정점을 공유 할 수 있습니다.

이 방법은 GL_TEXTURE_CUBE_MAP_SEAMLESS가 활성화 된 경우 이음새 문제도 해결합니다.

더 간단한 (더 나은) 방법

큐브를 렌더링하고 카메라가 큐브 안에 있으면 전체 뷰포트가 채워집니다. 스카이 박스의 최대 5 개의면을 언제든지 부분적으로 볼 수 있습니다. 큐브면의 삼각형이 투영되어 뷰포트에 잘리고 큐브 맵 샘플링 벡터가 정점 사이에 보간됩니다. 이 작업은 불필요합니다.

전체 뷰포트를 채우는 단일 쿼드를 채우고 모서리의 큐브 맵 샘플링 벡터를 계산할 수 있습니다. 큐브 맵 샘플링 벡터는 꼭짓점 좌표와 일치하므로 뷰포트 좌표를 월드 공간에 투영 해제하여 계산할 수 있습니다. 이것은 월드 좌표를 뷰포트에 투영하는 것과 반대이며 행렬을 반전시켜 얻을 수 있습니다. 또한 z- 버퍼 쓰기를 비활성화하거나 충분한 값을 쓰십시오.

아래는이 작업을 수행하는 버텍스 쉐이더입니다.

#version 330
uniform mat4 uProjectionMatrix;
uniform mat4 uWorldToCameraMatrix;

in vec4 aPosition;

smooth out vec3 eyeDirection;

void main() {
    mat4 inverseProjection = inverse(uProjectionMatrix);
    mat3 inverseModelview = transpose(mat3(uWorldToCameraMatrix));
    vec3 unprojected = (inverseProjection * aPosition).xyz;
    eyeDirection = inverseModelview * unprojected;

    gl_Position = aPosition;
} 

aPosition꼭짓점 좌표 {-1,-1; 1,-1; 1,1; -1,1}입니다. 셰이더 eyeDirection는 모델 뷰 프로젝션 매트릭스의 역으로 계산 합니다. 그러나 반전은 투영 및 월드-투-카메라 매트릭스에 대해 분할됩니다. 카메라 매트릭스의 3x3 부분 만 사용하여 카메라의 위치를 ​​제거해야하기 때문입니다. 카메라를 스카이 박스 중앙에 정렬합니다. 내 카메라에는 스케일링이나 전단 기능이 없으므로 반전을 조옮김으로 단순화 할 수 있습니다. 프로젝션 매트릭스의 반전은 비용이 많이 드는 작업이며 사전 계산 될 수 있지만이 코드는 일반적으로 프레임 당 4 회만 정점 셰이더에 의해 실행되므로 일반적으로 문제가되지 않습니다.

프래그먼트 셰이더는 단순히 eyeDirection벡터를 사용하여 텍스처 조회를 수행합니다 .

#version 330
uniform samplerCube uTexture;

smooth in vec3 eyeDirection;

out vec4 fragmentColor;

void main() {
    fragmentColor = texture(uTexture, eyeDirection);
}

호환성 모드를 제거하려면 textureCube그냥 바꾸고 texture출력 변수를 직접 지정해야합니다.


매트릭스 반전은 비용이 많이 드는 프로세스이므로 클라이언트 측 코드에서 더 잘 수행된다는 점도 언급해야한다고 생각합니다.
akaltar

1
풀 스크린 쿼드의 4 verts의 경우 반전 비용에 대해 크게 걱정할 필요가 없다고 생각합니다.
Maximus Minimus

1
GLSL ES 1.0 (GL ES 2.0에 사용)은 구현하지 않습니다inverse()
Steven Lu

uWorldToCameraMatrix가 카메라 변환 객체의 MVP입니까?
Sidar

@Sidar 아니요, ModelView 매트릭스 일뿐입니다. 투영은 분리되어 있습니다.
msell
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.