OpenGL开发利用lwjgl类库绘制一个三角形

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xyh930929/article/details/83311999

本文一共分为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);
}

运行结果:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xyh930929/article/details/83311999
今日推荐