对渲染相关操作封装的类库
开始准备做毕设了,利用周末的时间整理了一下图形学渲染相关的一些基础代码,尤其是opengl相关的,封装完后,如下只需简单几行就可以构建一个opengl框架。
#include <common/glfwApp.h> #include <openglWrappers/DemoMeshes/BlinnPhongMesh.h> //glfw setup auto application = redips::glfw::getInstance(720, 720); //standard pinhole camera redips::PhC phc(60, 1.0f, 1.0f, 10000); //load a obj and then wrap into a glMesh。 redips::BlinnPhongMesh mesh(new redips::Triangles("E:/Documents/models/maze_with_dragon.obj")); void movement(){ if (application->keydown(GLFW_KEY_F)) application->showFps(false); } //need register a display-callback function to tell glfwApp what to render void display(){ mesh.uniformFloat3("lightColor", redips::float3(1, 1, 1)); mesh.uniformFloat3("lightPos", phc.pos()); mesh.uniformFloat3("cameraPos", phc.pos()); mesh.uniformMat44f("projection", phc.glProjection().ptr()); mesh.uniformMat44f("view", phc.glView().ptr()); mesh.uniformMat44f("model", redips::Mat44f::eye().ptr()); mesh.draw(); movement(); } void initialize(){ //mesh center redips::float3 heart = mesh.model_ptr()->aabb_T().heart(); //set up camera phc.lookAt(heart + redips::float3(0, 0, 200), heart, redips::float3(0, 1, 0)); } int main(){ initialize(); application->registerDisplayCallback(display); application->bindCamera(&phc); application->loop(); return 0; }
简单介绍一下结构
1.依赖
需要FreeImage读/写图片,及glfw/glew。github链接里包含了所有代码及依赖的库文件。
不需要glm,assimp。
2. 环境搭建
a. 以VS2013为例,设置redips及GLEW/GLFW的头文件目录,库目录以及dll目录
X64 release #include E:\Documents\CG\CGLib\redips E:\Documents\CG\CGLib\redips\Dependencies\GLEW_GLFW_X64\include #libs E:\Documents\CG\CGLib\redips\Dependencies\FreeImage\x64 E:\Documents\CG\CGLib\redips\Dependencies\GLEW_GLFW_X64\libs\glew_x64 E:\Documents\CG\CGLib\redips\Dependencies\GLEW_GLFW_X64\libs\glfw_64_lib-vc2013 FreeImage.lib glew32.lib opengl32.lib glfw3.lib #dlls path=E:\Documents\CG\CGLib\redips\Dependencies\FreeImage\x64;E:\Documents\CG\CGLib\redips\Dependencies\GLEW_GLFW_X64\bin\x64;E:\Documents\CG\CGLib\redips\Dependencies\GLEW_GLFW_X64\libs\glfw_64_lib-vc2013
b. 因为OpenglWrappers\DemoMeshes\下面提供了BlinnPhongMesh,gBufferMesh,PhongMesh三个mesh,每个mesh都提供了默认的shader文件,所以在Common\constant.h里面定义了一个宏:_REDIPS_ROOT_PATH_。需要把其改为解压redips的根目录。
3. 类库
Cameras里面包括基类Camera以及派生类PhC,GLC,MPC,常用PhC(小孔相机)实现了getRay(uint x,uint y)[for raytracing]; glProjection()[for rasterization]等常用函数。
Common比较重要。里面包含了
- constant.h => 包含一些常量、宏、枚举类的定义。
- fImage.h => 对FreeImage的封装。可以方便的读、写图片。
- glfwApp.h => 对GLFW的封装。仅需一句 auto application = redips::glfw::getInstance(); 就可以创建好一个GLFW环境。
- rayTracer.h => 实现了一个简单的Ray Tracer。渲染一副图片并保存。
- utils.h => 实现了一些工具类,如StringUtil类实现了部分对字符串的操作。
- vec.h => Vec2/Vec3/Vec4/Mat33/Mat44的常用操作,遵循 column-major。
Geometry里包含了几何相关的类。
- geometry.h => 定义了简单常用的数据结构,如BOX,Light,Ray,HitRecord,以及一个工具类GeoUtil,提供如求重心坐标、三角形面积等工具函数
- kdtree.h => 实现了一个简单的kdtree用于加速raytracing。
- material.h => 定义了材质类Material以及MtlLib类用来load *.mtl文件。
- model.h => 定义了模型类的基类Model,暂时只派生出Triangles和Partiles两个类。Model类主要定义了几个接口规范。比如virtual bool intersect(const Ray& ray, HitRecord& record) = 0;用来判断光线是否与模型相交。
- triangles.h => 定义了Mesh类用来加载obj模型及存储相关信息。Triangles类是Model类的派生类。实现了Model的全部接口。还有 类似Triangles(const float3 &boxdim,float3 center = float3(0.0f,0.0f,0.0f))【快速创建一个正方体mesh。】的函数
- particles.h => 一堆小球组成的例子系统。暂时还不完善。
OpenglWrappers里面封装了OpenGL相关的类。
- glMeshWrapper.h => 用来将数据打包传到显卡,这包括材质中用到的纹理。实现了引用计数避免将同一个模型的数据多次传入显存。将Obj模型里使用相同material的group合并成一组放在一个Vbo里节约了资源。后面会介绍glMeshWrapper的使用。
- glslShaderWrapper.h => 封装了glsl shader的相关操作。ShaderSource类用来说明shader的来源(from file or existed shader program)用来避免多次创建同一个shader。Shader类主要负责加载shader文件。ShaderManager类会自动加载一个指定文件夹下的所有shader文件,并重载了[]运算符。shaderManager["light"],即可获得light.vs/light.fs定义的shader。
- glTextureWrapper.h => 封装了Texture的相关操作。
- glHelper.h => 定义了opengl相关的辅助类:glImageRender用于显示一张2d图片到opengl窗口(可以用来显示raytracing的结果),glScreenCapture用于保存渲染的图片。
4. glMeshWrapper的使用
glMeshWrapper只负责生成纹理、将数据绑定到VBO。而其派生类负责和shader交互,渲染等。所以glMeshWrapper的构造函数被定义成了protected不能创建对象。
其作为基类提供了一些常用操作及接口规范。定义一个glMeshWrapper派生类需要以下工作:
#pragma once #include <openglWrappers/glMeshWrapper.h> class LightMesh : public redips::glMeshWrapper{ public: LightMesh(const redips::Triangles* model, redips::ShaderSource shadersource = redips::ShaderSource(), unsigned int option = 1u) : glMeshWrapper(model, shadersource,option){ bindVaoAttribData(0, 1, -1); }; LightMesh(const glMeshWrapper& another, redips::ShaderSource shadersource = redips::ShaderSource()) : glMeshWrapper(another,shadersource){ bindVaoAttribData(0, 1, -1); } ~LightMesh(){}; };
1. 定义一个普通构造及一个拷贝构造函数,说明应该绑定哪些数据到vao。普通构造函数中的三个参数分别是:三角mesh、mesh使用的ShaderSource、打包mesh的option[传送模型数据时是以mtl为单位还是group为单位打包;是否要自动生成三角面片的法线]。ShaderSource有两个构造函数及一个默认构造,其中一个接受一个GLuint类型的参数[代表一个现有的shaderProgram],另外一个接受const char* 类型的参数[shader文件的路径]。所以可以像下面那样创建LightMesh.
LightMesh* mesh1 = new LightMesh(new Triangles("obj/file/path"), "shader/path"); LightMesh* mesh2 = new LightMesh(new Triangles("obj/file/path"), existedShader.Program); LightMesh* mesh3 = new LightMesh(new Triangles("obj/file/path")); mesh3->useShader(existedShader.Program); mesh3->useShader("shader/path");
也可以像mesh3那样后来再指定shader。
2. 重写glMeshWrapper的draw()函数用于定制化的渲染[比如激活纹理等操作]。LightMesh类没有重写,使用了glMeshWrapper提供的draw()函数。