osgearth_shadercomp示例,这是osgEarth着色器合成框架的一组单元测试。主要分析OpenGL着色器语言和方法的使用。是OpenGL与osgearth的结合。通过OpenGL的着色器设置地球的各种颜色状态。
执行命令
// 每一条命令,测试出一种结果
osgearth_shadercompd.exe --test1
osgearth_shadercompd.exe --test2
osgearth_shadercompd.exe --test3
osgearth_shadercompd.exe --test4
osgearth_shadercompd.exe --test5
osgearth_shadercompd.exe --test6
osgearth_shadercompd.exe --test7
osgearth_shadercompd.exe --test8
osgearth_shadercompd.exe --test9
test1:添加雾特效
test2:回调方法测试,当窗口宽度大于1000时,地球是红色,否则原色。
test3:LOD参数,各种调节,好像并没有起作用。
test4:频繁的安装卸载着色器程序,球上的红色一闪一闪的。
test5:两个三角形
test6:左下角4个相机
test7:注入,也就是将绘制的内容写入 out.osg 文件中。
test8:读取 out.osg 文件,并展示。
test9: 红色方块,像瓦片图一样,一级一级的添加。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace osgEarth;
using namespace osgEarth::Util::Controls;
// 根据输入参数不同,执行不同的测试程序,共9个测试程序
int usage( const std::string& msg )
{
OE_NOTICE
<< msg << "\n\n"
<< "USAGE: osgearth_shadercomp \n"
<< " [--test1] : Run the function injection test \n"
<< " [--test2] : Run the accept callback test \n"
<< " [--test3] : Run the shader LOD test \n"
<< " [--test4] : Run the memory test \n"
<< " [--test5] : Run the Program state set test \n"
<< " [--test6] : Run the 2-camera test \n"
<< " [--test7] : Run the geometry shader injection test \n"
<< " [--test8] : Run the VP serialization test \n"
<< " [--test9] : Run the 64-bit shader test \n"
<< std::endl;
return -1;
}
//Utility: 绘制一个三角形节点,作为一个最小单元
osg::Geode* makeGeom( float v )
{
osg::Geode* geode = new osg::Geode();
osg::Geometry* geom = new osg::Geometry();
osg::Vec3Array* verts = new osg::Vec3Array();
verts->push_back( osg::Vec3(v-1, 0, 0) );
verts->push_back( osg::Vec3(v+1, 0, 0) );
verts->push_back( osg::Vec3( v, 0, 2) );
geom->setVertexArray( verts );
geom->setUseDisplayList(false);
geom->setUseVertexBufferObjects(true);
osg::Vec4Array* colors = new osg::Vec4Array(osg::Array::BIND_OVERALL);
colors->push_back( osg::Vec4(0,0,1,1) );
geom->setColorArray(colors);
geom->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES,0,3));
geode->addDrawable(geom);
return geode;
}
//-------------------------------------------------------------------------
// Simple function injection test -- turns the earth gray with a haze.
// 简单的功能注入测试——使大地灰蒙蒙的。
namespace TEST_1
{
// 顶点着色器
char s_hazeVertShader[] =
"#version " GLSL_VERSION_STR "\n"
"out vec3 v_pos; \n"
"void setup_haze(inout vec4 VertexVIEW) \n"
"{ \n"
" v_pos = vec3(VertexVIEW); \n"
"} \n";
// 片段着色器
char s_hazeFragShader[] =
"#version " GLSL_VERSION_STR "\n"
"in vec3 v_pos; \n"
"void apply_haze(inout vec4 color) \n"
"{ \n"
" float dist = clamp( length(v_pos)/1e7, 0.0, 0.75 ); \n"
" color = mix(color, vec4(0.5, 0.5, 0.5, 1.0), dist); \n"
"} \n";
osg::StateAttribute* createHaze()
{
// 着色器程序
osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
vp->setFunction( "setup_haze", s_hazeVertShader, osgEarth::ShaderComp::LOCATION_VERTEX_VIEW );
vp->setFunction( "apply_haze", s_hazeFragShader, osgEarth::ShaderComp::LOCATION_FRAGMENT_LIGHTING );
vp->setShaderLogging(true, "shaders.txt");
return vp;
}
osg::Group* run( osg::Node* earth )
{
osg::Group* g = new osg::Group();
g->addChild( earth );
g->getOrCreateStateSet()->setAttributeAndModes( createHaze(), osg::StateAttribute::ON );
return g;
}
}
//-------------------------------------------------------------------------
// Tests the VirtualProgram's ShaderComp::AcceptCallback
// 测试着色器程序,接收回调方法
namespace TEST_2
{
const char* fragShader =
"#version 110\n"
"void make_it_red(inout vec4 color) {\n"
" color.r = 1.0;\n"
"}\n";
struct Acceptor : public ShaderComp::AcceptCallback
{
// Return true to activate the shader function.
bool operator()(const osg::State& state)
{
osg::Camera* camera = *state.getGraphicsContext()->getCameras().begin();
if (!camera) return false;
osg::Viewport* viewport = camera->getViewport();
if (!viewport) return false;
return viewport->width() > 1000;
}
};
osg::Group* run(osg::Node* node)
{
VirtualProgram* vp = VirtualProgram::getOrCreate(node->getOrCreateStateSet());
vp->setFunction("make_it_red", fragShader, ShaderComp::LOCATION_FRAGMENT_LIGHTING, new Acceptor());
osg::Group* g = new osg::Group();
g->addChild( node );
return g;
}
}
//-------------------------------------------------------------------------
// Tests the VirtualProgram's min/max range for functions (shader LOD)
// 测试着色器程序的LOD方法
namespace TEST_3
{
const char* fragShader =
"#version 110\n"
"void make_it_red(inout vec4 color) {\n"
" color.r = 1.0;\n"
"}\n";
osg::Group* run(osg::Node* node)
{
// 获取椭球的赤道半径
float radius = osgEarth::SpatialReference::get("wgs84")->getEllipsoid()->getRadiusEquator();
// 着色器程序
VirtualProgram* vp = VirtualProgram::getOrCreate(node->getOrCreateStateSet());
// Install the shader function:
vp->setFunction("make_it_red", fragShader, ShaderComp::LOCATION_FRAGMENT_LIGHTING);
// Set a maximum LOD range for the above function:设置LOD范围
vp->setFunctionMinRange( "make_it_red", 5000000 );// 50万米
vp->setFunctionMaxRange( "make_it_red", 10000000 );// 100万米
osg::Group* g = new osg::Group();
// Install a callback that will convey the LOD range to the shader LOD.
g->addCullCallback( new RangeUniformCullCallback() );
g->addChild( node );
return g;
}
}
//-------------------------------------------------------------------
// Tests memory management by installing and uninstalling shader
// functions every frame.
// 测试缓存功能。通过每隔一帧进行安装/卸载
namespace TEST_4
{
const char* fragShader =
"#version 110\n"
"void make_it_red(inout vec4 color) {\n"
" color.r *= 1.5;\n"
"}\n";
struct Acceptor : public ShaderComp::AcceptCallback
{
osg::ref_ptr _a;
Acceptor()
{
_a = new osg::Vec4Array(1024000);
}
bool operator()(const osg::State& state)
{
return true;
}
};
struct MyGroup : public osg::Group
{
bool _toggle;
MyGroup() : _toggle(false)
{
this->setNumChildrenRequiringUpdateTraversal(1);
}
void traverse(osg::NodeVisitor& nv)
{
if (nv.getVisitorType() == nv.UPDATE_VISITOR)
{
if ( (nv.getFrameStamp()->getFrameNumber() % 4) == 0 )// 通过修改4,可以调整频率
{
_toggle = !_toggle;
VirtualProgram* vp = VirtualProgram::getOrCreate(this->getOrCreateStateSet());
if ( _toggle )
{
vp->setFunction(
"make_it_red", fragShader,
osgEarth::ShaderComp::LOCATION_FRAGMENT_COLORING,
new Acceptor() );
}
else
{
vp->removeShader("make_it_red");// 移除
}
}
}
osg::Group::traverse(nv);
}
};
osg::Group* run(osg::Node* node)
{
MyGroup* g = new MyGroup();
g->addChild( node );
return g;
}
}
//-------------------------------------------------------------------------
// 运行程序状态集测试。
namespace TEST_5
{
char s_vert[] =
"#version " GLSL_VERSION_STR "\n"
"void main() { \n"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n"
"} \n";
char s_frag[] =
"#version " GLSL_VERSION_STR "\n"
"void main() { \n"
" gl_FragColor = vec4(1.0,0.0,0.0,1.0); \n"
"} \n";
char s_vp[] =
"#version " GLSL_VERSION_STR "\n"
"void test( inout vec4 color ) { color = vec4(1.0,0.0,0.0,1.0); } \n";
osg::Group* run()
{
osg::Node* n1 = makeGeom( -5 );
osg::Node* n2 = makeGeom( 5 );
VirtualProgram* vp = new VirtualProgram();
vp->setFunction("test", s_vp, ShaderComp::LOCATION_FRAGMENT_LIGHTING);
n1->getOrCreateStateSet()->setAttributeAndModes( vp, 1 );
osg::Group* root = new osg::Group();
root->getOrCreateStateSet()->setRenderBinDetails( 0, "TraversalOrderBin" );
GLUtils::setLighting(root->getOrCreateStateSet(), 0);
root->addChild( n1 );// 仅对n1节点做
root->addChild( n2 );
return root;
}
}
//-------------------------------------------------------------------------
// Tests the VirtualProgram's ShaderComp::AcceptCallback with multiple cameras
// in order to vertify that the State Stack Memory is being properly disabled
// when Accept Callbacks are in play.
// 测试着色器程序,接收多相机回调,
// 来证明 当Accept Callbacks正在播放时,状态堆栈内存是否被正确禁用。
namespace TEST_6
{
const char* fragShader =
"#version 110\n"
"void make_it_red(inout vec4 color) {\n"
" color.r = 1.0;\n"
"}\n";
const char* fragShader2 =
"#version 110\n"
"void make_it_blue(inout vec4 color) {\n"
" color.b = 1.0;\n"
"}\n";
// This acceptor will only include the fragment shader snippet above
// when the camera's viewport.x == 0. Normally the Program will only
// be applied once in succession.
struct Acceptor : public ShaderComp::AcceptCallback
{
Acceptor() : _fn(0) { }
// Return true to activate the shader function.
bool operator()(const osg::State& state)
{
const osg::Viewport* vp = state.getCurrentViewport();
return vp && vp->x() == 0.0;
}
unsigned _fn;
};
// 两个着色器,每个着色器渲染的场景,各设置两个相机,所以共4个相机
osg::Group* run(osg::Node* node)
{
osg::Group* group1 = new osg::Group();
VirtualProgram* vp1 = VirtualProgram::getOrCreate(group1->getOrCreateStateSet());
vp1->setFunction("make_it_red", fragShader, ShaderComp::LOCATION_FRAGMENT_LIGHTING, new Acceptor());
vp1->setAcceptCallbacksVaryPerFrame(true);
group1->addChild( node );
// osg相机的坐标原点在 viewer窗口的左下角
// 将所有相机的宽都比高大20,且cam1与2在底层,cam3和4在上层
osg::Camera* cam1 = new osg::Camera();
cam1->setViewport(0, 0, 220, 200);
cam1->addChild( group1 );
osg::Camera* cam2 = new osg::Camera();
cam2->setViewport(221, 0, 220, 200);
cam2->addChild( group1 );
osg::Group* group2 = new osg::Group();
VirtualProgram* vp2 = VirtualProgram::getOrCreate(group2->getOrCreateStateSet());
vp2->setFunction("make_it_blue", fragShader2, ShaderComp::LOCATION_FRAGMENT_LIGHTING);
group2->addChild( node );
osg::Camera* cam3 = new osg::Camera();
cam3->setViewport(0, 221, 220, 200);
cam3->addChild( group2 );
osg::Camera* cam4 = new osg::Camera();
cam4->setViewport(221, 221, 220, 200);
cam4->addChild( group2 );
osg::Group* g = new osg::Group();
g->addChild( cam1 );
g->addChild( cam2 );
g->addChild( cam3 );
g->addChild( cam4 );
return g;
}
}
//-------------------------------------------------------------------------
// 运行几何着色器注入测试。
namespace TEST_7
{
const char* vert =
"#version 120\n"
"out float oe_red; \n"
"void myVertShader(inout vec4 vertex) { \n"
" oe_red = 1.0; \n"
"} \n";
const char* geom =
"#version 330\n"
"#pragma vp_name ShaderComp Test 7 Geom Shader (Triangle Viewer)\n"
"layout(triangles) in; \n"
"layout(triangle_strip) out; \n"
"layout(max_vertices = 3) out; \n"
"void VP_LoadVertex(in int); \n"
"void VP_EmitVertex(); \n"
"uniform float osg_FrameTime; \n"
"void myGeomShader() \n"
"{ \n"
" float strength = 0.25 + sin(osg_FrameTime*2.0)*0.25; \n"
" vec4 cen = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position)/3.0; \n"
" for(int i=0; i < 3; ++i ) \n"
" { \n"
" VP_LoadVertex(i); \n"
" vec4 pos = gl_in[i].gl_Position; \n"
" pos += vec4(normalize(cen.xyz-pos.xyz) * distance(cen, pos) * strength, 0.0); \n"
" gl_Position = pos; \n"
" VP_EmitVertex(); \n"
" } \n"
" EndPrimitive(); \n"
"} \n";
const char* frag =
"#version 120\n"
"in float oe_red; \n"
"void myFragShader(inout vec4 color) \n"
"{ \n"
" // nop\n"
"} \n";
// 创建着色器程序
osg::StateAttribute* createVP()
{
osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
vp->setFunction( "myVertShader", vert, osgEarth::ShaderComp::LOCATION_VERTEX_MODEL );
vp->setFunction( "myGeomShader", geom, osgEarth::ShaderComp::LOCATION_GEOMETRY );
vp->setFunction( "myFragShader", frag, osgEarth::ShaderComp::LOCATION_FRAGMENT_COLORING );
vp->setShaderLogging(true, "test7.glsl");// 输出此文件
return vp;
}
osg::Group* run( osg::Node* earth )
{
osg::Group* g = new osg::Group();
g->addChild( earth );
g->getOrCreateStateSet()->setAttribute( createVP() );
g->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK));
return g;
}
}
//-------------------------------------------------------------------------
// Serialization test.序列化测试
namespace TEST_8
{
osg::Node* run()
{
// 将第五个测试生成的对象,保存,然后再读出展示
osg::ref_ptr node = TEST_5::run();
osgDB::writeNodeFile(*node, "out.osgt");
OE_NOTICE << "Wrote to out.osgt" << std::endl;
node = 0L;
node = osgDB::readRefNodeFile("out.osgt");
if (!node.valid()) {
OE_WARN << "Readback failed!!" << std::endl;
exit(0);
}
return node.release();
}
}
//-------------------------------------------------------------------------
// Double-precision GLSL Test.
//
// This test will turn terrain verts RED if their distance from the earth's
// center exceeds a certain number. Zoom down to a region where there is a
// boundary between red verts and normally-colored verts. If you use the 32-bit
// version of the vertex shader, small movements in the camera will cause
// red triangles to jump in and out as the world vertex position overflows
// the 32-bit values. If you use the 64-bit version, this no longer happens.
//
// For the 64-bit version, the OSG built-in uniform osg_ViewMatrixInverse is
// insufficient (since it's only single-precision). So we have to install a
// 64-bit version using a cull callback.
// 运行64位着色器测试
namespace TEST_9
{
osg::Node* run(osg::Node* earthfile)
{
// 32-bit vertex shader, for reference only. This shader will exceed
// the single-precision capacity and cause "jumping verts" at the
// camera make small movements.
const char* vs32 =
"#version 330 \n"
"uniform mat4 osg_ViewMatrixInverse; \n"
"flat out float isRed; \n"
"void vertex(inout vec4 v32) \n"
"{ \n"
" vec4 world = osg_ViewMatrixInverse * v32; \n"
" world /= world.w; \n"
" float len = length(world); \n"
" const float R = 6371234.5678; \n"
" isRed = 0.0; \n"
" if (len > R) \n"
" isRed = 1.0;"
"}\n";
// 64-bit vertex shader. This shader uses a double-precision inverse
// view matrix and calculates the altitude all in double precision;
// therefore the "jumping verts" problem in the 32-bit version is
// resolved. (Mostly-- you will still see the jumping if you view the
// earth from orbit, because the 32-bit vertex itself is very far from
// the camera in view coordinates. If that is an issue, you need to pass
// in 64-bit vertex attributes.)
const char* vs64 =
"#version 330 \n"
"#extension GL_ARB_gpu_shader_fp64 : enable \n"
"uniform dmat4 u_ViewMatrixInverse64; \n" // must use a 64-bit VMI.
"flat out float isRed; \n"
"flat out double vary64; \n" // just to test shadercomp framework
"void vertex(inout vec4 v32) \n"
"{ \n"
" dvec4 v64 = dvec4(v32); \n" // upcast to 64-bit, no precision loss
// unless camera is very far away
" dvec4 world = u_ViewMatrixInverse64 * v64; \n" // xform into world coords
" world /= world.w; \n" // divide by w
" double len = length(world.xyz); \n" // get double-precision vector length.
" const double R = 6371234.5678; \n" // arbitrary earth radius threshold
" isRed = (len > R) ? 1.0 : 0.0; \n"
"}\n";
// frag shader: color the terrain red if the incoming varying is non-zero.
const char* fs =
"#version 330 \n"
"#extension GL_ARB_gpu_shader_fp64 : enable \n"
"flat in float isRed; \n"
"flat in double vary64; \n"
"void fragment(inout vec4 color) \n"
"{ \n"
" if (isRed > 0.0f) { \n"
" color.r = 1.0; \n"
" color.gb *= 0.5; \n"
" } \n"
"} \n";
// installs a double-precision inverse view matrix for our shader to use.
struct VMI64Callback : public osg::NodeCallback
{
void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = dynamic_cast(nv);
osg::Uniform* u = new osg::Uniform(osg::Uniform::DOUBLE_MAT4, "u_ViewMatrixInverse64");
u->set(cv->getCurrentCamera()->getInverseViewMatrix());
osg::ref_ptr ss = new osg::StateSet();
ss->addUniform(u);
cv->pushStateSet(ss.get());
traverse(node, nv);
cv->popStateSet();
}
};
earthfile->setCullCallback(new VMI64Callback());
osg::StateSet* ss = earthfile->getOrCreateStateSet();
VirtualProgram* vp = VirtualProgram::getOrCreate(ss);
vp->setFunction("vertex", vs64, ShaderComp::LOCATION_VERTEX_VIEW);
vp->setFunction("fragment", fs, ShaderComp::LOCATION_FRAGMENT_COLORING);
return earthfile;
}
}
//-------------------------------------------------------------------------
int main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc,argv);
osgViewer::Viewer viewer(arguments);
bool test1 = arguments.read("--test1");
bool test2 = arguments.read("--test2");
bool test3 = arguments.read("--test3");
bool test4 = arguments.read("--test4");
bool test5 = arguments.read("--test5");
bool test6 = arguments.read("--test6");
bool test7 = arguments.read("--test7");
bool test8 = arguments.read("--test8");
bool test9 = arguments.read("--test9");
bool ok = test1 || test2 || test3 || test4 || test5 || test6 || test7 || test8||test9;
bool ui = !arguments.read("--noui");// 此参数不需要输入
if ( !ok )
{
return usage("");
}
osg::Group* root = new osg::Group();
viewer.setSceneData( root );
LabelControl* label = new LabelControl();
if ( ui )
{
// add a canvas: 添加画布
ControlCanvas* canvas = new ControlCanvas();
root->addChild( canvas );
// and a label: 添加标签
canvas->addControl(label);
}
// 1 2 3 4 6 需要earth文件
if ( test1 || test2 || test3 || test4 || test6 )
{
osg::ref_ptr earthNode = osgDB::readRefNodeFile( "F:\\osgData\\Data\\earth_image\\china-simple.earth" );
if (!earthNode.valid())
{
return usage( "Unable to load earth model." );
}
if ( test1 )
{
root->addChild( TEST_1::run(earthNode.get()) );
if (ui) label->setText( "Function injection test: the map appears hazy at high altitude." );
}
else if ( test2 )
{
root->addChild( TEST_2::run(earthNode.get()) );
if (ui) label->setText( "Accept callback test: the map turns red when viewport width > 1000" );
}
else if ( test3 )
{
root->addChild( TEST_3::run(earthNode.get()) );
if (ui) label->setText( "Shader LOD test: the map turns red between 500K and 1M meters altitude" );
}
else if ( test4 )
{
root->addChild( TEST_4::run(earthNode.get()) );
if (ui) label->setText("Memory management test; monitor memory for stability");
}
else if ( test6 )
{
root->addChild( TEST_6::run(earthNode.get()) );
if (ui) label->setText("State Memory Stack test; top row, both=blue. bottom left=red, bottom right=normal.");
}
viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
}
else if ( test5 )
{
osgEarth::Registry::instance()->getCapabilities();
root->addChild( TEST_5::run() );
if (ui) label->setText( "Leakage test: red tri on the left, blue on the right." );
}
else if ( test7 )
{
root->addChild( TEST_7::run( osgDB::readNodeFiles(arguments) ) );
if (ui) label->setText("Geometry Shader Injection Test.");
}
else if (test8)
{
root->addChild( TEST_8::run() );
if (ui) label->setText("Serialization test");
}
else if (test9)
{
osg::ref_ptr earthNode = osgDB::readRefNodeFile( "F:\\osgData\\Data\\earth_image\\china-simple.earth" );
if (!earthNode.valid())
{
return usage( "Unable to load earth model." );
}
root->addChild(TEST_9::run(earthNode.get()));
if (ui) label->setText("DP Shader Test - see code comments");
viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
}
// add some stock OSG handlers:
viewer.addEventHandler(new osgViewer::StatsHandler());
viewer.addEventHandler(new osgViewer::WindowSizeHandler());
viewer.addEventHandler(new osgViewer::ThreadingHandler());
viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
return viewer.run();
}