簡體   English   中英

如何在OpenGL中縮放模型?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM