第十八節(jié)—紋理金字塔

本文為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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容