面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗

最近知识星球的一位同学,面试时被问到:纹理贴图必须要输入顶点坐标或纹理坐标吗?

他一下子被这个问题问蒙了,虽然他知道正确答案是否定的,但是说不上来理由。

这个就引出了文本提到的全屏三角形,它不需要顶点缓冲区,而是利用顶点着色器直接生成所需的顶点坐标和纹理坐标。

这种方法通常用于后处理效果,例如屏幕空间效果(屏幕空间反射、屏幕空间环境光遮蔽等),其中整个屏幕的片段都需要处理。

通过生成全屏三角形,可以避免显式地传递顶点数据,从而简化管线配置。

全屏三角形

全屏三角形实际上是一种讨巧的优化方法,用于渲染全屏四边形或矩形,而不需要使用两个三角形和顶点缓冲区。

通过至少 3 个顶点的索引,在顶点着色器中计算一个覆盖整个屏幕的三角形顶点坐标,可以避免两个三角形之间的接缝问题,并减少顶点处理的开销。

顶点索引 gl_VertexID 是 OpenGL 的内建变量,它在顶点着色器中表示当前顶点的索引。

它不需要显式生成或传递,因为在调用绘制命令(如 glDrawArrays)时,OpenGL 会自动为每个顶点提供该索引。

当你使用 glDrawArrays(GL_TRIANGLES, 0, 3) 来绘制一个包含三个顶点的三角形时,gl_VertexID 会依次被设置为 0、1 和 2

这个索引值可以用来计算每个顶点的位置和其他属性。

全屏三角形的实现细节

gl_VertexID 是 OpenGL ES 中用于标识顶点索引的内建变量,利用它可以在顶点着色器中生成覆盖整个屏幕的三角形。

以下是顶点着色器的详细说明,其中包括对 gl_VertexID 的使用:

#version 300 es                             
out vec2 v_texCoord;                       
void main()                                

    // 根据顶点索引计算纹理坐标
    // gl_VertexID 的值依次为 0, 1, 2                                             
    v_texCoord = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);


    // 将纹理坐标转换为标准化设备坐标 
    // v_texCoord 的值依次为 (0, 0), (2, 0), (0, 2)
    gl_Position = vec4(v_texCoord * 2.0 - 1.0, 0.0, 1.0);

在这个顶点着色器中,gl_VertexID 的值为 0、1、2,这三次调用对应于三角形的三个顶点。下面是每个顶点的计算细节:

顶点 0 (gl_VertexID = 0):

v_texCoord = vec2((0 << 1) & 2, 0 & 2) -> v_texCoord = vec2(0, 0)
gl_Position = vec4(v_texCoord * 2.0 – 1.0, 0.0, 1.0) -> gl_Position = vec4(-1.0, -1.0, 0.0, 1.0)

顶点 1 (gl_VertexID = 1):

v_texCoord = vec2((1 << 1) & 2, 1 & 2) -> v_texCoord = vec2(2, 0)
gl_Position = vec4(v_texCoord * 2.0 – 1.0, 0.0, 1.0) -> gl_Position = vec4(3.0, -1.0, 0.0, 1.0)


顶点 2 (gl_VertexID = 2):

v_texCoord = vec2((2 << 1) & 2, 2 & 2) -> v_texCoord = vec2(0, 2)
gl_Position = vec4(v_texCoord * 2.0 – 1.0, 0.0, 1.0) -> gl_Position = vec4(-1.0, 3.0, 0.0, 1.0)

通过这三个顶点,生成了一个覆盖整个屏幕的三角形。注意,这个三角形覆盖了标准化设备坐标 (NDC) 空间,从左下角 (-1, -1) 到右上角 (1, 1),因此它可以覆盖整个屏幕。

此时生成的顶点坐标:

面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗

此时生成的纹理坐标:

面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗

可以看到这个大的三角形超出了屏幕区域,这个没有问题,渲染的时候将会被裁剪,不会影响性能。

对应的顶点着色器:

#version 300 es                             
precision mediump float;                    
in vec2 v_texCoord;                         
layout(location = 0) out vec4 outColor;     
uniform sampler2D s_Texture;                
void main()                                 
{                                           
  outColor = texture(s_Texture, v_texCoord);
}                                           

渲染代码这个时候变得非常简单了:

    //指定着色器程序
    glUseProgram (m_ProgramObj);

    //激活并绑定纹理 id
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    glUniform1i(m_SamplerLoc, 0);

    glDrawArrays(GL_TRIANGLES, 0, 3);

由于 Android 设备坐标系原点在左上角,OpenGL 纹理坐标系原点在左下角,纹理坐标需要做一下上下镜像:

#version 300 es                            
layout(location = 0) in vec4 a_position;   
layout(location = 1) in vec2 a_texCoord;   
out vec2 v_texCoord;                       
void main()                                
{                                          
   vec2 uv = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);
   v_texCoord = vec2(uv.x, 1.0 - uv.y);//纹理坐标做一下上下镜像(针对 Android 设备)
   gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);

渲染结果:

面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗

— END —

进技术交流群,扫码添加我的微信:Byte-Flow

字节流动

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/49224.html

(0)

相关推荐

发表回复

登录后才能评论