OpenGL-ES 学习(15) ----纹理

纹理简介

现实生活中,纹理(Texture) 类似于游戏中皮肤的概念,最通常的作用是装饰 3D 物体,它像贴纸一样贴在物体的表面,丰富物体的表面和细节
OpenGL-ES 开发中,纹理除了用于装饰物体表面,还可以用来作为存储数据的容器
所以在 OpenGL-ES 中,纹理实际上是一个可以被采样的复杂数据集合,是 GPU 的图像数据结构,纹理分为 2D纹理、立方图纹理和 3D纹理

  • 2D 纹理是 OpenGL-ES 中最常见和最常用的纹理形式,是一个表示图像数据的二维数组,纹理中一个单独的数据单元被称为纹素或者纹理像素
  • 立方图纹理(CubeMap)是一个由6个单独的 2D 纹理面组成的纹理,立方图纹理像素的读取是使用三维坐标(s, t, r)作为纹理坐标
  • 3D 纹理 可以看做 2D 纹理的集合,2D 纹理是 3D 纹理的一个切面,使用三维坐标对齐进行访问

纹理映射

OpenGL-ES 中,纹理映射就是通过为图元的顶点坐标指定恰当的纹理坐标,通过纹理坐标在纹理图中选定特定范围的纹理区域,最后通过纹理坐标和顶点的映射关系,将选定的纹理区域映射到指定的图元上
纹理映射也称为纹理贴图,简单说就是将纹理坐标所指定的纹理区域,映射到顶点坐标对应的渲染区域
纹理坐标是使用纹理坐标系
顶点坐标是使用渲染坐标系或者 OpenGL-ES 坐标系
纹理坐标映射.jpg
4个纹理坐标T0(0,0), T1(0,1), T2(1,1), T3(1,0) 对应的顶点坐标为V0(-1,0.5),V1(-1,-0.5),V2(1,-0.5),V3(1,0.5)
OpenGL-ES 的基本图元是以三角形为单位的,设置绘制两个三角形V0V1V2V0V2V3
当我们调整纹理坐标的顺序保持顶点坐标的顺序不变,T0T1T2T3 ==》T1T2T3T0 绘制后将会得到一个顺时针旋转 90 度为纹理贴图,所以调整纹理坐标和顶点坐标的对应关系可以实现纹理贴图的简单旋转
纹理坐标和纹理像素的映射关系如下:
纹理坐标和纹理像素.png

纹理坐标(0,0)对应纹理像素的第一个元素
纹理坐标(1,0)对应纹理像素的index = width - 1的元素
纹理坐标(0,1)对应纹理像素的index = width* (height - 1) 的元素
纹理坐标(1,1)对应纹理像素的index = width* height - 1 的元素
对应纹理像素区域内的元素都会被采样

纹理映射流程

纹理映射的一般步骤:

  • 生成纹理,编译链接着色器程序
  • 确定纹理坐标和对应的顶点坐标
  • 加载图形数据到纹理,加载顶点坐标和纹理坐标到着色器程序
  • 开始绘制

软件流程如下:
texture_rendering.jpg

对纹理采样的 FragmentShader

"#version 300 es                                     \n"
"precision mediump float;                            \n"
"in vec2 v_texCoord;                                 \n"
"layout(location = 0) out vec4 outColor;             \n"
"uniform sampler2D s_texture;                        \n"
"void main()                                         \n"
"{                                                   \n"
"  outColor = texture( s_texture, v_texCoord );      \n"
"}      

其中 texture 是内置的采样函数,v_texCoord 是顶点着色器传入的纹理坐标,根据纹理坐标进行采样,输出为4向量的 RGBA 值
uniform sampler2D s_texture 是加载后的纹理内容

示例代码:

typedef struct
{
	// Handle to a program object
	GLuint programObject;
	// Sampler location
	GLint samplerLoc;
	// Texture handle
	GLuint textureId;
} UserData;

// load texture form tga file 
static GLuint CreateSimpleTexture2D()
{
	// Texture object handle
	GLuint textureId;

	// load texture from tga file
    const char* files = "./Huskey.tga";
    int width;
    int height;
	GLubyte* pixels = esLoadTGA(NULL, files, &width, &height);
	// Use tightly packed data
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	// Generate a texture object
	glGenTextures(1, &textureId);
	// Bind the texture object
	glBindTexture(GL_TEXTURE_2D, textureId);

	// Load the texture notice width height  format must align with real size
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
	// Set the filtering mode
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	return textureId;
}

///
// Initialize the shader and program object
//
static int Init(ESContext *esContext) {
	UserData *userData = esContext->userData;
	char vShaderStr[] =
		"#version 300 es                            \n"
		"layout(location = 0) in vec4 a_position;   \n"
		"layout(location = 1) in vec2 a_texCoord;   \n"
		"out vec2 v_texCoord;                       \n"
		"void main()                                \n"
		"{                                          \n"
		"   gl_Position = a_position;               \n"
		"   v_texCoord = a_texCoord;                \n"
		"}                                          \n";

	char fShaderStr[] =
		"#version 300 es                                     \n"
		"precision mediump float;                            \n"
		"in vec2 v_texCoord;                                 \n"
		"layout(location = 0) out vec4 outColor;             \n"
		"uniform sampler2D s_texture;                        \n"
		"void main()                                         \n"
		"{                                                   \n"
//使用内建函数在Fragmanet Shader 中进行纹理采样
		"  outColor = texture( s_texture, v_texCoord );      \n"
		"}                                                   \n";
	// Load the shaders and get a linked program object
	userData->programObject = esLoadProgram(vShaderStr, fShaderStr);
	// Get the sampler location
	userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");
	// Load the texture
	userData->textureId = CreateSimpleTexture2D();

	glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
	return TRUE;
}

///
// Draw a triangle using the shader pair created in Init()
//
static void Draw(ESContext *esContext)
{
	UserData *userData = esContext->userData;
	GLfloat vVertices[] = { -0.5f,  0.5f, 0.0f,  // Position 0
		0.0f,  0.0f,        // TexCoord 0 
		-0.5f, -0.5f, 0.0f,  // Position 1
		0.0f,  1.0f,        // TexCoord 1
		0.5f, -0.5f, 0.0f,  // Position 2
		1.0f,  1.0f,        // TexCoord 2
		0.5f,  0.5f, 0.0f,  // Position 3
		1.0f,  0.0f         // TexCoord 3
	};
	GLushort indices[] = { 0, 1, 2, 0, 2, 3 };

	// Set the viewport
	glViewport(0, 0, esContext->width, esContext->height);

	// Clear the color buffer
	glClear(GL_COLOR_BUFFER_BIT);

	// Use the program object
	glUseProgram(userData->programObject);

	// Load the vertex position
	glVertexAttribPointer(0, 3, GL_FLOAT,
		GL_FALSE, 5 * sizeof(GLfloat), vVertices);
	// Load the texture coordinate
	glVertexAttribPointer(1, 2, GL_FLOAT,
		GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]);

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);

	// Bind the texture
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, userData->textureId);

	// Set the sampler texture unit to 0
	glUniform1i(userData->samplerLoc, 0);

	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}

注意其中的glTexImage2D是用于加载纹理的函数:
void glTexImage2D( GLenum target,GLint level,GLint internalFormat,GLsizei width,GLsizei height,GLint border,
GLenum format,GLenum type,const void * data);

  • GLenum target : 指定texture 类型,一般为 GL_TEXTURE_2D
  • GLint level : 一般设置为0
  • GLint internalFormat : 设置纹理的存储格式 GL_RGB
  • GLsizei width : texture 的宽度
  • GLsizei height : texture 的高度
  • GLint border : 一般设置为0
  • GLenum format : 设置纹理输入图片的存储格式 GL_RGB
  • GLenum type : 设置纹理输入图片的存储格式 GL_RGB
  • const void * data : 指向加载为纹理内容的图片的指针

实际显示效果:
texture_result.jpg

纹理的环绕和过滤方式

纹理坐标的范围通常是从(0, 0) 到 (1, 1),如果设置的坐标超出这个范围,就会有重复的效果,比如 纹理的 S 坐标设置到 2,就表示 S 轴上要重复两次采样纹理,如果 将T 坐标设置为 2,表示 T 轴上重复两次采样纹理,通过设置不同的纹理坐标,就可以产生不同的重复的效果,纹理的环绕方式也可以设置,可以设置的类型如下:
纹理环绕方式.jpg

GL_REPEAT.jpg

GL_MIRRORED_REPEAT.jpg
GL_CLAMP_TO_EDGE.jpg

设置的代码如下:

// GL_REPEAT  GL_CLAMP_TO_EDGE  GL_MIRRORED_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

纹理的过滤方式

纹理映射的原理是:光栅化之后,图元转换为一个个光栅,每个光栅化之后的 pixel 都会经过 PixelShaderPixelShader 中从纹理采样得到每个 pixel 的颜色,这里就会涉及采样方式的问题,如果图元的分辨率很大,但是关联到图元的纹理很小,就需要进行插值,纹理的过滤方式实际就是纹理到光栅的插值算法
现在只讨论最重要的两种:GL_NEARESTGL_LINEAR

  • GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是 OpenGL 默认的纹理过滤方式。当设置为 GL_NEAREST 的时候,OpenGL 会选择中心点最接近纹理坐标的那个像素,原理是最近邻插值算法

  • GL_LINEAR(也叫线性过滤,(Bilinear Filtering)它会基于纹理坐标附近的四个纹理像素的值,计算出一个插值,原理是双线性插值算法

邻近过滤算法简单,但是在放大图像的时候,会有严重的马赛克,线性过滤计算过程复杂,但是效果比邻近过滤算法好
GL_NEAREST.jpg

OpenGL ES 3.0 英文版 第1章——OpenGL ES 3.0简介   第1章简单介绍OpenGL ES,概述了OpenGL ES 3.0图形管线,讨论了OpenGL ES 3.0的设计理念和限制,最后介绍了OpenGL ES 3.0中使用的一些约定和类型。   第2章——你好,三角形:一个OpenGL ES 3.0示例   第2章介绍绘制三角形的一个简单OpenGL ES 3.0示例。我们的目的是说明OpenGL ES 3.0程序的样子,向读者介绍一些API概念,并说明如何构建和运行OpenGL ES 3.0示例程序。   第3章——EGL简介   第3章介绍EGL——为OpenGL ES 3.0创建表面和渲染上下文的API。我们说明与原生窗口系统通信、选择配置和创建EGL渲染上下文及表面的方法,传授足够多的EGL知识,你可以了解到启动OpenGL ES 3.0进行渲染所需的所有知识。   第4章——着色器和程序   着色器对象和程序对象是OpenGL ES 3.0中最基本的对象。第4章介绍创建着色器对象、编译着色器和检查编译错误的方法。这一章还说明如何创建程序对象、将着色器对象连接到程序对象以及链接最终程序对象的方法。我们讨论如何查询程序对象的信息以及加载统一变量(uniform)的方法。此外,你将学习有关源着色器和程序二进制代码之间的差别以及它们的使用方法。   第5章——OpenGL ES着色语言   第5章介绍编写着色器所需的着色语言的基础知识。这些着色语言基础知识包括变量和类型、构造器、结构、数组、统一变量、统一变量块(uniform block)和输入/输出变量。该章还描述着色语言的某些更细微的部分,例如精度限定符和不变性。   第6章——顶点属性、顶点数组和缓冲区对象   从第6章开始(到第11章为止),我们将详细介绍管线,教授设置和编程图形管线各个部分的方法。这一旅程从介绍几何形状输入图形管线的方法开始,包含了对顶点属性、顶点数组和缓冲区对象的讨论。   第7章——图元装配和光栅化   在前一章讨论几何形状输入图形管线的方法之后,第7章将讨论几何形状如何装配成图元,介绍OpenGL ES 3.0中所有可用的图元类型,包括点精灵、直线、三角形、三角形条带和三角扇形。此外,我们还说明了在顶点上进行坐标变换的方法,并简单介绍了OpenGL ES 3.0管线的光栅化阶段。   第8章——顶点着色器   我们所介绍的管线的下一部分是顶点着色器。第8章概述了顶点着色器如何融入管线以及OpenGL ES 着色语言中可用于顶点着色器的特殊变量,介绍了多个顶点着色器的示例,包括逐像素照明和蒙皮(skinning)。我们还给出了用顶点着色器实现OpenGL ES 1.0(和1.1)固定功能管线的示例。   第9章——纹理   第9章开始介绍片段着色器,描述OpenGL ES 3.0中所有可用的纹理功能。该章提供了创建纹理、加载纹理数据以及纹理渲染的细节,描述了纹理包装模式、纹理过滤、纹理格式、压缩纹理、采样器对象、不可变纹理、像素解包缓冲区对象和Mip贴图。该章介绍了OpenGL ES 3.0支持的所有纹理类型:2D纹理、立方图、2D纹理数组和3D纹理。   第10章——片段着色器   第9章的重点是如何在片段着色器中使用纹理,第10章介绍编写片段着色器所需知道的其他知识。该章概述了片段着色器和所有可用的特殊内建变量,还演示了用片段着色器实现OpenGL ES 1.1中所有固定功能技术的方法。多重纹理、雾化、Alpha测试和用户裁剪平面的例子都使用片段着色器实现。   第11章——片段操作   第11章讨论可以适用于整个帧缓冲区或者在OpenGL ES 3.0片段管线中执行片段着色器后适用于单个片段的操作。这些操作包括剪裁测试、模板测试、深度测试、多重采样、混合和抖动。本章介绍OpenGL ES 3.0图形管线的最后阶段。   第12章——帧缓冲区对象   第12章讨论使用帧缓冲区对象渲染屏幕外表面。帧缓冲区对象有多种用法,最常见的是渲染到一个纹理。本章提供API帧缓冲区对象部分的完整概述。理解帧缓冲区对象对于实现许多高级特效(如反射、阴影贴图和后处理)至关重要。   第13章——同步对象和栅栏   第13章概述同步对象和栅栏,它们是在OpenGL ES 3.0主机应用和GPU执行中同步的有效图元。我们讨论同步对象和栅栏的使用方法,并以一个示例作为结束。   第14章——OpenGL ES 3.0高级编程   第14章是核心章节,将本书介绍的许多主题串联在一起。我们已经选择了高级渲染技术的一个样本,并展示了实现这些功能的示例。该章包含使用法线贴图的逐像素照明、环境贴图、粒子系统、图像后处理、程序纹理、阴影贴图、地形渲染
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值