当前位置: 首页 > 工具软件 > OGLplus > 使用案例 >

oglplus学习笔记 -- Outline

谢叶五
2023-12-01

       最近一直在thinking inmodern C++,大部分都是使用基于对象(object based)的技术。使得对象相较于C++的面向对象风格的程序而言,对象要散列一些,结构更清晰。对象之间适配使用了大量的泛型编程(Generic programming)技术。Boost C++库的发展和C++11语言新特性对模板的支持,优秀的泛型库大量涌现。它们都继承了STL极高的可复用性与很低的学习曲线。Jeremy Ong写的Selene库是笔者目前阅读过的非常精彩的modern C++风格的程序库,但也有一些show off的感觉。还有两个数学库glm与mtl。

      对于图形API的C++ wapper库确实不多,OGLplus算是一个。代码并算不上是非常精彩,诸如没有使用traits/policy编程技法来降低复杂性,使用了decltype关键字这种较为初学的泛型技术,没有考虑空基类优化等细节问题。但也有很多出彩的地方,比如对OpenGL具名对象的封装(参见模板类Named)以及对OpenGL的Operation的封装达到完全消除对OpenGL API函数的裸调用(Raw Call)等。

        OGLplus是一个仅包含头文件(header only)的modern C++风格的库。它主要是对OpenGL的C++包装(Wrapper)。官网上面,它自称是面向对象(Object oriented)的OpenGL外观,但是笔者认为它是基于对象的(Object based)。下面用一段代码来展示OGLplus的Object based风格的使用。

#include <cassert>
#include <iostream>
#include <GL/glew.h>
#include <GL/glut.h>
#include <oglplus/all.hpp>

class Example
{
private:
	oglplus::Context gl;
	oglplus::VertexShader vs;
	oglplus::FragmentShader fs;
	oglplus::Program prog;
	oglplus::Buffer verts;

public:
	Example(void)
	{
		using namespace oglplus;

		vs.Source(" \
			#version 330\n \
			in vec3 Position; \
			void main(void) \
			{ \
				gl_Position = vec4(Position, 1.0); \
			} \
		");

		vs.Compile();

		fs.Source(" \
			#version 330\n \
			out vec4 fragcolor; \
			void main(void) \
			{ \
				fragcolor = vec4(1.0, 0.0, 0.0, 1.0); \
			} \
		");
		fs.Compile();

		prog.AttachShader(vs);
		prog.AttachShaders(fs);
		prog.Link();
		prog.Use();

		GLfloat triangle_verts[9] = {
			0.0f, 0.0f, 0.0f,
			1.0f, 0.0f, 0.0f,
			0.0f, 1.0f, 0.0f
		};
		verts.Bind(Buffer::Target::Array);
		verts.Data(Buffer::Target::Array, 9, triangle_verts);
		VertexAttribArray vert_attr(prog, "Position");
		vert_attr.Setup<GLfloat>(3);
		vert_attr.Enable();

		gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		gl.ClearDepth(1.0f);
	}

	void Display(void)
	{
		using namespace oglplus;
		gl.Clear().ColorBuffer().DepthBuffer();
		gl.Viewport( 0,0, 800, 600 );
		gl.DrawArrays(PrimitiveType::TriangleStrip, 0, 3);
	}

	static Example& GetInstance()
	{
		static Example example;
		return example;
	}
};

static void Display(void)
{
	Example::GetInstance().Display();
	glutSwapBuffers();
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutInitWindowPosition(100,100);
	glutCreateWindow("OGLplus+GLUT+GLEW");

	if(glewInit() == GLEW_OK) try
	{
		glGetError();
		glutDisplayFunc(Display);
		glutMainLoop();
		return 0;
	}
	catch(oglplus::Error& err)
	{
		std::cerr <<
			"Error (in " << err.GLSymbol() << ", " <<
			err.ClassName() << ": '" <<
			err.ObjectDescription() << "'): " <<
			err.what() <<
			" [" << err.File() << ":" << err.Line() << "] ";
		std::cerr << std::endl;
		err.Cleanup();
	}
	return 1;
}

        Example类包裹的是OpenGL标准,这也是OGLplus所做的所有关于OpenGL的事情。main函数与一个glut的渲染回调::Display()都是与平台gl实现相关的代码,这些OGLplus并没有考虑,也算是合理的。虽然OGLplus在OpenGL API外层封装了C++风格的Wrapper,但OGLplus保留了OpenGL的状态机编程风格,使得拥有一定OpenGL编程经验的程序员能够非常快的上手。OGLplus的封装到底做了哪些事情呢?

        OGLplus是类型安全的OpenGL包装。OpenGL管理创建在OpenGL环境(context, 在不同实现中由不同的系统内核对象管理)下的名字对象(如Buffer,Texture,Shader,FrameBuffer等,通常用一个GLuint类型的标识。这种无型别的标识使得程序在运行期出错的几率大大增加。资源在创建时便已知其型别,使用C++型别来管理这些OpenGL资源,应属上策。OpenGL的名字对象由oglplus::Named类统一管理,这也是官网提及的资源自动管理的特性支持的基础组件。OGLplus为我们封装了所有的OpenGL函数调用,并同时封装了调用这些函数的异常处理,并抛出C++异常。OGLplus的封装,是不是让代码变得clean了许多呢?

        最后再来谈谈我觉得这段代码一个非常出彩的地方。就是Example::Display方法中的gl.Clear().ColorBuffer().DepthBuffer()一句。这一句fluent interface风格的代码中的三次函数调用创建了三个oglplus::context::ClrBits的对象,Clear()创建一个空对象为了接下来的调用提供interface,后面依然依次创建ClrBIts型别的对象,并为后面的调用提供接口。创建的对象都是临时对象,离开代码段即进行对象的析构,而在ClrBits型别的析构函数中调用的则是glClear方法。这是使用RAII对象的良好实践。

        好了,Outline先写到这里,modern C++设计的旅程刚刚开始。

 类似资料: