OpenGL 按照三角形仿射变换并贴图渲染(基于shader)(二)
2017-08-28 19:48
501 查看
标签(空格分隔):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,那么纹素在纹理中的坐标为:
通常的约定是使用U和V作为纹理空间中的轴线(纹理坐标),U对应于2D坐标系的X轴,V对应于Y轴。在OpenGL中对UV轴上的值的处理方式为:在U轴上从左往右递增,V轴上从下往上递增(原点在左下角)。看下面的示意图:
![](http://7xl3j2.com1.z0.glb.clouddn.com/17-8-12/35732817.jpg)
纹理坐标作为核心属性一样固定到了顶点上(图示中的坐标都是纹理坐标),不会因为变换而发生任何错位变化。在对纹理坐标进行插值时多数的像素可以获得原图片中对应相同的纹理坐标(因为它们相对于顶点的相对位置相同),并且当三角形翻转时贴在三角形上面的纹理贴图也会跟着翻转。
也就是说,当三角形图元旋转、拉伸或者挤压时,纹理贴图会不断地跟着作相应的变换。
设置窗口缓冲区(即默认和窗口绑定的的FBO)对应的待映射的三角形区域以及对应的纹理三角形区域:
这里我们借助索引缓冲区进行三角形顶点的指定:
渲染函数:
顶点着色器:
片段着色器:
效果图:
原始图片
![](http://7xl3j2.com1.z0.glb.clouddn.com/17-8-28/78695956.jpg)
贴图渲染之后的图片
![](http://7xl3j2.com1.z0.glb.clouddn.com/17-8-28/61453710.jpg)
完整代码(有多余代码,仅供参考):
转载请说明出处: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轴上从下往上递增(原点在左下角)。看下面的示意图:
![](http://7xl3j2.com1.z0.glb.clouddn.com/17-8-12/35732817.jpg)
纹理坐标作为核心属性一样固定到了顶点上(图示中的坐标都是纹理坐标),不会因为变换而发生任何错位变化。在对纹理坐标进行插值时多数的像素可以获得原图片中对应相同的纹理坐标(因为它们相对于顶点的相对位置相同),并且当三角形翻转时贴在三角形上面的纹理贴图也会跟着翻转。
也就是说,当三角形图元旋转、拉伸或者挤压时,纹理贴图会不断地跟着作相应的变换。
设置窗口缓冲区(即默认和窗口绑定的的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); }
效果图:
原始图片
![](http://7xl3j2.com1.z0.glb.clouddn.com/17-8-28/78695956.jpg)
贴图渲染之后的图片
![](http://7xl3j2.com1.z0.glb.clouddn.com/17-8-28/61453710.jpg)
完整代码(有多余代码,仅供参考):
/* 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; }
相关文章推荐
- OpenGL 按照三角形仿射变换并贴图渲染(正常渲染或离屏渲染以及异步优化)(一)
- [置顶] 基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像
- qt5_qml_Opengl_shader 第二弹----------------------纹理贴图(RGB)
- 【OpenGL4.0】GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形 【转】
- 基于过程渲染的小地图shader
- OpenGL 顶点数组与缓冲区对象进行渲染(含纹理贴图)
- qt5_qml_Opengl_shader 第三弹-----------------------纹理贴图(YUV)
- qt5_qml_Opengl_shader 第四弹----------------------纹理贴图(YUV视频循环播放)
- 【OpenGL4.0】GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形
- unity shader:基于物理渲染
- 基于OpenGL的渲染引擎
- 基于OpenGL的渲染引擎
- 基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之Shader准备(7)
- 调试 OpenGL 4.2 和 Shader - 基于 NVIDIA Nsight Visual Studio 3.2
- 在 opengl 中画出三角形并用 shader 改变颜色
- 基于OpenGL的渲染引擎
- 基于MFC的OpenGL shader编程--我的第一个shader
- OpenGL基于帧缓存FBO的离屏渲染
- 基于Android的OpenGL—在 GLSurfaceView上绘制三角形和使用投影和相机视图
- Android基于OpenGL在GLSurfaceView上绘制三角形及使用投影和相机视图方法示例