美文网首页
Swift下通过C进行OpenGL ES开发

Swift下通过C进行OpenGL ES开发

作者: 浪淘沙008 | 来源:发表于2023-04-18 15:50 被阅读0次

在iOS开发中可以通过苹果提供的GLKBaseEffect来配置光照、纹理、缓冲区等信息,但其配置的信息有所限制并且无法跨平台使用,可以通过引入C/C++开发文件来实现跨平台开发,实现代码如下:

swift文件

import UIKit
import OpenGLES
import GLKit

class ViewController: GLKViewController, GLKViewControllerDelegate {
    var context: EAGLContext!
    var _esContext: ESContext!
    override func viewDidLoad() {
        super.viewDidLoad()
        
        context = EAGLContext.init(api: .openGLES3)
        guard let context = context else {
            print("Failed to create es context")
            return
        }

        let newView = self.view as! GLKView
        newView.context = context
        newView.drawableDepthFormat = .format24
        self.delegate = self
        setupGL()
        
    }
    deinit {
        tearDownGL()
        if EAGLContext.current() == self.context {
            EAGLContext.setCurrent(nil)
        }
    }
}

extension ViewController {
    
    func glkViewControllerUpdate(_ controller: GLKViewController) {
        // 在swift中updata()方法不会调用,可以在此处调用或将逻辑移动到此处执行
        update()
    }
    
    func setupGL() {
        EAGLContext.setCurrent(self.context)
        memset(&_esContext, 0, MemoryLayout.size(ofValue: _esContext))
        _esContext.width = GLint(self.view.frame.width);
        _esContext.height = GLint(self.view.frame.height);
        esMain(&_esContext);
    }
    
    func update() {
        if _esContext.updateFunc != nil {
            _esContext.updateFunc(&_esContext, Float(self.timeSinceLastUpdate));
        }
    }
    
    override func glkView(_ view: GLKView, drawIn rect: CGRect) {
        _esContext.width = GLint(view.drawableWidth)
        _esContext.height = GLint(view.drawableHeight)
        
        if (_esContext.drawFunc != nil) {
            _esContext.drawFunc(&_esContext)
        }
    }
    
    func tearDownGL() {
        EAGLContext.setCurrent(self.context)
        if _esContext.shutdownFunc != nil {
            _esContext.shutdownFunc(&_esContext)
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        if self.isViewLoaded && self.view.window == nil {
            self.view = nil
            
            tearDownGL()
            
            if EAGLContext.current() == self.context {
                EAGLContext.setCurrent(nil)
            }
            self.context = nil
        }
    }
}

C文件实现代码如下:

#include "Hello_Triangle.h"

//
typedef struct {
    GLuint programObject;
} UserData;

GLuint LoadShader(GLenum type, const char *shaderSrc)
{
    GLuint shader;
    GLint compiled;
    
    shader = glCreateShader(type);
    if (shader == 0) {
        return 0;
    }
    // 加载shader源文件
    glShaderSource(shader, 1, &shaderSrc, NULL);
    // 编译shader
    glCompileShader(shader);
    
    // 获取shader编译状态
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    
    if (!compiled) {
        // 获取编译中出错的问题
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        
        if (infoLen > 1) {
            char *infoLog = malloc(sizeof(char) * infoLen);
            
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            esLogMessage("Error compiling shader:\n %s\n", infoLog);
            free(infoLog);
        }
        // 标记删除
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

int Init(ESContext * esContext)
{
    UserData * userData = esContext->userData;
    char vShaderStr[] =
       "#version 300 es                          \n"
       "layout(location = 0) in vec4 vPosition;  \n"
       "void main()                              \n"
       "{                                        \n"
       "   gl_Position = vPosition;              \n"
       "}                                        \n";

    char fShaderStr[] =
       "#version 300 es                              \n"
       "precision mediump float;                     \n"
       "out vec4 fragColor;                          \n"
       "void main()                                  \n"
       "{                                            \n"
       "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
       "}                                            \n";
    
    GLuint vertexShader;
    GLuint fragmentShader;
    GLuint programObject;
    GLint linked;
    
    // 加载顶点着色器和片源着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
    
    // 创建链接器
    programObject = glCreateProgram();
    
    if (programObject == 0) {
        return 0;
    }
    
    // 绑定shader
    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);
    
    // 链接链接器
    glLinkProgram(programObject);
    
    // 获取链接器状态
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
    if (!linked) {  // 如果链接器连接失败
        // 获取日志长度
        GLint infoLen = 0;
        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
        
        // 获取输出日志
        if (infoLen > 1) {
            char *infoLog = malloc(sizeof(char) * infoLen);
            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
            free(infoLog);
        }
        // 失败时删除程序,并返回失败
        glDeleteProgram(programObject);
        return FALSE;
    }
    
    // 标记删除,当所有链接断开后才会释放
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    
    // 将链接器存储起来
    userData->programObject = programObject;
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    return GL_TRUE;
}

// 绘制三角形
void Draw(ESContext * esContext)
{
    UserData * userData = esContext->userData;
    GLfloat vVertices[] = {
        0.0f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };
    
    // 设置视口
    glViewport(0, 0, esContext->width, esContext->height);
    
    // 清除缓存区颜色
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 设置连接器
    glUseProgram(userData->programObject);
    // 获取vPosition在链接器中的位置,也可以直接使用layout(location = 0) in vec4 vPosition;中location的值
     GLint location = glGetAttribLocation(userData->programObject, "vPosition");
    
    /**
     glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr)
     */
    // 将vvertices数据绑定到顶点着色器
    /**
     glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr)
     indx: 指定要修改的顶点属性的索引值,即在着色器中指定的统一变量的编号
     size: 每次读取数据的个数(如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a),纹理则是2个.)
     type: 数据类型,可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。
     normalized: 指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)
     stride:指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0
     ptr:指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0
     */
    glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
    //数据在GPU端是否可见,即,着色器能否读取到数据,由是否启用了对应的属性决定,这就是glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据
    glEnableVertexAttribArray(0);
    
    /**
     glDrawArrays(GLenum mode, GLint first, GLsizei count)
     */
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

void Shutdown(ESContext *esContext)
{
    UserData * userData = esContext->userData;
    glDeleteProgram(userData->programObject);
}


int esMain ( ESContext *esContext )
{
    esContext->userData = malloc(sizeof(UserData));
    esCreateWindow(esContext, "Hello Triangle", esContext->width, esContext->height, ES_WINDOW_RGB);
    if (!Init(esContext)) {
        return GL_FALSE;
    }
    
    // 注册释放和绘制方法
    esRegisterShutdownFunc(esContext, Shutdown);
    esRegisterDrawFunc(esContext, Draw);
    
    return GL_TRUE;
}

项目地址

相关文章

网友评论

      本文标题:Swift下通过C进行OpenGL ES开发

      本文链接:https://www.haomeiwen.com/subject/guohjdtx.html