目录
1. 概述
2. 固定管线着色
3. 纹理着色
4. 参考
1. 概述
以往在 OpenGL 中学习渲染管线的时候, 是依次按照申请数据, 传送缓冲区, 顶点着色器, 片元着色器这几个步骤编程的. OSG 是 OpenGL 的一些顶层的封装, 使用 shader 的时候看不到这些步骤了, 所以有点不习惯. 这里我总结了两个最简单的例子.
2. 固定管线着色
OSG 一个最简单的示例是展示自带的数据 glider.osg:
- #include <iostream>
- #include <Windows.h>
- #include <osgViewer/Viewer>
- #include <osgDB/ReadFile>
- using namespace std;
- int main()
- {
- osg::ref_ptr<osg::Group> root= new osg::Group();
- string osgPath = "D:/Work/OSGBuild/OpenSceneGraph-Data/glider.osg";
- osg::Node * node = osgDB::readNodeFile(osgPath);
- root->addChild(node);
- osgViewer::Viewer viewer;
- viewer.setSceneData(root);
- viewer.setUpViewInWindow(100, 100, 800, 600);
- return viewer.run();
- }
显示的结果是一个简单的滑翔机:
用文本的方式打开 glider.osg 这个数据, 里面记录的是其顶点信息:
这个数据应该是通过固定管线渲染出来的, 那么可以为这个场景加入 Shader:
- #include <iostream>
- #include <Windows.h>
- #include <osgViewer/Viewer>
- #include <osgDB/ReadFile>
- using namespace std;
- // 设置纹理着色
- static void ColorShader(osg::ref_ptr<osg::Node> node)
- {
- const char * vertexShader = {
- "void main(void ){\n"
- "gl_FrontColor = gl_Color;\n"
- "gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;\n"
- "}\n"
- };
- const char * fragShader = {
- "void main(void){\n"
- "gl_FragColor = gl_Color;\n"
- "}\n"
- };
- osg::StateSet * ss = node->getOrCreateStateSet();
- osg::ref_ptr<osg::Program> program = new osg::Program();
- program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragShader));
- program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShader));
- ss->setAttributeAndModes(program, osg::StateAttribute::ON);
- }
- int main()
- {
- osg::ref_ptr<osg::Group> root= new osg::Group();
- string osgPath = "D:/Work/OSGBuild/OpenSceneGraph-Data/glider.osg";
- osg::Node * node = osgDB::readNodeFile(osgPath);
- root->addChild(node);
- ColorShader(node);
- osgViewer::Viewer viewer;
- viewer.setSceneData(root);
- viewer.setUpViewInWindow(100, 100, 800, 600);
- return viewer.run();
- }
这段着色器代码是什么意思呢? 其实很简单, 当使用固定管线的 glColor 函数后, 该颜色值就以作为内置 gl_Color 变量传入顶点着色器, 顶点着色器计算通过 gl_FontColor 和 gl_BackColor 保存正面和反面的值; 而继续传入到片元着色器之后, gl_Color 则会变成一个由 FontColor 和 BackColor 插值计算出来的变量. 最终 gl_FragColor 接受到的就是固定管线渲染得到的值. 运行的结果如下:
最终的结果与之前的结果有所差异, 这是 osgViewer 的默认场景中是有灯光效果的, 可编程管线的渲染效果覆盖了固定管线的效果. 可以在之前固定管线渲染的例子中加入一句代码
root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
去除光照效果, 两者的渲染效果就完全一致了.
3. 纹理着色
另一个例子是通过 OSG 加载一个带纹理的 OSGB 模型:
- #include <iostream>
- #include <Windows.h>
- #include <osgViewer/Viewer>
- #include <osgDB/ReadFile>
- using namespace std;
- int main()
- {
- osg::ref_ptr<osg::Group> root= new osg::Group();
- string osgPath = "D:/Data/scene/Dayanta_OSGB/Data/MultiFoderReader.osgb";
- osg::Node * node = osgDB::readNodeFile(osgPath);
- root->addChild(node);
- osgViewer::Viewer viewer;
- viewer.setSceneData(root);
- viewer.setUpViewInWindow(100, 100, 800, 600);
- return viewer.run();
- }
运行结果会发现某些视角下场景发暗, 这同样也是由于场景中的默认光线造成的:
采取同样的方式, 通过 shader 覆盖固定管线的渲染效果:
- // 设置纹理着色
- static void TextureShader(osg::ref_ptr<osg::Node> node)
- {
- const char * vertexShader = {
- "void main(void ){\n"
- "gl_TexCoord[0] = gl_MultiTexCoord0;\n"
- "gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;\n"
- "}\n"
- };
- const char * fragShader = {
- "uniform sampler2D baseTexture;\n"
- "void main(void){\n"
- "vec2 coord = gl_TexCoord[0].xy;\n"
- "vec4 C = texture2D(baseTexture, coord)\n;"
- "gl_FragColor = C;\n"
- "}\n"
- };
- osg::StateSet * ss = node->getOrCreateStateSet();
- osg::ref_ptr<osg::Program> program = new osg::Program();
- program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragShader));
- program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShader));
- ss->setAttributeAndModes(program, osg::StateAttribute::ON);
- }
- int main()
- {
- osg::ref_ptr<osg::Group> root= new osg::Group();
- string osgPath = "D:/Data/scene/Dayanta_OSGB/Data/MultiFoderReader.osgb";
- osg::Node * node = osgDB::readNodeFile(osgPath);
- root->addChild(node);
- TextureShader(node);
- osgViewer::Viewer viewer;
- viewer.setSceneData(root);
- viewer.setUpViewInWindow(100, 100, 800, 600);
- return viewer.run();
- }
这段 shader 代码也比较简单, 在顶点着色器中, gl_MultiTexCoord0 表示在启用多重纹理时的 0 号纹理单元的坐标顶点, 将其保存在预先定义的纹理坐标 gl_TexCoord[0] 中. gl_TexCoord[0] 经过插值后传入片元着色器, 通过自定义的纹理单元变量 sampler2D baseTexture, 使用 texture2D 函数获取像素值. 最终的渲染效果如下:
4. 参考
[1].GLSL 下几个简单的 Shader
[2].GLSL 纹理贴图
来源: https://www.cnblogs.com/charlee44/p/11427066.html