OSG與Shader的結合使用


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 紋理貼圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM