复习 OpenGL 纹理映射 总结

今天系统的复习了一下OpenGL纹理映射

主要参考资料是这两个 OpenGL 教程

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/

https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/06%20Textures/

总体来看,第二个教程更详尽一些,比第一个教程多介绍了一下 如何把 Texture2D 对象,传到 fragment shader 的 sampler2D 的 uniform 变量中。

很久以前,自己总结过OpenGL Texture2D 使用的关键步骤

opengl 使用 texture2d 关键步骤

今天再结合现在的经验补充几点。

OpenGL Texture2D 对象 的生成,其实和 一个 VBO 的生成步骤类似。

首先是 Generate 

vbo 是  glGenBuffers(1,&_vbo);

texture 是 glGenTextures(1,&_tex);

然后,需要 bind ,才能指明要操作哪个 vbo / 哪个 texture2d

vbo : glBindBuffer(_vbo);

texture: glBindTexture(_tex);

接下来,就是真正把内存中的数据,存储到缓冲区内.

vbo 使用 glBufferData()

texture 使用 glTexImage2D()

针对 VBO 来说,通常是顶点的位置数据,颜色数据,uv数据,法线数据等等模型顶点相关的数据。至于这些顶点的数据,是直接写在代码的 数组里面的,还是 从 模型文件里读取的,这些OpenGL 都不关心,只要把数据的指针准备好,用 glBufferData() 存储到vbo就好了。

相应的,对于 texture2d 来说,glTexImage2D() 是把一段表示图片每个像素颜色信息的 内存数据,存储到  texture 里。同样它也并不关心数据的来路。无论是读取图片,还是自己在内存里手写,无论是 bmp ,jpg,png ,tga 格式, 无论是否带 alpha 通道,这些都无所谓,只要有数据指针就行。

但是区别的地方在于,glTexImage2D 需要关心 颜色信息的具体格式,也就是说 它有些参数,需要知道 这段内存数据交由它之后,它要如何理解。比如是 rgb 还是 rgba ,每个像素 所占位数 等信息。

二者之间有所区别的原因是 ,vbo 里的数据,能够通过 vao 传到 shader 里,可以自己随意编写 glsl 来解析。但是 sampler2D 类型的数据,在 shader 里面 的 灵活性 就要差很多,在 shader 里面使用时,颜色信息已经通过传参数决定了,没有更多底层的细节暴露出来。所以 给 texture 一段内存数据时,参数要更加明确,让 GL 知道 图片 每个像素的颜色 在内存里 是如何排布的。

vbo ,texture2d 二者相同:数据给到 OpenGL 之后,内存里的数据就可以释放了,因为数据已经存储到了 OpenGL 的显存里 (?).所以数据指针 指向的内存,可以尽快 delete 销毁 .

说到图片数据加载到内存,最简单的图片数据格式,应该是 bmp 格式,因为它 基本上就是把每个像素的颜色值,按照顺序摆放在 文件里,所以解析起来非常方便。

这里有一篇自己以前的总结

http://blog.csdn.net/korekara88730/article/details/78304643

里面有现成的自己手写的解析 24位 bmp 格式的代码。每个教程在取图片像素数据时,都使用了不同的库。我这次复习时,使用的图片解析“库” 就是 上面链接里 ,自己手写的 解析 24 位 bmp 的 代码。它可以将图片解析为一个The24BitBmp对象,此对象包含 图片的 宽度,高度,包含颜色数据的一个 连续 内存的指针。给 OpenGL 的 glTexImage2D() 做参数 完全够用。


texture2d 已经准备好了,在使用时,有几个值得留意的地方。

1. texture2d 是一张图片,体现在 shader 里,通常是 fragment shader 里的一个 sampler2D 的 uniform 变量。形如 uniform sampler2D myTextureSampler;

那么,如何把 c++ 代码里的 GLuint texture2d 与 shader 里的 sampler2D 变量 联系起来呢 ?

方法类似  VAO .

VAO 是 VBO 数据 到 vertex shader 参数 的一个桥梁。VAO 负责用 glEnableVertexAttribArray()  来控制 shader 开启/关闭 vertex shader 里面 哪些 in 类型的变量。

而 glActiveTexture(GL_TEXTURE0 ... N ) 用于决定开启 哪些 texture2D 对象,可以给 fragment shader 调用。

默认 GL_TEXTURE0 总是开启的。

通过 glActiveTexture(GL_TEXTURE ...N) 告诉 shader 第 N 个 texture2D 开启了

然后通过 glBindTexture(GL_TEXTURE_2D,_tex) 告诉 第 N 个 texture2D 对象 究竟 对应哪个图片数据。

最后,把 shader 里面 sampler2D 的变量名, 和 C++ 里面 texture2D 对象真正对应起来, 是依靠 glUniform1i().

比如 fragment shader 里 ,有个 uniform 的 sampler2D 变量 名叫 outTexture1 ,它想要对应 C++ 里 GL_TEXURE1 这张图片,则代码 如下 :

glUniform1i(glGetUniformLocation(shaderProgram, "outTexture1"),1);


这样,通过 

glActiveTexture(GL_TEXTUREN)

glBindTexture(GL_TEXTUREN,tex)

glUniform1i(glGetUniformLocation(shaderProgram, "着色器里的变量名"),N);

这 3个函数组合的套路, 就真正的把 一张图片的颜色信息,传递到 shader 里面了。


在 fragment shader 里最终使用时,就可以简单的通过

color = texture(textureSampler,UV).rgb

来输出,显示 图片的颜色了。


uv 参数的传递方法,和  顶点位置数据,顶点颜色数据 的传递方法完全一样。

uv 就是 该点 所对应 在 图片里的2d坐标,所以是一个 vec2 类型的变量。需要通过 VBO->VAO->VertexShader->FragmentShader 这样一个完整的流程,最终传递到  fragment shader 里面去。


总结一下,如果想渲染一个 带有图片的 多边形,需要经过如下几个关键步骤 :

1. 多边形位置数据数组

2.位置数据数组 -> posVBO

3. 多边形各个顶点 uv 数据数组

4.uv数据数组 -> uvVBO

5.解析图片各像素颜色数据,存到一个 数组中

6.颜色数据数组 -> 存储到 texture2D 的 handle 中

7. 编写 vertex shader ,包含 vertex pos ,vertex uv

8. 编写 fragment shader ,包含  uv ,和 sampler2D 

9. 生成 VAO ,把 posVBO ,uvVBO 传递到 vertex shader 

10. 激活 和 使用 GL_TEXTURE0(N) ,把 texture2D 的 handle 与 fragment shader 的 sampler2D 变量对应起来

11. 做最终渲染


经过这么一大串 折腾,才能在 OpenGL 里看到一个能够显示 图片的面片。


下一步继续跟着教程走,做好 键盘鼠标控制 镜头移动,再下一步是 读取 model 文件的数据,和 手写 顶点数据说拜拜。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenGL是一种跨平台的图形库,用于渲染2D和3D图形。纹理映射是一种将纹理图像映射到物体表面的技术。贝塞尔曲面是一种由贝塞尔曲线生成的曲面。 以下是一个简单的OpenGL纹理映射的代码示例: ```cpp #include <GL/glut.h> GLuint texture; // 纹理对象ID void init() { // 加载纹理图像 unsigned char image[] = { // 纹理图像数据 }; // 创建纹理对象 glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // 设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加载纹理图像数据 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); } void display() { glClear(GL_COLOR_BUFFER_BIT); // 绑定纹理对象 glBindTexture(GL_TEXTURE_2D, texture); // 绘制物体并进行纹理映射 glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); glEnd(); glFlush(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow("OpenGL Texture Mapping"); init(); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 贝塞尔曲面的代码示例比较复杂,这里给出一个简单的二次贝塞尔曲面的代码示例: ```cpp #include <GL/glut.h> void display() { glClear(GL_COLOR_BUFFER_BIT); // 设置贝塞尔曲面控制点 GLfloat controlPoints[3][3][3] = { {{-1.5, -1.5, 4.0}, {-0.5, -1.5, 2.0}, {0.5, -1.5, -1.0}}, {{-1.5, -0.5, 1.0}, {-0.5, -0.5, 3.0}, {0.5, -0.5, 0.0}}, {{-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0}, {0.5, 0.5, 3.0}} }; // 绘制贝塞尔曲面 glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 3, 0.0, 1.0, 9, 3, &controlPoints[0][0][0]); glEnable(GL_MAP2_VERTEX_3); glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0); glEvalMesh2(GL_FILL, 0, 20, 0, 20); glFlush(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow("OpenGL Bezier Surface"); glutDisplayFunc(display); glutMainLoop(); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值