我的webgl学习之路(二) 画一个点

我的webgl学习之路(二) 画一个点

想学webgl那么必须知道shader着色器,这是webgl最核心的部分,也是最难的部分;shader是一系列的指令,它会对屏幕的每个像素同时下达命名,也就是说,你要根据屏幕的每个位置(也就是每个像素)执行不同的操作;为什么shader运行特别快?上节我已经大概的讲过了(它是并行处理);

在这里不得不介绍一下GLSL,GLSL是什么?

       GLSL全称是 openGL Shading Language,openGL 着色语言,它们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性;它的主要目的是对图形图像顶点和片段像素的渲染;

着色器中的两种着色器:

顶点着色器(vertexshader):它是用来描述顶点特性(如位置、颜色等)的程序。顶点是指二维或三维空间中的一个点;

片元着色器(fragmentshader):进行逐片元处理过程,就是对像素颜色处理;一个像素也就是一个单元;

Webgl的执行的整个过程(我就引用webgl编程指南一书中的图,其实要多看看其它的资料,每个人的思维是不一样的,并不能完全的帮人理解,我就是这样的人,不同的资料有着不同的理解):

页面加载执行JavaScript程序,然后执行webgl相关的方法,先执行顶点着色器,然后执行片元着色器,把最后的结果给缓冲区,让它渲染到屏幕中,这就是整个过程;

绘制一个点的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>绘制点</title>
    <script id="vs" type="x-shader/x-vertex">
        void main(void){
            gl_PointSize = 20.0;
            gl_Position =vec4(0.0,0.0,0.0,1.0);
        }
    </script>
    <script id="fs" type="x-shader/x-fragment">
        void main(void){
            gl_FragColor =vec4(1.0,0.0,0.0,1.0);
        }
    </script>
</head>
<body>
<canvas id="canvas" style="background-color:black"></canvas>

<script>
    onload =function () {
        var canvas = document.getElementById("canvas");
        canvas.width = 500;
        canvas.height = 500;
        var gl = canvas.getContext("webgl");

        /*清空画板上的颜色,并初始化颜色*/
       
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 program= create_program(v_shader,f_shader);
        //开始绘制,显示器显示结果
       
gl.drawArrays(gl.POINT, 0, 1);

        function create_program(v_shader,f_shader) {
            varprogram = gl.createProgram();

            gl.attachShader(program, v_shader);
            gl.attachShader(program, f_shader);

            gl.linkProgram(program);
            gl.useProgram(program);
            returnprogram;
        }

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

            //根据idHTML中获取指定的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));
            }
        }
    }
</script>
</body>
</html>

绘制一个点的流程:

 

1、获取webgl的上下文;

2、创建shader,并获取;

3、获取程序对象,并把着色器放入程序对象中,执行;

4、绘制;

顶点着色器的代码如下

<script id="vs" type="x-shader/x-vertex">
    void main(void){
        gl_PointSize = 20.0;
        gl_Position = vec4(0.0,0.0,0.0,1.0);
    }
</script>

片元着色器代码如下:

<script id="fs" type="x-shader/x-fragment">
    void main(void){
        gl_FragColor =vec4(1.0,0.0,0.0,1.0);
    }
</script>

 

type="x-shader/x-vertex" type="x-shader/x-fragment" 不是js内部属性;所以js执行的时候回直接省略过去;在生成着色器的时候只是把字符串赋值给着色器;所以你会看到有人直接用字符串拼接着色器;

着色器代码是给gup执行的,它使用的是类C语言;着色器里必须有main函数;并且无返回值类型;gl_* 是内部命名方式;gl_PointSize 是内部定义好的变量;gl_PointSize = 20.0表示这个点的大小是20.0float类型(注意别搞错了,在着色器内部可不会转型,它是会报错的);gl_Position 也是内部属性,表示的是点位置及透明度;前面三个是颜色,后面一个是透明度,rgba模式;范围是01gl_FragColor内部定义属性,表示片元颜色;前三个表示颜色,最后一个表示透明度;

create_shader()方法是获取着色器过程;因为它们的过程都是一样的,所有封装在一起;

Vec4 参数是(floatfloatfloatfloatfloat)里面存放四个float类型的参数;

 

gl.drawArrays(gl.POINT,0,1); 参数一:是绘制模式;参数二:表示从哪个点开始绘制;参数三:表示绘制几个点;

drawArrays会告诉GPU开始把设置好的顶点绘制出来,顶点从 gl_Position =vec4(0.0,0.0,0.0,1.0);设置的空间坐标,到显示在显示器上的像素,经历一个比较复杂的过程,也就是渲染管线,过去是固定管线,现在支持可编程;

上面的代码你会发现你想要不同位置的点的时候做不到;那么怎么得到它不同位置的点呢?

修改顶点着色里的gl_Position = vec4(0.0,0.0,0.0,1.0);把vec4里的前三个数,范围是-1到1之间;比如gl_Position = vec4(-1.0,0.0,0.0,1.0);你会发现点的位置改变了;为什么它们的范围是-1到1;
Webgl 的坐标要对应到Canvas的坐标;
Webgl的中心位置(0.0,0.0,0.0)在canvas中心点位置(canvas.width/2,canvas.height/2);
Webgl的上边缘和下边缘(-1.0,0.0,0.0)和(1.0,0.0,0.)在canvas下边缘,canvas上边缘;
Webgl的左边缘和右边缘(0.0,-1.0,0.0)和(0.0,1.0,0.0)和canvas左边缘和右边缘;
如图:(webgl使用的是右手坐标系)
 
  
 

那么,怎么在外边改变点的位置呢?

就要用到 attribute 这个变量,attribute变量传输的是那些与顶点相关的数据;

使用attribute变量有以下步骤:

1、在顶点着色器中声明attribute变量;

2、将attribute变量赋值给gl_Position变量;

3、向attribute变量传输数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>在外边改变点的位置</title>
    <script id="vs" type="x-shader/x-vertex">
        attribute vec4 a_Position;
        void main(void){
            gl_PointSize = 20.0;
            gl_Position = a_Position;
        }
    </script>
    <script id="fs" type="x-shader/x-fragment">
        void main(void){
            gl_FragColor =vec4(1.0,0.0,0.0,1.0);
        }
    </script>
</head>
<body>
<canvas id="canvas" style="background-color:black"></canvas>

<script>
    onload =function () {
        var canvas = document.getElementById("canvas");
        canvas.width = 500;
        canvas.height = 500;
        var gl = canvas.getContext("webgl");

        /*清空画板上的颜色,并初始化颜色*/
       
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 program= create_program(v_shader,f_shader);
        //获取a_Position变量的存储位置
       
var a_Position= gl.getAttribLocation(program,"a_Position");

        //将顶点位置传递给a_Position变量;
       
gl.vertexAttrib3f(a_Position,1.0,0.0,0.0);//后面的3f表示传3float变量;数量的范围是1-4;不够会自动补上0.0,透明度(最后一个1.0补上)

/*       
gl.clearColor(0.0,0.0,0.0,1.0);
        gl.clear(gl.COLOR_BUTTER_BIT);*/
        //开始绘制,显示器显示结果
       
gl.drawArrays(gl.POINT, 0, 1);

        functioncreate_program(v_shader, f_shader) {
            varprogram = gl.createProgram();

            gl.attachShader(program, v_shader);
            gl.attachShader(program, f_shader);

            gl.linkProgram(program);
            gl.useProgram(program);
            returnprogram;
        }

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

            //根据idHTML中获取指定的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));
            }
        }
        functioncreate_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));
            }
        }
    }
</script>
</body>
</html>


Attribute vec4 a_Position; //attribute被称为存储限定符, vec4 表示类型,a_Position表示变量名;这就是着色器的声明方式;




猜你喜欢

转载自blog.csdn.net/qq_25909453/article/details/80161083