Oren-Nayar 반사 모델과 Lambertian 반사 모델은 모두 빛이 반사되는 표면의 색상을 모델링하는 데 사용되는 반사 모델이다 몇가지 차이가 있다. Michael Oren 과 Shree K. Nayar가 Generalization of Lambert’s reflectance model”, Michael Oren and Shree K. Nayar 논문에서 발표한 모델로 거친 표면으로부터의 diffuse 를 표현하기 위한 reflectance model 이라고 한다.(반대로 거친 표면으로부터의 Specular를 고려한 모델은 Cook-Torrance Model)
Lambertian 모델은 완전히 무광택한, 매끄러운 표면의 반사를 모델링한다. 이 모델에서는 모든 방향으로 동일한 방향성을 가진 빛이 표면에 직각으로 떨어질 때, 모든 방향으로 같은 밝기로 반사되며, 이 모델에서는 표면의 마이크로 구조나 입사각 등의 다른 변수들은 고려되지 않는다
Oren-Nayar 반사 모델은 Lambertian 모델과 비슷하지만, 더 현실적인 반사를 모델링하게 된다. 이 모델은 표면이 거칠고, 입사하는 빛이 여러 방향으로 반사될 때, 즉 입사각이 달라질 때 밝기와 색상이 어떻게 변하는지를 설명하게 된다. 이 모델에서는 표면의 거칠기와 입사각에 따른 반사률의 변화를 고려한다.
또한, Oren-Nayar 모델은 표면이 거칠기 때문에 입사하는 빛이 반사되는 방향이 일치하지 않는 경우를 고려한다. 이러한 경우에는 반사되는 빛이 표면에서 분산되어 다른 방향으로 향하게 되며, 이러한 분산을 표현하는 데 사용되는 표면의 광택도(Glossiness) 또한 고려하여 표현된다.
따라서, Lambertian 모델은 매끄럽고 무광택한 표면의 반사를 모델링하는 반면, Oren-Nayar 모델은 더 현실적인 반사를 모델링하며, 표면의 거칠기와 입사각의 변화에 따른 반사율의 변화를 고려하여 표현되게 된다.
Oren-Nayer Model의 공식을 유도할 때 사용되는 거친 표면(roughness Surface)이라는 것은 서로 다른 각도를 가진 Torrance 와 Sparrow에 의해 제안된 미세면(microfacet)들의 집합으로서 설명될 수 있다. 여기에서 미세면의 표면은 길고 대칭적인 V 자형태의 굴곡(cavity)으로 구성된다고 가정합니다.
또한, 각 cavity 는 두 개의 평면의 면으로 구성. 면의 거친 정도는 경사진 면의 분산을 위한 확률 함수로 결정되는데, 보통 Gaussian distribution 이 사용된다
세부 설명은 https://gamedevforever.com/m/93 참조
float OrenNayar(vec3 lightDir, vec3 viewDir, vec3 surfaceNormal, float roughness, float albedo) { float sigma2 = roughness * roughness; float A = 1.0f - 0.5f * (sigma2 / (sigma2 + 0.33f)); float B = 0.45f * (sigma2 / (sigma2 + 0.09f)); float cosTheta_i = max(dot(surfaceNormal, lightDir), 0.0f); float cosTheta_o = max(dot(surfaceNormal, viewDir), 0.0f); float sinTheta_i = sqrt(max(0.0f, 1.0f - cosTheta_i * cosTheta_i)); float sinTheta_o = sqrt(max(0.0f, 1.0f - cosTheta_o * cosTheta_o)); float maxCos = max(0.0f, dot(lightDir, viewDir)); float sinAlpha, tanBeta; if (cosTheta_i > cosTheta_o) { sinAlpha = sinTheta_o; tanBeta = sinTheta_i / max(1e-7f, maxCos); } else { sinAlpha = sinTheta_i; tanBeta = sinTheta_o / max(1e-7f, maxCos); } float a = 1.0f - 0.5f * (sigma2 / (sigma2 + 0.57f)); float b = 0.45f * (sigma2 / (sigma2 + 0.09f)); float diffuseTerm = albedo * (A + B * max(0.0f, dot(lightDir, surfaceNormal)) * sinAlpha * tanBeta); return diffuseTerm; } |
포프님 블로그에 소개된 경량화 모델
https://kblog.popekim.com/2011/11/blog-post_16.html
half ComputeOrenNayarLighting_Fakey( half3 N, half3 L, half3 V, half roughness ) { // Through brute force iteration I found this approximation. Time to test it out. half LdotN = dot( L, N ); half VdotN = dot( V, N ); half result = saturate(LdotN); half soft_rim = saturate(1-VdotN/2); //soft view dependant rim half fakey = pow(1-result*soft_rim,2); //modulate lambertian by rim lighting half fakey_magic = 0.62; //(1-fakey)*fakey_magic to invert and scale down the lighting fakey = fakey_magic - fakey*fakey_magic; return lerp( result, fakey, roughness ); } |
float4 psOrenNayarSimple(in VS_OUTPUT f, uniform bool UseLookUpTexture) : SV_TARGET { // Make sure the interpolated inputs and // constant parameters are normalized float3 n = normalize( f.normal ); float3 l = normalize( -vLightDirection ); float3 v = normalize( pCameraPosition - f.world ); // Compute the other aliases float gamma = dot ( v - n * dot( v, n ), l - n * dot( l, n ) ); float rough_sq = fRoughness * fRoughness; float A = 1.0f - 0.5f * (rough_sq / (rough_sq + 0.33f)); float B = 0.45f * (rough_sq / (rough_sq + 0.09)); float C; if( UseLookUpTexture ) { // The two dot-products will be in the range of // 0.0 to 1.0 which is perfect for a texture lookup: float tc = float2 ( (VdotN + 1.0f) / 2.0f, (LdotN + 1.0f) / 2.0f ); C = texSinTanLookup.Sample( DefaultSampler, tc ).r; } else { float alpha = max( acos( dot( v, n ) ), acos( dot( l, n ) ) ); float beta = min( acos( dot( v, n ) ), acos( dot( l, n ) ) ); C = sin(alpha) * tan(beta); } float3 final = (A + B * max( 0.0f, gamma ) * C); return float4( cDiffuse * max( 0.0f, dot( n, l ) ) * final, 1.0f ); } |
이때 cDiffuse = ( Albedo / PI ).
https://github.com/przemyslawzaworski/Unity3D-CG-programming/blob/master/oren_nayar.shader
Shader "Oren-Nayar lightmodel" { Properties { roughness("Roughness",Range(0.0,1.0)) = 0.5 } Subshader { Pass { CGPROGRAM #pragma vertex vertex_shader #pragma fragment pixel_shader #pragma target 3.0 float roughness; struct custom_type { float4 screen_vertex : SV_POSITION; float3 world_vertex : TEXCOORD1; }; float4 cuboid (float3 p,float3 c,float3 s) { float3 m = float3(1.0,1.0,0.0); float3 d = abs(p-c)-s; return float4(m,max(max(d.x,d.y),d.z)); } float substraction( float d1, float d2 ) { return max(-d1,d2); } float4 map (float3 p) { float solid=substraction(cuboid(p,float3(0.0,0.0,0.0),float3(1.0,1.0,6.0)).w,cuboid(p,float3(0.0,0.0,0.0),float3(2.0,2.0,2.0)).w); solid=substraction(cuboid(p,float3(0.0,0.0,0.0),float3(1.0,6.0,1.0)).w,solid); solid=substraction(cuboid(p,float3(0.0,0.0,0.0),float3(6.0,1.0,1.0)).w,solid); return float4(float3(1.0,0.0,0.0),solid); } float3 set_normal (float3 p) { float3 x = float3 (0.01,0.00,0.00); float3 y = float3 (0.00,0.01,0.00); float3 z = float3 (0.00,0.00,0.01); return normalize(float3(map(p+x).w-map(p-x).w, map(p+y).w-map(p-y).w, map(p+z).w-map(p-z).w)); } float3 oren_nayar(float3 rd, float3 ld, float3 n) { float3 col = float3(0.0,0.0,0.0); float NdotL = dot(n, ld); float NdotV = dot(-rd, n); float angleVN = acos(NdotV); float angleLN = acos(NdotL); float mu = roughness; float A = 1.0-0.5*mu*mu/(mu*mu+0.57); float B = 0.45*mu*mu/(mu*mu+0.09); float alpha = max(angleVN, angleLN); float beta = min(angleVN, angleLN); float gamma = dot(-rd -(n * NdotV), ld - (n * NdotL)); float albedo = 1.1; float e0 = 3.1; float L1 = max(0.0, NdotL) * (A + B * max(0.0, gamma) * sin(alpha) * tan(beta)); return float3(1.0,1.0,1.0) * L1; } float3 lighting (float3 p, float3 rd) { float3 AmbientLight = float3 (0.5,0.5,0.5); float3 LightDirection = normalize(float3 (4.0,10.0,-10.0)); float3 LightColor = float3 (0.0,0.0,1.0); float3 NormalDirection = set_normal(p); return oren_nayar (rd,LightDirection,NormalDirection); } float4 raymarch (float3 ro, float3 rd) { for (int i=0; i<128; i++) { float ray = map(ro).w; float3 material = map(ro).xyz; if (ray < 0.001) return float4 (lighting(ro,rd)*material,1.0); else ro+=ray*rd; } return float4 (0.0,0.0,0.0,1.0); } custom_type vertex_shader (float4 vertex : POSITION) { custom_type vs; vs.screen_vertex = mul (UNITY_MATRIX_MVP, vertex); vs.world_vertex = mul (_Object2World, vertex); return vs; } float4 pixel_shader (custom_type ps ) : SV_TARGET { float3 worldPosition = ps.world_vertex; float3 viewDirection = normalize(ps.world_vertex - _WorldSpaceCameraPos.xyz); return raymarch (worldPosition,viewDirection); } ENDCG } } } |
'Technical Report > R&D test' 카테고리의 다른 글
GLSL Rotate image shader (0) | 2017.07.21 |
---|---|
Unity Bilboard shader (0) | 2017.07.13 |
Texture Array (0) | 2016.08.02 |
Ai Standard Material Library (0) | 2016.01.22 |
Fast Decal (0) | 2015.05.16 |