首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

OpenGL GLSL 透視投影矩陣 C 語言完整實作

     編輯:關於C語言

專案架構

本範例展示使用 GLSL 330 + C (GLUT/GLEW + vmath) 實現 3D 透視投影渲染管線。著色器負責頂點變換與著色,C 端負責建立 VAO/VBO、編譯鏈接著色器、計算透視矩陣並每幀渲染。

著色器程式碼

perspective.vert(頂點著色器)

#version 330
layout(location = 0) in vec4 vertexPosition;
layout(location = 1) in vec4 vertexColor;

smooth out vec4 theColor;
uniform vec2 offset;
uniform mat4 perspectiveMatrix;

void main() {
    vec4 cameraPosition = vertexPosition + vec4(offset.x, offset.y, 0.0, 0.0);
    gl_Position = perspectiveMatrix * cameraPosition;
    theColor = vertexColor;
}

perspective.frag(片段著色器)

#version 330
smooth in vec4 theColor;
out vec4 outputColor;

void main() {
    outputColor = theColor;
}

C 端核心邏輯 (perspective.c)

關鍵資料結構

GLuint vShader, fShader, programHandle;
GLint vaoHandle, vboHandles[2], positionBufferHandle, colorBufferHandle;
Matrix44f M; // vmath 4x4 浮點矩陣
GLint offsetUniformLocation, perspectiveMatrixUniformLocation;

著色器編譯鏈接流程

void SetupShader() {
    // 1. 讀取原始碼
    char *vs = readText("shader/perspective.vert");
    char *fs = readText("shader/perspective.frag");

    // 2. 建立並編譯頂點著色器
    vShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vShader, 1, &vs, NULL);
    glCompileShader(vShader);
    checkShaderCompile(vShader); // 檢查編譯錯誤並輸出 log
    free(vs);

    // 3. 建立並編譯片段著色器
    fShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fShader, 1, &fs, NULL);
    glCompileShader(fShader);
    checkShaderCompile(fShader);
    free(fs);

    // 4. 建立程式物件、鏈接、使用
    programHandle = glCreateProgram();
    glAttachShader(programHandle, vShader);
    glAttachShader(programHandle, fShader);
    glLinkProgram(programHandle);
    checkProgramLink(programHandle);
    glUseProgram(programHandle);
}

VAO/VBO 初始化

void InitVBO() {
    glGenBuffers(2, vboHandles);
    positionBufferHandle = vboHandles[0];
    colorBufferHandle = vboHandles[1];

    // 位置資料:36 個頂點,每頂點 4 分量
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
    glBufferData(GL_ARRAY_BUFFER, sizeof(positionData), positionData, GL_STATIC_DRAW);

    // 顏色資料
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colorData), colorData, GL_STATIC_DRAW);

    // VAO 綁定屬性指標
    glGenVertexArrays(1, &vaoHandle);
    glBindVertexArray(vaoHandle);

    glEnableVertexAttribArray(0); // position
    glEnableVertexAttribArray(1); // color

    glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL);

    glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, NULL);
}

渲染迴圈:每幀計算透視矩陣並繪製

void RenderScene() {
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(programHandle);

    // 取得 uniform 位置(僅需一次,此處為示範放在迴圈)
    offsetUniformLocation = glGetUniformLocation(programHandle, "offset");
    perspectiveMatrixUniformLocation = glGetUniformLocation(programHandle, "perspectiveMatrix");

    // 計算透視矩陣:frustum(left, right, top, bottom, near, far)
    perspectiveFrustum(M, -1.0, 1.0, 1.0, -1.0, 1.0, 3.0);

    // 上傳 uniform
    glUniform2f(offsetUniformLocation, 0.5, 0.5); // 平移偏移
    glUniformMatrix4fv(perspectiveMatrixUniformLocation, 1, GL_TRUE, M); // vmath 為行優先,需轉置

    glBindVertexArray(vaoHandle);
    glDrawArrays(GL_TRIANGLES, 0, 36); // 36 頂點 = 12 三角形
    glBindVertexArray(0);

    glutSwapBuffers();
    glutPostRedisplay(); // 持續重繪
}

透視矩陣參數說明

perspectiveFrustum(M,
    -1.0, 1.0,   // left, right
    1.0, -1.0,   // top, bottom (注意:此處 top > bottom,配合 GL_CW 正面)
    1.0, 3.0);   // near, far

若將 offset 設為 (0,0),僅見正方形;設為 (0.5,0.5) 即可觀察透視縮放效果。

編譯與執行

# 依賴:GLEW, GLUT, vmath.lib, readtext.lib
gcc perspective.c -o perspective -lGLEW -lGL -lGLU -lglut -lvmath -lreadtext
./perspective

關鍵技術點總結

項目說明
著色器版本#version 330 (OpenGL 3.3+)
屬性位置layout(location=N) 顯式指定,避免 glGetAttribLocation
矩陣庫vmath.h (行優先),上傳時 GL_TRUE 轉置為列優先
面剔除glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CW);
雙緩衝glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH)

常見問題排查

  • 白屏/無輸出:檢查 glGetUniformLocation 是否返回 -1(拼寫錯誤或 uniform 被優化掉)。
  • 矩陣錯誤:確認 vmath 矩陣佈局(行優先)與 OpenGL 預期(列優先)一致,需 GL_TRUE 轉置。
  • 著色器編譯失敗:務必檢查 glGetShaderInfoLog 與 glGetProgramInfoLog。

資源下載

vmath 數學庫可從作者資源頁面下載,或改用 glm、linmath.h 等替代方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved