欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

简易教程:在iOS上使用Assimp库进行OpenGL重学和编译

最编程 2024-08-04 10:38:37
...

先看我们最终实现的效果如下.

assimp 在Mac 中的编译

assimp 的编译依赖cmake 工具,因此我们需要在mac上安装 cmake

查看cmake 是否安装

打开终端 输入

cmake

要是显示以下内容就代表安装了cmake


image.png

安装cmake

终端输入

brew install cmake

结果如下说明安装完成


brew 是 homebrew 工具,这个工具的安装比较简单,就不说明了

�## 编译 assimp

打开终端

    1. cd 到指定目录下 我的根目录是 assimpFile
    1. git clone https://github.com/assimp/assimp.git
    1. cd assimp/port/iOS/
    1. ./build.sh 这个过程时间比较长

编译结束


image.png

从上截图我们能看出来编译的结果路径在./lib/iOS 中

由于我只编译了x86-64 和arm64 因此,我的 ./lilb/iOS 文件是这样子的


这里面的.a 文件就是我们需要的库文件

编译完成 assimp/include中的文件就是头文件
assimp/lib/ios 中的文件就是生成的assimp 库
将以上两部分copy 的一个文件夹下面供使用就可以了
这里build.sh 文件编译了6中结构体arm64e arm64 armv7s armv7 x86_64 i386 为了减少编译速度,我们可以修改编译的结构体选择我们需要的即可

我copy 到一个文件的样子如下


assimp 集成的Xcode 中
  • 1 创建一个新的工程


  • 2 将includes 和lib/ios中的.a 文件拖入到工程中去
    工程结构如下


    1. 修改 include 搜索路径 :Build Setting search paths 中的 Header Search Paths 添加include 所在本地路径 (可以看看.a 文件,根据.a文件路径修改 include 路径)
    1. 将使用该库的.m 文件修改成 .mm 该工程使用的是viewcontroller.m 修改成


  • 4.修改viewController.mm 文件内容如下

#import "ViewController.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}


@end

    1. 编译工程
      要是工程编译不出错,说明库安装成功

assimp 库在ios中的使用

基础工程

GLBaseViewController 文件 ,我们使用opengl 的基本方法顺序

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import "OpenGLUtilsHeader.h"
#import "GLBaseBindObject.h"

#define __weakSelf  __weak typeof(self) weakSelf = self;

NS_ASSUME_NONNULL_BEGIN



@interface GLBaseViewController : GLKViewController
@property (nonatomic ,strong) EAGLContext * eagcontext;
@property (nonatomic ,assign) GLuint  program;
@property (nonatomic ,strong) Shader * shader ;
@property (nonatomic ,strong) Vertex * vertex ;
@property (nonatomic ,strong) GLBaseBindObject * bindObject ;
@property (nonatomic ,strong) NSMutableArray * vertexArr ;

///眼的位置在 0,0,1 看向 原点 ,眼的正方向是y轴,   看的区域是0.1 到20   角度是85
-(GLKMatrix4 )mvp;

-(void)loadVertex;
-(void)initSubObject;
-(void)createTextureUnit;
@end
#import "GLBaseViewController.h"

@interface GLBaseViewController ()
@property (nonatomic ,strong) TextureUnit * textureUnit0 ;

@end

@implementation GLBaseViewController

-(void)createEagContext{
    self.eagcontext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:self.eagcontext];
}

-(void)configure{
    GLKView *view = (GLKView*)self.view;
    view.context = self.eagcontext;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    
    
}
-(void)initCustom{
    self.vertexArr = [NSMutableArray array];
    glEnable(GL_DEPTH_TEST);
    
}

-(void)initSubObject{
    
}
-(void)createTextureUnit{
  
}

-(void)createShader{
    __weakSelf
    self.shader = [Shader new];
    [self.shader compileLinkSuccessShaderName:self.bindObject.getShaderName completeBlock:^(GLuint program) {
        [weakSelf.bindObject BindAttribLocation:program];
    }];
    [self.bindObject setUniformLocation:self.shader.program];
    
 
}

-(void)loadVertex{
    
    
}


-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    
  
}

-(void)viewDidLoad{
    [super viewDidLoad];
    [self createEagContext];
    [self initSubObject];
    [self configure];
    [self initCustom];
    [self createShader];
    [self createTextureUnit];
    [self loadVertex];
}


@end

GLBaseBindObject 是shader 的配置基础类


#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>



#define uniformsMaxCount 100

NS_ASSUME_NONNULL_BEGIN

@interface GLBaseBindObject : NSObject
{
    @public
     GLint uniforms[uniformsMaxCount];
}

-(void)BindAttribLocation:(GLuint) program;
-(void)setUniformLocation:(GLuint)program;
-(NSString *)getShaderName;



@end

NS_ASSUME_NONNULL_END


#import "GLBaseBindObject.h"

@implementation GLBaseBindObject

-(void)setUniformLocation:(GLuint)program{
    
}

-(void)BindAttribLocation:(GLuint) program{
   
}
-(NSString *)getShaderName{
    return nil;
}
@end

shader 文件, 名字是Assimp

precision mediump float;
varying vec2 TexCoords;
uniform sampler2D texture_diffuse;
uniform sampler2D texture_specular;
uniform sampler2D texture_height;

void main()
{
    vec4 diffuse = texture2D(texture_diffuse, TexCoords);
    vec4 specular = texture2D(texture_specular, TexCoords);
    vec4 height = texture2D(texture_height, TexCoords);
    gl_FragColor = diffuse+specular+height;
}

precision mediump float;
attribute vec3 aPos;
//attribute vec3 aNormal;
attribute vec2 aTexCoords;

varying vec2 TexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    TexCoords = aTexCoords;
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

assimp 通用代码

AssimpViewController 是真正加载 模型的类


#import "AssimpViewController.h"
#import "AssimpBindObject.h"
#import "AssimpParse.h"


@interface AssimpViewController ()
@property (nonatomic ,strong) AssimpParse * assimpParse ;
@end


@implementation AssimpViewController

-(void)initSubObject{
    //生命周期三秒钟
    self.bindObject = [AssimpBindObject new];
    self.assimpParse = [ AssimpParse new];
}

-(void)loadVertex{
    [self.assimpParse parse];
}


-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glClearColor(1, 1, 1, 1);
    static GLfloat angle=0;
    angle ++ ;
    //    angle = 45;
    GLKMatrix4 mode =GLKMatrix4MakeRotation(angle*M_PI/180, 0, 1, 0);
    float scale = 0.2;
    mode = GLKMatrix4Scale(mode, scale, scale, scale);
    mode = GLKMatrix4Translate(mode, 0, -5, 0);
    glUniformMatrix4fv(self.bindObject->uniforms[uniform_model], 1, 0,mode.m);
    
    GLKMatrix4 viewMatrix =
    GLKMatrix4MakeLookAt(
                         0.0, 0.0, 5.0,   // Eye position
                         0.0, 0.0, 0.0,   // Look-at position
                         0.0, 1.0, 0.0);  // Up direction
    glUniformMatrix4fv(self.bindObject->uniforms[uniform_view], 1, 0,viewMatrix.m);
    
    GLfloat aspectRatio= CGRectGetWidth([UIScreen mainScreen].bounds) / CGRectGetHeight([UIScreen mainScreen].bounds);
    GLKMatrix4 projectionMatrix =
    GLKMatrix4MakePerspective(
                              GLKMathDegreesToRadians(85.0f),
                              aspectRatio,
                              0.1f,
                              20.0f);
    glUniformMatrix4fv(self.bindObject->uniforms[uniform_projection], 1, 0,projectionMatrix.m);

    [self.assimpParse draw:self.bindObject];

}
#import "GLBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN

@interface AssimpViewController : GLBaseViewController

@end
NS_ASSUME_NONNULL_END

AssimpBindObject shader 的具体绑定类


#import "GLBaseBindObject.h"

NS_ASSUME_NONNULL_BEGIN
// Attribute identifiers
typedef enum {
    aPos,
//    aNormal,
    aTexCoords,
} BaseBindAttribLocation;


typedef union {
    struct{
        GLKVector3 aPos;
//        GLKVector3 aNormal;
        GLKVector2 aTexCoords;
    };
    float a[5];
}assimpBindAtt;

typedef enum {
    uniform_model,
    uniform_view,
    uniform_projection,
    uniform_diffuse,
    uniform_specular,
    uniform_height
} assimpUniformLocation;

@interface AssimpBindObject : GLBaseBindObject

@end

NS_ASSUME_NONNULL_END

#import "AssimpBindObject.h"

@implementation AssimpBindObject
-(void)BindAttribLocation:(GLuint)program{
    glBindAttribLocation(program, aPos, "aPos");
    glBindAttribLocation(program, aTexCoords, "aTexCoords");
}
-(void)setUniformLocation:(GLuint)program{
    self->uniforms[uniform_model] = glGetUniformLocation(program, "model");
    self->uniforms[uniform_view] = glGetUniformLocation(program, "view");
    self->uniforms[uniform_projection] = glGetUniformLocation(program, "projection");
    self->uniforms[uniform_diffuse] = glGetUniformLocation(program, "texture_diffuse");
    self->uniforms[uniform_specular] = glGetUniformLocation(program, "texture_specular");
    self->uniforms[uniform_height] = glGetUniformLocation(program, "texture_height");
}

-(NSString *)getShaderName{
    return @"Assimp";
}
@end

具体绑定内容不做介绍了

核心代码介绍

这里我们采用库加载文件 .obj 文件 .因此我们需要知道 assimp 库的具体使用.

assimp 数据结构
image
  • 和材质和网格(Mesh)一样,所有的场景/模型数据都包含在Scene对象中。Scene对象也包含了场景根节点的引用。
  • 场景的Root node(根节点)可能包含子节点(和其它的节点一样),它会有一系列指向场景对象中mMeshes数组中储存的网格数据的索引。Scene下的mMeshes数组储存了真正的Mesh对象,节点中的mMeshes数组保存的只是场景中网格数组的索引。
  • 一个Mesh对象本身包含了渲染所需要的所有相关数据,像是顶点位置、法向量、纹理坐标、面(Face)和物体的材质。
  • 一个网格包含了多个面。Face代表的是物体的渲染图元(Primitive)(三角形、方形、点)。一个面包含了组成图元的顶点的索引。由于顶点和索引是分开的,使用一个索引缓冲来渲染是非常简单的。
  • 最后,一个网格也包含了一个Material对象,它包含了一些函数能让我们获取物体的材质属性,比如说颜色和纹理贴图(比如漫反射和镜面光贴图)。

上述官方的话在拆开说
scene 是 manager . 包含场景和模型所有数据 .
node 相当于 组件或者零件, 一个模型有很多node组成. 打个比方scene 相当于汽车,node 是组成汽车的零件
scene->mrootNode 指向的是node的根 . 相当于汽车部件 ,汽车部件可能有引擎和轮胎组成,而轮胎可能有橡胶和螺丝等组成,因此node 是一种树机构
scene->mMehses 装有所有的顶点数据, 包含 顶点 法线 纹理贴图 以及顶点索引等 (我们node 中的 mMeshes 的 int数组,每个值其实指向的是scene->mMehses的下标)
scene->mMehses 的装有的对象是 Mesh , 装有使用opengl进行一次绘制的所有信息 ,包括 顶点 法线 纹理贴图 以及顶点索引等. (一个模型的加载需要opengl 进行多次顶点渲染,渲染是以mesh为基本单位的,因此我们这里只要学会使用mesh的一次加载和node的树状遍历就可以正确解析模型了)

assimp 的使用 具体步骤
  • 1.获取 scene
  Assimp::Importer importer;
    NSString * pathStr = [[NSBundle mainBundle]pathForResource:@"nanosuit" ofType:@"obj"];
    const char *path = pathStr.UTF8String;
    const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
    if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
    {
        NSLog(@"ERROR::ASSIMP: %s",importer.GetErrorString());
        return;
    }
    
    NSString * directory =[pathStr stringByDeletingLastPathComponent];
 NSLog(@"%@",directory);
    self.path = directory;

就是用库读取资源文件.

  • 2.遍历node
-(void)processNode:(aiNode *)node{
    for(unsigned int i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh *mesh = self.scene->mMeshes[node->mMeshes[i]];
        Mesh * meshObj =[self processMesh:mesh];
        [meshObj parse];
        [self.meshs addObject:meshObj];
    }
    // 接下来对它的子节点重复这一过程
    for(unsigned int i = 0; i < node->mNumChildren; i++)
    {
        [self processNode:node->mChildren[i]];
    }
}

1 .检查 node 是否含有mesh . 有mesh的标志是 node->mNumMeshes 的数量不为0. 有mesh ,就加载mesh
2.遍历node是否有 childNode , 有mNumChildren的标志是node->mNumChildren 不为0,有childNode 就递归调用加载node

    1. 加载mesh
-(Mesh*) processMesh:(aiMesh *)mesh{
    Mesh * meshObj= [Mesh new];
    for(unsigned int i = 0; i < mesh->mNumVertices; i++)
    {
        MeshVertex * vertex = [MeshVertex new];
        GLKVector3  vector;
        // positions
        vector.x = mesh->mVertices[i].x;
        vector.y = mesh->mVertices[i].y;
        vector.z = mesh->mVertices[i].z;
        vertex.Position = vector;

        // texture coordinates
        if(mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
        {
            GLKVector2 vec;
            vec.x = mesh->mTextureCoords[0][i].x;
            vec.y = mesh->mTextureCoords[0][i].y;
            vertex.TexCoords = vec;
        }
        else
            vertex.TexCoords = GLKVector2Make(0.0f, 0.0f);
        [meshObj.vertices  addObject:vertex];
    }
    
    for(unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];
        // retrieve all indices of the face and store them in the indices vector
        for(unsigned int j = 0; j < face.mNumIndices; j++)
            [meshObj.indices addObject:@(face.mIndices[j])];
    }
    aiMaterial* material = self.scene->mMaterials[mesh->mMaterialIndex];
    //   1. diffuse maps
    NSMutableArray * diffuse =[self loadMaterialTextures:material type:aiTextureType_DIFFUSE typeName:@"texture_diffuse"];
    [meshObj.textures addObjectsFromArray:diffuse];
    // 2. specular maps
    NSMutableArray *  specularMaps = [self loadMaterialTextures:material type:aiTextureType_SPECULAR typeName:@"texture_specular"];
    [meshObj.textures addObjectsFromArray:specularMaps];

    // 4. height maps
    NSMutableArray * heightMaps = [self loadMaterialTextures:material type:aiTextureType_AMBIENT typeName:@"texture_height"];
    [meshObj.textures addObjectsFromArray:heightMaps];
//
    return meshObj;
}

mesh的类型是aiMesh 结构体
aiMesh->mNumVertices 代表该mesh的顶点数量
aiMesh->mVertices装有顶点数据
aiMesh->mTextureCoords装有顶点纹理
aiMesh->mNormals装有顶点发现

aiMesh->mNumFaces 装有三角形或者线,点数量(这里是三角形)
aiMesh->mFaces代表面,代表所有的三角形
aiMesh->mFaces[i]->mNumIndices 包含 三角形的索引个数
aiMesh->mFaces[i]-mIndices[j] 具体每个顶点的索引
该索引索引的顶点是该mesh中装有的顶点

aiMesh ->mMaterialIndex 指示该mesh 所用的材质索引,所有的材质都是在scene的mMaterials中

该方法 中我们把 解析mesh 所有的顶点 纹理 以及顶点索引 和材质都存放在了对象Mesh 中. 我们知道mesh 是加载的最小单位,因此我们把这些数据组装在一起

    1. mesh 顶点的具体加载
-(void)parse{
    self.vertex= [Vertex new];
    int vertexNum =self.vertices.count;
    [self.vertex allocVertexNum:vertexNum andEachVertexNum:5];
    for (int i=0; i<vertexNum; i++) {
        float onevertex[5];
        MeshVertex * meshVertex = self.vertices[i];
        for (int j=0; j<3; j++) {
            onevertex[j]=meshVertex.Position.v[j];
        }
        for (int j=0; j<2; j++) {
            onevertex[j+3]=meshVertex.TexCoords.v[j];;
        }
        [self.vertex setVertex:onevertex index:i];
    }
    [self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
    self.elementVertex  = [VertexElement new];
    [self.elementVertex allocWithArray:self.indices];
    
}

这里Vertex 和elementVertex 是对顶点加载的封装,具体代码会在文章结尾贴出

  • 5.纹理加载
-(NSMutableArray*) loadMaterialTextures:(aiMaterial *)mat type:(aiTextureType) type typeName:(NSString *) typeName{
    NSMutableArray * textures =  [NSMutableArray new];
    for(unsigned int i = 0; i < mat->GetTextureCount(type); i++)
    {
        aiString str;
        mat->GetTexture(type, i, &str);
        NSString * filename =[NSString stringWithFormat:@"%@/%s",self.path,str.C_Str()];
        UIImage * image = [UIImage imageWithContentsOfFile:filename];
        AssimpTextureUnit * unit = [AssimpTextureUnit new];
        unit.typeName = typeName;
        [unit setImage:image andConfigTextureUnit:nil];
        [textures addObject:unit];
    }
    return textures;
}
    1. 绘制 mesh
-(void)draw:(AssimpBindObject * )bindObject{
    for (int i=0; i<self.textures.count; i++) {
        AssimpTextureUnit * unit = [self.textures objectAtIndex:i];
        [unit activeTextureUnit:GL_TEXTURE0+i];
        GLint location = -1;
        if ([unit.typeName isEqualToString:@"texture_diffuse"]) {
            location = uniform_diffuse;
        }else if ([unit.typeName isEqualToString:@"texture_height"]){
            location = uniform_height;

        }else if ([unit.typeName isEqualToString:@"texture_specular"]){
            location = uniform_specular;

        }
        [unit bindtextureUnitLocationAndShaderUniformSamplerLocation:bindObject->uniforms[location]];
    }
    
    
    [self.vertex enableVertexInVertexAttrib:aPos numberOfCoordinates:3 attribOffset:0];
    [self.vertex enableVertexInVertexAttrib:aTexCoords numberOfCoordinates:2 attribOffset:sizeof(float)*3];
    [self.elementVertex drawElementIndexWithMode:GL_TRIANGLES];
}

这里很关键

首先我们先加载纹理图 我们在解析纹理的时候,只是把纹理图读取到内存中了.但是没有和纹理单元绑定.因此在绘制的时候,我们首先需要绑定纹理单元和纹理
我们需要enable (使能) 我们已经在内存创建好的顶点数据.告诉opengl 我们现在需要使用那些顶点,很关键,忘记了,图像出不来
使用索引进行 绘制

到此主要流程完毕.

vertex 和 顶点索引以及纹理贴图省略的代码

@interface Vertex()

@property (nonatomic ,assign)   GLfloat  *vertex; ;
@property (nonatomic ,assign) GLsizei vertexNum ;
@property (nonatomic ,assign) GLsizei eachVertexNum ;
@property (nonatomic, assign) GLuint vertexBuffers;
@property (nonatomic ,assign) GLenum usage ;
@end

@implementation Vertex

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self _customInit];
    }
    return self;
}

-(void)_customInit{
     glGenBuffers(1, &_vertexBuffers);
}

-(NSInteger)getAllocSpaceByteNum{
    return self.getVertexWidth*self.vertexNum;
}
-(GLsizei)getVertexWidth{
    return sizeof(GLfloat) * self.eachVertexNum;
}

-(void)allocVertexNum:(GLsizei)vertexNum andEachVertexNum:(GLsizei)eachVertexNum{
    [self releaseVertex];
    self.vertexNum = vertexNum;
    self.eachVertexNum = eachVertexNum;
     self.vertex =(GLfloat*)malloc(self.getAllocSpaceByteNum);
    memset( self.vertex, 0,  self.getAllocSpaceByteNum);
}

-(void)setVertex:(GLfloat *)vertex index:(NSInteger)index{
    if (self.vertex) {
        NSInteger offset = index * self.eachVertexNum;
        for (NSInteger i = 0; i<self.eachVertexNum; i++) {
             self.vertex[offset+i] = vertex[i];
        }
    }else{
        NSLog(@"顶点没有空间");
    }
}

-(void)releaseVertex{
    if (self.vertex) {
        free( self.vertex);
         self.vertex = NULL;
    }
}

-(void)bindBufferWithUsage: (GLenum) usage{
    if (!self.vertexBuffers) {
        [self _customInit];
    }
    glBindBuffer(GL_ARRAY_BUFFER,
                 self.vertexBuffers);
    glBufferData( GL_ARRAY_BUFFER,
                 self.getAllocSpaceByteNum,
                 self.vertex,
                 usage);
    
}


-(void)enableVertexInVertexAttrib:(GLuint)index   numberOfCoordinates:(GLint)count attribOffset:(GLsizeiptr)offset{
    glBindBuffer(GL_ARRAY_BUFFER,
                 self.vertexBuffers);
    glEnableVertexAttribArray(index);
    glVertexAttribPointer(index,               // Identifies the attribute to use
                          count,               // number of coordinates for attribute
                          GL_FLOAT,            // data is floating point
                          GL_FALSE,            // no fixed point scaling
                          self.getVertexWidth ,         // total num bytes stored per vertex
                          NULL + offset);
#ifdef DEBUG
    {  // Report any errors
        GLenum error = glGetError();
        if(GL_NO_ERROR != error)
        {
            NSLog(@"GL Error: 0x%x", error);
        }
    }
    //    GL_INVALID_OPERATION  operation;
//    GL_INVALID_VALUE
#endif
}
-(void)drawVertexWithMode:(GLenum)mode  startVertexIndex:(GLint)first
         numberOfVertices:(GLsizei)count {
    NSAssert([self getAllocSpaceByteNum] >=
             ((first + count) *sizeof(GLfloat) * self.eachVertexNum),
             @"Attempt to draw more vertex data than available.");
    glBindBuffer(GL_ARRAY_BUFFER,
                 self.vertexBuffers);
     glDrawArrays(mode, first, count);
}

- (void)dealloc
{
    [self releaseVertex];
}

@end

#import "TextureUnit.h"

@interface TextureUnit ()
@property (nonatomic, assign) GLuint textureBuffer;
@property (nonatomic ,assign) int textureUnitLocation ;
@end

@implementation TextureUnit
- (instancetype)init
{
    self = [super init];
    if (self) {
        [self _customInit];
    }
    return self;
}
-(void)_customInit{
    self.textureUnitLocation = -1;
    glGenTextures(1, &_textureBuffer);
}

#pragma mark  - public
-(void)setPixels: (float*) pixels pixelsWidth:(GLsizei)width pixelsHeight:(GLsizei)height IntoTextureUnit:(GLenum)textureUnit andConfigTextureUnit:(nullable void(^)(void))configTextureUnitBlock PixelFormat:(GLint)internalformat{
    glActiveTexture(textureUnit);
    self.textureUnitLocation = [self _getTextureBindLocationForTexture:textureUnit];
    glBindTexture(GL_TEXTURE_2D,  _textureBuffer);
     glTexImage2D(GL_TEXTURE_2D, 0, internalformat , width, height, 0, internalformat, GL_FLOAT, pixels);
    if (configTextureUnitBlock) {
        configTextureUnitBlock();
    }else{
      [self _textureBaseConfig];
    }
}

-(void)setImage:(UIImage *)image IntoTextureUnit:(GLenum)textureUnit andConfigTextureUnit:(nullable void(^)(void))configTextureUnitBlock  {
    [self setImage:image IntoTextureUnit:textureUnit andConfigTextureUnit:configTextureUnitBlock PixelFormat:GL_RGBA];
}
-(void)setImage:(UIImage *)image IntoTextureUnit:(GLenum)textureUnit andConfigTextureUnit:(void(^)(void))configTextureUnitBlock  PixelFormat:(GLint)internalformat{
    glActiveTexture(textureUnit);
    self.textureUnitLocation = [self _getTextureBindLocationForTexture:textureUnit];
    glBindTexture(GL_TEXTURE_2D,  _textureBuffer);
    GLubyte *imageData = [self _getImageData:image];
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat , image.size.width, image.size.height, 0, internalformat, GL_UNSIGNED_BYTE, imageData);
    free(imageData);
    if (configTextureUnitBlock) {
        configTextureUnitBlock();
    }else{
        [self _textureBaseConfig];
    }
}
-(void)setImage:(UIImage *)image andConfigTextureUnit:(nullable  void(^)(void))configTextureUnitBlock {
    [self setImage:image andConfigTextureUnit:configTextureUnitBlock PixelFormat:GL_RGBA];
}
-(void)setImage:(UIImage *)image andConfigTextureUnit:(nullable  void(^)(void))configTextureUnitBlock  PixelFormat:(GLint)internalformat{
    glBindTexture(GL_TEXTURE_2D,  _textureBuffer);
    GLubyte *imageData = [self _getImageData:image];
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat , image.size.width, image.size.height, 0, internalformat, GL_UNSIGNED_BYTE, imageData);
    free(imageData);
    if (configTextureUnitBlock) {
        configTextureUnitBlock();
    }else{
        [self _textureBaseConfig];
    }
}
-(void)activeTextureUnit:(GLenum)textureUnit{
    glActiveTexture(textureUnit);
    glBindTexture(GL_TEXTURE_2D,  _textureBuffer);
    self.textureUnitLocation = [self _getTextureBindLocationForTexture:textureUnit];
}


-(void)bindtextureUnitLocationAndShaderUniformSamplerLocation:(GLint) uniformSamplerLocation {
    if (self.textureUnitLocation == -1) {
        NSLog(@"没有设置纹理单元或者设置纹理单元错误");
        return;
    }
    glUniform1i(uniformSamplerLocation, self.textureUnitLocation);
    GLenum error = glGetError();
    if(GL_NO_ERROR != error)
    {
        NSLog(@"GL Error: bindtextureUnitLocationAndShaderUniformSamplerLocation 0x%x", error);
    }
    
}

#pragma mark  - private
-(int)_getTextureBindLocationForTexture:(GLenum)texture{
    int textureLocation = texture-GL_TEXTURE0;
    if (textureLocation>=0 && textureLocation <32) {
        return textureLocation;
    }
    NSLog(@"超出纹理单元");
    return -1;
}
-(void)_textureBaseConfig{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

- (void*)_getImageData:(UIImage*)image{
    CGImageRef imageRef = [image CGImage];
    size_t imageWidth = CGImageGetWidth(imageRef);
    size_t imageHeight = CGImageGetHeight(imageRef);
    GLubyte *imageData = (GLubyte *)malloc(imageWidth*imageHeight*4);
    memset(imageData, 0,imageWidth *imageHeight*4);
    CGContextRef imageContextRef = CGBitmapContextCreate(imageData, imageWidth, imageHeight, 8, imageWidth*4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);
    CGContextTranslateCTM(imageContextRef, 0, imageHeight);
    CGContextScaleCTM(imageContextRef, 1.0, -1.0);
    CGContextDrawImage(imageContextRef, CGRectMake(0.0, 0.0, (CGFloat)imageWidth, (CGFloat)imageHeight), imageRef);
    CGContextRelease(imageContextRef);
    return  imageData;
}

@end

#import "VertexElement.h"

@interface VertexElement()
@property (nonatomic, assign) GLuint indexBuffer;
@property (nonatomic ,assign)  GLuint  *indexs;
@property (nonatomic ,assign) GLsizei count ;

@end

@implementation VertexElement
- (instancetype)init
{
    self = [super init];
    if (self) {
        [self _customInit];
    }
    return self;
}

-(void)_customInit{
    glGenBuffers(1, &_indexBuffer);
}

-(void)allocWithArray:(NSArray *)indexArr{
    if (self.indexs) {
        [self releaseIndexs];
    }
    self.count = indexArr.count;
    self.indexs =(GLuint*)malloc( self.count *sizeof(GLuint));
    memset( self.indexs, 0,   self.count *sizeof(GLuint));
    for (int i =0; i< self.count; i++) {
        self.indexs[i]=((NSNumber *)indexArr[i]).intValue;
    }
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,  self.count *sizeof(GLuint),self.indexs, GL_STATIC_DRAW);
#ifdef DEBUG
    {  // Report any errors
        GLenum error = glGetError();
        if(GL_NO_ERROR != error)
        {
            NSLog(@"GL Error: 0x%x", error);
        }
    }
#endif
}

-(void)allocWithIndexNum:(GLsizei)count  indexArr:(GLuint*)indexArr{
    if (self.indexs) {
        [self releaseIndexs];
    }
    self.count = count;
    self.indexs =(GLuint*)malloc(count *sizeof(GLuint));
    memset( self.indexs, 0,  count *sizeof(GLuint));
    for (int i =0; i<count; i++) {
        self.indexs[i]=indexArr[i];
    }
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, count *sizeof(GLuint),self.indexs, GL_STATIC_DRAW);
#ifdef DEBUG
    {  // Report any errors
        GLenum error = glGetError();
        if(GL_NO_ERROR != error)
        {
            NSLog(@"GL Error: 0x%x", error);
        }
    }
#endif
}

-(void)releaseIndexs{
    if (self.indexs) {
        free( self.indexs);
        self.indexs = NULL;
    }
}

-(void)drawElementIndexWithMode:(GLenum)mode{
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.indexBuffer);
     glDrawElements(mode, self.count, GL_UNSIGNED_INT,  0);
}

+ (void)drawElementIndexWithMode:(GLenum)mode indexNum:(GLsizei)count  indexArr:(GLuint*)indexArr
{
    glDrawElements(mode, count, GL_UNSIGNED_INT, indexArr);
}

- (void)dealloc
{
    [self releaseIndexs];
}


@end

git 中的OpenGLZeroStudyDemo(8)-Assimp(模型加载) 不能直接运行,因为缺少 .a 文件,需要读者自己编译 ,引用才行


编译工程git-TestAssimp
OpenGLZeroStudyDemo(8)-Assimp(模型加载)

以上工程的 编译的assist 库 .a 都没有上传,需要 读者自己编译按照博客顺序添加. .a 文件太大了. 上传不了git.实在是抱歉

assimp 编译
assimp git地址