如何将法线打包到GL_INT_2_10_10_10_REV

时间:2016-03-12 17:35:36

标签: opengl

在我的宠物项目中,视频内存开始成为一个问题,因此我研究了各种技术来减少内存占用。我尝试使用GL_INT_2_10_10_10_REV,但是使用我的打包方法获得了光照工件。这些工件似乎不是不准确的结果,因为使用标准化的char[3]short[3]可以完美地工作。由于无用的填充,我宁愿使用更节省空间的GL_INT_2_10_10_10_REV

这是包装代码:

union Vec3IntPacked {
    int i32;
    struct {
        int a:2;
        int z:10;
        int y:10;
        int x:10;
    } i32f3;
};

int vec3_to_i32f3(const Vec3* v) {
    Vec3IntPacked packed;
    packed.i32f3.x = to_int(clamp(v->x, -1.0f, 1.0f) * 511);
    packed.i32f3.y = to_int(clamp(v->y, -1.0f, 1.0f) * 511);
    packed.i32f3.z = to_int(clamp(v->z, -1.0f, 1.0f) * 511);
    return packed.i32;
} // NOTE: to_int is a static_cast

如果我正确地阅读了spec(第10.3.8节,“打包的顶点数据格式”以及2.1和2.2中的转换规则),这应该可行,但事实并非如此。

我还应该注意,上面的代码是在多个操作系统上测试的(虽然所有64位,但int仍然应该是32位)和显卡供应商检查它是否是与驱动程序相关的问题。< / p>

此外,还使用 OpenGL 3.3核心配置文件

顶点结构如下:

struct BasicVertex {
     float position[3];
     unsigned short uv[2];
     int normal;
     int tangent;
     int bitangent;
} // resulting in a 4-byte aligned 28 byte structure

希望我提供了足够的信息,有人可以了解如何将法线正确包装到GL_INT_2_10_10_10_REV

2 个答案:

答案 0 :(得分:8)

您的位域声明中的顺序看起来不正确。根据规范文档(3.3规范中第32页的2.8.2压缩顶点数据格式&#34;第32页),每个组件的位范围为:

x: bits 0-9
y: bits 10-19
z: bits 20-29
w: bits 30-31

经过一些搜索后,看起来像是C标准没有定义位域中的位顺序。参见例如Which end of a bit field is the most significant bit?

我见过的编译器通常使用从最低到最高的位顺序。例如,Microsoft为其编译器定义了这个:

  

位字段在从最低有效位到最高有效位的整数内分配。

如果您依赖于使用此订单的编译器,您的声明应如下所示:

union Vec3IntPacked {
    int i32;
    struct {
        int x:10;
        int y:10;
        int z:10;
        int w:2;
    } i32f3;
};

为保证完全可移植性,您可以使用移位运算符来构建值,而不是使用位域。

根据您在顶点着色器中声明和使用属性的方式,您可能还需要确保将w组件设置为1.当然,如果您不使用{{顶点着色器中的1}}组件,这是不必要的。

答案 1 :(得分:2)

我只是将其留在这里,因为我很难使它工作,并且StackOverflow上没有全面的答案。 Reto Koradi关于字节/位顺序(OpenGL wiki也显示了布局)和使用移位是正确的,但是您仍然需要正确到达那里。 示例代码(以及other questions在此处的StackOverflow上)似乎rely on undefined behaviour,但它对我不起作用。对我有用的(对于OpenGL <= 4.1 为)是

inline uint32_t Pack_INT_2_10_10_10_REV(float x, float y, float z, float w)
{
    const uint32_t xs = x < 0;
    const uint32_t ys = y < 0;
    const uint32_t zs = z < 0;
    const uint32_t ws = w < 0;
    uint32_t vi =
        ws << 31 | ((uint32_t)(w + (ws << 1)) & 1) << 30 |
        zs << 29 | ((uint32_t)(z * 511 + (zs << 9)) & 511) << 20 |
        ys << 19 | ((uint32_t)(y * 511 + (ys << 9)) & 511) << 10 |
        xs << 9  | ((uint32_t)(x * 511 + (xs << 9)) & 511);
    return vi;
}
我发现here

。对于法线,只需省略“ w”部分。如果您有更快/更轻松的方法,我很想知道。要设置属性指针,请确保使用

glVertexAttribPointer(1, 4, GL_INT_2_10_10_10_REV, GL_TRUE, stride, dataPointer);

,正常数据作为vec4到达着色器,映射到[-1,1]。如果不需要w组件,并且OpenGL只会给您xyz,您也可以方便地使用vec3,因此可能根本不需要更改任何着色器代码。一些答案指出您必须使用“ glVertexAttribIPointer”,但这是错误的。
请注意,从OpenGL 4.2开始从浮点数到打包格式was changed的映射,因此转换是不同的,但是比较容易。这也是supported in OpenGL ES 3.0及以上版本的顶点格式。