본문으로 바로가기

approximate oren-nayar shader

category Technical Report/R&D test 2017. 6. 19. 14:48
반응형

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 ).

위와같은 sin()과 tan()함수를 계산한 텍스처를 참조해서 사용

 

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