es纹理 ios opengl
2017-11-29 11:12:00 weixin_33753845 阅读数 20

OpenGL ES on iOS --- 2D纹理

简介

纹理是用来丰富我们绘制物体细节的,它可以是一张2D图片(除了图像外,纹理也被用来存储大量数据,传递到着色器上),就像贴图一样贴在绘制的物体上.

纹理属性

纹理坐标

为了将纹理映射到绘制的物体上,我们需要指定 某个顶点对应着 纹理的那个位置. 通过纹理坐标,标明顶点从纹理图像那一部分采样,之后在图形的其它片段进行片段插值.

纹理坐标系和顶点坐标系有所不同,顶点坐标系 (0,0)点位于窗口中心. 纹理坐标系 (0,0)点位于 纹理左下角.

5472088-052b868865699040.png
顶点坐标系
5472088-17f3895cfa7a9253.png
纹理坐标系

纹理环绕方式

当我们将顶点位置设置到纹理坐标之外时,则需要设置纹理环绕方式 来显示纹理图案

环绕属性 效果
GL_REPEAT 重复纹理图案(默认)
GL_MIRRORED_REPEAT 镜像重复纹理图案
GL_CLAMP_TO_EDGE 将纹理锁定在0~1之间,超出部分重复纹理边缘图案,产生拉伸效果
GL_CLAMP_TO_BORDER 超出部分为用户指定边缘颜色
5472088-2a9f5012d322a13e.png
环绕示意图

纹理环绕函数

glTexParameteri (GLenum target, GLenum pname, GLint param);
参数:

target: 指定纹理目标,若为2D纹理 则为 GL_TEXTURE_2D
pname: 对应的纹理坐标轴(这里 s,t,r 对应 x,y,z) GL_TEXTURE_WRAP_S ,GL_TEXTURE_WRAP_T
param: 环绕方式,填入上面的方式.

对2D纹理时,必须对 s,t坐标轴都进行设置. 若是设置 GL_CLAMP_TO_BORDER 形式,则还需要额外设置 环绕颜色

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

纹理过滤

纹理坐标是不依赖于 纹理大小和分辨率的, 1就表示纹理的边缘.但是物体和和纹理大小可能不一致,这就造成了对纹理的放大和拉伸.这时如何将纹理像素映射到纹理坐标上,就需要我们设置. 该属性就是 纹理过滤.

纹理过滤有很多种,下面是最重要的两种

邻近过滤

GL_NEAREST 是默认的纹理过滤方式,它会选择距离 纹理坐标最近的像素点作为样本颜色.当纹理被放大时,会有颗粒感


5472088-734de9c931d55303.png
临近过滤

线性过滤

GL_LINEAR, 会基于当前纹理坐标附近的像素点计算一个插值,也就是附近纹理的混合色,离得越近的像素点,颜色贡献越大,当纹理被放大时,会比临近过滤更平滑.


5472088-83aef5d9947d5076.png
线性过滤
5472088-26d7ae1a3d2d10d5.png
临近过滤和线性过滤比较

过滤函数

我们需要对放大(Magnify)和缩小(Minify)的情况设置过滤效果

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多级渐远纹理

在3D世界,根据物体的远近不同,物体也存在缩放的效果,要显示不同的分辨率,若都使用相同的分辨率,一是会使物体产生不真实的效果,二是会造成内存的浪费. 在研究3D纹理时再说.~~

纹理代码

首先是设置 上下文,着色器,程序对象...的方法.

context1 = [[EAGLContext alloc] initWithAPI:(kEAGLRenderingAPIOpenGLES3)];
    BOOL isSetCOntextRight = [EAGLContext setCurrentContext:context1];
    if (!isSetCOntextRight) {
        printf("设置Context失败");
    }

    NSString* verStr = [[NSBundle mainBundle] pathForResource:@"Texture2D_Vert.glsl" ofType:nil];
    NSString* fragStr = [[NSBundle mainBundle]pathForResource:@"Texture2D_Frag.glsl" ofType:nil];

    program1 = createGLProgramFromFile(verStr.UTF8String, fragStr.UTF8String);
    glUseProgram(program1);

    //创建,绑定渲染缓存 并分配空间
    glGenRenderbuffers(1, &renderBuf1);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuf1);
    // 为 color renderbuffer 分配存储空间
    [context1 renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];

    //创建,绑定帧缓存 并分配空间
    glGenFramebuffers(1, &frameBuf1);
    // 设置为当前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuf1);
    // 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, renderBuf1);


    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);

然后是 绘制物体的代码

float verData[] = {
        // 位置              颜色                 纹理坐标
        0.5f,0.5f,0.0f,     1.0f,0.0f,0.0f,     1.0f,1.0f,
        0.5f,-0.5f,0.0f,    0.0f,1.0f,0.0f,     1.0f,0.0f,
        -0.5f,-0.5f,0.0f,   0.0f,0.0f,1.0f,     0.0f,0.0f,
        -0.5f,0.5f,0.0f,    1.0f,1.0f,0.0f,     0.0f,1.0f,
    };
    unsigned int indices[] = {
        0,1,3,
        1,2,3
    };




    glGenVertexArrays(1, &VAO1);
    glGenBuffers(1, &VBO1);
    glGenBuffers(1, &EBO1);

    glBindVertexArray(VAO1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO1);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO1);

    glBufferData(GL_ARRAY_BUFFER, sizeof(verData), verData, GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(3*sizeof(float)));
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

纹理设置~~

stbi_image 一个非常流行的单头文件图像加载库 stbi_image

    //因为使用 stbi 函数导入的图片会颠倒,所以需要将其摆正
    stbi_set_flip_vertically_on_load(true);
    
    NSString* imPath = [[NSBundle mainBundle] pathForResource:@"wall.jpg" ofType:nil];
    int width,height,nrChannels;

    //加载图片
    unsigned char * imdata = stbi_load(imPath.UTF8String, &width, &height, &nrChannels, 0);

    //创建 纹理
    unsigned int texture;
    glGenTextures(1, &texture);

    //激活纹理单元0
    glActiveTexture(GL_TEXTURE0);
    //绑定纹理
    glBindTexture(GL_TEXTURE_2D, texture);
    //将图像传入纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imdata);

    //glGenerateMipmap(GL_TEXTURE_2D);

    //设置纹理环绕和纹理过滤
    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);

    //将不用的图像释放
    stbi_image_free(imdata);

    //将纹理作为统一变量传入显存
    glUniform1i(glGetUniformLocation(program1, "outTexture"), 0);


    //这里PNG格式 通过stbi_load 拉入时 会导致产生 BGRA格式(我也不大清楚原因,懂的朋友 告告我 先O(∩_∩)O谢谢了) ,这时 图片作为纹理显示时色彩会出错.所以将其转换一蛤~
    NSString* imPath1 = [[NSBundle mainBundle] pathForResource:@"face.png" ofType:nil];
    int width1,height1,nrChannels1;
    unsigned char * imdata1 = stbi_load(imPath1.UTF8String, &width1, &height1, &nrChannels1, STBI_rgb_alpha);
    for (int i = 0; i<width1*height1; i++ ) {
        char tR = imdata1[i*4+2];
        imdata1[i*4+2] = imdata1[i*4];
        imdata1[i*4] = tR;
    }

    unsigned int texture1;

    glGenTextures(1, &texture1);
    glActiveTexture(GL_TEXTURE1);       //必须先写这个再绑定
    glBindTexture(GL_TEXTURE_2D, texture1);

    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_RGBA, width1, height1, 0, GL_RGBA, GL_UNSIGNED_BYTE, imdata1);
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(imdata1);
    
    glUniform1i(glGetUniformLocation(program1, "outTexture1"), 1);

    //绘制显示
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    [context1 presentRenderbuffer:GL_RENDERBUFFER];

片段着色器代码

#version 300 es

precision mediump float;


in vec2 outTexCoord;

uniform sampler2D outTexture;
uniform sampler2D outTexture1;

in vec3 outColor;

out vec4 FragColor;

void main()
{
FragColor = mix(texture(outTexture,outTexCoord),texture(outTexture1,outTexCoord),0.2);
}

GLSL内建的mix函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。

函数补充说明

glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);

target 纹理目标 设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理
level 为纹理指定多级渐远纹理的级别,0表示基本级别
internalformat 希望将纹理存储为何等格式
width height 图像宽高
border 设置为0 说是历史遗留问题
format 源图格式 type 数据格式
pixels 图像数据

纹理单元

在代码中 使用 glUniform1i方法进行纹理传递 是因为 纹理单元 这个概念.
使用glUniform1i可以为纹理采样器分配一个位置值,通过把纹理单元赋值给采样器,就可以一次绑定多个纹理.

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

需要注意的是 在绑定纹理时 先要激活纹理单元才可以

5472088-6e5ee514fa54c664.png
结果
2019-07-04 13:57:00 weixin_33881753 阅读数 13

  

OpenGL 中任何复杂的图形都是由点,线 和三角形组成的. 那么一个矩形 就需要有两个三角形组成.

纹理, 可以理解为一张图片, 我么可以将整张or部分图片绘制到圆形, 矩形等目标图形中. 

 

下图表示了顶点数据 对应 的纹理中的点.

左侧代表定点数据, 其坐标原点是屏幕中央 ; 右侧图片(纹理), 坐标原点是左下角

 

GLKBaseEffect让我们避开了写shader Language 着色器语言, 相当于对glsl的封装

 

typedef struct {
    GLKVector3 positonCoords;//顶点
    GLKVector2 textureCoords;//纹理
}SceneVertex;

@interface GLViewController ()

@property(nonatomic,strong)GLKBaseEffect *baseEffect;
@property(nonatomic,assign)GLuint vertexBufferID;//缓存ID属性

@end

@implementation GLViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //1.创建OpenGLE ES上下文
    GLKView *view = (GLKView *)self.view;
    view.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:view.context];
    
    //2.GLKBaseEffect属性, 使我们不需要编写shader language(着色器)代码就可以简单完成绘制
    self.baseEffect = [[GLKBaseEffect alloc]init];
    self.baseEffect.useConstantColor = GL_TRUE;//使用静态颜色绘制
    self.baseEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);//设置绘制颜色 rgba
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//背景颜色
    
    //3.定点数据
    //矩形的六个顶点
    static const SceneVertex vertices[] = {
        {{1, -1, 0.0f,},{1.0f,0.0f}}, //右下
        {{1, 1,  0.0f},{1.0f,1.0f}}, //右上
        {{-1, 1, 0.0f},{0.0f,1.0f}}, //左上
        
        {{1, -1, 0.0f},{1.0f,0.0f}}, //右下
        {{-1, 1, 0.0f},{0.0f,1.0f}}, //左上
        {{-1, -1, 0.0f},{0.0f,0.0f}}, //左下
    };
    
    //4.生成缓存,并为缓存提供数据
    glGenBuffers(1, &_vertexBufferID);//申请一个标识符
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID);//将标识符绑定到GL_ARRAY_BUFFER
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//复制定点数据从CPU 到 GPU
    
    glEnableVertexAttribArray(GLKVertexAttribPosition);//顶点缓存数据
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL + offsetof(SceneVertex, positonCoords));
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(SceneVertex), NULL + offsetof(SceneVertex, textureCoords));

    
    //5.生成纹理
    //使用GLkit中的GLKTextureInfo方便的生成图片纹理。
    CGImageRef imageRef = [[UIImage imageNamed:@"1.png"]CGImage];
    //GLKTextureInfo封装了纹理缓存的信息,包括是否包含MIP贴图
    //option 防止图片是倒立了,这个是因为CoreGraphics的坐标系问题
    NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft, nil];
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:imageRef options:options error:nil];
    self.baseEffect.texture2d0.name = textureInfo.name;
    self.baseEffect.texture2d0.target = textureInfo.target;
    
    //6.代理, 绘制
    
}

//系统给我们回调的绘制消息,该方法会一直被调用,和display方法一致
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
   
    //清除背景色
    glClearColor(0.0f,0.0f,0.0f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    [self.baseEffect prepareToDraw];
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
}

- (void)dealloc{
    GLKView *view = (GLKView *)self.view;
    [EAGLContext setCurrentContext:view.context];
    if (_vertexBufferID != 0) {
        glDeleteBuffers(1, &_vertexBufferID);
        _vertexBufferID = 0;
    }
    
    
}



@end

 

转载于:https://www.cnblogs.com/daxueshan/p/11132026.html

2016-08-04 17:22:29 jeffasd 阅读数 183

http://blog.163.com/luckyjackyt@126/blog/static/1404476132012323105227214

OpenGL ES on iOS【IOS的OpenGL ES】

OpenGL ES provides a procedural API for submitting primitives to be rendered by a hardware accelerated graphics pipeline.【OpenGL ES提供一个程序API来把原始数据提交给硬件加速图形管线去渲染】Graphics commands are consumed by OpenGL to generate images that can be displayed to the user or retrieved for further processing outside of OpenGL ES.【图形命令被OpenGL消耗来生成图像,这些图像显示给用户或存起来等待OpenGL ES外更多的处理】

The OpenGL ES specification defines the precise behavior for each function. 【OpenGL ES说明书为每个功能定义了精确的行为】Most commands in OpenGL ES perform one of the following activities:【大多数OpenGL ES的命令执行下列行为之一】

  • Reading detailed information provided by the implementation about its capabilities.【读取OpenGL ES的能力的执行的详细信息】 See【见】 “Determining OpenGL ES Capabilities.”

  • Reading and writing state variables defined by the specification.【说明书定义的可变读写状态】 OpenGL ES state typically represents the current configuration of the graphics pipeline. 【OpenGL ES状态通常表现了图形管道的当前设置】For example, OpenGL ES 1.1 uses state variables extensively to configure lights, materials, and the computations performed by the fixed-function pipeline.【比如,OpenGL ES1.1用状态可变广泛的设置:光、材质和固定功能管线提供的计算】

  • Creating, modifying or destroying OpenGL ES objects. 【创建、修改、销毁OpenGL对象】OpenGL ES objects are not Objective-C objects; they are OpenGL ES resources manipulated through OpenGL ES’s procedural API. 【OpenGL ES对象不是Objective-C对象,他们是通过OpenGL ES程序API操作的OpenGL ES资源】See 【更多信息见】“OpenGL ES Objects Encapsulate Resources on Behalf of Your Application” for more information.

  • Drawing primitives. Vertices are submitted to the pipeline where they are processed, assembled into primitives and rasterized to a framebuffer.【绘制原始数据,顶点被提交给管线,在那,它们被处理、装配成原始数据并且光栅化进一个帧缓存】

Which Version(s) of OpenGL ES Should I Target?【我应该以哪个版本的OpenGL ES为目标】

When designing your OpenGL ES application, a critical question you must answer is whether your application should support OpenGL ES 2.0, OpenGL ES 1.1 or both.【当你设计你的OpenGL ES程序时,有个关键问题你必须想清楚,就是你的程序应该支持OpenGL ES2.0还是1.1或都支持】

  • OpenGL ES 2.0 is more flexible and more powerful than OpenGL ES 1.1 and is the best choice for new applications. 【OpenGL ES2.0比1.1更灵活更强力,对于新程序是最好地选择】Custom vertex or fragment calculations can be implemented more clearly and concisely in shaders, with better performance. 【自定的顶点或片段计算可以更清楚和简明地在着色器里执行】To perform the same calculations in OpenGL ES 1.1 often requires multiple rendering passes or complex state configurations that obscure the intent of the algorithm.【要在OpenGL ES1.1里执行相同的计算经常需要多重的渲染通道或复杂的状态设置,不易看懂算法的目的】 As your algorithms grow in complexity, shaders convey your calculations more clearly and concisely and with better performance. 【当你的算法变得复杂时,着色器更清晰和简明地传送你的计算,而且有更好的性能】OpenGL ES 2.0 requires more work up front to build your application; you need to recreate some of the infrastructure that OpenGL ES 1.1 provides by default.【OpenGL ES2.0在你创建程序前需要更加精心制定,你需要重建某些基础结构,而这些1.1都默认提供】

  • OpenGL ES 1.1 provides a standard fixed-function pipeline that provides good baseline behavior for a 3D application, from transforming and lighting vertices to blending fragments into the framebuffer.【OpenGL ES1.1提供了一个标准固定功能管线,这个管线提供为3D程序提供很好的基础管线行为,从变形和光照顶点到混合片段进帧缓存】 If your needs are simple, OpenGL ES 1.1 requires less coding to add OpenGL ES support to your application. 【如果你的需要是简单的,OpenGL ES1.1需要更少的代码来加入OpenGL ES来支持你的程序】Your application should target OpenGL ES 1.1 if your application needs to support all iOS devices【如果你的程序需要支持所有IOS设备,那应该以OpenGL ES1.1为目标】

If you choose to implement an OpenGL ES 1.1 application primarily for compatibility with older devices, consider adding an OpenGL ES 2.0 rendering option that takes advantage of the greater power of OpenGL ES 2.0-capable graphics processors found on newer iOS devices.【如果你选择执行一个OpenGL ES1.1程序主要是为了旧设备的通用性,那么考虑加一个OpenGL ES2.0的渲染设置,好利用在新IOS设备上发现的更有力的OpenGL ES2.0的图形处理能力】

Important If your application does not support both an OpenGL ES 1.1 and an OpenGL ES 2.0 rendering path, restrict your application to devices that support the version you selected.【重要。如果你的程序不是1.1和2.0都支持,则要限制你的程序对设备版本的支持】See【见】 “Declaring the Required Device Capabilities” in iOS App Programming Guide.

Understanding the OpenGL ES Architecture【理解OpenGL ES的架构】

OpenGL ES works on a few key principles. 【OpenGL ES的工作基于很少几条关键原则】To design efficient OpenGL ES applications, you need to understand the underlying architecture.【要设计有效率的OpenGL ES程序,你需要理解底层架构】

Client-Server Model【“客户端-服务器端”模型】

OpenGL ES uses a client-server model, as shown in Figure 1-1. 【如图1-1所示,OpenGL ES使用“客户端-服务器端”模型】When your application calls an OpenGL ES function, it talks to an OpenGL ES client.【当你的程序调用一个OpenGL ES功能,它是告诉OpenGL ES客户端的】 The client processes the function call and, where necessary, converts it into commands to deliver to the OpenGL server. 【客户端处理功能调用,并在必要的地方转变成命令送往OpenGL服务器】A client-server model allows the work to process a function call to be divided between the client and the server. 【客户-服务模型允许作业在客户端-服务器间处理一个功能调用】The nature of the client, the server, and the communication path between them is specific to each implementation of OpenGL ES; on an iOS device, the workload is divided between the CPU and a dedicated graphics processor.【客户端、服务器端和它们之间的通讯路径的性质对于每个OpenGL ES的执行是特定的,在一个IOS设备上,工作负荷在CPU和专用图形处理器之间分配】

Figure 1-1  OpenGL ES client-server model【OpenGL ES客户端-服务器模型】

OpenGL ES Relies on Platform-Specific Libraries For Critical Functionality【OpenGL ES依赖特定平台的关键功能库】

The OpenGL ES specification defines how OpenGL ES works, but does not define functions to manage the interaction between OpenGL ES and the hosting operating system. 【OpenGL ES说明书定义了OpenGL ES怎样工作,但是没定义管理OpenGL ES与主操作系统之间的合作的功能】Instead, the specification expects each implementation to provide functions to allocate rendering contexts and system framebuffers.【取而代之的是,说明书希望每个执行提供功能来分配渲染内容和系统帧缓存】

A rendering context stores the internal state for the OpenGL ES state machinsettinge. 【渲染环境为OpenGL ES状态机器存储内部状态】Rendering contexts allow each OpenGL ES application to each maintain a copy of the state data without worrying about other applications. 【渲染环境允许每个OpenGL ES程序保持一个状态数据的拷贝,而不用担心其他程序】See【见】 “Configuring OpenGL ES Contexts.” You can also use multiple rendering contexts in a single application.【你也可以在一个简单的程序里用多重渲染环境】

A system framebuffer is a destination for OpenGL ES drawing commands, and is typically associated with the host operating system’s graphics subsystem. iOS does not provide system framebuffers.【系统帧缓存是OpenGL ES绘图命令的终点,通常与主系统的图形子系统有联系。IOS不提供系统帧缓存】 Instead, iOS extends the framebuffer object provided by OpenGL ES to allow framebuffers that share data with Core Animation. 【取而代之,IOS扩展OpenGL ES提供的缓存对象以允许帧缓存与核心动画分享数据】See【更多帧缓存对象的信息见】 “Framebuffer Objects are the Only Rendering Target on iOS” for more information on framebuffer objects and【关于在程序里创建和使用帧缓存的详细讨论见】 “Drawing With OpenGL ES” for a detailed discussion on creating and using framebuffers in your application.

Commands May Be Executed Asynchronously【命令可以不同步扩展】

A benefit of the OpenGL ES client-server model is that an OpenGL ES function can return control to the application before the requested operation completes.【OpenGL ES客户端-服务器模型的好处在于,OpenGL ES功能可以在需求的操作完成前把控制返回给程序】If OpenGL ES required every function to complete before returning control to your application, the CPU and GPU would run in lockstep, eliminating many opportunities for parallelism in your application. 【如果OpenGL ES在把控制返回给程序前需要每个功能都完成,则CPU和GPU会顺序运行,从而限制了很多程序里的并发执行的机会】On iOS, deferring execution of drawing commands is quite common. 【在IOS里,延迟绘图命令的执行是非常常见的】By deferring several drawing commands and handling them simultaneously, the graphics hardware can remove hidden surfaces before performing costly fragment calculations.【借助同时延迟并处理几个绘图命令,图形硬件可以在执行高代价片段计算前移除隐藏的面】

Many OpenGL ES functions implicitly or explicitly flush commands to the graphics hardware. 【许多OpenGL ES功能隐式或显式地刷新命令到图形硬件】Other OpenGL functions flush commands to the graphics processor and wait until some or all pending commands have completed. 【其他OpenGL功能刷新到图形处理器并等待,直到一些或全部未执行命令完成】Whenever possible, design your application to avoid client-server synchronizations.【尽可能把程序设计成:避免客户端-服务器同步】

Commands Are Executed In Order【命令是顺序执行的】

OpenGL ES guarantees that the results of function calls made to a rendering context act as if they executed in the same order that the functions were called by the client application. 【OpenGL ES保证调用建立渲染环境的功能的结果起作用,就像它们在客户端程序里调用功能的相同序列里执行】When your application makes a function call to OpenGL ES, your application can assume the results from previous functions are available, even if some of the commands have not finished executing.【当你的程序对OpenGL ES进行功能调用时,你的程序可以承担来自先前的可以得到的功能的结果,即使某些命令还没完成执行】

Parameters are Copied at Call-Time【参数在调用时被复制】

To allow commands to be processed asynchronously, when your application calls an OpenGL ES function, any parameter data required to complete the function call must be copied by OpenGL ES before control is returned to your application. 【当你的程序调用一个OpenGL ES功能时,为了允许命令被异步执行,在控制返回程序前,任意为了完成功能调用所需的参数数据,必须被OpenGL ES复制】If a parameter points at an array of vertex data stored in application memory, OpenGL ES must copy the vertex data before returning. 【如果一个参数指向一个数组,这个数组是存在程序内存里的顶点数据的数组,OpenGL ES必须在返回前复制顶点数据】This has a couple of important implications. 【这包括两个重要的含义】First, an application is free to change any memory it owns regardless of the calls it makes to OpenGL ES, because OpenGL ES and your application never access the same memory simultaneously. 【首先,程序是自由变换它拥有的任何内存,而不管它对OpenGL ES所做的调用,因为OpenGL ES和你的程序从来不同时存取相同的内存】Second, copying parameters and reformatting them so that the graphics hardware can read the data adds overhead to every function call. 【第二,复制参数和重新对它们定义格式,以便图形硬件可以读取数据,数据是加在为每个功能调用的开销里】For best performance, your application should define its data in format that are optimized for the graphics hardware, and it should use buffer objects to explicitly manage memory copies between your application and OpenGL ES.【要得到最佳性能,你的程序应该把它的数据定义为专为图形硬件优化的格式,并且应该用更好地缓冲对象来显示的管理程序与OpenGL ES之间的内存复制】

Implementations May Extend the Capabilities Defined in the Specification【OpenGL ES的执行可以扩展说明书中定义的能力】

An OpenGL ES implementation may extend the OpenGL ES specification in one of two ways. OpenGL ES的执行可以用两种方法扩展OpenGL ES说明书】First, the specification sets specific minimum requirements that implementations must meet, such as the size of a texture or the number of texture units that the application may access. 【第一,说明书设置了执行必须满足的专门的最小需求,例如程序可能存取的材质单元的大小或数量】An OpenGL ES implementation is free to support larger values — a larger texture, or more texture units. 【OpenGL ES的执行对支持大数值是自由的,比如大的材质或更多的材质单元】Second, OpenGL ES extensions allow an implementation to provide new OpenGL ES functions and constants. 【第二,OpenGL ES的扩展允许一个执行提供新的OpenGL ES功能和连续的(constants)】Extensions allow an implementation to add entirely new features.【扩展允许一个执行加入全新的特性】 Apple implements many extensions to allow applications to take advantage of hardware features and to help you improve the performance of your applications. 【苹果执行许多扩展来允许程序利用硬件特性并帮助你提高程序性能】The actual hardware limits and the list of extensions offered by each implementation may vary depending on which device your application runs on and the version of iOS running on the device. 【实际的硬件限制和由每个执行提供的扩展列表,可以根据程序运行的硬件和IOS的版本来改变】Your application must test the capabilities at runtime and alter its behavior to match.【你的程序必须测试运行时的能力,并改变性能来匹配】

OpenGL ES Objects Encapsulate Resources on Behalf of Your Application【OpenGL ES对象为了你的程序利益把资源打包】

Objects are opaque containers that your application uses to hold configuration state or data needed by the renderer. 【对象是不透明的容器,你的程序用它保持设置状态或渲染器需要的数据】Because the only access to objects is through the procedural API, the OpenGL ES implementation can choose different strategies when allocating objects for your application. 【因为唯一对对象的存取是通过程序API的,OpenGL ES的执行可以在给你的程序分配对象时选择不同的策略】It can store your data in a format or memory location that is optimal for the graphics processor.【它可以以一个格式或在一个专为图形优化的存储单元里存储你的数据】Another advantage to objects is that they are reusable, allowing your application to configure the object once and use it multiple times.【对象的另一个好处是他们是可重复使用的,允许你的程序设置对象一次,并多次使用】

The most important OpenGL ES object types include:【最重要的OpenGL ES对象类型包括】

  • texture is an image that can be sampled by the graphics pipeline.【texture(材质)是一个图片,可以被图形管线抽样】This is typically used to map a color image onto primitives but can also be used to map other data, such as a normal map or pre-calculated lighting information. 【典型的用于原始数据的彩色图片的贴图,也可用于其他数据贴图,比如普通贴图或提前计算好的光影信息】The chapter “Best Practices for Working with Texture Data” discusses critical topics for using textures on iOS.【章节《Best Practices for Working with Texture Data》讨论了在IOS里用贴图的关键题目】

  • buffer object is a block of memory owned by OpenGL ES used to store data for your application. 【buffer(缓存)对象是一块儿OpenGL ES拥有的内存,常用于为你的程序存储数据】Buffers are used to precisely control the process of copying data between your application and OpenGL ES. 【缓存常用于精确地控制你的程序和OpenGL ES之间复制数据】For example, if you provide a vertex array to OpenGL ES, it must copy the data every time you submit a drawing call. 【例如,如果你提供了一个顶点数组给OpenGL ES,它必须在你每次提交绘图调用时复制数据】In contrast, if your application stores its data in a vertex buffer object, the data is copied only when your application sends commands to modify the contents of the vertex buffer object. 【相比之下,如果你的程序把数据存进一个顶点缓存对象里,则数据仅在程序发送命令改变顶点缓存对象的内容时被复制】Using buffers to manage your vertex data can significantly boost the performance of your application.【用缓存管理你的顶点数据可以显著地提升程序的性能】

  • vertex array object holds a configuration for the vertex attributes that are to be read by the graphics pipeline.【vertex array(顶点数组)对象保持对顶点属性的设置,这些属性被图形管线读取】 Many applications require different pipeline configurations for each entity it intends to render. 【许多程序针对每个要渲染的实体需要不同的管线设置】By storing a configuration in a vertex array, you avoid the cost of reconfiguring the pipeline and may allow the implementation to optimize its handling of that particular vertex configuration.【借助把设置存进顶点数组,你节省了重新设置管线的代价,并可以允许程序的执行来优化它对特殊顶点设置的处理】

  • Shader programs, also known as shaders, are also objects. 【shader programs(着色器程序)也叫着色器,也是对象】An OpenGL ES 2.0 application creates vertex and fragment shaders to specify the calculations that are to be performed on each vertex or fragment, respectively.【一个OpenGL ES2.0程序创建顶点和片断着色器,来指定在每个顶点或片断上分别执行的计算】

  • renderbuffer is a simple 2D graphics image in a specified format.【renderbuffer(渲染缓存)是一个简单的指定格式的2D图形图片】This format usually is defined as color, depth or stencil data. 【这个格式通常被定义为颜色、深度或模板数据】Renderbuffers are not usually used in isolation, but are instead used as attachments to a framebuffer.【渲染缓存通常不单独用,而是用于帧缓存的附件】

  • Framebuffer objects are the ultimate destination of the graphics pipeline.【framebuffer(帧缓存)对象是图形管线的最终目的地】A framebuffer object is really just a container that attaches textures and renderbuffers to itself to create a complete configuration needed by the renderer. 【帧缓存对象恰是一个真正的容器,这个容器把材质和渲染缓存贴在自己身上以创建一个完整的渲染需要的设置】A later chapter, “Drawing With OpenGL ES,” describes strategies for creating and using framebuffers in iOS applications.【后面的章节《Drawing With OpenGL ES》讨论了在IOS程序里创建和使用帧缓存的策略】

Although each object type in OpenGL ES has its own functions to manipulate it, all objects share a similar programming model:【虽然OpenGL ES里的每个对象的类型有他自己的功能来操作它,但是所有对象分享一个相似的程序模型】

  1. Generate an object identifier.【生成一个对象标识】

    An identifier is a plain integer used to identify a specific object instance. 【一个标识是一个自然数,用于标明一个确切的对象实例】Whenever you need a new object, call OpenGL ES to create a new identifier. 【当你需要一个新对象时,调用OpenGL ES来创建一个新的标识】Creating the object identifier does not actually allocate an object, it simply allocates a reference to it.【创建对象标识不是实际上分配一个对象,它简单地分配一个引文】

     

  2. Bind your object to the OpenGL ES context.【把你的对象捆绑到OpenGL ES环境】

    Most OpenGL ES functions act implicitly on an object, rather than requiring you to explicitly identify the object in every function call. 【大多数OpenGL ES功能隐式地对对象起作用,而不需要你在每个功能调用时显式地标识对象】You set the object to be configured by binding it to the context. 【你借助把对象捆绑进环境来设置对象】Each object type uses different functions to bind it to the context. 【每个对象用不同的方法来捆绑进环境】The first time you bind an object identifier, OpenGL ES allocates and initializes the object.【第一次捆绑对象标识时,OpenGL ES分配和初始化对象】

     

  3. Modify the state of the object.【改变对象的状态】

    Your application makes one or more function calls to configure the object.【你的程序用一个或更多的功能调用来设置对象】For example, after binding a texture object, you typically would configure how the texture is filtered and then load image data into the texture object.【例如,在捆绑一个材质对象后,你通常会设置材质怎样过滤,然后载入图片到材质对象】

    Changing an object can potentially be expensive, as it may require new data to be sent to the graphics hardware. 【当它需要新的数据送往图形硬件时,换一个对象可能要潜在地贵的】Where reasonable, create and configure your objects once, and avoid changing them afterwards for the duration of your application.【在适度的地方,创建和设置你的对象一次,并避免后来在整个程序期间换它们】

  4. Use the object for rendering.【使用渲染用的对象】

    Once you’ve created and configured all the objects needed to render a scene, you bind the objects needed by the pipeline and execute one or more drawing functions. 【一旦你已经创建和设置了全部的渲染场景所需的对象,你捆绑管线需要的对象并执行一个或更多的绘图功能】OpenGL ES uses the data stored in the objects to render your primitives. 【OpenGL ES用存在对象里的数据渲染你的原始模型】The results are sent to the bound framebuffer object.【把结果送进捆绑好的帧缓存里】

     

  5. Delete the object.【删除对象】

    When you are done with an object, your application should delete it. 【当一个对象用完后,你的程序应该删除它】When an object is deleted, its contents are destroyed and the object identifier is recycled.【当一个对象被删除了,它的容器就被毁掉,对象标识被回收】

Framebuffer Objects are the Only Rendering Target on iOS【帧缓存对象是IOS里唯一的渲染目标】

Framebuffer objects are the destination for rendering commands. 【帧缓存对象是渲染命令的终点】OpenGL ES 2.0 provides framebuffer objects as part of the core specification; they are provided on OpenGL ES 1.1 by theOES_framebuffer_object extension. 【OpenGL ES2.0提供帧缓存对象作为核心说明书的部分】Because framebuffer objects are the only rendering target on iOS, Apple guarantees that the OES_framebuffer_object extension will always be provided by every OpenGL ES 1.1 implementation on iOS.【因为帧缓存对象是IOS里唯一的渲染目标,苹果保证OES_framebuffer_object扩展总是会被IOS里的每个OpenGL ES1.1的执行提供】

Framebuffer objects provide storage for color, depth and/or stencil data by attaching images to the framebuffer, as shown in Figure 1-2. 【借助把图片附在帧缓存上,帧缓存对象提供对颜色、深度和(或)模板数据的存储,如图1-2】The most common image attachment is a renderbuffer object. 【最通常的图片附着是一个渲染缓存对象】However, a OpenGL ES texture can be attached to the color attachment point of a framebuffer instead, allowing image to be rendered directly into a texture. 【然而,一个OpenGL ES材质可以被附在帧缓存的颜色附着点上,取而代之,允许图片被直接渲染进材质】Later, the texture can act as an input to future rendering commands.【再后来,材质可以作为一个对未来渲染命令的输入起作用】

Figure 1-2  Framebuffer with color and depth renderbuffers【带颜色和深度渲染缓存的帧缓存】Framebuffer with attachments.

Creating a framebuffer uses the following steps:【用下列步骤创建帧缓存】

  1. Generate and bind a framebuffer object.【生成并绑定一个帧缓存的对象】

  2. Generate, bind, and configure an image.【生成、捆绑、设置一个图片】

  3. Attach the image to one of the framebuffer’s attachment points.【把图片贴在其中一个帧缓存的附着点上】

  4. Repeat steps 2 and 3 for other images.【对其他图片重复2、3步骤】

  5. Test the framebuffer for completeness. 【测试帧缓存的完整性】The rules for completeness are defined in the OpenGL ES specification. 【OpenGL ES的说明书定义了完整性的规则】These rules ensure the framebuffer and its attachments are well-defined.【这些规则确保帧缓存和它的附属物是清晰可辨的】




? 2011 Apple Inc. All Rights Reserved. (Last updated: 2011-02-24)

Did this document help you? Yes It's good, but... Not helpful...

2017-05-06 22:19:00 weixin_33724059 阅读数 15

前言:如果你没有 OpenGL ES 2 的基础知识,请先移步 《OpenGL ES 2.0 (iOS) 笔记大纲》 学习一下基础的知识。


目录

一、软件运行效果演示
(一)、最终效果
(二)、信息提取

二、纹理处理的流程【核心】
(一)、Texture 是什么?
(二)、Texture
(三)、引入了 Texture 的 Shader 文件
(四)、Texture 正确的 “书写” 顺序

三、知识扩充:图片加载
使用 Quartz Core 的知识加载图片数据


一、软件运行效果演示

(一)、最终效果

工程地址:Github

1411747-60ea63824004b4a6.gif
Texture-Base.gif

(二)、信息提取

  1. 不同的模型【2D & 3D】,不同维度下,Texture 的处理区别;

  2. 单一像素信息【pixelBuffer】 与 复杂像素信息【图片】的显示区别;

  3. 正方图【单张或多张图片】 与 长方图,像素的显示控制区别;


二、纹理处理的流程【核心】

(一)、Texture 是什么?

Texture 纹理,就是一堆被精心排列过的像素;

  1. 因为 OpenGL 就是图像处理库,所以 Texture 在 OpenGL 里面有多重要,可想而知;

  2. 其中间接地鉴明了一点,图片本身可以有多大变化,OpenGL 就可以有多少种变化;

学好 Texture 非常重要

(二)、Texture

Texture 在 OpenGL 里面有很多种类,但在 ES 版本中就两种——Texture_2D + Texture_CubeMap;

Texture_2D: 就是 {x, y} 二维空间下的像素呈现,也就是说,由效果图上演示可知,很难做到使正方体的六个面出现不同的像素组合;图片处理一般都使用这个模式;[x 、y 属于 [0, 1] 这个范围]


1411747-df5dd471f9b93e01.png

Texture_CubeMap: 就是 { x, y, z } 三维空间下的像素呈现,也就如效果图中演示的正方体的六个面可以出现不同的像素组合;它一般是用于做环境贴图——就是制作一个环境,让 3D 模型如同置身于真实环境中【卡通环境中也行】。[x、y、z 属于 [-1, 1] 这个范围,就是与 Vertex Position 的值范围一致]


1411747-5826d247e0bde7bb.png

注:上面提到的所有坐标范围是指有效渲染范围,也就是说你如果提供的纹理坐标超出了这个范围也没有问题,只不过超出的部分就不渲染了;

感受一下怎么具体表达:

// VYVertex
typedef struct {
    GLfloat position[3];
    GLfloat texCoord[2];
    GLfloat normalCoord[3];
}VYVertex;

Texture_2D:

// Square
static const VYVertex tex2DSquareDatas[] = {
    {{-1.0, -1.0, 0.0}, {0.0, 0.0}},
    {{ 1.0, -1.0, 0.0}, {1.0, 0.0}},
    {{ 1.0,  1.0, 0.0}, {1.0, 1.0}},
    {{-1.0,  1.0, 0.0}, {0.0, 1.0}},
};
// Cube
static const VYVertex tex2DCubeDatas[] = {

    // Front [Front 的 z 是正的]
    {{-1.0, -1.0,  1.0}, {0.0, 0.0}}, // 0
    {{ 1.0, -1.0,  1.0}, {1.0, 0.0}}, // 1
    {{ 1.0,  1.0,  1.0}, {1.0, 1.0}}, // 2
    {{-1.0,  1.0,  1.0}, {0.0, 1.0}}, // 3
    // Back [Back 的 z 是负的]
    {{-1.0,  1.0, -1.0}, {0.0, 0.0}}, //4[3: -Z]
    {{ 1.0,  1.0, -1.0}, {1.0, 0.0}}, //5[2: -Z]
    {{ 1.0, -1.0, -1.0}, {1.0, 1.0}}, //6[1: -Z]
    {{-1.0, -1.0, -1.0}, {0.0, 1.0}}, //7[0: -Z]
    // Left [Left 的 x 是负的]
    {{-1.0, -1.0,  1.0}, {0.0, 0.0}}, //8[0]
    {{-1.0,  1.0,  1.0}, {1.0, 0.0}}, //9[3]
    {{-1.0,  1.0, -1.0}, {1.0, 1.0}}, //10[4]
    {{-1.0, -1.0, -1.0}, {0.0, 1.0}}, //11[7]
    // Right [Right 的 x 是正的]
    {{ 1.0, -1.0,  1.0}, {0.0, 0.0}}, //12[1]
    {{ 1.0, -1.0, -1.0}, {1.0, 0.0}}, //13[6]
    {{ 1.0,  1.0, -1.0}, {1.0, 1.0}}, //14[5]
    {{ 1.0,  1.0,  1.0}, {0.0, 1.0}}, //15[2]
    // Top [Top 的 y 是正的]
    {{-1.0,  1.0,  1.0}, {0.0, 0.0}}, //16[3]
    {{ 1.0,  1.0,  1.0}, {1.0, 0.0}}, //17[2]
    {{ 1.0,  1.0, -1.0}, {1.0, 1.0}}, //18[5]
    {{-1.0,  1.0, -1.0}, {0.0, 1.0}}, //19[4]
    // Bottom [Bottom 的 y 是负的]
    {{-1.0, -1.0,  1.0}, {0.0, 0.0}}, //20[0]
    {{-1.0, -1.0, -1.0}, {1.0, 0.0}}, //21[7]
    {{ 1.0, -1.0, -1.0}, {1.0, 1.0}}, //22[6]
    {{ 1.0, -1.0,  1.0}, {0.0, 1.0}}, //23[1]
    
};

Texture_CubeMap:

// Cube Map
static const VYVertex texCubemapCubeDatas[] = {
    
    // Front [Front 的 z 是正的]
    {{-1.0, -1.0,  1.0}, {}, {-1.0, -1.0,  1.0}}, // 0
    {{ 1.0, -1.0,  1.0}, {}, { 1.0, -1.0,  1.0}}, // 1
    {{ 1.0,  1.0,  1.0}, {}, { 1.0,  1.0,  1.0}}, // 2
    {{-1.0,  1.0,  1.0}, {}, {-1.0,  1.0,  1.0}}, // 3
    // Back [Back 的 z 是负的]
    {{-1.0,  1.0, -1.0}, {}, {-1.0,  1.0, -1.0}}, //4[3: -Z]
    {{ 1.0,  1.0, -1.0}, {}, { 1.0,  1.0, -1.0}}, //5[2: -Z]
    {{ 1.0, -1.0, -1.0}, {}, { 1.0, -1.0, -1.0}}, //6[1: -Z]
    {{-1.0, -1.0, -1.0}, {}, {-1.0, -1.0, -1.0}}, //7[0: -Z]
    // Left [Left 的 x 是负的]
    {{-1.0, -1.0,  1.0}, {}, {-1.0, -1.0,  1.0}}, //8[0]
    {{-1.0,  1.0,  1.0}, {}, {-1.0,  1.0,  1.0}}, //9[3]
    {{-1.0,  1.0, -1.0}, {}, {-1.0,  1.0, -1.0}}, //10[4]
    {{-1.0, -1.0, -1.0}, {}, {-1.0, -1.0, -1.0}}, //11[7]
    // Right [Right 的 x 是正的]
    {{ 1.0, -1.0,  1.0}, {}, { 1.0, -1.0,  1.0}}, //12[1]
    {{ 1.0, -1.0, -1.0}, {}, { 1.0, -1.0, -1.0}}, //13[6]
    {{ 1.0,  1.0, -1.0}, {}, { 1.0,  1.0, -1.0}}, //14[5]
    {{ 1.0,  1.0,  1.0}, {}, { 1.0,  1.0,  1.0}}, //15[2]
    // Top [Top 的 y 是正的]
    {{-1.0,  1.0,  1.0}, {}, {-1.0,  1.0,  1.0}}, //16[3]
    {{ 1.0,  1.0,  1.0}, {}, { 1.0,  1.0,  1.0}}, //17[2]
    {{ 1.0,  1.0, -1.0}, {}, { 1.0,  1.0, -1.0}}, //18[5]
    {{-1.0,  1.0, -1.0}, {}, {-1.0,  1.0, -1.0}}, //19[4]
    // Bottom [Bottom 的 y 是负的]
    {{-1.0, -1.0,  1.0}, {}, {-1.0, -1.0,  1.0}}, //20[0]
    {{-1.0, -1.0, -1.0}, {}, { 1.0, -1.0,  1.0}}, //21[7]
    {{ 1.0, -1.0, -1.0}, {}, { 1.0, -1.0, -1.0}}, //22[6]
    {{ 1.0, -1.0,  1.0}, {}, {-1.0, -1.0, -1.0}}, //23[1]
    
};

这种坐标,是刚好贴合【完全覆盖】的状态;

数据特点:一个顶点数据绑定一个纹理数据;

【有没有注意到,CubeMap 里面就是直接拷贝顶点数据到纹理坐标上,就行了。(CubeMap 中间那个空的 {} 是结构体中的 2D 纹理数据(就是空的))】

其它的数据形态【对于不是正方的图片】,
【希望大一点,或小一点,即只显示某一部分】:


1411747-acf79c15e3a81bb2.png

都是类似图中的分割一样,划分成多个小图片【小的 TexCoord】,最终的数据形态是:

static const VYVertex tex2DElongatedDDCubeDatas[] = {
    
    // Front [Front 的 z 是正的]
    {{-1.0, -1.0,  1.0}, {0.000, 0.000}}, // 0
    {{ 1.0, -1.0,  1.0}, {0.250, 0.000}}, // 1
    {{ 1.0,  1.0,  1.0}, {0.250, 0.500}}, // 2
    {{-1.0,  1.0,  1.0}, {0.000, 0.500}}, // 3
    // Back [Back 的 z 是负的]
    {{-1.0,  1.0, -1.0}, {0.000, 0.500}}, //4[3: -Z]
    {{ 1.0,  1.0, -1.0}, {0.250, 0.500}}, //5[2: -Z]
    {{ 1.0, -1.0, -1.0}, {0.250, 1.000}}, //6[1: -Z]
    {{-1.0, -1.0, -1.0}, {0.000, 1.000}}, //7[0: -Z]
    // Left [Left 的 x 是负的]
    {{-1.0, -1.0,  1.0}, {0.250, 0.000}}, //8[0]
    {{-1.0,  1.0,  1.0}, {0.500, 0.000}}, //9[3]
    {{-1.0,  1.0, -1.0}, {0.500, 0.500}}, //10[4]
    {{-1.0, -1.0, -1.0}, {0.250, 0.500}}, //11[7]
    // Right [Right 的 x 是正的]
    {{ 1.0, -1.0,  1.0}, {0.250, 0.500}}, //12[1]
    {{ 1.0, -1.0, -1.0}, {0.500, 0.500}}, //13[6]
    {{ 1.0,  1.0, -1.0}, {0.500, 1.000}}, //14[5]
    {{ 1.0,  1.0,  1.0}, {0.250, 1.000}}, //15[2]
    // Top [Top 的 y 是正的]
    {{-1.0,  1.0,  1.0}, {0.500, 0.000}}, //16[3]
    {{ 1.0,  1.0,  1.0}, {0.750, 0.000}}, //17[2]
    {{ 1.0,  1.0, -1.0}, {0.750, 0.500}}, //18[5]
    {{-1.0,  1.0, -1.0}, {0.500, 0.500}}, //19[4]
    // Bottom [Bottom 的 y 是负的]
    {{-1.0, -1.0,  1.0}, {0.750, 0.000}}, //20[0]
    {{-1.0, -1.0, -1.0}, {1.000, 0.000}}, //21[7]
    {{ 1.0, -1.0, -1.0}, {1.000, 0.500}}, //22[6]
    {{ 1.0, -1.0,  1.0}, {0.750, 0.500}}, //23[1]
    
};

也可以是没有填充完整的图片,只取其中的一部分,数据形态也是上面的:


1411747-29e1b99a467a6db6.png

扩展:
CubeMap 用于做环境贴图,还需要 Light + Shadow 【光 + 阴影】的知识,为什么?环境,有物体 + 自然光 + 人造光 + 光与物体产生的阴影 + 光与物体作用后的颜色;【颜色和阴影是因为有光才产生的,OpenGL 本身默认有一个全局光,不然你没有写光的代码,为什么可以看到你渲染的模型体】
即只有在具备了 光 + 影 的知识,去学习 环境贴图才好理解;【贴图:HDR 图片 (效果中的那张蓝色森林就是 HDR 图,没有做 CubeMap) + CubeMap 格式】

CubeMap 图片格式,就是把下图中的 HDR 图片直接转换成,六个黄色框框的图像,框框之间的边缘是连接的哦:


1411747-e2aae96836c82bdc.png

1411747-2ce71f2a3a0531e1.png
连接

MipMapping: 根据不同的情形加载不同大小的图片进行渲染;【不同情形,指不同远近,不同光影环境下对图片“看清”“看不清”的程度,OpenGL 自动选择合适的图片大小】【不同大小的图片,程序员要事先加载一张图片的不同大小 ( 2^n , 2^m ) 的像素数据(0 ~ n level),又因为 ES 是基于移动端的,所以内存容易告急,即能不用则不用】

Fliter + 特效 : 我们天天看到的最多的东西,就是给图片像素加入各种“想法”变成你想要的效果【加雾、马赛克、调色、镜像、模糊、素描、液化、叠加、艺术化 ......】,它的核心知识在 Fragment Shader【重点】 + OpenGL ES 提供的基础混合模式【滤波 + Blend】,放在下一篇文章专门讲;

粒子系统:Texture + Point Sprites,制作雨水、下雪、飞舞的花瓣...... 只要渲染效果要求有多个相似点在那动来动去的,都可以用它们来实现;【数学中的分形理论好像也可以用上】【粒子,会用专门的一篇文章讲】

所有的 “花样” 特效,不管被称之为什么,都与 数学知识【算法】 和 颜色构成知识【光构成、色彩构成】 密不可分;

所以我就要怕了吗?
错,你应该兴奋;因为~~ **反正我也没有什么可以失去的了,上来不就是干了吗?
** ^ _ ^ + ~_~ + $-$

(三)、引入了 Texture 的 Shader 文件

Texture_2D:

2D Vertex:

#version 100

uniform mat4 u_modelViewMat4;
uniform mat4 u_projectionMat4;

attribute vec4 a_position;
attribute vec2 a_texCoord;

varying highp vec2 v_texCoord;

void main(void) {
    gl_Position = u_projectionMat4 * u_modelViewMat4 * a_position;
    v_texCoord  = a_texCoord;
}

纹理输入输出:

...
attribute vec2 a_texCoord;
varying highp vec2 v_texCoord;

void main(void) {
    ...
    v_texCoord  = a_texCoord;
}

输入:
vec2 a_texCoord,上面提到过它是 {x, y} 的坐标,所以使用的也是 vec2 ;

输出:
同样是 vec2 ,但是一定要记住加 highp 精度限定符,不然编译会报错哦;

不知道,你是否还记得渲染管线中的 Texture Memory ,看下图:


1411747-bb735640891f5a7b.png
渲染管线

红色框框住的虚线,就是指代 Vertex Shader 中的纹理坐标信息;

直接给的,为什么是虚线?

看清楚 Shader 代码,这里是直接就赋值【输入 = 输出,经过其它变换也行】了,也就是 Vertex Shader 内部不需要使用到它,它只是为了传到 Fragment 里面使用的【varying 的作用】,所以就使用虚线来表示;

2D Fragment:

#version 100

uniform sampler2D us2d_texture;

varying highp vec2 v_texCoord;

void main(void) {
//    gl_FragColor = vec4(1, 1, 0.5, 1);
    gl_FragColor = texture2D(us2d_texture, v_texCoord);
}

上面的渲染管线图中,黄色框框住的实线,就是指代 Fragment Shader 中的像素数据【sampler2D】来源;

这里是核心,输入输出:

uniform sampler2D us2d_texture;
...

void main(void) {
    gl_FragColor = texture2D(us2d_texture, ...);
}

输入:
sampler2D 就是一堆静态数据的意思,像素信息就是一堆固定【不管是写死,还是程序自动生成,都一样】的颜色信息,所以要使用这种常量块的类型限定符;

输出:
这里要使用 texture2D 内置函数来处理像素信息生成 vec4 的颜色信息,原型 vec4 texture2D(sampler2D s, vec2 texCoord);

所以剩下的问题就是如何得到 sampler2D 数据,并如何将像素数据写入到 Shader 中

Texture_CubeMap:


#version 100

uniform mat4 u_modelViewMat4;
uniform mat4 u_projectionMat4;

attribute vec4 a_position;
attribute vec3 a_normalCoord;
varying highp vec3 v_normalCoord;

void main(void) {
    gl_Position = u_projectionMat4 * u_modelViewMat4 * a_position;
    v_normalCoord  = a_normalCoord;
}
#version 100

uniform samplerCube us2d_texture;
varying highp vec3 v_normalCoord;

void main(void) {
    gl_FragColor = textureCube(us2d_texture, v_normalCoord);
}

CubeMap 与 2D 的 Fragment 区别并不大,原理一样的;

CubeMap Vertex ,只要把 vec2 --> vec3 即可;

CubeMap Fragment , 只要把 sampler2D --> samplerCube , texture2D 函数改成 textureCube 即可;

(四)、Texture 正确的 “书写” 顺序

前提,假设基本的渲染管线已经配置完成了,这里只重点讲纹理相关的;

1、 绑定 Texture Coord 纹理坐标:

GLuint texCoordAttributeComCount = 2;
    
glEnableVertexAttribArray(texCoordAttributeIndex);
if ( texture2D ) {
    glVertexAttribPointer(texCoordAttributeIndex,
                          texCoordAttributeComCount,
                          GL_FLOAT, GL_FALSE,
                          sizeof(VYVertex),
                          (const GLvoid *) offsetof(VYVertex, texCoord));
 } else {
    texCoordAttributeComCount = 3;
    glVertexAttribPointer(texCoordAttributeIndex,
                          texCoordAttributeComCount,
                          GL_FLOAT, GL_FALSE,
                          sizeof(VYVertex),
                          (const GLvoid *) offsetof(VYVertex, normalCoord));
 }

【如果看不懂,请回去看看第一篇文章,里面有详细讲】

2、 请求 Texture 内存:

    GLuint texture = 0;
    glGenTextures(1, &texture);
    
    GLenum texMode = texture2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP;
    glBindTexture(texMode, texture);

glGenTextures(GLsizei n, GLuint* textures); 和 glGenBuffers 等的使用是一样的;它的意思就是,向 GPU 请求一块 Texture 内存;

glBindTexture (GLenum target, GLuint texture); 和其它的 glBind... 方法一样;它的意思是,告诉 GPU 请求一块 target 【只有 2D 和 CubeMap 两种】 类型的内存,只有当这个方法完成请求后,这块 Texture 内存才会生成【如果当前内存标识符指向的内存已经存在,则不会再创建,只会指向此处】;

3、 加载像素数据:

    glUseProgram(programObject);
    
    [self setTextureWithProgram:programObject 
                        texture:texture
                        texMode:texMode];

(1)一定要在 glUseProgram 函数后进行这个步骤,为什么?

因为 Fragment 使用的是 uniform samplerXXX 的数据,uniform 常量数据要在 glUseProgram 后再加载才有效,而且它的内存标识符【内存】要在 link Program 之后 OpenGL 才会分配;

(2)进入 setTextureWithProgram: texture: texMode:方法

先准备像素数据【pixelsDatas 或 ImageDatas】:
这里的是,Pixels 的数据,就是写死的数据

// 2 * 2 For Texture_2D
static const GLfloat tex2DPixelDatas[3*4] = {
    1.000, 1.000, 0.108,//[UIColor colorWithRed:1.000 green:1.000 blue:0.108 alpha:1.000]
    0.458, 1.000, 0.404,//[UIColor colorWithRed:0.458 green:1.000 blue:0.404 alpha:1.000]
    0.458, 1.000, 0.770,//[UIColor colorWithRed:0.458 green:1.000 blue:0.770 alpha:1.000]
    0.729, 0.350, 0.770,//[UIColor colorWithRed:0.729 green:0.350 blue:0.770 alpha:1.000]
};

// (2 * 2 * 6) For Texture_CubeMap
static const GLfloat texCubemapPixelDatas[6][3*4] = {
    1.000, 1.000, 0.108,//[UIColor colorWithRed:1.000 green:1.000 blue:0.108 alpha:1.000]
    0.458, 1.000, 0.404,//[UIColor colorWithRed:0.458 green:1.000 blue:0.404 alpha:1.000]
    0.458, 1.000, 0.770,//[UIColor colorWithRed:0.458 green:1.000 blue:0.770 alpha:1.000]
    0.729, 0.350, 0.770,//[UIColor colorWithRed:0.729 green:0.350 blue:0.770 alpha:1.000]
    
    0.145, 0.319, 0.308,//[UIColor colorWithRed:0.145 green:0.319 blue:0.308 alpha:1.000]
    0.732, 0.319, 0.308,//[UIColor colorWithRed:0.732 green:0.319 blue:0.308 alpha:1.000]
    0.732, 0.727, 0.308,//[UIColor colorWithRed:0.732 green:0.727 blue:0.308 alpha:1.000]
    0.732, 0.727, 0.889,//[UIColor colorWithRed:0.732 green:0.727 blue:0.889 alpha:1.000]
    
    0.633, 0.820, 0.058,//[UIColor colorWithRed:0.633 green:0.820 blue:0.058 alpha:1.000]
    0.936, 0.820, 0.994,//[UIColor colorWithRed:0.936 green:0.820 blue:0.994 alpha:1.000]
    0.017, 0.029, 0.994,//[UIColor colorWithRed:0.017 green:0.029 blue:0.994 alpha:1.000]
    0.000, 0.000, 0.000,//[UIColor colorWithWhite:0.000 alpha:1.000]
    
    0.593, 0.854, 0.000,//[UIColor colorWithRed:0.593 green:0.854 blue:0.000 alpha:1.000]
    0.593, 0.337, 0.000,//[UIColor colorWithRed:0.593 green:0.337 blue:0.000 alpha:1.000]
    1.000, 0.407, 0.709,//[UIColor colorWithRed:1.000 green:0.407 blue:0.709 alpha:1.000]
    0.337, 0.407, 0.709,//[UIColor colorWithRed:0.337 green:0.407 blue:0.709 alpha:1.000]
    
    0.337, 0.738, 0.709,//[UIColor colorWithRed:0.337 green:0.738 blue:0.709 alpha:1.000]
    0.337, 0.994, 0.709,//[UIColor colorWithRed:0.337 green:0.994 blue:0.709 alpha:1.000]
    0.186, 0.105, 0.290,//[UIColor colorWithRed:0.186 green:0.105 blue:0.290 alpha:1.000]
    0.633, 0.872, 0.500,//[UIColor colorWithRed:0.633 green:0.872 blue:0.500 alpha:1.000]
    
    0.290, 0.924, 0.680,//[UIColor colorWithRed:0.290 green:0.924 blue:0.680 alpha:1.000]
    0.290, 0.924, 0.174,//[UIColor colorWithRed:0.290 green:0.924 blue:0.174 alpha:1.000]
    0.982, 0.163, 0.174,//[UIColor colorWithRed:0.982 green:0.163 blue:0.174 alpha:1.000]
    0.628, 0.970, 0.878,//[UIColor colorWithRed:0.628 green:0.970 blue:0.878 alpha:1.000]
};

因为 Texture_2D 状态下,只有 {x, y} 平面的数据需要填充,所以这里就只有一个面的颜色数据;

而在 Texture_CubeMap 状态下,是 { x, y, z } 三维坐标,即六个面需要填充,所以就是6 * 1(1 = 2 * 2) = 6个面的颜色数据;

注:图片类型的数据要自己写转换方法,生成像素数据;当然也可以使用 GLKit 提供的 TextureLoder 类来加载图片像素数据;

(3)【核心】glTexImage2D得到纹理像素的方法,就是加载纹理像素到 GPU 的方法:

glTexImage2D
void glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
target *指 如果是 2D ,就是 GL_Texture_2D,如果是 CubeMap 就是 GL_TEXTURE_CUBE_MAP_XXX [+-x, +-y, +-z, 六个面] *
level *指 mipmapping level 没有做 mipmapping 则为 0 ;如果做了,则为 0 ~ levelMax [这个 max 是由你自己图片数据决定的] *
internalformat 指 像素数据的格式是什么 GL_RGB 等等
width 指 一块像素的宽 [2D 下只有一块,cubemap 会有多块(六个面)]
height 指 一块像素的高
border *指 ES 下是 GL_FALSE *
format 指 与 internalformat 格式一致
type 指 像素数据存储的类型,如:GL_FLOAT, GL_UNSIGNED_BYTE
pixels 指 一块像素的内存首地址

a. 像素模式下的使用:

if (texMode == GL_TEXTURE_2D) {
    glTexImage2D(texMode, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT, tex2DPixelDatas);
} else {
    
//                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[0]);
//                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[1]);
//                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[2]);
//                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[3]);
//                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[4]);
//                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, 2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[5]);

    GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
    for (NSUInteger i = 0; i < 6; i++) {
        glTexImage2D(target, 0, GL_RGB,
                     2, 2, GL_FALSE, GL_RGB, GL_FLOAT,  texCubemapPixelDatas[i]);
        target++;
    }
    
}

上面在 GL_TEXTURE_2D 状态下的加载,只要理解了glTexImage2D函数参数的意思,也就会使用且明白了,这里就不再赘述了;

特别要注意的是在 GL_Texture_Cube_Map 状态下的使用,一定要六个面都进行像素数据加载;

#define GL_TEXTURE_CUBE_MAP_POSITIVE_X                   0x8515
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X                   0x8516
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y                   0x8517
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y                   0x8518
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z                   0x8519
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z                   0x851A

看看GL_TEXTURE_CUBE_MAP_POSITIVE_X它们的定义,因为定义是连续的,所以我们才可以用 for 循环来 “偷懒”;

b. 图片像素模式下的使用:

if (texMode == GL_TEXTURE_2D) {
    
    UIImage *img = // img;
    
    [self.loadTexture textureDataWithResizedCGImageBytes:img.CGImage completion:^(NSData *imageData, size_t newWidth, size_t newHeight) {
        glTexImage2D(texMode, 0, GL_RGBA,
                     (GLsizei)newWidth, (GLsizei)newHeight,
                     GL_FALSE, GL_RGBA, GL_UNSIGNED_BYTE,
                     imageData.bytes);
    }];
    
} else {
    
    NSArray<UIImage *> *imgs = // imgs;
    
    GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
    [self.loadTexture textureDatasWithResizedUIImages:imgs completion:^(NSArray<NSData *> *imageDatas, size_t newWidth, size_t newHeight) {
        [imageDatas enumerateObjectsUsingBlock:^(NSData * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            glTexImage2D((GLenum)(target + idx), 0, GL_RGBA,
                         (GLsizei)newWidth, (GLsizei)newHeight,
                         GL_FALSE, GL_RGBA, GL_UNSIGNED_BYTE,
                         obj.bytes);
        }];
    }];
    
}

这里的核心就是,self.loadTexture 的图片加载方法,这是自己写的加载方法,使用的技术是 Quartz Core ;具体的在下一节【三、知识扩充:图片加载】会讲到;

两者的使用并不会有什么区别,这只是两种像素数据提供的方式不同罢了

(4)指定滤波设置【下一篇会重点讲】 + 像素绑定 + 激活纹理

glTexParameteri(texMode, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texMode, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
GLuint textureSourceLoc = glGetUniformLocation(programObject, "us2d_texture");
glUniform1i(textureSourceLoc, 0);
    
glEnable(texMode);
glActiveTexture(GL_TEXTURE0);
glBindTexture(texMode, texObj);

a. 设置滤波模式,函数就是glTexparameteri方法
原型:glTexParameteri (GLenum target, GLenum pname, GLint param);

ES 2 有四个这种滤波函数,下图的参数已经说得很明白了,我就不一一解释了:


1411747-4b172a6f4d24ee52.png

1411747-d864e54da5fdd8f8.png

MIN / MAG ?:


1411747-d93b6525eff3c2f7.png

1411747-fa7408911bd78a2c.png

magnification【MAG】:放大的意思,指显示在屏幕上的一个像素是纹理像素放大后的结果;

【只有 x、y 方向都进行放大,才需要这个参数,也就是说它是可选的】

minification【MIN】: 缩小的意思,指显示在屏幕上的一个像素是一个纹理像素集缩小后的结果;

【一定要做的设置,如上述代码中的glTexParameteri(xxx, GL_TEXTURE_MIN_FILTER, xxx);

【MipMapping 发挥作用的地方就是在缩小的时候,OpenGL 会自动选择合适大小的像素数据】

如果纹理像素在 x、y 方向上是做同一个动作【拉伸或压缩】,则需要放大或缩小像素;如果纹理像素在 x、y 方向上是做不同的动作,则需要放大或者缩小,不确定【由 OpenGL 自己选择】;

WRAP_S / WRAP_T ? : 就是 x 或 y 方向填充覆盖的意思;

LINEAR / NEAREST ? :


1411747-251d52380d148d16.png

前者是指启用线性滤波【就是平滑过渡】,后者是禁用线性滤波;

平滑过滤使用的技术——信号采样,先看看一维的信号采样:


1411747-02015521776c3966.png

意思就是,采样提供的纹理像素,在放大、缩小的时候,使相邻的像素进行“一定程度的融合”产生新的像素信息,使最终显示在屏幕在的图片更加平滑;上图【猴子】中的效果就是利用这项技术来的,对于二维、三维,就相应地做多次采样【二维,两次;三维,三次......】;

b. 像素绑定【就是告诉 GPU Shader 的像素数据在那】+ 激活纹理

GLuint textureSourceLoc = glGetUniformLocation(programObject, 
                                               "us2d_texture");
glUniform1i(textureSourceLoc, 0);

glEnable(texMode);
glActiveTexture(GL_TEXTURE0);

glBindTexture(texMode, texObj);

glUniform1i类函数,可以理解成绑定一块内存【像素块内存】,也可以理解成绑定一个内存空间【一般常量】;

函数原型:void glUniform1i(GLint location, GLint x)

glEnable函数,就是打开一些什么东西,这里是打开 GL_TEXTURE_XXX ,不写也行,这里和其它地方的默认一样, 0 这个位置的纹理就是打开的;【为了良好习惯,还是写吧】

glActiveTexture函数,名字已经告诉是激活纹理的意思,不用多说了;

重点:glUniform1i 的第二个参数是和 glActiveTexture 的第二个参数是对应的,前者使用的是 0,那么后者就是对应 GL_TEXTURE0 【0~31,共32个】,依此类推

为什么还要做glBindTexture(texMode, texObj);重新绑定像素内存,其实就是防止中途有什么地方把它给改了【如,bind 了其它的纹理】,所以是为了保险起见,就最好写上;但是因为这里很明显地,只有 layoutSubviews 函数【此渲染代码都是写在这个函数内运行的】会绑定它,而且都是同一个的,所以也可以不写;


三、知识扩充:图片加载

使用 Quartz Core 技术 加载图片数据,Bitmap Context :

1411747-544b9a43b1ef0eff.png

本来它不属于 OpenGL 的内容,但是它本身也是图像处理的技术,包括 Core Image、 Accelerate等图像处理的框架,如果可以,请尽量去了解或去掌握或去熟练。

核心代码:

#define kBitsPerComponent   8

#define kBytesPerPixels     4
#define kBytesPerRow(width)         ((width) * kBytesPerPixels)

- (NSData *)textureDataWithResizedCGImageBytes:(CGImageRef)cgImage
                                      widthPtr:(size_t *)widthPtr
                                     heightPtr:(size_t *)heightPtr {
    
    if (cgImage == nil) {
        NSLog(@"Error: CGImage 不能是 nil ! ");
        return [NSData data];
    }
    
    if (widthPtr == NULL || heightPtr == NULL) {
        NSLog(@"Error: 宽度或高度不能为空。");
        return [NSData data];
    }
    
    size_t originalWidth  = CGImageGetWidth(cgImage);
    size_t originalHeight = CGImageGetHeight(cgImage);
    
    // Calculate the width and height of the new texture buffer
    // The new texture buffer will have power of 2 dimensions.
    size_t width  = [self aspectSizeWithDataDimension:originalWidth];
    size_t height = [self aspectSizeWithDataDimension:originalHeight];
    
    // Allocate sufficient storage for RGBA pixel color data with
    // the power of 2 sizes specified
    NSMutableData *imageData =
    [NSMutableData dataWithLength:height * width * kBytesPerPixels]; // 4 bytes per RGBA pixel
    
    // Create a Core Graphics context that draws into the
    // allocated bytes
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef cgContext = CGBitmapContextCreate([imageData mutableBytes],
                                                   width, height,
                                                   kBitsPerComponent,
                                                   kBytesPerRow(width),
                                                   colorSpace,
                                                   kCGImageAlphaPremultipliedLast); // RGBA
    CGColorSpaceRelease(colorSpace);
    // Flip the Core Graphics Y-axis for future drawing
    CGContextTranslateCTM (cgContext, 0, height);
    CGContextScaleCTM (cgContext, 1.0, -1.0);
    // Draw the loaded image into the Core Graphics context
    // resizing as necessary
    CGContextDrawImage(cgContext, CGRectMake(0, 0, width, height), cgImage);
    CGContextRelease(cgContext);
    
    *widthPtr  = width;
    *heightPtr = height;
    
    return imageData;
}

主流程:

1、规格化图片尺寸,让其符合 (2^n, 2^m)[n,m 均为自然数 ]
为什么?

(1)因为 CGBitmapContextCreate支持的是 size_t ((long) unsigned int) 的【来个 0.25 个像素也是醉了】;

(2)而且 OpenGL ES 支持的最大像素尺寸也是有限制的,当前环境支持的最大值是 (4096, 4096),这个值由以下两个 xx_MAX_xx 得到【就在 aspectSizeWithDataDimension: 方法内】:

    GLint _2dTextureSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_2dTextureSize);
    
    GLint cubeMapTextureSize;
    glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &cubeMapTextureSize);

glGetIntegerv函数是可以获取当前环境下所有的默认常量的方法;

2、确定图片像素最终输出的颜色空间

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();,这个最容易出错,它的颜色格式要和你使用glTexImage2D函数指名的颜色格式要一致,不然不可能显示正常【如,你这里定义成 CYMK, 指名了 GL_RGB 那么肯定不对的】

3、确定最终像素的位深与位数

这里是明确用多少位来表示一个像素位【如:R 用 8 位表示】,一个像素由多少个成员组成【如:RGBA 就是 4 个】

4、创建上下文环境

Bitmap 图就是像素图,包含所有的像素信息,没有什么 jpg / png 容器什么的;
CGBitmapContextCreate函数的各个参数都很明显了,所以就不废话了;

5、变换像素的坐标空间

为什么?
Texture 纹理坐标空间的坐标原点在,左下角,而苹果设备显示的图形的坐标系的坐标原点在左上角,刚好是反的;

6、绘制生成最终的像素数据


谢谢看完,如果有描述不清或讲述错误的地方,请评论指出!!!

2013-12-10 11:15:33 shaojieli 阅读数 1046


范例工程:

LxOpenglES_Texture.zip


- (void)setupTexture {

    

    

// 2 x 2 Image, 3 bytes per pixel(R, G, B)

    GLubyte pixels[4 *3] =

    {

        255, 0, 0, // Red

        0, 255, 0, // Green

        0, 0, 255, // Blue

        255, 255, 0 // Yellow

    };

    

    // 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

    glTexImage2D(GL_TEXTURE_2D,0, GL_RGB,2, 2,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);

    

}



- (void)render {

 

    // 改变背景

    glClearColor(0,104.0/255.0, 55.0/255.0,1.0);

    glClear(GL_COLOR_BUFFER_BIT);

    

    

    // 指定渲染的范围

    glViewport(0,0, self.frame.size.width,self.frame.size.height);

    

    // 关联顶点数据

    glVertexAttribPointer(0,3, GL_FLOAT,GL_FALSE, 0,Vertices);

    

    // 启用属性

    glEnableVertexAttribArray(0);

    

    

    //启用纹理

    glVertexAttribPointer(1,2, GL_FLOAT,GL_FALSE, 0,texCoords);

    glEnableVertexAttribArray(1);

    

    

    

    // 绘制图形

//    glDrawArrays(GL_POINTS, 0, 3);

//    glDrawArrays(GL_LINE_LOOP, 0, 3);

    glDrawArrays(GL_TRIANGLES,0, 3);

    

    //缓存中的内容输出至屏幕

    [_contextpresentRenderbuffer:GL_RENDERBUFFER];

}



OpenGL ES on iOS

阅读数 3024

iOS之OpenGL ES

阅读数 187

OpenGL ES on iOS

阅读数 14

OpenGL ES on iOS

阅读数 11

没有更多推荐了,返回首页