基于JavaScript的OpenGL 01 之Hello Triangle

1. 引言

本文基于JavaScript语言,描述OpenGL(即,WebGL)的绘制流程,这里描述的是OpenGL的核心模式(Core-profile)

笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:

2. 流程综述

OpenGL的绘制流程(图形渲染管线,Graphics Pipeline)如下:

img

  • 顶点着色(vertex shader)阶段将CPU传入的数据进行一定的变换处理
  • 图元装配(shape assembly)阶段的就是上阶段的顶点数据处理成图元(如,三角形)
  • 几何着色(geometry shader)阶段是根据一定规则将输入的图元变更或输出更多的图元(可选)
  • 光栅化(rasterization)阶段的是将上阶段的图元进行计算得到图元占据的屏幕像素列表
  • 片元着色(fragment)阶段是将上阶段生成的片元进行着色处理后
  • 测试与混合阶段计算片元的深度、颜色等从而进行舍弃或保留

绘制流程繁琐,然而,我们能配置的只有三个蓝色的着色器部分。几何着色器可选,一般配置顶点着色器和片段着色器即可,即,以下步骤就是配置顶点着色器和片段着色器

3. 生成顶点数据

生成顶点缓冲对象(Vertex Buffer Objects, VBO)并加载数据:

const vertices = new Float32Array([
    -0.5, -0.5, 0.0,
    0.5, -0.5, 0.0,
    0.0, 0.5, 0.0,
]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

4. 链接属性数据

顶点数组对象(Vertex Array Object, VAO)与VBO绑定,用于保存属性数据(先绑定VAO,再创建VBO就会绑定到VAO上):

gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0)

5. 创建顶点着色器

创建顶点着色器(Vertex Shader)并编译:

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
                attribute vec3 aPos;

                void main()
                {
                    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
                }
                `);
gl.compileShader(vertexShader);

6. 创建片段着色器

创建片段着色器(Fragment Shader)并编译:

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
                #version 100
                void main()
                {
                    gl_FragColor  = vec4(1.0, 0.5, 0.2, 1.0);
                }
                `);
gl.compileShader(fragmentShader);

7. 链接着色器

着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本:

const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

8. 绘制

开始绘制:

gl.clearColor(0.2, 0.3, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

gl.useProgram(shaderProgram);
gl.drawArrays(gl.TRIANGLES, 0, 3);

9. 完整代码

基于浏览器以及Canvas创建OpenGL开发环境,完整代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <canvas style="width: 800px;height: 600px;"></canvas>
    <script>
        var canvas = document.querySelector('canvas');
        var gl = canvas.getContext('webgl');
        if (!gl) {
            console.log('WebGL not supported, falling back to experimental-webgl');
        }
        
        const vertices = new Float32Array([
            -0.5, -0.5, 0.0,
            0.5, -0.5, 0.0,
            0.0, 0.5, 0.0,
        ]);
        const vertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(0)
        
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, `
        attribute vec3 aPos;

            void main()
            {
                gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
            }
        `);
        gl.compileShader(vertexShader);
        if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
            alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(vertexShader));
            gl.deleteShader(vertexShader);

        }

        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, `
            #version 100
            void main()
            {
                gl_FragColor  = vec4(1.0, 0.5, 0.2, 1.0);
            }
        `);
        gl.compileShader(fragmentShader);
        if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
            alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(fragmentShader));
            gl.deleteShader(fragmentShader);

        }
        
        const shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);
        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
         }
            
        gl.clearColor(0.2, 0.3, 0.3, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.useProgram(shaderProgram);
        gl.drawArrays(gl.TRIANGLES, 0, 3);


    </script>
</body>

</html>

运行结果如下:

image-20220716145529051

10. 参考资料

[1]你好,三角形 - LearnOpenGL CN (learnopengl-cn.github.io)

[2]【Learn OpenGL笔记】三角形(Triangle) - 知乎 (zhihu.com)

[3]Hello GLSL - Web API 接口参考 | MDN (mozilla.org)

[4]WebGL - Web API 接口参考 | MDN (mozilla.org)

posted @ 2022-07-16 15:11  当时明月在曾照彩云归  阅读(488)  评论(0编辑  收藏  举报