작성일 :


개념

Phong Shading, 퐁 셰이딩은 컴퓨터 그래픽스에서 사용되는 광원 모델의 한 종류로,

1970년대 초 Bui Tuong Phong 에 의해 개발되었다.

퐁 모델에서는 표면의 반사, 확산, 주변 광에 대하여 시뮬레이션하여 물체에 입체감을 부여한다.


Ambient Reflection, 주변 반사


주변 반사는 물체가 받는 주변 환경 광을 나타낸다.

모든 물체는 주변 환경의 광을 일정량 반사하게 되므로, 이를 통해 실제 세계에서 볼 수 있는 최소한의 밝기를 표현할 수 있다.

퐁 모델에서의 주변 반사는 다음과 같이 계산된다.

Ia = Iambient * Ka

여기서,

  • Ia최종 주변 반사량
  • Iambient주변 광의 강도
  • Ka물체의 주변 반사 계수


Diffuse Reflection, 확산 반사(난반사)

확산 반사는 물체의 불규칙한 표면 때문에 광원의 빛이 여러 방향으로 퍼지는 것을 나타낸다.

이 반사는 광원의 방향과 물체의 표면 방향에만 의존한다.
퐁 모델에서의 확산 반사는 다음과 같이 계산된다.

Id = Idiffuse * Kd * (NL)

여기서,

  • Id최종 확산 반사량
  • Idiffuse광원의 확산 광 강도
  • Kd물체의 확산 반사 계수
  • N표면의 단위 법선 벡터
  • L광원의 방향 단위 벡터

(NL) 로 두 벡터를 내적하기 때문에, 표면과 광원 사이의 각도에 따라서 확산 반사량이 결정된다.


Specular Reflection, 완전 반사(정반사)

완전 반사는 광원의 빛이 특정 방향으로 집중되어 반사되는 것을 나타낸다.

물체의 표면의 매끄러움, 광택 등을 표현할 수 있다.

퐁 모델에서의 완전 반사는 다음과 같이 계산된다.

Is = Ispecular * Ks * (RV)n

여기서,

  • Is최종 완전 반사량
  • Ispecular광원의 반사 광 강도
  • Ks물체의 완전 반사 계수
  • R반사된 빛의 방향 단위 벡터
  • V관찰자의 방향 단위 벡터
  • n반사의 세기 지수



최종적으로, 물체의 특정 픽셀에서의 광 반사량은 다음과 같이 계산된다.

I = Ia + Id + Is



구현 예시 (Unity)

Phong Shading




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PhongShadingModel : MonoBehaviour
{
  public Light lightSource;

  public Color objectColor = Color.white;

  [Range(0, 1)]
  public float ambientIntensity = 1f;

  [Range(0, 1)]
  public float diffuseIntensity = 0f;

  [Range(0, 1)]
  public float specularIntensity = 0f;

  public float shininess = 10f;

  public Slider redSlider, greenSlider, blueSlider;

  public Slider ambientSlider, diffuseSlider, specularSlider;

  private Mesh mesh;

  private Vector3[] normals;

  private Color[] colors;

  private void Start() {
    mesh = GetComponent<MeshFilter>().mesh;
    normals = mesh.normals;
    colors = new Color[normals.Length];

    ambientSlider.onValueChanged.AddListener(UpdateAmbientIntensity);
    diffuseSlider.onValueChanged.AddListener(UpdateDiffuseIntensity);
    specularSlider.onValueChanged.AddListener(UpdateSpecularIntensity);
    redSlider.onValueChanged.AddListener(UpdateRedColor);
    greenSlider.onValueChanged.AddListener(UpdateGreenColor);
    blueSlider.onValueChanged.AddListener(UpdateBlueColor);

    ambientSlider.value = ambientIntensity;
    diffuseSlider.value = diffuseIntensity;
    specularSlider.value = specularIntensity;
    redSlider.value = objectColor.r;
    greenSlider.value = objectColor.g;
    blueSlider.value = objectColor.b;
  }

  private void Update() {
    CalculatePhongShading();
    mesh.colors = colors;
  }

  private void CalculatePhongShading() {
    for (int i = 0; i < normals.Length; i++) {
      Vector3 worldNormal = transform.TransformDirection(normals[i]);
      Vector3 toLight = (lightSource.transform.position - transform.position).normalized;
      Vector3 toCamera = (Camera.main.transform.position - transform.position).normalized;
      Vector3 reflected = Vector3.Reflect(-toLight, worldNormal);

      // Ambient
      Color ambient = objectColor * ambientIntensity;

      // Diffuse
      float diffuseFactor = Mathf.Max(Vector3.Dot(worldNormal, toLight), 0);
      Color diffuse = objectColor * lightSource.color * diffuseFactor * diffuseIntensity;

      // Specular
      float specFactor = Mathf.Pow(Mathf.Max(Vector3.Dot(reflected, toCamera), 0), shininess);
      Color specular = lightSource.color * specFactor * specularIntensity;

      colors[i] = new Color(
        Mathf.Clamp01(ambient.r + diffuse.r + specular.r),
        Mathf.Clamp01(ambient.g + diffuse.g + specular.g),
        Mathf.Clamp01(ambient.b + diffuse.b + specular.b)
      );
    }
  }

  private void UpdateAmbientIntensity(float value) {
    ambientIntensity = value;
  }

  private void UpdateDiffuseIntensity(float value) {
    diffuseIntensity = value;
  }

  private void UpdateSpecularIntensity(float value) {
    specularIntensity = value;
  }

  private void UpdateRedColor(float value) {
    objectColor.r = value;
  }

  private void UpdateGreenColor(float value) {
    objectColor.g = value;
  }

  private void UpdateBlueColor(float value) {
    objectColor.b = value;
  }
}