欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenGL Mac环境搭建

程序员文章站 2023-12-25 10:57:33
...

什么是OpenGL

一般它被认为是一个API(Application Programming Interface, 应用程序编程接口),包含了一系列可以操作图形、图像的函数。然而,OpenGL本身并不是一个API,它仅仅是一个由Khronos组织制定并维护的规范

OpenGL英文全称 Open Graphics Library ,是一个开放图形库,定义了一个跨编程语言、跨平台的API规范,它用于生成二维、三维图像。这个接口由近三百五十个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。而另一种程序界面系统是仅用于Microsoft Windows上的Direct3D。OpenGL常用于CAD、虚拟实境、科学可视化程序和电子游戏开发。

实际的OpenGL库的开发者通常是显卡的生产商。你购买的显卡所支持的OpenGL版本都为这个系列的显卡专门开发的。当你使用Apple系统的时候,OpenGL库是由Apple自身维护的。在Linux下,有显卡生产商提供的OpenGL库,也有一些爱好者改编的版本。这也意味着任何时候OpenGL库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。
(以上摘自learnopengl)

在我们画出出色的效果之前,首先要做的就是创建一个OpenGL上下文(Context)和一个用于显示的窗口。然而,这些操作在每个系统上都是不一样的,OpenGL有目的地从这些操作抽象(Abstract)出去。这意味着我们不得不自己处理创建窗口,定义OpenGL上下文以及处理用户输入。幸运的是,有一些库已经提供了我们所需的功能,其中一部分是特别针对OpenGL的。这些库节省了我们书写操作系统相关代码的时间,提供给我们一个窗口和上下文用来渲染。最流行的几个库有GLUT,SDL,SFML和GLFW。

我们搭建的使用两个库GLFW和GLEW,我的理解就是他们可以帮我们方便跨平台的开发使用代码。

开源库简介

  • GLFW:
    GLFW是一个跨平台的OpenGL应用框架,支持窗口创建,接受输入和事件等功能。

  • GLEW:
    GLEW是一个基于OpenGL图形接口的跨平台的C++扩展库。GLEW能自动识别当前平台所支持的全部OpenGL高级扩展涵数。只要包含glew.h头文件,就能使用gl,glu,glext,wgl,glx的全部函数。GLEW支持目前流行的各种操作系统。

Mac Xcode PC应用环境配置

开发环境:MacOs Sierra 10.12.6
开发工具:Xcode 8.3.3

看了网上的一些教程都是在电脑上面运行的,在移动端Android和IOS上面运行的没有那么多,这里配置Xcode的环境可以在看到教程的代码可以直接运行在PC上面看到效果。

配置GLFW和GLEW库

看了几种安装的方式,第一种在GitHub上面下载库的源代码然后使用Cmake编译来安装,这种我感觉有点麻烦,我使用的是使用Homebrew来安装。两种方式最后都是在Mac的usr下面生成C++头文件和库文件。Homebrew的安装过程省略了因为比较简单。

安装命令

brew install glfw
brew install glew

  • 两个库安装完成以后如果提示没有link那么就执行link指令link一下

    brew link glfw
    brew link glew

  • 得到的如果link失败我看了提示是没有权限读取usr/local/include目录的权限

    使用sudo chmod u+w /usr/local/include 提升一下这个文件为可写

安装完成以后brew把安装包放到了/usr/local/Cellar下面,拷贝安装包link到了/usr/local/include下面,拷贝lib放到了/usr/local/lib下面

Xcode配置

  • 打开Perference菜单找到Locations选项,打开CustomPaths选项卡配置如下信息。
Name Display Name Path
glew_header glew_header /usr/local/Cellar/glew/2.1.0/include
glew_lib glew_lib /usr/local/Cellar/glew/2.1.0/lib
glfw_header glfw_header /usr/local/Cellar/glfw/3.2.1/include
glfw_lib glfw_lib /usr/local/Cellar/glfw/3.2.1/lib
  • 创建Xcode command line tool项目,现在C++ 语言在Build Settings 的Header Search Paths里面配置
$(glew_header) $(glfw_header)

在Library Search Paths配置

$(glew_lib) $(glfw_lib)

在项目General中找到Linked Frameworks and libraries 添加OpenGL.framework,libGLEW.2.1.0.dylib,libglfw3.3.2.dylib文件
后面那两个库在Xcode中找不到,点击Finder右键前往文件夹到/usr/local/Cellar/下面的两个安装包的lib下面找到我们要的文件。

配置完成以后在main.cpp中试运行这些代码

#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

int main(int argc, const char * argv[]) {
    GLFWwindow* window;
    if (!glfwInit())
        return -1;
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    while (!glfwWindowShouldClose(window))
    {
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();
    return 0;
}

运行成功显示一个窗口则一切OK了。

Mac AndroidStudio环境配置

因为本人做Android开发比较多,一些教程的PC实例会转变成为Android手机的实例来看效果。我们没有使用OpenGL的Java提供的API来进行开发学习,直接使用的C++。

开发环境:MacOs Sierra 10.12.6
开发工具:AndroidStudio 2.3.3

AndroidStudio环境配置

首先NDK的东西和Cmake的使用要都弄好,可以看以前的一篇文章NDK配置。我们以后使用实例都是通过cmake来编译C++的代码的。

完成经典的三角形实例

创建一个Android项目,并且勾选C++的支持,我们就可以直接使用模版里面提供的Cmake的使用了,没有勾选支持C++的Android项目见上面的Ndk配置的文章使用Cmake拷贝那些东西过去就可以了。完整的项目代码结构如下:

OpenGL Mac环境搭建

Android应用层代码构建

  • 声明jni函数以及引入c++库文件类TriangleLib
public class TriangleLib {
    static {
        System.loadLibrary("triangle-lib");
    }

    //初始化GLES
    public static native boolean init();

    //设置GLES宽高
    public static native void resize(int wight, int height);

    //绘制三角形
    public static native void drawTriangle();
}
  • 创建TriangleView继承自GLSurfaceView。这个View我现在的理解就是链接OpenGL代码和Android显示的一个中介。
public class TriangleView extends GLSurfaceView {
    public TriangleView(Context context) {
        this(context, null);
    }

    public TriangleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        //设置颜色模式为RGB_888,透明度,色度深度为16
        setEGLConfigChooser(8, 8, 8, 0, 16, 0);
        //设置EGL版本为3
        setEGLContextClientVersion(3);
        //创建GLSurfaceView的渲染器,现在不知道具体有啥作用
        setRenderer(new TriangleRender());
    }

    class TriangleRender implements Renderer {

        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            TriangleLib.init();
        }

        @Override
        public void onSurfaceChanged(GL10 gl10, int wight, int height) {
            TriangleLib.resize(wight, height);
        }

        @Override
        public void onDrawFrame(GL10 gl10) {
            TriangleLib.drawTriangle();
        }
    }

C++底层代码构建

在cpp目录下面建立triangle-lib.cpp文件来写我们的OpenGL代码,这个是我们要重点理解的。

#include <GLES3/gl3.h>
#include <android/log.h>

#define LOG_TAG "TRIANGLE-LIB"
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

#include <jni.h>
#include <stdlib.h>

//顶点着色器
static const char VERTEX_SHADER[] =
        "#version 300 es\n"
                "layout(location = 0) in vec4 vPosition;\n"
                "void main(){\n"
                "gl_Position = vPosition;\n"
                "}\n";
//片段着色器
static const char FRAGMENT_SHADER[] =
        "#version 300 es\n"
                "precision mediump float;\n"
                "out vec4 fragColor;\n"
                "void main(){\n"
                "fragColor = vec4(1.0,0.0,0.0,1.0);\n"
                "}\n";
//顶点着色器在标准化设备坐标中的位置
static const GLfloat VERTEX[] = {
        0.0f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
};

bool checkGlError(const char *funcName) {
    GLint err = glGetError();
    if (err != GL_NO_ERROR) {
        ALOGE("GL error after %s(): 0x%08x\n", funcName, err);
        return true;
    }
    return false;
}

//创建Shader
GLuint createShader(GLenum shaderType, const char *src) {
    GLuint shader = glCreateShader(shaderType);
    if (!shader) {
        checkGlError("glCreateShader");
        return 0;
    }
    glShaderSource(shader, 1, &src, NULL);
    GLint compiled = GL_FALSE;
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled) {
        GLint infoLogLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);
        if (infoLogLen > 0) {
            GLchar *infoLog = (GLchar *) malloc(infoLogLen);
            if (infoLog) {
                glGetShaderInfoLog(shader, infoLogLen, NULL, infoLog);
                ALOGE("Could not compile %s shader:\n%s\n",
                      shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment",
                      infoLog);
                free(infoLog);
            }
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

//创建着色器应用
GLuint createProgram(const char *vtxSrc, const char *fragSrc) {
    GLuint vtxShader = 0;
    GLuint fragShader = 0;
    GLuint program = 0;
    GLint linked = GL_FALSE;
    vtxShader = createShader(GL_VERTEX_SHADER, vtxSrc);
    if (!vtxShader)
        goto exit;
    fragShader = createShader(GL_FRAGMENT_SHADER, fragSrc);
    if (!fragShader)
        goto exit;
    program = glCreateProgram();
    if (!program) {
        checkGlError("glCreateProgram");
        goto exit;
    }
    glAttachShader(program, vtxShader);
    glAttachShader(program, fragShader);
    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &linked);
    if (!linked) {
        ALOGE("Could not link program");
        GLint infoLogLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
        if (infoLogLen) {
            GLchar *infoLog = (GLchar *) malloc(infoLogLen);
            if (infoLog) {
                glGetProgramInfoLog(program, infoLogLen, NULL, infoLog);
                ALOGE("Could not link program:\n%s\n", infoLog);
                free(infoLog);
            }
        }
        glDeleteProgram(program);
        program = 0;
    }
    exit:
    glDeleteShader(vtxShader);
    glDeleteShader(fragShader);
    return program;
}

GLuint program;


extern "C" {
JNIEXPORT jboolean JNICALL
Java_com_lyman_hellotriangle_lib_TriangleLib_init(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL
Java_com_lyman_hellotriangle_lib_TriangleLib_resize(JNIEnv *env, jobject obj, jint width,
                                                    jint height);
JNIEXPORT void JNICALL
Java_com_lyman_hellotriangle_lib_TriangleLib_drawTriangle(JNIEnv *env, jobject obj);
}

JNIEXPORT jboolean JNICALL
Java_com_lyman_hellotriangle_lib_TriangleLib_init(JNIEnv *env, jobject obj) {
    program = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
    if (!program) {
        ALOGE("程序创建失败");
        return JNI_FALSE;
    }
    glClearColor(0, 0, 0, 0);
    return JNI_TRUE;
}

JNIEXPORT void JNICALL
Java_com_lyman_hellotriangle_lib_TriangleLib_resize(JNIEnv *env, jobject obj, jint width,
                                                    jint height) {
    glViewport(0, 0, width, height);
    glClear(GL_COLOR_BUFFER_BIT);
}

JNIEXPORT void JNICALL
Java_com_lyman_hellotriangle_lib_TriangleLib_drawTriangle(JNIEnv *env, jobject obj) {
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(program);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, VERTEX);
    glEnableVertexAttribArray(0);
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

总结

这个代码看上去很多,也有很多概念要先理解

  • Q:什么是着色器?
    A:OpenGL Mac环境搭建

  • Q:什么是 GLSL
    A:OpenGL着色器使用的是OpenGL着色器语言(OpenGL Shading Language)GLSL写的,以后再了解他的具体写法。

  • Q:上述代码的执行步骤总结
    A:
    OpenGL Mac环境搭建

    这里的每一个步骤具体的我也不知道是干啥,但是先有个大纲就好。
  • Q:上述代码步骤本人总结:

    A:声明顶点着色器(它就是我们在手机标准化设备坐标看到的2d画面的三个顶点)和片段着色器(计算每一个像素的最终颜色)的数据;

    创建上面两个着色器;

    创建着色器应用并且把上面两个着色器附加到着色器应用程序上;

    绘制图形;

上面的Android应用层的代码和C++的代码会成为我们以后学习使用OpenGL的一个固定的步骤。

项目完整代码

上一篇:

下一篇: