在我的宠物项目中,视频内存开始成为一个问题,因此我研究了各种技术来减少内存占用。我尝试使用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
。
答案 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及以上版本的顶点格式。