새소식

인기 검색어

게임 개발/그래픽스

블린-퐁 쉐이딩 (Blinn-Phong Shading)

  • -
vec4 MyPixelShader(const PSInput psInput) {

    vec3 eye = vec3(0.0f, 0.0f, -1.0f);
    vec3 toEye = glm::normalize(eye - psInput.position);
    vec3 color = ComputeDirectionalLight(constants.light, constants.material,
                                         psInput.normal, toEye);

    return vec4(color, 1.0f);
}

쉐이딩을 어디서 하는 것이 좋을까?

vertex shader?

pixel shader?

vertex shader는 pixel shader 보다 처리해야할 계산 수가 훨씬 적다. 따라서 계산이 빠를것이다.

그러나 퀄리티는 떨어질 것이다.

요즘 GPU는 빠르기때문에 pixel shader에서 처리한다.

 

퐁 쉐이딩을 개선해서 조금더 빠르게 만든 블린-퐁 쉐이딩에 대해 배워보자.

Ambient Color 는 왜 필요?

Ambient color를 사용하면 간접광을 아주 단순화시켜서 구현한것과 같은 효과를 낼 수 있다.

Directional Light

태양은 지구로부터 매우 멀리 떨어져 있어서 태양의 빛은 모두 지구에 올때 평행하게(같은 방향으로) 온다고 가정할 수 있다.

그래픽스에서는 Directional Light, Point Light, Spotlight 이 세가지를 사용한다.

Lambetian Surface (Diffuse)

어떤 표면이 미세하게 울퉁불퉁하다고 했을때

위 그림처럼 구를 반 잘라놓은 모양으로 난반사가 일어남.

그런데, 그 난반사 벡터들의 길이는 표면의 normal vector(법선)의 방향과 입사각 사이의 세타 각도로 정의된다.

cos을 이용해 정확히 표면에 수직으로 들어오면 최대, 옆으로 누울수록 작아진다.

Diffuse vs Specular Reflection

퐁 모델 vs 블린-퐁 모델

블린-퐁 모델은 퐁 모델을 속도개선한 것.

  • R: 완전 반사방향
  • V: 우리가 렌더링 하고싶은 지점으로부터 눈으로 향하는 벡터
  • N: 해당 지점으로부터 표면의 노멀벡터 
  • H: V와 L 벡터의 중간(half way vector)
  • L: 조명의 방향

기존 퐁 모델

기존 퐁 모델은 완전 반사방향 R을 사용했다.

그래서 specular 계산시에 R dot V를 사용했다.

R벡터 계산시에 계산량이 많다.

블린-퐁 모델

기존 퐁 모델에서 사용하던 R벡터 계산량이 많다고 판단하고 R대신 H벡터 사용.

N dot H 값을 R dot V대신 사용한다.

 

퐁 모델과 블린-퐁 모델은 결과가 다르다

계산해서 렌더링하는 결과가 기존 퐁 모델과 완전히 같지는 않다.

그러나 계산량은 많이 줄이면서도 렌더 결과는 충분히 그럴듯 하다.

Phong vs Blinn-phong

이미지 출처: LearnOpenGL.com

phong은 조명이 비추는 영역의 가장자리가 날카롭다.

blinn-phong은 부드럽다.

정리

 

코드

vec3 BlinnPhong(vec3 lightStrength, vec3 lightVec, vec3 normal, vec3 toEye,
                Material mat) {

    // Halfway vector 계산
    // vec3 halfway = (lightVec + toEye) / glm::length(lightVec + toEye);
    vec3 halfway = normalize(toEye + lightVec);

    // Halyway vector를 이용해서 specular albedo 계산
    vec3 specular = mat.specular * pow(glm::max(glm::dot(normal, halfway), 0.0f), mat.shininess);

    // ambient, diffuse, specular 합쳐서 계산
    return mat.ambient + (mat.diffuse + specular) * lightStrength;
}
vec3 ComputeDirectionalLight(Light L, Material mat, vec3 normal, vec3 toEye) {

    // 계산에 사용하는 lightVector는 directional light가 향하는 방향의 반대
    vec3 lightVec = -L.direction;

    // 
    float ndotl = glm::max(glm::dot(lightVec, normal), 0.0f);
    vec3 lightStrength = L.strength * ndotl;

    // Luna DX12 책에서는 Specular 계산에도
    // Lambert's law가 적용된 lightStrength를 사용합니다.
    return BlinnPhong(lightStrength, lightVec, normal, toEye, mat);
}

 

'게임 개발 > 그래픽스' 카테고리의 다른 글

벡터  (0) 2023.11.12
조명  (0) 2023.11.11
원근 투영 (Perspective Projection)  (0) 2023.11.10
뒷면 제거 (backface culling)  (0) 2023.11.10
쉐이더 개념  (0) 2023.11.08
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.