[英]How to scale a model in OpenGL?
我正在嘗試在 OpenGL 中縮放模型,但我不知道從哪里開始,我正在嘗試使用 glScalef(),但我不知道是否是這種方式,我對 openGL 了解不多,我對理論了解更多(那我必須將向量乘以矩陣,但我沒有找到任何關於此的好的教程)...我的代碼是:
bool res = loadOBJ(object, vertices, uvs, normals);
indexVBO(vertices, uvs, normals, indices, indexed_vertices, indexed_uvs, indexed_normals);
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3), &indexed_vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2), &indexed_uvs[0], GL_STATIC_DRAW);
glGenBuffers(1, &normalbuffer);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3), &indexed_normals[0], GL_STATIC_DRAW);
// Generate a buffer for the indices as well
glGenBuffers(1, &elementbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 2nd attribute buffer : UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(
1, // attribute
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 3rd attribute buffer : normals
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glVertexAttribPointer(
2, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glScalef(10, 10, 10);
glDrawElements(
GL_TRIANGLES, // mode
indices.size(), // count
GL_UNSIGNED_SHORT, // type
(void*)0 // element array buffer offset
);
縮放是您可以對模型矩陣進行的三種變換之一,以及平移和旋轉。
模型矩陣是將頂點轉換為屏幕上像素的三個矩陣之一,以及視圖和投影矩陣。
由於您想要的縮放類型僅適用於模型矩陣,我們現在將跳過其他兩個。 然而,我建議閱讀所有這三個以及它們如何交互,因為它們對 OpenGL 至關重要。
在我們開始之前,我建議選擇一個像GLM這樣的庫,因為它會為我們做很多繁重的工作。 從這里開始,我將使用 GLM 語法來保持簡潔。
首先讓我們將比例存儲在 3d 向量中:
glm::vec3 scale = glm::vec3(10f, 10f, 10f);
現在我們需要一個沒有轉換的基本模型矩陣:
glm::mat4 modelMatrix = glm::mat4();
現在我們可以將比例應用於我們的模型矩陣:
modelMatrix = glm::scale(modelMatrix, scale);
現在我們有了一個矩陣,它可以應用於任何一組頂點,並在所有三個維度上將它們縮放 10。
接下來我們需要將這些信息傳遞給着色器。 就像glVertexAttribPointer告訴着色器在哪里可以找到頂點屬性一樣,我們將使用glUniform發送我們的矩陣:
GLuint location = getUniformLocation("model");
glUniformMatrix4fv(location, 1, false, glm::value_ptr(modelMatrix));
在這里,我們向着色器查詢“模型”制服的位置。 然后我們將 1 個統一矩陣(我們的modelMatrix
)提交到該位置。
最后,我們需要在着色器中使用該矩陣。 這是一個超級簡單的頂點着色器,可以滿足我們的需求:
#version 400 core
in vec3 position;
in vec3 normal;
in vec2 uv;
uniform mat4 model;
void main() {
gl_Position = model * vec4(position, 1.0f);
}
通常我們會將法線和 uv 信息傳遞給片段着色器,但我現在省略它以保持簡單。
就是這樣。 希望這能讓你到達你想去的地方。
附帶說明一下,函數glScalef
在 GL3 和更新版本中已棄用。 我喜歡使用docs.gl作為參考,因為它區分版本。
編輯:如何構建着色器程序
我上面發布的代碼只是一個頂點着色器的源代碼,它只是一個更大的着色器程序的一部分。 着色器程序可以具有頂點着色器、幾何着色器和片段着色器。 雖然現在我們只關注兩個必需的; 頂點和片段着色器。
首先將上面的頂點着色器代碼放入一個名為vertex.glsl
的文件中。 加載文件超出了這個答案的范圍,所以我假設你有一個名為loadSourceFromFile
的函數,它接受一個參數,文件名。
在我們繼續之前,讓我們創建幾個實用函數:
function Gluint compileShader(const char* source, GLuint type) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, source);
glCompileShader(shader);
return shader;
}
function void verifyShaderCompilation(GLuint shader) {
GLunit status = glGetShaderi(shader, GL_COMPILE_STATUS);
assert(status == GL_TRUE);
}
現在,讓我們編譯頂點着色器:
const char* vertexSource = loadSourceFromFile("vertex.glsl");
Gluint vertexShader = compileShader(vertexSource, GL_VERTEX_SHADER);
verifyShaderCompilation(vertexShader);
接下來我們必須構建片段着色器。 將以下代碼放入另一個名為fragment.glsl
文件中:
#version 400 core
out vec4 color;
void main()
{
color = vec4(0, 0.5, 0, 1);
}
這個片段着色器將使它處理的每個片段都變成綠色。 現在讓我們像編譯頂點着色器一樣編譯它:
const char* fragmentSource = loadSourceFromFile("fragment.glsl");
Gluint fragmentShader = compileShader(fragmentSource, GL_FRAGMENT_SHADER);
verifyShaderCompilation(fragmentShader);
現在我們有兩個編譯的着色器。 是時候將它們鏈接到一個着色器程序中了:
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
您需要在調用glVertexAttribPointer
之前完成所有這些操作,因為它需要與您構建的着色器程序進行通信。
編輯:調試 OpenGL 函數
在嘗試解決問題時,我建議在每次 GL 函數調用后使用以下函數:
function void verifyNoGLError() {
int errorCode = glGetError();
assert(errorCode == 0);
}
OpenGL 是一個復雜的狀態機,一旦出現問題,您就會想知道。
您真的應該使用 MVP(模型、視圖和投影)矩陣。 矩陣是浮點數的二維數組,最常用的大小為 4x4。
那么什么是模型、視圖和投影矩陣呢?
模型是表示對象變換的矩陣。 我們使用矩陣,因為我們可以將平移、縮放和旋轉存儲在一個變量中。 位置向量存儲在第一行,旋轉向量存儲在第二行,尺度向量存儲在第三行。
View 是一個表示相機的矩陣。 它的工作原理類似於模型矩陣。
最后但並非最不重要的是,我們有投影矩陣。 這保存了相機投影的數據,因為我們無法將其存儲在 View 矩陣中。 這個矩陣有點難以解釋,但它基本上描述了事物相對於相機位置的外觀。 有兩種常見的投影類型,透視和正交。 透視使物體離相機越遠越小。 你用眼睛看到的一切都是透視。 正交使一切看起來都一樣大小,無論距離相機多遠。
當然,在我們的代碼中處理矩陣對我們來說並不容易,所以有一些庫可以幫助我們做到這一點。 我最喜歡的是GLM 。 GLM 幫助我們創建和使用矩陣。 我建議你閱讀它。 一旦我們有了模型、視圖和投影矩陣,我們就可以將它們與頂點位置相乘,以獲得頂點的最終位置。
現在,MVP 和轉換有點太高級了,無法在答案中解釋。 我建議你看看這兩個教程,它們解釋了矩陣和變換(比我做的好得多),並告訴你如何在你的代碼和着色器中使用它們:教程 1 ,教程 2 。 您還可以查看下一個教程,了解如何制作出色的相機系統。
遵循這些很棒的教程,我可以確保您獲得縮放模型:)
PS 這是我的第一個真正的答案,如果您覺得難以理解,那么抱歉;)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.