ANGLEは、OpenGl ESのDirectX実装です。WindowsのアプリケーションでOpenGL ESの開発する際にとても便利なのです。この記事では、ANGLEを使って開発した際にフルシーンアンチエイリアス(マルチサンプリングアンチエイリアス)を有効にする方法を紹介します。
(2015年11月に試した際の記事です。現在は、こんなに面倒なことを必要は無いかもしれません。)
eglChooseConfigの際に有効にしてみる > 結論、失敗
大前提として、OpenGL ES 2.0はマルチサンプリングをサポートしているわけではないようです。ただし、デバイスのほうが拡張としてサポートしていることがあるようで、例えば、iOS用のアプリではXCodeから設定できます。AndroidもOSの設定でできたような気がします。
こんな感じのことをANGLEでもできると思い、EGLの初期化時にマルチサンプリングを有効にするように、こんな感じでコンフィグを試してみました。
EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint eglMajorVersion;
EGLint eglMinorVersion;
if(eglDisplay == EGL_NO_DISPLAY)
{
return;
}
if(!eglInitialize(eglDisplay, &eglMajorVersion, &eglMinorVersion))
{
return;
}
EGLint attrs[] =
{
EGL_SURFACE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE,16,
EGL_STENCIL_SIZE, 8,
EGL_SAMPLE_BUFFERS, 1, //マルチサンプリングを有効
EGL_SAMPLES, 4, //サンプリング数を指定
EGL_NONE
};
EGLint numConfig = 0;
const EGLint maxConfigs = 10;
EGLConfig eglConfigs[maxConfigs];
EGLBoolean bsuccess = eglChooseConfig(eglDisplay, attrs, eglConfigs, maxConfigs, &numConfig);
ChromeのWebGLでもアンチエイリアスは有効になるため、これでうまくいくだろうと思ったのですが、残念ながら失敗してしまいました。
ANGLEのソースコードを見てみる
eglChooseConfigが失敗を返したので、この辺のソースコードを見てみました。Render11::generateConfigs()内です。おそらく、対応するコンフィグの生成部分かと思います。
//981行目付近
config.sampleBuffers = 0; // FIXME: enumerate multi-sampling
config.samples = 0;
なるほど、対応していないようでした。
強制的にアンチエイリアスを掛けるように書き換える
とりあえず、アンチエイリアスを掛けたいというモチベーションを元に、強制的にアンチエイリアスが掛かるように改造したいと思います。ANGLEはDirectX11レンダラとDirectX9レンダラの2つがありますが、ここではとりあえずDirectX11レンダラの方の改造をします。
SwapChain11の改造
バックバッファ関係を管理しているのはSwapChain11というクラスです。ここで生成されるバックバッファをマルチサンプリングするものに変更すれば、アンチエイリアスが掛かるはずです。libANGLEぷrジェクト内の「libANGLE」-[renderer]-[d3d]-[d3d11]のSwapChain11.hおよびSwapChain11.cppを改造します。
メンバ変数の追加
SwapChain11.hに以下の3つのメンバ変数を追加します。
// SwapChain11クラスの定義内
int sampleNum;
ID3D11Texture2D* pResolveTexture;
ID3D11ShaderResourceView* pRenderTextureSRV;
DXGI_FORMAT resolveFormat;
これらのメンバ変数は、コンストラクタで初期化しておきましょう。
//SwapChain11::SwapChain11定義内
sampleNum = 8; //サンプリング数
pResolveTexture = NULL;
pRenderTextureSRV = NULL;
とりあえず、サンプリング数は8に設定しておきます。
改造箇所
SwapChain11の実装では、resetOffscreenTextureメソッドでデフォルトのバックバッファと深度バッファを生成し、swapRectメソッドで描画された結果を表示するようです。そのため、この2つのメソッドを改造する必要があります。
resetOffscreenTextureメソッドの改造
SwapChain11::resetOffscreenTextureメソッドを改造します。
まず、サンプリング数に対応したqualityを算出します。大体こんな感じです。
UINT quality = 0;
device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, sampleNum, &quality);
quality -= 1;
sampleNumとqualityをテキスチャー生成時に指定するようにします。バックバッファと深度バッファの2箇所を変更しましょう。
offscreenTextureDesc.SampleDesc.Count = sampleNum;
offscreenTextureDesc.SampleDesc.Quality = quality;
depthStencilTextureDesc.SampleDesc.Count = sampleNum;
depthStencilTextureDesc.SampleDesc.Quality = quality;
マルチサンプリングを有効にするためには、D3D11_RENDER_TARGET_VIEW_DESC等のViewDimensionもD3D11_RTV_DIMENSION_TEXTURE2DMSに変更する必要があります。以下の4箇所も書き換えます。
offscreenRTVDesc.ViewDimension = (sampleNum > 1) ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
offscreenSRVDesc.ViewDimension = (sampleNum > 1) ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D;
depthStencilDesc.ViewDimension = (sampleNum > 1) ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilSRVDesc.ViewDimension = (sampleNum > 1) ? D3D11_SRV_DIMENSION_TEXTURE2DMS: D3D11_SRV_DIMENSION_TEXTURE2D;
さて、先程も述べたように、作成したバックバッファの画像は、swapRectメソッド内で描画されて画面に反映されます。しかしながら、マルチサンプリングが有効なテクスチャはそのままシェーダリソースとしては利用できないため、一旦マルチサンプリングを解決したテクスチャに描画する必要があります。そのため、解決用のテクスチャとシェーダーリソースもここで作成します。
mOffscreenTexture作成のコードブロック内(mKeyedMutex = d3d11::DynamicCastComObject<IDXGIKeyedMutex>(mOffscreenTexture);
の後くらい)にこれらの生成用のコードを追加します。解決時にはテクスチャのフォーマットも必要になりますので、ついでにこちらに保存しておきます。
//アンチエイリアスの解決用
if (sampleNum > 1)
{
D3D11_TEXTURE2D_DESC descTex = offscreenTextureDesc;
descTex.SampleDesc.Count = 1;
descTex.SampleDesc.Quality = 0;
device->CreateTexture2D(&descTex, NULL, &pResolveTexture);
D3D11_SHADER_RESOURCE_VIEW_DESC resolveSRVDesc;
resolveSRVDesc.Format = backbufferFormatInfo.srvFormat;
resolveSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
resolveSRVDesc.Texture2D.MostDetailedMip = 0;
resolveSRVDesc.Texture2D.MipLevels = static_cast<UINT>(-1);
device->CreateShaderResourceView(pResolveTexture, &resolveSRVDesc, &pRenderTextureSRV);
resolveFormat = backbufferFormatInfo.texFormat;
}
新しいテクスチャオブジェクトを追加したため、releaseOffscreenTextureメソッド内にこれらを開放するコードを追加しておきます。
SafeRelease(pResolveTexture);
SafeRelease(pRenderTextureSRV);
swapRectメソッドの改造
swapRect内で、レンダリングされた画像を画面に表示しています。その際に、マルチサンプリングテクスチャを解決し、そのテクスチャのシェーダーリソースを設定してやることで、アンチエイリアスのかかった画像が画面に表示されるはずです。シェーダーリソースを設定しているmRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, mOffscreenSRView);
を以下のように変更します。
if (sampleNum > 1)
{
deviceContext->ResolveSubresource(
pResolveTexture, 0, mOffscreenTexture, 0,resolveFormat);
mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, pRenderTextureSRV);
}
else
{
mRenderer->setShaderResource(gl::SAMPLER_PIXEL, 0, mOffscreenSRView);
}
以上でアンチエイリアスの掛かるバックバッファが作成されるはずです。
実行結果
サンプルのhello_triangleを実行してみます。はじめの画像が改造前、あとの画像が改造後の結果です。
ANGLEのビルド方法などは、こちらの記事を御覧ください。
まとめ
ANGLEでマルチサンプリングを有効にするための改造方法を紹介しました。正直、本当にこんなことをしなければならないか怪しいのですが、調べた限り出てこなかったため無理やり改造を行いました。もっとスマートな(もしくは正規な)やり方をご存じの方がいらっしゃれば、お知らせいただけるとありがたいです。