OpenGL初識之OpenGL渲染基礎概念傳送門 juejin.im/post/5d67e5…數組
接着上一節咱們學習了OpenGL渲染基礎,如今咱們來綜合使用一下,練習一下繪製OpenGL圖元並組合圖元:bash
1、繪製圖元點
2、繪製圖元線段/連續線段/線環
3、繪製金字塔
4、圖元渲染三角形帶/三角形扇
複製代碼
先上繪製的總結思路圖 函數
開始準備環境和對應的函數(詳細介紹請移步我以前的文章,傳送門:基本函數解釋 juejin.im/post/5d67e4… 這裏主要說明有區別的地方)oop
一、設置全局變量post
GLShaderManager shaderManager;//着色器管理
GLMatrixStack modelViewMatrix;//模型視圖矩陣堆棧
GLMatrixStack projectViewMatrix;//投影矩陣堆棧
GLFrame cameraFrame;//觀察者
GLFrame objectFrame;
GLFrustum viewFrustum;//投影矩陣
//批次類(7種不一樣的圖元,對應7種容器對象)
GLBatch pointBatch;//點
GLBatch lineBatch;//線
GLBatch lineStripBatch;//連線
GLBatch lineLoopBatch;//線環
GLBatch triangleBatch;//三級形
GLBatch triangleStripBatch;//三角形帶
GLBatch triangleFanBatch;//三角形扇
//幾何變換管道
GLGeometryTransform transformPipeline;
//顏色RGBA
GLfloat vGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
GLfloat vYellow[] = {1.0f,1.0f, 0.0f, 1.0f};
//設置按空格鍵來切換不一樣圖元展現
//記錄按了幾回空格
int nStep = 0;
複製代碼
二、設置main函數學習
int main(int argc,char* argv[]){
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow("Triangle");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
//註冊點擊空格會調用的函數
glutKeyboardFunc(KeyPressFunc);
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
複製代碼
三、設置SetupRC函數,註釋我都寫在裏面了測試
//一、設置背景顏色
glClearColor(0.7f, 0.7f, 0.7f,1.0);
//二、初始化着色器管理器
shaderManager.InitializeStockShaders();
//三、開啓深度測試
glEnable(GL_DEPTH_TEST);
//四、設置變換管道
transformPipeline.SetMatrixStacks(modelViewMatrix, projectViewMatrix);
//五、設置觀察者位置,便於觀察效果
cameraFrame.MoveForward(-15);
//六、設置一些點
//建立一維的六個點的數組
GLfloat vCoast[18] = {
3,3,0,
-3,3,0,
3,0,0,
-3,0,0,
3,-3,0,
-3,-3,0
};
//設置圖元渲染方式,點
pointBatch.Begin(GL_POINTS, 6);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
複製代碼
四、設置ChangeSize函數,須要注意的是,這個函數的調用時機,就是窗口已更改大小,或剛剛建立,不管是那種狀況,咱們都須要使用窗口維度設置視口和設置投影矩陣ui
//一、設置視口
glViewport(0, 0, w, h);
//二、投影矩陣: 須要設置縱橫比
viewFrustum.SetPerspective(35, floorf(w) / floorf(h), 1.0, 500);
//三、設置投影矩陣,加載投影矩陣
projectViewMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//四、模型視圖視圖矩陣堆棧,加載單元矩陣
modelViewMatrix.LoadIdentity();
複製代碼
五、設置渲染RenderScence函數spa
//一、清除緩衝區
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//二、模型視圖矩陣---壓棧
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);//矩陣相乘,獲得變換後的新矩陣
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
modelViewMatrix.MultMatrix(mObjectFrame);//矩陣相乘,獲得變換後的新矩陣
//三、挑選平面着色器渲染
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
//四、容器類提交繪製
switch (nStep) {
case 0:
//設置點的大小
glPointSize(20.0f);
pointBatch.Draw();
glPointSize(1.0f);
break;
default:
break;
}
//五、模型視圖矩陣---出棧,還原到之前的模型視圖矩陣
modelViewMatrix.PopMatrix();
//六、交換緩衝區
glutSwapBuffers();
複製代碼
六、設置特殊鍵位函數3d
void SpecialKeys (int key , int x, int y){
//點擊鍵盤上的上下左右鍵
if (key == GLUT_KEY_UP) {
//往上移動,圍繞X軸,是順時針方向,因爲逆時針爲正,因此是-5.0
//m3dDegToRad() 角度轉弧度函數
objectFrame.RotateWorld(m3dDegToRad(-5.0), 1.0, 0, 0);//其中的1.0表示YES,0.0表示NO
}else if (key == GLUT_KEY_DOWN) {
//往下移動,圍繞X軸,是逆時針方向,因爲逆時針爲正,因此是5.0
objectFrame.RotateWorld(m3dDegToRad(5.0), 1.0, 0, 0);
}else if (key == GLUT_KEY_LEFT) {
//往左移動,圍繞Y軸,是順時針方向,因爲逆時針爲正,因此是-5.0
objectFrame.RotateWorld(m3dDegToRad(-5.0), 0.0, 1.0, 0);
}else if (key == GLUT_KEY_RIGHT) {
//往右移動,圍繞Y軸,是逆時針方向,因爲逆時針爲正,因此是5.0
objectFrame.RotateWorld(m3dDegToRad(5.0), 0.0, 1.0, 0);
}
//發生變化須要從新渲染
glutPostRedisplay();
}
複製代碼
七、設置空格切換函數KeyPressFunc(這個主要是用來切換不一樣圖元)
void KeyPressFunc(unsigned char key, int x, int y){
//不斷點擊空格修改nStep記錄步數
if(key == 32){
nStep++;
if(nStep > 6){
nStep = 0;
}
}
//修改窗口名稱
switch(nStep)
{
case 0:
glutSetWindowTitle("GL_POINTS");
break;
case 1:
glutSetWindowTitle("GL_LINES");
break;
case 2:
glutSetWindowTitle("GL_LINE_STRIP");
break;
case 3:
glutSetWindowTitle("GL_LINE_LOOP");
break;
case 4:
glutSetWindowTitle("GL_TRIANGLES");
break;
case 5:
glutSetWindowTitle("GL_TRIANGLE_STRIP");
break;
case 6:
glutSetWindowTitle("GL_TRIANGLE_FAN");
break;
}
glutPostRedisplay();
}
複製代碼
繪製圖元點的寫完後,設置繪製圖元線段/連續線段/線環 ,只須要配置SetupRC裏面設置對應圖元,而後再RenderScence修改圖元繪製就好了,對應修改的位置以下
一、修改SetupRC部分代碼
//1/設置圖元渲染方式,點
pointBatch.Begin(GL_POINTS, 6);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
//2/設置圖元渲染方式,線段
lineBatch.Begin(GL_LINES, 6);
lineBatch.CopyVertexData3f(vCoast);
lineBatch.End();
//3/設置圖元渲染方式,連續線段
lineStripBatch.Begin(GL_LINE_STRIP, 6);
lineStripBatch.CopyVertexData3f(vCoast);
lineStripBatch.End();
//4/設置圖元渲染方式,線環
lineLoopBatch.Begin(GL_LINE_LOOP, 6);
lineLoopBatch.CopyVertexData3f(vCoast);
lineLoopBatch.End();
複製代碼
二、修改RenderScence對應部分代碼
switch (nStep) {
case 0:
//設置點的大小
glPointSize(20.0f);
pointBatch.Draw();
glPointSize(1.0f);
break;
case 1:
//設置線段寬度
glLineWidth(5.0f);
lineBatch.Draw();
glLineWidth(1.0f);
break;
case 2:
//
glLineWidth(5.0f);
lineStripBatch.Draw();
glLineWidth(1.0f);
break;
case 3:
glLineWidth(5.0f);
lineLoopBatch.Draw();
glLineWidth(1.0f);
break;
default:
break;
}
複製代碼
接下來咱們繪製金字塔,金字塔是由一個底面和四個側面組成,金字塔沒有底面的話,其實側面是由4個三角形組成的,若是有底面的話,底面是兩個三角形組成的,和上面不一樣圖元繪製的方式相似,只須要修改SetupRC和RenderScene對應的代碼就好了
一、SetupRC函數中設置金字塔四個頂點座標數據和圖元裝配方式
//設置金字塔的頂點數據
//經過三角形建立金字塔
GLfloat vPyramid[12][3] = {
-2.0f, 0.0f, -2.0f,
2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, -2.0f,
2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
-2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f};
//GL_TRIANGLES 每3個頂點定義一個新的三角形
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
複製代碼
二、RenderScence繪製金字塔
case 4:
triangleBatch.Draw();
break;
複製代碼
一、繪製六邊形三角形扇 修改SetupRC,設置頂點數據,該頂點的座標數據是經過半徑和角度求來的,下面這段代碼的循環,就是爲了求六邊形七個頂點的位置座標,六個頂點和一箇中心點
// 三角形扇形--六邊形
GLfloat vPoints[100][3];
int nVerts = 0;
//半徑
GLfloat r = 3.0f;
//原點(x,y,z) = (0,0,0);
vPoints[nVerts][0] = 0.0f;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
//M3D_2PI 就是2Pi 的意思,就一個圓的意思。 繪製圓形
for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
//數組下標自增(每自增1次就表示一個頂點)
nVerts++;
/*
弧長=半徑*角度,這裏的角度是弧度制,不是平時的角度制
既然知道了cos值,那麼角度=arccos,求一個反三角函數就好了
*/
//x點座標 cos(angle) * 半徑
vPoints[nVerts][0] = float(cos(angle)) * r;
//y點座標 sin(angle) * 半徑
vPoints[nVerts][1] = float(sin(angle)) * r;
//z點的座標
vPoints[nVerts][2] = -0.5f;
}
// 結束扇形 前面一共繪製7個頂點(包括圓心)
//添加閉合的終點
//若是屏蔽173-177行代碼,並把繪製節點改成7.則三角形扇形是沒法閉合的。
nVerts++;
vPoints[nVerts][0] = r;
vPoints[nVerts][1] = 0;
vPoints[nVerts][2] = 0.0f;
// 加載!
//GL_TRIANGLE_FAN 以一個圓心爲中心呈扇形排列,共用相鄰頂點的一組三角形
triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
triangleFanBatch.CopyVertexData3f(vPoints);
triangleFanBatch.End();
複製代碼
二、繪製六邊形三角形扇,修改RenderScence
case 6:
triangleFanBatch.Draw();
break;
複製代碼
三、繪製三角形帶,配置頂點座標以及圖元裝配方式
//三角形條帶,一個小環或圓柱段
//頂點下標
int iCounter = 0;
//半徑
GLfloat radius = 3.0f;
//從0度~360度,以0.3弧度爲步長
for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
{
//或許圓形的頂點的X,Y
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
//繪製2個三角形(他們的x,y頂點同樣,只是z點不同)
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = 0.5;
iCounter++;
}
// 關閉循環
//結束循環,在循環位置生成2個三角形
vPoints[iCounter][0] = vPoints[0][0];
vPoints[iCounter][1] = vPoints[0][1];
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = vPoints[1][0];
vPoints[iCounter][1] = vPoints[1][1];
vPoints[iCounter][2] = 0.5;
iCounter++;
// GL_TRIANGLE_STRIP 共用一個條帶(strip)上的頂點的一組三角形
triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
triangleStripBatch.CopyVertexData3f(vPoints);
triangleStripBatch.End();
複製代碼
四、繪製三角形帶,修改RenderScence
case 5:
triangleStripBatch.Draw();
break;
複製代碼
運行獲得全部的樣式,以下圖: