我的webgl学习之路(五)用webgl画彩色三角形

别等时光非礼了梦想. 提交于 2019-12-08 14:16:35
要想做彩色三角线必须知道uniform、attribute、varying这三个声明;
uniform 表示一次渲染过程中保存不变的
attribute 表示是实时在改变的
varying 用来着色器之间的通讯,也就是顶点着色器和片段着色去之间的桥梁

还需要注意的是varying 在顶点和片元着色器两个中都声明一个一样的变量;那么它就会自动默认,两个有关系,但并不是相同的;片元着色器中得到的varying 是经过插值运行得到后的值;要想改变三角形的颜色,就得改变顶点颜色,它是通过顶点的颜色,来进行计算的;就以线来说:两个顶点的颜色不同,那么中间的颜色怎么办,只能通过两端点的颜色进行插值计算;怎么插值,就像渐变一样,想象一下,在取色器中,连接任意两个点,它是不是是以一种渐变的方式进行变化;这些计算方式都是渲染管进行自动计算,你只需要把顶点额颜色传入;

 

 

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多彩三角形</title>
    <script src="../js/minMatrix.js"></script>

    <script id="vs" type="x-shader/x-vertex">
        attribute vec3 position;
        attribute vec4 color;
        uniform mat4 mvpMatrix;
        varying vec4 vColor;

        void main(void){
            vColor = color;
            gl_Position = mvpMatrix * vec4(position,1.0);
        }
    </script>
    <script id="fs" type="x-shader/x-fragment">
        precision mediump float;
        varying vec4 vColor;
        void main(void){
            gl_FragColor = vColor;
        }
    </script>
</head>
<body>
<!--
uniform 表示一次渲染过程中保存不变的
attribute 表示是实时在改变的
varying 用来着色器之间的通讯,也就是顶点着色器和片段着色去之间的桥梁
-->
<!--
>>指定精确度的precision

这一次的片段着色器中的第一行,出现了一个陌生的precision,这个precision是用来指定数值的精确度的关键字,紧接着跟在precision后面的是精确度修饰符。
这个修饰符有三种,简单点说就是指定精确度为上,中,下。其实,变量中使用的小数发生变化时(也就是说,处理的数值的位数增加或是减少),根据运行的环境不同得到的结果是不太统一的。
lowp   :精确度低
mediump:精确度中
highp  :精确度高
上面的片段着色器代码中,precision后面紧接着写的是mediump float,这是说,让片段着色器中的float类型的数值的精确度都按照mediump来用。
不管在片段着色器中有没有做什么特殊的处理,首先要将precision相关的设定写上,否则在编译着色器的时候会出错。这就像魔法的咒语一样,逃不掉的。
-->
<script>

    onload = function () {
        var c = document.getElementById("canvas");
        c.width = 500;
        c.height = 300;
        //获取context
        var gl = c.getContext("webgl") || c.getContext('experimental-webgl');
        if (gl == null) {
            return;
        }
        /*清空画板上的颜色,并初始化颜色*/
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        //设定canvas初始化时候的深度
        gl.clearDepth(1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//清空画面上的颜色

        //顶点着色器和片段着色器生成
        var v_shader = create_shader('vs');
        var f_shader = create_shader('fs');

        // 程序对象的生成和连接
        var prg = create_program(v_shader,f_shader);

        //attributeLocation的获取,获取的是着色器里的position变量
        var attLocation = gl.getAttribLocation(prg,'position');
        //获取顶点着色器中的color变量
        var attLocationColor = gl.getAttribLocation(prg,'color');

        //attribute的元素数量(这次只使用xyz,所有是3)
        var attStride = 3;

        // 模型(顶点)数据
        var vertex_position = [
            0.0, 1.0, 0.0,
            1.0, 0.0, 0.0,
            -1.0, 0.0, 0.0
        ];

        // 保存顶点的颜色情报的数组
        var vertex_color = [
            1.0, 0.0, 0.0, 1.0,
            0.0, 1.0, 0.0, 1.0,
            0.0, 0.0, 1.0, 1.0
        ];

        // 生成VBO
        var vbo = create_vbo(vertex_position);
        // 绑定VBO,为了将顶点缓存和顶点着色器中的attribute变量联系起来,首先要向WebGL中绑定VBO
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        //设定attribute属性有效;使用WebGL的函数enableVertexAttribArray可以让指定属性变为有效。
        gl.enableVertexAttribArray(attLocation);
        // 添加attribute属性;attLocativon (position)的声明是vec3 , attStride=3
        /*使用WebGL函数vertexAttribPointer向着色器中写入数据。刚才定义的两个变量attLocation和attStride,在这里也用到了。vertexAttribPointer函数的
        第一个参数是attribute变量的序号,第二个参数是元素数,第三个参数是指定了数据类型的内置常量。gl.FLOAT是一个表示浮点型的常量。第四~第六各参数基本上是不怎么变的,
        根据内存有时候会传入其他的内容。需要注意的是,执行vertexAttribPointer的时候,VBO对象必须先进行绑定,哪个VBO以及和它关联的attribute属性是必须的,所以不要忘了先将VBO跟WebGL进行绑定。*/
        gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);

        /*把colors 生成vbo*/
        var color_vbo = create_vbo(vertex_color);
        gl.bindBuffer(gl.ARRAY_BUFFER,color_vbo);
        gl.enableVertexAttribArray(attLocationColor);
        gl.vertexAttribPointer(attLocationColor,4,gl.FLOAT,false,0,0);

// 使用minMatrix.js对矩阵的相关处理
        // matIV对象生成
        var m = new matIV();

// 各种矩阵的生成和初始化
        var mMatrix = m.identity(m.create());
        var vMatrix = m.identity(m.create());
        var pMatrix = m.identity(m.create());
        var mvpMatrix = m.identity(m.create());

        // 视图变换坐标矩阵
        m.lookAt([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);

        // 投影坐标变换矩阵
        m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);

        // 各矩阵相乘,得到最终的坐标变换矩阵
        m.multiply(pMatrix, vMatrix, mvpMatrix);
        m.multiply(mvpMatrix, mMatrix, mvpMatrix);

        // uniformLocation的获取,获取的是着色器里的mvpMatrix的变量
        var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix');

        // 向uniformLocation中传入坐标变换矩阵
        /*一个参数是uniform变量的序号,第二个参数是矩阵是否进行转置(true的话,有时候程序会崩溃),第三个参数是实际的坐标变换矩阵。*/
        gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);

        // 绘制模型
        /*第一个参数是指定如何使用顶点进行绘图的一个常量,第二个参数是从第几个顶点开始使用,第三个参数是绘制几个顶点。*/
        gl.drawArrays(gl.TRIANGLES, 0, 3);

        // context的刷新
        gl.flush();

        function create_shader(id) {
            // 用来保存着色器的变量
            var shader;

            // 根据id从HTML中获取指定的script标签
            var scriptElement = document.getElementById(id);

            // 如果指定的script标签不存在,则返回
            if (!scriptElement) {
                return;
            }

            // 判断script标签的type属性
            switch (scriptElement.type) {

                // 顶点着色器的时候
                case 'x-shader/x-vertex':
                    shader = gl.createShader(gl.VERTEX_SHADER);//生成顶点着色器
                    break;

                // 片段着色器的时候
                case 'x-shader/x-fragment':
                    shader = gl.createShader(gl.FRAGMENT_SHADER);//生成片元着色器
                    break;
                default :
                    return;
            }

            // 将标签中的代码分配给生成的着色器
            gl.shaderSource(shader, scriptElement.text);

            // 编译着色器
            gl.compileShader(shader);

            // 判断一下着色器是否编译成功
            if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {

                // 编译成功,则返回着色器
                return shader;
            } else {

                // 编译失败,弹出错误消息
                alert(gl.getShaderInfoLog(shader));
            }
        }
        function create_program(vs, fs) {
            // 程序对象的生成
            var program = gl.createProgram();

            // 向程序对象里分配着色器
            gl.attachShader(program, vs);
            gl.attachShader(program, fs);

            // 将着色器连接
            gl.linkProgram(program);

            // 判断着色器的连接是否成功
            if (gl.getProgramParameter(program, gl.LINK_STATUS)) {

                // 成功的话,将程序对象设置为有效
                gl.useProgram(program);

                // 返回程序对象
                return program;
            } else {

                // 如果失败,弹出错误信息
                alert(gl.getProgramInfoLog(program));
            }
        }
        function create_vbo(data){
            // 生成缓存对象
            var vbo = gl.createBuffer();

            // 绑定缓存
            gl.bindBuffer(gl.ARRAY_BUFFER, vbo);

            // 向缓存中写入数据;gl.STATIC_DRAW这个常量,定义了这个缓存中内容的更新频率
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);

            // 将绑定的缓存设为无效;这是为了防止WebGL中的缓存一致保留,而出现和预想不一致的情况
            gl.bindBuffer(gl.ARRAY_BUFFER, null);

            // 返回生成的VBO
            return vbo;
        }

    }
</script>
<canvas id="canvas"></canvas>
</body>
</html>

 

 

 

思路是:

获取到声明的顶点颜色地址var attLocationColor = gl.getAttribLocation(prg,'color');
 
传颜色数据跟传顶点一样的步骤;唯一的区别是在着色器中;把获取到的颜色color 赋值给vColor;
片元着色器中会自动的获取到传过来的颜色值vColor;但是需要注意的是vColor是进过插值运算后的值;
 
 

怎么渲染每个片元(相当于像素)? 也许你不明白,你传过来的只是几个点而已;

 

渲染管大概过程:

       顶点变换–>图元装配和光栅化(组装成图形)à 片段纹理映射和着色 à光栅化操作–->更新像素

 

光栅化:相当于根据几个顶点,绘制出图形,然后看被这个图形盖住的有哪些片元;然后根据顶点计算每一个片元的颜色;

       片元中vColor 返回的其实就是这些片元,如果你想做很好看的着色器,就得理解这个;

A_FragColor;其实就是指向的每一个片元,所以着色器就是计算每一个片元的颜色得到它的结果并把它放到缓冲器,让系统一一绘制到屏幕上;

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!