OpenGLES demo - 12. 材质贴图 Texture Mapping

来源:互联网 发布:淘宝店铺动态 编辑:程序博客网 时间:2024/05/17 04:55

原创文章,转帖请注明链接 http://blog.csdn.net/hoytgm/article/details/37992167

终于开始讲这个比较高档的话题了,材质贴图(Texture Mapping),也叫做贴图。贴图功能可是图形学中的一个重大突破,无论是Direct3D,OpenGL还是OpenGLES,没有贴图功能,效果就是一个渣啊,因为怎么做也没法做到很真实的效果。在Edwin Catmull首次引入了材质贴图后,这个功能受到了图形学的热烈欢迎,它能用很简单的方式把场景或者物体变得十分真实。


先说说贴图的方法吧,简单来讲,就是把一张准备好的图片,按照一定规则覆盖在一个模型物体上。像之前我们画的三角形,正方形或者立方体,都是一个单一的颜色,如果我们有贴图的话,颜色信息就会丰富的多。我们以一张矩形的图贴到一个矩形上为例来讲解贴图的规则吧。


做材质贴图,需要指明模型表面的像素去取贴图中的哪个部位,这就是贴图坐标的作用了。以一个矩形贴图为例,它的贴图坐标范围是0.0 ~ 1.0。所以我们如果需要把一个矩形贴图完整的贴在一个128x128的矩形物体上,那么对应方式如下



然后就是过滤方式(Filter),OpenGLES中提供了两种最基本的方式,GL_NEAREST,GL_LINEAR,也就是最近点采样和线性采样。先说图片吧,我们把图片中的一个像素称作Texel。一般用浮点或定点数来表示模型光栅化后的像素位置,而这个值需要对应到图片中的Texel。但是由于Texel是整形,所以像素和Texel不一定是刚好对齐或对应。这个时候就需要用过滤方式来决定使用哪一个Texel。如果是最近点采样,很明显,就是浮点或定点数直接转成最接近的Texel的位置。如果是线性采样,那么就需要把像素位置对应的前后位置的Texel做一个线性插值。


过滤方式还区分了MagFilter和MinFilter,也就是放大和缩小的过滤方式。MinFilter还有一个mipmap,而mipmap也需要设置过滤方式,这样下来的话,组合就比较多了。。。Mipmap以后再作为一个单独的话题讲吧,这次就不再涉及mipmap的话题了。


另外一个规则就是Wrap方式。OpenGLES中提供了三种方式,GL_REPEAT,GL_CLAMP_TO_EDGE和GL_MIRRORED_REPEAT,他们分别指重复,采用最边缘值和镜像重复。刚才说了一个矩形贴图的贴图坐标是0.0 ~ 1.0,那如果超过1.0或者小于0.0怎么办?这三个方式就是处理这种情况的,他们产生的效果如下面三个图



好了,说了这么多,下面看看代码里面怎么实现。为了拿到贴图的数据,我们需要写一个函数来从贴图文件里面获取数据,在这里最好最直接的方式就是用Objective-C来直接读取图像文件并获取里面的原始数据,这里我们读取一个“wood.png”的图片,并把原始数据存入我们的pixels指针。

    UIImage *img = [UIImage imageNamed:@"wood.png"];        CFDataRef rawdata = CGDataProviderCopyData(CGImageGetDataProvider(img.CGImage));    GLuint *pixels = (GLuint *)CFDataGetBytePtr(rawdata);


对了,我们还需要把wood.png拖进工程里面,像这样



还可以得到图片的高宽,这个对后面也有用

    int width = img.size.width;    int height = img.size.height;


然后,需要在Shader里面加一个特殊的Uniform,就是sampler2D,代表这个Uniform是一个贴图。同时,在Pixel Shader里面,需要调用一个内置函数来采贴图,那就是texture2D(),它的第一个参数是贴图,第二个参数就是贴图坐标,更新之后的Vertex Shader和Fragment Shader如下:

char* vssource ="precision mediump float;\n""attribute vec4 aPosition;\n""attribute vec2 aTexCoord;\n""uniform mat4 uMvp;\n""varying vec2 vTexCoord;\n""void main() {\n""    gl_Position = uMvp*aPosition;\n""    vTexCoord = aTexCoord;\n""}\n";char* fssource ="precision mediump float;\n""uniform sampler2D uTex;\n""varying vec2 vTexCoord;\n""void main() {\n""    gl_FragColor = texture2D(uTex, vTexCoord);\n""}\n";


在程序里面拿到Shader中贴图的句柄。这里有一个概念稍微和硬件有点关系,就是贴图单元。一般一个显卡的参数描述里面都会写出该显卡有多少个贴图单元啊什么的,那如果一个显卡有4个贴图单元(OpenGLES spec中要求Fragment Shader中的Texture单元至少要支持8个),它们分别是0,1,2,3,那么刚才Shader里面的贴图应该放到哪个里面呢?glUniform1i(tex, 0),表示我们把它放在0号贴图单元上面,并且要用glActiveTexture来启用它(默认情况下,0号应该是启用的,而其他是禁用的,Spec里面怎么写忘了,空了查查)。

先拿到贴图的句柄

    uLocTex = glGetUniformLocation(program, "uTex");


配置贴图单元0
    glBindTexture(GL_TEXTURE_2D, tex);    glActiveTexture(GL_TEXTURE0);    glUniform1i(uLocTex, 0);


好了,拿到贴图数据,也写好了Shader并配置成功,下面该在程序里面来创建一个贴图对象,并绑定这个贴图对象。

    glGenTextures(1, &tex);    glBindTexture(GL_TEXTURE_2D, tex);


然后设置贴图数据和过滤方式以及Wrap模式了

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);


画一个矩形看看效果,不错吧!!



代码地址 http://download.csdn.net/detail/hoytgm/7674491

参考链接 http://open.gl/textures

0 0
原创粉丝点击