前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:纹理贴图必须要输入顶点坐标或纹理坐标吗

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

作者头像
字节流动
发布2024-06-18 15:38:27
970
发布2024-06-18 15:38:27
举报
文章被收录于专栏:字节流动字节流动

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

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

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

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

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

全屏三角形

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

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

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

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

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

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

全屏三角形的实现细节

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

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

代码语言:javascript
复制
#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),因此它可以覆盖整个屏幕。

此时生成的顶点坐标:

此时生成的纹理坐标:

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

对应的顶点着色器:

代码语言:javascript
复制
#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);
}                                           

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

代码语言:javascript
复制
    //指定着色器程序
    glUseProgram (m_ProgramObj);

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

    glDrawArrays(GL_TRIANGLES, 0, 3);

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

代码语言:javascript
复制
#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 --

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节流动 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 全屏三角形
  • 全屏三角形的实现细节
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档