360视频显示
我们使用经纬图映射方式(ERP,Equirectangular Projection)的视频作为源.
渲染基本步骤:
- 初始化 OpenGL
- 设置球体坐标和纹理坐标
- 设置纹理数据
- 绘制等等
绘制球
由于OpenGL无法绘制出标准的球体.
往往需要将球形细分为三角形面片,当面片数足够多时,看起来就比较接近球形了.
面片的数量太多会影响绘制时间,引起帧率下降,占用内存过大等.
注:
- OpenGL默认使用逆时针绘制图形
void CMainApplication::SetupScene()
{
if (!m_pHMD)
return;
std::vector<float> vertdataarray;
Matrix4 matScale;
matScale.scale(1.0, 1.0, 1.0);
Matrix4 matTransform;
Matrix4 mat = matScale * matTransform;
float radius = 5; // 半径
int pieces = 512; // 面片数量
double step_z = PI / (pieces / 2); // 纵向被划分为多少份
double step_xy = 2 * PI / pieces; // 横向被划分为多少份
double x[4], y[4], z[4], u[4], v[4];
double angle_z = 0.0;
double angle_xy = 0.0;
int i = 0, j = 0;
// 纵向个数
for (i = 0; i < (pieces / 2); i++)
{
angle_z = i * step_z;
// 横向个数
for (j = 0; j < pieces; j++)
{
angle_xy = j * step_xy;
// 右手坐标系
z[0] = radius * sin(angle_z) * cos(angle_xy);
x[0] = -radius * sin(angle_z) * sin(angle_xy);
y[0] = radius * cos(angle_z);
u[0] = j / (float)pieces;
v[0] = i / (float)(pieces / 2);
z[1] = radius * sin(angle_z + step_z) * cos(angle_xy);
x[1] = -radius * sin(angle_z + step_z) * sin(angle_xy);
y[1] = radius * cos(angle_z + step_z);
u[1] = j / (float)pieces;
v[1] = (i + 1) / (float)(pieces / 2);
z[2] = radius*sin(angle_z + step_z)*cos(angle_xy + step_xy);
x[2] = -radius*sin(angle_z + step_z)*sin(angle_xy + step_xy);
y[2] = radius*cos(angle_z + step_z);
u[2] = (j + 1) / (float)pieces;
v[2] = (i + 1) / (float)(pieces / 2);
z[3] = radius * sin(angle_z) * cos(angle_xy + step_xy);
x[3] = -radius * sin(angle_z) * sin(angle_xy + step_xy);
y[3] = radius * cos(angle_z);
u[3] = (j + 1) / (float)pieces;
v[3] = i / (float)(pieces / 2);
// 6个顶点添加到
AddCubeVertex(x[0], y[0], z[0], u[0], v[0], vertdataarray);
AddCubeVertex(x[1], y[1], z[1], u[1], v[1], vertdataarray);
AddCubeVertex(x[2], y[2], z[2], u[2], v[2], vertdataarray);
AddCubeVertex(x[2], y[2], z[2], u[2], v[2], vertdataarray);
AddCubeVertex(x[3], y[3], z[3], u[3], v[3], vertdataarray);
AddCubeVertex(x[0], y[0], z[0], u[0], v[0], vertdataarray);
}
}
m_uiVertcount = vertdataarray.size() / 5; // 每个顶点的数据为 x,y,z,u,v,所以除以5.
// VAO 顶点数组对象
glGenVertexArrays(1, &m_unSceneVAO);
glBindVertexArray(m_unSceneVAO);
// 顶点缓冲区
glGenBuffers(1, &m_glSceneVertBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_glSceneVertBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)* vertdataarray.size(), &vertdataarray[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_glSceneVertBuffer);
GLsizei stride = sizeof(VertexDataScene);
uintptr_t offset = 0;
// 使用 glVertexAttribPointer 来描述缓冲区的布局
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset);
offset += sizeof(Vector3);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (const void *)offset);
glBindVertexArray(0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
纹理贴图
我们直接渲染的是 YUV
数据. 在OpenGL着色器中将YUV转换为RGB.
第一次渲染前先要产生纹理.
且第一次渲染使用 glTexImage2D
.
后面使用 glTexSubImage2D
.
初始化纹理
void CMainApplication::InitializeTextures(){
glGenTextures(3, yuvTextures);
glUseProgram(m_unSceneProgramID);
for (int i = 0; i < 3; i++){
glUniform1i(glGetUniformLocation(m_unSceneProgramID, TEXTURE_UNIFORMS[i]), i);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, yuvTextures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
}
传输纹理数据
if (firstTimeL){
InitializeTextures();
for (int i = 0; i < 3; i++){
int h = (i == 0) ? pixelHeight : pixelHeight / 2;
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, yuvTextures[i]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, yuvStride[i],
h, 0, GL_RED, GL_UNSIGNED_BYTE, yuvPlane[i]);
}
firstTimeL = false;
}
else{
for (int i = 0; i < 3; i++){
int h = (i == 0) ? pixelHeight : pixelHeight / 2;
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, yuvTextures[i]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, yuvStride[i], h, GL_RED, GL_UNSIGNED_BYTE, yuvPlane[i]);
}
}
glGenerateMipmap(GL_TEXTURE_2D);
绘制
glUseProgram(m_unSceneProgramID);
// 传输一些矩阵参数
glUniformMatrix4fv(m_nSceneMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix(nEye).get());
glUniformMatrix4fv(m_nRotateSceneMatrixLocation, 1, GL_FALSE, GetCurrentRotateMatrix().get());=
glBindVertexArray(m_unSceneVAO);
for (int i = 0; i < 3; i++){
// 绑定yuv纹理
glBindTexture(GL_TEXTURE_2D, yuvTextures[i]);
}
// 绘制操作!
glDrawArrays(GL_TRIANGLES, 0, m_uiVertcount);
glBindVertexArray(0);
YUV转RGB着色器
"#version 410 core\n"
"uniform sampler2D y_tex;\n"
"uniform sampler2D u_tex;\n"
"uniform sampler2D v_tex;\n"
"in vec2 v2UVcoords;\n"
"out vec4 outputColor;\n"
"void main() {\n"
" float y = 1.164 *(texture(y_tex, v2UVcoords).r - 0.0625);\n"
" float u = texture(u_tex, v2UVcoords).r - 0.5;\n"
" float v = texture(v_tex, v2UVcoords).r - 0.5;\n"
" outputColor = vec4(y + 1.596*v,y - 0.391*u -0.813 *v,y +2.018 *u,1.0);\n"
"}\n"