【一步步學OpenGL 8】 -《縮放變換》
教程8
縮放變換
背景
縮放變換非常簡單,它的目的是增大或者縮小物體的尺寸。比如你想使用同一個模型來製作很多不同的物體(大小不一的樹組成的樹林,用的同一個模型),或者你想按照比例讓物體和現實世界尺寸一致。在上面的情形中你就需要在三個座標軸上同等縮放頂點的位置。當然,有時也希望物體只在一個軸上或者兩個軸上縮放使模型更薄、更瘦或者更高等等。
進行縮放變換其實很簡單。我們從最開始的原變換矩陣來看,回憶平移變換矩陣的樣子,我們保持結果矩陣中V1,V2和V3保持原樣的辦法是讓變換矩陣主對角線上的值都為’1’,這樣原向量一次都和1相乘之後依然保持不變,各分量之間互不影響。所以,這裡的縮放變換,只要把那些‘1’換成我們想縮放的值,原向量各分量分別乘以這些值之後就會在相應座標軸上進行相應的縮放了,值大於1則放大,值小於1則縮小。
原始碼詳解
World.m[0][0]=sinf(Scale); World.m[0][1]=0.0f; World.m[0][2]=0.0f; World.m[0][3]=0.0f;
World.m[1][0]=0.0f; World.m[1][1]=sinf(Scale); World.m[1][2]=0.0f; World.m[1][3]=0.0f;
World.m[2][0]=0.0f; World.m[2][1]=0.0f; World.m[2][2]=sinf(Scale); World.m[2][3]=0.0f;
World.m[3][0]=0.0f; World.m[3][1]=0.0f; World.m[3][2]=0.0f; World.m[3][3]=1.0f;
和上個教程相比程式碼還是隻是根據上面的描述改變了變換矩陣的內容。可以看到,這裡我們使用一個-1到1之間的一個縮放值來使物體在各座標軸上進行縮放,(0,1]範圍內三角形會在原尺寸和極小尺寸間變化,當變換矩陣對角線上的值為0時三角形徹底消失。在[-1,0)範圍內效果看上去一樣但三角形翻轉了,因為對角線上的值改變了頂點座標的正負符號。
示例Demo
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "ogldev_util.h"
#include "ogldev_math_3d.h"
GLuint VBO;
GLuint gWorldLocation;
const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";
static void RenderSceneCB()
{
glClear(GL_COLOR_BUFFER_BIT);
static float Scale = 0.0f;
Scale += 0.001f;
// 縮放變換矩陣
Matrix4f World;
World.m[0][0] = sinf(Scale) ; World.m[0][1] = 0.0f ; World.m[0][2] = 0.0f; World.m[0][3] = 0.0f;
World.m[1][0] = 0.0f ; World.m[1][1] = sinf(Scale); World.m[1][2] = 0.0f; World.m[1][3] = 0.0f;
World.m[2][0] = 0.0f; ; World.m[2][1] = 0.0f; ; World.m[2][2] = sinf(Scale); World.m[2][3] = 0.0f;
World.m[3][0] = 0.0f; ; World.m[3][1] = 0.0f; ; World.m[3][2] = 0.0f; World.m[3][3] = 1.0f;
glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &World.m[0][0]);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
static void InitializeGlutCallbacks()
{
glutDisplayFunc(RenderSceneCB);
glutIdleFunc(RenderSceneCB);
}
static void CreateVertexBuffer()
{
Vector3f Vertices[3];
Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
Vertices[1] = Vector3f(1.0f, -1.0f, 0.0f);
Vertices[2] = Vector3f(0.0f, 1.0f, 0.0f);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
}
static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
GLuint ShaderObj = glCreateShader(ShaderType);
if (ShaderObj == 0) {
fprintf(stderr, "Error creating shader type %d\n", ShaderType);
exit(1);
}
const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0]= strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);
glCompileShader(ShaderObj);
GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
GLchar InfoLog[1024];
glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
exit(1);
}
glAttachShader(ShaderProgram, ShaderObj);
}
static void CompileShaders()
{
GLuint ShaderProgram = glCreateProgram();
if (ShaderProgram == 0) {
fprintf(stderr, "Error creating shader program\n");
exit(1);
}
string vs, fs;
if (!ReadFile(pVSFileName, vs)) {
exit(1);
};
if (!ReadFile(pFSFileName, fs)) {
exit(1);
};
AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);
GLint Success = 0;
GLchar ErrorLog[1024] = { 0 };
glLinkProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
if (Success == 0) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
exit(1);
}
glValidateProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
if (!Success) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
exit(1);
}
glUseProgram(ShaderProgram);
gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
assert(gWorldLocation != 0xFFFFFFFF);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutInitWindowSize(1024, 768);
glutInitWindowPosition(100, 100);
glutCreateWindow("Tutorial 08");
InitializeGlutCallbacks();
// Must be done after glut is initialized!
GLenum res = glewInit();
if (res != GLEW_OK) {
fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
return 1;
}
printf("GL version: %s\n", glGetString(GL_VERSION));
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
CreateVertexBuffer();
CompileShaders();
glutMainLoop();
return 0;
}
頂點著色器shader.vs和片斷著色器shader.fs的程式碼保持不變:
shader.vs
#version 330
layout (location = 0) in vec3 Position;
uniform mat4 gWorld;
void main()
{
gl_Position = gWorld * vec4(Position, 1.0);
}
shader.fs
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
執行效果
位於螢幕中心的紅色三角形動態從0放大到原尺寸又縮小到消失,然後翻轉放大後又縮小如此迴圈。
和教程5一樣的效果: