Phong Shading - soo:bak
작성일 :
개념
Phong Shading
, 퐁 셰이딩은 컴퓨터 그래픽스에서 사용되는 광원 모델의 한 종류로,
1970년대 초 Bui Tuong Phong
에 의해 개발되었다.
퐁 모델에서는 표면의 반사, 확산, 주변 광에 대하여 시뮬레이션하여 물체에 입체감을 부여한다.
Ambient Reflection, 주변 반사
주변 반사는 물체가 받는 주변 환경 광을 나타낸다.
모든 물체는 주변 환경의 광을 일정량 반사하게 되므로, 이를 통해 실제 세계에서 볼 수 있는 최소한의 밝기를 표현할 수 있다.
퐁 모델에서의 주변 반사는 다음과 같이 계산된다.
Ia = Iambient * Ka
여기서,
- Ia 는
최종 주변 반사량
- Iambient 는
주변 광의 강도
- Ka 는
물체의 주변 반사 계수
Diffuse Reflection, 확산 반사(난반사)
확산 반사는 물체의 불규칙한 표면 때문에 광원의 빛이 여러 방향으로 퍼지는 것을 나타낸다.
이 반사는 광원의 방향과 물체의 표면 방향에만 의존한다.
퐁 모델에서의 확산 반사는 다음과 같이 계산된다.
Id = Idiffuse * Kd * (N ⋅ L)
여기서,
- Id 는
최종 확산 반사량
- Idiffuse 는
광원의 확산 광 강도
- Kd는
물체의 확산 반사 계수
- N 은
표면의 단위 법선 벡터
- L 은
광원의 방향 단위 벡터
(N ⋅ L) 로 두 벡터를 내적하기 때문에, 표면과 광원 사이의 각도에 따라서 확산 반사량이 결정된다.
Specular Reflection, 완전 반사(정반사)
완전 반사는 광원의 빛이 특정 방향으로 집중되어 반사되는 것을 나타낸다.
물체의 표면의 매끄러움, 광택 등을 표현할 수 있다.
퐁 모델에서의 완전 반사는 다음과 같이 계산된다.
Is = Ispecular * Ks * (R ⋅ V)n
여기서,
- Is 는
최종 완전 반사량
- Ispecular 는
광원의 반사 광 강도
- Ks 는
물체의 완전 반사 계수
- R 은
반사된 빛의 방향 단위 벡터
- V 는
관찰자의 방향 단위 벡터
- n 은
반사의 세기 지수
최종적으로, 물체의 특정 픽셀에서의 광 반사량은 다음과 같이 계산된다.
I = Ia + Id + Is
구현 예시 (Unity)
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;
}
}