갬장장이
[3DGraphics] 16. Point lights
갬장장이
갬장장이의 코드 대장간
갬장장이
전체
오늘
어제
  • 분류 전체보기
    • 게임 연구소
    • 게임 제작
      • Banditors (2024~)
      • Rovenhell (2023)
      • Geophyte (2020~2021)
      • 아드레날린 러시 (2021)
      • Treadmill (2019)
      • 습작들 (2019~)
      • 그 외 (~2018)
    • 개발
      • 언리얼 엔진
      • 수학
      • 네트워크
      • 그래픽스
      • OpenGL
      • DirectX
    • 일상
    • 기타 제작

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • [공지] 블로그 안내

인기 글

최근 댓글

최근 글

hELLO · Designed By 정상우.
개발/그래픽스

[3DGraphics] 16. Point lights

2022. 9. 9. 16:04

image


Directional light로는 표현할 수 없는 상황들이 게임 속에는 다수 존재한다.
빛과 물체의 상대적인 위치에 따라 물체에 가해지는 명암이 달라지는 경우 특히 그러한데, 방 안에 전구가 있고 그 전구를 기점으로 물체가 빙글빙글 돌아갈 때의 명암을 생각하면 쉽다.
또한 빛과 물체의 거리와 상관없이 빛의 세기가 일정한 directional light와는 다르게, 먼 물체일 수 록 더 어둡게 표현하고 싶다면 다른 형태의 빛이 필요하다.

image


이런 상황들에서 사용할 수 있는 게 바로 point light 이다.

image


상대적인 위치에 따라 명암을 동적으로 계산할 뿐만 아니라, 거리에 따라 빛이 감소(dropoff)하기도 한다.

image


이는 1/(거리)^2 에 비례한 형태로 나타나는데, 이때 명암을 단순히 1/x^2로 설정해버리면 빛과 물체의 거리가 0에 가까워질 때 물체의 밝기가 무한대를 향해 발산하는 문제가 발생한다.

image


이를 해결하기 위해 분모 식을 Ad^2+Bd+C라는 일반식의 형태로 바꾸면 다양한 형태의 빛을 표현할 수 있다.

image


일반적으로 위와 같은 형태가 자주 사용되며, 검색해보면 자주 사용되는 A,B,C (Coefficient)의 값을 찾을 수 있으니 참고.

이렇게 거리에 따른 명암의 구현을 살펴보았으니 이번에는 상대적 위치에 따른 명암의 구현을 살펴보자.

// GouraudPointEffect.h
#pragma once

#include "Pipeline.h"
#include "DefaultGeometryShader.h"

// flat shading with vertex normals
class GouraudPointEffect
{
public:
    // the vertex type that will be input into the pipeline
    class Vertex
    {
    public:
        Vertex() = default;
        Vertex( const Vec3& pos )
            :
            pos( pos )
        {}
        Vertex( const Vec3& pos,const Vertex& src )
            :
            n( src.n ),
            pos( pos )
        {}
        Vertex( const Vec3& pos,const Vec3& n )
            :
            n( n ),
            pos( pos )
        {}
        Vertex& operator+=( const Vertex& rhs )
        {
            pos += rhs.pos;
            return *this;
        }
        Vertex operator+( const Vertex& rhs ) const
        {
            return Vertex( *this ) += rhs;
        }
        Vertex& operator-=( const Vertex& rhs )
        {
            pos -= rhs.pos;
            return *this;
        }
        Vertex operator-( const Vertex& rhs ) const
        {
            return Vertex( *this ) -= rhs;
        }
        Vertex& operator*=( float rhs )
        {
            pos *= rhs;
            return *this;
        }
        Vertex operator*( float rhs ) const
        {
            return Vertex( *this ) *= rhs;
        }
        Vertex& operator/=( float rhs )
        {
            pos /= rhs;
            return *this;
        }
        Vertex operator/( float rhs ) const
        {
            return Vertex( *this ) /= rhs;
        }
    public:
        Vec3 pos;
        Vec3 n;
    };
    // calculate color based on normal to light angle
    // no interpolation of color attribute
    class VertexShader
    {
    public:
        class Output
        {
        public:
            Output() = default;
            Output( const Vec3& pos )
                :
                pos( pos )
            {}
            Output( const Vec3& pos,const Output& src )
                :
                color( src.color ),
                pos( pos )
            {}
            Output( const Vec3& pos,const Vec3& color )
                :
                color( color ),
                pos( pos )
            {}
            Output& operator+=( const Output& rhs )
            {
                pos += rhs.pos;
                color += rhs.color;
                return *this;
            }
            Output operator+( const Output& rhs ) const
            {
                return Output( *this ) += rhs;
            }
            Output& operator-=( const Output& rhs )
            {
                pos -= rhs.pos;
                color -= rhs.color;
                return *this;
            }
            Output operator-( const Output& rhs ) const
            {
                return Output( *this ) -= rhs;
            }
            Output& operator*=( float rhs )
            {
                pos *= rhs;
                color *= rhs;
                return *this;
            }
            Output operator*( float rhs ) const
            {
                return Output( *this ) *= rhs;
            }
            Output& operator/=( float rhs )
            {
                pos /= rhs;
                color /= rhs;
                return *this;
            }
            Output operator/( float rhs ) const
            {
                return Output( *this ) /= rhs;
            }
        public:
            Vec3 pos;
            Vec3 color;
        };
    public:
        void BindRotation( const Mat3& rotation_in )
        {
            rotation = rotation_in;
        }
        void BindTranslation( const Vec3& translation_in )
        {
            translation = translation_in;
        }
        Output operator()( const Vertex& v ) const
        {
            // transform mech vertex position before lighting calc
            const auto pos = v.pos * rotation + translation;
      // 빛 연산을 해줘야 하므로 좌표계 통일을 위해 월드좌표계로 좌표를 우선 변환시켜준다.
      // v.pos는 셰이더 연산을 적용시킬 모델(물체)의 정점이다.

            // vertex to light data
            const auto v_to_l = light_pos - pos; // vector to light = 물체에서 빛으로 향하는 벡터
            const auto dist = v_to_l.Len();
            const auto dir = v_to_l / dist; // direction은 그냥 그 벡터를 normalize 시킨 것

      // calculate attenuation
      // 거리에 따른 명암 적용 (상수들이 곱해진 것을 볼 수 있다)
            const auto attenuation = 1.0f / 
                (constant_attenuation + linear_attenuation * dist * quadradic_attenuation * sq( dist ));

      // calculate intensity based on angle of incidence and attenuation
      // 각과 거리에 따른 명암 조정 (각에 의한 명암 조정은 directional light때와 동일)
            const auto d = light_diffuse * attenuation * std::max( 0.0f,(v.n * rotation) * dir );

            // add diffuse+ambient, filter by material color, saturate and scale
      // 마찬가지로 directional light와 동일한 과정
            const auto c = material_color.GetHadamard( d + light_ambient ).Saturate() * 255.0f;
            return{ pos,c };
        }
        void SetDiffuseLight( const Vec3& c )
        {
            light_diffuse = c;
        }
        void SetAmbientLight( const Vec3& c )
        {
            light_ambient = c;
        }
        void SetLightPosition( const Vec3& pos_in )
        {
            light_pos = pos_in;
        }
    private:
        Mat3 rotation;
        Vec3 translation;
        Vec3 light_pos = { 0.0f,0.0f,0.5f };
        Vec3 light_diffuse = { 1.0f,1.0f,1.0f };
        Vec3 light_ambient = { 0.1f,0.1f,0.1f };
        Vec3 material_color = { 0.8f,0.85f,1.0f };
        float linear_attenuation = 1.0f;
        float quadradic_attenuation = 2.619f;
        float constant_attenuation = 0.382f;
    };
    // default gs passes vertices through and outputs triangle
    typedef DefaultGeometryShader<VertexShader::Output> GeometryShader;
    // invoked for each pixel of a triangle
    // takes an input of attributes that are the
    // result of interpolating vertex attributes
    // and outputs a color
    class PixelShader
    {
    public:
        template<class Input>
        Color operator()( const Input& in ) const
        {
            return Color( in.color );
        }
    };
public:
    VertexShader vs;
    GeometryShader gs;
    PixelShader ps;
};

image


참고사항: 빛의 위치를 시각적으로 나타내기 위해 별도의 파이프라인을 GouraudPointScene.h에 추가했다.

image

 

image


잘 작동하는 듯 해 보이지만, 완전히 별도의 렌더링 파이프라인을 사용하기 때문에 모델 뒤로 빛이 지나가도 계속 빛이 보이는 것을 볼 수 있다.
이유는 단순한데, 각 파이프라인 별로 별도의 z-buffer를 기반으로 occlusion을 처리하기 때문에 이를 하나로 합쳐주는 과정이 필요하다.
이때 하나로 버퍼를 일일히 확인하면서 비교하는 것보다 훨씬 간단한 방법은 애초에 버퍼를 하나만 만들고 이를 공유하는 것인데, 이를 구현한 코드는 사진과 같다.

image

 

image


shared_ptr을 사용해 공동의 리소스를 사용하는 것으로, 크게 어려운 부분은 없다. 에러가 나지 않게 버퍼와 실제 화면의 크기 비교를 해주는 부분이 추가되었다.

image


잘 작동하는 것을 볼 수 있다.


image


그러나 우리의 파이프라인에는 아직 한 가지 큰 문제가 남아있는데, 이는 다음 글에서 다루도록 하겠다.

저작자표시 비영리 변경금지 (새창열림)

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

[3DGraphics] 18. Specular highlights  (1) 2022.10.11
[3DGraphics] 17. Phong shading & Per-pixel lighting  (0) 2022.09.09
[3DGraphics] 15. Gouraud shading  (0) 2022.09.09
[3DGraphics] 14. Flat shading and Mesh loading  (0) 2022.09.09
[3DGraphics] 13. Geometry shader  (0) 2022.07.24
'개발/그래픽스' 카테고리의 다른 글
  • [3DGraphics] 18. Specular highlights
  • [3DGraphics] 17. Phong shading & Per-pixel lighting
  • [3DGraphics] 15. Gouraud shading
  • [3DGraphics] 14. Flat shading and Mesh loading
갬장장이
갬장장이
갬장장이의 코드 대장간갬장장이 님의 블로그입니다.
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.