You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenGL GLSL方块纹理ID值莫名异常的技术求助

问题

近期用C++结合GLFW开发基于OpenGL的体素游戏,已完成区块系统,现需为方块添加纹理。实现逻辑为:向顶点数组中每个方块添加纹理ID(1=圆石,2=泥土等),在着色器中读取该ID以应用对应纹理。

仅使用圆石方块时运行正常,但使用泥土方块时出现异常。

顶点着色器代码

#version 330 core
layout (location = 0) in vec3 aPos ;
layout (location = 1) in vec3 aNormal ;
layout (location = 2) in vec2 aTexCoords ;
layout (location = 3) in float type ;   // 导入纹理ID

uniform mat4 model ;
uniform mat4 view ;
uniform mat4 projection ;

out vec3 Normal ;
out vec3 FragPos ;
out vec2 TexCoords ;
out float BlockType ;

void main(){
    gl_Position = projection * view * model * vec4(aPos, 1.0f) ;
    FragPos = vec3(model * vec4(aPos, 1.0f)) ;

    Normal = aNormal;

    TexCoords = aTexCoords ;

    BlockType = type ;  // 传递到片元着色器

    // 曾尝试传递常量值:
    // BlockType = 2 ;
    // 确认问题不来自顶点数组,但结果无变化,问题出在传递之后
}

片元着色器代码

#version 330 core
#define NR_POINT_LIGHTS 1
#define NB_TEXTURES_TYPE 3
out vec4 FragColor ;

struct Material {
    sampler2D diffuse[NB_TEXTURES_TYPE];    // 存储纹理的采样器数组
    sampler2D specular[NB_TEXTURES_TYPE];

    float shininess;
};

in vec2 TexCoords;
in vec3 FragPos;
in float BlockType ;

uniform Material material;  // 导入纹理的uniform

void main(){
    // 将ID转为整数用于数组索引
    int btype = int(BlockType) ;    

    if(btype > 0){
        btype -= 1 ; // 因为C++中0代表空气,这里数组从0开始,所以ID减1对应索引
    }
    
    FragColor = vec4(vec3(texture(material.diffuse[btype], TexCoords)), 1.0) ;  // 输出纹理颜色

    // 曾尝试直接用常量索引:
    // FragColor = vec4(vec3(texture(material.diffuse[1], TexCoords)), 1.0) ;
    // 此时泥土纹理显示正常
}

怀疑方块纹理ID在顶点着色器传递到片元着色器的过程中莫名改变,请求排查原因。


排查与解决方案

问题核心是float类型的纹理ID在光栅化阶段被插值导致精度丢失:顶点着色器输出的float变量会在片元着色阶段被插值计算,若一个面的不同顶点携带不同纹理ID,片元得到的是插值后的非整数值,转成int后会偏离原始ID,导致纹理索引错误。

方案1:改用整数类型传递纹理ID

直接用整数类型传递ID,OpenGL会禁用插值,保证片元拿到的是原始整数值:

修改后的顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos ;
layout (location = 1) in vec3 aNormal ;
layout (location = 2) in vec2 aTexCoords ;
layout (location = 3) in int type ;   // 改为int类型

uniform mat4 model ;
uniform mat4 view ;
uniform mat4 projection ;

out vec3 Normal ;
out vec3 FragPos ;
out vec2 TexCoords ;
out int BlockType ;  // 输出也改为int类型

void main(){
    gl_Position = projection * view * model * vec4(aPos, 1.0f) ;
    FragPos = vec3(model * vec4(aPos, 1.0f)) ;

    Normal = aNormal;
    TexCoords = aTexCoords ;
    BlockType = type ;  // 直接传递整数ID
}

修改后的片元着色器

#version 330 core
#define NR_POINT_LIGHTS 1
#define NB_TEXTURES_TYPE 3
out vec4 FragColor ;

struct Material {
    sampler2D diffuse[NB_TEXTURES_TYPE];
    sampler2D specular[NB_TEXTURES_TYPE];
    float shininess;
};

in vec2 TexCoords;
in vec3 FragPos;
in int BlockType ;  // 改为int类型输入

uniform Material material;

void main(){
    int btype = BlockType ;    

    if(btype > 0){
        btype -= 1 ; 
    }
    
    FragColor = vec4(vec3(texture(material.diffuse[btype], TexCoords)), 1.0) ;
}

C++端顶点属性配置

使用glVertexAttribIPointer而非glVertexAttribPointer来配置整数类型的顶点属性:

// 假设Vertex结构体中type是int类型
glVertexAttribIPointer(3, 1, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, type));
glEnableVertexAttribArray(3);

方案2:禁用float变量插值(兼容旧逻辑)

如果必须保留float类型,在顶点着色器输出和片元着色器输入时添加flat修饰符,强制禁用插值:

// 顶点着色器
flat out float BlockType;

// 片元着色器
flat in float BlockType;

这样片元会取该面某个顶点的原始ID值,不会产生插值误差,但不如整数类型直观可靠。


内容的提问来源于stack exchange,提问作者Thomas Boishu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:54:23