OpenGL超级宝典笔记(一)数学基础与基础变换
创建更多图形
批量三角形
GlTriangleBatch
可以塞进去多个三角形,用处是把重复的点剔除掉,坏处是当三角形加了太多了之后每次添加都会更慢一些。
几个关键的api如下
GLTriangleBatch b;
b.BeginMesh(200); // 200个顶点
b.AddTriangle(M3DVector3f verts[3], M3DVector3f vNorms[3], M3DVector3f vTexCoords[3]);
b.End();
b.Draw();
e.g.
创建一个不断转动的甜甜圈。
首先看ChangeSize。
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
g_viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 100.0f);
// 压栈投影矩阵到投影矩阵栈
g_projectionMatrix.LoadMatrix(g_viewFrustum.GetProjectionMatrix());
// 这是一个渲染管线,需要一个modelMatrix和一个投影矩阵
g_transformPipeline.SetMatrixStacks(g_modelViewMatrix, g_projectionMatrix);
}
frustum翻译为视体,Perspective则代表着透视。这个方法的意义是让当前的矩阵去乘以一个透视矩阵。
void SetPerspective(float fFov, float fAspect, float fNear, float fFar);
设置一个透视矩阵。
让projection(投影)矩阵从frustum那里拿到一个矩阵。
g_transformPipeline的类型是GLGeometryTransform,中文是几何变换。
所以就是绑定矩阵的作用,一个model的矩阵,一个透视的矩阵。
看一下SetupRC。
void SetupRC()
{
glClearColor(0.91f, 0.97f, 0.96f, 1.0f);
// OpenGL必须要有shader才能着色
g_shaderManager.InitializeStockShaders();
gltMakeTorus(g_torusBatch, 0.4f, 0.15f, 30, 30);
g_floorBatch.Begin(GL_LINES, 324);
for (GLfloat x = -20.0f; x <= 20.0f; x += 0.5f)
{
g_floorBatch.Vertex3f(x, -0.55f, 20.0f);
g_floorBatch.Vertex3f(x, -0.55f, -20.0f);
g_floorBatch.Vertex3f(20.0f, -0.55f, x);
g_floorBatch.Vertex3f(-20.0f, -0.55f, x);
}
g_floorBatch.End();
}
两个注意点:
- 传给torus的batch不需要add任何定点,一个空的batch足矣。
- 地板的线要研究一下怎么弄的,应该就是普通的网格而已
在这里说一下opengl的坐标系:
renderScene
void RenderScene()
{
std::cout << "RenderScene" << std::endl;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
static GLfloat vFloorColor[] = { 0, 1.0f, 0, 1.0f };
static GLfloat vTorusColor[] = { 1.0f, 0, 0, 1.0f };
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.f;
// 复制栈顶(单位矩阵)到栈顶,避免破坏数据
g_modelViewMatrix.PushMatrix();
g_shaderManager.UseStockShader(GLT_SHADER_FLAT, g_transformPipeline.GetModelViewProjectionMatrix()
,vFloorColor);
g_floorBatch.Draw();
// 把matrix旋转某个角度,再在上面画东西
g_modelViewMatrix.Translate(0, 0, -2.5f);
g_modelViewMatrix.Rotate(yRot, 0, 1.0f, 0);
// 这个时候管线里面的modelMatrix已经被改变了
// 就是把渲染管道中的两个矩阵相乘之后返回出来
g_shaderManager.UseStockShader(GLT_SHADER_FLAT, g_transformPipeline.GetModelViewProjectionMatrix()
, vTorusColor);
g_torusBatch.Draw();
// 丢弃栈顶,恢复成单位矩阵,以便下一帧可以用
g_modelViewMatrix.PopMatrix();
glutSwapBuffers();
glutPostRedisplay();
}
先看一下timer是啥。这就是返回一个已流逝的时间,然后乘以60,说明速度是60。
接下来进入modelMatrix的上下文,然后做位移,旋转,然后在这之上绘制model,最后退出上下文。
用的shader是flatshader,参数是转换器返回的已投影变换的model矩阵,以及颜色。
最后这句很关键了,就继续再绘制一次,相当于无限render。
过程相当于:
1. 创建transformer,绑定一个modelMatrix和一个投影matrix
2. 渲染的时候在matrix的上下文下旋转modelMatrix,然后画图
3. 2说的画图用的是flat的shader,参数是经过model * modelMatrix * projection变换之后的矩阵
换个写法是这样的:
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
// 位移矩阵
m3dTranslationMatrix44(mTranslate, 0, 0, -2.5f);
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0, 1.0f, 0);
// 把model层做位移和转动变换
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
// 将投影矩阵和model矩阵相乘,结果返回在mModelViewProjection中
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(), mModelview);
GLfloat vBlack[] = {0, 0, 0, 0};
// 所以最终的目的就是把变换矩阵传给shader了
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
torusBatch.Draw();
glutSwapBuffers();
glutPostRedisplay();
上面的乘法要记得顺序就是。
变换管线
首先是每个点都是一个1x4矩阵,w是缩放因子。要先拿去乘以模型视图矩阵,然后乘以投影矩阵,然后再去裁剪啥的。
由于这样子很麻烦,所以使用矩阵堆栈。就是上面那个例子了。
既然叫做stack,就是一个栈了,最大高度64,可以随意推matrix进去,一get就能get到栈顶。另外乘法运算也可以直接调用stack的方法,使用栈顶来乘以另一个矩阵,最后的结果会被推到栈顶去。
stack还有放射变换的api
void MatrixStack::Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
void MatrixStack::Translate(GLfloat x, GLfloat y, GLfloat z);
void MatrixStack::Scale(GLfloat x, GLfloat y, GLfloat z);