OpenGL マルチビューレンダリング

## マルチビューレンダリング
xR用に両眼描画するとき、モデルの状態は一緒なのでマルチビューレンダリングを使って両目分の絵を同時に描きます
マルチビューレンダリングを利用するために必要な設定を記載します
NVIDIAだとTuring以降のアーキテクチャで有効です


## サンプル
### バーテックスシェーダー
```GLSL
#version 430						
#extension GL_OVR_multiview : require
layout(num_views = 2) in;

#define MVR_VIEWS 2
uniform mat4 projMatrix[MVR_VIEWS];	
uniform mat4 modelMatrix;

in vec4 position;

void main()
{
    int viewID = int(gl_ViewID_OVR);
    gl_Position = projMatrix[viewID] * position;
}
```
GL_OVR_multiview拡張機能をrequireします
"layout(num_views = 2) in"　を記載します


### フラグメントシェーダ
赤色を出力しているだけです。
```GLSL
#version 430
out vec4 fragment;
void main()
{
    fragment = vec4(0.8, 0.0, 0.0, 1.0);
}
```

### Main
```C++
struct MultiViewRendering_FrameBuffer
{
	GLuint m_nDepthBufferId = 0;
	GLuint m_nRenderTextureId = 0;
	GLuint m_nRenderFramebufferId = 0;
	GLuint m_nLogicalFramebufferId = 0;
};

main()
{

/// 省略 ///

    GlShader shader;
    glUseProgram(shader.GetShaderID());

    MultiViewRendering_FrameBuffer multiviewFrameBuffer;
    CreateMultiViewFrameBuffer(multiviewFrameBuffer, nRenderWidth, nRenderHeight);

/// 省略 ///

     while(glfwWindowShouldClose(window.GetGlfwWindowPtr()) == GL_FALSE)
	{
		float a = nRenderWidth / nRenderHeight;
		
		glBindFramebuffer(GL_FRAMEBUFFER, multiviewFrameBuffer.m_nRenderFramebufferId);
		glViewport(0, 0, nRenderWidth, nRenderHeight);

		glClear(GL_COLOR_BUFFER_BIT);

		float proj[2][16];
		StaticFunc::GetProjectionMatrix(-5, 5, a, -5, 5, proj[0]);
		StaticFunc::GetProjectionMatrix(-5, 5, a, -5, 5, proj[1]);
		glUniformMatrix4fv(shader.GetProjectionMatrixLocation(), 2, false, (float*)proj);

		plate.Render(); //3Dモデルを１回だけ描画

		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
		glViewport(0, 0, window.GetWindowWidth(), window.GetWindowHeight());

		glBindFramebuffer(GL_READ_FRAMEBUFFER, multiviewFrameBuffer.m_nLogicalFramebufferId);
		glNamedFramebufferTextureLayer(multiviewFrameBuffer.m_nLogicalFramebufferId, GL_COLOR_ATTACHMENT0, multiviewFrameBuffer.m_nRenderTextureId, 0, 0);
		glBlitFramebuffer(0, 0, nRenderWidth, nRenderHeight, 0, 0, nRenderWidth, nRenderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);

		glNamedFramebufferTextureLayer(multiviewFrameBuffer.m_nLogicalFramebufferId, GL_COLOR_ATTACHMENT0, multiviewFrameBuffer.m_nRenderTextureId, 0, 1);
		glBlitFramebuffer(0, 0, nRenderWidth, nRenderHeight, nRenderWidth, 0, nRenderWidth * 2, nRenderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);

		glfwSwapBuffers(window.GetGlfwWindowPtr());
		glfwWaitEvents();
	}
 
/// 省略 ///

 }
```
```C++
bool CreateMultiViewFrameBuffer(MultiViewRendering_FrameBuffer &multiviewFrameBuffer, unsigned int nRenderWidth, unsigned int nRenderHeight)
{
	const int MAX_VIEWS = 2;
	const int nMultiViewCount = 2;

	glGenFramebuffers(1, &multiviewFrameBuffer.m_nRenderFramebufferId);
	glBindFramebuffer(GL_FRAMEBUFFER, multiviewFrameBuffer.m_nRenderFramebufferId);

	glGenTextures(1, &multiviewFrameBuffer.m_nDepthBufferId);

	glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, multiviewFrameBuffer.m_nDepthBufferId);
	glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 4, GL_DEPTH_COMPONENT24, nRenderWidth, nRenderHeight, MAX_VIEWS, GL_FALSE);
	glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, multiviewFrameBuffer.m_nDepthBufferId, 0, 1, nMultiViewCount);

	glGenTextures(1, &multiviewFrameBuffer.m_nRenderTextureId);

	glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, multiviewFrameBuffer.m_nRenderTextureId);
	glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 4, GL_RGBA8, nRenderWidth, nRenderHeight, MAX_VIEWS, GL_FALSE);

	glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, multiviewFrameBuffer.m_nRenderTextureId, 0, 1, nMultiViewCount);

	// check FBO status
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		return false;
	}

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	glGenFramebuffers(1, &multiviewFrameBuffer.m_nLogicalFramebufferId);

	return true;
}
```
#### void glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
フレームバッファオブジェクトとしてマルチテクスチャオブジェクトを指定する
- target : テクスチャオブジェクトをバインドするフレームバッファオブジェクト
- attachment : フレームバッファのアタッチメントポイント
- texture : テクスチャオブジェクト
- level : テクスチャ画像のミップマップレベル
- baseViewIndex : マルチテクスチャオブジェクトの何番目のバッファに書き込むか
- numViews : マルチレンダリングを行うビューの数

### 結果
”plate.Render”で、平面を１つ描画します。1回の呼び出しで２つの画像が作成されます
![res.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128191/5963eed1-5a8e-60b1-f063-0b9b66ee8eef.png)