如何对齐对象,自动旋转到屏幕,像素网格?
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();
}
我正在尝试显示一个矩形对象,以便它不会改变其在 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();
}