我正在寻找/编写一个16位浮点数的C++实现,以便与OpenGL顶点缓冲区(纹理坐标,法线等)一起使用。到目前为止,这是我的要求:

  • 必须为16位(显然)。
  • 必须能够使用GL_HALF_FLOAT上传到OpenGL顶点缓冲区。
  • 必须能够表示超出-1.0-+1.0的数字(否则,我将只使用归一化的GL_SHORT)。
  • 必须能够与正常的32位浮点数进行转换。
  • 算术运算无所谓-我只关心存储。
  • 速度不是主要问题,但正确性才是首要问题。

  • 到目前为止,这是我要使用的界面:
    class half
    {
    public:
        half(void) : data(0) {}
        half(const half& h) : data(h.data) {}
        half(const unsigned short& s) : data(s) {}
        half(const float& f) : data(fromFloat(f)) {}
        half(const double& d) : data(fromDouble(d)) {}
    
        inline operator const float() { return toFloat(data); }
        inline operator const double() { return toDouble(data); }
    
        inline const half operator=(const float& rhs) { data = fromFloat(rhs); return *this; }
        inline const half operator=(const double& rhs) { data = fromDouble(rhs); return *this; }
    
    private:
        unsigned short data;
    
        static unsigned short fromFloat(float f);
        static float toFloat(short h);
    
        inline static unsigned short fromDouble(double d) { return fromFloat((float)d); }
        inline static double toDouble(short h) { return (double)toFloat(h); }
    };
    
    std::ostream& operator<<(std::ostream& os, half h) { os << (float)h; }
    std::istream& operator>>(std::istream& is, half& h) { float f; is >> f; h = f; }
    

    最终,该类的真正关键在于toFloat()fromFloat()函数,这是我需要帮助的。我已经找到了很多16位浮点实现的示例,但是没有一个提到是否可以将它们上传到OpenGL。

    将16位浮点上传到OpenGL时,我应该注意哪些问题?有没有专门解决这些问题的半 float 实现方案?

    编辑:根据大众需求,这是我的顶点数据如何生成,上传和渲染的方式。

    这是在WireCubeEntity类中定义数据的方式:
    VertexHalf vertices[8] = {
            vec3(-1.0f, -1.0f, -1.0f),
            vec3(1.0f, -1.0f, -1.0f),
            vec3(1.0f, 1.0f, -1.0f),
            vec3(-1.0f, 1.0f, -1.0f),
            vec3(-1.0f, -1.0f, 1.0f),
            vec3(1.0f, -1.0f, 1.0f),
            vec3(1.0f, 1.0f, 1.0f),
            vec3(-1.0f, 1.0f, 1.0f)
        };
    
        unsigned char indices[24] = {
            0, 1,
            1, 2,
            2, 3,
            3, 0,
            4, 5,
            5, 6,
            6, 7,
            7, 4,
            0, 4,
            1, 5,
            2, 6,
            3, 7
        };
    
        va.load(GL_LINES, VF_BASICHALF, 8, vertices, GL_UNSIGNED_BYTE, 24, indices);
    

    其中va是VertexArray的实例。 va.load定义为:
    MappedBuffers VertexArray::load(GLenum primitive, VertexFormat vertexFormat, unsigned int vertexCount, void* vertices,
                                                      GLenum indexFormat, unsigned int indexCount, void* indices)
    {
        MappedBuffers ret;
    
        /* Check for invalid primitive types */
        if (primitive > GL_TRIANGLE_FAN)
        {
            error("in VertexFormat::load():\n");
            errormore("Invalid enum '%i' passed to 'primitive'.\n", primitive);
            return ret;
        }
    
        /* Clean up existing data */
        clean();
    
        /* Set up Vertex Array Object */
        glGenVertexArrays(1, &vao);
        bindArray();
    
        /* Create Vertex Buffer Object */
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, vertexSize(vertexFormat) * vertexCount, vertices, GL_STATIC_DRAW);
        if (!vertices) ret.vmap = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
    
        /* Save variables for later usage */
        prim = primitive;
        vformat = vertexFormat;
        vcount = vertexCount;
    
        /* If we've been given index data, handle it */
        if (indexSize(indexFormat) != 0)
        {
            glGenBuffers(1, &ibo);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize(indexFormat) * indexCount, indices, GL_STATIC_DRAW);
            if (!indices) ret.imap = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
    
            iformat = indexFormat;
            icount = indexCount;
        }
    
        /* Handle the vertex format */
        switch (vformat)
        {
        case VF_BASIC:
            /* VF_BASIC only has a position - a 3-component float vector */
            glEnableVertexAttribArray(0);
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
            break;
        case VF_32:
            /* VF_32 has 3 components for position, 2 for texture coordinates, and 3 for a normal.
            Position is at offset 0, TextureCoordinate is at offset 12, and Normal is at offset 20 */
            glEnableVertexAttribArray(0);
            glEnableVertexAttribArray(1);
            glEnableVertexAttribArray(2);
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertexSize(VF_32), (void*)0);
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, vertexSize(VF_32), (void*)12);
            glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, vertexSize(VF_32), (void*)20);
            break;
        case VF_BASICHALF:
            /* VF_BASICHALF is very similar to VF_BASIC, except using half-floats instead of floats. */
            glEnableVertexAttribArray(0);
            glVertexAttribPointer(0, 3, GL_HALF_FLOAT, GL_FALSE, 0, (void*)0);
            break;
        case VF_WITHTANGENTS:
            /* VF_WITHTANGENTS is similar to VF_32, but with additional components for a Tangent. */
            /* Tangent is at offset 32 */
            glEnableVertexAttribArray(0);
            glEnableVertexAttribArray(1);
            glEnableVertexAttribArray(2);
            glEnableVertexAttribArray(3);
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertexSize(VF_WITHTANGENTS), (void*)0);
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, vertexSize(VF_WITHTANGENTS), (void*)12);
            glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, vertexSize(VF_WITHTANGENTS), (void*)20);
            glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, vertexSize(VF_WITHTANGENTS), (void*)32);
            break;
        default:
            error("In VertexFormat::load():\n");
            errormore("Invalid enum '%i' passed to vertexFormat.\n", (int)vformat);
            clean();
            return MappedBuffers();
        }
    
        /* Unbind the vertex array */
        unbindArray();
    
        if (vertices) ready = true;
    
        return ret;
    }
    

    我知道我的功能很繁重。 MappedBuffers只是一个包含2个指针的结构,因此,如果我将NULL数据传递到VertexArray::load(),则可以使用指针将数据直接从文件加载到缓冲区(可能是从另一个线程加载)。 vertexSize是一个函数,它返回我传入的任何一种顶点格式的sizeof(),或者返回0表示无效的格式。

    VertexHalf结构为:
    struct VertexHalf
    {
        VertexHalf(void) {}
        VertexHalf(vec3 _pos) :x(_pos.x), y(_pos.y), z(_pos.z) {}
        VertexHalf(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
    
        half x, y, z, padding;
    };
    

    最后,使用我们之前加载的VertexArray渲染数据:
    void VertexArray::draw(void)
    {
        if (ready == false)
            return;
    
        /* Bind our vertex array */
        bindArray();
    
        /* Draw it's contents */
        if (ibo == 0)
            glDrawArrays(prim, 0, vcount);
        else
            glDrawElements(prim, icount, iformat, NULL);
    
        unbindArray();
    }
    

    最佳答案

    编辑:最明显的错误出现在您的VertexHalf结构中。您有填充元素。但是,当您指定glVertexAttribPointer时,请在跨步中指定一个0,表示该行已紧密打包。因此,您可以更改VertexHalf以删除填充,也可以将glVertexAttribPointer更改为8个字节。

    我将以下类与DirectX一起使用,以支持float16,它可以完美运行。

    Float16.h:

    #ifndef THE__FLOAT_16_H_
    #define THE__FLOAT_16_H_
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    extern short FloatToFloat16( float value );
    extern float Float16ToFloat( short value );
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    class Float16
    {
    protected:
        short mValue;
    public:
        Float16();
        Float16( float value );
        Float16( const Float16& value );
    
        operator float();
        operator float() const;
    
        friend Float16 operator + ( const Float16& val1, const Float16& val2 );
        friend Float16 operator - ( const Float16& val1, const Float16& val2 );
        friend Float16 operator * ( const Float16& val1, const Float16& val2 );
        friend Float16 operator / ( const Float16& val1, const Float16& val2 );
    
        Float16& operator =( const Float16& val );
        Float16& operator +=( const Float16& val );
        Float16& operator -=( const Float16& val );
        Float16& operator *=( const Float16& val );
        Float16& operator /=( const Float16& val );
        Float16& operator -();
    };
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16::Float16()
    {
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16::Float16( float value )
    {
        mValue  = FloatToFloat16( value );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16::Float16( const Float16 &value )
    {
        mValue  = value.mValue;
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16::operator float()
    {
        return Float16ToFloat( mValue );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16::operator float() const
    {
        return Float16ToFloat( mValue );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16& Float16::operator =( const Float16& val )
    {
        mValue  = val.mValue;
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16& Float16::operator +=( const Float16& val )
    {
        *this   = *this + val;
        return *this;
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16& Float16::operator -=( const Float16& val )
    {
        *this   = *this - val;
        return *this;
    
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16& Float16::operator *=( const Float16& val )
    {
        *this   = *this * val;
        return *this;
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16& Float16::operator /=( const Float16& val )
    {
        *this   = *this / val;
        return *this;
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16& Float16::operator -()
    {
        *this   = Float16( -(float)*this );
        return *this;
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    /*+----+                                 Friends                                       +----+*/
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16 operator + ( const Float16& val1, const Float16& val2 )
    {
        return Float16( (float)val1 + (float)val2 );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16 operator - ( const Float16& val1, const Float16& val2 )
    {
        return Float16( (float)val1 - (float)val2 );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16 operator * ( const Float16& val1, const Float16& val2 )
    {
        return Float16( (float)val1 * (float)val2 );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    inline Float16 operator / ( const Float16& val1, const Float16& val2 )
    {
        return Float16( (float)val1 / (float)val2 );
    }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    
    #endif
    

    Float16.cpp:
    #include "Types/Float16.h"
    
    //#include <d3dx9.h>
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    
    short FloatToFloat16( float value )
    {
        short   fltInt16;
        int     fltInt32;
        memcpy( &fltInt32, &value, sizeof( float ) );
        fltInt16    =  ((fltInt32 & 0x7fffffff) >> 13) - (0x38000000 >> 13);
        fltInt16    |= ((fltInt32 & 0x80000000) >> 16);
    
        return fltInt16;
    }
    

    /+ ---- + ---- + ---- + ---- + ---- + ---- + ---- + ---- + ---- + --- -+ ---- + ---- + ---- + ---- + ---- + ---- + ---- + ---- +/
    float Float16ToFloat( short fltInt16 )
    {
        int fltInt32    =  ((fltInt16 & 0x8000) << 16);
        fltInt32        |= ((fltInt16 & 0x7fff) << 13) + 0x38000000;
    
        float fRet;
        memcpy( &fRet, &fltInt32, sizeof( float ) );
        return fRet;
     }
    
    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
    

    08-16 09:21