转载请说明出处: 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);  

贴图渲染之后的图片

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

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; OpenGL 按照三角形仿射变换并贴图渲染(正常渲染或离屏渲染以及异步优化)(二)标签(空格分隔):CG opengl前面我们详细介绍过OpenGL 按照三角形仿射变换并贴图渲染的过程(一),是基于OpenGL2.0的接口来实现的,但是随着OpenGL的发展,3.0版本中关于shader的使用已经日渐普及,下面给出基于shader的纹理贴图渲染的写法,基本原理和(一)是一样的,我们着重介绍shade 资源名:基于仿射变换的数字图象置乱技术 MATLAB源程序代码 资源类型:matlab项目全套源码 源码说明: 全部项目源码都是经过测试校正后百分百成功运行的,如果您下载后不能运行可联系我进行指导或者更换。 适合人群:新手及有一定经验的开发人员 1. Object or model coordinates(模型坐标系) 2. World coordinates(世界坐标系) 3. Eye (or Camera) coordinates(视坐标系) 4. Clip coordinates(裁剪坐标系) 5. Normalized device coordinates(归一化设备坐标系) 6. Wi typedef double matrix3x3 [3][3]; //?????????? matrix3x3 commatrix; const double pi=3.1415926; int w=600; int h=600; class position public: GLfloa
1. 创建纹理图像 OpenGL要求纹理的高度和宽度都必须是2的n次方大小,只有满足这个条件,这个纹理图片才是有效的。     一旦获取了像素值,我们就可以将这些数据传给OpenGL,让OpenGL生成一个纹理贴图:     glGenTextures(1,@Texture);     glBindTexture(GL_TEXTURE_2D,Texture);     glTexIm
这篇文章基于上一篇的基础上,为三角形添加纹理贴图。首先我们要解码bmp文件,然后创建纹理对象,再将bmp数据转化成2D贴图,贴到三角形上。 在Utils.h中添加所需接口: #pragma once #include "ggl.h" unsigned char* LoadFileContent(const char* path, int& filesize); GLuint CompileShader(GLenum shaderType, const char* shaderCode);
上节 现代opengl 设计入门,着色器  介绍了着色器语言GLSL, 可以绘制多彩的三角形。这节介绍更高级的着色,纹理贴图。 先做纹理贴图的基本介绍,然后介绍其中几个重要设置:纹理环绕方式,纹理过滤,多级渐远纹理。接着介绍加载与创建纹理,应用纹理。最后是代码调试说明,三个附录文件。 本文参照 https://learnopengl.com/  和 https://learnopengl-cn...
写文章不易,如果您觉得此文对您有所帮助,请帮忙点赞、评论、收藏,感谢您! 一. 仿射变换介绍:         请参考:图解图像仿射变换:https://www.cnblogs.com/wojianxin/p/12518393.html . 仿射变换 公式: 仿射变换过程,(x,y)表示原图像中的坐标,(x’,y’)表示目标图像的坐标 ↑ 三. 仿射变换——图像平移 算法: 仿射变换—图像平移算法,其中tx为在横轴上移动的距离,ty为在纵轴上移动的距离 ↑ 四. python实现仿射变换——图像平移 import cv2 import numpy as np # 图像仿射变换->图
仿射变换是指在维平面上对图像进行旋转、缩放、平移、错切等变换的操作。在Python中,可以使用OpenCV库来实现仿射变换。 下面是一个简单的示例代码,用于对一张图片进行仿射变换: ```python import cv2 import numpy as np img = cv2.imread('image.jpg') rows, cols, ch = img.shape # 定义仿射变换矩阵 pts1 = np.float32([[50, 50], [200, 50], [50, 200]]) pts2 = np.float32([[10, 100], [200, 50], [100, 250]]) M = cv2.getAffineTransform(pts1, pts2) # 进行仿射变换 dst = cv2.warpAffine(img, M, (cols, rows)) cv2.imshow('image', dst) cv2.waitKey(0) cv2.destroyAllWindows() 在上述代码中,我们首先读入一张图片,并获取其宽高和通道数。然后,我们定义了一个仿射变换矩阵,其中pts1表示原始图像中的三个点,pts2表示变换后图像中对应的三个点。通过调用cv2.getAffineTransform()函数,我们可以得到这两组点之间的仿射变换矩阵M。最后,我们使用cv2.warpAffine()函数对原始图像进行仿射变换,并将结果显示出来。 需要注意的是,在定义仿射变换矩阵时,原始图像中的三个点和变换后图像中对应的三个点需要一一对应。如果对应不准确,可能会导致变换结果不正确。