WebGL入门(四)-在JavaScript程序通过uniform变量向片元着色器传值

1.demo效果

在这里插入图片描述

如上图,这个demo实现在黑色区域内点用鼠标点击,会在不同的区域画出不同颜色的小方块,第一象限的画红色方块,第三象限画绿色方块,第二、四象限画蓝色方块。

2.相关知识点

2.1 片元着色器中的uniform变量

uniform变量是一种被用来从外部着色器传输数据的变量。先介绍一下uniform变量的声明,该变量的声明有一定的格式要求:<存储限定符><类型><变量名>
在这里插入图片描述

友情提示:

  1. uniform变量必须声明成全局变量
  2. 所有的uniform变量最好以u_前缀开始,这样从变量的名字就可以区分出类型

2.2 gl.getUniformLocation()方法

与attribute变量一样,要在JavaScript程序中使用着色器中的变量,首先要获取该变量的存储地址,attribute变量通过gl.getAttribLocation() 方法获取,uniform变量则通过gl.getUniformLocation() 方法获取

函数功能:获取由name指定的uniform变量的存储地址
--------------------------------------------------------------------------
调用示例:gl.getUniformLocation(program, name)
--------------------------------------------------------------------------
参数		
			program				指定包含顶点着色器和片元着色器的着色器程序对象  
			name				指定想要获取其存储地址的uniform变量名称
--------------------------------------------------------------------------			
返回值		non-null			uniform变量的存储地址
			null				指定的uniform变量不存在,或命名具有gl_或webgl_前缀
--------------------------------------------------------------------------
错  误		INVALID_OPERATION	程序对象未能连接成功
			INVALID_VALUE		name参数的长度大于uniform变量名的最大长度,默认256字节		

示例代码

//获取片元着色器uniform变量u_FragColor的存储地址
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');

2.3 gl.uniform4f()方法

gl.getUniformLocation() 方法帮我们获取到着色器uniform变量的地址,我们在JavaScript程序中处理完,需要把新的值再传给这个uniform变量。这时就需要用到gl.uniform4f() 方法了

函数功能:将数据v0, v1, v2, v3 传给由location参数指定的uniform变量
---------------------------------------------------------------------------------
调用示例:gl.uniform4f(location, v0, v1, v2, v3)
---------------------------------------------------------------------------------
参数		
			location			指定将要修改的uniform变量的存储地址  
			v0					指定填充uniform变量第一个分量的值
			v1					指定填充uniform变量第二个分量的值
			v2					指定填充uniform变量第三个分量的值
			v2					指定填充uniform变量第四个分量的值
---------------------------------------------------------------------------------			
返回值		无
---------------------------------------------------------------------------------
错  误		INVALID_OPERATION	没有当前的program对象,或者location是非法的存储位置				

示例代码

//向片元着色器uniform变量u_FragColor传值
gl.uniform4f(u_FragColor, 1.0, 0.0, 0.0, 1.0)

2.4 gl.uniform4f()的同族函数

gl.uniform4f() 方法是一系列同族函数中的一个,该系列函数的任务就是从JavaScript程序中向着色器中的uniform变量传值

函数功能:将数据[1~4个分量]传给由location参数指定的uniform变量
------------------------------------------------------------------------------
gl.uniform1f(location, v0)
gl.uniform2f(location, v0, v1)
gl.uniform3f(location, v0, v1, v2)
gl.uniform4f(location, v0, v1, v2, v3)
------------------------------------------------------------------------------
参数		
			location			指定将要修改的uniform变量的存储地址  
			v0,v1,v2,v3			指定传输给uniform变量的四个分量的值
------------------------------------------------------------------------------			
返回值		无
------------------------------------------------------------------------------
错  误		INVALID_OPERATION	没有当前的program对象,或者location是非法的存储位置		

友情提示:
gl.uniform1f()只需要传输一个值,这个值将被填充到uniform变量的第一个分量中,第二、第三个分量将被设置为0.0,第四个分量将被设置为1.0,其他同族函数类推

2.5 uniform变量使用流程介绍

uniform变量的使用大致流程为:

  1. 在着色器中声明uniform变量
  2. 在JavaScript程序中获取uniform变量的存储地址
  3. 在JavaScript程序中通过gl.uniform4f()同族函数向着色器中的uniform变量传值

以下是示意图
在这里插入图片描述

2.6 注册鼠标点击响应事件

在JavaScript程序中我们已经获取到canvas对象的实例,我们就可以把鼠标点击事件与他绑定,即把鼠标点击响应事件注册到canvas的onmousedown事件上。示例如下

//注册鼠标点击响应事件
canvas.onmousedown = function (ev) {
  click(ev, gl, canvas, a_Position, u_FragColor)
}
// 定义点击事件的处理函数
function click(ev, gl, canvas, a_Position, u_FragColor) {
...
}

我们使用以上示例中的方式注册响应事件。而没有直接在canvas.onmousedown事件的匿名函数中处理响应逻辑,是因为匿名函数只有一个ev参数,而逻辑处理需要的gl, canvas, a_Position, u_FragColor参数无法改函数中直接使用,于是我们在canvas.onmousedown的匿名函数中调用了自己定义的click函数,传入了需要要到的参数。

3.demo代码

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

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <!--通过canvas标签创建一个400px*400px大小的画布-->
  <canvas id="webgl" width="400" height="400"></canvas>

  <script>
    //顶点着色器
    var VSHADER_SOURCE = '' +
      'attribute vec4 a_Position;\\n' + //声明attribute变量a_Position,用来存放顶点位置信息
      'attribute float a_PointSize;\\n' + //声明attribute变量a_PointSize,用来存放顶点大小信息
      'void main(){\\n' +
      '  gl_Position = a_Position;\\n' + //变量a_Position赋值给顶点着色器内置变量gl_Position
      '  gl_PointSize = a_PointSize;\\n' + //变量a_PointSize赋值给顶点着色器内置变量gl_PointSize
      '}\\n';

    //片元着色器
    var FSHADER_SOURCE = '' +
      'precision mediump float;\\n' + // 设置精度
      'uniform vec4 u_FragColor;\\n' + //声明uniform变量u_FragColor,用来存放顶点颜色信息
      'void main(){\\n' +
      //通过u_FragColor变量设置片元颜色
      ' gl_FragColor = u_FragColor;\\n' +
      '}\\n';

    //初始化着色器函数
    function initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE) {
      //创建顶点着色器对象
      var vertexShader = gl.createShader(gl.VERTEX_SHADER);
      //创建片元着色器对象
      var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      //引入顶点、片元着色器源代码
      gl.shaderSource(vertexShader, VSHADER_SOURCE);
      gl.shaderSource(fragmentShader, FSHADER_SOURCE);
      //编译顶点、片元着色器
      gl.compileShader(vertexShader);
      gl.compileShader(fragmentShader);

      //创建程序对象program
      var program = gl.createProgram();
      //附着顶点着色器和片元着色器到program
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      //链接program
      gl.linkProgram(program);
      //使用program
      gl.useProgram(program);
      gl.program = program
      //返回程序program对象
      return program;
    }

    function init() {
      //通过getElementById()方法获取canvas画布
      var canvas = document.getElementById('webgl');
      //通过方法getContext()获取WebGL上下文
      var gl = canvas.getContext('webgl');
      //初始化着色器
      initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE);

      //获取着色器attribute变量a_Position的存储地址
      var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
      //向着色器attribute变量a_Position传值
      gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)

      //获取着色器attribute变量a_PointSize的存储地址
      var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
      //向顶点着色器attribute变量a_PointSize传值
      gl.vertexAttrib1f(a_PointSize, 10.0)

      //获取片元着色器uniform变量u_FragColor的存储地址
      var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');

      // 设置canvas的背景色
      gl.clearColor(0.0, 0.0, 0.0, 1.0);

      //清空canvas
      gl.clear(gl.COLOR_BUFFER_BIT);

      //注册鼠标点击响应事件
      canvas.onmousedown = function (ev) {
        click(ev, gl, canvas, a_Position, u_FragColor)
      }
    }

    var g_points = []
    var g_colors = []

    // 定义点击事件的处理函数
    function click(ev, gl, canvas, a_Position, u_FragColor) {
      var x = ev.clientX; // 浏览器中鼠标点击的x坐标
      var y = ev.clientY; // 浏览器中鼠标点击的y坐标

      //将浏览器的坐标系转换成WebGL坐标系
      var rect = ev.target.getBoundingClientRect()
      x = ((x - rect.left) - canvas.height / 2) / (canvas.height / 2)
      y = (canvas.width / 2 - (y - rect.top)) / (canvas.width / 2)

      // 将点的位置信息存储到g_points中
      g_points.push([x, y])

      // 将点的颜色信息存储到g_colors中
      if (x >= 0.0 && y >= 0.0) { //第一象限
        g_colors.push([1.0, 0.0, 0.0, 1.0]) //红色
      } else if (x < 0.0 && y < 0.0) { //第三象限
        g_colors.push([0.0, 1.0, 0.0, 1.0]) //绿色
      } else { //第二、四象限
        g_colors.push([0.0, 0.0, 1.0, 1.0]) //蓝色
      }
      //清空canvas
      gl.clear(gl.COLOR_BUFFER_BIT)
      var len = g_points.length
      for (var i = 0; i < len; i++) {
        //获取当前点的颜色数组
        var rgba = g_colors[i]
        //向顶点着色器attribute变量a_Position传值
        gl.vertexAttrib3f(a_Position, g_points[i][0], g_points[i][1], 0.0)
        //向片元着色器uniform变量u_FragColor传值
        gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3])
        //绘制点
        gl.drawArrays(gl.POINTS, 0, 1)
      }

    }
    init()
  </script>
</body>

</html>

© 版权声明
THE END
喜欢就支持一下吧
点赞321 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容