OGL(教程11)——连接变换

原文地址:http://ogldev.atspace.co.uk/www/tutorial11/tutorial11.html

背景知识:
在之前的几节中,我们开发了几个变换,用来移动3D世界中的物体。我们还需要学习其他的一些知识,比如摄像机的控制、透视变换。但是你可能已经猜到,连接变换还是很有必要的。在多数情况下,你想缩放3D世界中的物体,让它沿着某个方向旋转,把它移动到任意个位置。目前,我们都是每次执行一次变换。为了执行执行上面的一系列变换,我们需要把一个变换乘以另外一个变换,最终得到一个矩阵。我们这些变换矩阵提供给shader,在shader中执行所有的乘法。这个很低效,因为所有的顶点都是使用同一个矩阵。很幸运的是,线性变换提供了一系列的规则,使其简化操作。比如:

Mn * Mn-1 * ... * M0 * V = (Mn* Mn-1 * ... * M0) * V

如果你计算出:

N = Mn * Mn-1 * ... * M0

这样就有:

Mn * Mn-1 * ... * M0 * V = N * V

这样我们就可以只计算一次N,然后把这个值传入到shader使用。
我们怎样得到这个N呢?第一件你需要记得的事情,向量都是左乘以矩阵,向量放在所有矩阵的的最右边,这里是M0。然后此向量依次经过M0,M1,。。。的处理。在3D世界中,你通常需要先缩放物体、然后旋转、然后是平移。然后再执行相机的变换,到最终映射到2D。如果我们先旋转,然后再平移,看看会发生什么:
在这里插入图片描述

而如果先执行平移再旋转:
在这里插入图片描述

你可以注意到,当你先平移,就很难设置物体在世界坐标中的位置。因为你把它从移动远离原点了,然后再绕着原点旋转,那就意味着你又平移了它。第二种变换方式是需要避免的。通过先旋转然后平移,打断了两个操作之间的依赖性。

现在我们开始处理了很多的变换,我们就要不需要再渲染函数中直接更新矩阵了。这种方式不会缩放,而且会易产生错误。管线类被引入,这个类隐藏了很多矩阵操作的细节。

代码注释:

#define ToRadian(x) ((x) * M_PI / 180.0f)
#define ToDegree(x) ((x) * 180.0f / M_PI)

定义了两个宏,用于在弧度和角度之间做变换。

inline Matrix4f operator*(const Matrix4f& Right) const
{
    Matrix4f Ret;
    for (unsigned int i = 0 ; i < 4 ; i++) {
       for (unsigned int j = 0 ; j < 4 ; j++) {
           Ret.m[i][j] = m[i][0] * Right.m[0][j] +
                         m[i][1] * Right.m[1][j] +
                         m[i][2] * Right.m[2][j] +
                         m[i][3] * Right.m[3][j];
       }
    }

    return Ret;
}

编写矩阵类,用以实现矩阵的计算。

class Pipeline
{
    public:
       Pipeline() { ... }
       void Scale(float ScaleX, float ScaleY, float ScaleZ) { ... }
       void WorldPos(float x, float y, float z) { ... }
       void Rotate(float RotateX, float RotateY, float RotateZ) { ... }
       const Matrix4f* GetTrans();
    private:
       Vector3f m_scale;
       Vector3f m_worldPos;
       Vector3f m_rotateInfo;
       Matrix4f m_transformation;
};

管线类抽象了变换的细节。这里有三个私有成员,用以存放缩放、位置、旋转。

const Matrix4f* Pipeline::GetTrans()
{
    Matrix4f ScaleTrans, RotateTrans, TranslationTrans;
    InitScaleTransform(ScaleTrans);
    InitRotateTransform(RotateTrans);
    InitTranslationTransform(TranslationTrans);
    m_transformation = TranslationTrans * RotateTrans * ScaleTrans;
    return &m_transformation;
}

这个函数使用私有的方法,提倡不同的变换。在下一节,我们会扩展它,用来处理摄像机的控制和透视变换。

Pipeline p;
p.Scale(sinf(Scale * 0.1f), sinf(Scale * 0.1f), sinf(Scale * 0.1f));
p.WorldPos(sinf(Scale), 0.0f, 0.0f);
p.Rotate(sinf(Scale) * 90.0f, sinf(Scale) * 90.0f, sinf(Scale) * 90.0f);
glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());

代码清单:
math_3d.h

#ifndef MATH_3D_H
#define	MATH_3D_H
#include <math.h>
const float M_PI = 3.14f;

#define ToRadian(x) ((x) * M_PI / 180.0f)
#define ToDegree(x) ((x) * 180.0f / M_PI)

struct Vector3f
{
	float x;
	float y;
	float z;
	Vector3f()
	{
	}
	Vector3f(float _x, float _y, float _z)
	{
		x = _x;
		y = _y;
		z = _z;
	}
};
class Matrix4f
{
public:
	float m[4][4];
	Matrix4f()
	{
	}
	inline void InitIdentity()
	{
		m[0][0] = 1.0f; m[0][1] = 0.0f; m[0][2] = 0.0f; m[0][3] = 0.0f;
		m[1][0] = 0.0f; m[1][1] = 1.0f; m[1][2] = 0.0f; m[1][3] = 0.0f;
		m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = 1.0f; m[2][3] = 0.0f;
		m[3][0] = 0.0f; m[3][1] = 0.0f; m[3][2] = 0.0f; m[3][3] = 1.0f;
	}
	inline Matrix4f operator*(const Matrix4f& Right) const
	{
		Matrix4f Ret;
		for (unsigned int i = 0; i < 4; i++) {
			for (unsigned int j = 0; j < 4; j++) {
				Ret.m[i][j] = m[i][0] * Right.m[0][j] +
					m[i][1] * Right.m[1][j] +
					m[i][2] * Right.m[2][j] +
					m[i][3] * Right.m[3][j];
			}
		}
		return Ret;
	}
};
#endif	/* MATH_3D_H */

pipeline.h

#ifndef PIPELINE_H
#define	PIPELINE_H
#include "math_3d.h"

class Pipeline
{
public:
	Pipeline()
	{
		m_scale = Vector3f(1.0f, 1.0f, 1.0f);
		m_worldPos = Vector3f(0.0f, 0.0f, 0.0f);
		m_rotateInfo = Vector3f(0.0f, 0.0f, 0.0f);
	}
	void Scale(float ScaleX, float ScaleY, float ScaleZ)
	{
		m_scale.x = ScaleX;
		m_scale.y = ScaleY;
		m_scale.z = ScaleZ;
	}
	void WorldPos(float x, float y, float z)
	{
		m_worldPos.x = x;
		m_worldPos.y = y;
		m_worldPos.z = z;
	}
	void Rotate(float RotateX, float RotateY, float RotateZ)
	{
		m_rotateInfo.x = RotateX;
		m_rotateInfo.y = RotateY;
		m_rotateInfo.z = RotateZ;
	}
	const Matrix4f* GetTrans();
private:
	void InitScaleTransform(Matrix4f& m) const;
	void InitRotateTransform(Matrix4f& m) const;
	void InitTranslationTransform(Matrix4f& m) const;
	Vector3f m_scale;
	Vector3f m_worldPos;
	Vector3f m_rotateInfo;
	Matrix4f m_transformation;
};
#endif	/* PIPELINE_H */

pipeline.cpp

#include "pipeline.h"
void Pipeline::InitScaleTransform(Matrix4f& m) const
{
	m.m[0][0] = m_scale.x; m.m[0][1] = 0.0f; m.m[0][2] = 0.0f; m.m[0][3] = 0.0f;
	m.m[1][0] = 0.0f; m.m[1][1] = m_scale.y; m.m[1][2] = 0.0f; m.m[1][3] = 0.0f;
	m.m[2][0] = 0.0f; m.m[2][1] = 0.0f; m.m[2][2] = m_scale.z; m.m[2][3] = 0.0f;
	m.m[3][0] = 0.0f; m.m[3][1] = 0.0f; m.m[3][2] = 0.0f; m.m[3][3] = 1.0f;
}
void Pipeline::InitRotateTransform(Matrix4f& m) const
{
	Matrix4f rx, ry, rz;
	const float x = ToRadian(m_rotateInfo.x);
	const float y = ToRadian(m_rotateInfo.y);
	const float z = ToRadian(m_rotateInfo.z);
	rx.m[0][0] = 1.0f; rx.m[0][1] = 0.0f; rx.m[0][2] = 0.0f; rx.m[0][3] = 0.0f;
	rx.m[1][0] = 0.0f; rx.m[1][1] = cosf(x); rx.m[1][2] = -sinf(x); rx.m[1][3] = 0.0f;
	rx.m[2][0] = 0.0f; rx.m[2][1] = sinf(x); rx.m[2][2] = cosf(x); rx.m[2][3] = 0.0f;
	rx.m[3][0] = 0.0f; rx.m[3][1] = 0.0f; rx.m[3][2] = 0.0f; rx.m[3][3] = 1.0f;
	ry.m[0][0] = cosf(y); ry.m[0][1] = 0.0f; ry.m[0][2] = -sinf(y); ry.m[0][3] = 0.0f;
	ry.m[1][0] = 0.0f; ry.m[1][1] = 1.0f; ry.m[1][2] = 0.0f; ry.m[1][3] = 0.0f;
	ry.m[2][0] = sinf(y); ry.m[2][1] = 0.0f; ry.m[2][2] = cosf(y); ry.m[2][3] = 0.0f;
	ry.m[3][0] = 0.0f; ry.m[3][1] = 0.0f; ry.m[3][2] = 0.0f; ry.m[3][3] = 1.0f;
	rz.m[0][0] = cosf(z); rz.m[0][1] = -sinf(z); rz.m[0][2] = 0.0f; rz.m[0][3] = 0.0f;
	rz.m[1][0] = sinf(z); rz.m[1][1] = cosf(z); rz.m[1][2] = 0.0f; rz.m[1][3] = 0.0f;
	rz.m[2][0] = 0.0f; rz.m[2][1] = 0.0f; rz.m[2][2] = 1.0f; rz.m[2][3] = 0.0f;
	rz.m[3][0] = 0.0f; rz.m[3][1] = 0.0f; rz.m[3][2] = 0.0f; rz.m[3][3] = 1.0f;
	m = rz * ry * rx;
}
void Pipeline::InitTranslationTransform(Matrix4f& m) const
{
	m.m[0][0] = 1.0f; m.m[0][1] = 0.0f; m.m[0][2] = 0.0f; m.m[0][3] = m_worldPos.x;
	m.m[1][0] = 0.0f; m.m[1][1] = 1.0f; m.m[1][2] = 0.0f; m.m[1][3] = m_worldPos.y;
	m.m[2][0] = 0.0f; m.m[2][1] = 0.0f; m.m[2][2] = 1.0f; m.m[2][3] = m_worldPos.z;
	m.m[3][0] = 0.0f; m.m[3][1] = 0.0f; m.m[3][2] = 0.0f; m.m[3][3] = 1.0f;
}
const Matrix4f* Pipeline::GetTrans()
{
	Matrix4f ScaleTrans, RotateTrans, TranslationTrans;
	InitScaleTransform(ScaleTrans);
	InitRotateTransform(RotateTrans);
	InitTranslationTransform(TranslationTrans);
	m_transformation = TranslationTrans * RotateTrans * ScaleTrans;
	return &m_transformation;
}

GLUT.cpp

// GLUT.cpp : 定义控制台应用程序的入口点。
//

#include <stdio.h>
#include <glew.h>
#include <freeglut.h>
#include <assert.h>
#include <math.h>
#include "math_3d.h"
#include "pipeline.h"


#pragma comment( lib, "glew32d.lib" )

GLuint VBO;
GLuint IBO;
GLuint gWorldLocation;
static const char* pVS = " \n\
#version 330 \n\
\n\
layout (location = 0) in vec3 Position; \n\
\n\
uniform mat4 gWorld; \n\
\n\
out vec4 Color; \n\
\n\
void main() \n\
{ \n\
gl_Position = gWorld * vec4(Position, 1.0); \n\
Color = vec4(clamp(Position, 0.0, 1.0), 1.0); \n\
}";
static const char* pFS = " \n\
#version 330 \n\
\n\
in vec4 Color; \n\
\n\
out vec4 FragColor; \n\
\n\
void main() \n\
{ \n\
FragColor = Color; \n\
}";
static void RenderSceneCB()
{
	glClear(GL_COLOR_BUFFER_BIT);
	static float Scale = 0.0f;
	Scale += 0.001f;
	Pipeline p;
	p.Scale(sinf(Scale * 0.1f), sinf(Scale * 0.1f), sinf(Scale * 0.1f));
	p.WorldPos(sinf(Scale), 0.0f, 0.0f);
	p.Rotate(sinf(Scale) * 90.0f, sinf(Scale) * 90.0f, sinf(Scale) * 90.0f);
	glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
	glDisableVertexAttribArray(0);
	glutSwapBuffers();
}
static void InitializeGlutCallbacks()
{
	glutDisplayFunc(RenderSceneCB);
	glutIdleFunc(RenderSceneCB);
}
static void CreateVertexBuffer()
{
	Vector3f Vertices[4];
	Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
	Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);
	Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);
	Vertices[3] = 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 CreateIndexBuffer()
{
	unsigned int Indices[] = { 0, 3, 1,
		1, 3, 2,
		2, 3, 0,
		0, 2, 1 };
	glGenBuffers(1, &IBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, 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(0);
	}
	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);
	}
	AddShader(ShaderProgram, pVS, GL_VERTEX_SHADER);
	AddShader(ShaderProgram, pFS, 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 11");
	InitializeGlutCallbacks();
	// Must be done after glut is initialized!
	GLenum res = glewInit();
	if (res != GLEW_OK) {
		fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
		return 1;
	}
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	CreateVertexBuffer();
	CreateIndexBuffer();
	CompileShaders();
	glutMainLoop();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值