OpenGL 按照三角形仿射变换并贴图渲染(基于shader)(二)

标签(空格分隔):CG opengl


转载请说明出处:http://blog.csdn.net/hust_sheng/article/details/77652864

前面我们详细介绍过OpenGL 按照三角形仿射变换并贴图渲染的过程(一),是基于OpenGL2.0的接口来实现的,但是随着OpenGL的发展,3.0版本中关于shader的使用已经日渐普及,下面给出基于shader的纹理贴图渲染的写法,基本原理和(一)是一样的,我们着重介绍shader的代码部分。


首先介绍一下纹理贴图的概念:

为了实现纹理贴图我们需要做三件事:(1)将一张贴图加载到OpenGL中;(2)提供纹理坐标和顶点(将纹理对应匹配到顶点上);(3)并使用纹理坐标从纹理中进行取样操作取得像素颜色。

由于三角形会被缩放、旋转、平移变换导致最后会以不同的结果投影显示到屏幕上,而且由于camera的不同操作看上去也会很不一样。GPU要做的就是让纹理紧跟三角形图元顶点的移动使其看上去真实(如果纹理看上去明显游离在三角形上产生错位就不真实了)。为实现这个效果开发者需要为每个顶点提供一系列纹理坐标。在GPU光栅化三角形阶段,会对纹理坐标进行插值计算并覆盖到整个三角形面上,并且在片段着色器中开发者要将这些坐标跟纹理进行匹配。这个操作叫做‘取样’,取样的结果叫做‘纹素’(纹理中的一个像素)。纹素通常包含一个颜色值用于画屏幕上对应的一个像素。

OpenGL支持几种不同类型的纹理:1D,2D,3D,立方体等等,可以应用于不同的技术中。现在这里就先只使用2D纹理。一张2D纹理可以在某些特殊限制下有任意宽度和高度的,宽度和高度相乘可以计算得到纹素的数量。那么如何确定纹理坐标和顶点呢?事实上这里的坐标并不是在纹理中纹素的坐标,那样局限性太大了,因为这样如果要用一张宽度高度不一样的纹理替换一张纹理的话我们得更新所有顶点的坐标来匹配新的纹理图片。
理想的方案是要能够在不改变纹理坐标的情况下随意更换纹理贴图。纹理坐标是定义在‘纹理空间’的,也就是定义在单位化的[0,1]范围内。所以说纹理坐标事实上是个分数,纹理的宽度高度乘以相应的比例分数就可以算出纹素在纹理中的坐标。例如:如果纹理坐标是[0.5,0.1]并且纹理的宽度为320,高度为200,那么纹素在纹理中的坐标为:
(160,20) 即(0.5 * 320 = 160 和 0.1 * 200 = 20)

通常的约定是使用U和V作为纹理空间中的轴线(纹理坐标),U对应于2D坐标系的X轴,V对应于Y轴。在OpenGL中对UV轴上的值的处理方式为:在U轴上从左往右递增,V轴上从下往上递增(原点在左下角)。看下面的示意图:

纹理坐标作为核心属性一样固定到了顶点上(图示中的坐标都是纹理坐标),不会因为变换而发生任何错位变化。在对纹理坐标进行插值时多数的像素可以获得原图片中对应相同的纹理坐标(因为它们相对于顶点的相对位置相同),并且当三角形翻转时贴在三角形上面的纹理贴图也会跟着翻转。
也就是说,当三角形图元旋转、拉伸或者挤压时,纹理贴图会不断地跟着作相应的变换。


设置窗口缓冲区(即默认和窗口绑定的的FBO)对应的待映射的三角形区域以及对应的纹理三角形区域:

struct Vertex
{
	Vector3f m_pos;         // 顶点坐标
	Vector2f m_tex;         // 纹理坐标

	Vertex() {}

	Vertex(Vector3f pos, Vector2f tex)
	{
		m_pos = pos;
		m_tex = tex;
	}
};
...
static void CreateVertexBuffer()
{
    // 可以看出,设置了4个三角形的对应关系(窗口FBO与纹理之间的对应)
    // 前者是三角形顶点坐标,后者是对应的纹理坐标
	Vertex Vertices[12] = { Vertex(Vector3f(-1.0f, 1.0f, 0), Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(0.0f, 0.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(-1.0f, 0.0f, 0),  Vector2f(0.0f, 0.0f)),

							Vertex(Vector3f(0.0f, 1.0f, 0),  Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(1.0f, 0.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(0.0f, 0.0f, 0),  Vector2f(1.0f, 1.0f)),

							Vertex(Vector3f(-1.0f, 0.0f, 0), Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(0.0f, -1.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(-1.0f, -1.0f, 0), Vector2f(0.0f, 0.0f)),

							Vertex(Vector3f(0.0f, 0.0f, 0),  Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(1.0f, -1.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(0.0f, -1.0f, 0), Vector2f(0.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, 1, 2,
							   3, 4, 5,
							   6, 7, 8,
							   9, 10, 11};

	glGenBuffers(1, &IBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}

渲染函数:

static void RenderSceneCB()
{
	pGameCamera->OnRender();

	glClear(GL_COLOR_BUFFER_BIT);

	static float Scale = 0.0f;

	Scale += 0.1f;
    ...

	//glUniformMatrix4fv(gWVPLocation, 1, GL_TRUE, (const GLfloat*)p.GetWVPTrans());

	glEnableVertexAttribArray(0);        
	glEnableVertexAttribArray(1);       
	glBindBuffer(GL_ARRAY_BUFFER, VBO); // 再次绑定顶点buffer
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); // 再次绑定索引buffer
	pTexture->Bind(GL_TEXTURE0);                
	glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);       // 四个三角形,12个图元(顶点)

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);

	glutSwapBuffers();
}

顶点着色器:

#version 410 core

layout(location = 0) in vec3 Position;
layout(location = 1) in vec2 TexCoord;


out vec2 TexCoord0;     // 传递至片段着色器(光栅器会在这个过程进行插值操作)

void main()
{
	gl_Position = vec4(Position, 1.0);
	TexCoord0 = TexCoord;
}

片段着色器:

#version 410 core
   
in vec2 TexCoord0;      // 收到顶点着色器经光栅器插值之后的纹理信息

out vec4 FragColor;

uniform sampler2D gSampler;

void main()
{
    // 通过采样器gSampler以及内置的texture2D函数进行纹理的提取
    // TexCoord0.xy就表示对应纹理中的点坐标
    FragColor = texture2D(gSampler, TexCoord0.xy);  
}

效果图:

原始图片

贴图渲染之后的图片


完整代码(有多余代码,仅供参考):

/*

Copyright 2011 Etay Meiri

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Tutorial 16 - Basic Texture Mapping
*/

#include <stdio.h>
#include <string.h>

#include <math.h>
#include <GL/glew.h>
#include <GL/freeglut.h>

#include "ogldev_util.h"
#include "ogldev_glut_backend.h"
#include "ogldev_pipeline.h"
#include "ogldev_camera.h"
#include "ogldev_texture.h"

#define WINDOW_WIDTH  1280
#define WINDOW_HEIGHT 1024

struct Vertex
{
	Vector3f m_pos;
	Vector2f m_tex;

	Vertex() {}

	Vertex(Vector3f pos, Vector2f tex)
	{
		m_pos = pos;
		m_tex = tex;
	}
};


GLuint VBO;
GLuint IBO;
//GLuint gWVPLocation;
GLuint gSampler;
Texture* pTexture = NULL;
Camera* pGameCamera = NULL;
PersProjInfo gPersProjInfo;

const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";

static void RenderSceneCB()
{
	pGameCamera->OnRender();

	glClear(GL_COLOR_BUFFER_BIT);

	static float Scale = 0.0f;

	Scale += 0.1f;

	Pipeline p;
	p.Rotate(0.0f, Scale, 0.0f);
	p.WorldPos(0.0f, 0.0f, 3.0f);
	p.SetCamera(*pGameCamera);
	p.SetPerspectiveProj(gPersProjInfo);

	//glUniformMatrix4fv(gWVPLocation, 1, GL_TRUE, (const GLfloat*)p.GetWVPTrans());

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	pTexture->Bind(GL_TEXTURE0);
	glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);

	glutSwapBuffers();
}


static void SpecialKeyboardCB(int Key, int x, int y)
{
	OGLDEV_KEY OgldevKey = GLUTKeyToOGLDEVKey(Key);
	pGameCamera->OnKeyboard(OgldevKey);
}


static void KeyboardCB(unsigned char Key, int x, int y)
{
	switch (Key) {
	case 'q':
		glutLeaveMainLoop();
	}
}


static void PassiveMouseCB(int x, int y)
{
	pGameCamera->OnMouse(x, y);
}


static void InitializeGlutCallbacks()
{
	glutDisplayFunc(RenderSceneCB);
	glutIdleFunc(RenderSceneCB);
	glutSpecialFunc(SpecialKeyboardCB);
	glutPassiveMotionFunc(PassiveMouseCB);
	glutKeyboardFunc(KeyboardCB);
}


static void CreateVertexBuffer()
{
	Vertex Vertices[12] = { Vertex(Vector3f(-1.0f, 1.0f, 0), Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(0.0f, 0.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(-1.0f, 0.0f, 0),  Vector2f(0.0f, 0.0f)),

							Vertex(Vector3f(0.0f, 1.0f, 0),  Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(1.0f, 0.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(0.0f, 0.0f, 0),  Vector2f(1.0f, 1.0f)),

							Vertex(Vector3f(-1.0f, 0.0f, 0), Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(0.0f, -1.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(-1.0f, -1.0f, 0), Vector2f(0.0f, 0.0f)),

							Vertex(Vector3f(0.0f, 0.0f, 0),  Vector2f(0.0f, 1.0f)),
							Vertex(Vector3f(1.0f, -1.0f, 0), Vector2f(1.0f, 0.0f)),
							Vertex(Vector3f(0.0f, -1.0f, 0), Vector2f(0.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, 1, 2,
							   3, 4, 5,
							   6, 7, 8,
							   9, 10, 11};

	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(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);

	//gWVPLocation = glGetUniformLocation(ShaderProgram, "gWVP");
	//assert(gWVPLocation != 0xFFFFFFFF);
	gSampler = glGetUniformLocation(ShaderProgram, "gSampler");
	assert(gSampler != 0xFFFFFFFF);
}


int main(int argc, char** argv)
{
	//    Magick::InitializeMagick(*argv);    
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("Tutorial 16");
	glutGameModeString("1280x1024@32");
	//  glutEnterGameMode();

	InitializeGlutCallbacks();

	pGameCamera = new Camera(WINDOW_WIDTH, WINDOW_HEIGHT);

	// 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);
	glFrontFace(GL_CW);
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);

	CreateVertexBuffer();
	CreateIndexBuffer();

	CompileShaders();

	glUniform1i(gSampler, 0);

	pTexture = new Texture(GL_TEXTURE_2D, "./Content/test.png");

	if (!pTexture->Load()) {
		return 1;
	}

	gPersProjInfo.FOV = 60.0f;
	gPersProjInfo.Height = WINDOW_HEIGHT;
	gPersProjInfo.Width = WINDOW_WIDTH;
	gPersProjInfo.zNear = 1.0f;
	gPersProjInfo.zFar = 100.0f;

	glutMainLoop();

	return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值