1.OpenGL(3.3)可编程管线
可编程管线可以理解为一系列的三维顶点经过加工变成二维离散的像素点.并且允许在特定的着色阶段自有配置(顶点着色,几何着色,片源着色).
这里主要介绍一下几何着色,几何着色器是在紧挨着顶点着色器的后面.理论上可以将一些原来在顶点着色器中的计算操作转移到几何着色器中进行,但实际操作中并不建议这么做,因为几何着色器并行调用硬件困难,并行度低,效率和顶点着色器有很大的区别.
顶点着色器是逐顶点进行操作,可以进行坐标变换等操作.
片源着色器是逐片源/片段/像素进行的,主要用于最终颜色的计算.
几何着色器是逐图元的操作,输入的是图元,输出的也是图元,(图元是渲染对象在顶点着色器之后,光栅化之前的一种状态。简单的来说,就是包含点【点Point而不是顶点Vertex】或者线段或者三角形的集合。)
2.osg结合vertex shader和fragment shader
#include <osg/Program>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
static const char* vertSource = {
"varying vec3 normal;\n"
"void main()\n"
"{\n"
" normal = normalize(gl_NormalMatrix * gl_Normal);\n"
" gl_Position = ftransform();\n"
"}\n"
};
static const char* fragSource = {
"uniform vec4 color1;\n"
"uniform vec4 color2;\n"
"uniform vec4 color3;\n"
"uniform vec4 color4;\n"
"varying vec3 normal;\n"
"void main()\n"
"{\n"
" float intensity = dot(vec3(gl_LightSource[0].position), normal);\n"
" if (intensity > 0.95) gl_FragColor = color1;\n"
" else if (intensity > 0.5) gl_FragColor = color2;\n"
" else if (intensity > 0.25) gl_FragColor = color3;\n"
" else gl_FragColor = color4;\n"
"}\n"
};
int main( int argc, char** argv )
{
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile( "cow.osg" );
osg::ref_ptr<osg::Shader> vertShader = new osg::Shader( osg::Shader::VERTEX, vertSource );
osg::ref_ptr<osg::Shader> fragShader = new osg::Shader( osg::Shader::FRAGMENT, fragSource );
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader( vertShader.get() );
program->addShader( fragShader.get() );
osg::StateSet* stateset = model->getOrCreateStateSet();
stateset->setAttributeAndModes( program.get() );
stateset->addUniform( new osg::Uniform("color1", osg::Vec4(1.0f, 0.5f, 0.5f, 1.0f)) );
stateset->addUniform( new osg::Uniform("color2", osg::Vec4(0.5f, 0.2f, 0.2f, 1.0f)) );
stateset->addUniform( new osg::Uniform("color3", osg::Vec4(0.2f, 0.1f, 0.1f, 1.0f)) );
stateset->addUniform( new osg::Uniform("color4", osg::Vec4(0.1f, 0.05f, 0.05f, 1.0f)) );
osgViewer::Viewer viewer;
viewer.setSceneData( model.get() );
return viewer.run();
}
3.osg结合geometry shader
geometry shader是在Opengl3.2时加入核心代码库的,如果低版本想要使用它,需要在shader code中声明GL_EXT_geometry_shader4.
geometry shader将会根据vertex shader传递给的图元信息,产生新的邻接图元.geometry shader需要指定一些变量来指定对shader的操作.
GL_GEOMETRY_VERTICES_OUT_EXT :vertex shader发送给geometry shader的顶点数.
GL_GEOMETRY_INPUT_TYPE_EXT :定义传送给geometry shader的图元类型
GL_GEOMETRY_OUTPUT_TYPE_EXT :定义从geometry shader传出的图元类型
program->setParameter( GL_GEOMETRY_VERTICES_OUT_EXT, 100 );
例子
#include <osg/Program>
#include <osg/LineWidth>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
static const char* vertSource = {
"#version 120\n"
"#extension GL_EXT_geometry_shader4 : enable\n"
"void main()\n"
"{ gl_Position = ftransform(); }\n"
};
//贝赛尔曲线方程P(t) = (1-t) 3 *P0 + 3*t*(1-t) 2 *P1 + 3*t 2 *(1-t)*P2 + t 3 *P3
static const char* geomSource = {
"#version 120\n"
"#extension GL_EXT_geometry_shader4 : enable\n"
"uniform int segments;\n"
"void main(void)\n"
"{\n"
" float delta = 1.0 / float(segments);\n"
" vec4 v;\n"
" for ( int i=0; i<=segments; ++i )\n"
" {\n"
" float t = delta * float(i);\n"
" float t2 = t * t;\n"
" float one_minus_t = 1.0 - t;\n"
" float one_minus_t2 = one_minus_t * one_minus_t;\n"
" v = gl_PositionIn[0] * one_minus_t2 * one_minus_t +\n"
" gl_PositionIn[1] * 3.0 * t * one_minus_t2 +\n"
" gl_PositionIn[2] * 3.0 * t2 * one_minus_t +\n"
" gl_PositionIn[3] * t2 * t;\n"
" gl_Position = v;\n"
" EmitVertex();\n"
" }\n"
" EndPrimitive();\n"
"}\n"
};
int main( int argc, char** argv )
{
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) );
vertices->push_back( osg::Vec3(1.0f, 1.0f, 1.0f) );
vertices->push_back( osg::Vec3(2.0f, 1.0f,-1.0f) );
vertices->push_back( osg::Vec3(3.0f, 0.0f, 0.0f) );
osg::ref_ptr<osg::Geometry> controlPoints = new osg::Geometry;
controlPoints->setVertexArray( vertices.get() );
controlPoints->addPrimitiveSet( new osg::DrawArrays(GL_LINES_ADJACENCY_EXT, 0, 4) );
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable( controlPoints.get() );
int segments = 10;
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader( new osg::Shader(osg::Shader::VERTEX, vertSource) );
program->addShader( new osg::Shader(osg::Shader::GEOMETRY, geomSource) );
program->setParameter( GL_GEOMETRY_VERTICES_OUT_EXT, segments+1 );
program->setParameter( GL_GEOMETRY_INPUT_TYPE_EXT, GL_LINES_ADJACENCY_EXT );
program->setParameter( GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_LINE_STRIP );
osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth;
lineWidth->setWidth( 2.0f );
osg::StateSet* stateset = geode->getOrCreateStateSet();
stateset->setAttributeAndModes( program.get() );
stateset->setAttribute( lineWidth.get() );
stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
stateset->addUniform( new osg::Uniform("segments", segments) );
osgViewer::Viewer viewer;
viewer.setSceneData( geode.get() );
return viewer.run();
}