提要
之前为了学习shader自己手动搭建了一个框架,用到现在发现确实不太好用,考虑了一下,决定重新搭建一个,工欲善其事,必先利其器!
新的框架要达到的特性:
● 开源;
● 平台无关,IDE无关;
● 基于SDL2;
● 轻量、灵活又强大;
● 可以加载多种格式模型;
看最终效果:
Dota2旱地神牛(多submesh,多纹理)
仙剑三魔尊重楼(单mesh,单纹理)
stanford bunny
dota灵魂守卫(渲染法线)
下面来一步步搭建咯!
环境:Ubuntu 12.04 64bit
编译安装三方库
OpenGL的编程环境就不说了,肯定要预先配置好。
编译assimp 版本3.0.1270
Assimp(Open Asset Import Library)是一个支持读取多种模型资源的开源库,当前最新的版本是3.0版,支持读取多种类型的3D模型。
下载地址:http://sourceforge.net/projects/assimp/files/assimp-3.0/
下载的是这个完整的源码包:assimp--3.0.1270-full.zip,我们要手动编译安装。
解压之后cd到对应文件夹,运行下面的命令
mkdir build
cd build
cmake ..
make
sudo make install
编译一下samples下面的SimpleOpenGL的例子,不要理它的make file,直接用命令行,简单粗暴有效。
gcc Sample_SimpleOpenGL.c -o test -lGL -lGLU -lassimp -lglut
./test
编译SDL2 版本 2.01
SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。
下载地址:http://www.libsdl.org/download-2.0.php
./configure
make
sudo make install
可以编译一下SDL自带的例子。。
cd test
./configure
make
sudo make install
./testshader
安装glew 版本1.10
OpenGL扩展库,下载地址:http://glew.sourceforge.net/
解压后cd至文件夹
make
sudo make install
sudo ldconfig
SDL_image 2.0
SDL图片加载库,下载地址https://www.libsdl.org/projects/SDL_image/
解压后cd至文件夹
./configure
make
sudo make install
恶补GLSL基础知识
VAO,VBO,EBO 三角关系
Vertex Buffer Objects (VBO) are used to save information about vertices that will be used as source of data for attributes of a vertex shader. To render an object you have to create and fill Vertex Buffer Object for each type of attribute (position, color, normal, etc.) present in the vertex shader, indicate which vertex buffer corresponds to which attribute in the vertex shader, indicate how to interpret data in created VBOs and then you can execute draw call.
Vertex Array Objects (VAO) save information about which Vertex Buffer Object is connected to which attribute of the vertex shader, and information about how attributes of the vertex shader should interpret data in connected vertex buffers. In OpenGL 3+ in order to render any object you should bind VAO.
Element Buffer Objects (EBO) are used as source of indices during rendering of indexed primitives.
shader中的变量种类
输入主要为待处理顶点相应的aattribute变量、uniform变量、sampler变量以及临时变量、输出主要为经过顶点着色器后生成的varying变量以及一些内建输出变量。
attribute变量指的是3D物体中每个顶点各自不同的信息所属的变量,一般顶点的位置、颜色、法向、法向量等每个顶点各自不同的信息都是以attribute变量的方式传入vertex shader。
uniform变量指的是对于同一组顶点组成的单个3D物体中所有顶点都相同的量,一般为场景中当前的光源位置、摄像机的位置、MVP矩阵等。
varying变量是从顶点着色器计算产生并传递到fragment shader的数据变量。
内建的变量是gl_Position,gl_FrontFacingh和gl_PointSize.gl_Position是经过矩阵变换后顶点的最终位置,gl_FrontFacing指的是fragment所在的面朝的方向,gl_PointSize指的是点的大小。
内建变量在新版本的GLSL中不建议使用,基本用in和out待替代。
纹理的绑定
GLSL中使用的纹理的顺序是:生成纹理——绑定纹理——设置纹理——激活纹理——传递变量给shader
对应的OpenGL函数的接口是
glGenTextures(1, &textureObj); /生成纹理对象
load(image)//用外部库加载纹理到内存
glBindTexture(textureTarget, textureObj);//绑定纹理到纹理对象
glTexImage2D(textureTarget, 0, mode, surface->w,surface->h, 0, mode,GL_UNSIGNED_BYTE,surface->pixels);/绑定纹理到纹理对象
glTexParameteri //设置纹理滤波
free(image) //释放内存
在使用的时候要进行纹理的激活
glActiveTexture(textureUnit); //激活纹理单元
glBindTexture(textureTarget, textureObj);//绑定纹理对象到当前的纹理单元
详细解释下上面的两个函数,网上一个老外的说法很清楚:
glActiveTexture only sets the currently active texture unit (or "texture image unit" in OpenGL ES 2.0).
Valid values to pass to glActiveTexture are GL_TEXTURE0 to GL_TEXTUREn-1, where n is the implementation dependent number GL_MAX_TEXTURE_UNITS (or GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS for OpenGL ES 2.0).
glActiveTexture does nothing in itself, it only means that subsequent calls to change texturing state will target this texture unit (until you call glActiveTexture again with another index).
glBindTexture binds a texture object to a certain target (GL_TEXTURE_2D, GL_TEXTURE_CUBE, etc.) on the currently active texture unit. This means two things:
- calls to set up texture object state (glTexParameter) and data (glTexImage) affect the texture object bound to the current texture unit.
- when rendering, this texture unit will use the texture object state (e.g. wrap and filter modes) and image data from the bound texture object.
glActiveTexture 只处理当前激活的纹理单元(在OpenGLES中为纹理图形单元)。
传到glActiveTexture 的有效的参数是 GL_TEXTURE0 to GL_TEXTUREn-1,n是纹理单元的最大值。glActiveTexture自己并不做什么事,它只是说下面的更改纹理状态的语句影响的就是当前的纹理单元,直到用glActiveTexture激活其它单元。
glBindTexture将纹理对象绑定一个特定的目标,比如(GL_TEXTURE_2D, GL_TEXTURE_CUBE等等.)到当前的激活纹理单元, 其实是两件事:
- glTexParameter设置纹理对象的状态,glTexImage设置纹理对象数据;
-渲染的时候,纹理单元会使用纹理对象中对应的状态和数据
还要注意纹理单元和纹理对象的关系。这其实是两个独立的概念, 纹理对象和纹理单元的独立的。先看看纹理单元做什么事情。所谓单元就是指一个单位。OpenGL中的纹理映射是以纹理单元为单位的。默认情况下只有GL_TEXTURE0这一个纹理单元是活跃的。纹理作用时先作用GL_TEXTURE0,再作用GL_TEXTURE1,再作用GL_TEXTUE2,以此类推。 纹理对象是真正关联了纹理图像的单位。它关联纹理图像的同时也指定了这个纹理图像的作用方式,也就是glTexParameter指定的东西。
框架基本结构
项目是用cmake构建的,看下项目下目录结构:
assets用来放模型,纹理,build用来编译工程,include放头文件,shader放shader程序,src放cpp文件,main.cpp和CMakeLists.txt放在外面。
来看CMakeLists.txt吧.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) PROJECT(QSLFrameWork) INCLUDE_DIRECTORIES( ${QSLFrameWork_SOURCE_DIR}/include ) LINK_DIRECTORIES( /usr/lib64/ ) SET(SRC_LIST main.cpp src/cgl.cpp src/shaderprogram.cpp src/util.cpp src/csdl2.cpp src/cassimpmodel.cpp src/ctexture.cpp) ADD_EXECUTABLE(main ${SRC_LIST}) TARGET_LINK_LIBRARIES(main SDL2 GL GLU GLEW assimp SDL2_image)
只有简单的几行,添加搜索路径,链接外部库之类的。
主要类的说明
SDL2: 窗口管理类,封装了SDL的窗口处理函数;
CGL: OpenGL类,负责渲染;
Cassimpmodel:模型类,用于加载模型,提供模型的一些接口;
CTexture:纹理类,用SDL_image来加载纹理;
Util:提供一些工具函数;
具体的代码就不一一分析了。
关联本地项目到github
首先当然是在Github上创建一个项目,然后得到一个.git的地址(最好用ssh的那个)。
终端进入到项目目录,执行下列的命令
git init
git add include src CMakeLists.txt ...
git commit -am "initialization"
git remote rm origin
git remote add origin git@github.com:SilangQuan/QSLFrameWork.git
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
git push -u origin master
稍等片刻,项目就上传好了。
项目地址 (欢迎加星,fork)
https://github.com/SilangQuan/QSLFrameWork
小结
原本以为很随意的一个框架几乎搞了一周才完成,代码量其实也不是很多,大约2k-3k,主要是调试起来很费劲,感觉很多东西理解得并不透彻,看来GLSL还是没学好。不过经过这次码砖的过程,还是学到了挺多的东西,很多以前很模糊的概念也变得清晰了,更重要的是,希望这个框架能够帮助到和我一样学习OpenGL的同学。
非常感谢ZwqXin学长的无私帮助。