개발/쉐이더 (8)
카메라 이미지이펙트 - 그레이톤 , 울렁거림 , 왜곡, 색수차

울렁거림 + 그레이톤

노이즈 텍스처를 사용한 왜곡

색수차 (갑자기 깨지는건 gif캡처문제)

 

 

울렁거림할때 알아둬야할 것

마치 카메라가 움직이는 듯한 효과를 주는것은 uv맵에 있는 값들에 균등하게 값을 높이거나 낮춰준 것이다.

그러므로 이런 uv맵을 균등하게 움직일때는 https://www.desmos.com/calculator/geupxd7k4h을 이용해서 움직여주자

예를들어 이렇게 uv에 값을 주면 카메라가 원모양으로 움직이게 된다.

  Comments,     Trackbacks
카메라 이미지 이펙트 만들기 - 기초지식, 반전효과

[공부한 걸 정리하는 글이므로 자세한 내용은 알아갈 수 없습니다.]

[혹시라도 배우기 위해 제 블로그를 찾아주신거라면 다른 블로그를 찾아주시면 감사드리겠습니다.]

 

 

 

필요한 사전 이해가 필요한 API

https://docs.unity3d.com/kr/530/ScriptReference/Graphics.Blit.html

 

Unity - 스크립팅 API: Graphics.Blit

This is mostly used for implementing image effects. Blit sets dest as the render target, sets source _MainTex property on the material, and draws a full-screen quad. Note that if you want to use depth or stencil buffer that is part of the source (Render)te

docs.unity3d.com

 

Graphics.Blit

카메라에 랜더링할 이미지를 교체해주는 작업이라고 생각하면 됨 자세한 내용은 확인

 

https://docs.unity3d.com/kr/530/ScriptReference/MonoBehaviour.OnRenderImage.html

 

Unity - 스크립팅 API: MonoBehaviour.OnRenderImage(RenderTexture,RenderTexture)

포스트프로세싱 효과. 쉐이더 기반의 필터로 처리하여 최종 이미지를 변경할 수 있습니다. incoming 이미지는 source 렌더텍스쳐입니다. 결과로는 destination render texture. You must always issue a Graphics.Blit()

docs.unity3d.com

OnRenderImage()

OnDestroy()나 Update같이 특정 상황에 발동되는 함수로 화면에 랜더링 될때

최종이미지를 변경하기 위해 사용되는 함수

 

 

 

 

 

 

 

 

위의 스크립트들을 이용해서  이미지 이펙트 쉐이더작성하고 머테리얼로 만들어 넣어주면 된다.

 

1번째 결과물 인버스 쉐이더

 

 

이미지 이펙트 쉐이더 작성시 주의할 점

 

Cull off ( 모델의 앞 뒤 모두 랜더링 해야하고)

ZWrite off ( 뒤에있는 오브젝트들이 랜더링 되도록 해야한다)

자세한 내용

https://m.blog.naver.com/PostView.nhn?blogId=ateliersera&logNo=220398051030&proxyReferer=https:%2F%2Fwww.google.com%2F

 

알기 쉬운 유니티 쉐이더 강좌 #004 -ShaderLab : Culling & Depth Testing / Blending / Tags / Pass Tags

이번엔 앞에서 언급한 컬링과 뎁스 테스팅, 블랜딩 등에 대해서 알아보겠습니다. Culling & Depth T...

blog.naver.com

 

 

  Comments,     Trackbacks
노드 툰 쉐이더 멀티라이팅

사용한 디퓨즈 워래핑 그라디언트 이미지

Lighting정보는 cs파일로 직접 넣어줌

  Comments,     Trackbacks
유니티 쉐이더 - 툰 쉐이더

오늘 할 툰 쉐이더는 아웃라이너를 frensel로 표현한 방법이다.

cull back cull front를 이용한 쉐이더 방식이 아니다.

 

 

우선 툰 쉐이더를 만들기 전에 half Lamber공식을 이용한 기본적인 쉐이더를 만들어 준다.(자세한건 간단하니 검색)

 

그러고 나서 끊어지는 음영을 처리해줘야 하는데,

 

Half람버트의 빛의 강도 공식

float ndot = dot(s.Normal, lightDir)*0.5f+0.5f;

여기서 ndot의 값을 변경해주어 표현하면 된다.

 

그 표현 방법이 if문이나 ceil을 이용하는 방법인데

 

if문을 이용하는 방법은 아래와같고 굉장히 무겁다.

if(ndot>0.7f)

{

    ndot=1;

}

else if(ndot>0.4f)

{

    ndot=0.7;

}

위의 공식대로 하면

ceil을 이용하는 방법은

 

ceil은 소수점아래를 올리는 것이다.

※0.01~1.0 = 1

1.01~2.0 = 2

 

 그러므로 이때 그림자를 5분할 하고 싶다면

ndot*=5;(0~5)값을 나타냄

ndot=ceil(ndot)/5;(나누어 줘서 0~1사이 값으로 나타내기)

하지만 이렇게하면 좀 더 세부적으로 표현할 수 없다.

 

 

 

이제 외곽선을 표현해볼것인데.

림을 이용하여 표현해 볼거다.

림은 표현방식이 빛의강도와 다르게

viewDir과 Normal을 내적한 값이다.

이때 이를 abs(절대값)을 취해서

 

if(rim>0.3)

rim=1;

else

rim=-1;// 노멀과 viewDir의 사이각이 크면 외곽선 처리를 해줌 (보통 가장자리거나 눈 아래같은 곳이겠지!)

 

그럼 이런식으로 된다.

 

 

끝!

 

 

덧붙여서, Diffuse Wrapping 방식은 지정된 텍스처를 음영으로 이용해서 음영을 표현하는 방식인데

내가 지금 자료가 없는 관계로 나중에 찾아서 해보거나 자료를 찾아보길 바란다!

  Comments,     Trackbacks
유니티 쉐이더 - 블린퐁

블린 퐁은 퐁쉐이딩을 간단화한 공식이다.

 

퐁 쉐이딩은 다루지 않을 예정이니 간단하게 말하고 넘어가면

 

"내가 보는 방향으로부터 반사된 방향에 조명이 잇으면 그 부분의 하이라이트가 가장 높다"

라는 것이 퐁 쉐이딩의 이론이다.

 

그래서 퐁 쉐이딩은 넘어가고

 

블린 퐁 쉐이딩이라니는 퐁쉐이딩을 간단화한 공식은

H = normalize(lightDir+viewDir):이다 ( 벡터의 방향은 위의 그림과같음 )

 

빛의 강도는

float specLightPower = pow(saturate(dot(H,surface의 NomalVector)),원하는 강도); 이고

빛의 표현은 이렇게 해주면되고

float3 specColor = specColor(입력값)*specLightPower;

 

float4 finalColor = float4(디퓨즈 컬러 + specColor,surface의 알파값);

 

을 해주면 된다.

 

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
Shader "Custom/BlinPhong"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("NormalMap",2D) = "bump"{}
        _SpecCol("Specular Color", Color) = (1,1,1,1)
        _SpecPow("Specular Pow",Range(10,100)) = 100
    }
            SubShader
            {
                Tags { "RenderType" = "Opaque" }
                LOD 200
 
                CGPROGRAM
            // Physically based Standard lighting model, and enable shadows on all light types
            #pragma surface surf Test
 
            // Use shader model 3.0 target, to get nicer looking lighting
            #pragma target 3.0
            
        sampler2D _MainTex;
        sampler2D _BumpMap;
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };
        float4 _SpecCol;
        float _SpecPow;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        float4 LightingTest(SurfaceOutput s, float3 lightDir, float3 viewDir,float atten)
        {
            float nDot = saturate(dot(s.Normal, lightDir));
            float4 diffuse;
            diffuse.rgb= nDot * s.Albedo*_LightColor0.rgb*atten;
            
            float3 h = normalize(lightDir + viewDir);
            float specDot = saturate(dot(h,s.Normal));
            specDot = pow(specDot, _SpecPow);
            float3 specColor = specDot* _SpecCol.rgb;
 
            float4 final;
            final.rgb = diffuse.rgb + specColor.rgb;
            final.a = s.Alpha;
 
            return final;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 
그래서 코딩을 하면 이런식으로 코딩할 수 있다.
참고로 Surface 쉐이딩을 할때,
커스텀 라이팅을 쓰려면 
pragma 부분에 surf 다음에 원하는 이름을 넣고, 커스텀 라이팅 함수이름을
Lighting+prgma부분에서 지정한 이름 을 넣어주면 된다.

그리고 매개변수는 항상 정해져있고 ( 갯수에 따라 다른게 오버로딩 된다)

아래를 가보면 정확하게 알 수 있다.

https://docs.unity3d.com/kr/2018.4/Manual/SL-SurfaceShaderLighting.html

 

 

 

아래는 완성된 스페큘러 + 디퓨즈

 

 

 

  Comments,     Trackbacks
Unity Shader 공부- 림라이트

림라이트는 저렇게 경계면에서 빛나는 듯한 연출을 할때 사용되는 빛이다.

 

게임에서 보면 이렇다

그래서 오늘은 림라이트를 만들어 볼건데..

 

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
Shader "Custom/RimShader"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("NormalMap",2D) = "bump"{}
        _RimColor("RimColor", Color) = (1,1,1,1)
        _RimPower("RimPower",Range(1,10))=3
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
       
 
        CGPROGRAM 
        #pragma surface surf Lambert noambient
 
 
        sampler2D _MainTex;
        sampler2D _BumpMap;
        float4 _RimColor;
        float _RimPower;
        
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_Bump;
            float3 viewDir;
        };
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);//우선 uv에 텍스처를 입힌다.
            o.Albedo = c.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_Bump));//노멀맵을 뽑아내서 나중에 빛계산을 위해 노말을 뽑아놓음
            float lightPower = dot(o.Normal, IN.viewDir);//노멀맵에 ViewDir를 내적하면 해당 버텍스의 빛의 세기가 나온다.
            //o.Emission = pow(1 - lightPower, _RimPower) * _RimColor.rgb; //1-lightPower을 해서 빛을 역전시켜줌,거기에 _RimPower만큼 제곱
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

우선 림을 적용시키지 않고 기본적인 람버트 쉐이딩으로 표현하면

요론식으로 된다.

그리고 이때 Rim을 적용시키면? *위의 코드에서 주석만 제거*

 

오웅 이렇게 후광있는듯이 가능해진다.

 

여기까지 아주 간단한 림라이트였고, 이거로 홀로그램을 만들어 보려한다.

 

참고로 o.Emission은 자체적으로 방출하는 색상으로

 

Albedo에 + 되는 색상이다.

그러므로 o.Emission에 림값을 넣어줌으로써 저렇게 경계면이 하얗게 올라오는 것

 

 

 

이걸 응용해서 홀로그램을 만들 수 있는데.

 

 

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
Shader "Custom/RimShader"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        //_BumpMap("NormalMap",2D) = "bump"{}[제거]
        _RimColor("RimColor", Color) = (1,1,1,1)
        _RimPower("RimPower",Range(1,10))=3
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
       
 
        CGPROGRAM 
        #pragma surface surf Lambert noambient alpha:fade//[변환]
 
 
        sampler2D _MainTex;
        //sampler2D _BumpMap;[제거]
        float4 _RimColor;
        float _RimPower;
        
        struct Input
        {
            float2 uv_MainTex;
            //float2 uv_Bump; [제거]
            float3 viewDir;
        };
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);//우선 uv에 텍스처를 입힌다.
            //o.Albedo = c.rgb;[제거]
            //o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_Bump));[제거]
            float lightPower = dot(o.Normal, IN.viewDir);//노멀맵에 ViewDir를 내적하면 해당 버텍스의 빛의 세기가 나온다.
            float rim = pow(1 - lightPower, _RimPower);//1-lightPower을 해서 빛을 역전시켜줌,거기에 _RimPower만큼 
            o.Emission = _RimColor.rgb;
            o.Alpha = rim;//라이트 파워만큼의 알파
        }
        ENDCG
    }
    FallBack "Diffuse"
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

일단은 제거하고 변경해준다. (위처럼)

오오.. 그럼 이렇게 albedo는 투명하게 나오고 Rim이 처리된 부분만 보이는 투명물질이 만들어진다!

 

그리고 여기서 홀로그램같은 느낌을 표현하려면

 

우선 연출을 위한 코드를 구현해야하는데

1
2
o.Emission = frac(IN.worldPos.g);
o.Alpha = 1;//라이트 파워만큼의 알파
 

 우선 Emission 부분을 이런식으로 바꿔보자

frac 내장함수는 소수점 부분만을 표현하는 것이다 즉0~1사이라는 것.

 

그렇다면 오브젝트의 월드좌표에서 y값이 0일시 저런식으로 표현된다.

하얀색이 끝나는부분이 (??,1,??) 인 것.

 

1
o.Emission = pow(frac(IN.worldPos.g*3),30);
 

이 코드에 대해 확실히 알았다면 *3을 해줬을때 컬러의 0~1사이 값을 표현하는게 더 많아 지는 것을 눈치 챌 수 있을거고,

pow를 해주면 림라이트에서 더 경계면쪽으로 색상이 모여들었듯이

 

1아래의 값들이 좀더 작아지면서 어두운 부분이 더 많아 지는것을 눈치 챌것이다.

1
2
3
4
5
6
7
8
void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);//우선 uv에 텍스처를 입힌다.
            float lightPower = dot(o.Normal, IN.viewDir);//노멀맵에 ViewDir를 내적하면 해당 버텍스의 빛의 세기가 나온다.
            float rim = pow(1 - lightPower, _RimPower)+ pow(frac(IN.worldPos.g * 3 - _Time.y), 30);//1-lightPower을 해서 빛을 역전시켜줌,거기에 _RimPower만큼 
            o.Emission = _RimColor;
            o.Alpha = rim;//라이트 파워만큼의 알파
        }
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

그리고 색상을 결정할 때 _Time.y 만큼 빼주면 흰선들이 올라가는걸 표현할 수 있고 Rim에다가 더해주고

아까처럼 코드를 바꿔주면

요로코롬 된다.

 

노멀맵을 적용 시켜주면

완성!

 

역시 쉐이더는 보이는게 있어서 재미있다.

 

내일은 블린퐁 만들어봐야징

  Comments,     Trackbacks
유니티 쉐이더 공부 - 디퓨즈 , 앰비언트 , 스페큘러 , 텍스처 구현

오늘의 실험체가 될 오리쿤

오늘은 커스텀으로 디퓨즈, 앰비언트 , 스페큘러를 텍스처맵핑을 구현해보려한다.

 

가르쳐주는 블로그가아닌 공부하는 블로그이기 때문에 자세한 내용은 생략하고,

 

우선 차근차근 텍스처부터 씌워보도록 하겠다

 

유니티 Defalut 텍스처가 입혀져있는 오리쿤

위의 모델에 텍스처를 입히려면 일단 머테리얼이 필요한데

일단 DuckMat 머테리얼과 DuckShader라는 언릿 쉐이더를 추가해보았다. 당연히 DuckMat는 DuckShader를 사용

기본 Shader구성

기본적으로 세팅되있는 쉐이더를 깔끔하게 Fog 설정부분을 삭제해준뒤에.. vert frag 쉐이더를 보면..

이와 같은 걸 볼 수 있다.

 _MainTex는 프로퍼티에서 받아온 텍스처!

 

결국 이 부분이 텍스처를 넣는 부분이라는 것!

appdata 의 uv 좌표에 texture를 씌어넣는 작업이

o.uv = TRNASFORM_TEX(v.uv, _MainTex);이고

 

마지막으로 frag쉐이더에서

tex2D(_MainTex, i.uv);를 해줌으로써 해당 픽셀의 색상에 텍스처의 해당 픽셀 색상을 집어넣음으로서 결정!

 

참고 : _MainTex_ST 라는 프로퍼티에 추가도 하지 않은 변수가 있는데 이게 TRNSFORM_TEX를 사용하기 위해 존재한다고합니당 ㅇㅇ 일단 초보니까 넘어가도록 하죵

 

이제 디퓨즈,앰비언트를 계산해보자!

 

디퓨즈를 사용하기 위해선

 

우선 빛의 방향, 노멀벡터 가 필요 ( 빛의 밝기를 정하기 위해서)

 

vert 쉐이더에서

 

worldNormal = float3 worldNormal = UnityObjectToWorldNormal(v.normal);

// 월드 노멀벡터계산
o.worldNormal = worldNormal;
o.vertexWorld = mul(unity_ObjectToWorld, v.vertex);

//mul함수는 두행렬의 곱을 계산하는 것 unity_ObjectToWorld는 오브젝트의 모델행렬

 

frag 쉐이더에서

 

float3 normalVector = normalize(i.worldNormal);
float3 lightDirection = normalize(UnityWorldSpaceLightDir(i.vertexWorld));

 

float nl = max(0.0f, dot(lightDirection, normalVector)); //위를 통해서 밝기를 찾아내서
float diffuseTerm = nl*_Color*tex*_LightColor0;//디퓨즈의 컬러를 구한다!

 

_LightColoor0는 셰이더 내장 변수로 lightColor를 반환함

출처)

https://docs.unity3d.com/kr/530/Manual/SL-UnityShaderVariables.html

 

유니티 - 매뉴얼: Built-in shader variables

Built-in shader helper functions Making multiple shader program variants Built-in shader variables Unity provides a handful of built-in global variables for your shaders: things like current object’s transformation matrices, light parameters, current time

docs.unity3d.com

 

디퓨즈는 이대로 되었고

 

다음은 앰비언트인데 앰비언트는 cutOff의 역할을 수행한다고한다 ( 조명이 없어도 최소밝기 )

그래서


float nl = max(_Ambient, dot(lightDirection, normalVector));

 

프로퍼티에_Ambient라는 Range(0,1)값을 추가해서 이런식으로 디퓨즈에서 사용하면 된다.

 

위의 코드를 수행하고나면??

 

오오.. 영롱하군여

앰비언트 값을 변경해주면 이렇게된다

 

이제 마지막으로 스펙큘러!

 

스펙큘러는 퐁쉐이딩을 사용할 예정이고

 

프로퍼티에 추가해야할 값

_Shininess("Shininess", Float) = 10.0
_SpecCol ("SpecColor", Color) = (1,1,1,1)

 

frag 쉐이더에서 처리해줘야할 일


float3 reflectDirection = normalize(reflect(-lightDirection, normalVector));

//정반사 빛의 방향
float3 specularNL = max(0.0, dot(viewDirection, reflectDirection));

//해당 frag의 정반사된 빛의 밝기
float3 specular = pow(specularNL, _Shininess);

//원하는만큼 제곱해서 더밝게하기
float4 specularTerm = float4(specular, 1.0f)*_SpecCol * _LightColor0;

//결과

 

//참고로 specularNL과 specular는 원래 float만 해도되는데 내가 공부했던 책에는 이렇게 되있어서 올림..

float specularNL = max(0.0f, dot(viewDirection, reflectDirection));
float specular = pow(specularNL, _Shininess);

float4 specularTerm = specular *_SpecCol * _LightColor0;

이렇게해도 상관없음

 

요로코롬 해주면?

 

float4 finalCol = diffuseTerm+specularTerm;
                return finalCol;

해줬을시에

 

완성!

 

역시 쉐이더쪽은 뭔가 보이는게 확실해서 너무 재밌는거같다

 

 

 

번외) 다중 라이팅을 지원하려면 어떻게해야하는가??

오른쪽이 다중라이팅을 지원하지 않는 지금까지 만든 쉐이더이고

왼쪽은 다중라이팅을 지원하는 쉐이더이다.

 

이를 만드려면

기존 페스에
#pragma multi_compile_fwdbase를 추가하고

 

 새로운 페스를 추가하고 복붙한뒤에

"LightMode" = "FowardBase" -> "LightMode" = " ForwardAdd"

Blend One One

#pragma multi_compile_fwdadd

을 추가한 뒤

새로운 페스의 디퓨즈값에 Ambient값을 0.0으로 해주면 된다.

 

'개발 > 쉐이더' 카테고리의 다른 글

노드 툰 쉐이더 멀티라이팅  (0) 2020.06.26
유니티 쉐이더 - 툰 쉐이더  (0) 2020.05.08
유니티 쉐이더 - 블린퐁  (0) 2020.05.04
Unity Shader 공부- 림라이트  (0) 2020.05.04
유니티 쉐이더 공부 - 각종 빛  (0) 2020.04.02
  Comments,     Trackbacks
유니티 쉐이더 공부 - 각종 빛

실제 빛

실제 빛은 x라는 표면에 w방향 마다 모든 빛을 계산해야하지만 이는 연산이 너무많아서..

컴퓨터에서는 근사값으로 처리한다.

 

 

컴퓨터 빛의 종류

디퓨즈-다양항 방향으로 빛을 나타냄(반사)

스펙큘러-특정 방향으로 빛을 나타냄(반사)

엠비언트-씬안에서의 최소의 빛강도

 

 

 

 

밝기를 계산하는 방법

Float brightness = cos(입사각)

Float brightness = dot(normal,lightDir) //중요!

->증명

->Dot(a,b) = |a||b|cos(입사각)  [ |a||b| 가 노멀라이즈되서 1이기 때문에 ] normallightDir의 내적은 cos(입사각)과 같고 밝기와 같다

 

 

위의 확률은 각도에따라 빛이 얼마나 반사되는지를 말해준다.

0도일 때 100%라면 당연히 cos이겠지? 이런식으로 외워두면 좋을것 같다.

 

Vec3 normal = normalize(Normal);

Vec3 lightDir = normalize(lightPos – FragPos);

아까 위에서 말한 밝기 계산공식중

Float brightness = dot(normal,lightDir);

 

아까 위에서도 말했지만 중요하니 다시말하자면 Dot(a,b) = |a||b|cos(입사각)  [ |a||b| 가 노멀라이즈되서 1이기 때문에 ] normallightDir의 내적은 cos(입사각)과 같고 밝기와 같다

 

  Comments,     Trackbacks