헤더파일

HLSL 프로그래밍 - 디렉셔널 라이트 본문

DirectX

HLSL 프로그래밍 - 디렉셔널 라이트

헤더파일 2018. 3. 1. 20:06

디렉셔널 라이트는 해나 달처럼 매우 멀리서 부터 도달하는 넓은 빛을 시뮬레이션할 때 쓰입니다. 실내 렌더링에서는 디렉셔널 라이트는 없으므로 따로 원래는 상수버퍼를 따로 만들어서 빛의 방향, 세기, 색상을 전달 합니다.

다이렉션 라이트는 모든 광선이 평행하게 온다고 가정하기 때문에 미리 계산한 ㅈㅎ며===


OnFrameMove 함수


g_LightManager.SetDirectional(g_vDirLightDir, g_vDirLightColor);


추가로 디렉셔널 라이트의 색상과 방향을 설정.


OnD3D11FrameRender 함수


g_SceneManager.DepthPrepass(pd3dImmediateContext);


 z버퍼 계산용 셰이더(픽셀 셰이더는 없고 위치를 투영공간으로만 변환)를 만들어서 z버퍼만 일단 계산. 그 이후에 다시 한번 그리면 픽셀셰이더에서 z값을 기준으로 뒤에있는 건 안 그릴 수 있으므로 필요없는 픽셀은 안 그릴수 있습니다. 

(참고 https://ypchoi.gitbooks.io/rendering-techniques/content/z_prepass.html)


상수 버퍼 설정

Usage 

 D3D11_USAGE_DYNAMIC

BindFlags

 D3D11_BIND_CONSTANT_BUFFER

CPUAccessFlags

 D3D11_CPU_ACCESS_WRITE

ByteWidth

 CB_DIRECTIONAL




#pragma pack(push,1)

struct CB_DIRECTIONAL

{

D3DXVECTOR3 vAmbientLower;

float pad;

D3DXVECTOR3 vAmbientRange;

float pad2;

D3DXVECTOR3 vDirToLight;

float pad3;

D3DXVECTOR3 vDirectionalColor;

float pad4;

};

#pragma pack(pop)


전에 만든 상수 버퍼 구조체에 빛의 방향 벡터, 빛의 색깔을 추가로 선언합니다.


셰이더 파일


/////////////////////////////////////////////////////////////////////////////

// Constant Buffers

/////////////////////////////////////////////////////////////////////////////

cbuffer cbPerObjectVS : register( b0 ) // Model constants

{

    float4x4 WorldViewProjection : packoffset( c0 );

float4x4 World : packoffset( c4 );

}


cbuffer cbPerObjectPS : register( b0 ) // Model constants

{

      float3 EyePosition : packoffset( c0 );

float specExp : packoffset( c0.w );

float specIntensity : packoffset( c1 );

}


cbuffer cbDirLightPS : register( b1 ) // Directional and ambient light constants

{

float3 AmbientDown : packoffset( c0 );

float3 AmbientRange : packoffset( c1 );

float3 DirToLight : packoffset( c2 );

float3 DirLightColor : packoffset( c3 );

}


/////////////////////////////////////////////////////////////////////////////

// Diffuse texture and linear sampler

/////////////////////////////////////////////////////////////////////////////

Texture2D    DiffuseTexture : register( t0 );

SamplerState LinearSampler : register( s0 );


/////////////////////////////////////////////////////////////////////////////

// shader input/output structure

/////////////////////////////////////////////////////////////////////////////

struct VS_INPUT

{

float4 Position : POSITION; // vertex position 

float3 Normal : NORMAL; // vertex normal

float2 UV : TEXCOORD0; // vertex texture coords 

};


struct VS_OUTPUT

{

float4 Position : SV_POSITION; // vertex position 

float2 UV : TEXCOORD0; // vertex texture coords

float3 Normal : TEXCOORD1; // vertex normal

float3 WorldPos : TEXCOORD2; // vertex world position

};


/////////////////////////////////////////////////////////////////////////////

// Vertex shader

/////////////////////////////////////////////////////////////////////////////

float4 DepthPrePassVS(float4 Position : POSITION) : SV_POSITION

{

return mul( Position, WorldViewProjection );

}


VS_OUTPUT RenderSceneVS( VS_INPUT input )

{

VS_OUTPUT Output;

float3 vNormalWorldSpace;

    

// Transform the position from object space to homogeneous projection space

Output.Position = mul( input.Position, WorldViewProjection );

    

// Transform the position to world space

Output.WorldPos = mul(input.Position, World).xyz;


// Just copy the texture coordinate through

Output.UV = input.UV; 


Output.Normal = mul(input.Normal, (float3x3)World);

    

return Output;    

}


/////////////////////////////////////////////////////////////////////////////

// Pixel shaders

/////////////////////////////////////////////////////////////////////////////


// Ambient light calculation helper function

float3 CalcAmbient(float3 normal, float3 color)

{

// Convert from [-1, 1] to [0, 1]

float up = normal.y * 0.5 + 0.5;


// Calculate the ambient value

float3 ambient = AmbientDown + up * AmbientRange;


// Apply the ambient value to the color

return ambient * color;

}


// Material preparation

struct Material

{

float3 normal;

float4 diffuseColor;

float specExp;

float specIntensity;

};


Material PrepareMaterial(float3 normal, float2 UV)

{

Material material;


// Normalize the interpulated vertex normal

material.normal = normalize(normal);


// Sample the texture and convert to linear space

    material.diffuseColor = DiffuseTexture.Sample( LinearSampler, UV );

material.diffuseColor.rgb *= material.diffuseColor.rgb;


// Copy the specular values from the constant buffer

material.specExp = specExp;

material.specIntensity = specIntensity;


return material;


// Directional light calculation helper function

float3 CalcDirectional(float3 position, Material material)

{

// Phong diffuse

float NDotL = dot(DirToLight, material.normal);

float3 finalColor = DirLightColor.rgb * saturate(NDotL);


// Blinn specular

float3 ToEye = EyePosition.xyz - position;

ToEye = normalize(ToEye);

float3 HalfWay = normalize(ToEye + DirToLight);

float NDotH = saturate(dot(HalfWay, material.normal));

finalColor += DirLightColor.rgb * pow(NDotH, material.specExp) * material.specIntensity;


// Apply the final light color to the pixels diffuse color

return finalColor * material.diffuseColor.rgb;

}


float4 DirectionalLightPS( VS_OUTPUT In ) : SV_TARGET0

{

// Prepare the material structure

Material material = PrepareMaterial(In.Normal, In.UV);


// Calculate the ambient color

float3 finalColor = CalcAmbient(material.normal, material.diffuseColor.rgb);

   

// Calculate the directional light

finalColor += CalcDirectional(In.WorldPos, material);


// Return the final color

return float4(finalColor, 1.0);

}


cbuffer cbPerObjectPS : register( b0 ) // Model constants

{

      float3 EyePosition : packoffset( c0 );

float specExp : packoffset( c0.w );

float specIntensity : packoffset( c1 );

}


SceneManager에서 픽셀셰이더로 넘겨 받는 상수버퍼가 추가됬습니다. 

여기에는 카메라 위치, 정반사광의 지수(얼마나 퍼질 것인지), 정반사광의 강도가 들어있습니다.


cbuffer cbDirLightPS : register( b1 ) // Directional and ambient light constants

{

float3 AmbientDown : packoffset( c0 );

float3 AmbientRange : packoffset( c1 );

float3 DirToLight : packoffset( c2 );

float3 DirLightColor : packoffset( c3 );

}


빛의 방향, 빛의 색깔이 추가되었습니다.  DirectionalLight.cpp에 OnFrameMove함수에서  SetAmbient와 SetDirectional을 불러와 입력됩니다.

빛의 방향은 (1.0, -1.0f, 1.0f) 로 설정되어 있습니다.


// Directional light calculation helper function

float3 CalcDirectional(float3 position, Material material)

{

// Phong diffuse

float NDotL = dot(DirToLight, material.normal);

float3 finalColor = DirLightColor.rgb * saturate(NDotL);


// Blinn specular

float3 ToEye = EyePosition.xyz - position;

ToEye = normalize(ToEye);

float3 HalfWay = normalize(ToEye + DirToLight);

float NDotH = saturate(dot(HalfWay, material.normal));

finalColor += DirLightColor.rgb * pow(NDotH, material.specExp) * material.specIntensity;


// Apply the final light color to the pixels diffuse color

return finalColor * material.diffuseColor.rgb;

}


퐁의 난반사계산 공식에 따르면 난반사(diffuse)량은 노멀값과 빛의 방향의 내적의 값과 같습니다. 0이하의 값은 빛이 물체의 뒤에 있다는 것이므로

sarturate 함수로 버려줍니다. 이 값을 텍셀 값에 곱해 텍셀이 얼마 밝게 표현될지 나타냅니다.


정반사광 계산을 위해 반사를 계산하는건 리소스가 많이 필요하므로 카메라 벡터(To Eye)와 입사광 벡터(DirToLight)의 중간 각도의 베터를 이용합니다.

반사벡터와 카메라벡터를 차이를 계산하는 것과 중간벡터와 노멀벡터의 차이를 계산하는 것이 거의 근사하기 때문입니다.

NDotH가 이렇게 구한 중간벡터와 노멀벡터의 차이인데 이 차이에 거듭제곱을 하는 이유는 정반사광은 난반사광보다 작은 점에 모이기 때문입니다.

코사인 거듭제곱 그래프를 보시면 제곱할 수록 그래프가 더 빨리 감소하는 걸 볼 수 있습니다.


엠비언트 라이트와 다이렉션 라이트를 합한 값이 최종 픽셀의 색깔이 됩니다.

Comments