1. 程式人生 > >OpenGL開發利用lwjgl類庫繪製一個三角形

OpenGL開發利用lwjgl類庫繪製一個三角形

文章目錄

本文一共分為2部分

  1. 建立展示視窗。
  2. 在視窗上繪製三角形。

在繪製三角形之前,需要建立一個OpenGL上下文(Context)和一個用於顯示的視窗。然而,這些操作在每個系統上都是不一樣的,OpenGL將這部分抽離了出去。

GLFW

GLFW是一個專門針對OpenGL的C語言庫,它提供了一些渲染物體所需的最低限度的介面。它允許使用者建立OpenGL上下文,定義視窗引數以及處理使用者輸入。

一、只繪製渲染視窗的程式碼

public class Demo01_open_window {

    public static void main(String[] args){
    
        glfwInit();//初始化

        glfwWindowHint(GLFW_VISIBLE, GL_FALSE);//設定視窗的可見性為false
        glfwWindowHint(GLFW_RESIZABLE,
GL_TRUE);//設定視窗是否可以重新調整大小 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL的主版本號 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);//OpenGL的副版本號 int width = 500;//視窗寬度 int height = 300;//視窗高度 long window = glfwCreateWindow(width, height, "Hello World!", NULL, NULL);//建立一個視窗,返回值是個long值。
glfwMakeContextCurrent(window);//通知GLFW將window的上下文設定為當前執行緒的主上下文 glfwShowWindow(window);//展示當前的視窗 GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());//獲取當前裝置的一些屬性 glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);//設定視窗的位置在最中間 GL.createCapabilities();//建立opengl上下文 //不間斷的一直渲染視窗,如果沒有這步迴圈,視窗會一閃而過。 while (!glfwWindowShouldClose(window)) { // 清除之前渲染的快取 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glfwSwapBuffers(window); // 交換前後緩衝區 glfwPollEvents();//檢查是否有鍵盤或是滑鼠事件 } glfwTerminate();//釋放之前分配的所有資源。 } }

執行結果:
在這裡插入圖片描述

上面程式碼中的相關說明

  • 如果不在一開始就呼叫glfwInit();函式,則後面的函式將都會失效。
  • OpenGL的主版本號在繪製圖形時,一定要設定。
  • 雙緩衝(Double Buffer):應用程式使用單緩衝繪圖時可能會存在影象閃爍的問題。 這是因為生成的影象不是一下子被繪製出來的,而是按照從左到右,由上而下逐畫素地繪製而成的。最終影象不是在瞬間顯示給使用者,而是通過一步一步生成的,這會導致渲染的結果很不真實。為了規避這些問題,我們應用雙緩衝渲染視窗應用程式。前緩衝儲存著最終輸出的影象,它會在螢幕上顯示;而所有的的渲染指令都會在後緩衝上繪製。當所有的渲染指令執行完畢後,我們glfwSwapBuffers()函式就是用來交換(Swap)前緩衝和後緩衝的,這樣影象就立即呈顯出來,之前提到的不真實感就消除了。

二、繪製三角形的程式碼

將展示視窗進行封裝整理,分別封裝成以下幾個類:

  • DrawTriangleMain:主函式入口。
  • Window:展示視窗。
  • ShaderProgram:著色器處理類。
  • Model:三角形頂點相關。
  • Renderer:負責渲染,即迴圈繪製圖像。

DrawTriangleMain類


public class DrawTriangleMain {

    public static void main(String[] args) throws Exception {

        //建立一個視窗
        Window window = new Window();

        //建立一個著色器程式處理類
        ShaderProgram shaderProgram = new ShaderProgram();

        try {
            //載入著色器程式
            shaderProgram.createShader(GL_VERTEX_SHADER,"/shader/demo02_vertex.vs");
            shaderProgram.createShader(GL_FRAGMENT_SHADER,"/shader/demo02_fragment.fs");
            shaderProgram.linkShader();

        } catch (Exception e) {
            e.printStackTrace();
        }

        //建立一個vao集合,將所有需要被渲染的vao全部裝入該集合中。
        List<Integer> vaoList = new ArrayList<>();

        Model model = new Model();
        //渲染時,只用vao就可以了
        int vaoId = model.getVaoId();
        vaoList.add(vaoId);

        //建立一個渲染器
        Renderer renderer = new Renderer();

        //呼叫渲染程式碼
        renderer.render(window,shaderProgram.getProgramId(),vaoList);

    }
}

Window類

public class Window {

    private long windowId;
    
    private int width = 600;
    private int height = 500;
    private String title = "暫無";

    public Window() {
        init();
    }

    public Window(int width, int height, String title) {
        this.width = width;
        this.height = height;
        this.title = title;
        init();
    }

    public void init(){

        //初始化
        glfwInit();

        //設定視窗的可見性為false
        glfwWindowHint(GLFW_VISIBLE, GL_FALSE);

        //設定視窗是否可以重新調整大小
        glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL的主版本號
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);//OpenGL的副版本號

        //建立一個視窗,返回值是個long值。
        windowId = glfwCreateWindow(width, height, title, NULL, NULL);

        //通知GLFW將window的上下文設定為當前執行緒的主上下文
        glfwMakeContextCurrent(windowId);

        //展示當前的視窗
        glfwShowWindow(windowId);

        //獲取當前裝置的一些屬性
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());

        //設定視窗的位置在最中間
        glfwSetWindowPos(windowId, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);

        //建立opengl上下文
        GL.createCapabilities();
        
    }

    //獲取視窗Id
    public long getWindowId() {
        return windowId;
    }

    //迴圈時的判斷條件
    public boolean windowShouldClose() {
        return glfwWindowShouldClose(windowId);
    }

    public void swapBuffers(){
        glfwSwapBuffers(windowId);
        glfwPollEvents();
    }
}

ShaderProgram類

public class ShaderProgram {

    private int programId;

    public ShaderProgram() {
        createProgram();
    }

    //建立一個著色器程式
    public void createProgram(){
        programId = glCreateProgram();
    }

    /**
     * 建立並繫結相應的著色器程式
     * @param shaderType:著色器型別(頂點著色器GL_VERTEX_SHADER | 片段著色器 GL_FRAGMENT_SHADER)
     * @param shaderPath
     * @throws Exception
     */
    public void createShader(int shaderType,String shaderPath) throws Exception {
        int vertexShaderId = glCreateShader(shaderType);
        glShaderSource(vertexShaderId, loadResource(shaderPath));
        glCompileShader(vertexShaderId);
        glAttachShader(programId, vertexShaderId);
    }

    /**
     * 連結著色器程式,也可以理解成將著色器啟用
     */
    public void linkShader(){
        glLinkProgram(programId);
    }

    /**
     * 載入著色器檔案
     * @param fileName:檔案路徑
     * @return : 返回著色器程式碼的字串形式
     * @throws Exception
     */
    private static String loadResource(String fileName) throws Exception {
        String result;
        try (InputStream in = Class.forName(Utils.class.getName()).getResourceAsStream(fileName);
             Scanner scanner = new Scanner(in, "UTF-8")) {
            result = scanner.useDelimiter("\\A").next();
        }
        return result;
    }

    public int getProgramId() {
        return programId;
    }
}

Model類

這個類中提到了頂點載入,以及VAO和VBO的概念。裡面的相關API參考另外一篇文章OpenGL開發關於VAO和VBO的理解

public class Model {

    private int vaoId;
    private int vboId;

    public Model() {
        init();
    }

    //獲取vao
    public int getVaoId() {
        return vaoId;
    }

    public void init(){

        float[] vertices = new float[]{
                0.0f, 0.5f, 0.0f,
                -0.5f, -0.5f, 0.0f,
                0.5f, -0.5f, 0.0f
        };

        FloatBuffer verticesBuffer = null;
        try {
            //將頂點陣列封裝至FloatBuffer中。
            verticesBuffer = MemoryUtil.memAllocFloat(vertices.length);
            verticesBuffer.put(vertices).flip();

            //生成一個vao
            vaoId = glGenVertexArrays();

            //繫結該vao
            glBindVertexArray(vaoId);

            //建立一個vbo
            vboId = glGenBuffers();

            //宣告型別的同時並繫結。
            glBindBuffer(GL_ARRAY_BUFFER, vboId);

            //將頂點資料放入vbo中。
            glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);

            //將頂點的資料格式告訴opengl,否則opengl不知道該如何去解析這些頂點陣列。
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            //將vbo進行解綁
            glBindBuffer(GL_ARRAY_BUFFER, 0);

            //將vao解綁
            glBindVertexArray(0);
        } finally {
            if (verticesBuffer != null) {
                MemoryUtil.memFree(verticesBuffer);
            }
        }
    }
}


Renderer類

public class Renderer {

    public void render(Window window,int programId,List<Integer> vaoList){
        while (!window.windowShouldClose()) {

            //清除window
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            //呼叫著色器程式
            glUseProgram(programId);

            for(int item : vaoList){

                //繫結相應id的vao記憶體
                glBindVertexArray(item);

                //啟用頂點屬性
                glEnableVertexAttribArray(0);

                //開始繪製該頂點
                glDrawArrays(GL_TRIANGLES, 0, 3);

                //禁用頂點屬性
                glDisableVertexAttribArray(0);
                
                //解綁vao
                glBindVertexArray(0);

            }

            //呼叫著色器程式
            glUseProgram(0);

            //交換視窗的前後快取幀
            window.swapBuffers();

        }
    }
}

頂點著色器程式

#version 330

layout (location =0) in vec3 position;

void main()
{
	gl_Position = vec4(position, 1.0);
}

片段著色器程式

#version 330

out vec4 fragColor;

void main()
{
	fragColor = vec4(0.0, 0.5, 0.5, 1.0);
}

執行結果:

在這裡插入圖片描述