如何对齐对象,自动旋转到屏幕,像素网格?

How to make align objects, autorotated to screen, to pixel grid?

我正在尝试显示一个矩形对象,以便它不会改变其在 OpenSceneGraph 中旋转和缩放时的外观。我发现 osg::AutoTransform 应该适合我。

但是使用下面的代码,即使我将纹理过滤器设置为 NEAREST 而不是默认值 LINEAR,它似乎也会给出错误的结果。使用 LINEAR 结果只是模糊,但是使用 NEAREST 它有时会缺少一些纹素线。

#include <osg/Node>
#include <osgViewer/Viewer>
#include <osg/Texture2D>
#include <osg/Geode>
#include <osg/AutoTransform>

osg::ref_ptr<osg::Node> createFixedSizeTexture(osg::Image *image,int W,int H)
{
    osg::Vec3Array& verts = *new osg::Vec3Array(4);
    verts[0] = osg::Vec3(-W/2., -H/2., 0);
    verts[1] = osg::Vec3(+W/2., -H/2., 0);
    verts[2] = osg::Vec3(+W/2., +H/2., 0);
    verts[3] = osg::Vec3(-W/2., +H/2., 0);

    osg::Vec2Array& texcoords = *new osg::Vec2Array(4);
    texcoords[0].set(0,0);
    texcoords[1].set(1,0);
    texcoords[2].set(1,1);
    texcoords[3].set(0,1);

    osg::Geometry*const geometry = new osg::Geometry;
    geometry->setVertexArray( &verts );
    geometry->setTexCoordArray(0, &texcoords);
    geometry->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4));

    osg::Texture2D*const texture = new osg::Texture2D( image );
    texture->setResizeNonPowerOfTwoHint(false);

    geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);

    osg::Geode*const geode = new osg::Geode;
    geode->addDrawable( geometry );

    return geode;
}

int main()
{
    static constexpr int W=21, H=15;
    unsigned bits[W*H];
    for(int x=0;x<W;++x)
        for(int y=0;y<H;++y)
            bits[x+W*y] = (x&y&1)*0xffffffff;

    osg::Image *formImage = new osg::Image;
    formImage->setImage(W, H, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE,
                        reinterpret_cast<unsigned char*>(bits), osg::Image::NO_DELETE);

    osg::AutoTransform *at = new osg::AutoTransform;
    at->setAutoScaleToScreen(true);
    at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN );
    at->addChild(createFixedSizeTexture(formImage,W,H));

    osgViewer::Viewer viewer;
    viewer.setUpViewInWindow(0, 0, 800, 600);
    viewer.setSceneData(at);

    return viewer.run();
}

这是由于最终对象的非整数屏幕坐标。所以要解决这个问题,我必须将对象与像素网格对齐。我怎样才能做到这一点?

可以使用自定义顶点着色器解决未对齐问题,该着色器将屏幕坐标四舍五入为整数。

以下示例基于 OP 中的代码,但它没有使用 osg::AutoTransform,而是在单个着色器中完成了删除旋转和缩放 + 舍入屏幕坐标的全部操作:

#include <osg/Node>
#include <osgViewer/Viewer>
#include <osg/Texture2D>
#include <osg/Geode>

const char*const vertexShader=R"(
    #version 120

    uniform vec2 screenSize;

    vec4 roundCoords(vec4 clipPos)
    {
        vec2 halfScreenSize=screenSize/2.;
        vec2 ndcPos=clipPos.xy/clipPos.w;            // transform to normalized device coordinates
        vec2 screenPos=(ndcPos+1)*halfScreenSize;    // transform from NDC space to screen space
        vec2 screenPosRounded=floor(screenPos);      // round the screen coordinates
        ndcPos=screenPosRounded.xy/halfScreenSize-1; // transform back to NDC space
        clipPos.xy=ndcPos*clipPos.w;                 // transform back to clip space
        return clipPos;
    }

    void main()
    {
        gl_TexCoord[0]=gl_MultiTexCoord0;
        gl_FrontColor=gl_Color;

        vec4 translCol=gl_ModelViewProjectionMatrix[3];
        // Prevent rotation and unneeded scaling
        mat4 mvp=mat4(vec4(2./screenSize.x,  0,       0,0),
                      vec4(      0,   2./screenSize.y,0,0),
                      vec4(      0,         0,        1,0),
                      vec4(translCol.xyz/translCol.w,   1));

        gl_Position=roundCoords(mvp*gl_Vertex);
    }
)";

static constexpr int windowWidth=800, windowHeight=600;

osg::ref_ptr<osg::Node> createFixedSizeTexture(osg::Image *image,int W,int H)
{
    osg::Vec3Array& verts = *new osg::Vec3Array(4);
    verts[0] = osg::Vec3(-W/2., -H/2., 0);
    verts[1] = osg::Vec3(+W/2., -H/2., 0);
    verts[2] = osg::Vec3(+W/2., +H/2., 0);
    verts[3] = osg::Vec3(-W/2., +H/2., 0);

    osg::Vec2Array& texcoords = *new osg::Vec2Array(4);
    texcoords[0].set(0,0);
    texcoords[1].set(1,0);
    texcoords[2].set(1,1);
    texcoords[3].set(0,1);

    osg::Geometry*const geometry = new osg::Geometry;
    geometry->setVertexArray( &verts );
    geometry->setTexCoordArray(0, &texcoords);
    geometry->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4));

    osg::Texture2D*const texture = new osg::Texture2D( image );
    texture->setResizeNonPowerOfTwoHint(false);

    geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);

    osg::Geode*const geode = new osg::Geode;
    geode->addDrawable( geometry );

    osg::Program*const program = new osg::Program;
    program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShader));

    osg::StateSet*const set = geode->getOrCreateStateSet();
    set->setAttributeAndModes(program, osg::StateAttribute::ON);
    set->addUniform(new osg::Uniform("screenSize" , osg::Vec2(windowWidth,windowHeight)));

    return geode;
}

int main()
{
    static constexpr int W=21, H=15;
    unsigned bits[W*H];
    for(int x=0;x<W;++x)
        for(int y=0;y<H;++y)
            bits[x+W*y] = (x&y&1)*0xffffffff;

    osg::Image *formImage = new osg::Image;
    formImage->setImage(W, H, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE,
                        reinterpret_cast<unsigned char*>(bits), osg::Image::NO_DELETE);

    osgViewer::Viewer viewer;
    viewer.setUpViewInWindow(0, 0, windowWidth, windowHeight);
    viewer.setSceneData(createFixedSizeTexture(formImage,W,H));

    return viewer.run();
}