今回は無限平面の描画エフェクトの実装になります。
以下みたいな実装のことですね。
自分が勝手に思っているだけなんですが、
無限平面の描画というのは、
ゲームプログラミングにおいて必ず通る道みたいなものであり
ただ実装するには必要となる数学も結構必要で
手ごわい中ボスって感じです。
これをこなすことでゲームプログラマーの四天王最弱になれる!!って
感じです。皆さんも是非試してください。
実装するにあたっての資料をネット上から探したところ、
有名どころとして以下の3点がありました。
①[Unity]無限平面を描画する過程でGPUの理解を深める
日本語で図も複数あり、丁寧に説明してあります。
ただ、環境がUnityだったので、デバッグ環境が無く
Unity専用の関数UnityWorldToClipPos()やTRANSFORM_TEXマクロ
なども複数使われており、チャレンジしてみましたが
私の技術が足りず、実装までは至りませんでした。
②【Unity道場 2月】シェーダを書けるプログラマになろう
これも技術的には①と同様。より説明メインで
とても勉強にはなりましたが、これで実装はちょっと無理でした。
③How to make an infinite grid.
英語ですが、説明が最小限でシンプル、1ステップ1ステップ
丁寧に説明されているのでわかりやすいです。
GLSLなのが残念でしたが、私は今回この資料で実装しました。
調べてみると多くの人が参考にしているようで
大人気(?)だと思いました。
以下にフルソースもあるので参考にしてください。
内容も100%同じではなく多少改善しているみたいです。
実際の実装は以下です。
Direct3D11への移植は、特に問題なくストレートに移植出来た様に
思います。
ソース全てのアップは難しいのでエフェクトファイルのみ
載せておきます。
--------------------------------------------
infinite_plane_effect.fx
--------------------------------------------
#include "common_definition.hlsl"
#include "math.hlsl"
//--------------------------------------------------------------------------------------
// Constant buffers
//--------------------------------------------------------------------------------------
cbuffer cb_view_transform
{
float4x4_t g_mView;
float4x4_t g_mProj;
float4x4_t g_m_inv_view;
float4x4_t g_m_inv_proj;
};
static const float3_t g_grid_plane[6] =
{
float3_t(1, 1, 0), float3_t(-1, -1, 0), float3_t(-1, 1, 0),
float3_t(-1, -1, 0), float3_t(1, 1, 0), float3_t(1, -1, 0)
};
static const float_t x_axis_width = 10.0;
static const float_t z_axis_width = 10.0;
static const float_t near_plane = 0.1;
static const float_t far_plane = 100;
static const float_t plane_width_scale_0 = 0.1; // plane四角形のサイズを指定。0.1がデフォルト。0.1→0.05で四角形のサイズが増える。;
static const float_t plane_width_scale_1 = 0.1; // グリッド線の中のテクスチャ個数を指定。0.1がデフォルト。0.1→0.05でテクスチャ個数が増える。;
static const float_t plane_color_intensity = 0.75; // テクスチャ画像の色の濃さを指定。0.75がデフォルト。0.75→0.5→0.25で白→黒;
//--------------------------------------------------------------------------------------
// State
//--------------------------------------------------------------------------------------
RasterizerState g_state_render_solid
{
FillMode = SOLID;
CullMode = NONE;
FrontCounterClockwise = FALSE;
DepthBias = 0;
SlopeScaledDepthBias = 0.0f;
DepthBiasClamp = 0.0f;
DepthClipEnable = FALSE;
ScissorEnable = FALSE;
MultisampleEnable = FALSE;
AntialiasedLineEnable = FALSE;
};
BlendState g_state_no_blend
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = FALSE;
SrcBlend[0] = ONE;
DestBlend[0] = ZERO;
BlendOp[0] = ADD;
SrcBlendAlpha[0] = ONE;
DestBlendAlpha[0] = ZERO;
BlendOpAlpha[0] = ADD;
RenderTargetWriteMask[0] = 0x0f;
BlendEnable[1] = FALSE;
RenderTargetWriteMask[1] = 0x00;
};
BlendState g_state_blend_alphablend_on
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = TRUE;
SrcBlend[0] = SRC_ALPHA;
DestBlend[0] = INV_SRC_ALPHA;
BlendOp[0] = ADD;
SrcBlendAlpha[0] = ONE;
DestBlendAlpha[0] = ONE;
BlendOpAlpha[0] = ADD;
RenderTargetWriteMask[0] = 0x0f;
BlendEnable[1] = FALSE;
RenderTargetWriteMask[1] = 0x00;
};
DepthStencilState g_state_disable_depth
{
DepthEnable = FALSE;
StencilEnable = FALSE;
};
DepthStencilState g_state_enable_depth
{
DepthEnable = TRUE;
DepthWriteMask = ALL;
DepthFunc = LESS;
StencilEnable = FALSE;
};
//--------------------------------------------------------------------------------------
// Textures
//--------------------------------------------------------------------------------------
Texture2D g_tex_decal;
//--------------------------------------------------------------------------------------
// Texture samplers
//--------------------------------------------------------------------------------------
SamplerState g_sam_linear
{
Filter = ANISOTROPIC;
AddressU = WRAP;
AddressV = WRAP;
//AddressU = Border;
//AddressV = Border;
//BorderColor = float4_t(1,1,1,1);
};
struct draw_infinite_grid__VS_input_t
{
float4_t position : POSITION;
uint_t vertex_id : SV_VertexID;
};
struct draw_infinite_grid__VS_output_t
{
float4_t vertex_position_inWVP : SV_POSITION;
float3_t near_point : TEXCOORD0;
float3_t far_point : TEXCOORD1;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
float3_t unproject_point( const in float_t x,
const in float_t y,
const in float_t z,
const in float4x4_t inv_view,
const in float4x4_t inv_proj)
{
const float4x4_t inv_view_inv_proj = mul(inv_proj, inv_view);
const float4_t unprojected_point = mul( float4_t(x, y, z, 1.0), inv_view_inv_proj);
return unprojected_point.xyz / unprojected_point.w;
}
draw_infinite_grid__VS_output_t VS_main( const in draw_infinite_grid__VS_input_t input)
{
draw_infinite_grid__VS_output_t output = (draw_infinite_grid__VS_output_t)0;
float3_t p = g_grid_plane[input.vertex_id].xyz;
output.near_point = unproject_point(p.x, p.y, 0.0, g_m_inv_view, g_m_inv_proj).xyz; // unprojecting on the near plane
output.far_point = unproject_point(p.x, p.y, 1.0, g_m_inv_view, g_m_inv_proj).xyz; // unprojecting on the far plane
output.vertex_position_inWVP = float4_t(p, 1.0);
return output;
}
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4_t grid(const in float3_t fragPos3D, const in float_t scale)
{
const float2_t coord = fragPos3D.xz * scale; // use the scale variable to set the distance between the lines
const float2_t derivative = fwidth(coord);
const float2_t grid = abs( frac(coord - 0.5) - 0.5) / derivative;
const float_t line_ = min(grid.x, grid.y);
const float_t minimumz = min(derivative.y, 1);
const float_t minimumx = min(derivative.x, 1);
float4_t color = float4_t(0.2, 0.2, 0.2, 1.0 - min(line_, 1.0));
// z axis
if(fragPos3D.x > -z_axis_width * minimumx && fragPos3D.x < z_axis_width * minimumx)
color.z = 1.0;
// x axis
if(fragPos3D.z > -z_axis_width * minimumz && fragPos3D.z < z_axis_width * minimumz)
color.x = 1.0;
return color;
}
float_t computeDepth(const in float3_t pos)
{
const float4x4_t view_proj = mul(g_mView, g_mProj);
const float4_t unprojected_point = mul( float4_t(pos.xyz, 1.0), view_proj);
return (unprojected_point.z / unprojected_point.w);
}
float_t computeLinearDepth(const in float3_t pos)
{
const float4x4_t view_proj = mul(g_mView, g_mProj);
const float4_t clip_space_pos = mul( float4_t(pos.xyz, 1.0), view_proj);
float_t clip_space_depth = (clip_space_pos.z / clip_space_pos.w) * 2.0 - 1.0; // put back between -1 and 1
float_t linearDepth = (2.0 * near_plane * far_plane) / (far_plane + near_plane - clip_space_depth * (far_plane - near_plane)); // get linear value between 0.01 and 100
return linearDepth / far_plane; // normalize
}
//[earlydepthstencil]
float4_t PS_main( draw_infinite_grid__VS_output_t input, out float_t depth : SV_Depth) : SV_Target
//float4_t PS_main( draw_infinite_grid__VS_output_t input) : SV_Target
{
//// 「 Step 3 : Draw the plane when it intersects the floor 」の描画
//const float_t t = -input.near_point.y / (input.far_point.y - input.near_point.y);
//return float4_t(1.0, 0.0, 0.0, 1.0 * float(t > 0)); // opacity = 1 when t > 0, opacity = 0 otherwise
//// 「 Step 4 : Draw the actual grid 」+ 「 Step 5 : Output depth 」の描画
//const float_t t = -input.near_point.y / (input.far_point.y - input.near_point.y);
//const float3_t fragPos3D = input.near_point + t * (input.far_point - input.near_point);
//depth = computeDepth(fragPos3D);
//return grid(fragPos3D, plane_width_scale_1) * float(t > 0);
//// 「 Step 4 : Draw the actual grid 」+ 「 Step 5 : Output depth 」+ テクスチャの描画
//const float_t t = -input.near_point.y / (input.far_point.y - input.near_point.y);
//const float3_t fragPos3D = input.near_point + t * (input.far_point - input.near_point);
//const float4_t tex_decal = g_tex_decal.Sample( g_sam_linear, fragPos3D.xz * float2_t(plane_width_scale_0,-plane_width_scale_0));
//depth = computeDepth(fragPos3D);
//return (tex_decal * plane_color_intensity + grid(fragPos3D, plane_width_scale_1)) * float(t > 0);
//「 Step 6 : Fade out the grid 」+ テクスチャの描画
const float_t t = -input.near_point.y / (input.far_point.y - input.near_point.y);
const float3_t fragPos3D = input.near_point + t * (input.far_point - input.near_point);
const float4_t tex_decal = g_tex_decal.Sample( g_sam_linear, fragPos3D.xz * float2_t(plane_width_scale_0,-plane_width_scale_0));
depth = computeDepth(fragPos3D);
float_t linearDepth = computeLinearDepth(fragPos3D);
float_t fading = max(0, (0.5 - linearDepth));
float4_t outColor;
outColor = (tex_decal * plane_color_intensity + grid(fragPos3D, plane_width_scale_1)) * float(t > 0); // adding multiple resolution for the grid
outColor.a *= fading;
return outColor;
}
VertexShader g_VS_main = CompileShader(vs_5_0, VS_main());
PixelShader g_PS_main = CompileShader(ps_5_0, PS_main());
//--------------------------------------------------------------------------------------
// technique
//--------------------------------------------------------------------------------------
technique11 t0
{
pass p0
{
SetVertexShader( g_VS_main);
SetHullShader(NULL);
SetDomainShader(NULL);
SetGeometryShader( NULL);
SetPixelShader( g_PS_main);
//SetBlendState( g_state_no_blend, float4_t(0,0,0,0), 0xFFFFFFFF);
SetBlendState( g_state_blend_alphablend_on, float4_t(0,0,0,0), 0xFFFFFFFF);
SetDepthStencilState( g_state_enable_depth, 0);
SetRasterizerState( g_state_render_solid);
}
}
--------------------------------------------
最後に③ですが、テクスチャ画像の貼り付けが無かったので
その実装を載せておきます。
static const float_t plane_width_scale_0 = 0.1; // plane四角形のサイズを指定。0.1がデフォルト。0.1→0.05で四角形のサイズが増える。;
static const float_t plane_width_scale_1 = 0.1; // グリッド線の中のテクスチャ個数を指定。0.1がデフォルト。0.1→0.05でテクスチャ個数が増える。;
const float4_t tex_decal = g_tex_decal.Sample( g_sam_linear, fragPos3D.xz * float2_t(plane_width_scale_0,-plane_width_scale_0));
return (tex_decal * plane_color_intensity + grid(fragPos3D, plane_width_scale_1)) * float(t > 0);