개요
Phong Shading 모델에서는 단순히, R(Reflection)와 L(ight)의 dot
product를 이용해서 viewer가 보고 있는 surface의 빛의 강도를 구할 수 있다.
Blinn-Phong의 경우, H(Halfway Vector)
라는 개념이 추가로 등장한다.
본격적으로 소개하기전 Phong 모델의 경우, $Ambient + Diffuse + Specular$ 을 통해 해당 부분의 Color 값을 도출한다. Blinn-Phong
에 의해 달라진 점은 $Specular$을 계산하는 부분이다.
참고
- https://web.archive.org/web/20180816064924/http://www.sunandblackcat.com/tipFullView.php?l=eng&topicid=30
- https://en.wikipedia.org/wiki/Phong_reflection_model
- https://en.wikipedia.org/wiki/Phong_shading
- https://learnopengl.com/Advanced-Lighting/Advanced-Lighting
원리
Color값을 알기 위해서는 3개의 값을 알아야 한다.
1. Ambient Lighting
Ambient lighting represents lighting that isn't created by direct light rays. Because, This is indirect lighting.
The most simple way to implement ambient lighing is to use constant value
of ambient light intensity.
Constant value
for ambient lighting gives good performance to the shader. but it's very unrealistic.
You think just Ambient is like albedo or colorRGB
.
2. Diffuse reflection
This definition of reflection corresponds to Lambertial reflectance. Lambertian reflectance defines behavior of light rays after hit with matte surface.
Why use the dot(N, L)
?
Because Diffuse is the bright color. Thus when Light is vertical with frag, diffuse is more strong.
3. Speclar reflection
Specular reflection creates highlights on the surface. Highlight is a bright spot, which is visible only when light from light source reflects directly to the camera.
JUST Phong model don't use H(Halfway Vector)
.
결론
Blinn-Phong reflactance model also takes into account a material of the surface. The material has following parameters: level of diffuse reflectance $M_{dif}$, level of specular reflectance $M_{spec}$ and level of reflectance for ambient lighting $M_{amb}$.
즉, ambient + diffuse + specular의 합을 통해서 색을 계산한다.
- ambient는 물체가 가진 색 자체라고 볼 수 있다. 즉,
constant value
이다. - diffuse는
NdotL
와diffuse color
를 통해 값을 도출한다.bright color
라고 소개된다. 그렇다면, 빛의 세기가 강할 수록 더욱 강하게 표현되어야 한다는 것이다.- 그렇기 때문에,
NdotL
의 계산이 들어가는 것이다. 단위 벡터로 변환된 두 벡터의 계산 결과는 두 벡터 사이의cosTheta
이기 때문이다. 두 사이각이 직각에 가까울수록 0에 가까워지고 두 사이각이 0에 가까울 수록 1에 가까워진다. - 이러한 특징을 이용해 해당 영역이 받는 빛의 세기를 알 수 있다. 그리고 이것을 통해서 diffuse를 계산한다.
- specular는
bright spot
이라고 소개된다. 기존 Phong 모델에서는 R(reflection)과 L(light)의 dot product를 통해서 계산이 되었다. 하지만 R을 계산하는 연산은 H를 계산하는 것보다 많은 연산량을 소모할 뿐만 아니라, 사실적이지 않다.- 그렇기 때문에, Blinn-Phong 모델에서는 H를 이용해서 specular를 도출한다.
추가적으로, 거리에 따라 빛의 세기가 달라져야하므로, light까지의 거리를 나누어 줌으로써 거리에 따른 빛의 세기를 적용하였음.
왜 Blinn phong 모델이 작동하지 않을까?
Common.hlsli
내부에 dummy를 추가안해줌으로써, 배열 구조체로 작동하는 내부가 꼬여 문제가 발생한 것이다.
소스코드
precision mediump float;
in vec3 normalInterp;
in vec3 vertPos;
uniform int mode;
const vec3 lightPos = vec3(1.0, 1.0, 1.0);
const vec3 lightColor = vec3(1.0, 1.0, 1.0);
const float lightPower = 40.0;
const vec3 ambientColor = vec3(0.1, 0.0, 0.0);
const vec3 diffuseColor = vec3(0.5, 0.0, 0.0);
const vec3 specColor = vec3(1.0, 1.0, 1.0);
const float shininess = 16.0;
const float screenGamma = 2.2; // Assume the monitor is calibrated to the sRGB color space
void main() {
vec3 normal = normalize(normalInterp);
vec3 lightDir = lightPos - vertPos;
float distance = length(lightDir);
distance = distance * distance;
lightDir = normalize(lightDir);
float lambertian = max(dot(lightDir, normal), 0.0);
float specular = 0.0;
if (lambertian > 0.0) {
vec3 viewDir = normalize(-vertPos);
// this is blinn phong
vec3 halfDir = normalize(lightDir + viewDir);
float specAngle = max(dot(halfDir, normal), 0.0);
specular = pow(specAngle, shininess);
// this is phong (for comparison)
if (mode == 2) {
vec3 reflectDir = reflect(-lightDir, normal);
specAngle = max(dot(reflectDir, viewDir), 0.0);
// note that the exponent is different here
specular = pow(specAngle, shininess/4.0);
}
}
vec3 colorLinear = ambientColor +
diffuseColor * lambertian * lightColor * lightPower / distance +
specColor * specular * lightColor * lightPower / distance;
// apply gamma correction (assume ambientColor, diffuseColor and specColor
// have been linearized, i.e. have no gamma correction in them)
vec3 colorGammaCorrected = pow(colorLinear, vec3(1.0 / screenGamma));
// use the gamma corrected color in the fragment
gl_FragColor = vec4(colorGammaCorrected, 1.0);
}
#include "Common.hlsli"
struct PixelShaderOutput
{
float4 pixelColor : SV_Target0;
};
float3 blinnPhong(float NdotL, float NdotH, float3 diffuseColor, float3 specColor)
{
float3 diffuse = max(NdotL, 0.0) * diffuseColor; // Light Intensity.
float3 specular = pow(max(NdotH, 0.0), 16.0) * specColor;
return diffuse + specular;
}
PixelShaderOutput main(PixelShaderInput input)
{
PixelShaderOutput output;
float3 directLighting = float3(0, 0, 0);
float3 pixelToEye = normalize(eyeWorld - input.posWorld);
float3 ambient = 0.05 * albedoFactor;
[unroll]
for (int i = 0; i < MAX_LIGHTS; i++)
{
if (lights[i].type)
{
float3 lightVec = normalize(lights[i].position - input.posWorld);
float3 normalVec = input.normalWorld;
float3 halfWay = normalize(lightVec + pixelToEye);
float NdotL = max(0.0, dot(normalVec, lightVec));
float NdotH = max(0.0, dot(normalVec, halfWay));
directLighting += blinnPhong(NdotL, NdotH, albedoFactor, float3(0.3, 0.3, 0.3));
directLighting *= (lights[i].spotPower / length(lightVec)) * (lights[i].lightColor / length(lightVec));
}
}
output.pixelColor = float4(ambient + directLighting, 1.0);
return output;
}
'Graphics' 카테고리의 다른 글
6. Deferred Lighting (0) | 2024.07.01 |
---|---|
5. Normal Mapping (0) | 2024.06.27 |
4. Point Shadow Mapping (1) | 2024.06.24 |
3. Directional Shadow Mapping (0) | 2024.06.21 |
2. Depth Buffer와 안개 효과 (1) | 2024.06.14 |