关于shader的概念,可以参见我的另一篇博客《opengl版本发展史及各种概念的厘清》,这里列举一个例子,用来实现一个特效,屏幕的左半部显示为红色。首先我们准备好两个shader程序:
static const char* gl3_VertexShader = {
"#version 330 core\n"
"in vec4 osg_Vertex;\n"
"in vec4 osg_Color;\n"
"in vec4 osg_MultiTexCoord0;\n"
"uniform mat4 osg_ModelViewProjectionMatrix;\n"
"out vec2 texCoord;\n"
"out vec4 vertexColor;\n"
"void main(void)\n"
"{\n"
" gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;\n"
" texCoord = osg_MultiTexCoord0.xy;\n"
" vertexColor = osg_Color; \n"
"}\n"
};
static const char* gl3_FragmentShader = {
"#version 330 core\n"
"uniform sampler2D baseTexture;\n"
"in vec2 texCoord;\n"
"in vec4 vertexColor;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = vertexColor * texture(baseTexture, texCoord);\n"
"}\n"
};
这两段代码是OSG提供的默认的shader功能,从stateset中摘抄过来的。为了实现屏幕的左半部显示为红色的特效,我们重新定义一个fragment shader
static const char* sh1 = {
"#version 330 core\n"
"uniform sampler2D baseTexture;\n"
"in vec2 texCoord;\n"
"in vec4 vertexColor;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = vertexColor * texture(baseTexture, texCoord);\n"
" if(texCoord.x < 0.5){\n"
" color = vec4(1,0,0,1);\n"
" }\n"
"}\n"
};
OSG中与shader相关的有两个类:osg::Program 和 osg::Shader. osg::shader与具体的顶点shader或fragment shader 相关,而Program类封装了shader的编译链接等工作。使用这两个类的方法特别简单
/*set default shaders*/
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader(new osg::Shader(osg::Shader::VERTEX, gl3_VertexShader));
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, sh1));
stateset->setAttribute(program.get(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(0, _texture2D, osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(1, _depthTexture, osg::StateAttribute::ON);
上面的代码段使用了两种shader,vertex shader 与 fragment shader总是成对出现。stateset是某个node的状态集,我门对上篇博客《osg中实现HUD(OSG初级篇1)》的代码进行修改,验证本article的结论,完整代码如下:
static const char* gl3_VertexShader = {
"#version 330 core\n"
"in vec4 osg_Vertex;\n"
"in vec4 osg_Color;\n"
"in vec4 osg_MultiTexCoord0;\n"
"uniform mat4 osg_ModelViewProjectionMatrix;\n"
"out vec2 texCoord;\n"
"out vec4 vertexColor;\n"
"void main(void)\n"
"{\n"
" gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;\n"
" texCoord = osg_MultiTexCoord0.xy;\n"
" vertexColor = osg_Color; \n"
"}\n"
};
static const char* gl3_FragmentShader = {
"#version 330 core\n"
"uniform sampler2D baseTexture;\n"
"in vec2 texCoord;\n"
"in vec4 vertexColor;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = vertexColor * texture(baseTexture, texCoord);\n"
"}\n"
};
static const char* sh1 = {
"#version 330 core\n"
"uniform sampler2D baseTexture;\n"
"in vec2 texCoord;\n"
"in vec4 vertexColor;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = vertexColor * texture(baseTexture, texCoord);\n"
" if(texCoord.x < 0.5){\n"
" color = vec4(1,0,0,1);\n"
" }\n"
"}\n"
};
osg::Geometry* createQuad(int screen_width, int screen_heigh, float z) {
osg::Geometry* polyGeom = new osg::Geometry();
polyGeom->setSupportsDisplayList(false);
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec2Array* texcoords = new osg::Vec2Array;
osg::Vec4Array* colors = new osg::Vec4Array;
/*four vertex*/
vertices->push_back(osg::Vec3d(0, 0, z));
texcoords->push_back(osg::Vec2f(0, 0));
vertices->push_back(osg::Vec3d(screen_width, 0, z));
texcoords->push_back(osg::Vec2f(1, 0));
vertices->push_back(osg::Vec3d(screen_width, screen_heigh, z));
texcoords->push_back(osg::Vec2f(1, 1));
vertices->push_back(osg::Vec3d(0, screen_heigh, z));
texcoords->push_back(osg::Vec2f(0, 1));
/*four vertex*/
colors->push_back(osg::Vec4f(1.0,1.0,1.0,1.0));
colors->setBinding(osg::Array::BIND_OVERALL);
/*set vertex array and tex coord array*/
polyGeom->setVertexArray(vertices);
polyGeom->setTexCoordArray(0, texcoords);
polyGeom->setColorArray(colors);
polyGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN, 0, vertices->size()));
return polyGeom;
}
int
main(int argc, char** argv)
{
// initialize a viewer:
osgViewer::Viewer viewer;
///*create effect*/
viewer.setUpViewAcrossAllScreens();
osgViewer::Viewer::Windows windows;
viewer.getWindows(windows);
int screen_width = windows[0]->getTraits()->width;
int screen_heigh = windows[0]->getTraits()->height;
osg::Camera* camera = new osg::Camera;
camera->setName("Hud Camera");
// set the projection matrix
camera->setProjectionMatrixAsOrtho2D(0, screen_width, 0, screen_heigh);
camera->setViewport(0, 0, screen_width, screen_heigh);
// set the view matrix
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setViewMatrix(osg::Matrix::identity());
camera->setClearColor(osg::Vec4(1, 0.5, 0.5, 0.5));
/*create quad */
osg::Geode* _geode = new osg::Geode();
_geode->setName("GeodeOfHUD");
osg::Geometry* polyGeom = createQuad(screen_width, screen_heigh, -100);
polyGeom->setName("PolyGeomOfHUD_");
_geode->addDrawable(polyGeom);
osg::StateSet* stateset = _geode->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
/*set default shaders*/
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader(new osg::Shader(osg::Shader::VERTEX, gl3_VertexShader));
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, sh1));
stateset->setAttribute(program.get(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
camera->addChild(_geode);
viewer.setSceneData(camera);
int r = viewer.run();
return r;
}