本文為L_Ares個人寫作,包括圖片皆為個人親自操作,以任何形式轉(zhuǎn)載請表明原文出處。
首先介紹幾個函數(shù),下面要用到的。
1. 根據(jù)三個點尋找法線:
void m3dFindNormal(M3DVector3f result, const M3DVector3f point1, const M3DVector3f point2, const M3DVector3f point3);
參數(shù):
(1).result:找到的法線結(jié)果。
(2).(3).(4).point:用來找法線的三個點。這三個點是要有順序的,正面是逆時針排列,背面是順時針排列。
2. 設(shè)置法線:
void Normal3fv(M3DVector3f vNormal);
參數(shù):
vNormal:法線坐標(biāo)數(shù)組
3. 設(shè)置紋理坐標(biāo):
void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);
參數(shù):
(1).texture:紋理的mimap涂層數(shù),也就是你要設(shè)置哪個紋理的紋理坐標(biāo),填入紋理的順序數(shù),我們這節(jié)只有一個紋理,所以這個紋理就是第1個,所以寫0。
(2).s: 紋理的橫坐標(biāo)對應(yīng)的是多少。
(2).t:紋理的縱坐標(biāo)對應(yīng)的是多少。
4. 設(shè)置頂點坐標(biāo):
void Vertex3fv(M3DVector3f vVertex);
參數(shù):
vVertex:頂點坐標(biāo)數(shù)組
代碼如下:
//
// main.cpp
// 09紋理金字塔
//
// Created by EasonLi on 2020/9/2.
// Copyright ? 2020 EasonLi. All rights reserved.
//
#include <stdio.h>
#pragma mark - 引用類
//工具類GLTools,GLTool中大多數(shù)類似于C語言的工具類都在其中
#include "GLTools.h"
//著色器管理器
#include "GLShaderManager.h"
//參考幀
#include "GLFrame.h"
//矩陣投影工具類
#include "GLFrustum.h"
//容器類。三角形批次類
#include "GLBatch.h"
//矩陣堆棧類。
#include "GLMatrixStack.h"
//管道類。管理mvp矩陣的
#include "GLGeometryTransform.h"
//3D數(shù)學(xué)類
#include "math3d.h"
//根據(jù)系統(tǒng)引入GLUT
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#pragma mark - 公共變量
//著色器管理器
GLShaderManager shaderManager;
//三角形批次類
GLBatch pyramidBatch;
//觀察者參考幀
GLFrame cameraFrame;
//物體參考幀
GLFrame objFrame;
//模型視圖矩陣堆棧
GLMatrixStack mvMatrixStack;
//投影矩陣堆棧
GLMatrixStack projMatrixStack;
//矩陣投影工具對象
GLFrustum viewFrustum;
//變換管道
GLGeometryTransform transformPipeline;
//紋理變量,一般用無符號整型
GLuint textureID;
#pragma mark - 函數(shù)
//初建或重塑窗口
void ChangeSize (int w , int h)
{
//設(shè)置視口大小
if (h == 0) {
h = 1;
}
glViewport(0, 0, w, h);
//設(shè)置投影方式并創(chuàng)建投影矩陣
viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
//將投影視圖矩陣放入投影矩陣堆棧
projMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
//設(shè)置變換管道
transformPipeline.SetMatrixStacks(mvMatrixStack, projMatrixStack);
}
/**
將TGA文件加載成2D紋理
里面包括了讀取、加載、設(shè)置紋理
參數(shù)
(1)fileName:要加載的TGA文件的文件名
(2)minFilter:縮小過濾模式
(3)magFilter:方大過濾模式
(4)wrapMode:環(huán)繞方式
*/
bool LoadTGATexture(const char *fileName , GLenum minFilter , GLenum magFilter , GLenum wrapMode)
{
//先定義一下要用到的變量,用來存儲讀取紋理之后拿到的數(shù)據(jù)信息
//因為讀取紋理tga文件的函數(shù)返回的是字節(jié)類型
GLbyte *mByte;
//定義獲取到的寬、高、組成和顏色成分變量
int nWidth,nHeight,nComponent;
GLenum nFormat;
//1.讀取紋理。將TGA文件讀取成像素
//參數(shù)
//(1) 要讀取的tga的文件名,必要時可以添加路徑進(jìn)去
//注:從這里開始,后面的參數(shù)都是要的指針,所以給的是地址
//(2) tga文件的寬度
//(3) tga文件的高度
//(4) tga文件的顏色成分組成
//(5) tga文件的格式
mByte = gltReadTGABits(fileName, &nWidth, &nHeight, &nComponent, &nFormat);
if (mByte == NULL) {
return false;
}
//2.設(shè)置紋理
//設(shè)置紋理的環(huán)繞方式
//橫向s軸的環(huán)繞方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
//縱向t軸的環(huán)繞方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
//設(shè)置紋理的過濾方式
//紋理縮小時的過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
//3.載入紋理
//參數(shù)
//(1)紋理維度
//(2)mip貼圖的層次
//(3)每個紋理單元中存儲多少的顏色成分
//(4)加載紋理的寬度
//(5)加載紋理的高度
//(6)允許為紋理添加一個邊框,就是給一個邊界的限度,一般設(shè)置成0就行了
//(7)載入紋理要用的像素模式
//(8)像素數(shù)據(jù)的數(shù)據(jù)類型
//(9)指向紋理圖像數(shù)據(jù)的指針。
glTexImage2D(GL_TEXTURE_2D, 0, nComponent, nWidth, nHeight, 0, nFormat, GL_UNSIGNED_BYTE, mByte);
//使用完成之后要釋放
free(mByte);
//4.加載Mip紋理所生成的所有Mip層
//參數(shù)選擇:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
//設(shè)置金字塔頂點及紋理數(shù)據(jù)等
void MakePyramid(GLBatch &pyramidBatch)
{
//通過三角形批次類設(shè)置金字塔基本繪制信息
//參數(shù):
//(1)繪制方式
//(2)頂點數(shù)量,金字塔是6個三角形組成的,4個側(cè)面每個都是1個三角形,底面矩形是2個三角形,所以是6個三角形18個頂點
//(3)要用幾個紋理。如果這個參數(shù)不設(shè)置的話,默認(rèn)是0的,也就是不用紋理。
pyramidBatch.Begin(GL_TRIANGLES, 18 , 1);
//設(shè)置頂點坐標(biāo)
//金字塔頂點
//Front是靠近觀察者的頂點,Back是遠(yuǎn)離觀察者的頂點
M3DVector3f vApex = {0.f,1.f,0.f};
M3DVector3f vFrontLeft = {-1.f,-1.f,1.f};
M3DVector3f vFrontRight = {1.f,-1.f,1.f};
M3DVector3f vBackLeft = {-1.f,-1.f,-1.f};
M3DVector3f vBackRight = {1.f,-1.f,-1.f};
//臨時的法線變量
M3DVector3f vNormal;
//金字塔底部是兩個三角形構(gòu)成,分別命名三角形A和三角形B
/********************************************設(shè)置三角形A******************************************/
//這里注意,法線在獲取的時候,頂點的順序是有方向性的,正面是逆時針順序,背面是順時針順序,不然會出現(xiàn)黑色,沒有紋理
//頂點:vFrontLeft,vBackLeft,vBackRight
//根據(jù)三個點,找到三角形A的法線,也可以自己算法線,無所謂的
//參數(shù):
//(1)result:找到的法線。存儲到剛才定義的臨時法線變量里面
//(2)(3)(4):用來找法線的三個點
m3dFindNormal(vNormal, vFrontLeft, vBackLeft, vBackRight);
//利用找到的三角形A的法線,立即設(shè)置三角形A的三個頂點的紋理
//vBackLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontLeft);
//vBackRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vBackLeft);
//vFrontRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
//參數(shù):
//(1)texture:第幾個紋理?。?!而不是把紋理放上去,是紋理的疊加的時候,這個紋理是第幾個。要用哪個
//(2)s:紋理的橫坐標(biāo)
//(3)t:紋理的總坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vBackRight);
/********************************************設(shè)置三角形B******************************************/
//三角形B的三個點:vBackRight,vFrontRight,vFrontLeft
//找到三角形B的法線
m3dFindNormal(vNormal, vBackRight, vFrontRight, vFrontLeft);
//設(shè)置三角形B的三個頂點紋理
//vFrontLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vBackRight);
//vBackLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontRight);
//vFrontRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontLeft);
/********************************************設(shè)置正面三角形****************************************/
//頂點:vApex,vFrontLeft,vFrontRight
//找到正面三角形的法線
m3dFindNormal(vNormal, vApex, vFrontLeft, vFrontRight);
//設(shè)置正面三角形的頂點紋理
//vApex
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vApex);
//vFrontLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontLeft);
//vFrontRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontRight);
/********************************************設(shè)置左面三角形****************************************/
//頂點:vApex,vFrontLeft,vBackLeft
//找到左面三角形的法線
m3dFindNormal(vNormal, vApex, vBackLeft, vFrontLeft);
//設(shè)置左面三角形頂點紋理
//vApex
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vApex);
//vFrontLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontLeft);
//vBackLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
//設(shè)置頂點
pyramidBatch.Vertex3fv(vBackLeft);
/********************************************設(shè)置右面三角形****************************************/
//頂點:vApex,vFrontRight,vBackRight
//找到右面三角形的法線
m3dFindNormal(vNormal, vApex, vFrontRight, vBackRight);
//設(shè)置右面三角形頂點紋理
//vApex
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vApex);
//vFrontRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vFrontRight);
//vBackRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vBackRight);
/********************************************設(shè)置后面三角形****************************************/
//頂點:vApex,vBackLeft,vBackRight
//找到后面三角形的法線
m3dFindNormal(vNormal, vApex, vBackRight, vBackLeft);
//設(shè)置后面三角形頂點紋理
//vApex
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vApex);
//vBackLeft
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vBackLeft);
//vBackRight
//設(shè)置法線
pyramidBatch.Normal3fv(vNormal);
//設(shè)置紋理坐標(biāo)
pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
//設(shè)置頂點坐標(biāo)
pyramidBatch.Vertex3fv(vBackRight);
/***********************************************************************************************/
//結(jié)束三角形批次類的設(shè)置
pyramidBatch.End();
}
//設(shè)置渲染環(huán)境
void SetUpRC ()
{
//設(shè)置清屏顏色
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
//初始化著色器管理器
shaderManager.InitializeStockShaders();
//開啟深度測試
glEnable(GL_DEPTH_TEST);
//觀察者參考幀偏移一定的位置,不要和原點重合
cameraFrame.MoveForward(-10.f);
//分配紋理對象
//參數(shù):(1)要使用幾個紋理 (2)紋理對象的指針
glGenTextures(1, &textureID);
//綁定紋理狀態(tài)
//參數(shù):(1)紋理的狀態(tài) (2)紋理對象
glBindTexture(GL_TEXTURE_2D, textureID);
//讀取、加載、設(shè)置紋理參數(shù)
//這是一個自己寫的函數(shù),主要是把這三步合成一步
//就是將TGA文件加載成2D紋理
LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
//設(shè)置金字塔頂點及紋理數(shù)據(jù)等
MakePyramid(pyramidBatch);
}
//清理
void ShutDownRC ()
{
//
glDeleteTextures(1, &textureID);
}
//渲染
void RenderScene ()
{
//清理緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//光源位置
static GLfloat vLightPos[] = {1.f,1.f,0.f};
//顏色
static GLfloat vColor[] = {1.f,1.f,1.f,1.f};
//壓棧
mvMatrixStack.PushMatrix();
//獲取觀察者矩陣
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
mvMatrixStack.MultMatrix(mCamera);
//獲取物體矩陣
M3DMatrix44f mObj;
objFrame.GetMatrix(mObj);
mvMatrixStack.MultMatrix(mObj);
//綁定紋理。因為這里我們只有一個紋理,是可以不用再綁定的,但是多個紋理的時候,綁定一下保證紋理不錯
glBindTexture(GL_TEXTURE_2D, textureID);
//設(shè)置著色器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vColor,0);
//繪制
pyramidBatch.Draw();
//出棧
mvMatrixStack.PopMatrix();
//交換緩沖區(qū)
glutSwapBuffers();
}
//特殊鍵位
void SpecialKeys (int key , int x ,int y)
{
switch (key) {
case GLUT_KEY_UP:
objFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
break;
case GLUT_KEY_DOWN:
objFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
break;
case GLUT_KEY_RIGHT:
objFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
break;
case GLUT_KEY_LEFT:
objFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
break;
}
glutPostRedisplay();
}
#pragma mark - main
int main (int argc , char *argv[])
{
//設(shè)置工作目錄和項目目錄一致在/Resource下,glut已經(jīng)設(shè)置,手動安全
gltSetWorkingDirectory(argv[0]);
//初始化glut
glutInit(&argc, argv);
//初始化顯示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//設(shè)置窗口大小
glutInitWindowSize(600, 600);
//創(chuàng)建窗口并命名
glutCreateWindow("紋理金字塔");
//注冊重塑函數(shù)
glutReshapeFunc(ChangeSize);
//注冊渲染函數(shù)
glutDisplayFunc(RenderScene);
//注冊特殊鍵位
glutSpecialFunc(SpecialKeys);
//初始化glew庫
GLenum status = glewInit();
if (status != GLEW_OK) {
printf("glew init error : %s \n",glewGetErrorString(status));
return 1;
}
//設(shè)置渲染環(huán)境
SetUpRC();
//設(shè)置本地消息循環(huán)機(jī)制
glutMainLoop();
ShutDownRC();
return 0;
}
執(zhí)行結(jié)果如圖1.1:

1.1.png