
글에 사용된 모든 그림과 내용은 직접 작성한 것입니다.
[유튜브 영상]
[깃허브 보러가기]
https://github.com/Chang-Jin-Lee/D3D11-AliceTutorial/tree/main/25_ToonShading_Outline
[풀리퀘 보러가기]
https://github.com/Chang-Jin-Lee/D3D11-AliceTutorial/pull/50
D3D11에서 아웃라인을 그리는 방법을 정리하기 위함
| All Shader Collection |
|---|
VSOutOutline VSOutline(VSInOutline vIn)
{
VSOutOutline o;
// View-space XY 팽창: 깊이는 유지하여 실루엣만 보이도록
float4 posW = mul(float4(vIn.posL, 1.0f), g_World);
float4 posV = mul(posW, g_View);
float3 nW = normalize(mul(vIn.normalL, (float3x3) g_WorldInvTranspose));
float3 nV = normalize(mul(nW, (float3x3) g_View));
float2 dir = nV.xy;
float len = max(length(dir), 1e-5);
dir /= len;
posV.xy += dir * g_OutlineThickness;
o.posH = mul(posV, g_Proj);
return o;
}
VertexOut VSSkinnedOutline(VertexInSkinned vIn)
{
VertexOut vOut;
uint4 bi = vIn.boneIdx; float4 bw = vIn.boneW;
matrix M = bw.x * g_BonePalette[bi.x]
+ bw.y * g_BonePalette[bi.y]
+ bw.z * g_BonePalette[bi.z]
+ bw.w * g_BonePalette[bi.w];
float4 skinnedPos = mul(float4(vIn.posL,1.0f), M);
float3x3 M3 = (float3x3)M;
float3 skinnedN = normalize(mul(vIn.normalL, M3));
// 월드→뷰 변환
float4 posW = mul(skinnedPos, g_World);
float4 posV = mul(posW, g_View);
float3 nW = normalize(mul(skinnedN, (float3x3)g_WorldInvTranspose));
float3 nV = normalize(mul(nW, (float3x3)g_View));
// 화면 두께가 보이도록 z 성분 제거 후 XY 평면으로만 팽창
float2 nVP = normalize(max(abs(nV.x) + abs(nV.y), 1e-5) * (nV.xy / max(length(nV.xy), 1e-5)));
posV.xy += nVP * g_OutlineThickness;
// 프로젝션
vOut.posH = mul(posV, g_Proj);
vOut.posW = posW.xyz;
vOut.normalW = nW;
vOut.tangentW = float3(1,0,0);
vOut.bitanW = float3(0,1,0);
vOut.tex = vIn.tex;
vOut.color = float4(0,0,0,1);
return vOut;
}
struct PSInOutline
{
float4 posH : SV_POSITION;
};
float4 PSOutline(PSInOutline i) : SV_Target
{
float s = saturate(g_OutlineStrength);
return float4(g_OutlineColor.rgb * s, 1.0f);
}
CD3D11_RASTERIZER_DESC rasterizerDesc(CD3D11_DEFAULT{});
rasterizerDesc.FillMode = D3D11_FILL_SOLID;
rasterizerDesc.CullMode = D3D11_CULL_BACK;
rasterizerDesc.FrontCounterClockwise = true;
HR_T(m_->m_pDevice->CreateRasterizerState(&rasterizerDesc, &m_->RSCullClockWise));
m_->m_pDeviceContext->VSSetConstantBuffers(0, 1, &m_->m_pConstantBuffer);
m_->m_pDeviceContext->PSSetConstantBuffers(0, 1, &m_->m_pConstantBuffer);
if (m_->m_pDepthStencilStateReadOnly)
m_->m_pDeviceContext->OMSetDepthStencilState(m_->m_pDepthStencilStateReadOnly, 0);
m_->m_pDeviceContext->RSSetState(m_->RSCullClockWise);
if (useSkinned)
{
m_->m_pDeviceContext->IASetInputLayout(m_->m_pInputLayoutSkinned);
m_->m_pDeviceContext->VSSetShader(m_->m_pVertexShaderSkinnedOutline, nullptr, 0);
if (cbBones) m_->m_pDeviceContext->VSSetConstantBuffers(1, 1, &cbBones);
}
else
{
m_->m_pDeviceContext->IASetInputLayout(m_->m_pOutlineInputLayout ? m_->m_pOutlineInputLayout : m_->m_pInputLayout);
m_->m_pDeviceContext->VSSetShader(m_->m_pVertexShaderOutline, nullptr, 0);
ID3D11Buffer* nullCB = nullptr; m_->m_pDeviceContext->VSSetConstantBuffers(1, 1, &nullCB);
}
| Unlit | Lambert | BlinnPhong |
|---|---|---|
| Phong | TextureOnly | ToonShading | ToonShading + outline |
|---|---|---|---|