DirectX에서 에지 감지
노멀 맵과 깊이 값을 사용하여 윤곽을 강조하는 방법을 소개합니다.
개요
MRT로 1패스로 보통 렌더링하고, 2패스로 법선 맵, 3패스에서 카메라로부터의 깊이값 맵을 제작합니다.
1패스(일반 렌더링)
2패스(법선 맵)
3패스(심도값 맵)
노멀 맵과 깊이 값 맵을 사용하여 가장자리 추출을 수행합니다.
엣지 추출한 것을 1 패스째에 곱셈 합성합니다.
곱셈 합성에는 DirectX11의 블렌드 스테이트를 사용했습니다.
ID3D11BlendState* m_finalBlendState; //乗算合成用のブレンディングステート。
ID3D11Device* device; //d3d11デバイス
ID3D11DeviceContext* deviceContext //d3d11デバイスコンテキスト
//設定
{
CD3D11_DEFAULT defaultSettings;
//デフォルトセッティングで初期化する。
CD3D11_BLEND_DESC blendDesc(defaultSettings);
//合成用のブレンドステートを作成する。
//乗算合成。
blendDesc.RenderTarget[0].BlendEnable = true;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_COLOR;
device->CreateBlendState(&blendDesc, &m_finalBlendState);
}
//ドロー時
{
//乗算合成用のブレンディングステートを設定する。
float blendFactor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
deviceContext->OMSetBlendState(m_finalBlendState, blendFactor, 0xffffffff);
}
에지 검출
엣지 검출입니다만, 법선 맵, 심도치 맵 모두, 라플라시안 필터(8 방향)라고 하는 것을 사용했습니다.
예를 들어 픽셀
\left(
\begin{matrix}
1 & 0 & 0 \\
1 & 1 & 0 \\
1 & 1 & 1
\end{matrix}
\right)
그리고 있다고 가정합니다. 가운데가 가장자리인지 여부를 결정하려는 픽셀입니다. 각 픽셀에
\left(
\begin{matrix}
1 & 1 & 1 \\
1 & -8 & 1 \\
1 & 1 & 1
\end{matrix}
\right)
를 걸어 더합니다. 그 결과의 절대치가 일정수 이상이면 엣지로 간주합니다.
이 경우,
(1 * 1) * 5 + (0 * 1) * 3 + (1 * (-8))= -3
됩니다. 2보다 크면 엣지로 하고 있었을 경우, 이 픽셀은 엣지라고 하게 됩니다.
셰이더
/*!
*@brief 頂点シェーダーの入力。
*/
struct VSInput {
float4 pos : SV_Position;
float2 uv : TEXCOORD0;
};
/*!
*@brief ピクセルシェーダーへの入力。
*/
struct PS_EdgeInput {
float4 pos : SV_Position;
float2 tex0 : TEXCOORD0;
float4 tex1 : TEXCOORD1;
float4 tex2 : TEXCOORD2;
float4 tex3 : TEXCOORD3;
float4 tex4 : TEXCOORD4;
float4 tex5 : TEXCOORD5;
float4 tex6 : TEXCOORD6;
float4 tex7 : TEXCOORD7;
float4 tex8 : TEXCOORD8;
};
Texture2D<float4> normalTexture : register(t0); //シーンテクスチャ。
Texture2D<float4> depthValueTexture : register(t1); //深度値テクスチャ
sampler Sampler : register(s0); //サンプラー
PS_EdgeInput VSXEdge(VSInput In)
{
float2 texSize;
float level;
//テクスチャーのサイズを取得する
normalTexture.GetDimensions(0, texSize.x, texSize.y, level);
PS_EdgeInput Out;
Out.pos = In.pos;
float2 tex = In.uv;
float offset = 0.2f;
//法線
{
//真ん中のピクセル
Out.tex0 = tex;
//右上のピクセル
Out.tex1.xy = tex + float2(offset / texSize.x, -offset / texSize.y);
//上のピクセル
Out.tex2.xy = tex + float2(0.0f, -offset / texSize.y);
//左上のピクセル
Out.tex3.xy = tex + float2(-offset / texSize.x, -offset / texSize.y);
//右のピクセル
Out.tex4.xy = tex + float2(offset / texSize.x, 0.0f);
//左のピクセル
Out.tex5.xy = tex + float2(-offset / texSize.x, 0.0f);
//右下のピクセル
Out.tex6.xy = tex + float2(offset / texSize.x, offset / texSize.y);
//下のピクセル
Out.tex7.xy = tex + float2(0.0f, offset / texSize.y);
//左下のピクセル
Out.tex8.xy = tex + float2(-offset / texSize.x, offset / texSize.y);
}
//深度値
{
//深度値を取り出すときに使うUV座標
offset = 1.0f;
//右上のピクセル
Out.tex1.zw = tex + float2(offset / texSize.x, -offset / texSize.y);
//上のピクセル
Out.tex2.zw = tex + float2(0.0f, -offset / texSize.y);
//左上のピクセル
Out.tex3.zw = tex + float2(-offset / texSize.x, -offset / texSize.y);
//右のピクセル
Out.tex4.zw = tex + float2(offset / texSize.x, 0.0f);
//左のピクセル
Out.tex5.zw = tex + float2(-offset / texSize.x, 0.0f);
//右下のピクセル
Out.tex6.zw = tex + float2(offset / texSize.x, offset / texSize.y);
//下のピクセル
Out.tex7.zw = tex + float2(0.0f, offset / texSize.y);
//左下のピクセル
Out.tex8.zw = tex + float2(-offset / texSize.x, offset / texSize.y);
}
return Out;
}
float4 PSEdge(PS_EdgeInput In) : SV_Target0
{
//周囲のピクセルの法線の値の平均を計算する。
float3 Normal;
Normal = normalTexture.Sample(Sampler, In.tex0).xyz * -8.0f;
Normal += normalTexture.Sample(Sampler, In.tex1.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex2.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex3.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex4.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex5.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex6.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex7.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex8.xy).xyz;
//周囲のピクセルの深度値の平均を計算する。
float depth2 = depthValueTexture.Sample(Sampler, In.tex1).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex2.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex3.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex4.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex5.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex6.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex7.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex8.zw).x;
depth2 /= 8.0f;
float4 Color;
//法線の計算結果、あるいは深度値の計算結果が一定以上ならエッジとみなす。
if (length(Normal) >= 0.2f || abs(depth2-depth) > 0.001f ) {
Color = float4(0.0f, 0.0f, 0.0f, 1.0f);
}
else {
Color = float4(1.0f, 1.0f, 1.0f, 1.0f);
}
return Color;
}
Reference
이 문제에 관하여(DirectX에서 에지 감지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/akurobit/items/b5231ffae738810b63e7
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
ID3D11BlendState* m_finalBlendState; //乗算合成用のブレンディングステート。
ID3D11Device* device; //d3d11デバイス
ID3D11DeviceContext* deviceContext //d3d11デバイスコンテキスト
//設定
{
CD3D11_DEFAULT defaultSettings;
//デフォルトセッティングで初期化する。
CD3D11_BLEND_DESC blendDesc(defaultSettings);
//合成用のブレンドステートを作成する。
//乗算合成。
blendDesc.RenderTarget[0].BlendEnable = true;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_COLOR;
device->CreateBlendState(&blendDesc, &m_finalBlendState);
}
//ドロー時
{
//乗算合成用のブレンディングステートを設定する。
float blendFactor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
deviceContext->OMSetBlendState(m_finalBlendState, blendFactor, 0xffffffff);
}
엣지 검출입니다만, 법선 맵, 심도치 맵 모두, 라플라시안 필터(8 방향)라고 하는 것을 사용했습니다.
예를 들어 픽셀
\left(
\begin{matrix}
1 & 0 & 0 \\
1 & 1 & 0 \\
1 & 1 & 1
\end{matrix}
\right)
그리고 있다고 가정합니다. 가운데가 가장자리인지 여부를 결정하려는 픽셀입니다. 각 픽셀에
\left(
\begin{matrix}
1 & 1 & 1 \\
1 & -8 & 1 \\
1 & 1 & 1
\end{matrix}
\right)
를 걸어 더합니다. 그 결과의 절대치가 일정수 이상이면 엣지로 간주합니다.
이 경우,
(1 * 1) * 5 + (0 * 1) * 3 + (1 * (-8))= -3
됩니다. 2보다 크면 엣지로 하고 있었을 경우, 이 픽셀은 엣지라고 하게 됩니다.
셰이더
/*!
*@brief 頂点シェーダーの入力。
*/
struct VSInput {
float4 pos : SV_Position;
float2 uv : TEXCOORD0;
};
/*!
*@brief ピクセルシェーダーへの入力。
*/
struct PS_EdgeInput {
float4 pos : SV_Position;
float2 tex0 : TEXCOORD0;
float4 tex1 : TEXCOORD1;
float4 tex2 : TEXCOORD2;
float4 tex3 : TEXCOORD3;
float4 tex4 : TEXCOORD4;
float4 tex5 : TEXCOORD5;
float4 tex6 : TEXCOORD6;
float4 tex7 : TEXCOORD7;
float4 tex8 : TEXCOORD8;
};
Texture2D<float4> normalTexture : register(t0); //シーンテクスチャ。
Texture2D<float4> depthValueTexture : register(t1); //深度値テクスチャ
sampler Sampler : register(s0); //サンプラー
PS_EdgeInput VSXEdge(VSInput In)
{
float2 texSize;
float level;
//テクスチャーのサイズを取得する
normalTexture.GetDimensions(0, texSize.x, texSize.y, level);
PS_EdgeInput Out;
Out.pos = In.pos;
float2 tex = In.uv;
float offset = 0.2f;
//法線
{
//真ん中のピクセル
Out.tex0 = tex;
//右上のピクセル
Out.tex1.xy = tex + float2(offset / texSize.x, -offset / texSize.y);
//上のピクセル
Out.tex2.xy = tex + float2(0.0f, -offset / texSize.y);
//左上のピクセル
Out.tex3.xy = tex + float2(-offset / texSize.x, -offset / texSize.y);
//右のピクセル
Out.tex4.xy = tex + float2(offset / texSize.x, 0.0f);
//左のピクセル
Out.tex5.xy = tex + float2(-offset / texSize.x, 0.0f);
//右下のピクセル
Out.tex6.xy = tex + float2(offset / texSize.x, offset / texSize.y);
//下のピクセル
Out.tex7.xy = tex + float2(0.0f, offset / texSize.y);
//左下のピクセル
Out.tex8.xy = tex + float2(-offset / texSize.x, offset / texSize.y);
}
//深度値
{
//深度値を取り出すときに使うUV座標
offset = 1.0f;
//右上のピクセル
Out.tex1.zw = tex + float2(offset / texSize.x, -offset / texSize.y);
//上のピクセル
Out.tex2.zw = tex + float2(0.0f, -offset / texSize.y);
//左上のピクセル
Out.tex3.zw = tex + float2(-offset / texSize.x, -offset / texSize.y);
//右のピクセル
Out.tex4.zw = tex + float2(offset / texSize.x, 0.0f);
//左のピクセル
Out.tex5.zw = tex + float2(-offset / texSize.x, 0.0f);
//右下のピクセル
Out.tex6.zw = tex + float2(offset / texSize.x, offset / texSize.y);
//下のピクセル
Out.tex7.zw = tex + float2(0.0f, offset / texSize.y);
//左下のピクセル
Out.tex8.zw = tex + float2(-offset / texSize.x, offset / texSize.y);
}
return Out;
}
float4 PSEdge(PS_EdgeInput In) : SV_Target0
{
//周囲のピクセルの法線の値の平均を計算する。
float3 Normal;
Normal = normalTexture.Sample(Sampler, In.tex0).xyz * -8.0f;
Normal += normalTexture.Sample(Sampler, In.tex1.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex2.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex3.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex4.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex5.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex6.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex7.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex8.xy).xyz;
//周囲のピクセルの深度値の平均を計算する。
float depth2 = depthValueTexture.Sample(Sampler, In.tex1).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex2.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex3.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex4.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex5.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex6.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex7.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex8.zw).x;
depth2 /= 8.0f;
float4 Color;
//法線の計算結果、あるいは深度値の計算結果が一定以上ならエッジとみなす。
if (length(Normal) >= 0.2f || abs(depth2-depth) > 0.001f ) {
Color = float4(0.0f, 0.0f, 0.0f, 1.0f);
}
else {
Color = float4(1.0f, 1.0f, 1.0f, 1.0f);
}
return Color;
}
Reference
이 문제에 관하여(DirectX에서 에지 감지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/akurobit/items/b5231ffae738810b63e7
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
/*!
*@brief 頂点シェーダーの入力。
*/
struct VSInput {
float4 pos : SV_Position;
float2 uv : TEXCOORD0;
};
/*!
*@brief ピクセルシェーダーへの入力。
*/
struct PS_EdgeInput {
float4 pos : SV_Position;
float2 tex0 : TEXCOORD0;
float4 tex1 : TEXCOORD1;
float4 tex2 : TEXCOORD2;
float4 tex3 : TEXCOORD3;
float4 tex4 : TEXCOORD4;
float4 tex5 : TEXCOORD5;
float4 tex6 : TEXCOORD6;
float4 tex7 : TEXCOORD7;
float4 tex8 : TEXCOORD8;
};
Texture2D<float4> normalTexture : register(t0); //シーンテクスチャ。
Texture2D<float4> depthValueTexture : register(t1); //深度値テクスチャ
sampler Sampler : register(s0); //サンプラー
PS_EdgeInput VSXEdge(VSInput In)
{
float2 texSize;
float level;
//テクスチャーのサイズを取得する
normalTexture.GetDimensions(0, texSize.x, texSize.y, level);
PS_EdgeInput Out;
Out.pos = In.pos;
float2 tex = In.uv;
float offset = 0.2f;
//法線
{
//真ん中のピクセル
Out.tex0 = tex;
//右上のピクセル
Out.tex1.xy = tex + float2(offset / texSize.x, -offset / texSize.y);
//上のピクセル
Out.tex2.xy = tex + float2(0.0f, -offset / texSize.y);
//左上のピクセル
Out.tex3.xy = tex + float2(-offset / texSize.x, -offset / texSize.y);
//右のピクセル
Out.tex4.xy = tex + float2(offset / texSize.x, 0.0f);
//左のピクセル
Out.tex5.xy = tex + float2(-offset / texSize.x, 0.0f);
//右下のピクセル
Out.tex6.xy = tex + float2(offset / texSize.x, offset / texSize.y);
//下のピクセル
Out.tex7.xy = tex + float2(0.0f, offset / texSize.y);
//左下のピクセル
Out.tex8.xy = tex + float2(-offset / texSize.x, offset / texSize.y);
}
//深度値
{
//深度値を取り出すときに使うUV座標
offset = 1.0f;
//右上のピクセル
Out.tex1.zw = tex + float2(offset / texSize.x, -offset / texSize.y);
//上のピクセル
Out.tex2.zw = tex + float2(0.0f, -offset / texSize.y);
//左上のピクセル
Out.tex3.zw = tex + float2(-offset / texSize.x, -offset / texSize.y);
//右のピクセル
Out.tex4.zw = tex + float2(offset / texSize.x, 0.0f);
//左のピクセル
Out.tex5.zw = tex + float2(-offset / texSize.x, 0.0f);
//右下のピクセル
Out.tex6.zw = tex + float2(offset / texSize.x, offset / texSize.y);
//下のピクセル
Out.tex7.zw = tex + float2(0.0f, offset / texSize.y);
//左下のピクセル
Out.tex8.zw = tex + float2(-offset / texSize.x, offset / texSize.y);
}
return Out;
}
float4 PSEdge(PS_EdgeInput In) : SV_Target0
{
//周囲のピクセルの法線の値の平均を計算する。
float3 Normal;
Normal = normalTexture.Sample(Sampler, In.tex0).xyz * -8.0f;
Normal += normalTexture.Sample(Sampler, In.tex1.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex2.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex3.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex4.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex5.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex6.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex7.xy).xyz;
Normal += normalTexture.Sample(Sampler, In.tex8.xy).xyz;
//周囲のピクセルの深度値の平均を計算する。
float depth2 = depthValueTexture.Sample(Sampler, In.tex1).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex2.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex3.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex4.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex5.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex6.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex7.zw).x;
depth2 += depthValueTexture.Sample(Sampler, In.tex8.zw).x;
depth2 /= 8.0f;
float4 Color;
//法線の計算結果、あるいは深度値の計算結果が一定以上ならエッジとみなす。
if (length(Normal) >= 0.2f || abs(depth2-depth) > 0.001f ) {
Color = float4(0.0f, 0.0f, 0.0f, 1.0f);
}
else {
Color = float4(1.0f, 1.0f, 1.0f, 1.0f);
}
return Color;
}
Reference
이 문제에 관하여(DirectX에서 에지 감지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/akurobit/items/b5231ffae738810b63e7텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)