项目记录 : 360视频显示

360视频显示

我们使用经纬图映射方式(ERP,Equirectangular Projection)的视频作为源.

渲染基本步骤:

  1. 初始化 OpenGL
  2. 设置球体坐标和纹理坐标
  3. 设置纹理数据
  4. 绘制等等

绘制球

由于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"

参考文献

绘制球体

360视频显示原理

球坐标系

猜你喜欢

转载自blog.csdn.net/qjh5606/article/details/86686698
今日推荐