本範例展示使用 GLSL 330 + C (GLUT/GLEW + vmath) 實現 3D 透視投影渲染管線。著色器負責頂點變換與著色,C 端負責建立 VAO/VBO、編譯鏈接著色器、計算透視矩陣並每幀渲染。
#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;
}
#version 330
smooth in vec4 theColor;
out vec4 outputColor;
void main() {
outputColor = theColor;
}
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);
}
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 被優化掉)。GL_TRUE 轉置。glGetShaderInfoLog 與 glGetProgramInfoLog。vmath 數學庫可從作者資源頁面下載,或改用 glm、linmath.h 等替代方案。