엔진 렌더링 파이프 라인 : 셰이더를 일반으로 만들기


10

OpenGL ES 2.0 (현재 iOS)을 사용하여 2D 게임 엔진을 만들려고합니다. Objective C로 Application 레이어를 작성하고 C ++로 별도의 자체 포함 RendererGLES20을 작성했습니다. GL 전용 호출은 렌더러 외부에서 이루어지지 않습니다. 완벽하게 작동합니다.

그러나 셰이더를 사용할 때 디자인 문제가 있습니다. 각 셰이더에는 고유 한 속성과 유니폼이 있으며, 이는 기본 그리기 호출 직전에 설정해야합니다 (이 경우 glDrawArrays). 예를 들어, 일부 지오메트리를 그리려면 다음을 수행하십시오.

void RendererGLES20::render(Model * model)
{
    // Set a bunch of uniforms
    glUniformMatrix4fv(.......);
    // Enable specific attributes, can be many
    glEnableVertexAttribArray(......);
    // Set a bunch of vertex attribute pointers:
    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, m->pCoords);

    // Now actually Draw the geometry
    glDrawArrays(GL_TRIANGLES, 0, m->vertexCount);

    // After drawing, disable any vertex attributes:
    glDisableVertexAttribArray(.......);
}

보시다시피이 코드는 매우 엄격합니다. 리플 효과와 ​​같은 다른 셰이더를 사용하려면 여분의 유니폼, 정점 속성 등을 전달해야합니다. 즉, 새 셰이더를 통합하기 위해 RendererGLES20 렌더 소스 코드를 변경해야합니다.

셰이더 객체를 완전히 일반화하는 방법이 있습니까? 셰이더 오브젝트 만 변경하고 게임 소스 재 컴파일에 대해 걱정하지 않으려면 어떻게해야합니까? 렌더러가 유니폼과 속성 등을 무시하도록 만드는 방법은 무엇입니까? 데이터를 유니폼으로 전달해야하는데 가장 좋은 곳은 무엇입니까? 모델 수업? 모델 클래스가 쉐이더 특정 유니폼 및 속성을 알고 있습니까?

다음은 액터 클래스를 보여줍니다.

class Actor : public ISceneNode
{
    ModelController * model;
    AIController * AI;
};

모델 컨트롤러 클래스 : class ModelController {class IShader * shader; int textureId; vec4 색조; 플로트 알파; 구조체 정점 * vertexArray; };

셰이더 클래스에는 셰이더 객체, 서브 루틴 컴파일 및 링크 등이 포함됩니다.

Game Logic 클래스에서 실제로 객체를 렌더링하고 있습니다.

void GameLogic::update(float dt)
{
    IRenderer * renderer = g_application->GetRenderer();

    Actor * a = GetActor(id);
    renderer->render(a->model);
}

액터가 ISceneNode를 확장하더라도 SceneGraph 구현을 아직 시작하지 않았습니다. 이 문제를 해결하자마자 그렇게하겠습니다.

이 아이디어를 개선하는 방법이 있습니까? 관련 디자인 패턴 등?

질문을 읽어 주셔서 감사합니다.



그것이 정확한 복제인지 확실하지 않지만, 당신이하려는 일의 고기에 도달한다고 생각합니다.
Sean Middleditch

답변:


12

셰이더 시스템을보다 데이터 중심으로 만들 수 있으므로 균일하고 정점 형식을위한 셰이더 관련 코드가 많지 않고 셰이더에 연결된 메타 데이터를 기반으로 프로그래밍 방식으로 설정할 수 있습니다.

고지 사항 : 데이터 기반 시스템은 새로운 셰이더를 쉽게 추가 할 수 있지만, 시스템의 복잡성이 증가함에 따라 비용이 발생하므로 유지 관리 및 디버깅이 더 어려워집니다. 따라서 작은 데이터의 경우 어느 정도의 데이터 중심성이 좋을지 신중하게 생각하고 (대규모 프로젝트의 경우 대답은 "없음"일 수 있음) 지나치게 일반화 된 시스템을 구축하지 마십시오.

좋아, 먼저 정점 형식 (속성)에 대해 이야기하자. 전달할 데이터가 포함 된 구조체 ( glVertexAttribPointer단일 속성의 인덱스, 유형, 크기 등)를 만들고 전체 정점 형식을 나타내는 구조체의 배열을 사용하여 데이터 설명을 만들 수 있습니다 . 이 정보가 제공되면 정점 속성과 관련된 모든 GL 상태를 프로그래밍 방식으로 설정할 수 있습니다.

이 설명을 채우는 데이터는 어디에서 왔습니까? 개념적으로 가장 깨끗한 방법은 셰이더에 속하는 것입니다. 메시에 대한 정점 데이터를 빌드 할 때 메시에 사용되는 셰이더를 찾고 해당 셰이더에 필요한 정점 형식을 찾은 다음 그에 따라 정점 버퍼를 만듭니다. 각 셰이더가 기대하는 정점 형식을 지정하는 방법이 필요합니다.

이를 수행하는 다양한 방법이 있습니다. 예를 들어, 셰이더의 속성에 대한 표준 이름 세트 ( "attrPosition", "attrNormal"등)와 "position is 3 floats"와 같은 하드 코딩 된 규칙이있을 수 있습니다. 그런 다음 glGetAttribLocation또는 비슷한 것을 사용하여 셰이더가 사용하는 속성을 쿼리하고 규칙을 적용하여 정점 형식을 만듭니다. 또 다른 방법은 형식을 정의하고 셰이더 소스의 주석에 포함되어 도구에서 추출한 XML 스 니펫이나 해당 라인을 따르는 것입니다.

유니폼의 경우 OpenGL 3.1 이상을 사용할 수있는 경우 균일 한 버퍼 객체 (D3D 상수 버퍼와 동등한 OpenGL) 를 사용하는 것이 좋습니다 . 아아, GL ES 2.0에는 이러한 기능이 없으므로 유니폼을 개별적으로 처리해야합니다. 이를 수행하는 한 가지 방법은 카메라 매트릭스, 스펙 큘러 파워, 월드 매트릭스 등과 같이 설정하려는 각 파라미터의 균일 한 위치를 포함하는 구조체를 만드는 것입니다. 샘플러 위치도 여기에있을 수 있습니다. 이 접근 방식은 모든 셰이더에서 공유되는 표준 매개 변수 세트에 따라 다릅니다. 모든 셰이더가 모든 단일 파라미터를 사용해야하는 것은 아니지만 모든 파라미터는이 구조체에 있어야합니다.

각 셰이더에는이 구조체의 인스턴스가 있으며 셰이더를로드 할 때 glGetUniformLocation표준화 된 이름을 사용하여 모든 매개 변수의 위치를 ​​쿼리합니다 . 그런 다음 코드에서 유니폼을 설정해야 할 때마다 해당 셰이더에 있는지 확인하고 해당 위치를 찾아 설정하면됩니다.

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