카테고리 없음

쉐이더 심화 3-투명도와 깊이(~Depth Testing)

mazayong 2024. 2. 15. 20:40

컴퓨터 그래픽에서의 색상은 R, G, B, A 채널로 이루어져 있다. 여기서는 Alpha Channel을 이용해 다양한 형태의 오브젝트가 들어있는 씬을 그리고, 모든 대상을 렌더링하고, 캐릭터에 애니메이션 작업을 하는 방법을 배우겠다.

(=이와 관련된 연산은 fragment 처리 단계에서 이루어진다.)

 

 

1. Quad Mesh에 대상 그리기

2. Alpha Testing

3. Depth Testing

 

 

 

 

 

1. Quad Mesh에 대상 그리기

= 사각형이 아닌 형태를 그려보자!

= 2D 게임은 사용하지 않는 fragment를 감춰버린다.(모든 것을 Quad에 그리기 때문에 Alpha Testing으로 투명하게 만듦.)

(3D 게임 : 복잡한 mesh로 다양한 형태 렌더링.)

 

 

1.1. Mesh

= 원하는 좌표, 원하는 크기로 Mesh를 배치.

= Mesh 움직이는 방법을 배우지 않아서 원하는 곳에 vertex 생성.

 

opApp.cpp에서 quad 생성

void buildMesh(ofMesh& mesh, float w, float h, glm::vec3 pos)
{
    float verts[] = { -w+pos.x, -h+pos.y, pos.z,
    -w+pos.x, h+pos.y, pos.z,
    w+pos.x, h+pos.y, pos.z,
    w+pos.x, -h+pos.y, pos.z
    };
    float uvs[]={0,0, 0,1, 1,1, 1,0};

    for(int i=0; i<4; i++)
    {
        int idx=i*3;
        int uvIdx=i*2;
        mesh.addVertex(glm::vec3(verts[idx], verts[idx+1], verts[idx+2]));
        mesh.addTexCoord(glm::vec2(uvs[uvIdx], uvs[uvIdx+1]));
    }


    ofIndexType indices[6]={0,1,2,2,3,0};
    mesh.addIndices(indices, 6);
}

 

 

 

 

ofApp.cpp에서 원하는 크기 갖게 Mesh를 설정한다.

(예시는 수평 방향으로 화면의 1/4, 수직 방향은 절반 크기.)

draw()에서 uniform texture로 설정 후 작동되도록 코드도 작성한다.

void ofApp::setup(){
    ofDisableArbTex();

    buildMesh(charMesh, 0.25, 0.5, glm::vec3(0.0, 0.15, 0.0));

    alienImg.load("alien.png");
    charShader.load("passthrough.vert", "alphaTest.frag");
}
-------(생략)---------
void ofApp::draw(){
    charShader.begin();
    charShader.setUniformTexture("greenMan", alienImg, 0);
    charMesh.draw();
    charShader.end();
}

 

++ 표준에 가까운 텍스쳐를 가까우기 위해 스크린 픽셀 좌표를 사용하는 텍스쳐를 비활성한다.

(ofDisableArbTex();)

++ ofApp.h에서 charMesh, charShader, alienImg를 각각 ofMesh, ofShader, ofImage로 선언.

 

 

 

1.2. shader

1.2.1. vertex shader

vertex 정보만 다음 파이프라인에 전달하는 기본적인 기능만 수행하면 되므로, 이전에 사용한 shader와 유사하게 작성한다.

 

passthrough.vert

#version 410

layout (location=0) in vec3 pos;
layout (location=3) in vec2 uv;

out vec2 fragUV;

void main()
{
    gl_Position=vec4(pos, 1.0);
    fragUV=vec2(uv.x, 1.0-uv.y);
}

 

 

 

1.2.2. fragment shader

외계인 텍스쳐에서 alpha가 1.0보다 작은 영역에 해당하는 fragment를 quad에서 감춰야 하므로 alpha testing이 적용된 discard 명령어를 사용한다.

 

alphaTest.frag

#version 410

uniform sampler2D greenMan;
in vec2 fragUV;

out vec4 outCol;

void main()
{
	outCol = texture(greenMan, fragUV);
	if (outCol.a < 1.0) discard;
}

 

 

해당 코드의 결과는 아래와 같다.

 

 

 

 

 

 

 

 

2. Alpha Testing, Discard

우선 Alpha Testing 기법에 대해 알아보자.

 

2.1. Alpha Testing?

= alpha의 cutoff threshold를 정의한다.

= 각 fragment의 alpha를 threshold값과 비교한다.

= fragment alpha가 1보다 크면 렌더링하고, 작으면 처리를 중단시킨다. (버리는 명령어가 discard)

(++1.0인 경우, 100% 불투명하지 않은 fragment는 버린다.)

 

2.2. Alpha Blending?

= ofDisableAlphaBlending()으로 alpha blending을 비활성해도 1의 코드는 잘 작동된다.

= 기준값에 따라 값을 처리하는 과정에 GPU의 alpha blending이 필요하지 않기 때문.

= 2.1과 2.2는 분명히 다르지만, 둘 다 fragment의 alpha값에 영향을 미치므로 alpha blending 비활성화가 되어있을 시, 코드 작동이 원활하지 않을 수 있으므로 삭제하고 진행하는 것을 장려한다.

 

 

 

 

 

 

 

 

 

 

 

3. Depth Testing

3.1. 배경 texture 적용

= Z축 중요. (NDC에서 Z축은 우리가 컴퓨터 화면을 바라보는 방향을 향하개 해서 외계인보다 뒤에 그려져야 하기 때문.)

=> Z값이 작은 vertex가 큰 vertex보다 앞에 위치하므로 배경 mesh는 0보다 큰 Z값이어야 한다.

 

 

ofApp.cpp

buildMesh(backgroundMesh, 1.0, 1.0, glm::vec3(0.0, 0.0, 0.5));

스크린에 꽉 차도록 배경 mesh를 설정한다.

 

 

ofApp.cpp

void ofApp::draw(){
    alphaTestShader.begin();
	
	alphaTestShader.setUniformTexture("tex", alienImg, 0);
	charMesh.draw();

	alphaTestShader.setUniformTexture("tex", backgroundImg, 0);
	backgroundMesh.draw();

	alphaTestShader.end();
}

두 mesh를 한 스크린에 렌더링해야하기 때문에 같은 두 mesh를 같은 texture에 배치하도록 설정한다.

 

++ shader를 alphaTestShader로 바꾸고, 배경 Mesh와 이미지를 설정하기 위해 ofMesh, ofImage를 ofApp.h에 추가한다.

++ alphaTest fragment shader의 uniform sampler2D를 tex로 바꾼 뒤 진행한다.

 

 

위 코드의 결과는 아래와 같다.

 

 

3.2. Vertex Z값 비교

= 그래픽 파이프라인에서 Mesh처리시, GPU에서 현재 프레임 그려질 대상에 대한 정보 x.

-> 깊이 버퍼(Depth Buffer, 특정 프레임 렌더링시 깊이 정보를 저장하는 texture) 추가

=> 깊이 버퍼를 사용하는 모든 계산은 fragment 처리 단계에서 이루어진다.

 

 

= setup()에서 ofEnableDepthTest()를 추가해서 다시 빌드하면 GPU가 깊이 버퍼를 생성해서 빌드하면 아래와 같다.

 

외계인의 크기를 배경에 맞게 조절해준다. (아래의 예시와 같은 사이즈와 위치를 원할 경우 아래의 코드를 작성한다.)

buildMesh(charMesh, 0.1, 0.2, glm::vec3(0.0, -0.2, 0.0));

 

 

다음은 구름을 만들면서 alpha blending, alpha texture의 차이를 알아보고 태양을 통해 addictive blending, 스프라이트 애니메이션 생성 과정을 알아볼 것이다.